json-bible 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/JSON_bible_icon.png +0 -0
- package/README.md +119 -0
- package/add-js-extension.js +42 -0
- package/index.ts +243 -0
- package/lib/Bible.ts +45 -0
- package/lib/defaults.ts +136 -0
- package/lib/get.ts +164 -0
- package/lib/load.ts +70 -0
- package/lib/markdown.ts +63 -0
- package/lib/reference.ts +205 -0
- package/lib/search.ts +122 -0
- package/lib/util.ts +21 -0
- package/package.json +18 -0
- package/preview/bible.html +162 -0
- package/preview/domHelper.js +60 -0
- package/preview/style.css +216 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>JSON Bible Preview</title>
|
|
7
|
+
|
|
8
|
+
<link rel="icon" type="image/png" href="../JSON_bible_icon.png" />
|
|
9
|
+
|
|
10
|
+
<link rel="stylesheet" href="style.css" />
|
|
11
|
+
<script type="importmap">
|
|
12
|
+
{
|
|
13
|
+
"imports": {
|
|
14
|
+
"bible": "../dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
</head>
|
|
19
|
+
|
|
20
|
+
<body>
|
|
21
|
+
<header>
|
|
22
|
+
<h3 id="name"></h3>
|
|
23
|
+
|
|
24
|
+
<div class="search">
|
|
25
|
+
<input type="text" name="" id="bookSearch" placeholder="Book search... [John 3:16]" />
|
|
26
|
+
<input type="text" name="" id="textSearch" placeholder="Text search... [Jesus]" />
|
|
27
|
+
</div>
|
|
28
|
+
</header>
|
|
29
|
+
|
|
30
|
+
<div class="searchResults hidden"></div>
|
|
31
|
+
|
|
32
|
+
<div class="bible">
|
|
33
|
+
<div class="books">Loading...</div>
|
|
34
|
+
<div class="chapters"></div>
|
|
35
|
+
|
|
36
|
+
<div class="header"></div>
|
|
37
|
+
<div class="content">
|
|
38
|
+
<div class="invert">
|
|
39
|
+
<button id="previous">←</button>
|
|
40
|
+
<div class="verses"></div>
|
|
41
|
+
</div>
|
|
42
|
+
<button id="next">→</button>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="footer"></div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<!-- <mark> can be used to highlight verses! -->
|
|
49
|
+
|
|
50
|
+
<script src="domHelper.js"></script>
|
|
51
|
+
<script type="module">
|
|
52
|
+
import Bible from "bible"
|
|
53
|
+
|
|
54
|
+
const KJV_BIBLE = "https://api.npoint.io/a906dacba84513c298ce"
|
|
55
|
+
const bible = await Bible(KJV_BIBLE)
|
|
56
|
+
console.log("BIBLE:", bible)
|
|
57
|
+
|
|
58
|
+
const metadata = bible.getMetadata()
|
|
59
|
+
nameElem.innerHTML = `${metadata.title} (${bible.getAbbreviation()})`
|
|
60
|
+
footerElem.innerHTML = metadata.copyright || ""
|
|
61
|
+
|
|
62
|
+
// MAIN //
|
|
63
|
+
|
|
64
|
+
setBible()
|
|
65
|
+
function setBible() {
|
|
66
|
+
const hashed = location.hash.slice(1).split(".").filter(Boolean)
|
|
67
|
+
setBook(hashed[0], hashed[1])
|
|
68
|
+
|
|
69
|
+
const click = (id) => setBook(id)
|
|
70
|
+
const style = (a) => `border-bottom: 2px solid ${bible.getBook(a.number).getCategory().color}`
|
|
71
|
+
setHTML(booksElem, bible.data.books, (a) => a.name, { type: "book" }, click, style)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function setBook(bookId, chapterId) {
|
|
75
|
+
const book = bible.getBook(bookId)
|
|
76
|
+
|
|
77
|
+
const click = (id) => setChapter(bookId, id)
|
|
78
|
+
setHTML(chaptersElem, book.data.chapters, (a) => a.number, { type: "chapter" }, click)
|
|
79
|
+
|
|
80
|
+
// timeout to allow book elems to load before setting active
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
setActive(booksElem, book.number)
|
|
83
|
+
setChapter(bookId, chapterId || undefined)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function setChapter(bookId, chapterId) {
|
|
88
|
+
const book = bible.getBook(bookId)
|
|
89
|
+
const chapter = book.getChapter(chapterId)
|
|
90
|
+
|
|
91
|
+
headerElem.innerHTML = chapter.data.header || ""
|
|
92
|
+
versesElem.innerHTML = ""
|
|
93
|
+
createElement(versesElem, chapter.getVerses().getHTML(true), { type: "verse" })
|
|
94
|
+
|
|
95
|
+
// get all cross reference links
|
|
96
|
+
;[...versesElem.querySelectorAll(".cross-ref-id")].forEach((elem) => {
|
|
97
|
+
elem.addEventListener("click", () => {
|
|
98
|
+
openReference(elem.id)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
location.hash = `${book.data.id}.${chapter.number}`
|
|
103
|
+
setActive(chaptersElem, chapter.number)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// NAVIGATE //
|
|
107
|
+
|
|
108
|
+
nextElem.addEventListener("click", () => nextOrPrevious(true))
|
|
109
|
+
previousElem.addEventListener("click", () => nextOrPrevious(false))
|
|
110
|
+
|
|
111
|
+
function nextOrPrevious(next) {
|
|
112
|
+
let currentBookNumber = getActiveId(booksElem)
|
|
113
|
+
let currentChapterNumber = getActiveId(chaptersElem)
|
|
114
|
+
|
|
115
|
+
const chapter = bible.getBook(currentBookNumber).getChapter(currentChapterNumber)
|
|
116
|
+
const ref = next ? chapter.getNext() : chapter.getPrevious()
|
|
117
|
+
|
|
118
|
+
if (ref) setBook(ref.book, ref.chapter)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// SEARCH //
|
|
122
|
+
|
|
123
|
+
bookSearch.addEventListener("input", (e) => {
|
|
124
|
+
let value = e.target.value
|
|
125
|
+
|
|
126
|
+
const searchResult = bible.bookSearch(value)
|
|
127
|
+
console.log("BOOK SEARCH:", searchResult)
|
|
128
|
+
|
|
129
|
+
e.target.value = searchResult.autocompleted
|
|
130
|
+
|
|
131
|
+
if (searchResult.book) setBook(searchResult.book, searchResult.chapter)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
textSearch.addEventListener("change", (e) => {
|
|
135
|
+
let value = e.target.value
|
|
136
|
+
|
|
137
|
+
if (value) hideElem(bibleElem, searchResultsElem)
|
|
138
|
+
else hideElem(searchResultsElem, bibleElem)
|
|
139
|
+
|
|
140
|
+
const searchResults = bible.textSearch(value, 100)
|
|
141
|
+
console.log("TEXT SEARCH:", searchResults)
|
|
142
|
+
|
|
143
|
+
if (!searchResults.length) {
|
|
144
|
+
searchResultsElem.innerHTML = "No results!"
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
setHTML(searchResultsElem, searchResults, (a) => `<b>${a.reference}</b> ${a.verse.text}`, { type: "searchItem" }, openReference)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
function openReference(id) {
|
|
152
|
+
const ref = bible.getFromReference(id)[0]
|
|
153
|
+
if (!ref) return
|
|
154
|
+
|
|
155
|
+
textSearch.value = ""
|
|
156
|
+
hideElem(searchResultsElem, bibleElem)
|
|
157
|
+
|
|
158
|
+
setBook(ref.book, ref.chapter)
|
|
159
|
+
}
|
|
160
|
+
</script>
|
|
161
|
+
</body>
|
|
162
|
+
</html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// ELEMENTS //
|
|
2
|
+
|
|
3
|
+
const nameElem = document.querySelector("#name")
|
|
4
|
+
|
|
5
|
+
const bookSearch = document.querySelector("#bookSearch")
|
|
6
|
+
const textSearch = document.querySelector("#textSearch")
|
|
7
|
+
const searchResultsElem = document.querySelector(".searchResults")
|
|
8
|
+
|
|
9
|
+
const bibleElem = document.querySelector(".bible")
|
|
10
|
+
const booksElem = document.querySelector(".books")
|
|
11
|
+
const chaptersElem = document.querySelector(".chapters")
|
|
12
|
+
const versesElem = document.querySelector(".verses")
|
|
13
|
+
const headerElem = document.querySelector(".header")
|
|
14
|
+
const footerElem = document.querySelector(".footer")
|
|
15
|
+
|
|
16
|
+
const previousElem = document.querySelector("#previous")
|
|
17
|
+
const nextElem = document.querySelector("#next")
|
|
18
|
+
|
|
19
|
+
// HTML UPDATE //
|
|
20
|
+
|
|
21
|
+
function setHTML(parentElem, content, text, { type }, click, style) {
|
|
22
|
+
parentElem.innerHTML = ""
|
|
23
|
+
content.forEach((a) => {
|
|
24
|
+
const id = a.number ?? a.id
|
|
25
|
+
const elem = createElement(parentElem, text(a), { type, id })
|
|
26
|
+
if (style) elem.style = style(a)
|
|
27
|
+
|
|
28
|
+
elem.addEventListener("click", () => {
|
|
29
|
+
click(id)
|
|
30
|
+
setActive(parentElem, elem)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createElement(parentElem, html, { type, id }) {
|
|
36
|
+
const elem = document.createElement("div")
|
|
37
|
+
|
|
38
|
+
if (type) elem.classList.add(type)
|
|
39
|
+
if (id) elem.id = "_" + id
|
|
40
|
+
elem.innerHTML = html
|
|
41
|
+
|
|
42
|
+
parentElem.appendChild(elem)
|
|
43
|
+
return elem
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function setActive(parentElem, idOrElem) {
|
|
47
|
+
parentElem.querySelector(".active")?.classList.remove("active")
|
|
48
|
+
if (idOrElem === undefined) parentElem.children[0]?.classList.add("active")
|
|
49
|
+
else if (typeof idOrElem === "number" || typeof idOrElem === "string") parentElem.querySelector("#_" + idOrElem)?.classList.add("active")
|
|
50
|
+
else idOrElem?.classList.add("active")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function hideElem(elem, revealElem) {
|
|
54
|
+
elem?.classList.add("hidden")
|
|
55
|
+
revealElem?.classList.remove("hidden")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getActiveId(parentElem) {
|
|
59
|
+
return parentElem.querySelector(".active")?.id.slice(1)
|
|
60
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
body {
|
|
7
|
+
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
|
8
|
+
font-size: 1.1em;
|
|
9
|
+
background-color: #fff9ee;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
input,
|
|
13
|
+
button {
|
|
14
|
+
color: inherit;
|
|
15
|
+
font-size: inherit;
|
|
16
|
+
font-family: inherit;
|
|
17
|
+
background-color: inherit;
|
|
18
|
+
user-select: none;
|
|
19
|
+
|
|
20
|
+
border: 1px solid black;
|
|
21
|
+
border-radius: 2px;
|
|
22
|
+
padding: 3px 8px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
input::placeholder {
|
|
26
|
+
opacity: 0.7;
|
|
27
|
+
font-size: 0.9em;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.active {
|
|
31
|
+
text-decoration: underline;
|
|
32
|
+
background-color: rgb(0 0 0 / 0.08);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.hidden {
|
|
36
|
+
display: none !important;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* MAIN */
|
|
40
|
+
|
|
41
|
+
header {
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-wrap: wrap;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: space-between;
|
|
46
|
+
gap: 10px;
|
|
47
|
+
|
|
48
|
+
padding: 20px 10vw;
|
|
49
|
+
background-color: rgb(0 0 0 / 8%);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.bible {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
gap: 10px;
|
|
56
|
+
|
|
57
|
+
margin: 30px 10vw;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.books,
|
|
61
|
+
.chapters {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-wrap: wrap;
|
|
64
|
+
/* justify-content: space-between; */
|
|
65
|
+
}
|
|
66
|
+
.chapters {
|
|
67
|
+
margin-bottom: 20px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.books div,
|
|
71
|
+
.chapters div {
|
|
72
|
+
padding: 5px;
|
|
73
|
+
min-width: 24px;
|
|
74
|
+
text-align: center;
|
|
75
|
+
/* font-family: monospace; */
|
|
76
|
+
|
|
77
|
+
user-select: none;
|
|
78
|
+
}
|
|
79
|
+
.books div {
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
padding: 5px 8px;
|
|
82
|
+
/* flex: 1; */
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.content,
|
|
86
|
+
.invert {
|
|
87
|
+
display: flex;
|
|
88
|
+
/* align-items: center; */
|
|
89
|
+
gap: 20px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
button {
|
|
93
|
+
background-color: transparent;
|
|
94
|
+
border: none;
|
|
95
|
+
|
|
96
|
+
font-family: monospace;
|
|
97
|
+
font-size: 2.5em;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.header,
|
|
101
|
+
.footer {
|
|
102
|
+
text-align: center;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.verse {
|
|
106
|
+
font-size: 1.2em;
|
|
107
|
+
font-family: "Times New Roman", Times, serif;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.number {
|
|
111
|
+
font-weight: bold;
|
|
112
|
+
user-select: none;
|
|
113
|
+
}
|
|
114
|
+
.number.big {
|
|
115
|
+
font-size: 2em;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.uncertain {
|
|
119
|
+
font-style: italic;
|
|
120
|
+
opacity: 0.8;
|
|
121
|
+
}
|
|
122
|
+
h4 {
|
|
123
|
+
text-align: center;
|
|
124
|
+
padding: 20px 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.cross-ref {
|
|
128
|
+
position: relative;
|
|
129
|
+
}
|
|
130
|
+
.cross-ref::before {
|
|
131
|
+
content: "*";
|
|
132
|
+
padding: 0 2px;
|
|
133
|
+
color: blue;
|
|
134
|
+
}
|
|
135
|
+
.cross-ref .content {
|
|
136
|
+
position: absolute;
|
|
137
|
+
bottom: 5px;
|
|
138
|
+
left: 0;
|
|
139
|
+
transform: translateY(100%);
|
|
140
|
+
|
|
141
|
+
display: block;
|
|
142
|
+
|
|
143
|
+
background-color: rgb(0 0 0 / 0.6);
|
|
144
|
+
color: white;
|
|
145
|
+
border-radius: 8px;
|
|
146
|
+
padding: 8px 10px;
|
|
147
|
+
min-width: 130px;
|
|
148
|
+
|
|
149
|
+
transition: opacity 0.2s;
|
|
150
|
+
|
|
151
|
+
opacity: 0;
|
|
152
|
+
pointer-events: none;
|
|
153
|
+
}
|
|
154
|
+
.cross-ref:hover > .content {
|
|
155
|
+
opacity: 1;
|
|
156
|
+
pointer-events: inherit;
|
|
157
|
+
}
|
|
158
|
+
.cross-ref-id {
|
|
159
|
+
text-decoration: underline;
|
|
160
|
+
cursor: pointer;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* SEARCH */
|
|
164
|
+
|
|
165
|
+
.searchResults {
|
|
166
|
+
display: flex;
|
|
167
|
+
flex-direction: column;
|
|
168
|
+
|
|
169
|
+
margin: 30px 10vw;
|
|
170
|
+
}
|
|
171
|
+
.searchResults div {
|
|
172
|
+
padding: 8px 12px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* HOVER */
|
|
176
|
+
|
|
177
|
+
button,
|
|
178
|
+
.books div,
|
|
179
|
+
.chapters div,
|
|
180
|
+
.searchResults div {
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
transition: background-color 0.2s;
|
|
183
|
+
border-radius: 2px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
button:hover,
|
|
187
|
+
.books div:hover,
|
|
188
|
+
.chapters div:hover,
|
|
189
|
+
.searchResults div:hover {
|
|
190
|
+
background-color: rgb(0 0 0 / 0.1);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* MEDIA */
|
|
194
|
+
|
|
195
|
+
@media only screen and (max-width: 800px) {
|
|
196
|
+
.content {
|
|
197
|
+
flex-wrap: wrap;
|
|
198
|
+
gap: 0;
|
|
199
|
+
position: relative;
|
|
200
|
+
}
|
|
201
|
+
.invert {
|
|
202
|
+
flex-direction: column-reverse;
|
|
203
|
+
}
|
|
204
|
+
.verses {
|
|
205
|
+
width: 100%;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.content button {
|
|
209
|
+
width: 50%;
|
|
210
|
+
}
|
|
211
|
+
.content #next {
|
|
212
|
+
position: absolute;
|
|
213
|
+
bottom: 0;
|
|
214
|
+
right: 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "es2022",
|
|
4
|
+
"target": "es2022",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"forceConsistentCasingInFileNames": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist"
|
|
12
|
+
}
|
|
13
|
+
}
|