Texts library is exposed via separate menu
This commit is contained in:
1 parent
366aea76bb
commit
4e4f928630
5 files changed
+204
-370
No files matched your search
@@ -13,6 +13,7 @@ Derived from the original [Java Ponomar application](https://github.com/typiconm
|
||||
- **Bible Reader** — Read scripture across 13 versions (Church Slavonic, Russian Synodal, KJV, Brenton, Vulgate, etc.). Per-version reading position persists across sessions.
|
||||
- **Multi-language UI** — English, Russian, Church Slavonic (with dedicated Slavonic fonts).
|
||||
- **PWA** — Installable on desktop and mobile devices.
|
||||
- **Navigation** — 3 top-level items (Calendar, Library, Settings) with Library dropdown: Bible, Service (Horologion, Sbornik, Prayer Rule, Canons & Akathists, Parimii, Paraclete, Irmologion), Feasts (Menaion, Lenten Triodion).
|
||||
|
||||
## Tech Stack
|
||||
|
||||
@@ -42,6 +43,25 @@ npm run build # production build
|
||||
npm run preview # preview production build
|
||||
```
|
||||
|
||||
## QA / Validation
|
||||
|
||||
```bash
|
||||
npx tsx scripts/validate-i18n.ts # i18n completeness (en/ru/cu)
|
||||
npx tsx scripts/validate-tabs.ts # service tab/template consistency
|
||||
npx tsx scripts/assembler-test.ts # service assembler integration tests
|
||||
```
|
||||
|
||||
## Liturgical Text Categories (static/data/shared/services/)
|
||||
|
||||
- **Triodion** — 37 sections (Pre-Lent → Great Lent → Holy Week)
|
||||
- **Pentecostarion** — 50 days (Pascha → All Saints)
|
||||
- **Menaion (daily)** — 367 daily entries (one per Julian calendar day)
|
||||
- **Menaion (feasts)** — 34 fixed feasts with dedicated services
|
||||
- **Canons** — Paraclete (weekday, 8 tones × 6 days = 48), Octoechos (Sunday, 8 tones), Great Canon
|
||||
- **Services** — 78 data-backed services with full.json (Pascha, Paralytic, Sergius, Pokrov, etc.; Var/I-prefix + bare P-prefix varNodes now intercepted)
|
||||
- **Horologion / Sbornik / Prayer Rule / Akathists / Parimii / Paraclete / Irmologion** — daily-cycle collections
|
||||
- **Lives** — Saint lives (pre-existing schema issues, 945 known)
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
|
||||
@@ -27,6 +27,8 @@ export interface Translations {
|
||||
menaion: string;
|
||||
triodion: string;
|
||||
settings: string;
|
||||
library: string;
|
||||
festal: string;
|
||||
};
|
||||
|
||||
// Триодь постная
|
||||
@@ -514,6 +516,8 @@ const translations: Record<LanguageCode, Translations> = {
|
||||
menaion: '📕 Menaion',
|
||||
triodion: '📗 Lenten Triodion',
|
||||
settings: '⚙ Settings',
|
||||
library: '📚 Library',
|
||||
festal: '📗 Feasts',
|
||||
},
|
||||
|
||||
triodion: {
|
||||
@@ -1034,6 +1038,8 @@ const translations: Record<LanguageCode, Translations> = {
|
||||
menaion: '📕 Минея',
|
||||
triodion: '📗 Триодь постная',
|
||||
settings: '⚙ Настройки',
|
||||
library: '📚 Библиотека',
|
||||
festal: '📗 Праздники',
|
||||
},
|
||||
|
||||
triodion: {
|
||||
@@ -1554,6 +1560,8 @@ const translations: Record<LanguageCode, Translations> = {
|
||||
menaion: '📕 Мїне́ѧ',
|
||||
triodion: '📗 Трїѡ́дь по́стнаѧ',
|
||||
settings: '⚙ Настройки',
|
||||
library: '📚 Библіоте́ка',
|
||||
festal: '📗 Пра́здники',
|
||||
},
|
||||
|
||||
triodion: {
|
||||
|
||||
@@ -147,3 +147,74 @@ html, body {
|
||||
padding-bottom: calc(0.5rem + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
/* Nav dropdown styles */
|
||||
nav details.nav-group {
|
||||
position: relative;
|
||||
}
|
||||
nav details.nav-group summary {
|
||||
list-style: none;
|
||||
}
|
||||
nav details.nav-group summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
nav details.nav-group > div {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
margin-top: 0.25rem;
|
||||
z-index: 50;
|
||||
min-width: 14rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--color-navy-light);
|
||||
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
nav details.nav-group details {
|
||||
position: relative;
|
||||
}
|
||||
nav details.nav-group details summary {
|
||||
list-style: none;
|
||||
}
|
||||
nav details.nav-group details summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
nav details.nav-group details > div {
|
||||
margin-left: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
nav details.nav-group a.nav-link {
|
||||
padding: 0.5rem;
|
||||
display: block;
|
||||
}
|
||||
nav details.nav-group details a.nav-link {
|
||||
padding: 0.5rem 0.5rem 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Mobile: nested dropdowns render in-flow below 640px */
|
||||
@media (max-width: 640px) {
|
||||
nav details.nav-group > div,
|
||||
nav details.nav-group details > div {
|
||||
position: static;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
nav details.nav-group details > div {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
nav details.nav-group a.nav-link,
|
||||
nav details.nav-group details a.nav-link {
|
||||
padding: 0.5rem 0.5rem 0.5rem 1rem;
|
||||
}
|
||||
nav details[open] > summary {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
+49
-12
@@ -32,6 +32,18 @@ export class App {
|
||||
this.handleRoute();
|
||||
window.addEventListener('hashchange', () => this.handleRoute());
|
||||
|
||||
// Close dropdown menus on click/touch outside
|
||||
const closeOpenDropdowns = (e: Event) => {
|
||||
const target = e.target as Node;
|
||||
document.querySelectorAll('details.nav-group[open]').forEach(d => {
|
||||
if (!d.contains(target)) {
|
||||
d.removeAttribute('open');
|
||||
}
|
||||
});
|
||||
};
|
||||
document.addEventListener('click', closeOpenDropdowns);
|
||||
document.addEventListener('touchstart', closeOpenDropdowns, { passive: true });
|
||||
|
||||
// Register service worker for PWA install
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
||||
@@ -76,16 +88,31 @@ export class App {
|
||||
<h1 class="text-sm font-bold tracking-wide">${t.appName}</h1>
|
||||
<nav class="flex flex-wrap gap-4 text-sm">
|
||||
<a href="#calendar" class="nav-link hover:text-gold transition-colors" data-view="calendar">${t.nav.calendar}</a>
|
||||
<a href="#bible" class="nav-link hover:text-gold transition-colors" data-view="bible">${t.nav.bible}</a>
|
||||
<a href="#prayer" class="nav-link hover:text-gold transition-colors" data-view="prayer">${t.nav.prayer}</a>
|
||||
<a href="#akathists" class="nav-link hover:text-gold transition-colors" data-view="akathists">${t.nav.akathists}</a>
|
||||
<a href="#parimii" class="nav-link hover:text-gold transition-colors" data-view="parimii">${t.nav.parimii}</a>
|
||||
<a href="#horologion" class="nav-link hover:text-gold transition-colors" data-view="horologion">${t.nav.horologion}</a>
|
||||
<a href="#sbornik" class="nav-link hover:text-gold transition-colors" data-view="sbornik">${t.nav.sbornik}</a>
|
||||
<a href="#paraclete" class="nav-link hover:text-gold transition-colors" data-view="paraclete">${t.nav.paraclete}</a>
|
||||
<a href="#irmologion" class="nav-link hover:text-gold transition-colors" data-view="irmologion">${t.nav.irmologion}</a>
|
||||
<a href="#menaion" class="nav-link hover:text-gold transition-colors" data-view="menaion">${t.nav.menaion}</a>
|
||||
<a href="#triodion" class="nav-link hover:text-gold transition-colors" data-view="triodion">${t.nav.triodion}</a>
|
||||
<details class="nav-group relative" data-nav="library">
|
||||
<summary class="cursor-pointer list-none hover:text-gold transition-colors flex items-center gap-1">${t.nav.library} <span class="text-xs">▾</span></summary>
|
||||
<div class="absolute left-0 top-full mt-1 z-50 min-w-56 rounded-lg bg-navy-light shadow-xl p-2 flex flex-col gap-1">
|
||||
<a href="#bible" class="nav-link hover:text-gold transition-colors px-2 py-1" data-view="bible">${t.nav.bible}</a>
|
||||
<details class="nav-group">
|
||||
<summary class="cursor-pointer list-none hover:text-gold transition-colors px-2 py-1 flex items-center gap-1">${t.nav.service} <span class="text-xs">▸</span></summary>
|
||||
<div class="ml-2 flex flex-col gap-1">
|
||||
<a href="#horologion" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="horologion">${t.nav.horologion}</a>
|
||||
<a href="#sbornik" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="sbornik">${t.nav.sbornik}</a>
|
||||
<a href="#prayer" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="prayer">${t.nav.prayer}</a>
|
||||
<a href="#akathists" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="akathists">${t.nav.akathists}</a>
|
||||
<a href="#parimii" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="parimii">${t.nav.parimii}</a>
|
||||
<a href="#paraclete" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="paraclete">${t.nav.paraclete}</a>
|
||||
<a href="#irmologion" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="irmologion">${t.nav.irmologion}</a>
|
||||
</div>
|
||||
</details>
|
||||
<details class="nav-group">
|
||||
<summary class="cursor-pointer list-none hover:text-gold transition-colors px-2 py-1 flex items-center gap-1">${t.nav.festal} <span class="text-xs">▸</span></summary>
|
||||
<div class="ml-2 flex flex-col gap-1">
|
||||
<a href="#menaion" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="menaion">${t.nav.menaion}</a>
|
||||
<a href="#triodion" class="nav-link hover:text-gold transition-colors px-2 py-1 text-sm" data-view="triodion">${t.nav.triodion}</a>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</details>
|
||||
<a href="#settings" class="nav-link hover:text-gold transition-colors" data-view="settings">${t.nav.settings}</a>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -101,8 +128,18 @@ export class App {
|
||||
// Update active nav
|
||||
document.querySelectorAll('.nav-link').forEach(el => {
|
||||
const v = el.getAttribute('data-view');
|
||||
el.classList.toggle('text-gold', v === this.currentView);
|
||||
el.classList.toggle('font-bold', v === this.currentView);
|
||||
const isActive = v === this.currentView;
|
||||
el.classList.toggle('text-gold', isActive);
|
||||
el.classList.toggle('font-bold', isActive);
|
||||
if (isActive) {
|
||||
let parent = el.parentElement;
|
||||
while (parent) {
|
||||
if (parent.tagName === 'DETAILS') {
|
||||
parent.setAttribute('open', '');
|
||||
}
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Re-read settings fresh on each navigation
|
||||
|
||||
+56
-358
@@ -10,6 +10,10 @@ import { assembleService, type ServiceContext } from '../core/service-assembler'
|
||||
import { evalBool } from '../core/evaluator';
|
||||
import type { CanonData, GreatCanonPart, EvalContext, ServiceNode } from '../core/types';
|
||||
import { getTranslations, type LanguageCode } from '../core/i18n';
|
||||
import {
|
||||
TRIODION_NDAY, BRIGHT_WEEK, PENTECOSTARION, FIRST_WEEK, MENAION_FEAST,
|
||||
HOLY_WEEK, holyWeekVarName, type ServiceTabDef,
|
||||
} from '../core/service-tabs';
|
||||
import { loadSettings, fontClass } from './settings-view';
|
||||
|
||||
interface ServiceDef {
|
||||
@@ -146,31 +150,16 @@ export class ServiceView {
|
||||
|
||||
// Triodion movable days (by nday): Pre-Lent Sundays + Lent Sundays
|
||||
// (the ones not already covered: week 1, Marias Standing, Palm Sunday, Holy Week)
|
||||
const triodionDay: Record<number, { idx: number; name: string }> = {
|
||||
[-70]: { idx: 1, name: this.t.triodion.sections[0] },
|
||||
[-63]: { idx: 2, name: this.t.triodion.sections[1] },
|
||||
[-57]: { idx: 3, name: this.t.triodion.sections[2] },
|
||||
[-56]: { idx: 4, name: this.t.triodion.sections[3] },
|
||||
[-50]: { idx: 6, name: this.t.triodion.sections[5] },
|
||||
[-49]: { idx: 7, name: this.t.triodion.sections[6] },
|
||||
[-42]: { idx: 14, name: this.t.triodion.sections[13] },
|
||||
[-35]: { idx: 17, name: this.t.triodion.sections[16] },
|
||||
[-28]: { idx: 20, name: this.t.triodion.sections[19] },
|
||||
[-21]: { idx: 23, name: this.t.triodion.sections[22] },
|
||||
[-14]: { idx: 27, name: this.t.triodion.sections[26] },
|
||||
[-9]: { idx: 26, name: this.t.triodion.sections[25] },
|
||||
[-8]: { idx: 29, name: this.t.triodion.sections[28] },
|
||||
};
|
||||
const triodionHit = triodionDay[dayInfo.nday];
|
||||
if (triodionHit) {
|
||||
const triodionIdx = TRIODION_NDAY[dayInfo.nday];
|
||||
if (triodionIdx) {
|
||||
special.push({
|
||||
id: `triodion-${triodionHit.idx}`,
|
||||
name: triodionHit.name,
|
||||
id: `triodion-${triodionIdx}`,
|
||||
name: this.t.triodion.sections[triodionIdx - 1],
|
||||
serviceType: 'TRIODION',
|
||||
template: 'Triodion',
|
||||
description: this.t.triodion.title,
|
||||
titleNode: title('Triodion1', triodionHit.name),
|
||||
triodionIndex: triodionHit.idx,
|
||||
titleNode: title('Triodion1', this.t.triodion.sections[triodionIdx - 1]),
|
||||
triodionIndex: triodionIdx,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -252,53 +241,17 @@ export class ServiceView {
|
||||
|
||||
// Bright Week services (Pascha through Saturday)
|
||||
if (dayInfo.isBrightWeek) {
|
||||
const brightDay: Record<number, { id: string; name: string; type: string; desc: string }> = {
|
||||
[0]: { id: 'pascha', name: this.t.services.serviceNames.pascha, type: 'PASCHA', desc: this.t.services.serviceDescriptions.pascha },
|
||||
[1]: { id: 'brightmonday', name: this.t.services.serviceNames.brightmonday, type: 'BRIGHTMONDAY', desc: this.t.services.serviceDescriptions.brightmonday },
|
||||
[2]: { id: 'brighttuesday', name: this.t.services.serviceNames.brighttuesday, type: 'BRIGHTTUESDAY', desc: this.t.services.serviceDescriptions.brighttuesday },
|
||||
[3]: { id: 'brightwednesday', name: this.t.services.serviceNames.brightwednesday, type: 'BRIGHTWEDNESDAY', desc: this.t.services.serviceDescriptions.brightwednesday },
|
||||
[4]: { id: 'brightthursday', name: this.t.services.serviceNames.brightthursday, type: 'BRIGHTTHURSDAY', desc: this.t.services.serviceDescriptions.brightthursday },
|
||||
[5]: { id: 'brightfriday', name: this.t.services.serviceNames.brightfriday, type: 'BRIGHTFRIDAY', desc: this.t.services.serviceDescriptions.brightfriday },
|
||||
[6]: { id: 'brightsaturday', name: this.t.services.serviceNames.brightsaturday, type: 'BRIGHTSATURDAY', desc: this.t.services.serviceDescriptions.brightsaturday },
|
||||
};
|
||||
const bd = brightDay[dayInfo.nday];
|
||||
if (bd) {
|
||||
const templateName = bd.id.charAt(0).toUpperCase() + bd.id.slice(1);
|
||||
special.push({
|
||||
id: bd.id, name: bd.name, serviceType: bd.type,
|
||||
template: templateName,
|
||||
description: bd.desc,
|
||||
titleNode: title(`${templateName}1`, bd.name),
|
||||
});
|
||||
const def = BRIGHT_WEEK[dayInfo.nday];
|
||||
if (def) {
|
||||
special.push(this.defToService(def));
|
||||
}
|
||||
}
|
||||
|
||||
// Pentecostarion services (Antipascha through All Saints)
|
||||
if (dayInfo.pentecostarionFile !== null && dayInfo.nday > 6) {
|
||||
const pentDay: Record<number, { id: string; name: string; type: string; desc: string }> = {
|
||||
[7]: { id: 'antipascha', name: this.t.services.serviceNames.antipascha, type: 'ANTIPASCHA', desc: this.t.services.serviceDescriptions.antipascha },
|
||||
[14]: { id: 'myrrhbearers', name: this.t.services.serviceNames.myrrhbearers, type: 'MYRRHBEARERS', desc: this.t.services.serviceDescriptions.myrrhbearers },
|
||||
[21]: { id: 'paralytic', name: this.t.services.serviceNames.paralytic, type: 'PARALYTIC', desc: this.t.services.serviceDescriptions.paralytic },
|
||||
[25]: { id: 'prepolovenie', name: this.t.services.serviceNames.prepolovenie, type: 'PREPOLOVENIE', desc: this.t.services.serviceDescriptions.prepolovenie },
|
||||
[28]: { id: 'samaritan', name: this.t.services.serviceNames.samaritan, type: 'SAMARITAN', desc: this.t.services.serviceDescriptions.samaritan },
|
||||
[35]: { id: 'blindman', name: this.t.services.serviceNames.blindman, type: 'BLINDMAN', desc: this.t.services.serviceDescriptions.blindman },
|
||||
[38]: { id: 'apodosis', name: this.t.services.serviceNames.apodosis, type: 'APODOSIS', desc: this.t.services.serviceDescriptions.apodosis },
|
||||
[39]: { id: 'ascension', name: this.t.services.serviceNames.ascension, type: 'ASCENSION', desc: this.t.services.serviceDescriptions.ascension },
|
||||
[42]: { id: 'holyfathers', name: this.t.services.serviceNames.holyfathers, type: 'HOLYFATHERS', desc: this.t.services.serviceDescriptions.holyfathers },
|
||||
[48]: { id: 'pentecostsaturday', name: this.t.services.serviceNames.pentecostsaturday, type: 'PENTECOSTSATURDAY', desc: this.t.services.serviceDescriptions.pentecostsaturday },
|
||||
[49]: { id: 'pentecost', name: this.t.services.serviceNames.pentecost, type: 'PENTECOST', desc: this.t.services.serviceDescriptions.pentecost },
|
||||
[50]: { id: 'holyspirit', name: this.t.services.serviceNames.holyspirit, type: 'HOLYSPIRIT', desc: this.t.services.serviceDescriptions.holyspirit },
|
||||
[56]: { id: 'allsaints', name: this.t.services.serviceNames.allsaints, type: 'ALLSAINTS', desc: this.t.services.serviceDescriptions.allsaints },
|
||||
};
|
||||
const pd = pentDay[dayInfo.nday];
|
||||
const pd = PENTECOSTARION[dayInfo.nday];
|
||||
if (pd) {
|
||||
const templateName = pd.id.charAt(0).toUpperCase() + pd.id.slice(1);
|
||||
special.push({
|
||||
id: pd.id, name: pd.name, serviceType: pd.type,
|
||||
template: templateName,
|
||||
description: pd.desc,
|
||||
titleNode: title(`${templateName}1`, pd.name),
|
||||
});
|
||||
special.push(this.defToService(pd));
|
||||
}
|
||||
// All Saints of Russia (appendix, same Sunday as All Saints)
|
||||
if (dayInfo.nday === 56) {
|
||||
@@ -313,93 +266,29 @@ export class ServiceView {
|
||||
|
||||
// First Week of Lent: complete daily cycle (Matins, Hours, Typica, Vespers, Compline)
|
||||
if (dayInfo.isLent && dayInfo.nday >= -48 && dayInfo.nday <= -43) {
|
||||
const fwDay: Record<number, { id: string; name: string; type: string; desc: string }> = {
|
||||
[-48]: { id: 'firstweekmonday', name: this.t.services.serviceNames.firstweekmonday, type: 'FIRSTWEEKMONDAY', desc: this.t.services.serviceDescriptions.firstweekmonday },
|
||||
[-47]: { id: 'firstweektuesday', name: this.t.services.serviceNames.firstweektuesday, type: 'FIRSTWEEKTUESDAY', desc: this.t.services.serviceDescriptions.firstweektuesday },
|
||||
[-46]: { id: 'firstweekwednesday', name: this.t.services.serviceNames.firstweekwednesday, type: 'FIRSTWEEKWEDNESDAY', desc: this.t.services.serviceDescriptions.firstweekwednesday },
|
||||
[-45]: { id: 'firstweekthursday', name: this.t.services.serviceNames.firstweekthursday, type: 'FIRSTWEEKTHURSDAY', desc: this.t.services.serviceDescriptions.firstweekthursday },
|
||||
[-44]: { id: 'firstweekfriday', name: this.t.services.serviceNames.firstweekfriday, type: 'FIRSTWEEKFRIDAY', desc: this.t.services.serviceDescriptions.firstweekfriday },
|
||||
[-43]: { id: 'firstweeksaturday', name: this.t.services.serviceNames.firstweeksaturday, type: 'FIRSTWEEKSATURDAY', desc: this.t.services.serviceDescriptions.firstweeksaturday },
|
||||
};
|
||||
const fw = fwDay[dayInfo.nday];
|
||||
const fw = FIRST_WEEK[dayInfo.nday];
|
||||
if (fw) {
|
||||
const templateName = 'FirstWeek' + fw.id.charAt(0).toUpperCase() + fw.id.slice(1);
|
||||
special.push({
|
||||
id: fw.id, name: fw.name, serviceType: fw.type,
|
||||
template: templateName,
|
||||
description: fw.desc,
|
||||
titleNode: title(`${templateName}1`, fw.name),
|
||||
});
|
||||
special.push(this.defToService(fw));
|
||||
}
|
||||
}
|
||||
|
||||
// Festal Menaion: Great feasts on fixed Julian dates (Typikon Ch. 47)
|
||||
const menaionFeast: Record<string, { id: string; name: string; type: string; desc: string }> = {
|
||||
'9-8': { id: 'nativitytheotokos', name: this.t.services.serviceNames.nativitytheotokos, type: 'NATIVITYTHEOTOKOS', desc: this.t.services.serviceDescriptions.nativitytheotokos },
|
||||
'9-14': { id: 'exaltation', name: this.t.services.serviceNames.exaltation, type: 'EXALTATION', desc: this.t.services.serviceDescriptions.exaltation },
|
||||
'11-21': { id: 'vvedenie', name: this.t.services.serviceNames.vvedenie, type: 'VVEDENIE', desc: this.t.services.serviceDescriptions.vvedenie },
|
||||
'12-24': { id: 'nativityhours', name: this.t.services.serviceNames.nativityhours, type: 'NATIVITYHOURS', desc: this.t.services.serviceDescriptions.nativityhours },
|
||||
'12-25': { id: 'nativity', name: this.t.services.serviceNames.nativity, type: 'NATIVITY', desc: this.t.services.serviceDescriptions.nativity },
|
||||
'1-5': { id: 'theophanyhours', name: this.t.services.serviceNames.theophanyhours, type: 'THEOPHANYHOURS', desc: this.t.services.serviceDescriptions.theophanyhours },
|
||||
'1-6': { id: 'theophany', name: this.t.services.serviceNames.theophany, type: 'THEOPHANY', desc: this.t.services.serviceDescriptions.theophany },
|
||||
'2-2': { id: 'sretenie', name: this.t.services.serviceNames.sretenie, type: 'SRETENIE', desc: this.t.services.serviceDescriptions.sretenie },
|
||||
'3-25': { id: 'annunciation', name: this.t.services.serviceNames.annunciation, type: 'ANNUNCIATION', desc: this.t.services.serviceDescriptions.annunciation },
|
||||
'6-24': { id: 'forerunnerbirth', name: this.t.services.serviceNames.forerunnerbirth, type: 'FORERUNNERBIRTH', desc: this.t.services.serviceDescriptions.forerunnerbirth },
|
||||
'6-29': { id: 'peterpaul', name: this.t.services.serviceNames.peterpaul, type: 'PETERPAUL', desc: this.t.services.serviceDescriptions.peterpaul },
|
||||
'8-6': { id: 'transfiguration', name: this.t.services.serviceNames.transfiguration, type: 'TRANSFIGURATION', desc: this.t.services.serviceDescriptions.transfiguration },
|
||||
'8-15': { id: 'dormition', name: this.t.services.serviceNames.dormition, type: 'DORMITION', desc: this.t.services.serviceDescriptions.dormition },
|
||||
'8-29': { id: 'forerunnerbeheading', name: this.t.services.serviceNames.forerunnerbeheading, type: 'FORERUNNERBEHEADING', desc: this.t.services.serviceDescriptions.forerunnerbeheading },
|
||||
// Middle feasts (fixed dates)
|
||||
'9-25': { id: 'sergius', name: this.t.services.serviceNames.sergius, type: 'SERGIUS', desc: this.t.services.serviceDescriptions.sergius },
|
||||
'9-26': { id: 'johntheologiansep', name: this.t.services.serviceNames.johntheologiansep, type: 'JOHNTHEOLOGIANSEP', desc: this.t.services.serviceDescriptions.johntheologiansep },
|
||||
'10-1': { id: 'pokrov', name: this.t.services.serviceNames.pokrov, type: 'POKROV', desc: this.t.services.serviceDescriptions.pokrov },
|
||||
'10-10': { id: 'ambrose', name: this.t.services.serviceNames.ambrose, type: 'AMBROSE', desc: this.t.services.serviceDescriptions.ambrose },
|
||||
'10-11': { id: 'seventhcouncilefathers', name: this.t.services.serviceNames.seventhcouncilefathers, type: 'SEVENTHCOUNCILFATHERS', desc: this.t.services.serviceDescriptions.seventhcouncilefathers },
|
||||
'10-22': { id: 'kazan', name: this.t.services.serviceNames.kazan, type: 'KAZAN', desc: this.t.services.serviceDescriptions.kazan },
|
||||
'10-26': { id: 'demetrius', name: this.t.services.serviceNames.demetrius, type: 'DEMETRIUS', desc: this.t.services.serviceDescriptions.demetrius },
|
||||
'11-8': { id: 'michaelsynaxis', name: this.t.services.serviceNames.michaelsynaxis, type: 'MICHAELSYNAXIS', desc: this.t.services.serviceDescriptions.michaelsynaxis },
|
||||
'12-6': { id: 'nicholas', name: this.t.services.serviceNames.nicholas, type: 'NICHOLAS', desc: this.t.services.serviceDescriptions.nicholas },
|
||||
'1-1': { id: 'circumcision', name: this.t.services.serviceNames.circumcision, type: 'CIRCUMCISION', desc: this.t.services.serviceDescriptions.circumcision },
|
||||
'2-24': { id: 'findinghead1st', name: this.t.services.serviceNames.findinghead1st, type: 'FINDINGHEAD1ST', desc: this.t.services.serviceDescriptions.findinghead1st },
|
||||
'3-9': { id: 'fortymartyrs', name: this.t.services.serviceNames.fortymartyrs, type: 'FORTYMARTYRS', desc: this.t.services.serviceDescriptions.fortymartyrs },
|
||||
'5-8': { id: 'johntheologianmay', name: this.t.services.serviceNames.johntheologianmay, type: 'JOHNTHEOLOGIANMAY', desc: this.t.services.serviceDescriptions.johntheologianmay },
|
||||
'5-9': { id: 'nicholastranslation', name: this.t.services.serviceNames.nicholastranslation, type: 'NICHOLASTRANSLATION', desc: this.t.services.serviceDescriptions.nicholastranslation },
|
||||
'5-25': { id: 'findinghead3rd', name: this.t.services.serviceNames.findinghead3rd, type: 'FINDINGHEAD3RD', desc: this.t.services.serviceDescriptions.findinghead3rd },
|
||||
'7-15': { id: 'vladimir', name: this.t.services.serviceNames.vladimir, type: 'VLADIMIR', desc: this.t.services.serviceDescriptions.vladimir },
|
||||
'7-16': { id: 'sixcouncilfathers', name: this.t.services.serviceNames.sixcouncilfathers, type: 'SIXCOUNCILFATHERS', desc: this.t.services.serviceDescriptions.sixcouncilfathers },
|
||||
'7-20': { id: 'elijah', name: this.t.services.serviceNames.elijah, type: 'ELIJAH', desc: this.t.services.serviceDescriptions.elijah },
|
||||
'7-27': { id: 'panteleimon', name: this.t.services.serviceNames.panteleimon, type: 'PANTELEIMON', desc: this.t.services.serviceDescriptions.panteleimon },
|
||||
'8-1': { id: 'processioncross', name: this.t.services.serviceNames.processioncross, type: 'PROCESSIONCROSS', desc: this.t.services.serviceDescriptions.processioncross },
|
||||
};
|
||||
const feast = menaionFeast[`${dayInfo.month}-${dayInfo.day}`];
|
||||
const feast = MENAION_FEAST[`${dayInfo.month}-${dayInfo.day}`];
|
||||
if (feast) {
|
||||
const templateName = feast.id.charAt(0).toUpperCase() + feast.id.slice(1);
|
||||
special.push({
|
||||
id: feast.id, name: feast.name, serviceType: feast.type,
|
||||
template: templateName,
|
||||
description: feast.desc,
|
||||
titleNode: title(`${templateName}1`, feast.name),
|
||||
});
|
||||
special.push(this.defToService(feast));
|
||||
}
|
||||
// Movable Sundays of the Nativity season (dow-based)
|
||||
if (dayInfo.dow === 0 && dayInfo.month === 12) {
|
||||
const movable: { id: string; name: string; type: string; desc: string }[] = [];
|
||||
let def: ServiceTabDef | null = null;
|
||||
if (dayInfo.day >= 11 && dayInfo.day <= 17) {
|
||||
movable.push({ id: 'forefatherssunday', name: this.t.services.serviceNames.forefatherssunday, type: 'FOREFATHERSSUNDAY', desc: this.t.services.serviceDescriptions.forefatherssunday });
|
||||
def = HOLY_WEEK['FOREFATHERSSUNDAY'];
|
||||
} else if (dayInfo.day >= 18 && dayInfo.day <= 24) {
|
||||
movable.push({ id: 'holyfathersnativity', name: this.t.services.serviceNames.holyfathersnativity, type: 'HOLYFATHERSNATIVITY', desc: this.t.services.serviceDescriptions.holyfathersnativity });
|
||||
def = HOLY_WEEK['HOLYFATHERSNATIVITY'];
|
||||
} else if (dayInfo.day >= 26 && dayInfo.day <= 31) {
|
||||
movable.push({ id: 'sundayafternativity', name: this.t.services.serviceNames.sundayafternativity, type: 'SUNDAYAFTERNATIVITY', desc: this.t.services.serviceDescriptions.sundayafternativity });
|
||||
def = HOLY_WEEK['SUNDAYAFTERNATIVITY'];
|
||||
}
|
||||
const mv = movable[0];
|
||||
if (mv) {
|
||||
const templateName = mv.id.charAt(0).toUpperCase() + mv.id.slice(1);
|
||||
special.push({
|
||||
id: mv.id, name: mv.name, serviceType: mv.type,
|
||||
template: templateName,
|
||||
description: mv.desc,
|
||||
titleNode: title(`${templateName}1`, mv.name),
|
||||
});
|
||||
if (def) {
|
||||
special.push(this.defToService(def));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,85 +316,37 @@ export class ServiceView {
|
||||
if (dayInfo.isLent) {
|
||||
// Palm Sunday: Vespers + Matins
|
||||
if (dayInfo.nday === -7) {
|
||||
special.push({
|
||||
id: 'palmsunday', name: this.t.services.serviceNames.palmsunday, serviceType: 'PALMSUNDAY',
|
||||
template: 'PalmSunday',
|
||||
description: this.t.services.serviceDescriptions.palmsunday,
|
||||
titleNode: title('PalmSunday1', this.t.services.serviceNames.palmsunday),
|
||||
});
|
||||
special.push(this.defToService(HOLY_WEEK['PALMSUNDAY']));
|
||||
}
|
||||
// Great Monday through Thursday: daily cycle (Matins, Hours, Typica, Vespers)
|
||||
const greatDay: Record<number, { id: string; name: string; type: string; desc: string }> = {
|
||||
[-6]: { id: 'greatmonday', name: this.t.services.serviceNames.greatmonday, type: 'GREATMONDAY', desc: this.t.services.serviceDescriptions.greatmonday },
|
||||
[-5]: { id: 'greattuesday', name: this.t.services.serviceNames.greattuesday, type: 'GREATTUESDAY', desc: this.t.services.serviceDescriptions.greattuesday },
|
||||
[-4]: { id: 'greatwednesday', name: this.t.services.serviceNames.greatwednesday, type: 'GREATWEDNESDAY', desc: this.t.services.serviceDescriptions.greatwednesday },
|
||||
[-3]: { id: 'greatthursday', name: this.t.services.serviceNames.greatthursday, type: 'GREATTHURSDAY', desc: this.t.services.serviceDescriptions.greatthursday },
|
||||
const greatDay: Record<number, string> = {
|
||||
[-6]: 'GREATMONDAY', [-5]: 'GREATTUESDAY', [-4]: 'GREATWEDNESDAY', [-3]: 'GREATTHURSDAY',
|
||||
};
|
||||
const gd = greatDay[dayInfo.nday];
|
||||
if (gd) {
|
||||
special.push({
|
||||
id: gd.id, name: gd.name, serviceType: gd.type,
|
||||
template: gd.id.charAt(0).toUpperCase() + gd.id.slice(1),
|
||||
description: gd.desc,
|
||||
titleNode: title(`${gd.id.charAt(0).toUpperCase() + gd.id.slice(1)}1`, gd.name),
|
||||
});
|
||||
special.push(this.defToService(HOLY_WEEK[gd]));
|
||||
}
|
||||
// Matins of the 12 Passion Gospels: Great Thursday evening (Great Friday Matins)
|
||||
if (dayInfo.nday === -3) {
|
||||
special.push({
|
||||
id: 'passiongospels', name: this.t.services.serviceNames.passiongospels, serviceType: 'PASSIONGOSPELS',
|
||||
template: 'PassionGospels',
|
||||
description: this.t.services.serviceDescriptions.passiongospels,
|
||||
titleNode: title('PassionGospels1', this.t.services.serviceNames.passiongospels),
|
||||
});
|
||||
special.push(this.defToService(HOLY_WEEK['PASSIONGOSPELS']));
|
||||
}
|
||||
// Royal Hours of Great Friday
|
||||
if (dayInfo.nday === -2) {
|
||||
special.push({
|
||||
id: 'royalhoursfriday', name: this.t.services.serviceNames.royalhoursfriday, serviceType: 'ROYALHOURSFRIDAY',
|
||||
template: 'RoyalHoursFriday',
|
||||
description: this.t.services.serviceDescriptions.royalhoursfriday,
|
||||
titleNode: title('RoyalHoursFriday1', this.t.services.serviceNames.royalhoursfriday),
|
||||
});
|
||||
special.push(this.defToService(HOLY_WEEK['ROYALHOURSFRIDAY']));
|
||||
}
|
||||
// Lamentations Matins: Great Saturday Matins (served Friday night)
|
||||
if (dayInfo.nday === -1) {
|
||||
special.push({
|
||||
id: 'lamentations', name: this.t.services.serviceNames.lamentations, serviceType: 'LAMENTATIONS',
|
||||
template: 'Lamentations',
|
||||
description: this.t.services.serviceDescriptions.lamentations,
|
||||
titleNode: title('Lamentations1', this.t.services.serviceNames.lamentations),
|
||||
});
|
||||
special.push(this.defToService(HOLY_WEEK['LAMENTATIONS']));
|
||||
}
|
||||
// Vespers of the Burial + cell Compline: Great Friday evening
|
||||
if (dayInfo.nday === -2) {
|
||||
special.push({
|
||||
id: 'burialvespers', name: this.t.services.serviceNames.burialvespers, serviceType: 'BURIALVESPERS',
|
||||
template: 'BurialVespers',
|
||||
description: this.t.services.serviceDescriptions.burialvespers,
|
||||
titleNode: title('BurialVespers1', this.t.services.serviceNames.burialvespers),
|
||||
});
|
||||
special.push(this.defToService(HOLY_WEEK['BURIALVESPERS']));
|
||||
}
|
||||
// Holy Saturday: Hours, Vespers+Liturgy of St. Basil, Midnight Office
|
||||
if (dayInfo.nday === -1) {
|
||||
special.push({
|
||||
id: 'saturdayhours', name: this.t.services.serviceNames.saturdayhours, serviceType: 'SATURDAYHOURS',
|
||||
template: 'SaturdayHours',
|
||||
description: this.t.services.serviceDescriptions.saturdayhours,
|
||||
titleNode: title('SaturdayHours1', this.t.services.serviceNames.saturdayhours),
|
||||
});
|
||||
special.push({
|
||||
id: 'saturdayliturgy', name: this.t.services.serviceNames.saturdayliturgy, serviceType: 'SATURDAYLITURGY',
|
||||
template: 'SaturdayVespersLiturgy',
|
||||
description: this.t.services.serviceDescriptions.saturdayliturgy,
|
||||
titleNode: title('SaturdayVespersLiturgy1', this.t.services.serviceNames.saturdayliturgy),
|
||||
});
|
||||
special.push({
|
||||
id: 'saturdaymidnight', name: this.t.services.serviceNames.saturdaymidnight, serviceType: 'SATURDAYMIDNIGHT',
|
||||
template: 'SaturdayMidnight',
|
||||
description: this.t.services.serviceDescriptions.saturdaymidnight,
|
||||
titleNode: title('SaturdayMidnight1', this.t.services.serviceNames.saturdaymidnight),
|
||||
});
|
||||
special.push(this.defToService(HOLY_WEEK['SATURDAYHOURS']));
|
||||
special.push(this.defToService(HOLY_WEEK['SATURDAYLITURGY']));
|
||||
special.push(this.defToService(HOLY_WEEK['SATURDAYMIDNIGHT']));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,6 +356,19 @@ export class ServiceView {
|
||||
return [...special, ...daily];
|
||||
}
|
||||
|
||||
private defToService(def: ServiceTabDef): ServiceDef {
|
||||
const names = this.t.services.serviceNames as Record<string, string>;
|
||||
const descs = this.t.services.serviceDescriptions as Record<string, string>;
|
||||
return {
|
||||
id: def.id,
|
||||
name: names[def.id],
|
||||
serviceType: def.serviceType,
|
||||
template: def.template,
|
||||
description: descs[def.id],
|
||||
titleNode: title(`${def.template}1`, names[def.id]),
|
||||
};
|
||||
}
|
||||
|
||||
private async loadToneData(serviceType: string): Promise<{ troparion1?: string; kontakion1?: string; theotokion1?: string; prokimenon1?: string; alleluia1?: string; hypakoe1?: string } | null> {
|
||||
const computed = computeDay(this.currentDate);
|
||||
const { dayInfo } = computed;
|
||||
@@ -1001,167 +855,11 @@ export class ServiceView {
|
||||
}
|
||||
}
|
||||
|
||||
// Load Holy Week service data
|
||||
const holyWeekVarMap: Record<string, string> = {
|
||||
PALMSUNDAY: 'PPalmSunday',
|
||||
GREATMONDAY: 'PGreatMonday',
|
||||
GREATTUESDAY: 'PGreatTuesday',
|
||||
GREATWEDNESDAY: 'PGreatWednesday',
|
||||
GREATTHURSDAY: 'PGreatThursday',
|
||||
PASSIONGOSPELS: 'PPassionGospels',
|
||||
ROYALHOURSFRIDAY: 'PRoyalHoursFriday',
|
||||
LAMENTATIONS: 'PLamentations',
|
||||
BURIALVESPERS: 'PBurialVespers',
|
||||
SATURDAYHOURS: 'PSaturdayHours',
|
||||
SATURDAYLITURGY: 'PSaturdayVespersLiturgy',
|
||||
SATURDAYMIDNIGHT: 'PSaturdayMidnight',
|
||||
PASCHA: 'PPascha',
|
||||
BRIGHTMONDAY: 'PBrightMonday',
|
||||
BRIGHTTUESDAY: 'PBrightTuesday',
|
||||
BRIGHTWEDNESDAY: 'PBrightWednesday',
|
||||
BRIGHTTHURSDAY: 'PBrightThursday',
|
||||
BRIGHTFRIDAY: 'PBrightFriday',
|
||||
BRIGHTSATURDAY: 'PBrightSaturday',
|
||||
ANTIPASCHA: 'PAntipascha',
|
||||
MYRRHBEARERS: 'PMyrrhbearers',
|
||||
PARALYTIC: 'PParalytic',
|
||||
PREPOLOVENIE: 'PPrepolovenie',
|
||||
SAMARITAN: 'PSamaritan',
|
||||
BLINDMAN: 'PBlindMan',
|
||||
APODOSIS: 'PApodosis',
|
||||
ASCENSION: 'PAscension',
|
||||
HOLYFATHERS: 'PHolyFathers',
|
||||
PENTECOSTSATURDAY: 'PPentecostSaturday',
|
||||
PENTECOST: 'PPentecost',
|
||||
HOLYSPIRIT: 'PHolySpirit',
|
||||
ALLSAINTS: 'PAllSaints',
|
||||
RUSSIANSAINTS: 'PRussianSaints',
|
||||
FIRSTWEEKMONDAY: 'PFirstWeekMonday',
|
||||
FIRSTWEEKTUESDAY: 'PFirstWeekTuesday',
|
||||
FIRSTWEEKWEDNESDAY: 'PFirstWeekWednesday',
|
||||
FIRSTWEEKTHURSDAY: 'PFirstWeekThursday',
|
||||
FIRSTWEEKFRIDAY: 'PFirstWeekFriday',
|
||||
FIRSTWEEKSATURDAY: 'PFirstWeekSaturday',
|
||||
NATIVITYTHEOTOKOS: 'PNativityTheotokos',
|
||||
EXALTATION: 'PExaltation',
|
||||
VVEDENIE: 'PVvedenie',
|
||||
NATIVITYHOURS: 'PNativityHours',
|
||||
NATIVITY: 'PNativity',
|
||||
THEOPHANYHOURS: 'PTheophanyHours',
|
||||
THEOPHANY: 'PTheophany',
|
||||
SRETENIE: 'PSretenie',
|
||||
ANNUNCIATION: 'PAnnunciation',
|
||||
FORERUNNERBIRTH: 'PForerunnerBirth',
|
||||
PETERPAUL: 'PPeterPaul',
|
||||
TRANSFIGURATION: 'PTransfiguration',
|
||||
DORMITION: 'PDormition',
|
||||
FORERUNNERBEHEADING: 'PForerunnerBeheading',
|
||||
SERGIUS: 'PSergius',
|
||||
JOHNTHEOLOGIANSEP: 'PJohnTheologianSep',
|
||||
POKROV: 'PPokrov',
|
||||
AMBROSE: 'PAmbrose',
|
||||
SEVENTHCOUNCILFATHERS: 'PSeventhCouncilFathers',
|
||||
KAZAN: 'PKazan',
|
||||
DEMETRIUS: 'PDemetrius',
|
||||
MICHAELSYNAXIS: 'PMichaelSynaxis',
|
||||
NICHOLAS: 'PNicholas',
|
||||
CIRCUMCISION: 'PCircumcision',
|
||||
FINDINGHEAD1ST: 'PFindingHead1st',
|
||||
FORTYMARTYRS: 'PFortyMartyrs',
|
||||
JOHNTHEOLOGIANMAY: 'PJohnTheologianMay',
|
||||
NICHOLASTRANSLATION: 'PNicholasTranslation',
|
||||
FINDINGHEAD3RD: 'PFindingHead3rd',
|
||||
VLADIMIR: 'PVladimir',
|
||||
SIXCOUNCILFATHERS: 'PSixCouncilsFathers',
|
||||
ELIJAH: 'PElijah',
|
||||
PANTELEIMON: 'PPanteleimon',
|
||||
PROCESSIONCROSS: 'PProcessionCross',
|
||||
FOREFATHERSSUNDAY: 'PForefathersSunday',
|
||||
HOLYFATHERSNATIVITY: 'PHolyFathersNativity',
|
||||
SUNDAYAFTERNATIVITY: 'PSundayAfterNativity',
|
||||
};
|
||||
const holyWeekDir: Record<string, string> = {
|
||||
PALMSUNDAY: 'palm-sunday',
|
||||
GREATMONDAY: 'great-monday',
|
||||
GREATTUESDAY: 'great-tuesday',
|
||||
GREATWEDNESDAY: 'great-wednesday',
|
||||
GREATTHURSDAY: 'great-thursday',
|
||||
PASSIONGOSPELS: 'passion-gospels',
|
||||
ROYALHOURSFRIDAY: 'royal-hours-friday',
|
||||
LAMENTATIONS: 'lamentations',
|
||||
BURIALVESPERS: 'burial-vespers',
|
||||
SATURDAYHOURS: 'saturday-hours',
|
||||
SATURDAYLITURGY: 'saturday-vespers-liturgy',
|
||||
SATURDAYMIDNIGHT: 'saturday-midnight',
|
||||
PASCHA: 'pascha',
|
||||
BRIGHTMONDAY: 'bright-monday',
|
||||
BRIGHTTUESDAY: 'bright-tuesday',
|
||||
BRIGHTWEDNESDAY: 'bright-wednesday',
|
||||
BRIGHTTHURSDAY: 'bright-thursday',
|
||||
BRIGHTFRIDAY: 'bright-friday',
|
||||
BRIGHTSATURDAY: 'bright-saturday',
|
||||
ANTIPASCHA: 'antipascha',
|
||||
MYRRHBEARERS: 'myrrhbearers',
|
||||
PARALYTIC: 'paralytic',
|
||||
PREPOLOVENIE: 'prepolovenie',
|
||||
SAMARITAN: 'samaritan',
|
||||
BLINDMAN: 'blindman',
|
||||
APODOSIS: 'apodosis',
|
||||
ASCENSION: 'ascension',
|
||||
HOLYFATHERS: 'holyfathers',
|
||||
PENTECOSTSATURDAY: 'pentecostsaturday',
|
||||
PENTECOST: 'pentecost',
|
||||
HOLYSPIRIT: 'holyspirit',
|
||||
ALLSAINTS: 'allsaints',
|
||||
RUSSIANSAINTS: 'russiansaints',
|
||||
FIRSTWEEKMONDAY: 'first-week-monday',
|
||||
FIRSTWEEKTUESDAY: 'first-week-tuesday',
|
||||
FIRSTWEEKWEDNESDAY: 'first-week-wednesday',
|
||||
FIRSTWEEKTHURSDAY: 'first-week-thursday',
|
||||
FIRSTWEEKFRIDAY: 'first-week-friday',
|
||||
FIRSTWEEKSATURDAY: 'first-week-saturday',
|
||||
NATIVITYTHEOTOKOS: 'nativity-theotokos',
|
||||
EXALTATION: 'exaltation',
|
||||
VVEDENIE: 'vvedenie',
|
||||
NATIVITYHOURS: 'nativity-hours',
|
||||
NATIVITY: 'nativity',
|
||||
THEOPHANYHOURS: 'theophany-hours',
|
||||
THEOPHANY: 'theophany',
|
||||
SRETENIE: 'sretenie',
|
||||
ANNUNCIATION: 'annunciation',
|
||||
FORERUNNERBIRTH: 'forerunner-birth',
|
||||
PETERPAUL: 'peter-paul',
|
||||
TRANSFIGURATION: 'transfiguration',
|
||||
DORMITION: 'dormition',
|
||||
FORERUNNERBEHEADING: 'forerunner-beheading',
|
||||
SERGIUS: 'sergius',
|
||||
JOHNTHEOLOGIANSEP: 'johntheologian-sep',
|
||||
POKROV: 'pokrov',
|
||||
AMBROSE: 'ambrose',
|
||||
SEVENTHCOUNCILFATHERS: 'seventh-council-fathers',
|
||||
KAZAN: 'kazan',
|
||||
DEMETRIUS: 'demetrius',
|
||||
MICHAELSYNAXIS: 'michael-synaxis',
|
||||
NICHOLAS: 'nicholas',
|
||||
CIRCUMCISION: 'circumcision',
|
||||
FINDINGHEAD1ST: 'finding-head-1st',
|
||||
FORTYMARTYRS: 'forty-martyrs',
|
||||
JOHNTHEOLOGIANMAY: 'johntheologian-may',
|
||||
NICHOLASTRANSLATION: 'nicholas-translation',
|
||||
FINDINGHEAD3RD: 'finding-head-3rd',
|
||||
VLADIMIR: 'vladimir',
|
||||
SIXCOUNCILFATHERS: 'six-councils-fathers',
|
||||
ELIJAH: 'elijah',
|
||||
PANTELEIMON: 'panteleimon',
|
||||
PROCESSIONCROSS: 'procession-cross',
|
||||
FOREFATHERSSUNDAY: 'forefathers-sunday',
|
||||
HOLYFATHERSNATIVITY: 'holy-fathers-nativity',
|
||||
SUNDAYAFTERNATIVITY: 'sunday-after-nativity',
|
||||
};
|
||||
const hwVar = holyWeekVarMap[service.serviceType];
|
||||
if (hwVar) {
|
||||
const nodes = await this.loadHolyWeekData(holyWeekDir[service.serviceType]);
|
||||
if (nodes) varNodes.set(hwVar, nodes);
|
||||
// Load data-backed service content (Holy Week, Bright Week, feasts, first week)
|
||||
const hwDef = HOLY_WEEK[service.serviceType];
|
||||
if (hwDef && hwDef.dir) {
|
||||
const nodes = await this.loadHolyWeekData(hwDef.dir);
|
||||
if (nodes) varNodes.set(holyWeekVarName(hwDef), nodes);
|
||||
}
|
||||
|
||||
// Load Epistle and Gospel readings from commemoration
|
||||
@@ -1244,8 +942,8 @@ export class ServiceView {
|
||||
fetchText: this.createFetchText(),
|
||||
fetchPrayerNodes: this.createFetchPrayerNodes(),
|
||||
fetchServiceNodes: async (path: string): Promise<ServiceNode[] | null> => {
|
||||
const varMatch = path.match(/Services\/Var\/(\w+)\.xml$/);
|
||||
if (varMatch) {
|
||||
const varMatch = path.match(/Services\/(?:Var\/)?(\w+)\.xml$/);
|
||||
if (varMatch && varNodes.has(varMatch[1])) {
|
||||
return varNodes.get(varMatch[1]) || null;
|
||||
}
|
||||
return this.fetchServiceNodes(path);
|
||||
|
||||
Reference in new issue
Block a user