Compare commits

..

109 Commits

Author SHA1 Message Date
44a8a72219 upd: home
Add input-error style for validation feedback
2025-06-03 13:01:47 -04:00
c933e6b91b upd: registration
Add CSRF protection to registration form
2025-06-03 13:00:34 -04:00
573b8b1d26 upd: notes
add validation feedback if user doesn't fill out all required fields.
2025-06-03 12:58:42 -04:00
1aed314ae3 fix: registration
Fix a couple typos in registration JS
2025-06-03 12:44:03 -04:00
135a03f8d1 upd: data.db
Updates to starting database
2025-04-28 00:09:22 -04:00
57c9be1c0b fix: various templates
convert to assets
2025-04-27 23:50:07 -04:00
a34c0deee5 fix: reference-editor template
fix bug
2025-04-27 23:48:36 -04:00
57cbb44b91 rem: sidebar template
remove delete note link
2025-04-27 23:45:16 -04:00
238b3a89d7 ref: profile template
convert to asset code
2025-04-27 23:44:48 -04:00
3fc5ab0ba2 ref: home template
Add js module code
fix label
2025-04-27 23:40:53 -04:00
34435f885c fix: defaultcontroller
comment out onload for home
2025-04-27 23:11:51 -04:00
6369f5c7b9 fix: ajax
Bug fix, add flush for reference data
2025-04-27 23:07:03 -04:00
9aa49c9c2c fix: index.php 2025-04-27 23:04:10 -04:00
3f6d32b995 upd: *.js
Refactor methods to other scripts
2025-04-27 22:58:03 -04:00
eb2366ebd8 upd: register.js
Fix error in csrfToken element
2025-04-27 22:54:12 -04:00
13bcafcebf upd: app.js
add jquery
2025-04-27 22:53:32 -04:00
9e85edbb25 upd: main.css
reduce font-size for h1 tag items
2025-04-27 22:49:20 -04:00
879eeb10bf upd: README
Update for v1.1 to add warning about breaking change
2025-04-27 22:48:29 -04:00
b1f207b9be upd: install.sh
Do a check for .env before install and remove
2025-04-27 22:45:59 -04:00
0e1160d292 upd: importmap
add jquery, jquery-ui, datatables
2025-04-27 21:08:30 -04:00
d409c83f13 upd: Dockerfile
Update to php 8.4
add symfony asset compile
Set permissions and ownership of /data directory
2025-04-27 21:01:46 -04:00
95f17a2f3b upd: docker-compose
remove version tag
change data volume
add .env file
add restart
2025-04-27 20:55:34 -04:00
f114843e4c Other various changes 2025-04-04 15:27:16 -04:00
6af5f2a3f8 Upgrade to Symfony 7.2 2025-04-04 15:26:34 -04:00
f68f5b5aa1 Change document root 2025-04-04 15:26:12 -04:00
042f88f649 delete old files 2025-04-04 15:25:55 -04:00
432d6ca246 Move files 2025-04-04 15:25:13 -04:00
979443da36 JS updates and expand HC 2024-11-18 00:24:05 -05:00
cb849308cc Style updates 2024-11-18 00:22:40 -05:00
f61a5ff81b Delete previous classes 2024-07-21 21:33:43 -04:00
514ca624e5 Update styles 2024-07-12 20:33:57 -04:00
35042278ac Update base database 2024-07-12 20:13:13 -04:00
82700e2dc5 Add NoteShares 2024-07-02 19:24:48 -04:00
8aa1652283 Add email for registration notification 2024-07-02 19:24:21 -04:00
16d5df4095 Convert profile view to rows
Add additional sections for shared notes
2024-07-02 19:24:08 -04:00
885298437b Add parsedown library and symfony mailer 2024-07-02 01:57:29 -04:00
8cf8d82443 Add shareNote, openShareNote, closeShareNote and docblock comments for each function 2024-07-02 01:56:20 -04:00
37f3ade420 Add modal dialog to share note 2024-07-02 01:54:43 -04:00
258ff9eda7 Add template for note sharing 2024-07-02 01:53:45 -04:00
2c9511ecf4 migrate email to use Util class and method 2024-07-02 01:41:47 -04:00
97d656912c migrate email to use Util class and method 2024-07-02 01:41:33 -04:00
717b9398bc Added credential login check for visiting the profile page 2024-07-02 01:39:11 -04:00
6a070ca4e0 Added shareNote method to share an individual note with an email 2024-07-02 01:38:36 -04:00
fa8ef2ab78 Update styles 2024-07-02 01:33:24 -04:00
20dc1622f2 Fix error with not sending emails 2024-07-02 01:33:14 -04:00
fbf5ac564a To support note deletion 2024-06-24 15:43:03 -04:00
d24c304c97 Updates 2024-06-23 22:41:33 -04:00
752b2a291e Add profile data 2024-06-23 22:40:50 -04:00
87fb461c26 Update database and add new migration 2024-06-23 20:13:25 -04:00
70e9ef508c Style updates 2024-06-23 20:13:02 -04:00
fba8454743 Added formatted cheat sheet html content for help 2024-06-11 21:23:43 -04:00
da5e3b538d Updates to docker build 2024-06-09 20:42:39 -04:00
c2c08db342 Add saveFailCounter
Fix bug with not clearing noteId
Refactor a class removal and additions
Add script.min.js
2024-06-09 20:26:54 -04:00
4d55711190 Adding new reference type retrieval 2024-06-09 20:24:44 -04:00
164ed62a48 add method for retrieving 39 articles 2024-06-09 20:18:54 -04:00
bdc67942ea Add validation for email registration 2024-06-09 20:18:27 -04:00
4a6191e287 Add link to markdown cheat sheet 2024-06-09 20:17:40 -04:00
d809e7eb38 Add new reference types 2024-06-09 20:17:20 -04:00
83709509f2 Move markdown cheat sheet 2024-06-09 20:16:42 -04:00
7bb909dfe4 Add link for recording 2024-05-29 00:37:56 -04:00
1d98940341 removed onload method call since it's now in script.js 2024-05-29 00:37:27 -04:00
14b4ca0545 add moment.js and datatables.js 2024-05-29 00:37:12 -04:00
ea60cf2622 add methods to retrieve new references 2024-05-29 00:35:42 -04:00
53e5c48aa9 Changed reference editor to not pass through data 2024-05-29 00:34:50 -04:00
a1e921063a add getNotes method for open notes table 2024-05-29 00:32:07 -04:00
2552335513 Migrate openNotes table to use DataTables
Added couple helper methods
Fixed not clearing recording link on "New Note"
add retrieveReferenceType method for reference editor
2024-05-29 00:30:42 -04:00
74b24afc75 Style updates 2024-05-29 00:24:05 -04:00
451a61722e New starter dataset 2024-05-29 00:23:48 -04:00
a46253ec7d Add retrieveLBC and retrieveNote for references 2024-05-26 22:46:54 -04:00
45a3398ac7 Fix error with retrieving HC and CD references and add LBC 2024-05-26 22:46:25 -04:00
a20caf1fc8 Added LBC and note reference retrieval
Convert get-reference to retrieve data from database instead of files
Fixed error with saving new note
2024-05-26 22:43:56 -04:00
435402ee41 Add LBC and new reference field 2024-05-26 22:40:45 -04:00
8315970571 Reorg the note fields and add recording link field
Add LBC
2024-05-26 22:33:41 -04:00
d26874d88b Add recording link field 2024-05-26 22:30:53 -04:00
d1ac0578f3 Add recording link field to note 2024-05-26 22:30:39 -04:00
3d10944b6d Style updates 2024-05-26 22:30:20 -04:00
72bf4273f3 increase saveTimeout
add check for note text to see if something has changed
add recording link text field
fix error for check
covert reference buttons to match style of other buttons
2024-05-26 22:30:03 -04:00
77e2777c7a Add LBC 2024-05-26 22:24:51 -04:00
a1f5ce416c Style updates 2024-05-26 22:24:40 -04:00
6af0095470 Merge pull request 'Remerge' (#3) from main into 1.1
Reviewed-on: #3
2024-05-25 08:11:00 -04:00
11f566897f Fix install instructions 2024-05-25 08:01:46 -04:00
7044d16ee0 Update docker-compose 2024-05-24 11:48:08 -04:00
4a81718818 Rework dockerfile to final form 2024-05-24 00:54:38 -04:00
a624faa728 Rework install script to create .env and run symfony and composer commands 2024-05-24 00:54:11 -04:00
2b2b3c8dc4 Add symfony/debug-bundle because I get an error otherwise 2024-05-24 00:50:29 -04:00
e0309874d4 Change code to make first user an admin so they have access to reference editor 2024-05-24 00:49:56 -04:00
35748e6db4 Write expanded README 2024-05-23 20:46:23 -04:00
1aedc1887e Expand referenceType options for clarity 2024-05-23 20:46:06 -04:00
dcddb35925 Remove environment variables 2024-05-23 20:45:45 -04:00
777e81ae43 Add couple doc images and markdown cheat sheet 2024-05-23 19:32:00 -04:00
56d6e412f2 Add HomePage image and rename files with space 2024-05-23 18:11:00 -04:00
0dbe033435 Add doc images 2024-05-23 17:57:16 -04:00
5ae8e5c50b update docker-compse 2024-05-23 17:45:14 -04:00
2c2381a0f1 Update docker build file 2024-05-23 17:43:46 -04:00
591f80c288 Add open and close button for reference query 2024-05-23 17:42:58 -04:00
216ef0d1ec Update scripts 2024-05-23 17:42:21 -04:00
050b0cd8f7 Update more styles 2024-05-23 17:40:41 -04:00
345e904852 Update styles 2024-05-22 19:43:13 -04:00
b307d616f6 Add reverse sort for open notes and a couple cleanups 2024-05-22 19:42:50 -04:00
6c6bbf8990 Remove empty elements 2024-05-22 19:39:55 -04:00
d73b91cdc6 Remove data.js and substitute data.json 2024-05-22 19:37:57 -04:00
36cd8944e3 Add starting database 2024-05-17 19:30:12 -04:00
4c056aea8b Update docker build 2024-05-17 19:25:28 -04:00
6a81698f8a Delete .env 2024-05-17 18:57:03 -04:00
a30f1528da Add files for production 2024-05-17 10:14:16 -04:00
415e6e44c7 remove .env 2024-05-17 13:29:26 +00:00
9277eb0cad Update for production 2024-05-17 09:10:34 -04:00
212021b261 Add text to search criteria and expand placeholder note 2024-05-17 09:10:12 -04:00
9a76624d1c Fix width of menu 2024-05-17 09:08:38 -04:00
162 changed files with 4366 additions and 1897 deletions

42
.env
View File

@ -1,42 +0,0 @@
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
# https://symfony.com/doc/current/configuration/secrets.html
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=c9124e6a5434e81e428ab5236aa6259b
APP_DEBUG=1
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# DATABASE_URL="mysql://root:password@192.168.1.3:3306/sermon_notes"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ###
###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
###< symfony/messenger ###
###> symfony/mailer ###
# MAILER_DSN=null://null
###< symfony/mailer ###

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
###> symfony/framework-bundle ###
/.env
/.env.local
/.env.local.php
/.env.*.local

29
000-default.conf Normal file
View File

@ -0,0 +1,29 @@
<VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/public/index.php
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>

57
Dockerfile Normal file
View File

@ -0,0 +1,57 @@
FROM php:8.4-apache
RUN apt update && \
apt upgrade -y && \
apt install -y \
libzip-dev \
unzip \
libonig-dev \
libxml2-dev \
libpng-dev \
libjpeg-dev \
libicu-dev \
sqlite3 \
curl \
git \
nano
RUN docker-php-ext-configure gd --with-jpeg
RUN docker-php-ext-configure zip
RUN docker-php-ext-install \
zip \
mbstring \
exif \
pcntl \
bcmath \
xml \
intl
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
php composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
php -r "unlink('composer-setup.php');"
RUN curl -sS https://get.symfony.com/cli/installer | bash && \
mv /root/.symfony5/bin/symfony /usr/local/bin/symfony
COPY . /var/www/html/
RUN mv 000-default.conf /etc/apache2/sites-available/
RUN rm /var/www/html/.env*
RUN rm -rf /var/www/html/var/*
RUN rm -rf /var/www/html/vendor
RUN rm -rf /var/www/html/tests
RUN rm -rf /var/www/html/translations
RUN COMPOSER_ALLOW_SUPERUSER=1 composer install --no-scripts --no-dev --optimize-autoloader
RUN mkdir /data
COPY data/data.db /data/data.db
RUN mkdir /var/www/html/var/cache
RUN mkdir /var/www/html/var/log
RUN symfony console asset-map:compile
RUN chown -R 33:33 /var/www/html /data
RUN chmod -R 755 /var/www/html /data
EXPOSE 80

View File

@ -1,3 +1,83 @@
# Sermon Notes
A program to take sermon notes
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
1. Make a directory in your desired docker storage folder (e.g. `~/docker/sermon-notes`), then `cd` into it.
2. Create a file called `.env` in that folder, no need to add anything to it right now.
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
This is the home page you're first presented with.
![Home](/assets/images/Home.png)
Create an account with "Register".
![Register](/assets/images/Registration.png)
Once you create an account you'll need to login
![Login](/assets/images/Login.png)
Once you get logged in you'll be presented with the main home page
![HomePage](/assets/images/HomePage.png)
If you are accessing on a regular iPad the menu should be collapsed, if not, you may need to do that. I built this with a iPad 9th Gen in mind so visibility on another device may vary.
![Header](/assets/images/Header.png)
Across the top you will see a "plus" and "minus" buttons that will increase and decrease the size of font for the reference content. The 3rd button is to open the Reference opener box. On the right side you will see the "table" that will open the sermon details portion. The next button is a preview button to preview the notes you have typed. The final dropdown is to quickly apply a template for you to start taking notes.
![SermonData](/assets/images/SermonData.png)
These fields are meant to hide because you only need to fill them out once and they are the specifics of the sermon itself (e.g. title, date, speaker, series, and passage)
![Template](/assets/images/Template.png)
The notes you take are done in [Markdown](/docs/markdown-cheat-sheet.md). In the above example, there is an introduction and 3 bullet points. Each has a sub-bullet unordered list. Review the Markdown link above for more tips on how to write Markdown.
When you are done with your Notes it will look something like the following.
![Example](/assets/images/MarkdownExample.png)
The tool is built that it will auto-save every 10 seconds and have a 5 second timeout should you be in a place where you don't have good internet service. When the tool starts to save you will see an orange checkmark appear next to the "Notes" header above where your typing. When it is done saving it will turn green and fade away.
I built this tool because I needed a way to take notes and have reference material open next to me. So I built a reference opener to do just that
![ReferenceOpener](/assets/images/ReferenceOpener.png)
I have added many reference options (Bible, various creeds, Belgic Confession, Heidelberg Catechism, Canons of Dort, Westminster Confession of Faith, Westminster Larger Catechism, or Westminster Short Catechism). I plan to add others in the future. Simply select the type then filter to the book/chapter/week of the referenced work and hit "Search". This will open the reference on the left side of the page.
![References](/assets/images/References.png)
You can switch between the references by clicking the button on the left side. You can double-tap one of the buttons to remove that reference from the list.
Once a note has been saved, it can be retrieved in two different ways. You can open the open page (by clicking on the Menu (hamburger) then "Open Notes")
![OpenNotes](/assets/images/OpenNotes.png)
or if the note was recently made you will see the most recent 4 notes listed under the menu
![RecentNotes](/assets/images/RecentNotes.png)
By clicking either of the title links it will open the note and display it for you to edit or read. I can add "Notes" as future reference content.

View File

@ -6,5 +6,6 @@ import './bootstrap.js';
* which should already be in your base.html.twig.
*/
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! 🎉');

View File

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 448 B

View File

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 429 B

View File

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 448 B

View File

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

View File

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 408 B

View File

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 417 B

View File

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 497 B

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -105,6 +105,8 @@ hgroup,
menu,
nav {
display: block;
width: 250px;
margin-bottom: 1em;
}
body {
@ -305,7 +307,7 @@ h6 a {
}
h1 {
font-size: 4em;
font-size: 2em;
margin: 0 0 0.5em 0;
line-height: 1.3;
}
@ -1890,10 +1892,11 @@ header p {
text-transform: uppercase;
}
header.major> :last-child {
header.major {
/*> :last-child {*/
border-bottom: solid 3px #f56a6a;
display: inline-block;
margin: 0 0 2em 0;
margin: 0 0 10px 0;
padding: 0 0.75em 0.5em 0;
}
@ -1920,6 +1923,7 @@ input[type="email"],
input[type="tel"],
input[type="search"],
input[type="url"],
input[type="number"],
select,
textarea {
-moz-appearance: none;
@ -1966,7 +1970,7 @@ select {
background-size: 1.25em;
background-repeat: no-repeat;
background-position: calc(100% - 1em) center;
height: 2.75em;
height: 2em;
padding-right: 2.75em;
text-overflow: ellipsis;
}
@ -1991,8 +1995,9 @@ input[type="tel"],
input[type="search"],
input[type="url"],
input[type="date"],
input[type="number"],
select {
height: 2.75em;
height: 1.5em;
}
textarea {
@ -2232,7 +2237,6 @@ a.image:hover img {
ol {
list-style: decimal;
margin: 0 0 2em 0;
padding-left: 1.25em;
}
ol li {
@ -2242,11 +2246,6 @@ ol li {
ul {
list-style: disc;
margin: 0 0 2em 0;
padding-left: 1em;
}
ul li {
/*padding-left: 0.5em;*/
}
ul.alt {
@ -2682,8 +2681,7 @@ button:disabled,
/* Mini Posts */
.mini-posts article {
border-top: solid 1px rgba(210, 215, 217, 0.75);
margin-top: 2em;
padding-top: 2em;
padding-top: 1em;
}
.mini-posts article .image {
@ -2698,7 +2696,7 @@ button:disabled,
.mini-posts article:first-child {
border-top: 0;
margin-top: 0;
margin-top: 1em;
padding-top: 0;
}
@ -3091,13 +3089,13 @@ button:disabled,
}
#main>.inner {
padding: 0 0.5em 0.1em 0.5em;
padding: 0 0.25em 0.1em 0.25em;
margin: 0 auto;
max-width: 110em;
}
#main>.inner>section {
padding: 0.5em 0 0.5em 0;
padding: 0.25em 0 0.25em 0;
border-top: solid 2px rgba(210, 215, 217, 0.75);
}
@ -3107,18 +3105,14 @@ button:disabled,
@media screen and (max-width: 1680px) {
#main>.inner {
padding: 0 0 0 2em;
padding: 0 0 0 0;
}
#main>.inner>section {}
}
@media screen and (max-width: 1280px) {
#main>.inner {
padding: 0 0 0 2em;
padding: 0 0 0 0;
}
#main>.inner>section {}
}
@media screen and (max-width: 736px) {
@ -3198,7 +3192,7 @@ button:disabled,
}
#sidebar>.inner {
padding: 2.22222em 2.22222em 2.44444em 2.22222em;
padding: 2.22em 2.22em 2.44em 2.22em;
position: relative;
width: 26em;
}
@ -3206,7 +3200,7 @@ button:disabled,
#sidebar>.inner>* {
border-bottom: solid 2px rgba(210, 215, 217, 0.75);
margin: 0 0 3.5em 0;
padding: 0 0 3.5em 0;
/*padding: 0 0 3.5em 0;*/
}
#sidebar>.inner>*> :last-child {
@ -3222,9 +3216,9 @@ button:disabled,
#sidebar>.inner>.alt {
background-color: #eff1f2;
border-bottom: 0;
margin: -2.22222em 0 4.44444em -2.22222em;
padding: 2.22222em;
width: calc(100% + 4.44444em);
margin: -2.22em -2.33em 1.0em -2.22em;
padding: 2.22em;
width: calc(100% + 4.44em);
}
#sidebar .toggle {
@ -3236,9 +3230,9 @@ button:disabled,
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
border: 0;
display: block;
height: 7.5em;
height: 4.5em;
left: 26em;
line-height: 7.5em;
line-height: 4.5em;
outline: 0;
overflow: hidden;
position: absolute;
@ -3290,17 +3284,19 @@ button:disabled,
}
#sidebar>.inner>.alt {
margin: -1.66667em 0 3.33333em -1.66667em;
margin: 0;
padding: 1.66667em;
width: calc(100% + 3.33333em);
}
#sidebar .toggle {
height: 6.25em;
left: 24em;
line-height: 6.25em;
height: 4.5em;
left: 23em;
line-height: 4.5em;
text-indent: 5em;
width: 5em;
background-color: #fff;
margin-left: 1em;
}
#sidebar .toggle:before {
@ -3345,7 +3341,7 @@ button:disabled,
#sidebar .toggle {
text-indent: 6em;
width: 6em;
width: 4.5em;
}
#sidebar .toggle:before {
@ -3415,12 +3411,21 @@ button:disabled,
}
@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) {
#header {}
#header .logo {
font-size: 1.25em;
margin: 0;

BIN
assets/images/Header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/images/Home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
assets/images/HomePage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/images/Login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
assets/images/Menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
assets/images/OpenNotes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/images/Template.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

85
assets/js/reference.js Normal file
View 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 = '';
});
}

View File

@ -1,9 +1,8 @@
// Get references to the form elements
const nameInput = document.getElementById("name");
const emailInput = document.getElementById("emailAddress");
const passwordInput = document.getElementById("password");
const confirmPasswordInput = document.getElementById("confirmPassword");
const csrfToken = document.getElementById("csrfToken").value;
const nameInput = document.getElementById("registration_form_name");
const emailInput = document.getElementById("registration_form_email");
const passwordInput = document.getElementById("registration_form_plainPassword");
const csrfToken = document.getElementById("registration_form__token").value;
// Add event listeners to the form
const registerBtn = document.querySelector("#register-btn");
@ -18,28 +17,21 @@ function handleSubmit(event) {
const name = nameInput.value;
const email = emailInput.value;
const password = passwordInput.value;
const confirmPassword = confirmPasswordInput.value;
if (name === "" || email === "" || password === "") {
alert("Please fill in all fields.");
return;
}
if (password !== confirmPassword) {
alert("Passwords do not match.");
return;
}
// Send data to server for processing
const data = {
"name": name,
"email": email,
"password": password,
"plainPassword": password,
"csrf_token": csrfToken
"_token": csrfToken
};
fetch("/index.php/register", {
fetch("/register", {
method: "POST",
headers: {
"Content-Type": "application/json",

View File

@ -4,71 +4,21 @@ var references = {};
var tabs = [];
let saved = false;
let textDirty = false;
let saveTimeout = 10000;
var to = null;
let controller;
var BOOKS = {};
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');
}
});
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, saveTimeout);
}
}
/**
* Searches for notes based on the query entered in the search field.
* Sends a POST request to the '/search' endpoint with the query as a JSON payload.
* Updates the '#old-notes' element with the search results.
*
* @return {Promise} A Promise that resolves with the search results.
*/
function search() {
query = document.querySelector('#query').value;
fetch('/index.php/search', {
fetch('/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -99,6 +49,15 @@ function search() {
});
}
/**
* 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 = '';
@ -117,7 +76,8 @@ function newNote() {
document.querySelector('#series').value = 0;
document.querySelector('#template').value = 0;
document.querySelector('#passage').value = '';
document.querySelector('#noteId').value = uuidv4();
document.querySelector('#recording').value = '';
document.querySelector('#noteId').value = '';
document.querySelector('#ref-list').innerHTML = '';
document.querySelector('#ref').innerHTML = '';
@ -135,12 +95,24 @@ function saveNote(event) {
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()) {
clearTimeout(to);
to = setTimeout(saveNote, saveTimeout);
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,
@ -149,32 +121,58 @@ function saveNote(event) {
series: document.querySelector('#series').value,
passage: document.querySelector('#passage').value,
note: document.querySelector('#notes').value,
recording: document.querySelector('#recording').value,
refs: references
};
fetch('/index.php/save-note', {
$.ajax({
url: '/save-note',
method: 'POST',
headers: {
"Content-Type": 'application/json'
},
body: JSON.stringify(note)
contentType: 'application/json',
data: JSON.stringify(note),
dataType: 'json',
timeout: saveTimeout
})
.then(response => response.json())
.then(data => {
.done(function (data) {
if (data.msg == 'saved' && !saved) {
saveFailureCount = SAVE_FAILURE_LIMIT;
saveCheck.classList.remove('saving', 'error', 'fa-times-circle', 'fa-save');
showSave();
saved = true;
textDirty = false;
document.querySelector('#note-header-left h2').classList.remove('dirty');
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;
}
}
})
.catch(error => console.log(error))
.finally(() => function () { clearTimeout(to); to = setTimeout(saveNote, saveTimeout); });
.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');
@ -183,21 +181,45 @@ function validateNote() {
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; }
let ret = true;
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;
}
/**
* 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.
*
@ -208,15 +230,16 @@ 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;
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 >= 1) {
if (checkmark.style.opacity == 0.1) {
checkmark.style.opacity = 0;
clearInterval(si);
saved = false;
@ -227,33 +250,44 @@ function showSave() {
/**
* Function to discard the note by clearing all input fields and closing the menu.
*/
function discardNote() {
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 = '';
fetch('/index.php/discard-note', {
var row = link.parentElement.parentElement;
fetch('/delete-note', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'id': document.querySelector('#noteId').value
'id': noteId
})
.then(response => response.json())
.then(data => {
if (data.msg == 'deleted') {
alert('Note deleted.');
}
})
});
})
.then(response => response.json())
.then(data => {
if (data.msg != 'deleted') {
return;
}
openRef();
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';
@ -264,9 +298,15 @@ function newSpeaker() {
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', {
fetch('/save-speaker', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -292,6 +332,9 @@ function saveSpeaker(event) {
}
}
/**
* A description of the entire function.
*/
function newSeries() {
if (document.querySelector('#series').value == 'new') {
document.querySelector('#newSeries').style.display = 'inline-block';
@ -302,9 +345,15 @@ function newSeries() {
textDirty = true;
}
/**
* 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.
* @return {Promise} A Promise that resolves with the response from the server.
*/
function saveSeries(event) {
if (event.keyCode == 13) {
fetch('/index.php/save-series', {
fetch('/save-series', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -330,7 +379,14 @@ function saveSeries(event) {
}
}
/**
* 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';
@ -342,14 +398,35 @@ function openRef(closeSidebar = true) {
}
}
/**
* Closes the reference query and resets the reference search form.
*
* @return {void} This function does not return anything.
*/
function closeRef() {
refQuery = document.querySelector('#refQuery');
refQuery.style.display = 'none';
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 #search').value;
var input = document.querySelector('#refQuery #referenceSearch').value;
}
if (!type) {
var type = document.querySelector('#referenceType').value;
@ -357,7 +434,7 @@ function queryRef(type = null, book = null, input = null) {
if (!book) {
var book = document.querySelector('#referenceBook').value;
}
fetch('/index.php/retrieve-reference', {
fetch('/retrieve-reference', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -382,11 +459,6 @@ function queryRef(type = null, book = null, input = null) {
references[results.title] = results.text;
input.value = '';
document.querySelector('#referenceType').value = '';
document.querySelector('#referenceBook').value = '';
document.querySelector('#referenceBook').style.display = 'none';
document.querySelector('#refQuery #search').value = '';
closeRef();
saved = false;
@ -396,13 +468,21 @@ function queryRef(type = null, book = null, input = null) {
});
}
/**
* 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.style = 'line-height:normal;'
btn.addEventListener('click', function () {
removeActiveRef();
document.querySelector('#ref').innerHTML = md.render(references[title]);
this.classList.add('activeRef');
this.classList.add('active');
findRefLinks();
});
@ -417,83 +497,58 @@ function makeButton(title) {
});
removeActiveRef();
btn.classList.add('activeRef');
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('.activeRef');
tabs = document.querySelectorAll('.active');
for (var t in tabs) {
if (isFinite(parseInt(t))) {
tabs[t].classList.remove('activeRef');
tabs[t].classList.remove('active');
}
}
}
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.
* Toggles the visibility of the fields container and updates the active state of the show/hide button.
*
* @param boolean show
* @return {void}
*/
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);
});
}
function toggleFields() {
function toggleFields(show = false) {
const fieldsContainer = document.getElementById('fields-container');
const showHideBtn = document.getElementById('show-hide-btn');
if (fieldsContainer.classList.contains('show')) {
// Hide the fields when the button says "Show Fields"
fieldsContainer.classList.remove('show');
fieldsContainer.style.display = 'none';
showHideBtn.textContent = 'Show';
} else {
// Show the fields when the button says "Hide Fields"
if (show || !fieldsContainer.classList.contains('show')) {
fieldsContainer.classList.add('show');
fieldsContainer.style.display = 'block';
showHideBtn.textContent = 'Hide';
showHideBtn.classList.add('active');
} else {
fieldsContainer.classList.remove('show');
fieldsContainer.style.display = 'none';
showHideBtn.classList.remove('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; }
@ -501,13 +556,14 @@ function retrieveBooks() {
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 = BOOKS.bible[x];
newBook.text = x;
bookList.appendChild(newBook);
}
} else if (selectedType == 'creed') {
@ -531,6 +587,51 @@ function retrieveBooks() {
newBook.text = BOOKS.cd[x];
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') {
var none = document.createElement("option");
none.value = '';
none.text = '-- Select --';
bookList.appendChild(none);
fetch('/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];
@ -547,45 +648,55 @@ function retrieveBooks() {
}
}
function retrieveReference(el) {
fetch('/index.php/get-reference', {
method: "POST",
header: {
"Content-Type": "application/json"
},
body: JSON.stringify({
file: el.value,
type: el.options[el.selectedIndex].getAttribute('type')
})
})
.then(response => response.json())
.then(results => {
document.querySelector('#reference').value = results.text;
});
/**
* 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;
}
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);
/**
* 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;
}
document.querySelector('#reference').value = '';
document.querySelector('#references').value = '';
});
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;
}
/**
* 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');
@ -600,19 +711,24 @@ function previewNote() {
notePreview.innerHTML = md.render(markdownPreview);
if (previewButton.value == 'Preview') {
previewButton.value = 'Hide Preview';
noteText.style.display = 'none';
notePreview.style.display = 'block';
} else {
previewButton.value = 'Preview';
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');
@ -655,6 +771,9 @@ function findLinks() {
}
}
/**
* Function that finds reference links and fetches passage data when clicked.
*/
function findRefLinks() {
var links = document.querySelector('#ref').querySelectorAll('a');
@ -697,14 +816,21 @@ function findRefLinks() {
}
}
/**
* 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.pageXOffset;
let y = event.clientY + window.pageYOffset;
let x = event.clientX + window.scrollX;
let y = event.clientY + window.scrollY;
// Set the position of the popup element
popup.style.top = `${y}px`;
@ -712,12 +838,23 @@ function showPassage(event, text) {
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');
@ -735,8 +872,15 @@ function openNote(openSidebar = true) {
}
}
/**
* 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', {
fetch('/get-note', {
method: 'POST',
header: {
'Content-Type': 'application/json'
@ -782,14 +926,91 @@ function retrieveNote(id, runOpen = true) {
});
}
/**
* 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('/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';
document.querySelector('#notes').style.fontSize = (parseInt(currentSize) + 1) + 'pt';
document.querySelector('#notePreview').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';
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

File diff suppressed because one or more lines are too long

116
assets/js/site.js Normal file
View 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
View 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);
});
}

View File

@ -4,10 +4,6 @@ body {
}
/* Style for hamburger menu */
.fa-check {
color: green;
}
.inner {
display: flex;
flex-direction: row;
@ -15,14 +11,14 @@ body {
justify-content: space-evenly;
align-items: stretch;
align-content: flex-start;
max-width: 1020px;
max-width: 1060px;
/* Adjust to your desired width */
margin: 0 auto;
}
.ref-tab {
width: 60px;
padding-top: 65px !important;
padding-top: 60px !important;
}
.ref-tab ul {
@ -47,30 +43,18 @@ body {
height: 80px;
width: 100%;
text-align: center;
background-color: #f56a6a;
color: #fff !important;
border: none;
border-radius: 3px;
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.8);
font-size: 14px;
margin-bottom: 3px;
}
.tab button:active {
background-color: #7a0016;
}
.activeRef {
background-color: #3e8e41 !important;
}
.ref {
width: 35%;
width: 36%;
}
.ref > div#ref {
vertical-align: top;
justify-content: start;
align-content: start;
justify-content: flex-start;
align-content: flex-start;
overflow-y: scroll;
width: 100%;
height: 100%;
@ -80,8 +64,7 @@ body {
}
#ref {
padding: 5px;
font-size: 12pt;
padding: 3px 3px 3px 3px;
}
#passage {
@ -107,7 +90,7 @@ body {
}
.notes {
width: 55%;
width: 57%;
}
textarea#notes {
@ -119,6 +102,20 @@ textarea#notes {
#notePreview {
display: none;
overflow-x: scroll;
padding-left: 10px;
}
#notePreview ul,
#notePreview ol {
list-style-position: inside;
}
#previewBtn.active,
#show-hide-btn.active,
.tab button.active,
#openRefBtn.active {
background-color: #f56a6a !important;
color: white !important;
}
#note-header-left {
@ -139,6 +136,8 @@ textarea#notes {
#note-list {
display: none;
height: 100%;
overflow-y: scroll;
}
#note-list ul {
@ -152,12 +151,12 @@ div#refQuery {
display: none;
position: absolute;
z-index: 100;
background-color: rgba(0, 0, 0, 0.5);
background-color: rgba(0, 0, 0, 0.8);
width: 400px;
height: 200px;
}
div#refQuery #search {
div#refQuery #referenceSearch {
border: none;
border-radius: 5px;
padding: 10px 20px;
@ -168,6 +167,7 @@ div#refQuery #search {
width: 150px;
height: 25px;
cursor: pointer;
display: none;
}
#passage-popup {
@ -189,12 +189,21 @@ div#refQuery #search {
opacity: 0;
text-align: right;
font-size: 20pt;
margin-top: 15px;
margin-left: 15px;
color: green;
}
#save-check.saving {
color: orange;
}
#save-check.error {
color: red;
}
#fields-container input,
#fields-container select {
width: 19.5%;
width: 32.5%;
display: inline-block;
}
@ -209,4 +218,79 @@ div#refQuery #search {
#old-notes article p:last-child {
font-size: 12pt;
}
.inner {
padding-left: 0;
}
.ref h2 {
display: inline-block;
}
#ref {
font-size: 12pt;
}
#ref ol,
#ref ul {
list-style-position: inside;
}
#referenceBook {
display: none;
}
.recording-link {
font-size: 8pt;
color: blue;
text-decoration: none;
}
.fas-trash-alt {
color: red;
cursor: pointer;
}
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #000;
opacity: 0.5;
z-index: 1000;
}
.modal-container {
position: absolute;
width: 300px;
/* adjust this to your desired modal width */
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
z-index: 1001;
}
.modal-header {
background-color: #f5f5f5;
padding: 10px;
border-bottom: 1px solid #ddd;
}
.modal-body {
padding: 20px;
}
.modal-footer {
background-color: #f5f5f5;
padding: 10px;
border-top: 1px solid #ddd;
}
.btn-secondary {
color: #337ab7;
}/*# sourceMappingURL=style.css.map */

View File

@ -0,0 +1 @@
{"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"AAAA,kCAAA;AACA;EACI,SAAA;ACCJ;;ADEA,6BAAA;AAEA;EACI,aAAA;EACA,mBAAA;EACA,eAAA;EACA,6BAAA;EACA,oBAAA;EACA,yBAAA;EACA,iBAAA;EACA,iCAAA;EACA,cAAA;ACAJ;;ADGA;EACI,WAAA;EACA,4BAAA;ACAJ;;ADGA;EACI,SAAA;EACA,UAAA;ACAJ;;ADGA;EACI,gBAAA;EACA,qBAAA;ACAJ;;ADGA;EACI,kBAAA;ACAJ;;ADGA,GAAA;AACA;EACI,aAAA;EACA,uBAAA;EACA,mBAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,eAAA;EACA,kBAAA;ACAJ;;ADIA;EACI,UAAA;ACDJ;;ADIA;EACI,mBAAA;EACA,2BAAA;EACA,yBAAA;EACA,kBAAA;EACA,WAAA;EACA,YAAA;EACA,uBAAA;EACA,kBAAA;EACA,wCAAA;ACDJ;;ADIA;EACI,wBAAA;ACDJ;;ADIA;EACI,YAAA;ACDJ;;ADIA;EACI,aAAA;EACA,YAAA;ACDJ;;ADIA;EACI,aAAA;EACA,YAAA;ACDJ;;ADIA;EACI,aAAA;ACDJ;;ADIA;EACI,cAAA;ACDJ;;ADIA;EACI,UAAA;ACDJ;;ADIA;EACI,WAAA;EACA,YAAA;EACA,eAAA;ACDJ;;ADIA;EACI,aAAA;EACA,kBAAA;EACA,kBAAA;ACDJ;;ADIA;;EAEI,2BAAA;ACDJ;;ADIA;;;;EAII,oCAAA;EACA,uBAAA;ACDJ;;ADIA;EACI,oBAAA;EACA,mBAAA;EACA,UAAA;ACDJ;;ADIA;EACI,oBAAA;EACA,2BAAA;EACA,UAAA;ACDJ;;ADIA;EACI,iBAAA;ACDJ;;ADIA;EACI,aAAA;EACA,YAAA;EACA,kBAAA;ACDJ;;ADIA;EACI,SAAA;EACA,UAAA;EACA,qBAAA;EACA,gBAAA;ACDJ;;ADIA;EACI,aAAA;EACA,kBAAA;EACA,YAAA;EACA,oCAAA;EACA,YAAA;EACA,aAAA;ACDJ;;ADIA;EACI,YAAA;EACA,kBAAA;EACA,kBAAA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,yBAAA;EACA,YAAA;EACA,YAAA;EACA,eAAA;EACA,aAAA;ACDJ;;ADIA;EACI,aAAA;EACA,kBAAA;EACA,YAAA;EACA,sBAAA;EACA,YAAA;EACA,aAAA;EACA,sBAAA;EACA,kBAAA;EACA,wCAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;ACDJ;;ADIA;EACI,UAAA;EACA,iBAAA;EACA,eAAA;EACA,iBAAA;EACA,YAAA;ACDJ;;ADIA;EACI,aAAA;ACDJ;;ADIA;EACI,UAAA;ACDJ;;ADIA;;EAEI,YAAA;EACA,qBAAA;ACDJ;;ADIA;EACI,2BAAA;EACA,eAAA;ACDJ;;ADIA;EACI,eAAA;ACDJ;;ADIA;EACI,eAAA;ACDJ;;ADIA;EACI,eAAA;ACDJ;;ADIA;EACI,qBAAA;ACDJ;;ADIA;EACI,eAAA;ACDJ;;ADIA;;EAEI,2BAAA;ACDJ;;ADIA;EACI,aAAA;ACDJ;;ADIA;EACI,cAAA;EACA,WAAA;EACA,qBAAA;ACDJ;;ADIA;EACI,UAAA;EACA,eAAA;ACDJ;;ADIA;EACI,eAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;EACA,OAAA;EACA,sBAAA;EACA,YAAA;EACA,aAAA;ACDJ;;ADIA;EACI,kBAAA;EACA,YAAA;EACA,4CAAA;EACA,cAAA;EACA,aAAA;EACA,sBAAA;EACA,sBAAA;EACA,uCAAA;EACA,aAAA;ACDJ;;ADIA;EACI,yBAAA;EACA,aAAA;EACA,6BAAA;ACDJ;;ADIA;EACI,aAAA;ACDJ;;ADIA;EACI,yBAAA;EACA,aAAA;EACA,0BAAA;ACDJ;;ADIA;EACI,cAAA;ACDJ","file":"style.css"}

View File

@ -5,10 +5,6 @@ body {
/* Style for hamburger menu */
.fa-check {
color: green;
}
.inner {
display: flex;
flex-direction: row;
@ -16,14 +12,14 @@ body {
justify-content: space-evenly;
align-items: stretch;
align-content: flex-start;
max-width: 1020px;
max-width: 1060px;
/* Adjust to your desired width */
margin: 0 auto;
}
.ref-tab {
width: 60px;
padding-top: 65px !important;
padding-top: 60px !important;
}
.ref-tab ul {
@ -48,31 +44,19 @@ body {
height: 80px;
width: 100%;
text-align: center;
background-color: #f56a6a;
color: #fff !important;
border: none;
border-radius: 3px;
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.8);
font-size: 14px;
margin-bottom: 3px;
&:active {
background-color: #7a0016;
}
}
.activeRef {
background-color: #3e8e41 !important;
}
.ref {
width: 35%;
width: 36%;
}
.ref>div#ref {
vertical-align: top;
justify-content: start;
align-content: start;
justify-content: flex-start;
align-content: flex-start;
overflow-y: scroll;
width: 100%;
height: 100%;
@ -82,8 +66,7 @@ body {
}
#ref {
padding: 5px;
font-size: 12pt;
padding: 3px 3px 3px 3px;
}
#passage {
@ -109,7 +92,7 @@ body {
}
.notes {
width: 55%
width: 57%
}
textarea#notes {
@ -121,6 +104,20 @@ textarea#notes {
#notePreview {
display: none;
overflow-x: scroll;
padding-left: 10px;
}
#notePreview ul,
#notePreview ol {
list-style-position: inside;
}
#previewBtn.active,
#show-hide-btn.active,
.tab button.active,
#openRefBtn.active {
background-color: #f56a6a !important;
color: white !important;
}
#note-header-left {
@ -141,6 +138,8 @@ textarea#notes {
#note-list {
display: none;
height: 100%;
overflow-y: scroll;
}
#note-list ul {
@ -154,12 +153,12 @@ div#refQuery {
display: none;
position: absolute;
z-index: 100;
background-color: rgba(0, 0, 0, 0.5);
background-color: rgba(0, 0, 0, 0.8);
width: 400px;
height: 200px;
}
div#refQuery #search {
div#refQuery #referenceSearch {
border: none;
border-radius: 5px;
padding: 10px 20px;
@ -170,6 +169,7 @@ div#refQuery #search {
width: 150px;
height: 25px;
cursor: pointer;
display: none;
}
#passage-popup {
@ -191,12 +191,21 @@ div#refQuery #search {
opacity: 0;
text-align: right;
font-size: 20pt;
margin-top: 15px;
margin-left: 15px;
color: green;
}
#save-check.saving {
color: orange;
}
#save-check.error {
color: red;
}
#fields-container input,
#fields-container select {
width: 19.5%;
width: 32.5%;
display: inline-block;
}
@ -212,3 +221,78 @@ div#refQuery #search {
#old-notes article p:last-child {
font-size: 12pt;
}
.inner {
padding-left: 0;
}
.ref h2 {
display: inline-block;
}
#ref {
font-size: 12pt;
}
#ref ol,
#ref ul {
list-style-position: inside;
}
#referenceBook {
display: none;
}
.recording-link {
font-size: 8pt;
color: blue;
text-decoration: none;
}
.fas-trash-alt {
color: red;
cursor: pointer;
}
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #000;
opacity: 0.5;
z-index: 1000;
}
.modal-container {
position: absolute;
width: 300px;
/* adjust this to your desired modal width */
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
z-index: 1001;
}
.modal-header {
background-color: #f5f5f5;
padding: 10px;
border-bottom: 1px solid #ddd;
}
.modal-body {
padding: 20px;
}
.modal-footer {
background-color: #f5f5f5;
padding: 10px;
border-top: 1px solid #ddd;
}
.btn-secondary {
color: #337ab7;
}

View File

Before

Width:  |  Height:  |  Size: 730 KiB

After

Width:  |  Height:  |  Size: 730 KiB

Some files were not shown because too many files have changed in this diff Show More