@@ -209,146 +209,4 @@ const BookPreview = (() => {
209209 el . querySelector ( '#download-card-btn' ) . addEventListener ( 'click' , ( ) => {
210210 const card = document . getElementById ( 'book-card' ) ;
211211 html2canvas ( card ) . then ( canvas => {
212- const link = document . createElement ( 'a' ) ;
213- link . download = 'book-card.png' ;
214- link . href = canvas . toDataURL ( 'image/png' ) ;
215- link . click ( ) ;
216- } ) ;
217- } ) ;
218-
219- return el ;
220- }
221-
222- // ── State helpers ──────────────────────────────────────────────────────────
223-
224- function _setLoading ( ) {
225- const loading = document . getElementById ( 'preview-loading' ) ;
226- const container = document . getElementById ( 'preview-viewer-container' ) ;
227- const fallback = document . getElementById ( 'preview-fallback' ) ;
228- if ( loading ) loading . style . display = 'flex' ;
229- if ( container ) { container . style . display = 'none' ; container . innerHTML = '' ; }
230- if ( fallback ) fallback . style . display = 'none' ;
231- }
232-
233- function _showViewer ( ) {
234- const loading = document . getElementById ( 'preview-loading' ) ;
235- const container = document . getElementById ( 'preview-viewer-container' ) ;
236- const fallback = document . getElementById ( 'preview-fallback' ) ;
237- if ( loading ) loading . style . display = 'none' ;
238- if ( fallback ) fallback . style . display = 'none' ;
239- if ( container ) container . style . display = 'block' ;
240- }
241-
242- function _showFallback ( id , msg ) {
243- const loading = document . getElementById ( 'preview-loading' ) ;
244- const container = document . getElementById ( 'preview-viewer-container' ) ;
245- const fallback = document . getElementById ( 'preview-fallback' ) ;
246- const msgEl = document . getElementById ( 'preview-fallback-msg' ) ;
247- const link = document . getElementById ( 'preview-external-link' ) ;
248- if ( loading ) loading . style . display = 'none' ;
249- if ( container ) container . style . display = 'none' ;
250- if ( msgEl && msg ) msgEl . textContent = msg ;
251- if ( link ) link . href = `https://books.google.com/books?id=${ encodeURIComponent ( id ) } ` ;
252- if ( fallback ) fallback . style . display = 'flex' ;
253- }
254-
255- function _close ( ) {
256- const modal = document . getElementById ( 'book-preview-modal' ) ;
257- const container = document . getElementById ( 'preview-viewer-container' ) ;
258- if ( container ) container . innerHTML = '' ; // destroy iframe
259- if ( modal && modal . hasAttribute ( 'open' ) ) modal . close ( ) ;
260- document . body . style . overflow = '' ;
261- }
262-
263- // ── Viewer ─────────────────────────────────────────────────────────────────
264-
265- function _renderViewer ( id ) {
266- const container = document . getElementById ( 'preview-viewer-container' ) ;
267- if ( ! container ) { _showFallback ( id , 'Viewer container missing.' ) ; return ; }
268-
269- if ( ! window . google ?. books ?. DefaultViewer ) {
270- _showFallback ( id , 'The preview viewer could not be initialised.' ) ;
271- return ;
272- }
273-
274- // Hard timeout — show fallback if callbacks never fire
275- const timeout = setTimeout ( ( ) => {
276- _showFallback ( id , 'The preview took too long to load. You can view it on Google Books instead.' ) ;
277- } , 10000 ) ;
278-
279- const viewer = new window . google . books . DefaultViewer ( container ) ;
280-
281- viewer . load (
282- id ,
283- // notFoundCallback — no embeddable preview for this book
284- ( ) => {
285- clearTimeout ( timeout ) ;
286- _showFallback ( id , "This book doesn't have an embeddable preview. You can view it on Google Books instead." ) ;
287- } ,
288- // successCallback — viewer rendered successfully
289- ( ) => {
290- clearTimeout ( timeout ) ;
291- _showViewer ( ) ;
292- }
293- ) ;
294- }
295-
296- // ── Public API ─────────────────────────────────────────────────────────────
297-
298- /**
299- * Open the in-app preview modal.
300- * @param {string } googleBooksId
301- * @param {string } [title]
302- * @param {string } [author]
303- * @param {number } [rating]
304- * @param {string } [genre]
305- */
306-
307- async function open ( googleBooksId , title , author , rating , genre ) {
308- if ( ! _isValidId ( googleBooksId ) ) {
309- console . warn ( '[BookPreview] Invalid Google Books ID:' , googleBooksId ) ;
310- return ;
311- }
312-
313- const modal = _getOrCreateModal ( ) ;
314-
315- // Set title
316- const titleEl = document . getElementById ( 'preview-modal-title' ) ;
317- if ( titleEl ) titleEl . textContent = title || 'Book Preview' ;
318-
319- // populate the book card with real data after modal exists ──
320- populateBookCard ( title , author , rating , genre ) ;
321-
322- _setLoading ( ) ;
323- modal . showModal ( ) ;
324- document . body . style . overflow = 'hidden' ;
325-
326- try {
327- await _loadAPI ( ) ;
328- _renderViewer ( googleBooksId ) ;
329- } catch ( err ) {
330- console . error ( '[BookPreview] Error:' , err ) ;
331- _showFallback (
332- googleBooksId ,
333- 'Something went wrong loading the preview. You can view this book on Google Books instead.'
334- ) ;
335- }
336- }
337-
338- // populate hidden book card with current book's data
339- function populateBookCard ( title , author , rating , genre ) {
340- const titleEl = document . getElementById ( 'card-title' ) ;
341- const authorEl = document . getElementById ( 'card-author' ) ;
342- const ratingEl = document . getElementById ( 'card-rating' ) ;
343- const genreEl = document . getElementById ( 'card-genre' ) ;
344- if ( titleEl ) titleEl . textContent = title || '' ;
345- if ( authorEl ) authorEl . textContent = author ? 'by ' + author : '' ;
346- if ( ratingEl ) ratingEl . textContent = rating ? '★' . repeat ( rating ) + '☆' . repeat ( 5 - rating ) : '' ;
347- if ( genreEl ) genreEl . textContent = genre || '' ;
348- }
349-
350- return { open, populateBookCard } ;
351-
352- } ) ( ) ;
353-
354- window . BookPreview = BookPreview ;
212+ . catch ( err => console . error ( err ) )
0 commit comments