UNPKG

@gracious.tech/bible-references

Version:

Bible reference detection, parsing, and rendering that supports any language.

2,201 lines (2,192 loc) 40.1 kB
"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