codexparser 0.1.90 → 0.1.92

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/CodexParser.js +349 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexparser",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
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": {
@@ -352,8 +352,10 @@ class CodexParser {
352
352
  const { value, abbr } = sblEntry[1]
353
353
  const ref = passage.reference.replace(/\s*(LXX|MT)$/i, "").trim()
354
354
  parsedPassage.abbr = abbr
355
- ? `${value}. ${ref}${passage.version ? " " + passage.version : ""}`
356
- : `${value} ${ref}${passage.version ? " " + passage.version : ""}`
355
+ ? `${value}. ${ref}${
356
+ parsedPassage.version.value !== "ENG" ? " " + parsedPassage.version.value : ""
357
+ }`
358
+ : `${value} ${ref}${parsedPassage.version.value !== "ENG" ? " " + parsedPassage.version.value : ""}`
357
359
  } else {
358
360
  parsedPassage.abbr = parsedPassage.original
359
361
  }
@@ -803,6 +805,7 @@ class CodexParser {
803
805
  */
804
806
  getPassages() {
805
807
  const passagesArray = [...this.passages]
808
+ const self = this
806
809
 
807
810
  passagesArray.first = function () {
808
811
  return this.length > 0 ? this[0] : null
@@ -823,7 +826,6 @@ class CodexParser {
823
826
  return [...this]
824
827
  }
825
828
 
826
- const parser = new CodexParser()
827
829
  const groupedByBook = new Map()
828
830
 
829
831
  this.forEach((passage) => {
@@ -851,12 +853,12 @@ class CodexParser {
851
853
  if (passages.length === 1) {
852
854
  combinedPassages.push({ ...passages[0] })
853
855
  } else {
854
- const combined = parser.combine(passages)
856
+ const combined = self.combine(passages)
855
857
  combinedPassages.push(combined)
856
858
  }
857
859
  }
858
860
  } else {
859
- const combined = parser.combine(bookPassages)
861
+ const combined = self.combine(bookPassages)
860
862
  combinedPassages.push(combined)
861
863
  }
862
864
  }
@@ -864,6 +866,207 @@ class CodexParser {
864
866
  return combinedPassages
865
867
  }
866
868
 
869
+ passagesArray.getVersion = function (targetVersion) {
870
+ const targetAbbr = targetVersion.toLowerCase() === "bhs" ? "mt" : targetVersion.toLowerCase()
871
+ let versionObj
872
+ if (targetAbbr === "eng") {
873
+ versionObj = { name: "English", value: "ENG", abbreviation: "eng" }
874
+ } else if (targetAbbr === "mt") {
875
+ versionObj = { name: "Masoretic Text", value: "MT", abbreviation: "mt" }
876
+ } else if (targetAbbr === "lxx") {
877
+ versionObj = { name: "Septuagint", value: "LXX", abbreviation: "lxx" }
878
+ } else {
879
+ throw new Error("Invalid version: must be one of 'eng', 'mt', 'bhs', 'lxx'")
880
+ }
881
+
882
+ const converted = this.map((passage) => {
883
+ const cloned = JSON.parse(JSON.stringify(passage))
884
+ cloned.version = versionObj
885
+
886
+ cloned.passages.forEach((sub) => {
887
+ if (sub.versification && sub.versification[targetAbbr]) {
888
+ const [ch, v] = sub.versification[targetAbbr].split(":").map(Number)
889
+ sub.chapter = ch
890
+ sub.verse = v
891
+ }
892
+ // else remain unchanged
893
+ })
894
+
895
+ // Recompute summary fields
896
+ cloned.passages.sort((a, b) => a.chapter - b.chapter || a.verse - b.verse)
897
+
898
+ if (cloned.passages.length > 0) {
899
+ cloned.start = {
900
+ book: cloned.book,
901
+ chapter: cloned.passages[0].chapter,
902
+ verse: cloned.passages[0].verse,
903
+ }
904
+ cloned.end = {
905
+ book: cloned.book,
906
+ chapter: cloned.passages[cloned.passages.length - 1].chapter,
907
+ verse: cloned.passages[cloned.passages.length - 1].verse,
908
+ }
909
+ }
910
+
911
+ const chapterVersesMap = {}
912
+ cloned.passages.forEach((p) => {
913
+ if (!chapterVersesMap[p.chapter]) chapterVersesMap[p.chapter] = new Set()
914
+ chapterVersesMap[p.chapter].add(p.verse)
915
+ })
916
+
917
+ const sortedChs = Object.keys(chapterVersesMap)
918
+ .map(Number)
919
+ .sort((a, b) => a - b)
920
+ const chapterStrs = []
921
+
922
+ const mergeFunc = (verses) => {
923
+ const sorted = [...verses].sort((a, b) => a - b)
924
+ const merged = []
925
+ if (sorted.length === 0) return merged
926
+ let start = sorted[0]
927
+ let end = sorted[0]
928
+ for (let i = 1; i < sorted.length; i++) {
929
+ if (sorted[i] === end + 1) {
930
+ end = sorted[i]
931
+ } else {
932
+ merged.push(start === end ? `${start}` : `${start}-${end}`)
933
+ start = end = sorted[i]
934
+ }
935
+ }
936
+ merged.push(start === end ? `${start}` : `${start}-${end}`)
937
+ return merged
938
+ }
939
+
940
+ sortedChs.forEach((ch) => {
941
+ const vs = Array.from(chapterVersesMap[ch])
942
+ .filter((v) => v > 0)
943
+ .sort((a, b) => a - b)
944
+ if (vs.length > 0) {
945
+ const merged = mergeFunc(vs)
946
+ chapterStrs.push(`${ch}:${merged.join(",")}`)
947
+ }
948
+ })
949
+
950
+ if (chapterStrs.length === 0) {
951
+ return cloned // no verses, perhaps error but return as is
952
+ }
953
+
954
+ const firstCh = sortedChs[0]
955
+ const lastCh = sortedChs[sortedChs.length - 1]
956
+ cloned.chapter = firstCh
957
+
958
+ const mergedFirst = mergeFunc(chapterVersesMap[firstCh] || new Set())
959
+ cloned.verses = mergedFirst
960
+
961
+ if (firstCh !== lastCh) {
962
+ cloned.type = this.MULTI_CHAPTER_RANGE
963
+ cloned.to = {
964
+ book: cloned.book,
965
+ chapter: lastCh,
966
+ verses: mergeFunc(chapterVersesMap[lastCh] || new Set()),
967
+ }
968
+ cloned.original = `${cloned.book} ${chapterStrs.join("; ")}`
969
+ } else {
970
+ const hasRangeOrMultiple =
971
+ mergedFirst.length > 1 || (mergedFirst.length === 1 && mergedFirst[0].includes("-"))
972
+ cloned.type = hasRangeOrMultiple ? this.CHAPTER_VERSE_RANGE : this.CHAPTER_VERSE
973
+ if (cloned.to) delete cloned.to
974
+ cloned.original = `${cloned.book} ${chapterStrs[0]}`
975
+ }
976
+
977
+ const chString = chapterStrs.join("; ")
978
+ cloned.scripture = {
979
+ passage: `${cloned.book} ${chString}`,
980
+ cv: chString,
981
+ hash: `${cloned.book.toLowerCase()}_${chString
982
+ .replace(/:/g, ".")
983
+ .replace(/-/g, ".")
984
+ .replace(/[,; ]/g, ".")}`,
985
+ }
986
+
987
+ // Set abbr
988
+ const sblEntry = Object.entries(self.sblAbbreviations).find(
989
+ ([key]) => key.toLowerCase() === cloned.book.toLowerCase()
990
+ )
991
+ const suffix = versionObj.abbreviation === "eng" ? "" : ` ${versionObj.value}`
992
+ if (sblEntry) {
993
+ const { value, abbr } = sblEntry[1]
994
+ cloned.abbr = abbr
995
+ ? `${value}. ${cloned.scripture.cv}${suffix}`
996
+ : `${value} ${cloned.scripture.cv}${suffix}`
997
+ } else {
998
+ cloned.abbr = `${cloned.book} ${cloned.scripture.cv}${suffix}`
999
+ }
1000
+
1001
+ return cloned
1002
+ })
1003
+
1004
+ const newArray = [...converted]
1005
+
1006
+ newArray.first = function () {
1007
+ return this.length > 0 ? this[0] : null
1008
+ }
1009
+
1010
+ newArray.oldTestament = function () {
1011
+ return this.filter((passage) => passage.testament === "old")
1012
+ }
1013
+
1014
+ newArray.newTestament = function () {
1015
+ return this.filter((passage) => passage.testament === "new")
1016
+ }
1017
+
1018
+ newArray.combine = function (options = {}) {
1019
+ const { book = true, chapter = true } = options
1020
+
1021
+ if (!book) {
1022
+ return [...this]
1023
+ }
1024
+
1025
+ const groupedByBook = new Map()
1026
+
1027
+ this.forEach((passage) => {
1028
+ const bookKey = passage.book
1029
+ if (!groupedByBook.has(bookKey)) {
1030
+ groupedByBook.set(bookKey, [])
1031
+ }
1032
+ groupedByBook.get(bookKey).push(passage)
1033
+ })
1034
+
1035
+ const combinedPassages = []
1036
+
1037
+ for (const [book, bookPassages] of groupedByBook) {
1038
+ if (chapter) {
1039
+ const groupedByChapter = new Map()
1040
+ bookPassages.forEach((passage) => {
1041
+ const chapterKey = `${passage.book}-${passage.chapter}`
1042
+ if (!groupedByChapter.has(chapterKey)) {
1043
+ groupedByChapter.set(chapterKey, [])
1044
+ }
1045
+ groupedByChapter.get(chapterKey).push(passage)
1046
+ })
1047
+
1048
+ for (const passages of groupedByChapter.values()) {
1049
+ if (passages.length === 1) {
1050
+ combinedPassages.push({ ...passages[0] })
1051
+ } else {
1052
+ const combined = self.combine(passages)
1053
+ combinedPassages.push(combined)
1054
+ }
1055
+ }
1056
+ } else {
1057
+ const combined = self.combine(bookPassages)
1058
+ combinedPassages.push(combined)
1059
+ }
1060
+ }
1061
+
1062
+ return combinedPassages
1063
+ }
1064
+
1065
+ newArray.getVersion = passagesArray.getVersion
1066
+
1067
+ return newArray
1068
+ }
1069
+
867
1070
  return passagesArray
868
1071
  }
869
1072
 
@@ -1038,7 +1241,10 @@ class CodexParser {
1038
1241
  combined.scripture = {
1039
1242
  passage: `${combined.book} ${chapterString}`,
1040
1243
  cv: chapterString,
1041
- hash: `${combined.book.toLowerCase()}_${chapterString.replace(/:/g, ".").replace(/[,;]/g, ".")}`,
1244
+ hash: `${combined.book.toLowerCase()}_${chapterString
1245
+ .replace(/:/g, ".")
1246
+ .replace(/-/g, ".")
1247
+ .replace(/[,;]/g, ".")}`,
1042
1248
  }
1043
1249
 
1044
1250
  combined.start = {
@@ -1080,6 +1286,143 @@ class CodexParser {
1080
1286
  combined.abbr = `${combined.book} ${combined.scripture.cv}`
1081
1287
  }
1082
1288
 
1289
+ const self = this
1290
+ combined.getVersion = function (targetVersion) {
1291
+ const cloned = JSON.parse(JSON.stringify(this))
1292
+ const targetAbbr = targetVersion.toLowerCase() === "bhs" ? "mt" : targetVersion.toLowerCase()
1293
+ let versionObj
1294
+ if (targetAbbr === "eng") {
1295
+ versionObj = { name: "English", value: "ENG", abbreviation: "eng" }
1296
+ } else if (targetAbbr === "mt") {
1297
+ versionObj = { name: "Masoretic Text", value: "MT", abbreviation: "mt" }
1298
+ } else if (targetAbbr === "lxx") {
1299
+ versionObj = { name: "Septuagint", value: "LXX", abbreviation: "lxx" }
1300
+ } else {
1301
+ throw new Error("Invalid version: must be one of 'eng', 'mt', 'bhs', 'lxx'")
1302
+ }
1303
+
1304
+ cloned.version = versionObj
1305
+
1306
+ cloned.passages.forEach((sub) => {
1307
+ if (sub.versification && sub.versification[targetAbbr]) {
1308
+ const [ch, v] = sub.versification[targetAbbr].split(":").map(Number)
1309
+ sub.chapter = ch
1310
+ sub.verse = v
1311
+ }
1312
+ // else remain unchanged
1313
+ })
1314
+
1315
+ // Recompute summary fields
1316
+ cloned.passages.sort((a, b) => a.chapter - b.chapter || a.verse - b.verse)
1317
+
1318
+ if (cloned.passages.length > 0) {
1319
+ cloned.start = {
1320
+ book: cloned.book,
1321
+ chapter: cloned.passages[0].chapter,
1322
+ verse: cloned.passages[0].verse,
1323
+ }
1324
+ cloned.end = {
1325
+ book: cloned.book,
1326
+ chapter: cloned.passages[cloned.passages.length - 1].chapter,
1327
+ verse: cloned.passages[cloned.passages.length - 1].verse,
1328
+ }
1329
+ }
1330
+
1331
+ const chapterVersesMap = {}
1332
+ cloned.passages.forEach((p) => {
1333
+ if (!chapterVersesMap[p.chapter]) chapterVersesMap[p.chapter] = new Set()
1334
+ chapterVersesMap[p.chapter].add(p.verse)
1335
+ })
1336
+
1337
+ const sortedChs = Object.keys(chapterVersesMap)
1338
+ .map(Number)
1339
+ .sort((a, b) => a - b)
1340
+ const chapterStrs = []
1341
+
1342
+ const mergeFunc = (verses) => {
1343
+ const sorted = [...verses].sort((a, b) => a - b)
1344
+ const merged = []
1345
+ if (sorted.length === 0) return merged
1346
+ let start = sorted[0]
1347
+ let end = sorted[0]
1348
+ for (let i = 1; i < sorted.length; i++) {
1349
+ if (sorted[i] === end + 1) {
1350
+ end = sorted[i]
1351
+ } else {
1352
+ merged.push(start === end ? `${start}` : `${start}-${end}`)
1353
+ start = end = sorted[i]
1354
+ }
1355
+ }
1356
+ merged.push(start === end ? `${start}` : `${start}-${end}`)
1357
+ return merged
1358
+ }
1359
+
1360
+ sortedChs.forEach((ch) => {
1361
+ const vs = Array.from(chapterVersesMap[ch])
1362
+ .filter((v) => v > 0)
1363
+ .sort((a, b) => a - b)
1364
+ if (vs.length > 0) {
1365
+ const merged = mergeFunc(vs)
1366
+ chapterStrs.push(`${ch}:${merged.join(",")}`)
1367
+ }
1368
+ })
1369
+
1370
+ if (chapterStrs.length === 0) {
1371
+ return cloned // no verses, perhaps error but return as is
1372
+ }
1373
+
1374
+ const firstCh = sortedChs[0]
1375
+ const lastCh = sortedChs[sortedChs.length - 1]
1376
+ cloned.chapter = firstCh
1377
+
1378
+ const mergedFirst = mergeFunc(chapterVersesMap[firstCh] || new Set())
1379
+ cloned.verses = mergedFirst
1380
+
1381
+ if (firstCh !== lastCh) {
1382
+ cloned.type = "multi_chapter_verse_range"
1383
+ cloned.to = {
1384
+ book: cloned.book,
1385
+ chapter: lastCh,
1386
+ verses: mergeFunc(chapterVersesMap[lastCh] || new Set()),
1387
+ }
1388
+ cloned.original = `${cloned.book} ${chapterStrs.join("; ")}`
1389
+ } else {
1390
+ const hasRangeOrMultiple =
1391
+ mergedFirst.length > 1 || (mergedFirst.length === 1 && mergedFirst[0].includes("-"))
1392
+ cloned.type = hasRangeOrMultiple ? "chapter_verse_range" : "chapter_verse"
1393
+ if (cloned.to) delete cloned.to
1394
+ cloned.original = `${cloned.book} ${chapterStrs[0]}`
1395
+ }
1396
+
1397
+ const chString = chapterStrs.join("; ")
1398
+ cloned.scripture = {
1399
+ passage: `${cloned.book} ${chString}`,
1400
+ cv: chString,
1401
+ hash: `${cloned.book.toLowerCase()}_${chString
1402
+ .replace(/:/g, ".")
1403
+ .replace(/-/g, ".")
1404
+ .replace(/[,; ]/g, ".")}`,
1405
+ }
1406
+
1407
+ // Set abbr
1408
+ const sblEntry = Object.entries(self.sblAbbreviations).find(
1409
+ ([key]) => key.toLowerCase() === cloned.book.toLowerCase()
1410
+ )
1411
+ const suffix = versionObj.abbreviation === "eng" ? "" : ` ${versionObj.value}`
1412
+ if (sblEntry) {
1413
+ const { value, abbr } = sblEntry[1]
1414
+ cloned.abbr = abbr
1415
+ ? `${value}. ${cloned.scripture.cv}${suffix}`
1416
+ : `${value} ${cloned.scripture.cv}${suffix}`
1417
+ } else {
1418
+ cloned.abbr = `${cloned.book} ${cloned.scripture.cv}${suffix}`
1419
+ }
1420
+
1421
+ cloned.getVersion = this.getVersion
1422
+
1423
+ return cloned
1424
+ }
1425
+
1083
1426
  return combined
1084
1427
  }
1085
1428