@sahabaplus/mushaf-engine
Version:
TypeScript implementation of a Quran Mushaf navigation engine
142 lines (141 loc) • 5.17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.QuranMetadata = exports.SuraInfo = void 0;
/**
* Information about a Sura in the Quran
*/
class SuraInfo {
constructor(number = 0, totalVerses = 0, lines = 0, linesWithHeader = 0, startPage = 0, endPage = 0) {
this.number = number;
this.totalVerses = totalVerses;
this.lines = lines;
this.linesWithHeader = linesWithHeader;
this.startPage = startPage;
this.endPage = endPage;
}
}
exports.SuraInfo = SuraInfo;
/**
* Collection of Sura information for quick lookup
*/
class QuranMetadata {
constructor(suras = []) {
this.suras = suras;
}
/**
* Create a new QuranMetadata from the Mushaf
*
* @param mushaf - The Mushaf to extract metadata from
* @returns A new QuranMetadata instance
*/
static fromMushaf(mushaf) {
// Create an array with capacity for exactly 114 suras (0-indexed internally)
const suras = [];
// Maps to track sura statistics (1-indexed for easier reading)
let currentSura = 0;
const suraVerseCount = Array(115).fill(0);
const suraLines = Array(115).fill(0);
const suraStartPage = Array(115).fill(0);
const suraEndPage = Array(115).fill(0);
// Scan through the mushaf to collect information
for (const page of mushaf.pages) {
for (const verse of page.verses()) {
const suraNum = verse.sura;
// Track when we enter a new sura
if (suraNum !== currentSura) {
currentSura = suraNum;
suraStartPage[suraNum] = page.number;
}
// Count verses and lines
suraVerseCount[suraNum] += 1;
suraLines[suraNum] = Math.round((suraLines[suraNum] + verse.lines) * 100) / 100;
// Update end page
suraEndPage[suraNum] = page.number;
}
}
// Create SuraInfo for each sura (store in 0-indexed array)
for (let suraNum = 1; suraNum <= 114; suraNum++) {
// Add header lines for each sura
const headerLines = suraNum === 9 ? 1.0 : 2.0;
const totalLinesWithHeader = suraLines[suraNum] + headerLines;
suras.push(new SuraInfo(suraNum, suraVerseCount[suraNum], suraLines[suraNum], totalLinesWithHeader, suraStartPage[suraNum], suraEndPage[suraNum]));
}
return new QuranMetadata(suras);
}
/**
* Convert 1-based sura number to 0-based index for internal storage
*
* @param suraNumber - 1-based sura number
* @returns 0-based index for internal storage, or undefined if out of range
*/
static suraToIndex(suraNumber) {
if (suraNumber === 0 || suraNumber > 114) {
return undefined;
}
return suraNumber - 1;
}
/**
* Get information about a specific Sura (using 1-based sura number)
*
* @param suraNumber - 1-based sura number
* @returns Information about the specified Sura, or undefined if not found
*/
getSuraInfo(suraNumber) {
const idx = QuranMetadata.suraToIndex(suraNumber);
return idx !== undefined ? this.suras[idx] : undefined;
}
/**
* Find which Sura contains a particular page
*
* @param pageNumber - Page number to find suras for
* @returns Array of sura numbers that contain the specified page, or undefined if none
*/
findSuraByPage(pageNumber) {
const suras = this.suras
.filter(info => info.startPage <= pageNumber && info.endPage >= pageNumber)
.map(info => info.number);
return suras.length > 0 ? suras : undefined;
}
/**
* Get total number of lines for a range of Suras
*
* @param startSura - Starting sura number (1-based)
* @param endSura - Ending sura number (1-based)
* @returns Total number of lines for the specified range of suras
* @throws Error if startSura or endSura are outside valid range [1, 114]
*/
getLinesRange(startSura, endSura) {
if (startSura < 1 || startSura > 114 || endSura < 1 || endSura > 114) {
throw new Error("Invalid sura range: startSura and endSura must be between 1 and 114");
}
const start = Math.max(startSura, 1);
const end = Math.min(endSura, 114);
let totalLines = 0;
for (let suraNum = start; suraNum <= end; suraNum++) {
const info = this.getSuraInfo(suraNum);
if (info) {
totalLines += info.linesWithHeader;
}
}
return totalLines;
}
/**
* Get the number of Suras in the Quran
*
* @returns The total number of suras
*/
totalSuras() {
return this.suras.length;
}
/**
* Get an iterator over all Sura information
*
* @returns An iterator over all Sura information
*/
*[Symbol.iterator]() {
for (const sura of this.suras) {
yield sura;
}
}
}
exports.QuranMetadata = QuranMetadata;