This commit is contained in:
Ryan Prather 2024-04-27 22:45:24 -04:00
parent 14c5221ac1
commit 58f83c72c2
27 changed files with 1754 additions and 228 deletions

View File

@ -42,6 +42,7 @@
"symfony/validator": "6.4.*", "symfony/validator": "6.4.*",
"symfony/web-link": "6.4.*", "symfony/web-link": "6.4.*",
"symfony/yaml": "6.4.*", "symfony/yaml": "6.4.*",
"symfonycasts/verify-email-bundle": "^1.17",
"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"
}, },
@ -95,11 +96,12 @@
} }
}, },
"require-dev": { "require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.5",
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.5",
"symfony/browser-kit": "6.4.*", "symfony/browser-kit": "6.4.*",
"symfony/css-selector": "6.4.*", "symfony/css-selector": "6.4.*",
"symfony/debug-bundle": "6.4.*", "symfony/debug-bundle": "6.4.*",
"symfony/maker-bundle": "^1.0", "symfony/maker-bundle": "^1.58",
"symfony/phpunit-bridge": "^7.0", "symfony/phpunit-bridge": "^7.0",
"symfony/stopwatch": "6.4.*", "symfony/stopwatch": "6.4.*",
"symfony/web-profiler-bundle": "6.4.*" "symfony/web-profiler-bundle": "6.4.*"

View File

@ -14,4 +14,5 @@ return [
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle::class => ['all' => true],
]; ];

View File

@ -12,6 +12,19 @@ security:
main: main:
lazy: true lazy: true
provider: users_in_memory provider: users_in_memory
custom_authenticator: App\Security\AppAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
remember_me:
secret: '%kernel.secret%'
lifetime: 604800
path: /
# by default, the feature is enabled by checking a checkbox in the
# login form, uncomment the following line to always enable it.
#always_remember_me: true
# activate different ways to authenticate # activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall # https://symfony.com/doc/current/security.html#the-firewall

View File

@ -6,19 +6,25 @@
parameters: parameters:
services: services:
# default configuration for services in *this* file app.user_provider:
_defaults: class: App\Security\AppAuthenticator
autowire: true # Automatically injects dependencies in your services. arguments: [
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. "%kernel.container_params.user_entity_manager",
"%kernel.container_params.password_hasher",
] # default configuration for services in *this* file
# makes classes in src/ available to be used as services _defaults:
# this creates a service per class whose id is the fully-qualified class name autowire: true # Automatically injects dependencies in your services.
App\: autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed # makes classes in src/ available to be used as services
# please note that last definitions always *replace* previous ones # this creates a service per class whose id is the fully-qualified class name
App\:
resource: "../src/"
exclude:
- "../src/DependencyInjection/"
- "../src/Entity/"
- "../src/Kernel.php"
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

View File

@ -91,6 +91,72 @@ ul.menu-open {
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
} }
#passage {
width: 100px;
}
#newSpeaker {
display: none;
width: 110px;
}
#newSeries {
display: none;
width: 110px;
}
#noteSearch {
display: none;
}
#fields-container {
display: none;
}
#show-hide-btn {
position: absolute;
top: 10px;
right: 20px;
font-size: 16px;
cursor: pointer;
background-color: #4CAF50;
/* green */
color: #fff;
/* white */
border: none;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.6);
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
transition: all 0.3s ease-out;
}
#show-hide-btn:hover {
background-color: #3e8e41;
/* darker green */
}
#show-hide-btn::before {
font-size: 16px;
cursor: pointer;
}
#show-hide-btn::before {
font-size: 16px;
cursor: pointer;
transition: transform 0.5s ease-in-out;
}
#show-hide-btn:hover::before {
transform: translateY(-10px);
}
#fields-container.show {
display: block;
}
.notes { .notes {
width: 55%; width: 55%;
} }
@ -100,6 +166,22 @@ textarea#notes {
height: 100%; height: 100%;
} }
#notePreview {
display: none;
overflow-x: scroll;
}
#note-header-left,
#note-header-right {
display: inline-flex;
flex-direction: row;
width: 49%;
}
#note-header-right {
flex-direction: row-reverse !important;
}
div#refQuery { div#refQuery {
display: none; display: none;
position: absolute; position: absolute;

View File

@ -1 +1 @@
{"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"AAAA,kCAAA;AACA;EACI,SAAA;ACCJ;;ADEA;EACI,aAAA;EACA,mBAAA;EACA,eAAA;EACA,6BAAA;EACA,oBAAA;EACA,yBAAA;EACA,iBAAA;EACA,iCAAA;EACA,cAAA;ACCJ;;ADEA;EACI,YAAA;EACA,WAAA;ACCJ;;ADEA,6BAAA;AAEA;EACI,aAAA;EACA,8BAAA;EACA,mBAAA;EACA,wBAAA;EACA,uBAAA;EACA,eAAA;EACA,kBAAA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,wCAAA;ACAJ;;ADGA;EACI,YAAA;ACAJ;;ADGA;;EAEI,YAAA;ACAJ;;ADIA;EACI,yBAAA;EACA,yBAAA;EACA,aAAA;EACA,YAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;ACDJ;;ADWA;EACI,WAAA;ACRJ;;ADWA;EACI,aAAA;EACA,2BAAA;EACA,mBAAA;EACA,aAAA;EACA,WAAA;EACA,eAAA;EACA,kBAAA;ACRJ;;ADWA;EACI,SAAA;EACA,UAAA;ACRJ;;ADWA;EACI,gBAAA;EACA,qBAAA;ACRJ;;ADWA;EACI,UAAA;ACRJ;;ADWA;EACI,kBAAA;EACA,WAAA;EACA,YAAA;EACA,uBAAA;EACA,kBAAA;EACA,wCAAA;ACRJ;;ADWA;EACI,UAAA;ACRJ;;ADWA;EACI,WAAA;EACA,YAAA;ACRJ;;ADWA;EACI,aAAA;EACA,kBAAA;EACA,YAAA;EACA,oCAAA;ACRJ;;ADWA;EACI,YAAA;EACA,kBAAA;EACA,kBAAA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,yBAAA;EACA,YAAA;EACA,YAAA;EACA,eAAA;ACRJ","file":"style.css"} {"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"AAAA,kCAAA;AACA;EACI,SAAA;ACCJ;;ADEA;EACI,aAAA;EACA,mBAAA;EACA,eAAA;EACA,6BAAA;EACA,oBAAA;EACA,yBAAA;EACA,iBAAA;EACA,iCAAA;EACA,cAAA;ACCJ;;ADEA;EACI,YAAA;EACA,WAAA;ACCJ;;ADEA,6BAAA;AAEA;EACI,aAAA;EACA,8BAAA;EACA,mBAAA;EACA,wBAAA;EACA,uBAAA;EACA,eAAA;EACA,kBAAA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,wCAAA;ACAJ;;ADGA;EACI,YAAA;ACAJ;;ADGA;;EAEI,YAAA;ACAJ;;ADIA;EACI,yBAAA;EACA,yBAAA;EACA,aAAA;EACA,YAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;ACDJ;;ADIA;EACI,WAAA;ACDJ;;ADIA;EACI,aAAA;EACA,2BAAA;EACA,mBAAA;EACA,aAAA;EACA,WAAA;EACA,eAAA;EACA,kBAAA;ACDJ;;ADIA;EACI,SAAA;EACA,UAAA;ACDJ;;ADIA;EACI,gBAAA;EACA,qBAAA;ACDJ;;ADIA;EACI,UAAA;ACDJ;;ADIA;EACI,kBAAA;EACA,WAAA;EACA,YAAA;EACA,uBAAA;EACA,kBAAA;EACA,wCAAA;ACDJ;;ADIA;EACI,YAAA;ACDJ;;ADIA;EACI,aAAA;EACA,YAAA;ACDJ;;ADIA;EACI,aAAA;EACA,YAAA;ACDJ;;ADIA;EACI,aAAA;ACDJ;;ADIA;EACI,aAAA;ACDJ;;ADIA;EACI,kBAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,eAAA;EACA,yBAAA;EACA,UAAA;EACA,WAAA;EACA,UAAA;EACA,YAAA;EACA,kBAAA;EACA,wCAAA;EACA,kBAAA;EACA,kBAAA;EACA,qBAAA;EACA,qBAAA;EACA,eAAA;EACA,eAAA;EAGA,6BAAA;ACDJ;ADGI;EACI,yBAAA;EACA,iBAAA;ACDR;ADII;EACI,eAAA;EACA,eAAA;ACFR;;ADMA;EACI,eAAA;EACA,eAAA;EACA,sCAAA;ACHJ;;ADMA;EACI,4BAAA;ACHJ;;ADMA;EACI,cAAA;ACHJ;;ADMA;EACI,UAAA;ACHJ;;ADMA;EACI,WAAA;EACA,YAAA;ACHJ;;ADMA;EACI,aAAA;EACA,kBAAA;ACHJ;;ADMA;;EAEI,oBAAA;EACA,mBAAA;EACA,UAAA;ACHJ;;ADMA;EACI,sCAAA;ACHJ;;ADMA;EACI,aAAA;EACA,kBAAA;EACA,YAAA;EACA,oCAAA;ACHJ;;ADMA;EACI,YAAA;EACA,kBAAA;EACA,kBAAA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,yBAAA;EACA,YAAA;EACA,YAAA;EACA,eAAA;ACHJ","file":"style.css"}

View File

@ -1 +1 @@
body{margin:0}.container{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-evenly;align-items:stretch;align-content:flex-start;max-width:1020px;margin:0 auto}.top-tab{height:50px;width:100%}.hamburger{display:flex;justify-content:space-between;align-items:center;margin:15px 0 15px 15px;border:#000 solid 1px;cursor:pointer;border-radius:5px;width:20px;height:20px;padding:5px;box-shadow:0 2px 5px rgba(0,0,0,.3)}.fa-bars:before,.fa-navicon:before{padding:3px}ul.menu-open{display:block !important;background-color:#f9c74d;padding:10px;z-index:100;list-style:none;list-style-type:none;position:fixed}.ref-tab{width:60px}.tab button{display:flex;justify-content:flex-start;align-items:center;height:100px;width:100%;cursor:pointer;text-align:center}.ref-tab ul{margin:0;padding:0}.ref-tab ul li{list-style:none;list-style-type:none}.ref{width:35%}.ref>div#ref{overflow-y:scroll;width:100%;height:100%;border:#000 solid 1px;border-radius:3px;box-shadow:0 2px 5px rgba(0,0,0,.3)}.notes{width:55%}textarea#notes{width:100%;height:100%}div#refQuery{display:none;position:absolute;z-index:100;background-color:rgba(0,0,0,.5)}div#refQuery #search{border:none;border-radius:5px;padding:10px 20px;font-size:16px;line-height:1.5;color:#333;background-color:#f4f4f4;width:150px;height:25px;cursor:pointer}/*# sourceMappingURL=style.min.css.map */ body{margin:0}.container{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-evenly;align-items:stretch;align-content:flex-start;max-width:1020px;margin:0 auto}.top-tab{height:50px;width:100%}.hamburger{display:flex;justify-content:space-between;align-items:center;margin:15px 0 15px 15px;border:#000 solid 1px;cursor:pointer;border-radius:5px;width:20px;height:20px;padding:5px;box-shadow:0 2px 5px rgba(0,0,0,.3)}.fa-check{color:green}.fa-bars:before,.fa-navicon:before{padding:3px}ul.menu-open{display:block !important;background-color:#f9c74d;padding:10px;z-index:100;list-style:none;list-style-type:none;position:fixed}.ref-tab{width:60px}.tab button{display:flex;justify-content:flex-start;align-items:center;height:100px;width:100%;cursor:pointer;text-align:center}.ref-tab ul{margin:0;padding:0}.ref-tab ul li{list-style:none;list-style-type:none}.ref{width:35%}.ref>div#ref{overflow-y:scroll;width:100%;height:100%;border:#000 solid 1px;border-radius:3px;box-shadow:0 2px 5px rgba(0,0,0,.3)}#passage{width:100px}#newSpeaker{display:none;width:110px}#newSeries{display:none;width:110px}#noteSearch{display:none}#fields-container{display:none}#show-hide-btn{position:absolute;top:10px;right:20px;font-size:16px;cursor:pointer;background-color:#4caf50;color:#fff;border:none;border-radius:5px;box-shadow:0 2px 5px rgba(0,0,0,.6);padding:15px 32px;text-align:center;text-decoration:none;display:inline-block;font-size:16px;margin:4px 2px;transition:all .3s ease-out}#show-hide-btn:hover{background-color:#3e8e41}#show-hide-btn::before{font-size:16px;cursor:pointer}#show-hide-btn::before{font-size:16px;cursor:pointer;transition:transform .5s ease-in-out}#show-hide-btn:hover::before{transform:translateY(-10px)}#fields-container.show{display:block}.notes{width:55%}textarea#notes{width:100%;height:100%}#notePreview{display:none;overflow-x:scroll}#note-header-left,#note-header-right{display:inline-flex;flex-direction:row;width:49%}#note-header-right{flex-direction:row-reverse !important}div#refQuery{display:none;position:absolute;z-index:100;background-color:rgba(0,0,0,.5)}div#refQuery #search{border:none;border-radius:5px;padding:10px 20px;font-size:16px;line-height:1.5;color:#333;background-color:#f4f4f4;width:150px;height:25px;cursor:pointer}/*# sourceMappingURL=style.min.css.map */

View File

@ -1 +1 @@
{"version":3,"sources":["style.scss"],"names":[],"mappings":"AACA,KACI,QAAA,CAGJ,WACI,YAAA,CACA,kBAAA,CACA,cAAA,CACA,4BAAA,CACA,mBAAA,CACA,wBAAA,CACA,gBAAA,CAEA,aAAA,CAGJ,SACI,WAAA,CACA,UAAA,CAKJ,WACI,YAAA,CACA,6BAAA,CACA,kBAAA,CACA,uBAAA,CACA,qBAAA,CACA,cAAA,CACA,iBAAA,CACA,UAAA,CACA,WAAA,CACA,WAAA,CACA,mCAAA,CAGJ,mCAEI,WAAA,CAIJ,aACI,wBAAA,CACA,wBAAA,CACA,YAAA,CACA,WAAA,CACA,eAAA,CACA,oBAAA,CACA,cAAA,CAUJ,SACI,UAAA,CAGJ,YACI,YAAA,CACA,0BAAA,CACA,kBAAA,CACA,YAAA,CACA,UAAA,CACA,cAAA,CACA,iBAAA,CAGJ,YACI,QAAA,CACA,SAAA,CAGJ,eACI,eAAA,CACA,oBAAA,CAGJ,KACI,SAAA,CAGJ,aACI,iBAAA,CACA,UAAA,CACA,WAAA,CACA,qBAAA,CACA,iBAAA,CACA,mCAAA,CAGJ,OACI,SAAA,CAGJ,eACI,UAAA,CACA,WAAA,CAGJ,aACI,YAAA,CACA,iBAAA,CACA,WAAA,CACA,+BAAA,CAGJ,qBACI,WAAA,CACA,iBAAA,CACA,iBAAA,CACA,cAAA,CACA,eAAA,CACA,UAAA,CACA,wBAAA,CACA,WAAA,CACA,WAAA,CACA,cAAA","file":"style.min.css"} {"version":3,"sources":["style.scss"],"names":[],"mappings":"AACA,KACI,QAAA,CAGJ,WACI,YAAA,CACA,kBAAA,CACA,cAAA,CACA,4BAAA,CACA,mBAAA,CACA,wBAAA,CACA,gBAAA,CAEA,aAAA,CAGJ,SACI,WAAA,CACA,UAAA,CAKJ,WACI,YAAA,CACA,6BAAA,CACA,kBAAA,CACA,uBAAA,CACA,qBAAA,CACA,cAAA,CACA,iBAAA,CACA,UAAA,CACA,WAAA,CACA,WAAA,CACA,mCAAA,CAGJ,UACI,WAAA,CAGJ,mCAEI,WAAA,CAIJ,aACI,wBAAA,CACA,wBAAA,CACA,YAAA,CACA,WAAA,CACA,eAAA,CACA,oBAAA,CACA,cAAA,CAGJ,SACI,UAAA,CAGJ,YACI,YAAA,CACA,0BAAA,CACA,kBAAA,CACA,YAAA,CACA,UAAA,CACA,cAAA,CACA,iBAAA,CAGJ,YACI,QAAA,CACA,SAAA,CAGJ,eACI,eAAA,CACA,oBAAA,CAGJ,KACI,SAAA,CAGJ,aACI,iBAAA,CACA,UAAA,CACA,WAAA,CACA,qBAAA,CACA,iBAAA,CACA,mCAAA,CAGJ,SACI,WAAA,CAGJ,YACI,YAAA,CACA,WAAA,CAGJ,WACI,YAAA,CACA,WAAA,CAGJ,YACI,YAAA,CAGJ,kBACI,YAAA,CAGJ,eACI,iBAAA,CACA,QAAA,CACA,UAAA,CACA,cAAA,CACA,cAAA,CACA,wBAAA,CAEA,UAAA,CAEA,WAAA,CACA,iBAAA,CACA,mCAAA,CACA,iBAAA,CACA,iBAAA,CACA,oBAAA,CACA,oBAAA,CACA,cAAA,CACA,cAAA,CAGA,2BAAA,CAEA,qBACI,wBAAA,CAIJ,uBACI,cAAA,CACA,cAAA,CAIR,uBACI,cAAA,CACA,cAAA,CACA,oCAAA,CAGJ,6BACI,2BAAA,CAGJ,uBACI,aAAA,CAGJ,OACI,SAAA,CAGJ,eACI,UAAA,CACA,WAAA,CAGJ,aACI,YAAA,CACA,iBAAA,CAGJ,qCAEI,mBAAA,CACA,kBAAA,CACA,SAAA,CAGJ,mBACI,qCAAA,CAGJ,aACI,YAAA,CACA,iBAAA,CACA,WAAA,CACA,+BAAA,CAGJ,qBACI,WAAA,CACA,iBAAA,CACA,iBAAA,CACA,cAAA,CACA,eAAA,CACA,UAAA,CACA,wBAAA,CACA,WAAA,CACA,WAAA,CACA,cAAA","file":"style.min.css"}

View File

@ -56,13 +56,6 @@ ul.menu-open {
position: fixed; position: fixed;
} }
.ref-tab,
.ref,
.notes {
// float: left;
// padding: 20px;
}
.ref-tab { .ref-tab {
width: 60px; width: 60px;
} }
@ -100,6 +93,76 @@ ul.menu-open {
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
} }
#passage {
width: 100px;
}
#newSpeaker {
display: none;
width: 110px;
}
#newSeries {
display: none;
width: 110px;
}
#noteSearch {
display: none;
}
#fields-container {
display: none;
}
#show-hide-btn {
position: absolute;
top: 10px;
right: 20px;
font-size: 16px;
cursor: pointer;
background-color: #4CAF50;
/* green */
color: #fff;
/* white */
border: none;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.6);
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
-webkit-transition: all 0.3s ease-out;
-moz-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
&:hover {
background-color: #3e8e41;
/* darker green */
}
&::before {
font-size: 16px;
cursor: pointer;
}
}
#show-hide-btn::before {
font-size: 16px;
cursor: pointer;
transition: transform 0.5s ease-in-out;
}
#show-hide-btn:hover::before {
transform: translateY(-10px);
}
#fields-container.show {
display: block;
}
.notes { .notes {
width: 55% width: 55%
} }
@ -109,6 +172,22 @@ textarea#notes {
height: 100%; height: 100%;
} }
#notePreview {
display: none;
overflow-x: scroll;
}
#note-header-left,
#note-header-right {
display: inline-flex;
flex-direction: row;
width: 49%;
}
#note-header-right {
flex-direction: row-reverse !important;
}
div#refQuery { div#refQuery {
display: none; display: none;
position: absolute; position: absolute;

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

95
public/js/data.js Normal file
View File

@ -0,0 +1,95 @@
var BOOKS = {
"bible": [
"Genesis",
"Exodus",
"Leviticus",
"Numbers",
"Deuteronomy",
"Joshua",
"Judges",
"Ruth",
"1 Samuel",
"2 Samuel",
"1 Kings",
"2 Kings",
"1 Chronicles",
"2 Chronicles",
"Ezra",
"Nehemiah",
"Esther",
"Job",
"Psalms",
"Proverbs",
"Ecclesiastes",
"Song of Solomon",
"Isaiah",
"Jeremiah",
"Lamentations",
"Ezekiel",
"Daniel",
"Hosea",
"Joel",
"Amos",
"Obadiah",
"Jonah",
"Micah",
"Nahum",
"Habakkuk",
"Zephaniah",
"Haggai",
"Zechariah",
"Malachi",
"Matthew",
"Mark",
"Luke",
"John",
"Acts",
"Romans",
"1 Corinthians",
"2 Corinthians",
"Galatians",
"Ephesians",
"Philippians",
"Colossians",
"1 Thessalonians",
"2 Thessalonians",
"1 Timothy",
"2 Timothy",
"Titus",
"Philemon",
"Hebrews",
"James",
"1 Peter",
"2 Peter",
"1 John",
"2 John",
"3 John",
"Jude",
"Revelation"
],
"creed": {
"apc": "Apostle's Creed",
"nc": "Nicene Creed",
"atc": "Athanasian Creed",
"dc": "Definition of Chalcedon",
"fc": "French Confession"
},
"bc": [
1, 37
],
"hc": [
1, 52
],
"cd": [
"1", "2", "3", "5", "Conclusion"
],
"wcf": [
1, 33
],
"wsc": [
1, 107
],
"wlc": [
1, 196
]
};

View File

@ -1,7 +1,9 @@
// Get the link element // Get the link element
const link = document.querySelector('.hamburger'); const link = document.querySelector('.hamburger');
var converter = null; var converter = null;
var markdownit = null;
var references = {}; var references = {};
let saved = false;
// Add an event listener to the link // Add an event listener to the link
if (link) { if (link) {
@ -13,7 +15,13 @@ if (link) {
function setHeight() { function setHeight() {
converter = new showdown.Converter(); converter = new showdown.Converter();
converter.setFlavor('github'); //converter.setFlavor();
markdownit = new markdownit({
html: true,
linkify: true,
breaks: true
});
body = document.querySelector('body'); body = document.querySelector('body');
body.style.height = window.innerHeight + 'px'; body.style.height = window.innerHeight + 'px';
@ -28,9 +36,16 @@ function setHeight() {
ref.style.height = (window.innerHeight - 125) + 'px'; ref.style.height = (window.innerHeight - 125) + 'px';
notes = document.querySelector('.notes'); notes = document.querySelector('.notes');
notes.style.height = (window.innerHeight - 150) + 'px'; notes.style.height = (window.innerHeight - 130) + 'px';
setTimeout(saveNote, 5000); notePreview = document.querySelector('#notePreview');
notePreview.style.height = (window.innerHeight - 150) + 'px';
date = document.querySelector('#noteDate');
dt = new Date();
date.value = dt.getFullYear() + '-' + ((dt.getMonth() < 9) ? '0' + (dt.getMonth() + 1) : (dt.getMonth() + 1)) + '-' + dt.getDate();
setTimeout(saveNote, 10000);
} }
function newNote() { function newNote() {
@ -38,68 +53,92 @@ function newNote() {
notes.textContent = ''; notes.textContent = '';
} }
function openNote() { /**
* Save a note by sending it to the server for storage.
} *
* @param {Event} event - The event object triggering the save action.
* @return {void} No explicit return value.
*/
function saveNote(event) { function saveNote(event) {
event.preventDefault(); if (event) {
event.preventDefault();
}
if(!validateNote()) { if (!validateNote()) {
setTimeout(saveNote, 5000); setTimeout(saveNote, 5000);
return; return;
} }
var note = {
id: document.querySelector("#noteId").value,
date: document.querySelector('#noteDate').value,
title: document.querySelector('#noteTitle').value,
speaker: document.querySelector('#speaker').value,
series: document.querySelector('#series').value,
passage: document.querySelector('#passage').value,
note: document.querySelector('#notes').value
};
fetch('/index.php/save-note', { fetch('/index.php/save-note', {
method: 'POST', method: 'POST',
headers: { headers: {
"Content-Type": 'application/x-www-form-urlencoded' "Content-Type": 'application/json'
}, },
body: { body: JSON.stringify(note)
title: document.querySelector('#noteTitle').value, })
speaker: document.querySelector('#speaker').value, .then(response => response.json())
series: document.querySelector('#series').value, .then(data => {
note: document.querySelector('#notes').textContent if (data.msg == 'saved' && !saved) {
} showSave();
.then(response => response.text) saved = true;
.then(results => { }
results = JSON.parse(results);
alert(results);
showSave();
}) })
}); .catch(error => console.log(error));
setTimeout(saveNote, 5000); setTimeout(saveNote, 10000);
} }
function validateNote() { function validateNote() {
const note = document.querySelector('#notes'); const note = document.querySelector('#notes');
const date = document.querySelector('#noteDate');
const speaker = document.querySelector('#speaker'); const speaker = document.querySelector('#speaker');
const series = document.querySelector('#series'); const series = document.querySelector('#series');
const title = document.querySelector('#noteTitle'); const title = document.querySelector('#noteTitle');
const id = document.querySelector('#noteId');
const psg = document.querySelector('#passage');
if(!title.value.length) {return false;} if (!parseInt(id.value)) { return false; }
if (!title.value.length) { return false; }
if(!speaker.value) {return false;} if (!date.value) { return false; }
if (!parseInt(speaker.value)) { return false; }
if(!series.value) {return false;} if (!parseInt(series.value)) { return false; }
if (!psg.value) { return false; }
if(!note.textContent.length) {return false;} if (!note.value.length) { return false; }
return true; return true;
} }
function showSave() {// Get the element that will display the checkmark /**
* Displays a checkmark animation on the screen.
*
* @param {none} - This function does not take any parameters.
* @return {none} - This function does not return any value.
*/
function showSave() {
if (saved) { return; }
var checkmark = document.getElementById("save-check"); var checkmark = document.getElementById("save-check");
// Schedule the animation to run every 1 second (which is equivalent to a 1-second delay between each iteration) // Schedule the animation to run every 1 second (which is equivalent to a 1-second delay between each iteration)
setInterval(function() { var si = setInterval(function () {
// Increment the opacity of the checkmark by 0.01 each time // Increment the opacity of the checkmark by 0.01 each time
checkmark.style.opacity += 0.1; op = parseFloat(checkmark.style.opacity);
checkmark.style.opacity = op + 0.1;
// If the opacity is greater than or equal to 1, reset it back to 0 and stop the animation // If 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 >= 1) {
checkmark.style.opacity = 0; checkmark.style.opacity = 0;
clearInterval(setInterval); clearInterval(si);
} saved = false;
}
}, 100); }, 100);
} }
@ -111,11 +150,98 @@ function discardNote() {
document.querySelector('#speaker').value = 0; document.querySelector('#speaker').value = 0;
document.querySelector('#series').value = 0; document.querySelector('#series').value = 0;
document.querySelector('#template').value = 0; document.querySelector('#template').value = 0;
document.querySelector('#passage').value = '';
document.querySelector('#notes').value = ''; document.querySelector('#notes').value = '';
fetch('/index.php/discard-note', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'id': document.querySelector('#noteId').value
})
.then(response => response.json())
.then(data => {
if (data.msg == 'deleted') {
alert('Note deleted.');
}
})
});
openRef(); openRef();
} }
function newSpeaker() {
if (document.querySelector('#speaker').value == 'new') {
document.querySelector('#newSpeaker').style.display = 'block';
document.querySelector('#speaker').style.display = 'none';
}
}
function saveSpeaker(event) {
if (event.keyCode == 13) {
fetch('/index.php/save-speaker', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'speakerName': document.querySelector('#newSpeaker').value
})
})
.then(response => response.json())
.then(results => {
var newSpeaker = document.createElement('option');
newSpeaker.text = document.querySelector('#newSpeaker').value;
newSpeaker.value = results.id;
document.querySelector('#speaker').add(newSpeaker);
alert(results.msg);
document.querySelector('#newSpeaker').style.display = 'none';
document.querySelector('#speaker').style.display = 'block';
document.querySelector('#newSpeaker').value = '';
document.querySelector('#speaker').value = results.id;
});
}
}
function newSeries() {
if (document.querySelector('#series').value == 'new') {
document.querySelector('#newSeries').style.display = 'block';
document.querySelector('#series').style.display = 'none';
}
}
function saveSeries(event) {
if (event.keyCode == 13) {
fetch('/index.php/save-series', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'seriesName': document.querySelector('#newSeries').value
})
})
.then(response => response.json())
.then(results => {
var newSeries = document.createElement('option');
newSeries.text = document.querySelector('#newSeries').value;
newSeries.value = results.id;
document.querySelector('#series').add(newSeries);
alert(results.msg);
document.querySelector('#newSeries').style.display = 'none';
document.querySelector('#series').style.display = 'block';
document.querySelector('#newSeries').value = '';
document.querySelector('#series').value = results.id;
});
}
}
function openRef() { function openRef() {
refQuery = document.querySelector('#refQuery'); refQuery = document.querySelector('#refQuery');
if (refQuery.style.display === 'block') { if (refQuery.style.display === 'block') {
@ -130,13 +256,17 @@ function openRef() {
} }
function queryRef() { function queryRef() {
const input = document.querySelector('#refQuery #search'); var input = document.querySelector('#refQuery #search');
var type = document.querySelector('#referenceType');
var book = document.querySelector('#referenceBook');
fetch('/index.php/retrieve-reference', { fetch('/index.php/retrieve-reference', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'plain/text' 'Content-Type': 'plain/text'
}, },
body: JSON.stringify({ body: JSON.stringify({
'type': type.value,
'book': book.value,
'reference': input.value 'reference': input.value
}) })
}) })
@ -167,6 +297,8 @@ function queryRef() {
references[results.title] = results.text; references[results.title] = results.text;
input.value = ''; input.value = '';
document.querySelector('#referenceType').value = 0;
document.querySelector('#referenceBook').value = 0;
openRef(); openRef();
link.click(); link.click();
}); });
@ -215,21 +347,177 @@ function saveTemplate() {
}); });
} }
function retrieveSeries() { function toggleFields() {
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"
fieldsContainer.classList.add('show');
fieldsContainer.style.display = 'block';
showHideBtn.textContent = 'Hide';
}
setHeight();
} }
function saveSeries() { function retrieveBooks() {
const selectedType = document.querySelector('#referenceType').value;
if (!selectedType) { return; }
var bookList = document.querySelector('#referenceBook');
bookList.style.display = "block";
bookList.innerHTML = '';
if (selectedType == 'bible') {
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];
bookList.appendChild(newBook);
}
} else if (selectedType == 'creed') {
var none = document.createElement('option');
none.value = '';
none.text = '-- Select --';
bookList.appendChild(none);
for (var x in BOOKS.creed) {
var newBook = document.createElement('option');
newBook.value = x;
newBook.text = BOOKS.creed[x];
bookList.appendChild(newBook);
}
} else if (selectedType == 'cd') {
var none = document.createElement("option");
none.value = '';
none.text = '-- Select --';
bookList.appendChild(none);
for (var x in BOOKS.cd) {
var newBook = document.createElement("option");
newBook.text = BOOKS.cd[x];
bookList.appendChild(newBook);
}
} else {
var min = BOOKS[selectedType][0];
var max = BOOKS[selectedType][1];
var none = document.createElement("option");
none.value = '';
none.text = '-- Select --';
bookList.appendChild(none);
for (var x = min; x <= max; x++) {
var newBook = document.createElement("option");
newBook.value = x;
newBook.text = x;
bookList.appendChild(newBook);
}
}
} }
function retrieveSpeaker() { 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;
});
} }
function saveSpeaker() { 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 = '';
});
} }
function referenceEditor() { function previewNote() {
var noteText = document.querySelector('#notes');
var notePreview = document.querySelector('#notePreview');
var previewButton = document.querySelector('#previewBtn');
//notePreview.innerHTML = converter.makeHtml(noteText.value);
notePreview.innerHTML = markdownit.render(noteText.value);
if (previewButton.value == 'Preview') {
previewButton.value = 'Hide Preview';
noteText.style.display = 'none';
notePreview.style.display = 'block';
} else {
previewButton.value = 'Preview';
noteText.style.display = 'block';
notePreview.style.display = 'none';
}
findLinks();
}
function findLinks() {
var links = document.querySelector('#notePreview').querySelectorAll('a');
alert(links.length);
}
function showSearchNote(event) {
event.preventDefault();
var searchNote = document.querySelector('#noteSearch');
if (searchNote.style.display == 'none') {
searchNote.style.display = 'block';
} else {
searchNote.style.display = 'none';
}
}
function searchNote() {
const search = document.querySelector('#noteSearchQuery');
fetch('/index.php/search-note', {
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"search": search.value
})
})
.then(response => response.json())
.then(result => {
document.querySelector('#notes').value = result.text;
document.querySelector('#passage').value = result.passage;
document.querySelector('#series').value = result.series.id;
document.querySelector('#speaker').value = result.speaker.id;
document.querySelector('#noteTitle').value = result.title;
document.querySelector('#noteDate').value = result.date;
document.querySelector('#noteId').value = result.id;
document.querySelector('#noteSearch').style.display = 'none';
link.click();
});
} }

View File

@ -0,0 +1,153 @@
<?php
namespace App\Command;
use App\Entity\Reference;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'IngestReference',
aliases: ['app:ingest-ref'],
description: 'Script to use to crawl a directory and ingest references',
)]
class IngestReferenceCommand extends Command
{
/**
* Files found in the directory
*
* @var array
*/
private array $files = [];
/**
* IO
*
* @var SymfonyStyle
*/
private SymfonyStyle $io;
/**
* EntityManager
*
* @var EntityManagerInterface
*/
private EntityManagerInterface $emi;
/**
* IngestReferenceCommand constructor.
*/
public function __construct(
EntityManagerInterface $emi,
private string $dir = '',
private string $name = '',
private string $type = '',
private string $label = ''
) {
parent::__construct();
$this->emi = $emi;
}
/**
* {@inheritdoc}
*/
protected function configure(): void
{
$this
->addArgument('directory', InputArgument::REQUIRED, 'Directory to crawl')
->addArgument('name', InputArgument::REQUIRED, 'Name of the reference')
->addArgument('type', InputArgument::REQUIRED, 'Type of the reference')
->addArgument('label', InputArgument::REQUIRED, 'Label of the reference')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->io = new SymfonyStyle($input, $output);
$this->dir = $input->getArgument('directory');
$this->name = $input->getArgument('name');
$this->type = $input->getArgument('type');
$this->label = $input->getArgument('label');
if (!$this->dir || !file_exists($this->dir) ||!is_dir($this->dir)) {
$this->io->error('Directory not specific or does not exist');
return Command::FAILURE;
}
if (!$this->name) {
$this->io->error('No name specified');
return Command::FAILURE;
}
if (!$this->type) {
$this->io->error('No type specified');
return Command::FAILURE;
}
if (!$this->label) {
$this->io->error('No label specified');
return Command::FAILURE;
}
$this->io->note("Crawling {$this->dir} and ingesting {$this->name} as {$this->type}:{$this->label}");
foreach ($this->getFiles() as $file) {
$ref = $this->processFile($file);
$this->emi->persist($ref);
}
$this->emi->flush();
return Command::SUCCESS;
}
public function getFiles(): array
{
$this->files = glob($this->dir . '/*.md');
if(!$this->files || count($this->files) === 0) {
$this->io->warning("No files found in this directory\n{$this->dir}");
$this->files = [];
}
return $this->files;
}
public function processFile(string $file): Reference|bool
{
$this->io->info("Processing {$file}");
$md = trim(file_get_contents($file));
$ref = new Reference();
if (!$md) {
$this->io->warning("File is empty\n{$file}");
return false;
}
$match = [];
$label = str_replace("{\$ndx}", "", $this->label);
if(preg_match("/([\d]+)/", $file, $match)) {
$ndx = ltrim($match[1], "0");
$label = str_replace("{\$ndx}", $ndx, $this->label);
$ref->setNdx($ndx);
} elseif (preg_match("/\(([^\)]+)\)/", $file, $match)) {
$label = $match[1];
}
$ref->setContent($md);
$ref->setName($this->name);
$ref->setType($this->type);
$ref->setLabel($label);
$this->io->success("Ingested {$this->name} as {$this->type}:{$label}");
return $ref;
}
}

View File

@ -2,11 +2,15 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\Bible;
use DateTime;
use DateTimeZone;
use App\Entity\Series; use App\Entity\Series;
use App\Entity\Speaker; use App\Entity\Speaker;
use App\Entity\Template; use App\Entity\Template;
use App\Entity\Notes; use App\Entity\Notes;
use App\Entity\Reference;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -85,23 +89,16 @@ class AjaxController extends AbstractController
public function saveSpeaker(Request $req, EntityManagerInterface $emi): Response public function saveSpeaker(Request $req, EntityManagerInterface $emi): Response
{ {
$ret = new Response(); $ret = new Response();
$req = json_decode($req->getContent()); $data = json_decode($req->getContent());
$speaker_id = $req->speaker_id;
$speaker_name = $req->speaker_name;
if ($speaker_id) { $speaker = new Speaker();
$speaker = $emi->getRepository(Speaker::class)->find($speaker_id); $speaker->setName($data->speakerName);
$speaker->setName($speaker_name); $emi->persist($speaker);
$emi->persist($speaker); $emi->flush();
$emi->flush(); $ret->setContent(json_encode([
$ret->setContent('updated'); 'id' => $speaker->getId(),
} else { 'msg' => $speaker->getName().' added'
$speaker = new Speaker(); ]));
$speaker->setName($speaker_name);
$emi->persist($speaker);
$emi->flush();
$ret->setContent('added');
}
return $ret; return $ret;
} }
@ -129,89 +126,186 @@ class AjaxController extends AbstractController
public function saveSeries(Request $req, EntityManagerInterface $emi): Response public function saveSeries(Request $req, EntityManagerInterface $emi): Response
{ {
$ret = new Response(); $ret = new Response();
$req = json_decode($req->getContent()); $data = json_decode($req->getContent());
$series_id = $req->series_id;
$series_name = $req->series_name;
if ($series_id) { $series = new Series();
$series = $emi->getRepository(Series::class)->find($series_id); $series->setName($data->seriesName);
$series->setName($series_name); $emi->persist($series);
$emi->persist($series); $emi->flush();
$emi->flush();
$ret->setContent('updated'); $ret->setContent(json_encode([
} else { 'id' => $series->getId(),
$series = new Series(); 'msg' => $series->getName().' added'
$series->setName($series_name); ]));
$emi->persist($series);
$emi->flush();
$ret->setContent('added');
}
return $ret; return $ret;
} }
#[Route('/autocomplete-reference', name: 'app_autocomplete_reference')]
public function autocompleteReference(Request $req): Response
{
$res = new Response();
return $res;
}
#[Route('/retrieve-reference', name: 'app_retrive_reference')] #[Route('/retrieve-reference', name: 'app_retrive_reference')]
public function retrieveReference(Request $req): Response public function retrieveReference(Request $req, EntityManagerInterface $emi): Response
{ {
$type = null;
$search = null;
$passage = null;
$res = new Response(); $res = new Response();
$ref = json_decode($req->getContent())->reference; $data = json_decode($req->getContent());
if (count(explode(':', $ref)) > 2) { $search = $data->reference;
list($type, $search, $passage) = explode(':', $ref);
} else { $ref = new Reference();
list($type, $search) = explode(':', $ref); $ref->setType($data->type);
if((int) $data->book) {
$ref->setNdx($data->book);
} }
$ret = match(strtolower($type)) { ReferenceController::$emi = $emi;
'bible' => ReferenceController::retrieveBible("{$search}:{$passage}"),
'hc' => ReferenceController::retrieveHC($search), $ret = match(strtolower($data->type)) {
'bc' => ReferenceController::retrieveBC($search), 'bible' => ReferenceController::retrieveBible("{$data->book} {$search}"),
'dc' => ReferenceController::retrieveCD($search), 'hc' => ReferenceController::retrieveHC($ref),
'wcf' => ReferenceController::retrieveWCF($search), 'bc' => ReferenceController::retrieveBC($ref),
'wsc' => ReferenceController::retrieveWSC($search), 'cd' => ReferenceController::retrieveCD($ref),
'wlc' => ReferenceController::retrieveWLC($search), 'wcf' => ReferenceController::retrieveWCF($ref),
'creed' => ReferenceController::retrieveCreed($search) 'wsc' => ReferenceController::retrieveWSC($ref),
'wlc' => ReferenceController::retrieveWLC($ref),
'creed' => ReferenceController::retrieveCreed($data->book)
}; };
$res->setContent(json_encode(['text' => $ret, 'title' => "{$search}"])); $res->setContent(json_encode(['text' => $ret->getContent(), 'title' => "{$ret->getLabel()}"]));
return $res; return $res;
} }
#[Route('/open-note', name: 'app_open_note')] #[Route('/get-reference', name: 'app_get_reference')]
public function getReference(Request $req, EntityManagerInterface $emi): Response
{
$res = new Response();
$data = json_decode($req->getContent());
$ret = match ($data->type) {
'creed' => '/Creeds/',
'bc' => '/Belgic/',
'hc' => '/Heidelberg/',
'cd' => '/Dort/',
'wcf' => '/Westminster/Confessions/',
'wsc' => '/Westminster/Shorter Catechism/',
'wlc' => '/Westminster/Larger Catechism/'
};
$fc = file_get_contents(dirname(dirname(__DIR__))."/references{$ret}{$data->file}");
$res->setContent(json_encode(['text' => $fc]));
return $res;
}
#[Route('/save-reference', name: 'app_save_reference')]
public function saveReference(Request $req, EntityManagerInterface $emi): Response
{
$res = new Response();
$data = json_decode($req->getContent());
$path = match($data->type) {
'creed' => 'Creeds',
'bc' => 'Belgic',
'hc' => 'Heidelberg',
'cd' => 'Dort',
'wcf' => 'Westminster/Confessions',
'wsc' => 'Westminster/Shorter Catechism',
'wlc' => 'Westminster/Larger Catechism'
};
$ret = file_put_contents(dirname(dirname(__DIR__))."/references/{$path}/{$data->file}", $data->text);
if($ret !== false) {
$res->setContent(json_encode(['msg' => 'File Saved']));
} else {
$res->setContent(json_encode(['msg' => 'Failed to save file']));
}
return $res;
}
#[Route('/search-note', name: 'app_open_note')]
public function openNote(Request $req, EntityManagerInterface $emi): Response public function openNote(Request $req, EntityManagerInterface $emi): Response
{ {
$res = new Response(); $res = new Response();
$data = json_decode($req->getContent());
return $res; $note = $emi->getRepository(Notes::class)->findNote($data->search);
}
#[Route('/save-note', name: 'app_save_note')]
public function saveNote(Request $req, EntityManagerInterface $emi): Response
{
$res = new Response();
$note = new Notes();
$note->setTitle($req->get('title'));
$series = $emi->getRepository(Series::class)->find($req->get('series'));
$speaker = $emi->getRepository(Speaker::class)->find($req->get('speaker'));
$note->setSeries($series);
$note->setSpeaker($speaker);
$note->setText($req->get('note'));
$res->setContent(json_encode($note)); $res->setContent(json_encode($note));
return $res; return $res;
} }
#[Route('/save-note', name: 'app_save_note', methods: ['POST'])]
public function saveNote(Request $req, EntityManagerInterface $emi): Response
{
$data = json_decode($req->getContent());
$note = $emi->getRepository(Notes::class)->find($data->id);
if (!$note) {
$note = new Notes();
$note->setId($data->id);
}
$note->setTitle($data->title);
$note->setDate(new DateTime($data->date));
$series = $emi->getRepository(Series::class)->find($data->series);
$speaker = $emi->getRepository(Speaker::class)->find($data->speaker);
$note->setSeries($series);
$note->setSpeaker($speaker);
$note->setText($data->note);
$note->setPassage($data->passage);
$emi->persist($note);
$emi->flush();
$res = new Response();
$res->setContent(json_encode([
'msg' => 'saved',
'id' => $note->getId()
]));
return $res;
}
#[Route('/discard-note', name: 'app_discard_note', methods: ['POST'])]
public function discardNote(Request $req, EntityManagerInterface $emi): Response
{
$data = json_decode($req->getContent());
$note = $emi->getRepository(Notes::class)->find($data->id);
$emi->remove($note);
$emi->flush();
$res = new Response();
$res->setContent(json_encode([
'msg' => 'deleted'
]));
return $res;
}
#[Route('/get-passage/{passage}', name: 'app_get_passage')]
public function getPassage($passage, EntityManagerInterface $emi): Response
{
$passage = str_replace('+', ' ', $passage);
$book = Bible::findBook($passage);
$chapter = Bible::findChapter($passage);
$bible = new Bible();
$bible->setBook($book);
$bible->setChapter($chapter);
$verse = Bible::findVerse($passage);
$ret = $emi->getRepository(Bible::class)->findRange($bible, $verse);
if (is_array($ret)) {
$text = null;
foreach($ret as $b) {
$text .= "{$b->getVerse()}. {$b->getContent()}".PHP_EOL;
}
$bible->setContent($text);
} elseif (is_a($ret, Bible::class)) {
$bible->setContent($ret->getContent());
}
$res = new Response();
$res->setContent($bible->getContent());
return $res;
}
} }

View File

@ -5,6 +5,7 @@ namespace App\Controller;
use App\Entity\Speaker; use App\Entity\Speaker;
use App\Entity\Template; use App\Entity\Template;
use App\Entity\Series; use App\Entity\Series;
use App\Entity\Notes;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -19,12 +20,14 @@ class DefaultController extends AbstractController
$speakers = $emi->getRepository(Speaker::class)->findAll(); $speakers = $emi->getRepository(Speaker::class)->findAll();
$series = $emi->getRepository(Series::class)->findAll(); $series = $emi->getRepository(Series::class)->findAll();
$templates = $emi->getRepository(Template::class)->findAll(); $templates = $emi->getRepository(Template::class)->findAll();
$note = $emi->getRepository(Notes::class)->findLastNote();
return $this->render('default/index.html.twig', [ return $this->render('default/index.html.twig', [
'controller_name' => 'DefaultController', 'controller_name' => 'DefaultController',
'speakers' => $speakers, 'speakers' => $speakers,
'series' => $series, 'series' => $series,
'templates' => $templates 'templates' => $templates,
'noteId' => ($note->getId() + 1)
]); ]);
} }
@ -40,6 +43,32 @@ class DefaultController extends AbstractController
#[Route('/reference-editor', name: 'app_reference_editor')] #[Route('/reference-editor', name: 'app_reference_editor')]
public function referenceEditor(EntityManagerInterface $emi): Response public function referenceEditor(EntityManagerInterface $emi): Response
{ {
return $this->render('default/reference-editor.html.twig', []); $ref_dir = dirname(dirname(__DIR__)).'/references/';
$creeds = $this->stripPath(glob($ref_dir.'Creeds/*.md'));
$belgic = $this->stripPath(glob($ref_dir.'Belgic/*.md'));
$dort = $this->stripPath(glob($ref_dir.'Dort/*.md'));
$heidelberg = $this->stripPath(glob($ref_dir.'Heidelberg/*.md'));
$wcf = $this->stripPath(glob($ref_dir.'Westminster/Confessions/*.md'));
$wsc = $this->stripPath(glob($ref_dir.'Westminster/Shorter Catechism/*.md'));
$wlc = $this->stripPath(glob($ref_dir.'Westminster/Longer Catechism/*.md'));
return $this->render('default/reference-editor.html.twig', [
'creeds' => $creeds,
'belgic' => $belgic,
'heidelberg' => $heidelberg,
'dort' => $dort,
'wcf' => $wcf,
'wsc' => $wsc,
'wlc' => $wlc,
]);
}
private function stripPath(array $files): array
{
$ret = [];
foreach($files as $f) {
$ret[] = basename($f);
}
return $ret;
} }
} }

View File

@ -2,6 +2,9 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\Bible;
use App\Entity\Reference;
use Doctrine\ORM\EntityManagerInterface;
use \Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use \Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/** /**
@ -9,26 +12,63 @@ use \Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
*/ */
class ReferenceController extends AbstractController class ReferenceController extends AbstractController
{ {
/**
* @var EntityManagerInterface
*/
public static EntityManagerInterface $emi;
/** /**
* Method to retrieve a Bible reference * Method to retrieve a Bible reference
* *
* @param string $ref * @param string $ref
* *
* @return string * @return Reference
*/ */
public static function retrieveBible($ref): string public static function retrieveBible($ref): Reference
{ {
if(count(explode(' ', $ref)) > 2) { $book = Bible::findBook($ref);
list($index, $book, $chapter, $passage) = preg_split("/:| /", $ref); $chapter = Bible::findChapter($ref);
$book = "{$index}{$book}"; $passage = Bible::findVerse($ref);
} else { $bible = new Bible();
list($book, $chapter, $passage) = preg_split("/:| /", $ref); $bible->setBook($book);
$bible->setChapter($chapter);
$label = null;
if (is_array($passage)) {
$passage_start = $passage[0];
$passage_end = $passage[1];
$bible = self::$emi->getRepository(Bible::class)->findRange($bible, [$passage_start, $passage_end]);
$passage = "{$passage_start}-{$passage_end}";
$label = "{$bible[0]->getLabel()} {$bible[0]->getChapter()}:{$passage}";
} elseif (is_int($passage)) {
$bible = self::$emi->getRepository(Bible::class)->findBy(['book' => $book, 'chapter' => $chapter, 'verse' => $passage]);
$label = "{$bible[0]->getLabel()} {$bible[0]->getChapter()}:{$passage}";
} elseif ($passage === false) {
$bible = self::$emi->getRepository(Bible::class)->findBy(['book' => $book, 'chapter' => $chapter]);
$label = "{$bible[0]->getLabel()} {$bible[0]->getChapter()}";
$passage = null;
} }
$file = glob(dirname(dirname(__DIR__))."/references/Bible/* - {$book}/{$book}{$chapter}.md"); if(is_array($bible)) {
$res = implode("\n", file(current($file))); $text = [];
foreach($bible as $b) {
$text[] = "{$b->getVerse()}. {$b->getContent()}";
}
} else {
$text[] = "{$bible->getVerse()}. {$bible->getContent()}";
}
return $res; $reference = "{$book} {$chapter}".($passage === null ? '' : ":{$passage}");
$ref = new Reference();
$ref->setType('bible');
$ref->setName($reference);
$ref->setLabel($label);
$ref->setContent("# {$reference}\n\n".implode("\n", $text));
return $ref;
} }
/** /**
@ -36,9 +76,9 @@ class ReferenceController extends AbstractController
* *
* @param string * @param string
* *
* @return string * @return Reference
*/ */
public static function retrieveCreed($ref) public static function retrieveCreed($ref): Reference
{ {
$file = match($ref) { $file = match($ref) {
'apc' => "Apostle's Creed.md", 'apc' => "Apostle's Creed.md",
@ -48,58 +88,86 @@ class ReferenceController extends AbstractController
'nc' => 'Nicene Creed.md' 'nc' => 'Nicene Creed.md'
}; };
return implode("\n", file(dirname(dirname(__DIR__))."/references/Creeds/{$file}")); $r = self::$emi->getRepository(Reference::class)->findBy(['label' => $ref]);
$ref = $r[0];
return $ref;
} }
/** /**
* Method to retrieve a Heidelberg Catechism reference * Method to retrieve a Heidelberg Catechism reference
* *
* @param string $ref * @param Reference $ref
* *
* @return string * @return Reference
*/ */
public static function retrieveHC($ref) public static function retrieveHC($ref): Reference
{ {
$ref = strtoupper($ref); $r = self::$emi->getRepository(Reference::class)->findBy(['type' => $ref->getType(), 'ndx' => $ref->getNdx()]);
return implode("\n", file(dirname(dirname(__DIR__))."/references/Heidelberg/{$ref}.md")); return $r[0];
} }
/** /**
* Method to retrieve a Belgian Catechism reference * Method to retrieve a Belgian Catechism reference
* *
* @param string $ref * @param Reference $ref
* *
* @return string * @return Reference
*/ */
public static function retrieveBC($ref) public static function retrieveBC($ref): Reference
{ {
$ref = ucfirst($ref); $r = self::$emi->getRepository(Reference::class)->findBy(['type' => $ref->getType(), 'ndx' => $ref->getNdx()]);
return implode("\n", file(dirname(dirname(__DIR__))."/references/Belgic/{$ref}.md")); return $r[0];
} }
public static function retrieveCD($ref) /**
* Method to retrieve the Canon of Dort reference
*
* @param Reference $ref
*
* @return Reference
*/
public static function retrieveCD($ref): Reference
{ {
$ref = strtoupper($ref); $r = self::$emi->getRepository(Reference::class)->findBy(['type' => $ref->getType(), 'ndx' => $ref->getNdx()]);
return implode("\n", file(dirname(dirname(__DIR__))."/references/Dort/{$ref}.md")); return $r[0];
} }
public static function retrieveWSC($ref) /**
* Method to retrieve a WSC reference
*
* @param Reference $ref
*
* @return Reference
*/
public static function retrieveWSC($ref): Reference
{ {
$art = str_pad($ref, 3, '0', STR_PAD_LEFT); $r = self::$emi->getRepository(Reference::class)->findBy(['type' => $ref->getType(), 'ndx' => $ref->getNdx()]);
$files = glob(dirname(dirname(__DIR__))."/references/Westminster/Shorter Catechism/WSC{$art}.md"); return $r[0];
return implode("\n", file(current($files)));
} }
public static function retrieveWLC($ref) /**
* Method to retrieve a WLC reference
*
* @param Reference $ref
*
* @return Reference
*/
public static function retrieveWLC($ref): Reference
{ {
$art = str_pad($ref, 3, '0', STR_PAD_LEFT); $r = self::$emi->getRepository(Reference::class)->findBy(['type' => $ref->getType(), 'ndx' => $ref->getNdx()]);
$files = glob(dirname(dirname(__DIR__))."/references/Westminster/Larger Catechism/WLC{$art}.md"); return $r[0];
return implode("\n", file(current($files)));
} }
public static function retrieveWCF($ref) /**
* Method to retrieve a WCF reference
*
* @param Reference $ref
*
* @return Reference
*/
public static function retrieveWCF($ref): Reference
{ {
$files = glob(dirname(dirname(__DIR__))."/references/Westminster/Confessions/Chapter {$ref}.md"); $r = self::$emi->getRepository(Reference::class)->findBy(['type' => $ref->getType(), 'ndx' => $ref->getNdx()]);
return implode("\n", file(current($files))); return $r[0];
} }
} }

159
src/Entity/Bible.php Normal file
View File

@ -0,0 +1,159 @@
<?php
namespace App\Entity;
use App\Repository\BibleRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: BibleRepository::class)]
class Bible
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $book = null;
#[ORM\Column]
private ?int $chapter = null;
#[ORM\Column]
private ?int $verse = null;
#[ORM\Column(length: 4096)]
private ?string $content = null;
#[ORM\Column]
private ?int $book_index = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $label = null;
public function getId(): ?int
{
return $this->id;
}
public function getBook(): ?string
{
return $this->book;
}
public function setBook(string $book): static
{
$this->book = $book;
return $this;
}
public function getChapter(): ?int
{
return $this->chapter;
}
public function setChapter(int $chapter): static
{
$this->chapter = $chapter;
return $this;
}
public function getVerse(): ?int
{
return $this->verse;
}
public function setVerse(int $verse): static
{
$this->verse = $verse;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): static
{
$this->content = $content;
return $this;
}
public function getBookIndex(): ?int
{
return $this->book_index;
}
public function setBookIndex(int $book_index): static
{
$this->book_index = $book_index;
return $this;
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabel(?string $label): static
{
$this->label = $label;
return $this;
}
public static function findBook(string $reference): string
{
$book = '';
$tmp = explode(' ', $reference);
$book = "{$tmp[0]}";
if(count($tmp) > 2) {
$book = "{$tmp[0]}{$tmp[1]}";
}
return $book;
}
public static function findChapter(string $reference): string
{
$chapter = '';
$tmp = explode(' ', $reference);
if(count($tmp) > 2) {
$passage = $tmp[2];
} else {
$passage = $tmp[1];
}
$tmp = explode(':', $passage);
$chapter = $tmp[0];
return $chapter;
}
public static function findVerse(string $reference): int|array|bool
{
$verses = null;
$semicolon = strpos($reference, ':');
if ($semicolon === false) {
return false;
}
$tmp = substr($reference, $semicolon + 1);
if(strpos($tmp, '-') !== false) {
$verses = explode('-', $tmp);
} else {
$verses = (int) $tmp;
}
return $verses;
}
}

View File

@ -10,8 +10,8 @@ use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: NotesRepository::class)] #[ORM\Entity(repositoryClass: NotesRepository::class)]
class Notes implements JsonSerializable class Notes implements JsonSerializable
{ {
#[ORM\Id] #[ORM\Id()]
#[ORM\Column] #[ORM\Column(type: Types::INTEGER)]
private ?int $id = null; private ?int $id = null;
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
@ -30,11 +30,25 @@ class Notes implements JsonSerializable
#[ORM\Column(type: Types::DATE_MUTABLE)] #[ORM\Column(type: Types::DATE_MUTABLE)]
private ?\DateTimeInterface $date = null; private ?\DateTimeInterface $date = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $passage = null;
/**
* Retrieves the ID of the object.
*
* @return int|null The ID of the object, or null if it is not set.
*/
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;
} }
/**
* Sets the ID of the object.
*
* @param int $id The ID to set.
* @return static
*/
public function setId(int $id): static public function setId(int $id): static
{ {
$this->id = $id; $this->id = $id;
@ -42,11 +56,22 @@ class Notes implements JsonSerializable
return $this; return $this;
} }
/**
* Retrieves the title of the object.
*
* @return string|null The title of the object, or null if it is not set.
*/
public function getTitle(): ?string public function getTitle(): ?string
{ {
return $this->title; return $this->title;
} }
/**
* Sets the title of the object.
*
* @param string $title The title to set.
* @return static
*/
public function setTitle(string $title): static public function setTitle(string $title): static
{ {
$this->title = $title; $this->title = $title;
@ -54,6 +79,18 @@ class Notes implements JsonSerializable
return $this; return $this;
} }
public function getDate(): ?\DateTimeInterface
{
return $this->date;
}
public function setDate(\DateTimeInterface $date): static
{
$this->date = $date;
return $this;
}
public function getSpeaker(): ?Speaker public function getSpeaker(): ?Speaker
{ {
return $this->speaker; return $this->speaker;
@ -90,6 +127,18 @@ class Notes implements JsonSerializable
return $this; return $this;
} }
public function getPassage(): ?string
{
return $this->passage;
}
public function setPassage(?string $passage): static
{
$this->passage = $passage;
return $this;
}
public function jsonSerialize(): array public function jsonSerialize(): array
{ {
return [ return [
@ -98,18 +147,8 @@ class Notes implements JsonSerializable
'speaker' => $this->speaker, 'speaker' => $this->speaker,
'series' => $this->series, 'series' => $this->series,
'text' => $this->text, 'text' => $this->text,
'passage' => $this->passage,
'date' => $this->date->format('Y-m-d'),
]; ];
} }
public function getDate(): ?\DateTimeInterface
{
return $this->date;
}
public function setDate(\DateTimeInterface $date): static
{
$this->date = $date;
return $this;
}
} }

180
src/Entity/Reference.php Normal file
View File

@ -0,0 +1,180 @@
<?php
namespace App\Entity;
use JsonSerializable;
use App\Repository\ReferenceRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ReferenceRepository::class)]
class Reference implements JsonSerializable
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 64)]
private ?string $type = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column(length: 10)]
private ?string $label = null;
#[ORM\Column(nullable: true)]
private ?int $ndx = null;
#[ORM\Column(length: 65535)]
private ?string $content = null;
/**
* Getter method for id.
*
* @return int
*/
public function getId(): ?int
{
return $this->id;
}
/**
* Getter method for type.
*
* @return string
*/
public function getType(): ?string
{
return $this->type;
}
/**
* Setter method for type.
*
* @param string $type
*
* @return Reference
*/
public function setType(string $type): static
{
$this->type = $type;
return $this;
}
/**
* Getter method for name.
*
* @return string
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Setter method for name.
*
* @param string $name
*
* @return Reference
*/
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
/**
* Getter method for label.
*
* @return string
*/
public function getLabel(): ?string
{
return $this->label;
}
/**
* Setter method for label.
*
* @param string $label
*
* @return Reference
*/
public function setLabel(string $label): static
{
$this->label = $label;
return $this;
}
/**
* Getter method for ndx.
*
* @return int
*/
public function getNdx(): ?int
{
return $this->ndx;
}
/**
* Setter method for ndx.
*
* @param int $ndx
*
* @return Reference
*/
public function setNdx(int $ndx): static
{
$this->ndx = $ndx;
return $this;
}
/**
* Getter method for content.
*
* @return string
*/
public function getContent(): ?string
{
return $this->content;
}
/**
* Setter method for content.
*
* @param string $content
*
* @return Reference
*/
public function setContent(string $content): static
{
$this->content = $content;
return $this;
}
/**
* @inheritDoc
*
* @codeCoverageIgnore
*/
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'type' => $this->type,
'name' => $this->name,
'label' => $this->label,
'ndx' => $this->ndx,
'text' => $this->content,
];
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace App\Repository;
use App\Entity\Bible;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityManager;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Bible>
*
* @method Bible|null find($id, $lockMode = null, $lockVersion = null)
* @method Bible|null findOneBy(array $criteria, array $orderBy = null)
* @method Bible[] findAll()
* @method Bible[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class BibleRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Bible::class);
}
public function findRange(Bible $bible, array $passage): array
{
return $this->createQueryBuilder('b')
->andWhere('b.book = :book')
->andWhere('b.chapter = :chapter')
->andWhere('b.verse BETWEEN :verseStart AND :verseEnd')
->setParameter('book', $bible->getBook())
->setParameter('chapter', $bible->getChapter())
->setParameter('verseStart', $passage[0])
->setParameter('verseEnd', $passage[1])
->getQuery()
->getResult();
}
// /**
// * @return Bible[] Returns an array of Bible objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('b')
// ->andWhere('b.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('b.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Bible
// {
// return $this->createQueryBuilder('b')
// ->andWhere('b.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@ -19,6 +19,43 @@ class NotesRepository extends ServiceEntityRepository
public function __construct(ManagerRegistry $registry) public function __construct(ManagerRegistry $registry)
{ {
parent::__construct($registry, Notes::class); parent::__construct($registry, Notes::class);
}
/**
* @return Notes
*/
public function findLastNote(): Notes
{
$qb = $this->createQueryBuilder('n')
->orderBy('n.id', 'DESC')
->setMaxResults(1);
$query = $qb->getQuery();
$note = $query->getOneOrNullResult();
if (!$note) {
$note = new Notes();
$note->setId(0);
}
return $note;
}
public function findNote($data): Notes
{
$qb = $this->createQueryBuilder('notes');
$qb->select('n')
->from('\App\Entity\Notes', 'n')
->where("n.title LIKE ?1")
->orWhere('n.passage LIKE ?1')
->orWhere('n.text LIKE ?1')
->setParameter(1, "%{$data}%")
->setMaxResults(1);
$query = $qb->getQuery();
$res = $query->getSingleResult();
return $res;
} }
// /** // /**

View File

@ -0,0 +1,48 @@
<?php
namespace App\Repository;
use App\Entity\Reference;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Reference>
*
* @method Reference|null find($id, $lockMode = null, $lockVersion = null)
* @method Reference|null findOneBy(array $criteria, array $orderBy = null)
* @method Reference[] findAll()
* @method Reference[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ReferenceRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Reference::class);
}
// /**
// * @return Reference[] Returns an array of Reference objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('r.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Reference
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@ -21,6 +21,11 @@ class SeriesRepository extends ServiceEntityRepository
parent::__construct($registry, Series::class); parent::__construct($registry, Series::class);
} }
public function findAll(): array
{
return $this->findBy([], ['name' => 'ASC']);
}
// /** // /**
// * @return Series[] Returns an array of Series objects // * @return Series[] Returns an array of Series objects
// */ // */

View File

@ -21,6 +21,11 @@ class SpeakerRepository extends ServiceEntityRepository
parent::__construct($registry, Speaker::class); parent::__construct($registry, Speaker::class);
} }
public function findAll(): array
{
return $this->findBy([], ['name' => 'ASC']);
}
// /** // /**
// * @return Speaker[] Returns an array of Speaker objects // * @return Speaker[] Returns an array of Speaker objects
// */ // */

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title> <title>{% block title %}Welcome!{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>"> <!--<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">-->
{% block stylesheets %} {% block stylesheets %}
{% endblock %} {% endblock %}
</head> </head>

View File

@ -7,8 +7,10 @@
{% endblock %} {% endblock %}
{% block javascripts %} {% block javascripts %}
<script src='/js/data.js'></script>
<script src='/js/script.js'></script> <script src='/js/script.js'></script>
<script src="https://kit.fontawesome.com/f15a79324f.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/f15a79324f.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/13.0.2/markdown-it.min.js" integrity="sha512-ohlWmsCxOu0bph1om5eDL0jm/83eH09fvqLDhiEdiqfDeJbEvz4FSbeY0gLJSVJwQAp0laRhTXbUQG+ZUuifUQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js'></script>
{% endblock %} {% endblock %}
@ -18,11 +20,13 @@
<i class="fa fa-bars hamburger" aria-hidden="true"></i> <i class="fa fa-bars hamburger" aria-hidden="true"></i>
<ul class="hamburger-list" style="display: none"> <ul class="hamburger-list" style="display: none">
<li><a href="#">New Note</a></li> <li><a href="#">New Note</a></li>
<li><a href="#">Open Note</a></li> <li><a href="#" onclick='showSearchNote(event)'>Open Note</a></li>
<li><a href="#" onclick="saveNote();link.click();">Save Note</a></li> <li><a href="#" onclick="saveNote();link.click();">Save Note</a></li>
<li><a href='#' onclick='discardNote()'>Discard Note</a></li> <li><a href='#' onclick='discardNote()'>Discard Note</a></li>
<li><hr/></li>
<li><a href='/index.php/reference-editor'>Reference Editor</a></li> <li><a href='/index.php/reference-editor'>Reference Editor</a></li>
<li><a href='#' onclick="openRef()">Open Reference</a></li> <li><a href='#' onclick="openRef()">Open Reference</a></li>
<li><hr/></li>
<li><a href='/index.php/template-editor'>Template Editor</a></li> <li><a href='/index.php/template-editor'>Template Editor</a></li>
</ul>&nbsp;&nbsp; </ul>&nbsp;&nbsp;
<i id='save-check' class='fa fa-check' style='opacity:0;'></i> <i id='save-check' class='fa fa-check' style='opacity:0;'></i>
@ -37,34 +41,68 @@
<div id="ref"></div> <div id="ref"></div>
</section> </section>
<section class="notes"> <section class="notes">
<h2>Notes</h2> <div id='note-header-left'>
<input type="text" id="noteTitle" placeholder="Title..."> <h2>Notes</h2>
<select id="speaker"> <button id='show-hide-btn' onclick='toggleFields()'>Show</button>
<option value=0>-- Speaker --</option> </div>
{% for s in speakers %} <div id='note-header-right'>
<option value='{{ s.id }}'>{{ s.name }}</option> <select id='template' onchange="retrieveTemplate('template','notes')">
{% endfor %} <option value=0>-- Template --</option>
</select> {% for t in templates %}
<select id="series"> <option value="{{ t.id }}">{{ t.name }}</option>
<option value=0>-- Series --</option> {% endfor %}
{% for s in series %} </select>
<option value='{{ s.id }}'>{{ s.name }}</option> <input type="button" id="previewBtn" value="Preview" onclick='previewNote()'><br />
{% endfor %} </div>
</select>
<select id='template' onchange="retrieveTemplate('template','notes')"> <div id='fields-container'>
<option value=0>-- Template --</option> <input type="hidden" id="noteId" value="{{ noteId }}" />
{% for t in templates %} <input type="text" id="noteTitle" placeholder="Title..." />
<option value="{{ t.id }}">{{ t.name }}</option> <input type='date' id='noteDate' />
{% endfor %} <input type='text' id='newSpeaker' placeholder='Name...' onkeyup='saveSpeaker(event)' />
</select> <select id="speaker" onchange='newSpeaker()'>
<input type="button" id="previewBtn" value="Preview"><br /> <option value=0>-- Speaker --</option>
<option value='new'>New Speaker</option>
{% for s in speakers %}
<option value='{{ s.id }}'>{{ s.name }}</option>
{% endfor %}
</select>
<input type='text' id='newSeries' placeholder='Series...' onkeyup='saveSeries(event)' />
<select id="series" onchange='newSeries()'>
<option value=0>-- Series --</option>
<option value='new'>New Series</option>
{% for s in series %}
<option value='{{ s.id }}'>{{ s.name }}</option>
{% endfor %}
</select>
<input type='text' id='passage' placeholder='Passage...' />
</div>
<textarea id="notes" wrap="hard"></textarea> <textarea id="notes" wrap="hard"></textarea>
<div id='notePreview'></div>
</section> </section>
</main> </main>
<div id="refQuery"> <div id="refQuery">
<select id='referenceType' onchange='retrieveBooks()'>
<option>-- Select --</option>
<option value='bible'>Bible</option>
<option value='creed'>Creed</option>
<option value='bc'>Belgic</option>
<option value='hc'>Heidelberg</option>
<option value='cd'>Canons of Dort</option>
<option value='wcf'>Westminster CF</option>
<option value='wsc'>Shorter Catechism</option>
<option value='wlc'>Larger Catechism</option>
</select>
<select id='referenceBook' style='display:none;'>
</select><br/>
<input type="text" id="search" placeholder="Search"><br /> <input type="text" id="search" placeholder="Search"><br />
<button id="searchBtn" onclick="queryRef()">Search</button> <button id="searchBtn" onclick="queryRef()">Search</button>
</div> </div>
<div id='noteSearch'>
<input type='text' id='noteSearchQuery' placeholder='Search...' /><br/>
<button id='noteSearchButton' onclick='searchNote()'>Search</button>
</div>
{% endblock %} {% endblock %}

View File

@ -5,6 +5,48 @@
</head> </head>
<body> <body>
<select id='references' style='height:50px;' size='20' onchange='retrieveReference(this)'>
<option value=''>-- Select Reference --</option>
<optgroup id='creeds' label='Creeds'>
{% for c in creeds %}
<option type='creed'>{{ c }}</option>
{% endfor %}
</optgroup>
<optgroup id='belgic' label='Belgic Confession'>
{% for c in belgic %}
<option type='bc'>{{ c }}</option>
{% endfor %}
</optgroup>
<optgroup id='heidelberg' label='Heidelberg Catechism'>
{% for c in heidelberg %}
<option type='hc'>{{ c }}</option>
{% endfor %}
</optgroup>
<optgroup id='dort' label='Canons of Dort'>
{% for c in dort %}
<option type='cd'>{{ c }}</option>
{% endfor %}
</optgroup>
<optgroup id='wcf' label='Westminster Confession of Faith'>
{% for c in wcf %}
<option type='wcf'>{{ c }}</option>
{% endfor %}
</optgroup>
<optgroup id='wsc' label='Westminster Short Catechism'>
{% for c in wsc %}
<option type='wsc'>{{ c }}</option>
{% endfor %}
</optgroup>
<optgroup id='wlc' label='Westminster Larger Catechism'>
{% for c in wlc %}
<option type='wlc'>{{ c }}</option>
{% endfor %}
</optgroup>
</select>
<button id='save' name='save' onclick='saveReference()'>Save</button>
<a href='/index.php/'>Back</a><br />
<textarea id='reference' name='reference' rows=50 cols=50></textarea>
<script src='/js/script.js'></script> <script src='/js/script.js'></script>
</body> </body>
</html> </html>