versification
Version:
A library for parsing Paratext's vrs files.
200 lines (161 loc) • 6.64 kB
text/typescript
import * as canon from './Canon'
import Versification from './Versification'
export default class VerseRef
{
private _bbbcccvvv: number;
private _bbb: number;
private _ccc: number;
private _vvv: number;
private _vvvEnd: number | undefined;
private _segment: string | undefined;
private _versification;
public constructor(bbbcccvvv: number | string, rangeEnd?: number, segment?: string, versification?: Versification)
{
this._bbbcccvvv = typeof bbbcccvvv === 'string' ? parseInt(bbbcccvvv) : bbbcccvvv;
this._bbb = VerseRef.toBook(this._bbbcccvvv);
this._ccc = VerseRef.toChapter(this._bbbcccvvv);
this._vvv = VerseRef.toVerse(this._bbbcccvvv);
this._vvvEnd = rangeEnd;
this._segment = segment;
if (versification && !(versification instanceof Versification))
throw new TypeError(`versification : "${versification}" is wrong type.`)
this._versification = versification;
}
public static toBook(bbbcccvvv: number) {return Math.round(bbbcccvvv / 1000000)}
public static toChapter(bbbcccvvv: number) {return Math.round(bbbcccvvv / 1000) % 1000}
public static toVerse(bbbcccvvv: number) {return bbbcccvvv % 1000}
public static parse(verseRef: string, versification?: Versification): VerseRef | undefined
{
const [book, chapterVerse] = verseRef.split(' ');
const bookNumber = canon.bookIdToNumber(book);
const [chapter, verse] = chapterVerse.split(':');
if (bookNumber === undefined)
return undefined;
const verseNum = parseInt(verse);
const verseNumLength = `${verseNum}`.length;
if (verse.includes('-'))
{
const [startVerse, endVerse] = verse.split('-');
return this.fromBookChapterVerseWithRange(bookNumber, parseInt(chapter), parseInt(startVerse), parseInt(endVerse), versification);
}
else if (verseNumLength < verse.length)
{
const segment = verse.substring(verseNumLength);
return this.fromBookChapterVerseWithSegment(bookNumber, parseInt(chapter), verseNum, segment, versification);
}
return this.fromBookChapterVerse(bookNumber, parseInt(chapter), verseNum, versification);
}
public static fromBookIdChapterVerse(bookId: string, chapter: number | string, verse: number | string, versification?: Versification): VerseRef | undefined
{
if (bookId === null || bookId === undefined ||
chapter === null || chapter === undefined ||
verse === null || verse === undefined)
return undefined;
return VerseRef.parse(`${bookId} ${chapter}:${verse}`, versification);
}
public static fromBookChapterVerse(book: number, chapter: number, verse: number, versification?: Versification): VerseRef
{
return this.frombcv(book * 1000000 + chapter * 1000 + verse, versification);
}
protected static fromBookChapterVerseWithRange(book: number, chapter: number, startVerse: number, endVerse: number, versification?: Versification)
{
return this.frombcvWithRange(book * 1000000 + chapter * 1000 + startVerse, endVerse, versification);
}
protected static fromBookChapterVerseWithSegment(book: number, chapter: number, verse: number, segment: string, versification?: Versification)
{
return this.frombcvWithSegment(book * 1000000 + chapter * 1000 + verse, segment, versification);
}
protected static frombcvWithRange(bbbcccvvv: number | string, vvvEnd: number, versification?: Versification)
{
return new VerseRef(bbbcccvvv, vvvEnd, undefined, versification);
}
protected static frombcvWithSegment(bbbcccvvv: number | string, segment: string, versification?: Versification)
{
return new VerseRef(bbbcccvvv, undefined, segment, versification);
}
protected copyFrom(verseRef: VerseRef)
{
this._bbbcccvvv = verseRef._bbbcccvvv;
this._bbb = verseRef._bbb;
this._ccc = verseRef._ccc;
this._vvv = verseRef._vvv;
this._vvvEnd = verseRef._vvvEnd;
this._segment = verseRef._segment;
this._versification = verseRef._versification;
}
public static frombcv(bbbcccvvv: number | string, versification?: Versification): VerseRef
{
return new VerseRef(bbbcccvvv, undefined, undefined, versification);
}
book()
{
return canon.bookNumberToId(this._bbb);
}
chapter()
{
return this._ccc.toString();
}
verse()
{
const extra = (this._vvvEnd ? `-${this._vvvEnd}` : this._segment) || '';
return this._vvv.toString() + extra;
}
verseNum()
{
return this._vvv;
}
verseNumEnd()
{
return this._vvvEnd;
}
segment()
{
return this._segment;
}
versification()
{
return this._versification;
}
changeVersification(newVersification: Versification)
{
const newVerseRef = newVersification.changeVersification(this);
this.copyFrom(newVerseRef)
}
bbbcccvvv(): number
{
return this._bbbcccvvv;
}
// compare two verseRef assuming same versification.
static _equal(a: VerseRef, b: VerseRef)
{
return a.bbbcccvvv() === b.bbbcccvvv() && a.verseNumEnd() === b.verseNumEnd() && a.segment() === b.segment();
}
equals(verseRef: VerseRef | undefined)
{
if (!verseRef)
return false;
if (this.versification() === verseRef.versification() || !this._versification)
return VerseRef._equal(this, verseRef);
const updatedVerseRef = this._versification.changeVersification(verseRef);
return VerseRef._equal(this, updatedVerseRef);
}
isDefinedByVersification() : boolean
{
const versification = this.versification();
if (!versification)
return true;
const numberOfVerses = versification.books()?.[this._bbb]?.[this._ccc];
if (!numberOfVerses)
return false;
const inRange = numberOfVerses >= this._vvv;
if (!inRange)
return false;
if (versification.excludedVerses().has(this._bbbcccvvv))
return false;
return true;
}
toString()
{
return `${this.book()} ${this.chapter()}:${this.verse()}`;
}
}