Compare commits
No commits in common. "a279ff0cf374bee1b170b0ee3f72bd822956c1ae" and "d7fdf2df5044590a91d55e2c8425d40a5a6c0db8" have entirely different histories.
a279ff0cf3
...
d7fdf2df50
|
@ -11,7 +11,6 @@ node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
data_files/
|
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
21
README.md
21
README.md
|
@ -1,21 +0,0 @@
|
||||||
# Verse Checker
|
|
||||||
|
|
||||||
This is a simple web-application for checking verses.
|
|
||||||
|
|
||||||
You can try the application at: https://scripturememory.richardwong.io/
|
|
||||||
|
|
||||||
Features include:
|
|
||||||
- Languages supported: English, Korean
|
|
||||||
- Shuffling with random selection from selected packs
|
|
||||||
- Hide References
|
|
||||||
- Showing mistakes as deltas with respect to the answer
|
|
||||||
|
|
||||||
## To Use
|
|
||||||
|
|
||||||
This application uses `pnpm` as the node.js package manager.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/RichFree/VerseChecker.git
|
|
||||||
cd VerseChecker
|
|
||||||
pnpm run dev
|
|
||||||
```
|
|
|
@ -37,11 +37,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "dep-1",
|
"value": "dep-1",
|
||||||
"label": "DEP 1: Assurance of Salvation"
|
"label": "DEP 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "dep-2",
|
"value": "dep-2",
|
||||||
"label": "DEP 2: Quiet Time",
|
"label": "DEP 2",
|
||||||
"children": [
|
"children": [
|
||||||
{ "value": "dep-2-part-a", "label": "Why do we have Quiet Time?" },
|
{ "value": "dep-2-part-a", "label": "Why do we have Quiet Time?" },
|
||||||
{ "value": "dep-2-part-b", "label": "What is Quiet Time?" },
|
{ "value": "dep-2-part-b", "label": "What is Quiet Time?" },
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "dep-3",
|
"value": "dep-3",
|
||||||
"label": "DEP 3: The Word",
|
"label": "DEP 3",
|
||||||
"children": [
|
"children": [
|
||||||
{ "value": "dep-3-part-a", "label": "Authority of the Word" },
|
{ "value": "dep-3-part-a", "label": "Authority of the Word" },
|
||||||
{ "value": "dep-3-part-b", "label": "value of the Word" },
|
{ "value": "dep-3-part-b", "label": "value of the Word" },
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "dep-4",
|
"value": "dep-4",
|
||||||
"label": "DEP 4: Prayer",
|
"label": "DEP 4",
|
||||||
"children": [
|
"children": [
|
||||||
{ "value": "dep-4-part-a", "label": "Command of Prayer" },
|
{ "value": "dep-4-part-a", "label": "Command of Prayer" },
|
||||||
{ "value": "dep-4-part-b", "label": "Promises and Blessings of Prayer" },
|
{ "value": "dep-4-part-b", "label": "Promises and Blessings of Prayer" },
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "dep-5",
|
"value": "dep-5",
|
||||||
"label": "DEP 5: Fellowship",
|
"label": "DEP 5",
|
||||||
"children": [
|
"children": [
|
||||||
{ "value": "dep-5-part-a", "label": "Foundation of Christian Fellowship" },
|
{ "value": "dep-5-part-a", "label": "Foundation of Christian Fellowship" },
|
||||||
{ "value": "dep-5-part-b", "label": "Importance of fellowship" },
|
{ "value": "dep-5-part-b", "label": "Importance of fellowship" },
|
||||||
|
@ -82,28 +82,12 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value":"dep-6",
|
"value":"dep-6",
|
||||||
"label":"DEP 6: Witnessing",
|
"label":"DEP 6",
|
||||||
"children": [
|
"children": [
|
||||||
{ "value": "dep-6-part-a", "label": "Who is responsible for witnessing?" },
|
{ "value": "dep-6-part-a", "label": "Who is respondible for witnessing?" },
|
||||||
{ "value": "dep-6-part-b", "label": "Why should we witness?" },
|
{ "value": "dep-6-part-b", "label": "Why should we witness?" },
|
||||||
{ "value": "dep-6-part-c", "label": "How do we witness?" },
|
{ "value": "dep-6-part-c", "label": "How do we witness?" }
|
||||||
{ "value": "dep-6-part-d", "label": "Examples of witness" },
|
|
||||||
{ "value": "dep-6-part-e", "label": "Bridge Illustration" }
|
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"value":"dep-7",
|
|
||||||
"label":"DEP 7: The Lordship of Christ",
|
|
||||||
"children": [
|
|
||||||
{ "value": "dep-7-part-a", "label": "We must believe in the Lordship of Christ" },
|
|
||||||
{ "value": "dep-7-part-b", "label": "Blessings when surrendering to the Lordship" },
|
|
||||||
{ "value": "dep-7-part-c", "label": "What to surrender in the Lordship" },
|
|
||||||
{ "value": "dep-7-part-d", "label": "Paragons of surrendering to the Lordship" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dep-8",
|
|
||||||
"label": "DEP 8: World Vision"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -14,100 +14,15 @@
|
||||||
"verses": "Verses:"
|
"verses": "Verses:"
|
||||||
},
|
},
|
||||||
"verse_validator": {
|
"verse_validator": {
|
||||||
"input_reference": "Input Verse Reference: ",
|
"input_reference": "Input Verse Reference: (KR)",
|
||||||
"input_chapter_title": "Input Chapter Title: ",
|
"input_chapter_title": "Input Chapter Title: (KR)",
|
||||||
"input_title": "Input Title: ",
|
"input_title": "Input Title: (KR)",
|
||||||
"input_verse": "Input Verse: "
|
"input_verse": "Input Verse: (KR)"
|
||||||
},
|
},
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"value": "loa",
|
"value": "loa",
|
||||||
"label": "5확신"
|
"label": "Lessons on Assurance"
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "8구절",
|
|
||||||
"label": "8구절"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "tms60",
|
|
||||||
"label": "60구절",
|
|
||||||
"children": [
|
|
||||||
{ "value": "tms-60-pack-a", "label": "새로운 삶" },
|
|
||||||
{ "value": "tms-60-pack-b", "label": "그리스도를 전파함" },
|
|
||||||
{ "value": "tms-60-pack-c", "label": "하나님을 의뢰함" },
|
|
||||||
{ "value": "tms-60-pack-d", "label": "그리스도 제자의 자격" },
|
|
||||||
{ "value": "tms-60-pack-e", "label": "그리스도를 닮아감" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dep-1",
|
|
||||||
"label": "DEP 1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dep-2",
|
|
||||||
"label": "DEP 2",
|
|
||||||
"children": [
|
|
||||||
{ "value": "dep-2-part-a", "label": "왜 QT를 가져야 하는가?" },
|
|
||||||
{ "value": "dep-2-part-b", "label": "QT란 무엇인가?" },
|
|
||||||
{ "value": "dep-2-part-c", "label": "QT의 본" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dep-3",
|
|
||||||
"label": "DEP 3",
|
|
||||||
"children": [
|
|
||||||
{ "value": "dep-3-part-a", "label": "말씀의 권위" },
|
|
||||||
{ "value": "dep-3-part-b", "label": "말씀의 가치" },
|
|
||||||
{ "value": "dep-3-part-c", "label": "말씀에 대한 태도" },
|
|
||||||
{ "value": "dep-3-part-d", "label": "말씀의 섭취 방법 말씀의 손 예화" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dep-4",
|
|
||||||
"label": "DEP 4",
|
|
||||||
"children": [
|
|
||||||
{ "value": "dep-4-part-a", "label": "기도의 명령" },
|
|
||||||
{ "value": "dep-4-part-b", "label": "기도의 약속과 축복" },
|
|
||||||
{ "value": "dep-4-part-c", "label": "응답받는 기도의 조건" },
|
|
||||||
{ "value": "dep-4-part-d", "label": "기도의 본" },
|
|
||||||
{ "value": "dep-4-part-e", "label": "기도의 손 예화" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dep-5",
|
|
||||||
"label": "DEP 5",
|
|
||||||
"children": [
|
|
||||||
{ "value": "dep-5-part-a", "label": "교제의 기초" },
|
|
||||||
{ "value": "dep-5-part-b", "label": "교제의 중요성" },
|
|
||||||
{ "value": "dep-5-part-c", "label": "교제의 요소" },
|
|
||||||
{ "value": "dep-5-part-d", "label": "교제의 태도" },
|
|
||||||
{ "value": "dep-5-part-e", "label": "교제에서의 문제 해결" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value":"dep-6",
|
|
||||||
"label":"DEP 6",
|
|
||||||
"children": [
|
|
||||||
{ "value": "dep-6-part-a", "label": "전도는 누구의 책임인가?" },
|
|
||||||
{ "value": "dep-6-part-b", "label": "왜 전도를 해야 하나?" },
|
|
||||||
{ "value": "dep-6-part-c", "label": "어떻게 전도하나?" },
|
|
||||||
{ "value": "dep-6-part-d", "label": "전도의 모범" },
|
|
||||||
{ "value": "dep-6-part-e", "label": "Bridge Illustration" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value":"dep-7",
|
|
||||||
"label":"DEP 7",
|
|
||||||
"children": [
|
|
||||||
{ "value": "dep-7-part-a", "label": "주재권을 인정해야 함" },
|
|
||||||
{ "value": "dep-7-part-b", "label": "주재권을 인정할 때의 축복" },
|
|
||||||
{ "value": "dep-7-part-c", "label": "주재권을 인정할 영역" },
|
|
||||||
{ "value": "dep-7-part-d", "label": "주재권을 인정한 삶의 모범" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dep-8",
|
|
||||||
"label": "DEP 8"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -31,14 +31,13 @@ const GenerateTestList = ({ VerseData, packs, testCount, toShuffle, toHideRefere
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArrayTester = ({ array, toHideReference, translate}) => {
|
const ArrayTester = ({ array, toHideReference, translate}) => {
|
||||||
const list = array.map((element, index) => (
|
const list = array.map((element) => (
|
||||||
// key needs to be unique; chose 3 elements that will separate all elements
|
// key needs to be unique; chose 3 elements that will separate all elements
|
||||||
<VerseValidator
|
<VerseValidator
|
||||||
key={element.pack + element.title + element.reference}
|
key={element.pack + element.title + element.reference}
|
||||||
element={element}
|
element={element}
|
||||||
toHideReference={toHideReference}
|
toHideReference={toHideReference}
|
||||||
t={translate} // this passes the t i18 object to the function
|
t={translate} // this passes the t i18 object to the function
|
||||||
index={index + 1}
|
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
return list
|
return list
|
||||||
|
@ -105,10 +104,6 @@ function Page() {
|
||||||
// function hook to change language
|
// function hook to change language
|
||||||
// updates both i18n language and also the VerseData state variable
|
// updates both i18n language and also the VerseData state variable
|
||||||
const changeLanguage = (lng) => {
|
const changeLanguage = (lng) => {
|
||||||
// reset selection list
|
|
||||||
setChecked([]);
|
|
||||||
setExpanded([]);
|
|
||||||
|
|
||||||
// i18n.changeLanguage is async, so we should wait until its done to avoid
|
// i18n.changeLanguage is async, so we should wait until its done to avoid
|
||||||
// race conditions
|
// race conditions
|
||||||
// console.log("change language");
|
// console.log("change language");
|
||||||
|
@ -118,9 +113,25 @@ function Page() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // create checklist array for pack selection
|
||||||
|
// const packList = Object.keys(VerseData);
|
||||||
|
// // return a list of packObj's
|
||||||
|
// // 1. packObj.pack for the pack name
|
||||||
|
// // 2. packObj.include for whether to include the pack
|
||||||
|
// const packObjList = packList.map((element) => {
|
||||||
|
// // create object for each element in VerseData key list
|
||||||
|
// const packObj = new Object();
|
||||||
|
// packObj.pack = element;
|
||||||
|
// packObj.include = false;
|
||||||
|
// return packObj
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// const [packs, setPacks] = useState(packObjList)
|
||||||
|
|
||||||
// initialize state variable testCount
|
// initialize state variable testCount
|
||||||
// purpose: to set number of verses to test
|
// purpose: to set number of verses to test
|
||||||
const [testCount, setTestCount] = useState(30)
|
const [testCount, setTestCount] = useState(20)
|
||||||
const testCountChange = (e) => {
|
const testCountChange = (e) => {
|
||||||
const value = e.target.value
|
const value = e.target.value
|
||||||
setTestCount(value)
|
setTestCount(value)
|
||||||
|
@ -241,12 +252,7 @@ function Page() {
|
||||||
// loading component for suspense fallback
|
// loading component for suspense fallback
|
||||||
const Loader = () => (
|
const Loader = () => (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<img
|
<img src={logo} className="App-logo" alt="logo" />
|
||||||
src={logo}
|
|
||||||
className="App-logo"
|
|
||||||
alt="logo"
|
|
||||||
style={{ width: '20vw', height: 'auto' }}
|
|
||||||
/>
|
|
||||||
<div>loading...</div>
|
<div>loading...</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
.reference-box {
|
.reference-box {
|
||||||
max-width:400px;
|
max-width:400px;
|
||||||
height: 5vh;
|
height: 5vh;
|
||||||
field-sizing: content; /* not yet implemented in firefox and safari */
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 99%;
|
width: 99%;
|
||||||
border: 1px solid grey;
|
border: 1px solid grey;
|
||||||
|
@ -23,7 +22,6 @@
|
||||||
.chapter-title-box {
|
.chapter-title-box {
|
||||||
max-width:400px;
|
max-width:400px;
|
||||||
height: 5vh;
|
height: 5vh;
|
||||||
field-sizing: content; /* not yet implemented in firefox and safari */
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 99%;
|
width: 99%;
|
||||||
border: 1px solid grey;
|
border: 1px solid grey;
|
||||||
|
@ -34,7 +32,6 @@
|
||||||
.title-box {
|
.title-box {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
height: 5vh;
|
height: 5vh;
|
||||||
field-sizing: content; /* not yet implemented in firefox and safari */
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 99%;
|
width: 99%;
|
||||||
border: 1px solid grey;
|
border: 1px solid grey;
|
||||||
|
@ -49,8 +46,7 @@
|
||||||
|
|
||||||
.verse-box {
|
.verse-box {
|
||||||
max-width:400px;
|
max-width:400px;
|
||||||
min-height: 12vh;
|
height: 10vh;
|
||||||
field-sizing: content; /* not yet implemented in firefox and safari */
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 99%;
|
width: 99%;
|
||||||
border: 1px solid grey;
|
border: 1px solid grey;
|
||||||
|
@ -78,42 +74,10 @@
|
||||||
.correct {
|
.correct {
|
||||||
background-color: #e6ffe6; /* Change the background color as needed */
|
background-color: #e6ffe6; /* Change the background color as needed */
|
||||||
}
|
}
|
||||||
|
|
||||||
.partial {
|
|
||||||
background-color: #dafcff; /* Change the background color as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
.incorrect {
|
|
||||||
background-color: transparent; /* Change the background color as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--background-color-removed: #f1ebb3;
|
|
||||||
--background-color-added: #ffd7b6;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.correct {
|
.correct {
|
||||||
background-color: #2e5e2e; /* Change the background color as needed */
|
background-color: #495749; /* Change the background color as needed */
|
||||||
}
|
|
||||||
|
|
||||||
.partial {
|
|
||||||
background-color: #004d5c; /* Change the background color as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
.incorrect {
|
|
||||||
background-color: transparent; /* Change the background color as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--background-color-removed: #877e2a;
|
|
||||||
--background-color-added: #7b4418;
|
|
||||||
}
|
|
||||||
|
|
||||||
.VerseValidator {
|
|
||||||
border-top: 1px solid white; /* override for dark mode */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,76 +1,25 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import "./VerseValidator.css";
|
import "./VerseValidator.css";
|
||||||
import { StringDiff } from "react-string-diff";
|
import { StringDiff } from "react-string-diff";
|
||||||
import { containsKorean, jamoSubstringMatch } from './utils';
|
|
||||||
|
|
||||||
const STATE = {
|
|
||||||
INCORRECT: 0,
|
|
||||||
PARTIAL: 1,
|
|
||||||
CORRECT: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// function to render and handle logic of each of the cells
|
// function to render and handle logic of each of the cells
|
||||||
const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse } , toHideReference, t, index}) => { // useful use of destructuring here
|
const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse } , toHideReference, t}) => { // useful use of destructuring here
|
||||||
|
|
||||||
const [inputReference, setReference] = useState('')
|
const [inputReference, setReference] = useState('')
|
||||||
const [referenceBool, setReferenceBool] = useState(STATE.INCORRECT)
|
const [referenceBool, setReferenceBool] = useState(false)
|
||||||
const [inputChapterTitle, setChapterTitle] = useState('')
|
const [inputChapterTitle, setChapterTitle] = useState('')
|
||||||
const [chapterTitleBool, setChapterTitleBool] = useState(STATE.INCORRECT)
|
const [chapterTitleBool, setChapterTitleBool] = useState(false)
|
||||||
const [inputTitle, setTitle] = useState('')
|
const [inputTitle, setTitle] = useState('')
|
||||||
const [titleBool, setTitleBool] = useState(STATE.INCORRECT)
|
const [titleBool, setTitleBool] = useState(false)
|
||||||
const [inputVerse, setVerse] = useState('')
|
const [inputVerse, setVerse] = useState('')
|
||||||
const [verseBool, setVerseBool] = useState(STATE.INCORRECT)
|
const [verseBool, setVerseBool] = useState(false)
|
||||||
const[hintBool, setHintBool] = useState(false)
|
const[hintBool, setHintBool] = useState(false)
|
||||||
const[diffBool, setDiffBool] = useState(false)
|
const[diffBool, setDiffBool] = useState(false)
|
||||||
const [isComposing, setIsComposing] = useState(false);
|
|
||||||
|
|
||||||
// handle reset
|
|
||||||
const handleReset = () => {
|
|
||||||
setReference('');
|
|
||||||
setReferenceBool(STATE.INCORRECT);
|
|
||||||
setChapterTitle('');
|
|
||||||
setChapterTitleBool(STATE.INCORRECT);
|
|
||||||
setTitle('');
|
|
||||||
setTitleBool(STATE.INCORRECT);
|
|
||||||
setVerse('');
|
|
||||||
setVerseBool(STATE.INCORRECT);
|
|
||||||
setDiffBool(false); // optionally hide answer again
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
// function to check correctness of verse input
|
||||||
// Handle the start of composition
|
// so far only perform checking on full spelling of reference names
|
||||||
const handleCompositionStart = () => {
|
|
||||||
setIsComposing(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
function resultChecker(string1, string2) {
|
|
||||||
var result = STATE.INCORRECT; // init
|
|
||||||
// contains korean
|
|
||||||
if (containsKorean(string1)) {
|
|
||||||
if (string1 === string2) {
|
|
||||||
result = STATE.CORRECT;
|
|
||||||
} else if (jamoSubstringMatch(string2, string1) & string1 !== "") {
|
|
||||||
result = STATE.PARTIAL;
|
|
||||||
} else {
|
|
||||||
result = STATE.INCORRECT;
|
|
||||||
}
|
|
||||||
} else { // does not contain korean
|
|
||||||
if (string1 === string2) {
|
|
||||||
result = STATE.CORRECT;
|
|
||||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
|
||||||
result = STATE.PARTIAL;
|
|
||||||
} else {
|
|
||||||
result = STATE.INCORRECT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// function to check correctness of reference input
|
|
||||||
const referenceChange = (e) => {
|
const referenceChange = (e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
const string1 = String(value)
|
const string1 = String(value)
|
||||||
|
@ -81,21 +30,33 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.replace(/\s+/g, "")
|
.replace(/\s+/g, "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
const bool = (string1 === string2);
|
||||||
const result = resultChecker(string1, string2);
|
|
||||||
|
|
||||||
setReference(value);
|
setReference(value);
|
||||||
setReferenceBool(result);
|
setReferenceBool(bool);
|
||||||
};
|
};
|
||||||
|
|
||||||
const referenceClassName = `reference-box${
|
{/* function to check correctness of verse input */}
|
||||||
referenceBool === STATE.CORRECT ? " correct" :
|
const verseChange = (e) => {
|
||||||
referenceBool === STATE.PARTIAL ? " partial" :
|
const value = e.target.value;
|
||||||
" incorrect"
|
let string1 = value;
|
||||||
}`;
|
let string2 = verse;
|
||||||
|
string1 = String(string1)
|
||||||
|
.replace(/[\p{P}\p{S}]/gu, "")
|
||||||
|
.replace(/\s+/g, "")
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize("NFC");
|
||||||
|
string2 = String(string2)
|
||||||
|
.replace(/[\p{P}\p{S}]/gu, "")
|
||||||
|
.replace(/\s+/g, "")
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize("NFC");
|
||||||
|
|
||||||
|
const bool = string1 === string2;
|
||||||
|
|
||||||
|
setVerse(value);
|
||||||
|
setVerseBool(bool);
|
||||||
|
};
|
||||||
|
|
||||||
{/* function to check correctness of title input */}
|
{/* function to check correctness of title input */}
|
||||||
const titleChange = (e) => {
|
const titleChange = (e) => {
|
||||||
|
@ -114,19 +75,11 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
|
||||||
|
const bool = string1 === string2;
|
||||||
const result = resultChecker(string1, string2);
|
|
||||||
|
|
||||||
|
|
||||||
setTitle(value);
|
setTitle(value);
|
||||||
setTitleBool(result);
|
setTitleBool(bool);
|
||||||
};
|
};
|
||||||
const titleClassName = `chapter-title-box${
|
|
||||||
titleBool=== STATE.CORRECT ? " correct" :
|
|
||||||
titleBool === STATE.PARTIAL ? " partial" :
|
|
||||||
" incorrect"
|
|
||||||
}`;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* function to check correctness of chapter title input */}
|
{/* function to check correctness of chapter title input */}
|
||||||
|
@ -148,61 +101,26 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
|
||||||
const result = resultChecker(string1, string2);
|
const bool = string1 === string2;
|
||||||
|
|
||||||
setChapterTitle(value);
|
setChapterTitle(value);
|
||||||
setChapterTitleBool(result);
|
setChapterTitleBool(bool);
|
||||||
};
|
};
|
||||||
const chapterTitleClassName = `title-box${
|
|
||||||
chapterTitleBool=== STATE.CORRECT ? " correct" :
|
|
||||||
chapterTitleBool === STATE.PARTIAL ? " partial" :
|
|
||||||
" incorrect"
|
|
||||||
}`;
|
|
||||||
|
|
||||||
|
const DiffViewer = ({oldValue, newValue}) => {
|
||||||
// check verse input
|
const string1 = String(oldValue)
|
||||||
const verseChange = (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
let string1 = value;
|
|
||||||
let string2 = verse;
|
|
||||||
string1 = String(string1)
|
|
||||||
.replace(/[\p{P}\p{S}]/gu, "")
|
.replace(/[\p{P}\p{S}]/gu, "")
|
||||||
.replace(/\s+/g, "")
|
|
||||||
.toLowerCase()
|
|
||||||
.normalize("NFC");
|
|
||||||
string2 = String(string2)
|
|
||||||
.replace(/[\p{P}\p{S}]/gu, "")
|
|
||||||
.replace(/\s+/g, "")
|
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
|
||||||
const result = resultChecker(string1, string2);
|
|
||||||
|
|
||||||
setVerse(value);
|
const string2 = String(newValue)
|
||||||
setVerseBool(result);
|
.replace(/[\p{P}\p{S}]/gu, "")
|
||||||
};
|
.toLowerCase()
|
||||||
|
.normalize("NFC");
|
||||||
|
|
||||||
const verseClassName = `verse-box${
|
return (<StringDiff oldValue={string1} newValue={string2} diffMethod="diffWords" />)
|
||||||
verseBool === STATE.CORRECT ? " correct" :
|
}
|
||||||
verseBool === STATE.PARTIAL ? " partial" :
|
|
||||||
" incorrect"
|
|
||||||
}`;
|
|
||||||
|
|
||||||
|
|
||||||
// const DiffViewer = ({oldValue, newValue}) => {
|
|
||||||
// const string1 = String(oldValue)
|
|
||||||
// .replace(/[\p{P}\p{S}]/gu, "")
|
|
||||||
// .toLowerCase()
|
|
||||||
// .normalize("NFC");
|
|
||||||
|
|
||||||
|
|
||||||
// const string2 = String(newValue)
|
|
||||||
// .replace(/[\p{P}\p{S}]/gu, "")
|
|
||||||
// .toLowerCase()
|
|
||||||
// .normalize("NFC");
|
|
||||||
|
|
||||||
// return (<StringDiff oldValue={string1} newValue={string2} diffMethod="diffWords" />)
|
|
||||||
// }
|
|
||||||
|
|
||||||
const DiffViewerStrict = ({oldValue, newValue}) => {
|
const DiffViewerStrict = ({oldValue, newValue}) => {
|
||||||
const string1 = String(oldValue)
|
const string1 = String(oldValue)
|
||||||
|
@ -213,33 +131,12 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
|
||||||
|
return (<StringDiff oldValue={string1} newValue={string2} diffMethod="diffWords" />)
|
||||||
let diffStyle = {
|
|
||||||
added: {
|
|
||||||
backgroundColor: 'var(--background-color-added)'
|
|
||||||
},
|
|
||||||
removed: {
|
|
||||||
backgroundColor: 'var(--background-color-removed)'
|
|
||||||
},
|
|
||||||
default: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return (<StringDiff
|
|
||||||
oldValue={string1}
|
|
||||||
newValue={string2}
|
|
||||||
diffMethod="diffWords"
|
|
||||||
styles={diffStyle}
|
|
||||||
/>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="VerseValidator">
|
<div className="VerseValidator">
|
||||||
<div className="verse-number">
|
|
||||||
<h3>Verse {index}</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* toggle hiding reference */}
|
{/* toggle hiding reference */}
|
||||||
{toHideReference ? (
|
{toHideReference ? (
|
||||||
<div>
|
<div>
|
||||||
|
@ -247,17 +144,11 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
{t('verse_validator.input_reference')}
|
{t('verse_validator.input_reference')}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
className={referenceClassName}
|
className={`reference-box${referenceBool ? " correct" : ""}`}
|
||||||
type="text"
|
type="text"
|
||||||
id="referenceBox"
|
id="referenceBox"
|
||||||
name="referenceBox"
|
name="referenceBox"
|
||||||
value={inputReference}
|
onChange={referenceChange}
|
||||||
onChange={(event) => {
|
|
||||||
|
|
||||||
if (!isComposing) {
|
|
||||||
referenceChange(event);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
@ -273,22 +164,11 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
{t('verse_validator.input_chapter_title')}
|
{t('verse_validator.input_chapter_title')}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
className={chapterTitleClassName}
|
className={`chapter-title-box${chapterTitleBool ? " correct" : ""}`}
|
||||||
type="text"
|
type="text"
|
||||||
id="chapterTitleBox"
|
id="chapterTitleBox"
|
||||||
name="chapterTitleBox"
|
name="chapterTitleBox"
|
||||||
value={inputChapterTitle}
|
onChange={chapterTitleChange}
|
||||||
onChange={(event) => {
|
|
||||||
if (!isComposing) {
|
|
||||||
chapterTitleChange(event);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onCompositionStart={handleCompositionStart}
|
|
||||||
onCompositionEnd={(event) => {
|
|
||||||
setIsComposing(false);
|
|
||||||
chapterTitleChange(event);
|
|
||||||
}}
|
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -298,22 +178,11 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
{t('verse_validator.input_title')}
|
{t('verse_validator.input_title')}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
className={titleClassName}
|
className={`title-box${titleBool ? " correct" : ""}`}
|
||||||
type="text"
|
type="text"
|
||||||
id="titleBox"
|
id="titleBox"
|
||||||
name="titleBox"
|
name="titleBox"
|
||||||
value={inputTitle}
|
onChange={titleChange}
|
||||||
onChange={(event) => {
|
|
||||||
if (!isComposing) {
|
|
||||||
titleChange(event);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onCompositionStart={handleCompositionStart}
|
|
||||||
onCompositionEnd={(event) => {
|
|
||||||
setIsComposing(false);
|
|
||||||
titleChange(event);
|
|
||||||
}}
|
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* input box for verse */}
|
{/* input box for verse */}
|
||||||
|
@ -321,30 +190,17 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
{t('verse_validator.input_verse')}
|
{t('verse_validator.input_verse')}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
className={verseClassName}
|
className={`verse-box${verseBool ? " correct" : ""}`}
|
||||||
type="text"
|
type="text"
|
||||||
id="verseBox"
|
id="verseBox"
|
||||||
name="verseBox"
|
name="verseBox"
|
||||||
value={inputVerse}
|
onChange={verseChange}
|
||||||
onChange={(event) => {
|
|
||||||
if (!isComposing) {
|
|
||||||
verseChange(event);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onCompositionStart={handleCompositionStart}
|
|
||||||
onCompositionEnd={(event) => {
|
|
||||||
setIsComposing(false);
|
|
||||||
verseChange(event);
|
|
||||||
}}
|
|
||||||
|
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* button to toggle show answer*/}
|
{/* button to toggle show answer*/}
|
||||||
<div className="answer-button-box">
|
<div className="answer-button-box">
|
||||||
{/* <button onClick={() => setHintBool(!hintBool)}>Show Answer:</button> */}
|
{/* <button onClick={() => setHintBool(!hintBool)}>Show Answer:</button> */}
|
||||||
<button onClick={() => setDiffBool(!diffBool)}>Show Answer:</button>
|
<button onClick={() => setDiffBool(!diffBool)}>Show Answer:</button>
|
||||||
<button onClick={handleReset}>Reset</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* This shows the difference between given and input answers*/}
|
{/* This shows the difference between given and input answers*/}
|
||||||
|
@ -365,7 +221,7 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
{chapterTitle && (
|
{chapterTitle && (
|
||||||
<div>
|
<div>
|
||||||
ChapterTitle:
|
ChapterTitle:
|
||||||
<DiffViewerStrict
|
<DiffViewer
|
||||||
oldValue={chapterTitle}
|
oldValue={chapterTitle}
|
||||||
newValue={inputChapterTitle}
|
newValue={inputChapterTitle}
|
||||||
/>
|
/>
|
||||||
|
@ -375,7 +231,7 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
<p></p>
|
<p></p>
|
||||||
<div>
|
<div>
|
||||||
Title:
|
Title:
|
||||||
<DiffViewerStrict
|
<DiffViewer
|
||||||
oldValue={title}
|
oldValue={title}
|
||||||
newValue={inputTitle}
|
newValue={inputTitle}
|
||||||
/>
|
/>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
86
src/utils.js
86
src/utils.js
|
@ -1,86 +0,0 @@
|
||||||
// jamo utils
|
|
||||||
|
|
||||||
const BASE_CODE = 44032;
|
|
||||||
const CHO = 588;
|
|
||||||
const JUNG = 28;
|
|
||||||
|
|
||||||
const cho = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"];
|
|
||||||
const jung = ["ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ", "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ"];
|
|
||||||
const jong = ["", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"];
|
|
||||||
|
|
||||||
// Decompose the medial vowel into individual components, if necessary
|
|
||||||
const jungSplit = {
|
|
||||||
"ㅘ": ["ㅗ", "ㅏ"],
|
|
||||||
"ㅙ": ["ㅗ", "ㅐ"],
|
|
||||||
"ㅚ": ["ㅗ", "ㅣ"],
|
|
||||||
"ㅝ": ["ㅜ", "ㅓ"],
|
|
||||||
"ㅞ": ["ㅜ", "ㅔ"],
|
|
||||||
"ㅟ": ["ㅜ", "ㅣ"],
|
|
||||||
"ㅢ": ["ㅡ", "ㅣ"],
|
|
||||||
};
|
|
||||||
|
|
||||||
// decompose ending consonents
|
|
||||||
const jongSplit = {
|
|
||||||
"ㄲ": ["ㄱ", "ㄱ"],
|
|
||||||
"ㄳ": ["ㄱ", "ㅅ"],
|
|
||||||
"ㄵ": ["ㄴ", "ㅈ"],
|
|
||||||
"ㄶ": ["ㄴ", "ㅎ"],
|
|
||||||
"ㄺ": ["ㄹ", "ㄱ"],
|
|
||||||
"ㄻ": ["ㄹ", "ㅁ"],
|
|
||||||
"ㄼ": ["ㄹ", "ㅂ"],
|
|
||||||
"ㄽ": ["ㄹ", "ㅅ"],
|
|
||||||
"ㄾ": ["ㄹ", "ㅌ"],
|
|
||||||
"ㄿ": ["ㄹ", "ㅍ"],
|
|
||||||
"ㅀ": ["ㄹ", "ㅎ"],
|
|
||||||
"ㅄ": ["ㅂ", "ㅅ"],
|
|
||||||
"ㅆ": ["ㅅ", "ㅅ"],
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decomposeHangul(character) {
|
|
||||||
const code = character.charCodeAt(0) - BASE_CODE;
|
|
||||||
|
|
||||||
if (code < 0 || code > 11171) {
|
|
||||||
// Return character as is if it's not a Hangul syllable
|
|
||||||
return character;
|
|
||||||
}
|
|
||||||
|
|
||||||
const choIndex = Math.floor(code / CHO);
|
|
||||||
const jungIndex = Math.floor((code - (choIndex * CHO)) / JUNG);
|
|
||||||
const jongIndex = code % JUNG;
|
|
||||||
|
|
||||||
// Decompose cho, jung, and jong
|
|
||||||
const choJamo = cho[choIndex];
|
|
||||||
const jungJamo = jung[jungIndex];
|
|
||||||
const jongJamo = jong[jongIndex];
|
|
||||||
|
|
||||||
// Handle double jamo in jung
|
|
||||||
const jungComponents = jungSplit[jungJamo] || [jungJamo];
|
|
||||||
|
|
||||||
|
|
||||||
// handle double jamo in jong
|
|
||||||
const jongComponents = jongSplit[jongJamo] || [jongJamo];
|
|
||||||
|
|
||||||
return [choJamo, ...jungComponents, ...jongComponents].join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decomposeStringToJamo(inputString) {
|
|
||||||
return inputString.split('').map(decomposeHangul).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function jamoSubstringMatch(mainString, substring) {
|
|
||||||
const decomposedMain = decomposeStringToJamo(mainString)
|
|
||||||
.replace(/\s+/g, "");
|
|
||||||
const decomposedSub = decomposeStringToJamo(substring)
|
|
||||||
.replace(/\s+/g, "");
|
|
||||||
|
|
||||||
return decomposedMain.startsWith(decomposedSub);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unicode Ranges for Korean Characters:
|
|
||||||
// Hangul Syllables: \uAC00-\uD7A3
|
|
||||||
// Hangul Jamo: \u1100-\u11FF (for initial consonants, vowels, and final consonants)
|
|
||||||
// Hangul Compatibility Jamo: \u3130-\u318F
|
|
||||||
export function containsKorean(text) {
|
|
||||||
const koreanRegex = /[\u1100-\u11FF\u3130-\u318F\uAC00-\uD7A3]/;
|
|
||||||
return koreanRegex.test(text);
|
|
||||||
}
|
|
Loading…
Reference in New Issue