UNPKG

@sahabaplus/mushaf-engine

Version:

TypeScript implementation of a Quran Mushaf navigation engine

193 lines (192 loc) 8.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NavigationResult = exports.CycleInfo = void 0; /** * Information about navigation cycles and boundary crossings * * This class tracks how many times navigation passed through the starting verse * and provides information about cycle distances for bounded navigation. */ class CycleInfo { /** * Create a new CycleInfo with the specified cycle tracking data * * @param cyclesCompleted - Number of cycles through the starting verse * @param crossedBoundaries - Whether navigation wrapped around boundaries * @param cycleDistance - Distance in lines for one full cycle */ constructor(cyclesCompleted = 0, crossedBoundaries = false, cycleDistance = 0.0) { this.cyclesCompleted = cyclesCompleted; this.crossedBoundaries = crossedBoundaries; this.cycleDistance = cycleDistance; } /** * Create a new CycleInfo with no cycles * * @returns A CycleInfo instance with all fields set to default values */ static none() { return new CycleInfo(0, false, 0.0); } /** * Reconstruct total navigated distance from direct distance * * This helper method implements the formula: * ```text * total_distance = (cycles_completed - 1) * cycle_distance + direct_distance * ``` * * This formula works because: * - `cyclesCompleted` counts passes through the start verse * - Each pass adds one `cycleDistance` worth of lines * - The current (partial or complete) cycle contributes `directDistance` * - So we have (N-1) complete previous cycles + current cycle * * @param directDistance - The direct distance from start to end (from calculateLines) * @returns The total distance including cycles * * @example * ```typescript * // If we completed 3 cycles with cycleDistance=100 and directDistance=25 * const cycleInfo = new CycleInfo(3, true, 100.0); * const total = cycleInfo.reconstructDistance(25.0); * // total = (3-1)*100 + 25 = 225.0 * ``` */ reconstructDistance(directDistance) { if (this.cyclesCompleted > 0) { return (this.cyclesCompleted - 1) * this.cycleDistance + directDistance; } return directDistance; } } exports.CycleInfo = CycleInfo; /** * Comprehensive result of a navigation operation in the Quran * * This class contains the primary verse reached through navigation, * as well as optional information about any overflow conditions or boundary * verses (last verse of page or sura) encountered during navigation. */ class NavigationResult { /** * Create a new NavigationResult with all possible parameters * * @param verse - The verse reached through navigation * @param overflow - Optional information about overflow if navigation exceeded boundaries * @param endOfPage - Optional information about the last verse of the page if encountered * @param endOfSura - Optional information about the last verse of the sura if encountered * @param distanceMoved - The actual distance moved during navigation in lines * @param cycleInfo - Information about navigation cycles and boundary crossings */ constructor(verse, overflow, endOfPage, endOfSura, distanceMoved = 0, cycleInfo = CycleInfo.none()) { this.verse = verse; this.overflow = overflow; this.endOfPage = endOfPage; this.endOfSura = endOfSura; this.distanceMoved = distanceMoved; this.cycleInfo = cycleInfo; // Calculate remaining distance const remainingDistance = overflow ? overflow.overflowedVerse.lines - overflow.overflowLines : 0.0; this.remainingDistance = Math.round(remainingDistance * 100) / 100; } /** * Create a new NavigationResult for a normal navigation with no boundary conditions * * @param verse - The verse reached through navigation * @param distanceMoved - The actual distance moved during navigation in lines * @param cycleInfo - Optional cycle information * @returns A new NavigationResult instance with only the target verse and no boundary information */ static newNormal(verse, distanceMoved, cycleInfo = CycleInfo.none()) { return new NavigationResult(verse, undefined, undefined, undefined, distanceMoved, cycleInfo); } /** * Create a new NavigationResult for a navigation that includes overflow * * @param verse - The verse reached through navigation * @param overflow - Information about overflow during navigation * @param distanceMoved - The actual distance moved during navigation in lines * @param cycleInfo - Optional cycle information * @returns A new NavigationResult instance with the target verse and overflow information */ static newOverflowed(verse, overflow, distanceMoved, cycleInfo = CycleInfo.none()) { return new NavigationResult(verse, overflow, undefined, undefined, distanceMoved, cycleInfo); } /** * Create a new NavigationResult that reached the last verse of a page * * @param verse - The verse reached through navigation * @param lastOfPage - Information about the last verse of the page * @param distanceMoved - The actual distance moved during navigation in lines * @param cycleInfo - Optional cycle information * @returns A new NavigationResult instance with the target verse and last-of-page information */ static newPageBoundary(verse, lastOfPage, distanceMoved, cycleInfo = CycleInfo.none()) { return new NavigationResult(verse, undefined, lastOfPage, undefined, distanceMoved, cycleInfo); } /** * Create a new NavigationResult that reached the last verse of a sura * * @param verse - The verse reached through navigation * @param lastOfSura - Information about the last verse of the sura * @param distanceMoved - The actual distance moved during navigation in lines * @param cycleInfo - Optional cycle information * @returns A new NavigationResult instance with the target verse and last-of-sura information */ static newSuraBoundary(verse, lastOfSura, distanceMoved, cycleInfo = CycleInfo.none()) { return new NavigationResult(verse, undefined, undefined, lastOfSura, distanceMoved, cycleInfo); } /** * Check if this navigation result encountered any boundaries * * @returns true if the navigation hit the last verse of page, sura, or had an overflow */ hasBoundaries() { return this.overflow !== undefined || this.endOfPage !== undefined || this.endOfSura !== undefined; } /** * Check if this navigation result encountered an overflow condition * * @returns true if the navigation exceeded available boundaries */ hasOverflow() { return this.overflow !== undefined; } /** * Get the total overflow lines if any exist * * @returns The number of overflow lines, or 0.0 if no overflow occurred */ overflowLines() { return this.overflow ? this.overflow.overflowLines : 0.0; } /** * Create a string representation of the navigation result */ toString() { let result = `Target verse: ${this.verse}\n`; result += `Distance Moved: ${this.distanceMoved}\n`; result += `Remaining Distance: ${this.remainingDistance}\n`; if (this.cycleInfo.cyclesCompleted > 0) { result += `Cycles Completed: ${this.cycleInfo.cyclesCompleted}\n`; result += `Cycle Distance: ${this.cycleInfo.cycleDistance}\n`; result += `Crossed Boundaries: ${this.cycleInfo.crossedBoundaries}\n`; } if (this.overflow) { result += `Overflow: ${this.overflow}\n`; } if (this.endOfPage) { result += `End of page: ${this.endOfPage}\n`; } if (this.endOfSura) { result += `End of sura: ${this.endOfSura}\n`; } return result; } } exports.NavigationResult = NavigationResult;