codexparser 0.1.89 → 0.1.91

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 +260 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexparser",
3
- "version": "0.1.89",
3
+ "version": "0.1.91",
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
 
@@ -957,23 +1160,35 @@ class CodexParser {
957
1160
 
958
1161
  passages.forEach((passage) => {
959
1162
  passage.passages.forEach((p) => {
960
- if (!chapterVerses[p.chapter]) {
961
- chapterVerses[p.chapter] = new Set()
1163
+ let normChapter = p.chapter
1164
+ let normVerse = p.verse
1165
+ if (p.versification && p.versification.eng) {
1166
+ const [c, v] = p.versification.eng.split(":").map(Number)
1167
+ normChapter = c
1168
+ normVerse = v
962
1169
  }
963
- chapterVerses[p.chapter].add(p.verse)
964
- combined.passages.push(p)
965
-
966
- if (firstChapter === null || p.chapter < firstChapter) {
967
- firstChapter = p.chapter
968
- firstVerse = p.verse
969
- } else if (p.chapter === firstChapter && (firstVerse === null || p.verse < firstVerse)) {
970
- firstVerse = p.verse
1170
+ if (!chapterVerses[normChapter]) {
1171
+ chapterVerses[normChapter] = new Set()
971
1172
  }
972
- if (lastChapter === null || p.chapter > lastChapter) {
973
- lastChapter = p.chapter
974
- lastVerse = p.verse
975
- } else if (p.chapter === lastChapter && (lastVerse === null || p.verse > lastVerse)) {
976
- lastVerse = p.verse
1173
+ chapterVerses[normChapter].add(normVerse)
1174
+ combined.passages.push({
1175
+ book: p.book,
1176
+ chapter: normChapter,
1177
+ verse: normVerse,
1178
+ versification: p.versification,
1179
+ })
1180
+
1181
+ if (firstChapter === null || normChapter < firstChapter) {
1182
+ firstChapter = normChapter
1183
+ firstVerse = normVerse
1184
+ } else if (normChapter === firstChapter && (firstVerse === null || normVerse < firstVerse)) {
1185
+ firstVerse = normVerse
1186
+ }
1187
+ if (lastChapter === null || normChapter > lastChapter) {
1188
+ lastChapter = normChapter
1189
+ lastVerse = normVerse
1190
+ } else if (normChapter === lastChapter && (lastVerse === null || normVerse > lastVerse)) {
1191
+ lastVerse = normVerse
977
1192
  }
978
1193
  })
979
1194
  })
@@ -1003,12 +1218,18 @@ class CodexParser {
1003
1218
  throw new Error("No valid verses found in passages.")
1004
1219
  }
1005
1220
 
1221
+ combined.chapter = firstChapter
1222
+
1006
1223
  if (firstChapter !== lastChapter) {
1007
1224
  combined.type = this.MULTI_CHAPTER_RANGE
1008
1225
  combined.to = {
1009
1226
  book: combined.book,
1010
1227
  chapter: lastChapter,
1011
- verses: this.mergeRanges(Array.from(chapterVerses[lastChapter]).filter((verse) => verse > 0)),
1228
+ verses: this.mergeRanges(
1229
+ Array.from(chapterVerses[lastChapter])
1230
+ .filter((verse) => verse > 0)
1231
+ .sort((a, b) => a - b)
1232
+ ),
1012
1233
  }
1013
1234
  combined.original = `${combined.book} ${chapterStrings.join("; ")}`
1014
1235
  } else {
@@ -1020,7 +1241,10 @@ class CodexParser {
1020
1241
  combined.scripture = {
1021
1242
  passage: `${combined.book} ${chapterString}`,
1022
1243
  cv: chapterString,
1023
- hash: `${combined.book.toLowerCase()}_${chapterString.replace(/:/g, ".").replace(/[,;]/g, ".")}`,
1244
+ hash: `${combined.book.toLowerCase()}_${chapterString
1245
+ .replace(/:/g, ".")
1246
+ .replace(/-/g, ".")
1247
+ .replace(/[,;]/g, ".")}`,
1024
1248
  }
1025
1249
 
1026
1250
  combined.start = {
@@ -1048,6 +1272,20 @@ class CodexParser {
1048
1272
  delete combined.to
1049
1273
  }
1050
1274
 
1275
+ // Prefer English version
1276
+ combined.version = { name: "English", value: "ENG", abbreviation: "eng" }
1277
+
1278
+ // Set abbr without version suffix
1279
+ const sblEntry = Object.entries(this.sblAbbreviations).find(
1280
+ ([key]) => key.toLowerCase() === combined.book.toLowerCase()
1281
+ )
1282
+ if (sblEntry) {
1283
+ const { value, abbr } = sblEntry[1]
1284
+ combined.abbr = abbr ? `${value}. ${combined.scripture.cv}` : `${value} ${combined.scripture.cv}`
1285
+ } else {
1286
+ combined.abbr = `${combined.book} ${combined.scripture.cv}`
1287
+ }
1288
+
1051
1289
  return combined
1052
1290
  }
1053
1291