Compare commits
35 Commits
885298437b
...
1.1
Author | SHA1 | Date | |
---|---|---|---|
44a8a72219 | |||
c933e6b91b | |||
573b8b1d26 | |||
1aed314ae3 | |||
135a03f8d1 | |||
57c9be1c0b | |||
a34c0deee5 | |||
57cbb44b91 | |||
238b3a89d7 | |||
3fc5ab0ba2 | |||
34435f885c | |||
6369f5c7b9 | |||
9aa49c9c2c | |||
3f6d32b995 | |||
eb2366ebd8 | |||
13bcafcebf | |||
9e85edbb25 | |||
879eeb10bf | |||
b1f207b9be | |||
0e1160d292 | |||
d409c83f13 | |||
95f17a2f3b | |||
f114843e4c | |||
6af5f2a3f8 | |||
f68f5b5aa1 | |||
042f88f649 | |||
432d6ca246 | |||
979443da36 | |||
cb849308cc | |||
f61a5ff81b | |||
514ca624e5 | |||
35042278ac | |||
82700e2dc5 | |||
8aa1652283 | |||
16d5df4095 |
@ -9,7 +9,7 @@
|
|||||||
#ServerName www.example.com
|
#ServerName www.example.com
|
||||||
|
|
||||||
ServerAdmin webmaster@localhost
|
ServerAdmin webmaster@localhost
|
||||||
DocumentRoot /var/www/html/public
|
DocumentRoot /var/www/html/public/index.php
|
||||||
|
|
||||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||||
# error, crit, alert, emerg.
|
# error, crit, alert, emerg.
|
||||||
|
12
Dockerfile
@ -1,4 +1,4 @@
|
|||||||
FROM php:8.3-apache
|
FROM php:8.4-apache
|
||||||
|
|
||||||
RUN apt update && \
|
RUN apt update && \
|
||||||
apt upgrade -y && \
|
apt upgrade -y && \
|
||||||
@ -19,7 +19,6 @@ RUN docker-php-ext-configure gd --with-jpeg
|
|||||||
RUN docker-php-ext-configure zip
|
RUN docker-php-ext-configure zip
|
||||||
|
|
||||||
RUN docker-php-ext-install \
|
RUN docker-php-ext-install \
|
||||||
#pdo_sqlite \
|
|
||||||
zip \
|
zip \
|
||||||
mbstring \
|
mbstring \
|
||||||
exif \
|
exif \
|
||||||
@ -46,12 +45,13 @@ RUN rm -rf /var/www/html/translations
|
|||||||
|
|
||||||
RUN COMPOSER_ALLOW_SUPERUSER=1 composer install --no-scripts --no-dev --optimize-autoloader
|
RUN COMPOSER_ALLOW_SUPERUSER=1 composer install --no-scripts --no-dev --optimize-autoloader
|
||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
RUN chown -R 33:33 /data
|
|
||||||
RUN chmod -R 755 /data
|
|
||||||
COPY data/data.db /data/data.db
|
COPY data/data.db /data/data.db
|
||||||
|
|
||||||
RUN mkdir /var/www/html/var/cache
|
RUN mkdir /var/www/html/var/cache
|
||||||
RUN mkdir /var/www/html/var/log
|
RUN mkdir /var/www/html/var/log
|
||||||
|
RUN symfony console asset-map:compile
|
||||||
|
|
||||||
RUN chown -R 33:33 /var/www/html
|
RUN chown -R 33:33 /var/www/html /data
|
||||||
RUN chmod -R 755 /var/www/html
|
RUN chmod -R 755 /var/www/html /data
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
21
README.md
@ -2,11 +2,26 @@
|
|||||||
|
|
||||||
A program to take notes during a sermon. The web app was built with PHP and Symfony.
|
A program to take notes during a sermon. The web app was built with PHP and Symfony.
|
||||||
|
|
||||||
|
## ATTN: !!!!BREAKING CHANGE!!!! v1.0 -> v1.1
|
||||||
|
|
||||||
|
This was my first publicly available docker container so I did not realize what some decisions would do. If you are upgrading from v1 you first need to save your database OR you will lose all your current notes!! Follow the steps below to do that
|
||||||
|
|
||||||
|
1. You need to make sure that you have a running SSH server on your host computer
|
||||||
|
2. On your host computer, `docker exec -it sermon-notes bash`
|
||||||
|
3. `cd var/`
|
||||||
|
4. `scp data.db {user}@{host computer IP}:{path}`
|
||||||
|
5. Authenticate with the password
|
||||||
|
6. This will copy the file over SFTP to the host computer
|
||||||
|
7. After this then you run the `docker run...` command in Step 1 of the `Installation` instructions below, once the container is running you need to copy the `data.db` file into the working directory of the docker container.
|
||||||
|
- For example, if you have `~/docker/sermon-notes` as the path for the container on the host computer, you'll copy the `data.db` to `~/docker/sermon-notes/data`
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Run `docker run -d --name sermon-notes -p 80:80 ryanprather/sermon-notes:latest`, this will download and start the container and keep it running in the background. If you already have something on port 80 change the first `80` to whatever open port you'd like.
|
1. Make a directory in your desired docker storage folder (e.g. `~/docker/sermon-notes`), then `cd` into it.
|
||||||
2. Run `docker exec -it sermon-notes bash install.sh` This will run an install script to create an .env file specific to your install, populate with the beginning factors, and then run a `composer` command to download the necessary package dependancies.
|
2. Create a file called `.env` in that folder, no need to add anything to it right now.
|
||||||
3. Once complete you have a running system that you can navigate to in your browser with `http://{ip}:{port}|{hostname}:{port}`. Then you just need to register for an account. The first account that is created is made an admin so that you can access the `Reference Editor` and update any reference material if necessary.
|
3. Run `docker run -d --name sermon-notes -p 80:80 -v $PWD/data:/data -v $PWD/.env:/var/www/html/.env ryanprather/sermon-notes:latest`, this will download and start the container and keep it running in the background. If you already have something on port 80 change the first `80` to whatever open port you'd like.
|
||||||
|
4. Run `docker exec -it sermon-notes bash install.sh` This will run an install script to create an .env file specific to your install, populate with the beginning factors, and then run a `composer` command to download the necessary package dependancies.
|
||||||
|
5. Once complete you have a running system that you can navigate to in your browser with `http://{ip}:{port}`|`http://{hostname}:{port}`. Then you just need to register for an account. The first account that is created is made an admin so that you can access the `Reference Editor` and update any reference material if necessary.
|
||||||
|
|
||||||
## Operation
|
## Operation
|
||||||
|
|
||||||
|
@ -6,5 +6,6 @@ import './bootstrap.js';
|
|||||||
* which should already be in your base.html.twig.
|
* which should already be in your base.html.twig.
|
||||||
*/
|
*/
|
||||||
import './styles/app.css';
|
import './styles/app.css';
|
||||||
|
const $ = require('jquery');
|
||||||
|
|
||||||
console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉');
|
// console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉');
|
||||||
|
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 448 B |
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 429 B |
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 448 B |
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 408 B |
Before Width: | Height: | Size: 417 B After Width: | Height: | Size: 417 B |
Before Width: | Height: | Size: 497 B After Width: | Height: | Size: 497 B |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
@ -106,6 +106,7 @@ menu,
|
|||||||
nav {
|
nav {
|
||||||
display: block;
|
display: block;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -306,7 +307,7 @@ h6 a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 4em;
|
font-size: 2em;
|
||||||
margin: 0 0 0.5em 0;
|
margin: 0 0 0.5em 0;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
@ -1891,10 +1892,11 @@ header p {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
header.major> :last-child {
|
header.major {
|
||||||
|
/*> :last-child {*/
|
||||||
border-bottom: solid 3px #f56a6a;
|
border-bottom: solid 3px #f56a6a;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 0 2em 0;
|
margin: 0 0 10px 0;
|
||||||
padding: 0 0.75em 0.5em 0;
|
padding: 0 0.75em 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1921,6 +1923,7 @@ input[type="email"],
|
|||||||
input[type="tel"],
|
input[type="tel"],
|
||||||
input[type="search"],
|
input[type="search"],
|
||||||
input[type="url"],
|
input[type="url"],
|
||||||
|
input[type="number"],
|
||||||
select,
|
select,
|
||||||
textarea {
|
textarea {
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
@ -1967,7 +1970,7 @@ select {
|
|||||||
background-size: 1.25em;
|
background-size: 1.25em;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: calc(100% - 1em) center;
|
background-position: calc(100% - 1em) center;
|
||||||
height: 2.75em;
|
height: 2em;
|
||||||
padding-right: 2.75em;
|
padding-right: 2.75em;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
@ -1992,8 +1995,9 @@ input[type="tel"],
|
|||||||
input[type="search"],
|
input[type="search"],
|
||||||
input[type="url"],
|
input[type="url"],
|
||||||
input[type="date"],
|
input[type="date"],
|
||||||
|
input[type="number"],
|
||||||
select {
|
select {
|
||||||
height: 2.75em;
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
@ -2677,8 +2681,7 @@ button:disabled,
|
|||||||
/* Mini Posts */
|
/* Mini Posts */
|
||||||
.mini-posts article {
|
.mini-posts article {
|
||||||
border-top: solid 1px rgba(210, 215, 217, 0.75);
|
border-top: solid 1px rgba(210, 215, 217, 0.75);
|
||||||
margin-top: 2em;
|
padding-top: 1em;
|
||||||
padding-top: 2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-posts article .image {
|
.mini-posts article .image {
|
||||||
@ -2693,7 +2696,7 @@ button:disabled,
|
|||||||
|
|
||||||
.mini-posts article:first-child {
|
.mini-posts article:first-child {
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
margin-top: 0;
|
margin-top: 1em;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3197,7 +3200,7 @@ button:disabled,
|
|||||||
#sidebar>.inner>* {
|
#sidebar>.inner>* {
|
||||||
border-bottom: solid 2px rgba(210, 215, 217, 0.75);
|
border-bottom: solid 2px rgba(210, 215, 217, 0.75);
|
||||||
margin: 0 0 3.5em 0;
|
margin: 0 0 3.5em 0;
|
||||||
padding: 0 0 3.5em 0;
|
/*padding: 0 0 3.5em 0;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar>.inner>*> :last-child {
|
#sidebar>.inner>*> :last-child {
|
||||||
@ -3281,14 +3284,14 @@ button:disabled,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#sidebar>.inner>.alt {
|
#sidebar>.inner>.alt {
|
||||||
margin: -1.66667em 0 3.33333em -1.66667em;
|
margin: 0;
|
||||||
padding: 1.66667em;
|
padding: 1.66667em;
|
||||||
width: calc(100% + 3.33333em);
|
width: calc(100% + 3.33333em);
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .toggle {
|
#sidebar .toggle {
|
||||||
height: 4.5em;
|
height: 4.5em;
|
||||||
left: 24em;
|
left: 23em;
|
||||||
line-height: 4.5em;
|
line-height: 4.5em;
|
||||||
text-indent: 5em;
|
text-indent: 5em;
|
||||||
width: 5em;
|
width: 5em;
|
||||||
@ -3408,7 +3411,18 @@ button:disabled,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1680px) {
|
@media screen and (max-width: 1680px) {
|
||||||
#header {}
|
#header .logo {
|
||||||
|
font-size: 1.25em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header .icons {
|
||||||
|
height: 5em;
|
||||||
|
line-height: 5em;
|
||||||
|
position: absolute;
|
||||||
|
right: -0.5em;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 736px) {
|
@media screen and (max-width: 736px) {
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
85
assets/js/reference.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
export function retrieveReferenceType(el) {
|
||||||
|
fetch('/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
|
||||||
|
*/
|
||||||
|
export function retrieveReference(el) {
|
||||||
|
if (el.value == 'new') {
|
||||||
|
document.querySelector('#refName').style.display = 'inline-block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch('/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.
|
||||||
|
*/
|
||||||
|
export function saveReference() {
|
||||||
|
let ref = document.querySelector('#referenceSeries');
|
||||||
|
let cont = document.querySelector('#reference');
|
||||||
|
fetch('/save-reference', {
|
||||||
|
method: 'POST',
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
refId: ref.value,
|
||||||
|
text: cont.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(results => {
|
||||||
|
//alert(results.msg);
|
||||||
|
|
||||||
|
document.querySelector('#reference').value = '';
|
||||||
|
document.querySelector('#referenceType').value = '';
|
||||||
|
document.querySelector('#referenceSeries').value = '';
|
||||||
|
});
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
// Get references to the form elements
|
// Get references to the form elements
|
||||||
const nameInput = document.getElementById("name");
|
const nameInput = document.getElementById("registration_form_name");
|
||||||
const emailInput = document.getElementById("emailAddress");
|
const emailInput = document.getElementById("registration_form_email");
|
||||||
const passwordInput = document.getElementById("password");
|
const passwordInput = document.getElementById("registration_form_plainPassword");
|
||||||
const confirmPasswordInput = document.getElementById("confirmPassword");
|
const csrfToken = document.getElementById("registration_form__token").value;
|
||||||
const csrfToken = document.getElementById("csrfToken").value;
|
|
||||||
|
|
||||||
// Add event listeners to the form
|
// Add event listeners to the form
|
||||||
const registerBtn = document.querySelector("#register-btn");
|
const registerBtn = document.querySelector("#register-btn");
|
||||||
@ -18,28 +17,21 @@ function handleSubmit(event) {
|
|||||||
const name = nameInput.value;
|
const name = nameInput.value;
|
||||||
const email = emailInput.value;
|
const email = emailInput.value;
|
||||||
const password = passwordInput.value;
|
const password = passwordInput.value;
|
||||||
const confirmPassword = confirmPasswordInput.value;
|
|
||||||
|
|
||||||
if (name === "" || email === "" || password === "") {
|
if (name === "" || email === "" || password === "") {
|
||||||
alert("Please fill in all fields.");
|
alert("Please fill in all fields.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
|
||||||
alert("Passwords do not match.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send data to server for processing
|
// Send data to server for processing
|
||||||
const data = {
|
const data = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"email": email,
|
"email": email,
|
||||||
"password": password,
|
|
||||||
"plainPassword": password,
|
"plainPassword": password,
|
||||||
"csrf_token": csrfToken
|
"_token": csrfToken
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch("/index.php/register", {
|
fetch("/register", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
@ -8,133 +8,17 @@ var to = null;
|
|||||||
let controller;
|
let controller;
|
||||||
var BOOKS = {};
|
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.
|
* 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.
|
* Sends a POST request to the '/search' endpoint with the query as a JSON payload.
|
||||||
* Updates the '#old-notes' element with the search results.
|
* Updates the '#old-notes' element with the search results.
|
||||||
*
|
*
|
||||||
* @return {Promise} A Promise that resolves with the search results.
|
* @return {Promise} A Promise that resolves with the search results.
|
||||||
*/
|
*/
|
||||||
function search() {
|
function search() {
|
||||||
query = document.querySelector('#query').value;
|
query = document.querySelector('#query').value;
|
||||||
fetch('/index.php/search', {
|
fetch('/search', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -211,6 +95,13 @@ function saveNote(event) {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (!textDirty || !validateNote()) {
|
if (!textDirty || !validateNote()) {
|
||||||
clearTimeout(to);
|
clearTimeout(to);
|
||||||
to = setTimeout(saveNote, saveInterval);
|
to = setTimeout(saveNote, saveInterval);
|
||||||
@ -234,7 +125,7 @@ function saveNote(event) {
|
|||||||
refs: references
|
refs: references
|
||||||
};
|
};
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/index.php/save-note',
|
url: '/save-note',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
data: JSON.stringify(note),
|
data: JSON.stringify(note),
|
||||||
@ -290,14 +181,20 @@ function validateNote() {
|
|||||||
const title = document.querySelector('#noteTitle');
|
const title = document.querySelector('#noteTitle');
|
||||||
const psg = document.querySelector('#passage');
|
const psg = document.querySelector('#passage');
|
||||||
|
|
||||||
if (!title.value.length) { return false; }
|
let ret = true;
|
||||||
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;
|
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(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -366,7 +263,7 @@ function deleteNote(noteId, link) {
|
|||||||
|
|
||||||
var row = link.parentElement.parentElement;
|
var row = link.parentElement.parentElement;
|
||||||
|
|
||||||
fetch('/index.php/delete-note', {
|
fetch('/delete-note', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -409,7 +306,7 @@ function newSpeaker() {
|
|||||||
*/
|
*/
|
||||||
function saveSpeaker(event) {
|
function saveSpeaker(event) {
|
||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
fetch('/index.php/save-speaker', {
|
fetch('/save-speaker', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -437,7 +334,6 @@ function saveSpeaker(event) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A description of the entire function.
|
* A description of the entire function.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
function newSeries() {
|
function newSeries() {
|
||||||
if (document.querySelector('#series').value == 'new') {
|
if (document.querySelector('#series').value == 'new') {
|
||||||
@ -450,14 +346,14 @@ function newSeries() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a series by making a POST request to '/index.php/save-series' with the series name as the request body.
|
* Saves a series by making a POST request to '/save-series' with the series name as the request body.
|
||||||
*
|
*
|
||||||
* @param {Event} event - The keydown event.
|
* @param {Event} event - The keydown event.
|
||||||
* @return {Promise} A Promise that resolves with the response from the server.
|
* @return {Promise} A Promise that resolves with the response from the server.
|
||||||
*/
|
*/
|
||||||
function saveSeries(event) {
|
function saveSeries(event) {
|
||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
fetch('/index.php/save-series', {
|
fetch('/save-series', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -538,7 +434,7 @@ function queryRef(type = null, book = null, input = null) {
|
|||||||
if (!book) {
|
if (!book) {
|
||||||
var book = document.querySelector('#referenceBook').value;
|
var book = document.querySelector('#referenceBook').value;
|
||||||
}
|
}
|
||||||
fetch('/index.php/retrieve-reference', {
|
fetch('/retrieve-reference', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -582,6 +478,7 @@ function makeButton(title) {
|
|||||||
var btn = document.createElement('button');
|
var btn = document.createElement('button');
|
||||||
btn.innerText = title;
|
btn.innerText = title;
|
||||||
btn.class = 'button';
|
btn.class = 'button';
|
||||||
|
btn.style = 'line-height:normal;'
|
||||||
btn.addEventListener('click', function () {
|
btn.addEventListener('click', function () {
|
||||||
removeActiveRef();
|
removeActiveRef();
|
||||||
document.querySelector('#ref').innerHTML = md.render(references[title]);
|
document.querySelector('#ref').innerHTML = md.render(references[title]);
|
||||||
@ -619,73 +516,24 @@ function removeActiveRef() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
* Toggles the visibility of the fields container and updates the active state of the show/hide button.
|
||||||
*
|
*
|
||||||
|
* @param boolean show
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
function toggleFields() {
|
function toggleFields(show = false) {
|
||||||
const fieldsContainer = document.getElementById('fields-container');
|
const fieldsContainer = document.getElementById('fields-container');
|
||||||
const showHideBtn = document.getElementById('show-hide-btn');
|
const showHideBtn = document.getElementById('show-hide-btn');
|
||||||
|
|
||||||
if (fieldsContainer.classList.contains('show')) {
|
if (show || !fieldsContainer.classList.contains('show')) {
|
||||||
fieldsContainer.classList.remove('show');
|
|
||||||
fieldsContainer.style.display = 'none';
|
|
||||||
showHideBtn.classList.remove('active');
|
|
||||||
} else {
|
|
||||||
fieldsContainer.classList.add('show');
|
fieldsContainer.classList.add('show');
|
||||||
fieldsContainer.style.display = 'block';
|
fieldsContainer.style.display = 'block';
|
||||||
showHideBtn.classList.add('active');
|
showHideBtn.classList.add('active');
|
||||||
|
} else {
|
||||||
|
fieldsContainer.classList.remove('show');
|
||||||
|
fieldsContainer.style.display = 'none';
|
||||||
|
showHideBtn.classList.remove('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
setHeight();
|
setHeight();
|
||||||
@ -739,13 +587,34 @@ function retrieveBooks() {
|
|||||||
newBook.text = BOOKS.cd[x];
|
newBook.text = BOOKS.cd[x];
|
||||||
bookList.appendChild(newBook);
|
bookList.appendChild(newBook);
|
||||||
}
|
}
|
||||||
|
} else if (selectedType == 'hc') {
|
||||||
|
var none = document.createElement("option");
|
||||||
|
none.value = '';
|
||||||
|
none.text = '-- Select --';
|
||||||
|
bookList.appendChild(none);
|
||||||
|
for (var x in BOOKS[selectedType]) {
|
||||||
|
var newBook = document.createElement("optgroup");
|
||||||
|
newBook.label = "Lord's Day " + (parseInt(x) + 1)
|
||||||
|
var ld = document.createElement("option");
|
||||||
|
ld.value = 'ld' + (parseInt(x) + 1);
|
||||||
|
ld.text = "LD " + (parseInt(x) + 1) + " All";
|
||||||
|
newBook.appendChild(ld);
|
||||||
|
|
||||||
|
for (var y in BOOKS[selectedType][x]) {
|
||||||
|
var question = document.createElement("option");
|
||||||
|
question.value = 'hc' + BOOKS[selectedType][x][y];
|
||||||
|
question.text = "HC" + BOOKS[selectedType][x][y];
|
||||||
|
newBook.appendChild(question);
|
||||||
|
}
|
||||||
|
bookList.appendChild(newBook);
|
||||||
|
}
|
||||||
} else if (selectedType == 'note') {
|
} else if (selectedType == 'note') {
|
||||||
var none = document.createElement("option");
|
var none = document.createElement("option");
|
||||||
none.value = '';
|
none.value = '';
|
||||||
none.text = '-- Select --';
|
none.text = '-- Select --';
|
||||||
bookList.appendChild(none);
|
bookList.appendChild(none);
|
||||||
|
|
||||||
fetch('/index.php/retrieve-reference', {
|
fetch('/retrieve-reference', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -822,91 +691,6 @@ function filterVerse() {
|
|||||||
verseRange.innerText = 'Verse: ' + verse;
|
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.
|
* Previews a note by rendering the markdown content of the note in a preview section.
|
||||||
* Toggles between the note text and preview sections.
|
* Toggles between the note text and preview sections.
|
||||||
@ -1096,7 +880,7 @@ function openNote(openSidebar = true) {
|
|||||||
* @return {Promise<void>} A promise that resolves when the note is successfully retrieved and the UI is updated.
|
* @return {Promise<void>} A promise that resolves when the note is successfully retrieved and the UI is updated.
|
||||||
*/
|
*/
|
||||||
function retrieveNote(id, runOpen = true) {
|
function retrieveNote(id, runOpen = true) {
|
||||||
fetch('/index.php/get-note', {
|
fetch('/get-note', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
header: {
|
header: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -1185,7 +969,7 @@ function shareNote(event) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch('/index.php/share-note', {
|
fetch('/share-note', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -1213,6 +997,8 @@ function shareNote(event) {
|
|||||||
function increaseFont() {
|
function increaseFont() {
|
||||||
var currentSize = document.querySelector('#ref').style.fontSize;
|
var currentSize = document.querySelector('#ref').style.fontSize;
|
||||||
document.querySelector('#ref').style.fontSize = (parseInt(currentSize) + 1) + 'pt';
|
document.querySelector('#ref').style.fontSize = (parseInt(currentSize) + 1) + 'pt';
|
||||||
|
document.querySelector('#notes').style.fontSize = (parseInt(currentSize) + 1) + 'pt';
|
||||||
|
document.querySelector('#notePreview').style.fontSize = (parseInt(currentSize) + 1) + 'pt';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1223,6 +1009,8 @@ function increaseFont() {
|
|||||||
function decreaseFont() {
|
function decreaseFont() {
|
||||||
var currentSize = document.querySelector('#ref').style.fontSize;
|
var currentSize = document.querySelector('#ref').style.fontSize;
|
||||||
document.querySelector('#ref').style.fontSize = (parseInt(currentSize) - 1) + 'pt';
|
document.querySelector('#ref').style.fontSize = (parseInt(currentSize) - 1) + 'pt';
|
||||||
|
document.querySelector('#notes').style.fontSize = (parseInt(currentSize) - 1) + 'pt';
|
||||||
|
document.querySelector('#notePreview').style.fontSize = (parseInt(currentSize) - 1) + 'pt';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
1
assets/js/script.min.js
vendored
Normal file
116
assets/js/site.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
export function initHome() {
|
||||||
|
setHeight();
|
||||||
|
setBooks();
|
||||||
|
setEventListeners();
|
||||||
|
$('#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', openShareNote);
|
||||||
|
$('#modal-backdrop').on('click', closeShareNote);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) => {
|
||||||
|
BOOKS = data;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 >= 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}
|
||||||
|
*/
|
||||||
|
export function setHeight() {
|
||||||
|
md = new markdownit({
|
||||||
|
html: true,
|
||||||
|
linkify: true,
|
||||||
|
breaks: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = document.querySelector('body');
|
||||||
|
body.style.height = window.innerHeight + 'px';
|
||||||
|
|
||||||
|
var cont = document.querySelector('#main');
|
||||||
|
cont.style.height = (window.innerHeight) + 'px';
|
||||||
|
|
||||||
|
var tabs = document.querySelector('.ref-tab');
|
||||||
|
tabs.style.height = (window.innerHeight - 13) + 'px';
|
||||||
|
|
||||||
|
var ref = document.querySelector('.ref');
|
||||||
|
ref.style.height = (window.innerHeight - 60) + 'px';
|
||||||
|
|
||||||
|
var noteList = document.querySelector('#note-list');
|
||||||
|
noteList.style.height = (window.innerHeight - 60) + 'px';
|
||||||
|
|
||||||
|
var notes = document.querySelector('.notes');
|
||||||
|
notes.style.height = (window.innerHeight - 60) + 'px';
|
||||||
|
|
||||||
|
var 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);
|
||||||
|
}
|
||||||
|
}
|
49
assets/js/template.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
export function retrieveTemplate(orig, dest) {
|
||||||
|
const temp = document.querySelector('#' + orig);
|
||||||
|
if (temp.value == '0') {
|
||||||
|
document.querySelector('#' + dest).value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch('/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.
|
||||||
|
*/
|
||||||
|
export function saveTemplate() {
|
||||||
|
fetch('/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);
|
||||||
|
});
|
||||||
|
}
|
@ -18,7 +18,7 @@ body {
|
|||||||
|
|
||||||
.ref-tab {
|
.ref-tab {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
padding-top: 55px !important;
|
padding-top: 60px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ref-tab ul {
|
.ref-tab ul {
|
@ -19,7 +19,7 @@ body {
|
|||||||
|
|
||||||
.ref-tab {
|
.ref-tab {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
padding-top: 55px !important;
|
padding-top: 60px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ref-tab ul {
|
.ref-tab ul {
|
Before Width: | Height: | Size: 730 KiB After Width: | Height: | Size: 730 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 898 KiB After Width: | Height: | Size: 898 KiB |
@ -15,37 +15,37 @@
|
|||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
"phpdocumentor/reflection-docblock": "^5.4",
|
"phpdocumentor/reflection-docblock": "^5.4",
|
||||||
"phpstan/phpdoc-parser": "^1.28",
|
"phpstan/phpdoc-parser": "^1.28",
|
||||||
"symfony/asset": "7.0.*",
|
"symfony/asset": "7.2.*",
|
||||||
"symfony/asset-mapper": "7.0.*",
|
"symfony/asset-mapper": "7.2.*",
|
||||||
"symfony/console": "7.0.*",
|
"symfony/console": "7.2.*",
|
||||||
"symfony/debug-bundle": "7.0.*",
|
"symfony/debug-bundle": "7.2.*",
|
||||||
"symfony/doctrine-messenger": "7.0.*",
|
"symfony/doctrine-messenger": "7.2.*",
|
||||||
"symfony/dotenv": "7.0.*",
|
"symfony/dotenv": "7.2.*",
|
||||||
"symfony/expression-language": "7.0.*",
|
"symfony/expression-language": "7.2.*",
|
||||||
"symfony/flex": "^2",
|
"symfony/flex": "^2",
|
||||||
"symfony/form": "7.0.*",
|
"symfony/form": "7.2.*",
|
||||||
"symfony/framework-bundle": "7.0.*",
|
"symfony/framework-bundle": "7.2.*",
|
||||||
"symfony/http-client": "7.0.*",
|
"symfony/http-client": "7.2.*",
|
||||||
"symfony/intl": "7.0.*",
|
"symfony/intl": "7.2.*",
|
||||||
"symfony/mailer": "7.0.*",
|
"symfony/mailer": "7.2.*",
|
||||||
"symfony/mime": "7.0.*",
|
"symfony/mime": "7.2.*",
|
||||||
"symfony/monolog-bundle": "^3.0",
|
"symfony/monolog-bundle": "^3.0",
|
||||||
"symfony/notifier": "7.0.*",
|
"symfony/notifier": "7.2.*",
|
||||||
"symfony/process": "7.0.*",
|
"symfony/process": "7.2.*",
|
||||||
"symfony/property-access": "7.0.*",
|
"symfony/property-access": "7.2.*",
|
||||||
"symfony/property-info": "7.0.*",
|
"symfony/property-info": "7.2.*",
|
||||||
"symfony/runtime": "7.0.*",
|
"symfony/runtime": "7.2.*",
|
||||||
"symfony/security-bundle": "7.0.*",
|
"symfony/security-bundle": "7.2.*",
|
||||||
"symfony/serializer": "7.0.*",
|
"symfony/serializer": "7.2.*",
|
||||||
"symfony/stimulus-bundle": "^2.17",
|
"symfony/stimulus-bundle": "^2.17",
|
||||||
"symfony/string": "7.0.*",
|
"symfony/string": "7.2.*",
|
||||||
"symfony/translation": "7.0.*",
|
"symfony/translation": "7.2.*",
|
||||||
"symfony/twig-bundle": "7.0.*",
|
"symfony/twig-bundle": "7.2.*",
|
||||||
"symfony/uid": "7.0.*",
|
"symfony/uid": "7.2.*",
|
||||||
"symfony/ux-turbo": "^2.17",
|
"symfony/ux-turbo": "^2.17",
|
||||||
"symfony/validator": "7.0.*",
|
"symfony/validator": "7.2.*",
|
||||||
"symfony/web-link": "7.0.*",
|
"symfony/web-link": "7.2.*",
|
||||||
"symfony/yaml": "7.0.*",
|
"symfony/yaml": "7.2.*",
|
||||||
"twig/extra-bundle": "^2.12|^3.0",
|
"twig/extra-bundle": "^2.12|^3.0",
|
||||||
"twig/twig": "^2.12|^3.0"
|
"twig/twig": "^2.12|^3.0"
|
||||||
},
|
},
|
||||||
@ -96,16 +96,16 @@
|
|||||||
"extra": {
|
"extra": {
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"allow-contrib": false,
|
"allow-contrib": false,
|
||||||
"require": "7.0.*"
|
"require": "7.2.*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"symfony/browser-kit": "7.0.*",
|
"symfony/browser-kit": "7.2.*",
|
||||||
"symfony/css-selector": "7.0.*",
|
"symfony/css-selector": "7.2.*",
|
||||||
"symfony/maker-bundle": "^1.0",
|
"symfony/maker-bundle": "^1.0",
|
||||||
"symfony/phpunit-bridge": "^7.0",
|
"symfony/phpunit-bridge": "^7.2",
|
||||||
"symfony/stopwatch": "7.0.*",
|
"symfony/stopwatch": "7.2.*",
|
||||||
"symfony/web-profiler-bundle": "7.0.*"
|
"symfony/web-profiler-bundle": "7.2.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
data/data.db
@ -1,5 +1,3 @@
|
|||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
sermon-notes:
|
sermon-notes:
|
||||||
container_name: sermon-notes
|
container_name: sermon-notes
|
||||||
@ -7,4 +5,6 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data
|
- $PWD/data:/data
|
||||||
|
- $PWD/.env:/var/www/html/.env
|
||||||
|
restart: unless-stopped
|
||||||
|
@ -25,4 +25,13 @@ return [
|
|||||||
'@hotwired/turbo' => [
|
'@hotwired/turbo' => [
|
||||||
'version' => '7.3.0',
|
'version' => '7.3.0',
|
||||||
],
|
],
|
||||||
|
'jquery' => [
|
||||||
|
'version' => '3.3.1',
|
||||||
|
],
|
||||||
|
'jquery-ui' => [
|
||||||
|
'version' => '1.14.1',
|
||||||
|
],
|
||||||
|
'datatables' => [
|
||||||
|
'version' => '1.10.18',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
rm -rf .env*
|
if [ ! -f /var/www/html/.env ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
echo "APP_ENV=prod" > .env
|
echo "APP_ENV=prod" > .env
|
||||||
echo "APP_DEBUG=0" >> .env
|
echo "APP_DEBUG=0" >> .env
|
||||||
|
43
migrations/Version20240717022017.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20240717022017 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE TABLE shared_note (id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, note_id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, owner_id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, shared_user_id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, PRIMARY KEY(id), CONSTRAINT FK_754B918C26ED0855 FOREIGN KEY (note_id) REFERENCES note (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_754B918C26ED0855 ON shared_note (note_id)');
|
||||||
|
$this->addSql('CREATE TABLE shared_series (id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, series_id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, owner_id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, shared_user_id BLOB NOT NULL --(DC2Type:uuid)
|
||||||
|
, PRIMARY KEY(id), CONSTRAINT FK_59E803195278319C FOREIGN KEY (series_id) REFERENCES series (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_59E803195278319C ON shared_series (series_id)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('DROP TABLE shared_note');
|
||||||
|
$this->addSql('DROP TABLE shared_series');
|
||||||
|
}
|
||||||
|
}
|