codexparser 0.1.58 → 0.1.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexparser",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "description": "This is a Javascript Bible parser and text scanner. It will search through texts and collate all scripture references into an array and parse them into objects, and it will parse passages into objects by book, chapter, verse, and testament. ",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,3 +1,10 @@
1
+ /**
2
+ * CodexParser.js
3
+ * A class for scanning and parsing scripture references from text, supporting various formats
4
+ * (e.g., single verses, ranges, multi-chapter references) with validation and version-specific
5
+ * versification. Handles book names, abbreviations, and SBL-style formatting.
6
+ */
7
+
1
8
  const versified = require("./versified")
2
9
  const bible = require("./bible")
3
10
  const { bookRegex, chapterRegex, verseRegex, scripturesRegex } = require("./regex")
@@ -8,28 +15,37 @@ const dd = require("./functions").dd
8
15
  const sch = require("./functions").sch
9
16
  const chapter_verses = require("./chapterVerseCombine")
10
17
 
18
+ /**
19
+ * Class for parsing and validating scripture references.
20
+ * @class
21
+ */
11
22
  class CodexParser {
23
+ /**
24
+ * Initializes the parser with default properties and data.
25
+ */
12
26
  constructor() {
13
- this.found = []
14
- this.passages = []
15
- this.bible = bible
16
- this.bookRegex = bookRegex
17
- this.chapterRegex = chapterRegex
18
- this.verseRegex = verseRegex
19
- this.scripturesRegex = scripturesRegex
20
- this.abbreviations = abbreviations
21
- this.sblAbbreviations = sblAbbreviations
22
- this.versificationDifferences = versified
27
+ this.found = [] // Array to store detected references
28
+ this.passages = [] // Array to store parsed passages
29
+ this.bible = bible // Bible data (Old/New Testament books)
30
+ this.bookRegex = bookRegex // Regex for book names
31
+ this.chapterRegex = chapterRegex // Regex for chapters
32
+ this.verseRegex = verseRegex // Regex for verses
33
+ this.scripturesRegex = scripturesRegex // Regex for full references
34
+ this.abbreviations = abbreviations // Book abbreviation mappings
35
+ this.sblAbbreviations = sblAbbreviations // SBL-style abbreviation mappings
36
+ this.versificationDifferences = versified // Version-specific verse differences
23
37
  this.singleChapterBook = [
38
+ // Books with a single chapter and their verse counts
24
39
  sch("Jude", 25),
25
40
  sch("2 John", 13),
26
41
  sch("3 John", 15),
27
42
  sch("Obadiah", 21),
28
43
  sch("Philemon", 25),
29
44
  ]
30
- this.chapterVerses = chapter_verses
31
- this.error = false
32
- this.version = null
45
+ this.chapterVerses = chapter_verses // Chapter-verse mappings
46
+ this.error = false // Error flag
47
+ this.version = null // Bible version (e.g., lxx, mt, eng)
48
+ // Reference type constants
33
49
  this.SINGLE_CHAPTER = "single_chapter"
34
50
  this.CHAPTER_VERSE = "chapter_verse"
35
51
  this.CHAPTER_VERSE_RANGE = "chapter_verse_range"
@@ -38,15 +54,27 @@ class CodexParser {
38
54
  this.MULTI_CHAPTER_RANGE = "multi_chapter_verse_range"
39
55
  }
40
56
 
57
+ /**
58
+ * Retrieves available verses for a given book and chapter.
59
+ * @param {string} book - The book name (e.g., "Genesis").
60
+ * @param {number} chapter - The chapter number.
61
+ * @returns {number[]} Array of valid verse numbers.
62
+ */
41
63
  getChapterVerses(book, chapter) {
42
64
  const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === book)
43
65
  return singleChapterBook ? singleChapterBook[book][chapter] || [] : this.chapterVerses[book]?.[chapter] || []
44
66
  }
45
67
 
68
+ /**
69
+ * Scans text for scripture references and stores them in `this.found`.
70
+ * @param {string} text - The text to scan.
71
+ * @returns {CodexParser} The parser instance for method chaining.
72
+ */
46
73
  scan(text) {
47
74
  const fullNames = [...this.bible.old, ...this.bible.new]
48
75
  const abbreviations = Object.keys(this.abbreviations)
49
76
  this.found = []
77
+ // Normalize text: replace periods before digits with colons, remove trailing periods, collapse spaces
50
78
  let normalizedText = text
51
79
  .replace(/\.(?=\d)/g, ":")
52
80
  .replace(/(\b[A-Za-z]+)\.(?=\s|$)/g, "$1")
@@ -56,7 +84,9 @@ class CodexParser {
56
84
  const lowerCaseText = normalizedText.toLowerCase()
57
85
  let i = 0
58
86
 
87
+ // Check if a character is valid for chapter/verse (non-letter)
59
88
  const isValidChapterVerseChar = (char) => /[^A-Za-z]/.test(char)
89
+ // Check if the next segment starts with a Bible book
60
90
  const isNextBibleBook = (startIndex) => {
61
91
  const textAfterCurrentPosition = lowerCaseText.substring(startIndex).trim()
62
92
  return (
@@ -64,16 +94,19 @@ class CodexParser {
64
94
  lowercaseBibleAbbreviations.some((abbr) => textAfterCurrentPosition.startsWith(abbr))
65
95
  )
66
96
  }
97
+ // Detect version suffix (LXX or MT)
67
98
  const detectSuffix = (startIndex) => {
68
99
  const suffixMatch = normalizedText.substring(startIndex).match(/\b(LXX|MT)\b/i)
69
100
  return suffixMatch ? suffixMatch[0].toUpperCase() : null
70
101
  }
71
102
 
103
+ // Iterate through text to find book names or abbreviations
72
104
  while (i < lowerCaseText.length) {
73
105
  let foundBook = null
74
106
  let foundIndex = -1
75
107
  let matchedLength = 0
76
108
 
109
+ // Check for full book names
77
110
  for (let j = 0; j < lowercaseBibleFullNames.length; j++) {
78
111
  const book = lowercaseBibleFullNames[j]
79
112
  if (lowerCaseText.startsWith(book, i) && book.length > matchedLength) {
@@ -83,6 +116,7 @@ class CodexParser {
83
116
  }
84
117
  }
85
118
 
119
+ // Check for abbreviations if no full name found
86
120
  if (!foundBook) {
87
121
  for (let k = 0; k < lowercaseBibleAbbreviations.length; k++) {
88
122
  const abbreviation = lowercaseBibleAbbreviations[k]
@@ -99,6 +133,7 @@ class CodexParser {
99
133
  let chapterVerse = ""
100
134
  const references = []
101
135
 
136
+ // Collect chapter-verse reference until next book or invalid character
102
137
  while (i < normalizedText.length && isValidChapterVerseChar(normalizedText[i])) {
103
138
  if (isNextBibleBook(i)) break
104
139
  if (normalizedText[i] === ";") {
@@ -112,6 +147,7 @@ class CodexParser {
112
147
  i++
113
148
  }
114
149
 
150
+ // Add final reference if present
115
151
  if (chapterVerse.trim().length > 0) {
116
152
  const formattedReference = chapterVerse.trim().replace(/[^a-zA-Z0-9]+$/, "")
117
153
  if (formattedReference) references.push(formattedReference)
@@ -119,6 +155,7 @@ class CodexParser {
119
155
 
120
156
  const suffix = detectSuffix(i)
121
157
 
158
+ // Process each reference and determine its type
122
159
  references.forEach((ref) => {
123
160
  let type
124
161
  if (ref.includes(":")) {
@@ -159,6 +196,11 @@ class CodexParser {
159
196
  return this
160
197
  }
161
198
 
199
+ /**
200
+ * Sets the Bible version for parsing.
201
+ * @param {string} version - The version (e.g., "lxx", "mt", "eng").
202
+ * @returns {CodexParser} The parser instance.
203
+ */
162
204
  bibleVersion(version) {
163
205
  const lowerVersion = version.toLowerCase()
164
206
  this.version =
@@ -168,6 +210,11 @@ class CodexParser {
168
210
  return this
169
211
  }
170
212
 
213
+ /**
214
+ * Parses a scripture reference into structured passage objects.
215
+ * @param {string} reference - The reference to parse (e.g., "John 3:16").
216
+ * @returns {CodexParser} The parser instance.
217
+ */
171
218
  parse(reference) {
172
219
  this.scan(reference)
173
220
 
@@ -190,12 +237,13 @@ class CodexParser {
190
237
  end: null,
191
238
  }
192
239
 
240
+ // Parse reference parts (chapter, verses, ranges)
193
241
  this.parseReferenceParts(parsedPassage, passage.reference.split(","))
194
242
  parsedPassage.passages = this.populate(parsedPassage)
195
243
  parsedPassage.scripture = this.scripturize(parsedPassage)
196
244
  parsedPassage.valid = this._isValid(parsedPassage, passage.reference)
197
245
 
198
- // Add SBL abbreviation as full reference with en dashes and space after commas for comma-separated verses
246
+ // Generate SBL abbreviation with conditional period
199
247
  const sblEntry = this.sblAbbreviations[book] || { value: book, abbr: false }
200
248
  const sblBook = sblEntry.value + (sblEntry.abbr ? "." : "")
201
249
  let abbr = parsedPassage.scripture.passage.replace(book, sblBook).replace(/-/g, "–")
@@ -205,12 +253,14 @@ class CodexParser {
205
253
  }
206
254
  parsedPassage.abbr = abbr
207
255
 
256
+ // Handle multi-chapter ranges
208
257
  if (parsedPassage.type === this.MULTI_CHAPTER_RANGE) {
209
258
  this.handleMultiChapterRange(parsedPassage, passage.reference)
210
259
  } else {
211
260
  delete parsedPassage.to
212
261
  }
213
262
 
263
+ // Set start and end points for passage range
214
264
  if (parsedPassage.passages.length > 0) {
215
265
  const sortedPassages = parsedPassage.passages.slice().sort((a, b) => {
216
266
  if (a.chapter !== b.chapter) return a.chapter - b.chapter
@@ -230,6 +280,7 @@ class CodexParser {
230
280
  }
231
281
  }
232
282
 
283
+ // Default to English version if none specified
233
284
  if (!parsedPassage.version) {
234
285
  parsedPassage.version = {
235
286
  name: "English",
@@ -238,7 +289,7 @@ class CodexParser {
238
289
  }
239
290
  }
240
291
 
241
- // Attach the reference method to this individual passage object
292
+ // Attach reference method to passage
242
293
  parsedPassage.reference = function () {
243
294
  return this.scripture.passage
244
295
  }
@@ -250,6 +301,12 @@ class CodexParser {
250
301
  return this
251
302
  }
252
303
 
304
+ /**
305
+ * Parses reference parts into chapter and verse components.
306
+ * @param {Object} passage - The passage object to populate.
307
+ * @param {string[]} parts - Array of reference parts.
308
+ * @private
309
+ */
253
310
  parseReferenceParts(passage, parts) {
254
311
  const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === passage.book)
255
312
 
@@ -269,6 +326,13 @@ class CodexParser {
269
326
  })
270
327
  }
271
328
 
329
+ /**
330
+ * Parses chapter-verse references (e.g., "3:16").
331
+ * @param {Object} passage - The passage object.
332
+ * @param {string} part - The reference part.
333
+ * @param {boolean} isFirstPart - Whether this is the first part.
334
+ * @private
335
+ */
272
336
  parseChapterVerse(passage, part, isFirstPart) {
273
337
  const [chapter, verse] = part.split(":")
274
338
  if (isFirstPart) passage.chapter = Number(chapter)
@@ -276,6 +340,13 @@ class CodexParser {
276
340
  passage.verses.push(verse.includes("-") ? verse : Number(verse))
277
341
  }
278
342
 
343
+ /**
344
+ * Parses references for single-chapter books (e.g., "Jude 5").
345
+ * @param {Object} passage - The passage object.
346
+ * @param {string} part - The reference part.
347
+ * @param {boolean} isWholeChapter - Whether the reference is for the whole chapter.
348
+ * @private
349
+ */
279
350
  parseSingleChapterBook(passage, part, isWholeChapter) {
280
351
  const verseCount = this.getChapterVerses(passage.book, 1).length
281
352
  if (part === "1" && isWholeChapter) {
@@ -296,6 +367,13 @@ class CodexParser {
296
367
  }
297
368
  }
298
369
 
370
+ /**
371
+ * Parses range references (e.g., "1-3").
372
+ * @param {Object} passage - The passage object.
373
+ * @param {string} part - The reference part.
374
+ * @param {boolean} isFirstPart - Whether this is the first part.
375
+ * @private
376
+ */
299
377
  parseRange(passage, part, isFirstPart) {
300
378
  if (!passage.chapter && isFirstPart) {
301
379
  const [start, end] = part.split("-").map(Number)
@@ -318,6 +396,13 @@ class CodexParser {
318
396
  }
319
397
  }
320
398
 
399
+ /**
400
+ * Parses single number references (e.g., "3" for chapter or verse).
401
+ * @param {Object} passage - The passage object.
402
+ * @param {string} part - The reference part.
403
+ * @param {boolean} isFirstPart - Whether this is the first part.
404
+ * @private
405
+ */
321
406
  parseSingleNumber(passage, part, isFirstPart) {
322
407
  if (isFirstPart && !passage.chapter) {
323
408
  passage.chapter = Number(part)
@@ -332,6 +417,12 @@ class CodexParser {
332
417
  }
333
418
  }
334
419
 
420
+ /**
421
+ * Handles multi-chapter range references (e.g., "3:16-4:5").
422
+ * @param {Object} passage - The passage object.
423
+ * @param {string} reference - The full reference string.
424
+ * @private
425
+ */
335
426
  handleMultiChapterRange(passage, reference) {
336
427
  const parts = reference.split(",")
337
428
  const lastPart = parts[parts.length - 1]
@@ -345,6 +436,13 @@ class CodexParser {
345
436
  }
346
437
  }
347
438
 
439
+ /**
440
+ * Generates a range of numbers.
441
+ * @param {number} start - Start number.
442
+ * @param {number} end - End number.
443
+ * @returns {number[]} Array of numbers.
444
+ * @private
445
+ */
348
446
  _generateRange(start, end) {
349
447
  const range = []
350
448
  for (let i = start; i <= end; i++) {
@@ -353,10 +451,47 @@ class CodexParser {
353
451
  return range
354
452
  }
355
453
 
454
+ /**
455
+ * Searches for versification differences for a book and chapter.
456
+ * @param {string} book - The book name.
457
+ * @param {number} chapter - The chapter number.
458
+ * @param {string} version - The Bible version.
459
+ * @returns {Object|undefined} Updated chapter verses or undefined.
460
+ * @private
461
+ */
356
462
  _searchVersificationDifferences(book, chapter, version) {
357
463
  version = version.toLowerCase()
358
- if (!this.chapterVerses[book][chapter]) return
359
- if (!this.versificationDifferences[book]) return
464
+
465
+ // Handle single-chapter book "Obadiah"
466
+ if (book === "Obadiah") {
467
+ const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === "Obadiah")
468
+ if (!singleChapterBook || !singleChapterBook[book][chapter]) {
469
+ return // No data for Obadiah or chapter
470
+ }
471
+ if (!this.versificationDifferences[book]) {
472
+ return // No versification differences for Obadiah
473
+ }
474
+ // Process versification differences
475
+ for (const [key, value] of Object.entries(this.versificationDifferences[book])) {
476
+ if (value[version].startsWith(`${chapter}:`)) {
477
+ if (value[version]) {
478
+ const verse = value[version].split(":")[1]
479
+ singleChapterBook[book][chapter].push(Number(verse))
480
+ }
481
+ }
482
+ }
483
+ // Ensure unique verses and update the singleChapterBook entry
484
+ singleChapterBook[book][chapter] = Array.from(new Set(singleChapterBook[book][chapter]))
485
+ return singleChapterBook[book] // Return updated chapter data
486
+ }
487
+
488
+ // Handle all other books using chapterVerses
489
+ if (!this.chapterVerses[book][chapter]) {
490
+ return // No data for this book/chapter
491
+ }
492
+ if (!this.versificationDifferences[book]) {
493
+ return // No versification differences
494
+ }
360
495
  for (const [key, value] of Object.entries(this.versificationDifferences[book])) {
361
496
  if (value[version].startsWith(`${chapter}:`)) {
362
497
  if (value[version]) {
@@ -366,9 +501,16 @@ class CodexParser {
366
501
  }
367
502
  }
368
503
  this.chapterVerses[book][chapter] = Array.from(this.chapterVerses[book][chapter])
369
- return this.chapterVerses
504
+ return this.chapterVerses // Return updated chapterVerses
370
505
  }
371
506
 
507
+ /**
508
+ * Sets the Bible version and applies versification differences.
509
+ * @param {string} book - The book name.
510
+ * @param {number} chapter - The chapter number.
511
+ * @param {string} version - The Bible version.
512
+ * @private
513
+ */
372
514
  _setVersion(book, chapter, version) {
373
515
  this.version = version ? version : "eng"
374
516
  if (this.version !== "eng") {
@@ -376,6 +518,9 @@ class CodexParser {
376
518
  }
377
519
  }
378
520
 
521
+ /**
522
+ * Applies versification differences to parsed passages.
523
+ */
379
524
  versification() {
380
525
  this.passages.forEach((passage) => {
381
526
  const hasVersification = this.versificationDifferences[passage.book]
@@ -407,6 +552,11 @@ class CodexParser {
407
552
  })
408
553
  }
409
554
 
555
+ /**
556
+ * Populates passage with expanded verse objects.
557
+ * @param {Object} passage - The passage object.
558
+ * @returns {Object[]} Array of verse objects.
559
+ */
410
560
  populate(passage) {
411
561
  const { book, chapter, verses, type, to } = passage
412
562
  const version = passage.version?.abbreviation || "eng"
@@ -449,6 +599,13 @@ class CodexParser {
449
599
  return []
450
600
  }
451
601
 
602
+ /**
603
+ * Expands verse references into individual verse objects.
604
+ * @param {string} book - The book name.
605
+ * @param {number} chapter - The chapter number.
606
+ * @param {Array<string|number>} verses - Array of verses or ranges.
607
+ * @returns {Object[]} Array of verse objects.
608
+ */
452
609
  expandVerses(book, chapter, verses) {
453
610
  const passages = []
454
611
  const chapterVerses = this.getChapterVerses(book, chapter)
@@ -466,6 +623,11 @@ class CodexParser {
466
623
  return passages
467
624
  }
468
625
 
626
+ /**
627
+ * Normalizes book names using abbreviations or full names.
628
+ * @param {string|Array} book - The book name or array.
629
+ * @returns {string} Normalized book name.
630
+ */
469
631
  bookify(book) {
470
632
  if (typeof book !== "string") {
471
633
  book = book[0]
@@ -487,6 +649,10 @@ class CodexParser {
487
649
  return bookified
488
650
  }
489
651
 
652
+ /**
653
+ * Returns parsed passages with utility methods.
654
+ * @returns {Object[]} Array of passages with methods.
655
+ */
490
656
  getPassages() {
491
657
  const passagesArray = [...this.passages]
492
658
 
@@ -553,10 +719,19 @@ class CodexParser {
553
719
  return passagesArray
554
720
  }
555
721
 
722
+ /**
723
+ * Returns the first parsed passage.
724
+ * @returns {Object|null} The first passage or null.
725
+ */
556
726
  first() {
557
727
  return this.passages.length > 0 ? this.passages[0] : null
558
728
  }
559
729
 
730
+ /**
731
+ * Formats a passage into a human-readable reference.
732
+ * @param {Object} passage - The passage object.
733
+ * @returns {Object} Formatted passage data.
734
+ */
560
735
  scripturize(passage) {
561
736
  const formatChapterVerse = (chapter, verses) => {
562
737
  if (!chapter || !verses || verses.length === 0) return ""
@@ -602,6 +777,11 @@ class CodexParser {
602
777
  }
603
778
  }
604
779
 
780
+ /**
781
+ * Combines multiple passages into a single reference.
782
+ * @param {Object[]} passages - Array of passages to combine.
783
+ * @returns {Object} Combined passage object.
784
+ */
605
785
  combine(passages) {
606
786
  if (!passages || passages.length === 0) {
607
787
  throw new Error("No passages provided to join.")
@@ -717,6 +897,11 @@ class CodexParser {
717
897
  return combined
718
898
  }
719
899
 
900
+ /**
901
+ * Merges verses into ranges or comma-separated lists.
902
+ * @param {number[]} verses - Array of verse numbers.
903
+ * @returns {string[]} Array of verse strings.
904
+ */
720
905
  mergeRanges(verses) {
721
906
  const sortedVerses = [...new Set(verses)].sort((a, b) => a - b)
722
907
  const merged = []
@@ -746,6 +931,11 @@ class CodexParser {
746
931
  return merged
747
932
  }
748
933
 
934
+ /**
935
+ * Generates a table of contents for the Bible.
936
+ * @param {string} [version="ESV"] - The Bible version.
937
+ * @returns {Object} TOC with book-chapter-verse mappings.
938
+ */
749
939
  getToc(version = "ESV") {
750
940
  const toc = {}
751
941
  this.bible.old.forEach((book) => {
@@ -775,6 +965,13 @@ class CodexParser {
775
965
  return orderedToc
776
966
  }
777
967
 
968
+ /**
969
+ * Validates a passage for correctness.
970
+ * @param {Object} passage - The passage object.
971
+ * @param {string} reference - The original reference.
972
+ * @returns {boolean|Object} True if valid, error object if invalid.
973
+ * @private
974
+ */
778
975
  _isValid(passage, reference) {
779
976
  const { book, chapter, verses, type } = passage
780
977
 
@@ -806,6 +1003,15 @@ class CodexParser {
806
1003
  return this.validateVerses(book, chapter, verses, reference)
807
1004
  }
808
1005
 
1006
+ /**
1007
+ * Validates verse numbers for a chapter.
1008
+ * @param {string} book - The book name.
1009
+ * @param {number} chapter - The chapter number.
1010
+ * @param {Array<string|number>} verses - Array of verses or ranges.
1011
+ * @param {string} reference - The original reference.
1012
+ * @returns {boolean|Object} True if valid, error object if invalid.
1013
+ * @private
1014
+ */
809
1015
  validateVerses(book, chapter, verses, reference) {
810
1016
  const chapterVerses = this.getChapterVerses(book, chapter)
811
1017
  for (const verse of verses) {
@@ -826,6 +1032,13 @@ class CodexParser {
826
1032
  return true
827
1033
  }
828
1034
 
1035
+ /**
1036
+ * Creates an error object for validation failures.
1037
+ * @param {number} code - Error code.
1038
+ * @param {string} message - Error message.
1039
+ * @returns {Object} Error object.
1040
+ * @private
1041
+ */
829
1042
  validationError(code, message) {
830
1043
  return {
831
1044
  error: true,
@@ -834,6 +1047,13 @@ class CodexParser {
834
1047
  }
835
1048
  }
836
1049
 
1050
+ /**
1051
+ * Determines the Bible version for a passage.
1052
+ * @param {string} version - The version (e.g., "lxx").
1053
+ * @param {string} testament - The testament ("old" or "new").
1054
+ * @returns {Object} Version object.
1055
+ * @private
1056
+ */
837
1057
  _handleVersion(version, testament) {
838
1058
  const effectiveVersion = this.version || version || "eng"
839
1059
  const lowerVersion = effectiveVersion.toLowerCase()
package/src/abbr.js CHANGED
@@ -158,6 +158,7 @@ const abbrevations = {
158
158
  Mal: "Malachi",
159
159
  Ml: "Malachi",
160
160
  Matt: "Matthew",
161
+ Mat: "Matthew",
161
162
  Mt: "Matthew",
162
163
  Mark: "Mark",
163
164
  Mc: "Mark",