1+ import {
2+ apiTranslationCode ,
3+ fetchChapter ,
4+ getApiBookCode ,
5+ loadPassageFromAPI
6+ } from './api.js'
7+ import {
8+ clearError ,
9+ handleError ,
10+ showError ,
11+ showLoading
12+ } from '../main.js'
13+ import {
14+ displayPassage ,
15+ extractVerseText ,
16+ loadPassage
17+ } from './passage.js'
18+ import {
19+ BOOK_ORDER ,
20+ CHAPTER_COUNTS ,
21+ bookNameMapping ,
22+ getActivePlan ,
23+ saveToStorage ,
24+ state
25+ } from './state.js'
26+ import { updateReferencePanel } from './ui.js'
27+ export function populateBookDropdown ( ) {
28+ const bookSel = document . getElementById ( 'bookSelect' ) ;
29+ bookSel . innerHTML = '' ;
30+ BOOK_ORDER . forEach ( book => {
31+ const opt = document . createElement ( 'option' ) ;
32+ opt . value = book ;
33+ opt . textContent = book ;
34+ bookSel . appendChild ( opt ) ;
35+ } ) ;
36+ }
37+ export function populateChapterDropdown ( selectedBook ) {
38+ const chapSel = document . getElementById ( 'chapterSelect' ) ;
39+ chapSel . innerHTML = '' ;
40+ const max = CHAPTER_COUNTS [ selectedBook ] ;
41+ for ( let i = 1 ; i <= max ; i ++ ) {
42+ const opt = document . createElement ( 'option' ) ;
43+ opt . value = i ;
44+ opt . textContent = i ;
45+ chapSel . appendChild ( opt ) ;
46+ }
47+ }
48+ export async function loadSelectedChapter ( book = null , chapter = null ) {
49+ const selBook = book || document . getElementById ( 'bookSelect' ) . value ;
50+ const selChapter = chapter || document . getElementById ( 'chapterSelect' ) . value ;
51+ const apiBook = getApiBookCode ( selBook ) ;
52+ try {
53+ showLoading ( true ) ;
54+ const apiTranslation = apiTranslationCode ( state . settings . bibleTranslation ) ;
55+ const chapterData = await fetchChapter (
56+ apiTranslation ,
57+ apiBook ,
58+ selChapter
59+ ) ;
60+ const chapterFootnotes = chapterData . chapter . footnotes || [ ] ;
61+ const footnoteCounter = { value : 1 } ;
62+ const verses = chapterData . chapter . content
63+ . filter ( v => v . type === 'verse' )
64+ . map ( v => ( {
65+ number : v . number ,
66+ text : extractVerseText ( v . content , chapterFootnotes , footnoteCounter ) ,
67+ reference : `${ selBook } ${ selChapter } :${ v . number } `
68+ } ) ) ;
69+ document . getElementById ( 'passageReference' ) . textContent =
70+ `${ selBook } ${ selChapter } ` ;
71+ state . footnotes = { } ;
72+ displayPassage ( verses , `${ selBook } ${ selChapter } ` ) ;
73+ clearError ( ) ;
74+ document . getElementById ( 'scriptureSection' ) . scrollTop = 0 ;
75+ if ( state . settings . readingMode === 'manual' ) {
76+ state . settings . manualBook = selBook ;
77+ state . settings . manualChapter = Number ( selChapter ) ;
78+ saveToStorage ( ) ;
79+ }
80+ if ( state . settings . referencePanelOpen ) {
81+ updateReferencePanel ( ) ;
82+ }
83+ } catch ( err ) {
84+ handleError ( err , 'loadSelectedChapter' ) ;
85+ showError ( `Could not load ${ selBook } ${ selChapter } : ${ err . message } ` ) ;
86+ } finally {
87+ showLoading ( false ) ;
88+ }
89+ }
90+ export function initBookChapterControls ( ) {
91+ populateBookDropdown ( ) ;
92+ document . getElementById ( 'bookSelect' ) . addEventListener ( 'change' , e => {
93+ const book = e . target . value ;
94+ state . settings . readingMode = 'manual' ;
95+ populateChapterDropdown ( book ) ;
96+ state . settings . manualBook = book ;
97+ state . settings . manualChapter = 1 ;
98+ const chapterSel = document . getElementById ( 'chapterSelect' ) ;
99+ chapterSel . value = '1' ;
100+ loadSelectedChapter ( book , 1 ) ;
101+ saveToStorage ( ) ;
102+ } ) ;
103+ document . getElementById ( 'chapterSelect' ) . addEventListener ( 'change' , ( ) => {
104+ const book = document . getElementById ( 'bookSelect' ) . value ;
105+ const chap = Number ( document . getElementById ( 'chapterSelect' ) . value ) ;
106+ state . settings . readingMode = 'manual' ;
107+ state . settings . manualBook = book ;
108+ state . settings . manualChapter = chap ;
109+ loadSelectedChapter ( book , chap ) ;
110+ saveToStorage ( ) ;
111+ } ) ;
112+ populateChapterDropdown ( BOOK_ORDER [ 0 ] ) ;
113+ }
114+ export function manualPrevChapter ( ) {
115+ let bookIdx = BOOK_ORDER . indexOf ( state . settings . manualBook ) ;
116+ let chap = state . settings . manualChapter ;
117+ if ( chap > 1 ) {
118+ state . settings . manualChapter = chap - 1 ;
119+ } else {
120+ if ( bookIdx > 0 ) {
121+ const prevBook = BOOK_ORDER [ bookIdx - 1 ] ;
122+ const maxCh = CHAPTER_COUNTS [ prevBook ] ;
123+ state . settings . manualBook = prevBook ;
124+ state . settings . manualChapter = maxCh ;
125+ } else {
126+ return ;
127+ }
128+ }
129+ state . settings . readingMode = 'manual' ;
130+ loadSelectedChapter ( state . settings . manualBook , state . settings . manualChapter ) ;
131+ syncBookChapterSelectors ( ) ;
132+ saveToStorage ( ) ;
133+ if ( state . settings . referencePanelOpen ) {
134+ updateReferencePanel ( ) ;
135+ }
136+ }
137+ export function manualNextChapter ( ) {
138+ let bookIdx = BOOK_ORDER . indexOf ( state . settings . manualBook ) ;
139+ let chap = state . settings . manualChapter ;
140+ const maxCh = CHAPTER_COUNTS [ state . settings . manualBook ] ;
141+ if ( chap < maxCh ) {
142+ state . settings . manualChapter = chap + 1 ;
143+ } else {
144+ if ( bookIdx < BOOK_ORDER . length - 1 ) {
145+ const nextBook = BOOK_ORDER [ bookIdx + 1 ] ;
146+ state . settings . manualBook = nextBook ;
147+ state . settings . manualChapter = 1 ;
148+ } else {
149+ return ;
150+ }
151+ }
152+ state . settings . readingMode = 'manual' ;
153+ loadSelectedChapter ( state . settings . manualBook , state . settings . manualChapter ) ;
154+ syncBookChapterSelectors ( ) ;
155+ saveToStorage ( ) ;
156+ if ( state . settings . referencePanelOpen ) {
157+ updateReferencePanel ( ) ;
158+ }
159+ }
160+ export function prevPassage ( ) {
161+ if ( state . settings . readingMode === 'readingPlan' ) {
162+ const len = getActivePlan ( ) . length ;
163+ let newIndex = ( state . settings . currentPassageIndex - 1 + len ) % len ;
164+ state . settings . currentPassageIndex = newIndex ;
165+ loadPassage ( ) ;
166+ } else {
167+ manualPrevChapter ( ) ;
168+ }
169+ document . getElementById ( 'scriptureSection' ) . scrollTop = 0 ;
170+ }
171+ export function nextPassage ( ) {
172+ if ( state . settings . readingMode === 'readingPlan' ) {
173+ const len = getActivePlan ( ) . length ;
174+ let newIndex = ( state . settings . currentPassageIndex + 1 ) % len ;
175+ if ( newIndex < 0 ) newIndex = len - 1 ;
176+ state . settings . currentPassageIndex = newIndex ;
177+ loadPassage ( ) ;
178+ } else {
179+ manualNextChapter ( ) ;
180+ }
181+ document . getElementById ( 'scriptureSection' ) . scrollTop = 0 ;
182+ }
183+ export async function randomPassage ( ) {
184+ try {
185+ state . settings . readingMode = 'manual' ;
186+ const randomLoc = await getRandomBibleLocation ( ) ;
187+ state . settings . manualBook = randomLoc . book ;
188+ state . settings . manualChapter = randomLoc . chapter ;
189+ saveToStorage ( ) ;
190+ await loadPassageFromAPI ( randomLoc ) ;
191+ document . getElementById ( 'passageReference' ) . textContent = randomLoc . displayRef ;
192+ state . currentPassageReference = randomLoc . displayRef ;
193+ syncBookChapterSelectors ( ) ;
194+ if ( state . settings . referencePanelOpen ) {
195+ updateReferencePanel ( ) ;
196+ }
197+ } catch ( err ) {
198+ handleError ( err , 'randomPassage' ) ;
199+ showError ( 'Could not load a random passage – see console for details.' ) ;
200+ }
201+ }
202+ export function syncBookChapterSelectors ( ) {
203+ const bookSel = document . getElementById ( 'bookSelect' ) ;
204+ const chapterSel = document . getElementById ( 'chapterSelect' ) ;
205+ if ( bookSel . value !== state . settings . manualBook ) {
206+ bookSel . value = state . settings . manualBook ;
207+ populateChapterDropdown ( state . settings . manualBook ) ;
208+ }
209+ const curMax = CHAPTER_COUNTS [ state . settings . manualBook ] ;
210+ const curChap = state . settings . manualChapter ;
211+ populateChapterDropdown ( state . settings . manualBook ) ;
212+ chapterSel . value = ( curChap <= curMax ) ? curChap : curMax ;
213+ }
214+ export function syncSelectorsToReadingPlan ( ) {
215+ if ( state . settings . readingMode !== 'readingPlan' ) return ;
216+ const plan = getActivePlan ( ) ;
217+ const passage = plan [ state . settings . currentPassageIndex ] ;
218+ if ( ! passage || ! passage . book ) {
219+ console . error ( 'Invalid passage object:' , passage ) ;
220+ return ;
221+ }
222+ const bookSel = document . getElementById ( 'bookSelect' ) ;
223+ const chapterSel = document . getElementById ( 'chapterSelect' ) ;
224+ state . settings . manualBook = passage . book ;
225+ state . settings . manualChapter = passage . chapter ;
226+ if ( bookSel ) bookSel . value = passage . book ;
227+ populateChapterDropdown ( passage . book ) ;
228+ if ( chapterSel ) chapterSel . value = passage . chapter ;
229+ saveToStorage ( ) ;
230+ }
231+ async function getRandomBibleLocation ( ) {
232+ try {
233+ const randomBook = BOOK_ORDER [ Math . floor ( Math . random ( ) * BOOK_ORDER . length ) ] ;
234+ const maxCh = CHAPTER_COUNTS [ randomBook ] ;
235+ const randomChapter = Math . floor ( Math . random ( ) * maxCh ) + 1 ;
236+ const apiMap = apiTranslationCode ( state . settings . bibleTranslation ) ;
237+ const apiBook = bookNameMapping [ randomBook ] ||
238+ randomBook . replace ( / \s + / g, '' ) . toUpperCase ( ) ;
239+ const chapterData = await fetchChapter ( apiMap , apiBook , randomChapter ) ;
240+ const verses = chapterData . chapter . content . filter ( v => v . type === 'verse' ) ;
241+ const verseCount = verses . length || 1 ;
242+ return {
243+ book : randomBook ,
244+ chapter : randomChapter ,
245+ startVerse : 1 ,
246+ endVerse : verseCount ,
247+ displayRef : `${ randomBook } ${ randomChapter } `
248+ } ;
249+ } catch ( err ) {
250+ handleError ( err , 'getRandomBibleLocation' ) ;
251+ }
252+ }
0 commit comments