@gracious.tech/bible-references
Version:
Bible reference detection, parsing, and rendering that supports any language.
2,201 lines (2,192 loc) • 40.1 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// src/index.ts
var src_exports = {};
__export(src_exports, {
PassageReference: () => PassageReference,
book_abbrev_english: () => book_abbrev_english,
book_names_english: () => book_names_english,
books_ordered: () => books_ordered,
detect_references: () => detect_references,
english_abbrev_exclude: () => english_abbrev_exclude,
english_abbrev_include: () => english_abbrev_include,
get_chapters: () => get_chapters,
get_verses: () => get_verses,
last_verse: () => last_verse,
references_to_string: () => references_to_string,
simplify_references: () => simplify_references,
sort_references: () => sort_references
});
module.exports = __toCommonJS(src_exports);
// src/utils.ts
function parse_int(input, min, max) {
let int = parseInt(input, 10);
if (Number.isNaN(int)) {
return null;
}
if (min !== void 0) {
int = Math.max(int, min);
}
if (max !== void 0) {
int = Math.min(int, max);
}
return int;
}
// src/data.ts
var books_ordered = Object.freeze([
"gen",
"exo",
"lev",
"num",
"deu",
"jos",
"jdg",
"rut",
"1sa",
"2sa",
"1ki",
"2ki",
"1ch",
"2ch",
"ezr",
"neh",
"est",
"job",
"psa",
"pro",
"ecc",
"sng",
"isa",
"jer",
"lam",
"ezk",
"dan",
"hos",
"jol",
"amo",
"oba",
"jon",
"mic",
"nam",
"hab",
"zep",
"hag",
"zec",
"mal",
"mat",
"mrk",
"luk",
"jhn",
"act",
"rom",
"1co",
"2co",
"gal",
"eph",
"php",
"col",
"1th",
"2th",
"1ti",
"2ti",
"tit",
"phm",
"heb",
"jas",
"1pe",
"2pe",
"1jn",
"2jn",
"3jn",
"jud",
"rev"
]);
var book_names_english = Object.freeze({
"gen": "Genesis",
"exo": "Exodus",
"lev": "Leviticus",
"num": "Numbers",
"deu": "Deuteronomy",
"jos": "Joshua",
"jdg": "Judges",
"rut": "Ruth",
"1sa": "1 Samuel",
"2sa": "2 Samuel",
"1ki": "1 Kings",
"2ki": "2 Kings",
"1ch": "1 Chronicles",
"2ch": "2 Chronicles",
"ezr": "Ezra",
"neh": "Nehemiah",
"est": "Esther",
"job": "Job",
"psa": "Psalms",
"pro": "Proverbs",
"ecc": "Ecclesiastes",
"sng": "Song of Songs",
"isa": "Isaiah",
"jer": "Jeremiah",
"lam": "Lamentations",
"ezk": "Ezekiel",
"dan": "Daniel",
"hos": "Hosea",
"jol": "Joel",
"amo": "Amos",
"oba": "Obadiah",
"jon": "Jonah",
"mic": "Micah",
"nam": "Nahum",
"hab": "Habakkuk",
"zep": "Zephaniah",
"hag": "Haggai",
"zec": "Zechariah",
"mal": "Malachi",
"mat": "Matthew",
"mrk": "Mark",
"luk": "Luke",
"jhn": "John",
"act": "Acts",
"rom": "Romans",
"1co": "1 Corinthians",
"2co": "2 Corinthians",
"gal": "Galatians",
"eph": "Ephesians",
"php": "Philippians",
"col": "Colossians",
"1th": "1 Thessalonians",
"2th": "2 Thessalonians",
"1ti": "1 Timothy",
"2ti": "2 Timothy",
"tit": "Titus",
"phm": "Philemon",
"heb": "Hebrews",
"jas": "James",
"1pe": "1 Peter",
"2pe": "2 Peter",
"1jn": "1 John",
"2jn": "2 John",
"3jn": "3 John",
"jud": "Jude",
"rev": "Revelation"
});
var book_abbrev_english = Object.freeze({
"gen": "Gen",
"exo": "Exo",
"lev": "Lev",
"num": "Num",
"deu": "Deut",
"jos": "Josh",
"jdg": "Judg",
"rut": "Ruth",
"1sa": "1 Sam",
"2sa": "2 Sam",
"1ki": "1 King",
"2ki": "2 King",
"1ch": "1 Chr",
"2ch": "2 Chr",
"ezr": "Ezra",
"neh": "Neh",
"est": "Est",
"job": "Job",
"psa": "Psalm",
"pro": "Prov",
"ecc": "Ecc",
"sng": "Song",
"isa": "Isa",
"jer": "Jer",
"lam": "Lam",
"ezk": "Ezek",
"dan": "Dan",
"hos": "Hos",
"jol": "Joel",
"amo": "Amos",
"oba": "Obad",
"jon": "Jonah",
"mic": "Micah",
"nam": "Nahum",
"hab": "Hab",
"zep": "Zeph",
"hag": "Hag",
"zec": "Zech",
"mal": "Mal",
"mat": "Matt",
"mrk": "Mark",
"luk": "Luke",
"jhn": "John",
"act": "Acts",
"rom": "Rom",
"1co": "1 Cor",
"2co": "2 Cor",
"gal": "Gal",
"eph": "Eph",
"php": "Phil",
"col": "Col",
"1th": "1 Thes",
"2th": "2 Thes",
"1ti": "1 Tim",
"2ti": "2 Tim",
"tit": "Titus",
"phm": "Philem",
"heb": "Heb",
"jas": "James",
"1pe": "1 Pet",
"2pe": "2 Pet",
"1jn": "1 John",
"2jn": "2 John",
"3jn": "3 John",
"jud": "Jude",
"rev": "Rev"
});
var english_abbrev_include = Object.freeze([
// [code, abbrev]
["num", "nm"],
["ezr", "ez"],
["mic", "mc"],
["hab", "hb"],
["jhn", "jn"],
["php", "phil"],
["phm", "pm"],
["jas", "jm"],
["jud", "jud"],
["jud", "jd"]
]);
var english_abbrev_exclude = Object.freeze(["is", "so", "at", "am", "me", "he", "hi"]);
// src/last_verse.ts
var last_verse = Object.freeze({
"1ch": [
54,
55,
24,
43,
26,
81,
40,
40,
44,
14,
47,
40,
14,
17,
29,
43,
27,
17,
19,
8,
30,
19,
32,
31,
31,
32,
34,
21,
30
],
"1co": [
31,
16,
23,
21,
13,
20,
40,
13,
27,
33,
34,
31,
13,
40,
58,
24
],
"1jn": [
10,
29,
24,
21,
21
],
"1ki": [
53,
46,
28,
34,
18,
38,
51,
66,
28,
29,
43,
33,
34,
31,
34,
34,
24,
46,
21,
43,
29,
53
],
"1pe": [
25,
25,
22,
19,
14
],
"1sa": [
28,
36,
21,
22,
12,
21,
17,
22,
27,
27,
15,
25,
23,
52,
35,
23,
58,
30,
24,
42,
15,
23,
29,
22,
44,
25,
12,
25,
11,
31,
13
],
"1th": [
10,
20,
13,
18,
28
],
"1ti": [
20,
15,
16,
16,
25,
21
],
"2ch": [
17,
18,
17,
22,
14,
42,
22,
18,
31,
19,
23,
16,
22,
15,
19,
14,
19,
34,
11,
37,
20,
12,
21,
27,
28,
23,
9,
27,
36,
27,
21,
33,
25,
33,
27,
23
],
"2co": [
24,
17,
18,
18,
21,
18,
16,
24,
15,
18,
33,
21,
14
],
"2jn": [
13
],
"2ki": [
18,
25,
27,
44,
27,
33,
20,
29,
37,
36,
21,
21,
25,
29,
38,
20,
41,
37,
37,
21,
26,
20,
37,
20,
30
],
"2pe": [
21,
22,
18
],
"2sa": [
27,
32,
39,
12,
25,
23,
29,
18,
13,
19,
27,
31,
39,
33,
37,
23,
29,
33,
43,
26,
22,
51,
39,
25
],
"2th": [
12,
17,
18
],
"2ti": [
18,
26,
17,
22
],
"3jn": [
15
// Half of translations merge 15 into 14 (BSB,HCSB,etc) and half don't (NIV,ESV,etc)
],
"act": [
26,
47,
26,
37,
42,
15,
60,
40,
43,
48,
30,
25,
52,
28,
41,
40,
34,
28,
41,
38,
40,
30,
35,
27,
27,
32,
44,
31
],
"amo": [
15,
16,
15,
13,
27,
14,
17,
14,
15
],
"col": [
29,
23,
25,
18
],
"dan": [
21,
49,
30,
37,
31,
28,
28,
27,
27,
21,
45,
13
],
"deu": [
46,
37,
29,
49,
33,
25,
26,
20,
29,
22,
32,
32,
18,
29,
23,
22,
20,
22,
21,
20,
23,
30,
25,
22,
19,
19,
26,
68,
29,
20,
30,
52,
29,
12
],
"ecc": [
18,
26,
22,
16,
20,
12,
29,
17,
18,
20,
10,
14
],
"eph": [
23,
22,
21,
32,
33,
24
],
"est": [
22,
23,
15,
17,
14,
14,
10,
17,
32,
3
],
"exo": [
22,
25,
22,
31,
23,
30,
25,
32,
35,
29,
10,
51,
22,
31,
27,
36,
16,
27,
25,
26,
36,
31,
33,
18,
40,
37,
21,
43,
46,
38,
18,
35,
23,
35,
35,
38,
29,
31,
43,
38
],
"ezk": [
28,
10,
27,
17,
17,
14,
27,
18,
11,
22,
25,
28,
23,
23,
8,
63,
24,
32,
14,
49,
32,
31,
49,
27,
17,
21,
36,
26,
21,
26,
18,
32,
33,
31,
15,
38,
28,
23,
29,
49,
26,
20,
27,
31,
25,
24,
23,
35
],
"ezr": [
11,
70,
13,
24,
17,
22,
28,
36,
15,
44
],
"gal": [
24,
21,
29,
31,
26,
18
],
"gen": [
31,
25,
24,
26,
32,
22,
24,
22,
29,
32,
32,
20,
18,
24,
21,
16,
27,
33,
38,
18,
34,
24,
20,
67,
34,
35,
46,
22,
35,
43,
55,
32,
20,
31,
29,
43,
36,
30,
23,
23,
57,
38,
34,
34,
28,
34,
31,
22,
33,
26
],
"hab": [
17,
20,
19
],
"hag": [
15,
23
],
"heb": [
14,
18,
19,
16,
14,
20,
28,
13,
28,
39,
40,
29,
25
],
"hos": [
11,
23,
5,
19,
15,
11,
16,
14,
17,
15,
12,
14,
16,
9
],
"isa": [
31,
22,
26,
6,
30,
13,
25,
22,
21,
34,
16,
6,
22,
32,
9,
14,
14,
7,
25,
6,
17,
25,
18,
23,
12,
21,
13,
29,
24,
33,
9,
20,
24,
17,
10,
22,
38,
22,
8,
31,
29,
25,
28,
28,
25,
13,
15,
22,
26,
11,
23,
15,
12,
17,
13,
12,
21,
14,
21,
22,
11,
12,
19,
12,
25,
24
],
"jas": [
27,
26,
18,
17,
20
],
"jdg": [
36,
23,
31,
24,
31,
40,
25,
35,
57,
18,
40,
15,
25,
20,
20,
31,
13,
31,
30,
48,
25
],
"jer": [
19,
37,
25,
31,
31,
30,
34,
22,
26,
25,
23,
17,
27,
22,
21,
21,
27,
23,
15,
18,
14,
30,
40,
10,
38,
24,
22,
17,
32,
24,
40,
44,
26,
22,
19,
32,
21,
28,
18,
16,
18,
22,
13,
30,
5,
28,
7,
47,
39,
46,
64,
34
],
"jhn": [
51,
25,
36,
54,
47,
71,
53,
59,
41,
42,
57,
50,
38,
31,
27,
33,
26,
40,
42,
31,
25
],
"job": [
22,
13,
26,
21,
27,
30,
21,
22,
35,
22,
20,
25,
28,
22,
35,
22,
16,
21,
29,
29,
34,
30,
17,
25,
6,
14,
23,
28,
25,
31,
40,
22,
33,
37,
16,
33,
24,
41,
30,
24,
34,
17
],
"jol": [
20,
32,
21
],
"jon": [
17,
10,
10,
11
],
"jos": [
18,
24,
17,
24,
15,
27,
26,
35,
27,
43,
23,
24,
33,
15,
63,
10,
18,
28,
51,
9,
45,
34,
16,
33
],
"jud": [
25
],
"lam": [
22,
22,
66,
22,
22
],
"lev": [
17,
16,
17,
35,
19,
30,
38,
36,
24,
20,
47,
8,
59,
57,
33,
34,
16,
30,
37,
27,
24,
33,
44,
23,
55,
46,
34
],
"luk": [
80,
52,
38,
44,
39,
49,
50,
56,
62,
42,
54,
59,
35,
35,
32,
31,
37,
43,
48,
47,
38,
71,
56,
53
],
"mal": [
14,
17,
18,
6
],
"mat": [
25,
23,
17,
25,
48,
34,
29,
34,
38,
42,
30,
50,
58,
36,
39,
28,
27,
35,
30,
34,
46,
46,
39,
51,
46,
75,
66,
20
],
"mic": [
16,
13,
12,
13,
15,
16,
20
],
"mrk": [
45,
28,
35,
41,
43,
56,
37,
38,
50,
52,
33,
44,
37,
72,
47,
// Mark's ending: 1-8 common, 9-20 long ending
// Some also add the alternate short ending to end of long ending with 21-22
// Not supporting 21-22 as whether ending abrupt/long/short it definitely wasn't both
20
],
"nam": [
15,
13,
19
],
"neh": [
11,
20,
32,
23,
19,
19,
73,
18,
38,
39,
36,
47,
31
],
"num": [
54,
34,
51,
49,
31,
27,
89,
26,
23,
36,
35,
16,
33,
45,
41,
50,
13,
32,
22,
29,
35,
41,
30,
25,
18,
65,
23,
31,
40,
16,
54,
42,
56,
29,
34,
13
],
"oba": [
21
],
"phm": [
25
],
"php": [
30,
30,
21,
23
],
"pro": [
33,
22,
35,
27,
23,
35,
27,
36,
18,
32,
31,
28,
25,
35,
33,
33,
28,
24,
29,
30,
31,
29,
35,
34,
28,
28,
27,
28,
27,
33,
31
],
"psa": [
6,
12,
8,
8,
12,
10,
17,
9,
20,
18,
7,
8,
6,
7,
5,
11,
15,
50,
14,
9,
13,
31,
6,
10,
22,
12,
14,
9,
11,
12,
24,
11,
22,
22,
28,
12,
40,
22,
13,
17,
13,
11,
5,
26,
17,
11,
9,
14,
20,
23,
19,
9,
6,
7,
23,
13,
11,
11,
17,
12,
8,
12,
11,
10,
13,
20,
7,
35,
36,
5,
24,
20,
28,
23,
10,
12,
20,
72,
13,
19,
16,
8,
18,
12,
13,
17,
7,
18,
52,
17,
16,
15,
5,
23,
11,
13,
12,
9,
9,
5,
8,
28,
22,
35,
45,
48,
43,
13,
31,
7,
10,
10,
9,
8,
18,
19,
2,
29,
176,
7,
8,
9,
4,
8,
5,
6,
5,
6,
8,
8,
3,
18,
3,
3,
21,
26,
9,
8,
24,
13,
10,
7,
12,
15,
21,
10,
20,
14,
9,
6
],
"rev": [
20,
29,
22,
11,
14,
17,
17,
13,
21,
11,
19,
18,
// 12:18 Half of translations merge 18 into 17 (NIV,ESV...) and half don't (NET,NLT...)
18,
20,
8,
21,
18,
24,
21,
15,
27,
21
],
"rom": [
32,
29,
31,
25,
21,
23,
25,
39,
33,
21,
36,
21,
14,
// Some translations put Romans 16:25–27 at end of chapter 14 as Rom 14:24-26
// But only 1 of 6 early manuscripts support that, and only ~24 of 1000+ translations
// So best to append contents un-numbered to Rom 14:23 if it is included
23,
// Chapter 14
33,
27
],
"rut": [
22,
23,
18,
22
],
"sng": [
17,
17,
11,
16,
16,
13,
13,
14
],
"tit": [
16,
15,
15
],
"zec": [
21,
13,
10,
14,
11,
15,
14,
23,
17,
12,
17,
14,
9,
21
],
"zep": [
18,
15,
20
]
});
// src/passage.ts
var single_chapter_books = ["2jn", "3jn", "jud", "oba", "phm"];
function _verses_str_to_obj(ref) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
ref = ref.replace(/ /g, "").replace(/\./g, ":").replace(/:/gu, ":").replace(new RegExp("\\p{Dash}", "gu"), "-");
let start_chapter;
let start_verse;
let end_chapter;
let end_verse;
if (!ref.includes(":")) {
const parts = ref.split("-");
start_chapter = (_a = parse_int(parts[0])) != null ? _a : void 0;
end_chapter = (_c = parse_int((_b = parts[1]) != null ? _b : "")) != null ? _c : void 0;
} else {
const parts = ref.split("-");
const start_parts = parts[0].split(":");
start_chapter = (_d = parse_int(start_parts[0])) != null ? _d : void 0;
start_verse = (_f = parse_int((_e = start_parts[1]) != null ? _e : "")) != null ? _f : void 0;
if (parts[1]) {
const end_parts = parts[1].split(":");
if (end_parts.length > 1) {
end_chapter = (_g = parse_int(end_parts[0])) != null ? _g : void 0;
end_verse = (_h = parse_int(end_parts[1])) != null ? _h : void 0;
} else {
end_verse = (_i = parse_int(end_parts[0])) != null ? _i : void 0;
}
}
}
return { start_chapter, start_verse, end_chapter, end_verse };
}
function _detect_book(input, book_names, exclude_book_names = [], match_from_start = true) {
const clean = (string) => {
return string.trim().toLowerCase().replace(/^i /, "1").replace("1st ", "1").replace("first ", "1").replace(/^ii /, "2").replace("2nd ", "2").replace("second ", "2").replace(/^iii /, "3").replace("3rd ", "3").replace("third ", "3").replace(/[^\d\p{Letter}]/gui, "");
};
input = clean(input);
if (!input) {
return null;
}
exclude_book_names = exclude_book_names.map((name) => clean(name));
if (exclude_book_names.includes(input)) {
return null;
}
if (books_ordered.includes(input)) {
return input;
}
const normalised = book_names.map(([code, name]) => [code, clean(name)]).filter(([code, name]) => name);
const matches = [];
for (const [code, name] of normalised) {
if (input === name) {
return code;
} else if (name.startsWith(input)) {
matches.push([code, name]);
}
}
if (matches.length === 1) {
return matches[0][0];
} else if (matches.length) {
return null;
}
let input_regex_str = input.split("").join(".{0,4}");
if (match_from_start) {
if (["1", "2", "3"].includes(input[0])) {
input_regex_str = "^" + input[0] + input.slice(1).split("").join(".{0,4}");
} else {
input_regex_str = "^" + input_regex_str;
}
}
const input_regex = new RegExp(input_regex_str);
const fuzzy_matches = normalised.filter(([code, name]) => input_regex.test(name));
if (fuzzy_matches.length === 1) {
return fuzzy_matches[0][0];
}
return null;
}
var PassageReference = class _PassageReference {
constructor(book_or_obj, chapter, verse) {
__publicField(this, "type");
__publicField(this, "range");
__publicField(this, "book");
__publicField(this, "ot");
__publicField(this, "nt");
__publicField(this, "start_chapter");
__publicField(this, "start_verse");
__publicField(this, "end_chapter");
__publicField(this, "end_verse");
__publicField(this, "args_valid");
// Whether the original input was valid or not
__publicField(this, "_args");
var _a, _b, _c, _d, _e;
if (typeof book_or_obj !== "string") {
this._args = {
book: book_or_obj.book,
start_chapter: book_or_obj.start_chapter,
start_verse: book_or_obj.start_verse,
end_chapter: book_or_obj.end_chapter,
end_verse: book_or_obj.end_verse
};
} else {
this._args = {
book: book_or_obj,
start_chapter: chapter,
start_verse: verse
};
}
const chapters_given = typeof this._args.start_chapter === "number" || typeof this._args.end_chapter === "number";
const verses_given = typeof this._args.start_verse === "number" || typeof this._args.end_verse === "number";
this.book = this._args.book;
this.start_chapter = (_a = this._args.start_chapter) != null ? _a : 1;
this.start_verse = (_b = this._args.start_verse) != null ? _b : 1;
this.end_chapter = (_d = (_c = this._args.end_chapter) != null ? _c : this._args.start_chapter) != null ? _d : 1;
this.end_verse = (_e = this._args.end_verse) != null ? _e : this._args.end_chapter ? 999 : 1;
if (books_ordered.indexOf(this.book) === -1) {
this.book = "gen";
}
const last_verse_book = last_verse[this.book];
if (this.start_chapter < 1) {
this.start_chapter = 1;
this.start_verse = 1;
} else if (this.start_chapter > last_verse_book.length) {
this.start_chapter = last_verse_book.length;
this.start_verse = last_verse_book[last_verse_book.length - 1];
}
this.start_verse = Math.min(
Math.max(this.start_verse, 1),
last_verse_book[this.start_chapter - 1]
);
if (this.end_chapter < this.start_chapter || this.end_chapter === this.start_chapter && this.end_verse < this.start_verse) {
this.end_chapter = this.start_chapter;
this.end_verse = this.start_verse;
}
if (this.end_chapter > last_verse_book.length) {
this.end_chapter = last_verse_book.length;
this.end_verse = last_verse_book[last_verse_book.length - 1];
}
this.end_verse = Math.min(Math.max(this.end_verse, 1), last_verse_book[this.end_chapter - 1]);
const chapters_same = this.start_chapter === this.end_chapter;
const verses_same = this.start_verse === this.end_verse;
if (chapters_same && verses_same) {
this.type = chapters_given ? verses_given ? "verse" : "chapter" : "book";
} else {
this.type = chapters_same ? "range_verses" : verses_given ? "range_multi" : "range_chapters";
}
if (this.type === "range_multi" && this.start_verse === 1 && this.end_verse === last_verse_book[this.end_chapter - 1]) {
this.type = "range_chapters";
}
if (single_chapter_books.includes(this.book) && this.type === "chapter") {
this.type = "book";
}
this.range = this.type.startsWith("range_");
this.ot = books_ordered.indexOf(this.book) < 39;
this.nt = !this.ot;
const determine_args_valid = () => {
if (this._args.book !== this.book) {
return false;
}
const props = ["start_chapter", "start_verse", "end_chapter", "end_verse"];
for (const prop of props) {
if (Number.isInteger(this._args[prop]) && this._args[prop] !== this[prop]) {
return false;
}
}
if (!this._args.start_chapter && (this._args.end_chapter || this._args.start_verse || this._args.end_verse)) {
return false;
}
if (this._args.end_verse && !this._args.start_verse) {
return false;
}
if (this._args.start_verse && this._args.end_chapter && !this._args.end_verse) {
return false;
}
return true;
};
this.args_valid = determine_args_valid();
}
// Parse passage reference string
// book_names can be a list if a single book has multiple names [["gen", "Genesis"], ...]
static from_string(reference, book_names, exclude_book_names, min_chars = 2, match_from_start = true) {
if (!book_names) {
book_names = [...Object.entries(book_names_english), ...english_abbrev_include];
if (!exclude_book_names) {
exclude_book_names = [...english_abbrev_exclude];
}
}
const book_names_list = Array.isArray(book_names) ? book_names : Object.entries(book_names);
reference = reference.trim();
let verses_start = reference.slice(1).search(/\d/) + 1;
if (verses_start === 0) {
verses_start = reference.length;
}
const book_str = reference.slice(0, verses_start).trim();
if (book_str.length < min_chars) {
return null;
}
const book_code = _detect_book(
book_str,
book_names_list,
exclude_book_names,
match_from_start
);
if (!book_code) {
return null;
}
const verses_str = reference.slice(verses_start);
let verses = _verses_str_to_obj(verses_str);
if (single_chapter_books.includes(book_code) && verses.start_chapter && verses.start_verse === void 0 && verses.end_verse === void 0) {
verses = _verses_str_to_obj("1:" + verses_str);
}
return new _PassageReference(__spreadValues({ book: book_code }, verses));
}
// Return a new reference that extends from start of first ref to end of second ref
// NOTE get_end() used so that book/chapter types are treated as ranges
static from_refs(start, end) {
const actual_end = end.get_end();
return new _PassageReference({
book: start.book,
start_chapter: start.start_chapter,
start_verse: start.start_verse,
end_chapter: actual_end.end_chapter,
end_verse: actual_end.end_verse
});
}
// Restore reference from serialized form
// While deserializing using `from_string()` works, this method is faster & never returns null
static from_serialized(code) {
const book = code.slice(0, 3);
const verses = code.slice(3);
return new _PassageReference(__spreadValues({ book }, _verses_str_to_obj(verses)));
}
// Get name for book (defaults to English when book names not provided)
get_book_string(book_names = {}) {
return book_names[this.book] || book_names_english[this.book];
}
// Get string representation of verses
get_verses_string(verse_sep = ":", range_sep = "-") {
if (this.type === "book") {
return "";
} else if (this.type === "chapter") {
return `${this.start_chapter}`;
} else if (this.type === "range_chapters") {
return `${this.start_chapter}${range_sep}${this.end_chapter}`;
} else if (this.type === "verse") {
return `${this.start_chapter}${verse_sep}${this.start_verse}`;
}
let out = `${this.start_chapter}${verse_sep}${this.start_verse}${range_sep}`;
if (this.end_chapter !== this.start_chapter) {
out += `${this.end_chapter}${verse_sep}`;
}
return out + `${this.end_verse}`;
}
// Format passage reference to a readable string
toString(book_names = {}, verse_sep = ":", range_sep = "-") {
const out = this.get_book_string(book_names) + " " + this.get_verses_string(verse_sep, range_sep);
return out.trim();
}
// Serialize reference to string that can be restored by `from_serialized()`
to_serialized() {
return this.book + this.get_verses_string();
}
// Whether this reference is the same as the one provided
equals(ref) {
return this.type === ref.type && this.book === ref.book && this.start_chapter === ref.start_chapter && this.start_verse === ref.start_verse && this.end_chapter === ref.end_chapter && this.end_verse === ref.end_verse;
}
is_before(chapter_or_ref, verse) {
const ref = typeof chapter_or_ref === "number" ? new _PassageReference(this.book, chapter_or_ref, verse) : chapter_or_ref;
if (this.book !== ref.book) {
return books_ordered.indexOf(this.book) < books_ordered.indexOf(ref.book);
}
const this_end = this.get_end();
return this_end.end_chapter < ref.start_chapter || this_end.end_chapter === ref.start_chapter && this_end.end_verse < ref.start_verse;
}
is_after(chapter_or_ref, verse) {
const ref = typeof chapter_or_ref === "number" ? new _PassageReference(this.book, chapter_or_ref, verse) : chapter_or_ref;
if (this.book !== ref.book) {
return books_ordered.indexOf(this.book) > books_ordered.indexOf(ref.book);
}
const ref_end = ref.get_end();
return this.start_chapter > ref_end.end_chapter || this.start_chapter === ref_end.end_chapter && this.start_verse > ref_end.end_verse;
}
includes(chapter_or_ref, verse) {
const ref = typeof chapter_or_ref === "number" ? new _PassageReference(this.book, chapter_or_ref, verse) : chapter_or_ref;
return !ref.get_start().is_before(this) && !ref.get_end().is_after(this);
}
// Whether this reference shares any verses with the given reference
// NOTE 'book'/'chapter' types are interpreted as ranges for this
overlaps(ref) {
return !this.is_before(ref) && !this.is_after(ref);
}
// Get the total number of verses included in the range
total_verses() {
const { end_chapter, end_verse } = this.get_end();
if (this.start_chapter === end_chapter) {
return end_verse - this.start_verse + 1;
}
const last_verse_book = last_verse[this.book];
let count = last_verse_book[this.start_chapter - 1] - this.start_verse + 1;
for (let ch = this.start_chapter + 1; ch < end_chapter; ch++) {
count += last_verse_book[ch - 1];
}
return count + end_verse;
}
// Get a reference for just the start verse of this reference (no effect if single verse)
get_start() {
return new _PassageReference({
book: this.book,
start_chapter: this.start_chapter,
start_verse: this.start_verse
});
}
// Get a reference for just the end verse of this reference (no effect if single verse)
// NOTE 'book'/'chapter' types are interpreted as ranges for this
get_end() {
const last_verse_book = last_verse[this.book];
if (this.type === "book") {
return new _PassageReference({
book: this.book,
start_chapter: last_verse_book.length,
start_verse: last_verse_book[last_verse_book.length - 1]
});
} else if (this.type === "chapter") {
return new _PassageReference({
book: this.book,
start_chapter: this.start_chapter,
start_verse: last_verse_book[this.start_chapter - 1]
});
}
return new _PassageReference({
book: this.book,
start_chapter: this.end_chapter,
start_verse: this.end_verse
});
}
// Get a reference for the verse previous to this one (accounting for chapters)
// It can optionally be relative to the end verse, but a range is never returned (single verse)
// NOTE 'book'/'chapter' types are interpreted as ranges for this
get_prev_verse(prev_to_end = false) {
const end = prev_to_end ? this.get_end() : null;
let chapter = end ? end.end_chapter : this.start_chapter;
let verse = end ? end.end_verse : this.start_verse;
if (chapter === 1 && verse === 1) {
return null;
}
if (verse === 1) {
chapter -= 1;
verse = last_verse[this.book][chapter - 1];
} else {
verse -= 1;
}
return new _PassageReference({
book: this.book,
start_chapter: chapter,
start_verse: verse
});
}
// Get a reference for the verse after this one (accounting for chapters)
// It can optionally be relative to the end verse, but a range is never returned (single verse)
// NOTE 'book'/'chapter' types are interpreted as ranges for this
get_next_verse(after_end = false) {
const end = after_end ? this.get_end() : null;
let chapter = end ? end.end_chapter : this.start_chapter;
let verse = end ? end.end_verse : this.start_verse;
const last_verse_book = last_verse[this.book];
if (chapter === last_verse_book.length && verse === last_verse_book[last_verse_book.length - 1]) {
return null;
}
if (verse === last_verse_book[chapter - 1]) {
chapter += 1;
verse = 1;
} else {
verse += 1;
}
return new _PassageReference({
book: this.book,
start_chapter: chapter,
start_verse: verse
});
}
// Return new object that collapses ranges to a book/chapter type if possible
// NOTE This is not done automatically in constructor since book/chapter types are identifiers
// and can refer to e.g. navigating to a chapter rather than selecting its contents
simplify() {
const last_verse_book = last_verse[this.book];
if (this.type === "range_verses" && this.start_verse === 1 && this.end_verse === last_verse_book[this.start_chapter - 1]) {
return new _PassageReference(this.book, this.start_chapter);
}
if (this.type === "range_chapters" && this.start_chapter === 1 && this.end_chapter === last_verse_book.length) {
return new _PassageReference(this.book);
}
return this;
}
};
// src/detect.ts
var regex_verse_sep = "[:\uFF1A\\.]";
var regex_book_num_prefix = "(?:(?:[123]|I{1,3}) ? ?)?";
var regex_book_name_tmpl = "\\p{Letter}[\\p{Letter}\\p{Dash} ]{MIN_MID,16}END_LETTER\\.? ? ?";
var regex_integer_with_opt_sep = "\\d{1,3}[abc]?(?: ? ?" + regex_verse_sep + " ? ?\\d{1,3}[abc]?)?";
var regex_verse_range = regex_integer_with_opt_sep + "(?: ? ?\\p{Dash} ? ?" + regex_integer_with_opt_sep + ")?";
var regex_trailing = "(?![\\d\\p{Letter}@#$%])";
var regex_between_ranges = " ? ?[,;] ? ?";
var regex_additional_range = regex_between_ranges + "(" + regex_verse_range + ")" + regex_trailing;
function* detect_references(text, book_names, exclude_book_names, min_chars = 2, match_from_start = true) {
const from_string = (value) => {
return PassageReference.from_string(
value,
book_names,
exclude_book_names,
min_chars,
match_from_start
);
};
const regex_book_name = regex_book_name_tmpl.replace("MIN_MID", String(Math.max(0, min_chars - 2))).replace("END_LETTER", min_chars > 1 ? "\\p{Letter}" : "");
const regex_complete = regex_book_num_prefix + regex_book_name + regex_verse_range + regex_trailing;
const regex_book_check = regex_between_ranges + "(" + regex_book_num_prefix + regex_book_name + ")";
const regex = new RegExp(regex_complete, "uig");
let end_of_prev_match = 0;
while (true) {
const match = regex.exec(text);
if (!match) {
return null;
}
const ref = from_string(match[0]);
if (ref && ref.args_valid) {
yield {
ref,
text: match[0],
index: match.index,
index_from_prev_match: match.index - end_of_prev_match,
whole: true
};
end_of_prev_match = match.index + match[0].length;
const add_regex = new RegExp(regex_additional_range, "uiy");
add_regex.lastIndex = regex.lastIndex;
let prev_end_chapter = ref.end_chapter;
while (true) {
const book_look_ahead = new RegExp(regex_book_check, "uiy");
book_look_ahead.lastIndex = add_regex.lastIndex;
const possible_book = book_look_ahead.exec(text);
if (possible_book && from_string(possible_book[1])) {
break;
}
const add_match = add_regex.exec(text);
if (!add_match) {
break;
}
const add_match_real_index = add_match.index + add_match[0].indexOf(add_match[1]);
let prefix = ref.book;
const has_verse_sep = new RegExp(regex_verse_sep).test(add_match[1]);
if (!has_verse_sep && ["verse", "range_verses", "range_multi"].includes(ref.type)) {
prefix += `${prev_end_chapter}:`;
}
const add_ref = from_string(prefix + add_match[1]);
if (!add_ref || !add_ref.args_valid) {
break;
}
yield {
ref: add_ref,
text: add_match[1],
index: add_match_real_index,
index_from_prev_match: add_match_real_index - end_of_prev_match,
whole: false
};
end_of_prev_match = add_match_real_index + add_match[1].length;
prev_end_chapter = add_ref.end_chapter;
if (add_regex.lastIndex > regex.lastIndex) {
regex.lastIndex = add_regex.lastIndex;
}
}
} else {
const chars_to_next_word = match[0].indexOf(" ", 1);
if (chars_to_next_word >= 1) {
regex.lastIndex -= match[0].length - chars_to_next_word - 1;
}
}
}
}
// src/lists.ts
function sort_references(refs, canonical = true) {
const book_first_index = /* @__PURE__ */ new Map();
if (!canonical) {
refs.forEach((ref, i) => {
if (!book_first_index.has(ref.book)) {
book_first_index.set(ref.book, i);
}
});
}
return refs.slice().sort((a, b) => {
const book_diff = canonical ? books_ordered.indexOf(a.book) - books_ordered.indexOf(b.book) : book_first_index.get(a.book) - book_first_index.get(b.book);
if (book_diff !== 0) {
return book_diff;
}
if (a.start_chapter !== b.start_chapter) {
return a.start_chapter - b.start_chapter;
}
if (a.start_verse !== b.start_verse) {
return a.start_verse - b.start_verse;
}
if (a.end_chapter !== b.end_chapter) {
return a.end_chapter - b.end_chapter;
}
return a.end_verse - b.end_verse;
});
}
function simplify_references(refs) {
const result = [];
for (const ref of refs) {
const merge_i = result.findIndex((prev2) => {
var _a, _b;
return prev2.overlaps(ref) || ((_a = ref.get_prev_verse()) == null ? void 0 : _a.overlaps(prev2)) || ((_b = prev2.get_prev_verse()) == null ? void 0 : _b.overlaps(ref));
});
if (merge_i === -1) {
result.push(ref.simplify());
continue;
}
const prev = result[merge_i];
const start_ref = prev.get_start().is_after(ref.get_start()) ? ref : prev;
const end_ref = ref.get_end().is_after(prev) ? ref : prev;
result[merge_i] = PassageReference.from_refs(start_ref, end_ref).simplify();
}
return result;
}
function references_to_string(refs, book_names = {}, verse_sep = ":", range_sep = "-", chapter_sep = "; ") {
let result = "";
let current_book = null;
let current_chapter = null;
for (const ref of refs) {
const same_book = ref.book === current_book;
const verse_level = ref.type === "verse" || ref.type === "range_verses";
const same_chapter = same_book && ref.start_chapter === current_chapter && verse_level;
let sep;
let display;
if (result === "") {
sep = "";
display = ref.toString(book_names, verse_sep, range_sep);
} else if (same_chapter) {
sep = ", ";
display = ref.type === "verse" ? `${ref.start_verse}` : `${ref.start_verse}${range_sep}${ref.end_verse}`;
} else if (same_book) {
sep = chapter_sep;
display = ref.get_verses_string(verse_sep, range_sep);
} else {
sep = chapter_sep;
display = ref.toString(book_names, verse_sep, range_sep);
}
result += sep + display;
current_book = ref.book;
current_chapter = ref.start_chapter;
}
return result;
}
// src/stats.ts
function get_chapters(book) {
return [...Array(last_verse[book].length).keys()].map((i) => i + 1);
}
function get_verses(book, chapter) {
return [...Array(last_verse[book][chapter - 1]).keys()].map((i) => i + 1);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PassageReference,
book_abbrev_english,
book_names_english,
books_ordered,
detect_references,
english_abbrev_exclude,
english_abbrev_include,
get_chapters,
get_verses,
last_verse,
references_to_string,
simplify_references,
sort_references
});
//# sourceMappingURL=bundled.cjs.map