@readium/navigator 2.2.7 → 2.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/ar-DyHX_uy2.js +7 -0
  2. package/dist/da-Dct0PS3E.js +7 -0
  3. package/dist/fr-C5HEel98.js +7 -0
  4. package/dist/index.js +7001 -6154
  5. package/dist/index.umd.cjs +1571 -39
  6. package/dist/it-DFOBoXGy.js +7 -0
  7. package/dist/pt_PT-Di3sVjze.js +7 -0
  8. package/dist/sv-BfzAFsVN.js +7 -0
  9. package/package.json +4 -2
  10. package/src/dom/_readium_executionCleanup.js +13 -0
  11. package/src/dom/_readium_executionPrevention.js +65 -0
  12. package/src/dom/_readium_webpubExecution.js +4 -0
  13. package/src/epub/EpubNavigator.ts +26 -2
  14. package/src/epub/frame/FrameBlobBuilder.ts +37 -131
  15. package/src/epub/frame/FramePoolManager.ts +34 -5
  16. package/src/epub/fxl/FXLFramePoolManager.ts +20 -2
  17. package/src/helpers/minify.ts +14 -0
  18. package/src/index.ts +2 -1
  19. package/src/injection/Injectable.ts +85 -0
  20. package/src/injection/Injector.ts +356 -0
  21. package/src/injection/epubInjectables.ts +90 -0
  22. package/src/injection/index.ts +2 -0
  23. package/src/injection/webpubInjectables.ts +59 -0
  24. package/src/webpub/WebPubBlobBuilder.ts +19 -80
  25. package/src/webpub/WebPubFramePoolManager.ts +29 -4
  26. package/src/webpub/WebPubNavigator.ts +15 -1
  27. package/types/src/epub/EpubNavigator.d.ts +3 -0
  28. package/types/src/epub/frame/FrameBlobBuilder.d.ts +7 -4
  29. package/types/src/epub/frame/FramePoolManager.d.ts +3 -1
  30. package/types/src/epub/fxl/FXLFramePoolManager.d.ts +3 -1
  31. package/types/src/helpers/minify.d.ts +12 -0
  32. package/types/src/index.d.ts +1 -0
  33. package/types/src/injection/Injectable.d.ts +68 -0
  34. package/types/src/injection/Injector.d.ts +22 -0
  35. package/types/src/injection/epubInjectables.d.ts +6 -0
  36. package/types/src/injection/index.d.ts +2 -0
  37. package/types/src/injection/webpubInjectables.d.ts +5 -0
  38. package/types/src/webpub/WebPubBlobBuilder.d.ts +7 -3
  39. package/types/src/webpub/WebPubFramePoolManager.d.ts +3 -1
  40. package/types/src/webpub/WebPubNavigator.d.ts +3 -0
  41. package/types/src/epub/preferences/guards.d.ts +0 -9
  42. package/types/src/web/WebPubBlobBuilder.d.ts +0 -10
  43. package/types/src/web/WebPubFrameManager.d.ts +0 -20
  44. package/types/src/web/WebPubNavigator.d.ts +0 -48
  45. package/types/src/web/index.d.ts +0 -3
  46. package/types/src/webpub/css/WebPubStylesheet.d.ts +0 -1
@@ -0,0 +1,7 @@
1
+ const i = /* @__PURE__ */ JSON.parse(`{"format":{"audiobook":"Audiolibro","audiobookJSON":"Audiobook Manifest","cbz":"Comic book archive","divina":"Divina Publication","divinaJSON":"Divina Publication Manifest","epub":"EPUB","lcpa":"Audiolibro protetto con LCP","lcpdf":"PDF protetto con LCP","lcpl":"Licenza LCP","pdf":"PDF","rwp":"Readium Web Publication","rwpm":"Readium Web Publication Manifest","zab":"Audiobook Archive","zip":"ZIP Archive"},"kind":{"audiobook_many":"audiolibri","audiobook_one":"audiolibro","audiobook_other":"audiolibri","book_many":"libri","book_one":"libro","book_other":"libri","comic_many":"fumetti","comic_one":"fumetto","comic_other":"fumetti","document_many":"documenti","document_one":"documento","document_other":"documenti"},"metadata":{"accessibility":{"display-guide":{"accessibility-summary":{"no-metadata":"Nessuna informazione disponibile","publisher-contact":"Per ulteriori informazioni sull'accessibilità di questa risorsa, contattare l'editore: ","title":"Informazioni aggiuntive sull'accessibilità fornite dall'editore"},"additional-accessibility-information":{"aria":{"compact":"Ruoli ARIA inclusi","descriptive":"Il contenuto è semanticamente arricchito con ruoli ARIA per ottimizzare l'organizzazione e facilitare la navigazione"},"audio-descriptions":"Descrizioni audio","braille":"Braille","color-not-sole-means-of-conveying-information":"Il colore non è l'unico mezzo per trasmettere informazioni","dyslexia-readability":"Leggibilità adatta alla dislessia","full-ruby-annotations":"Annotazioni complete in Ruby","high-contrast-between-foreground-and-background-audio":"Elevato contrasto tra audio principale e sottofondo","high-contrast-between-text-and-background":"Contrasto elevato tra testo in primo piano e sfondo","large-print":"Stampa a caratteri ingranditi","page-breaks":{"compact":"Interruzioni di pagina incluse","descriptive":"Interruzioni di pagina identiche alla versione originale a stampa"},"ruby-annotations":"Alcune annotazioni in Ruby","sign-language":"Lingua dei segni","tactile-graphics":{"compact":"Grafica tattile inclusa","descriptive":"La grafica tattile è stata integrata per facilitare l'accesso agli elementi visivi alle persone non vedenti"},"tactile-objects":"Oggetti 3D tattili","text-to-speech-hinting":"Pronuncia migliorata per la sintesi vocale","title":"Ulteriori informazioni sull'accessibilità","ultra-high-contrast-between-text-and-background":"Contrasto molto elevato tra testo e sfondo","visible-page-numbering":"Numerazione delle pagine visibile","without-background-sounds":"Nessun suono in sottofondo"},"conformance":{"a":{"compact":"Questa pubblicazione soddisfa gli standard minimi di accessibilità","descriptive":"La pubblicazione contiene una dichiarazione di conformità che attesta il rispetto degli standard EPUB Accessibility e WCAG 2 Livello A"},"aa":{"compact":"Questa pubblicazione soddisfa gli standard di accessibilità accettati","descriptive":"La pubblicazione contiene una dichiarazione di conformità che attesta il rispetto degli standard EPUB Accessibility e WCAG 2 Livello AAA"},"aaa":{"compact":"Questa pubblicazione supera gli standard di accessibilità","descriptive":"La pubblicazione contiene una dichiarazione di conformità che attesta il rispetto degli standard EPUB Accessibility e WCAG 2 Livello AAA"},"certifier":"La pubblicazione è stata certificata da ","certifier-credentials":"Le credenziali del certificatore sono ","details":{"certification-info":"La pubblicazione è stata certificata il ","certifier-report":"Per ulteriori informazioni, consultare il report di accessibilità del certificatore","claim":"Questa pubblicazione è conforme ai requisiti di","epub-accessibility-1-0":"EPUB Accessibility 1.0","epub-accessibility-1-1":"EPUB Accessibility 1.1","level-a":"Livello A","level-aa":"Livello AA","level-aaa":"Livello AAA","wcag-2-0":{"compact":"WCAG 2.0","descriptive":"Linee guida per l'accessibilità dei contenuti web (WCAG) 2.0"},"wcag-2-1":{"compact":"WCAG 2.1","descriptive":"Linee guida per l'accessibilità dei contenuti web (WCAG) 2.1"},"wcag-2-2":{"compact":"WCAG 2.2","descriptive":"Linee guida per l'accessibilità dei contenuti web (WCAG) 2.2"}},"details-title":"Informazioni dettagliate sulla conformità","no":"Nessuna informazione disponibile","title":"Conformità","unknown-standard":"Nessuna indicazione sugli standard d'accessibilità"},"hazards":{"flashing":{"compact":"Contenuto lampeggiante","descriptive":"La pubblicazione contiene contenuti lampeggianti che possono causare crisi d'epilessia fotosensibile"},"flashing-none":{"compact":"Nessun contenuto lampeggiante","descriptive":"La pubblicazione non presenta contenuti lampeggianti che possono causare crisi d'epilessia fotosensibile"},"flashing-unknown":{"compact":"Nessuna informazione sulla presenza di contenuti lampeggianti","descriptive":"Non è stato possibile determinare la presenza di contenuti lampeggianti che possono causare crisi d'epilessia fotosensibile"},"motion":{"compact":"Simulazione del movimento","descriptive":"La pubblicazione contiene simulazioni di movimento che possono provocare cinetosi"},"motion-none":{"compact":"Nessun rischio di simulazione del movimento","descriptive":"La pubblicazione non contiene simulazioni di movimento che possono causare la malattia di movimento"},"motion-unknown":{"compact":"Nessuna informazione relativa alla presenza di simulazioni di movimento","descriptive":"Non è stato possibile determinare la presenza di contenuti che possono provocare cinetosi"},"no-metadata":"Nessuna informazione disponibile","none":{"compact":"Nessuna problematica","descriptive":"La pubblicazione non presenta contenuti a rischio di simulazione di movimento, di suoni, o di contenuti lampeggianti"},"sound":{"compact":"Suoni","descriptive":"La pubblicazione contiene suoni che possono causare problemi di sensibilità"},"sound-none":{"compact":"Nessun rischio acustico","descriptive":"La pubblicazione non contiene suoni che possono causare problemi di sensibilità"},"sound-unknown":{"compact":"Nessuna informazione sulla presenza di suoni","descriptive":"Non è stato possibile determinare la presenza di suoni che potrebbero causare problemi di sensibilità"},"title":"Problematiche","unknown":"La presenza di rischi è sconosciuta"},"legal-considerations":{"exempt":{"compact":"Dichiara di godere dell'esenzione d'accessibilità in alcune giurisdizioni","descriptive":"Questa risorsa gode dell'esenzione d'accessibilità in alcune giurisdizioni"},"no-metadata":"Nessuna informazione disponibile","title":"Note legali"},"navigation":{"index":{"compact":"Indice analitico interattivo","descriptive":"Indice analitico con link alle voci di riferimento"},"no-metadata":"Nessuna informazione disponibile","page-navigation":{"compact":"Vai alla pagina","descriptive":"Sono presenti i riferimenti ai numeri di pagina della versione a stampa corrispondente"},"structural":{"compact":"Intestazioni","descriptive":"Contiene elementi come titoli, elenchi e tabelle per permettere una navigazione strutturata"},"title":"Navigazione","toc":{"compact":"Indice interattivo","descriptive":"L’indice permette l’accesso diretto a tutti i capitoli tramite link"}},"rich-content":{"accessible-chemistry-as-latex":{"compact":"Formule chimiche in LaTeX","descriptive":"Formule chimiche in formato accessibile (LaTeX)"},"accessible-chemistry-as-mathml":{"compact":"Formule chimiche in MathML","descriptive":"Formule chimiche in formato accessibile (MathML)"},"accessible-math-as-latex":{"compact":"Matematica in LaTeX","descriptive":"Formule matematiche in formato accessibile (LaTeX)"},"accessible-math-described":"Sono disponibili descrizioni testuali per le formule matematiche","closed-captions":{"compact":"Sottotitoli disponibili per i video","descriptive":"Per i video sono disponibili dei sottotitoli"},"extended-descriptions":"Le immagini complesse presentano descrizioni estese","math-as-mathml":{"compact":"Matematica in MathML","descriptive":"Formule matematiche in formato accessibile (MathML)"},"open-captions":{"compact":"I video hanno i sottotitoli","descriptive":"I video inclusi nella pubblicazione hanno i sottotitoli"},"title":"Contenuti arricchiti","transcript":"Trascrizioni fornite","unknown":"Nessuna informazione disponibile"},"ways-of-reading":{"nonvisual-reading":{"alt-text":{"compact":"Immagini descritte","descriptive":"Le immagini sono descritte da un testo"},"no-metadata":"Nessuna informazione sulla lettura non visiva","none":{"compact":"Non leggibile con lettura ad alta voce o in braille","descriptive":"Il contenuto non è leggibile con la lettura ad alta voce o in braille"},"not-fully":{"compact":"Non è interamente leggibile con lettura ad alta voce o in braille","descriptive":"Non tutti i contenuti potranno essere letti con lettura ad alta voce o in braille"},"readable":{"compact":"Interamente leggibile con lettura ad alta voce o in braille","descriptive":"Tutti i contenuti possono essere letti con la lettura ad alta voce o con il display braille"}},"prerecorded-audio":{"complementary":{"compact":"Clip audio preregistrate","descriptive":"Le clip audio preregistrate sono integrate nel contenuto"},"no-metadata":"Non sono disponibili informazioni sull'audio preregistrato","only":{"compact":"Solo audio preregistrato","descriptive":"Audiolibro senza testi alternativi"},"synchronized":{"compact":"Audio preregistrato sincronizzato con il testo","descriptive":"Tutti i contenuti sono disponibili come audio preregistrato sincronizzato con il testo"}},"title":"Leggibilità","visual-adjustments":{"modifiable":{"compact":"La formattazione del testo e il layout della pagina possono essere modificati","descriptive":"La formattazione del testo e il layout della pagina possono essere modificati in base alle funzionalità presenti nella soluzione di lettura (ingrandimento dei caratteri del testo, modifica dei colori e dei contrasti per il testo e lo sfondo, modifica degli spazi tra lettere, parole, frasi e paragrafi)"},"unknown":"Non sono disponibili informazioni sulla possibilità di formattare il testo","unmodifiable":{"compact":"La formattazione del testo e il display della pagina non possono essere modificati","descriptive":"Il layout di testo e pagina non può essere modificato poiché l'esperienza di lettura è vicina a una versione di stampa, ma i sistemi di lettura possono ancora fornire opzioni di zoom"}}}}},"altIdentifier_many":"identificatori alternativi","altIdentifier_one":"identificatore alternativo","altIdentifier_other":"identificatori alternativi","artist_many":"","artist_one":"artista","artist_other":"artisti","author_many":"","author_one":"autore","author_other":"autori","collection_many":"","collection_one":"collana","collection_other":"collane","colorist_many":"","colorist_one":"colorista","colorist_other":"coloristi","contributor_many":"","contributor_one":"contributore","contributor_other":"contributori","description":"descrizione","duration":"durata","editor_many":"","editor_one":"editor","editor_other":"editori","identifier_many":"identificatori","identifier_one":"identificatore","identifier_other":"identificatori","illustrator_many":"","illustrator_one":"illustratore","illustrator_other":"illustratori","imprint_many":"","imprint_one":"marca editoriale","imprint_other":"marche editoriali","inker_many":"","inker_one":"inchiostratore","inker_other":"inchiostratori","language_many":"","language_one":"lingua","language_other":"lingue","letterer_many":"letteristi","letterer_one":"letterista","letterer_other":"letteristi","modified":"Data di modifica","narrator_many":"","narrator_one":"narratore","narrator_other":"narratori","numberOfPages":"impaginazione versione cartacea","penciler_many":"","penciler_one":"disegnatore","penciler_other":"disegnatori","published":"Data di pubblicazione","publisher_many":"","publisher_one":"editore","publisher_other":"editori","series_many":"","series_one":"serie","series_other":"serie","subject_many":"","subject_one":"categoria","subject_other":"categorie","subtitle":"sottotitolo","title":"titolo","translator_many":"","translator_one":"traduttore","translator_other":"traduttori"}}`), e = {
2
+ publication: i
3
+ };
4
+ export {
5
+ e as default,
6
+ i as publication
7
+ };
@@ -0,0 +1,7 @@
1
+ const e = { metadata: { accessibility: { "display-guide": { "accessibility-summary": { "no-metadata": "Sem informação disponível", "publisher-contact": "Para mais informações sobre a acessibilidade deste produto, contacte a editora: ", title: "Resumo de acessibilidade" }, "additional-accessibility-information": { aria: { compact: "Inclui funções ARIA", descriptive: "O conteúdo foi otimizado com funções ARIA para melhorar a organização e facilitar a navegação" }, "audio-descriptions": "Descrições em áudio", braille: "Braille", "color-not-sole-means-of-conveying-information": "A cor não é o único meio de transmitir informação", "dyslexia-readability": "Otimizado para dislexia", "full-ruby-annotations": "Anotações Ruby completas", "high-contrast-between-foreground-and-background-audio": "Alto contraste entre som principal e fundo", "high-contrast-between-text-and-background": "Alto contraste entre texto e fundo", "large-print": "Impressão ampliada", "page-breaks": { compact: "Inclui quebras de página", descriptive: "Inclui quebras de página da fonte impressa original" }, "ruby-annotations": "Algumas anotações Ruby", "sign-language": "Língua gestual", "tactile-graphics": { compact: "Inclui gráficos táteis", descriptive: "Inclui gráficos táteis que facilitam o acesso a elementos visuais para pessoas cegas" }, "tactile-objects": "Objetos táteis 3D", "text-to-speech-hinting": "Sugestões para leitura em voz alta", title: "Informação adicional de acessibilidade", "ultra-high-contrast-between-text-and-background": "Contraste muito elevado entre texto e fundo", "visible-page-numbering": "Numeração de páginas visível", "without-background-sounds": "Sem sons de fundo" }, conformance: { a: { compact: "Cumpre as normas mínimas de acessibilidade", descriptive: "A publicação contém uma declaração de conformidade que indica que cumpre o padrão EPUB Accessibility e WCAG 2 nível A" }, aa: { compact: "Cumpre as normas aceites de acessibilidade", descriptive: "A publicação contém uma declaração de conformidade que indica que cumpre o padrão EPUB Accessibility e WCAG 2 nível AA" }, aaa: { compact: "Excede as normas aceites de acessibilidade", descriptive: "A publicação contém uma declaração de conformidade que indica que cumpre o padrão EPUB Accessibility e WCAG 2 nível AAA" }, certifier: "A publicação foi certificada por ", "certifier-credentials": "As credenciais do certificador são ", details: { "certification-info": "A publicação foi certificada em ", "certifier-report": "Para mais informações, consulte o relatório do certificador", claim: "Esta publicação declara conformidade com", "epub-accessibility-1-0": "EPUB Accessibility 1.0", "epub-accessibility-1-1": "EPUB Accessibility 1.1", "level-a": "Nível A", "level-aa": "Nível AA", "level-aaa": "Nível AAA", "wcag-2-0": { compact: "WCAG 2.0", descriptive: "Diretrizes de Acessibilidade para Conteúdo Web (WCAG) 2.0" }, "wcag-2-1": { compact: "WCAG 2.1", descriptive: "Diretrizes de Acessibilidade para Conteúdo Web (WCAG) 2.1" }, "wcag-2-2": { compact: "WCAG 2.2", descriptive: "Diretrizes de Acessibilidade para Conteúdo Web (WCAG) 2.2" } }, "details-title": "Detalhes de conformidade", no: "Sem informação disponível", title: "Conformidade", "unknown-standard": "Não foi possível determinar a conformidade com as normas de acessibilidade aceites para esta publicação" }, hazards: { flashing: { compact: "Conteúdo intermitente", descriptive: "A publicação contém conteúdo intermitente que pode causar crises fotossensíveis" }, "flashing-none": { compact: "Sem perigos de intermitência", descriptive: "A publicação não contém conteúdo intermitente que possa causar crises fotossensíveis" }, "flashing-unknown": { compact: "Risco de intermitência desconhecido", descriptive: "Não foi possível determinar se existe conteúdo intermitente que possa causar crises fotossensíveis" }, motion: { compact: "Simulação de movimento", descriptive: "A publicação contém simulações de movimento que podem causar enjoo" }, "motion-none": { compact: "Sem simulação de movimento", descriptive: "A publicação não contém simulações de movimento que possam causar enjoo" }, "motion-unknown": { compact: "Risco de movimento desconhecido", descriptive: "Não foi possível determinar se existem simulações de movimento que possam causar enjoo" }, "no-metadata": "Sem informação disponível", none: { compact: "Sem perigos", descriptive: "A publicação não contém perigos conhecidos" }, sound: { compact: "Sons sensíveis", descriptive: "A publicação contém sons que podem causar sensibilidade auditiva" }, "sound-none": { compact: "Sem perigos sonoros", descriptive: "A publicação não contém sons que possam causar sensibilidade auditiva" }, "sound-unknown": { compact: "Risco sonoro desconhecido", descriptive: "Não foi possível determinar se a publicação contém sons que possam causar sensibilidade auditiva" }, title: "Perigos", unknown: "Presença de perigos não determinada" }, "legal-considerations": { exempt: { compact: "Declara isenção de conformidade em algumas jurisdições", descriptive: "Esta publicação declara isenção de conformidade em algumas jurisdições" }, "no-metadata": "Sem informação disponível", title: "Considerações legais" }, navigation: { index: { compact: "Índice remissivo", descriptive: "Índice com ligações para entradas referenciadas" }, "no-metadata": "Sem informação disponível", "page-navigation": { compact: "Ir para página", descriptive: "Lista de páginas que permite aceder às páginas da versão impressa original" }, structural: { compact: "Títulos e estrutura", descriptive: "Elementos como títulos, tabelas, etc., para navegação estruturada" }, title: "Navegação", toc: { compact: "Índice", descriptive: "Índice de conteúdos com ligações para todos os capítulos do texto" } }, "rich-content": { "accessible-chemistry-as-latex": { compact: "Fórmulas químicas em LaTeX", descriptive: "Fórmulas químicas em formato acessível (LaTeX)" }, "accessible-chemistry-as-mathml": { compact: "Fórmulas químicas em MathML", descriptive: "Fórmulas químicas em formato acessível (MathML)" }, "accessible-math-as-latex": { compact: "Matemática em LaTeX", descriptive: "Fórmulas matemáticas em formato acessível (LaTeX)" }, "accessible-math-described": "Descrição textual das fórmulas matemáticas", "closed-captions": { compact: "Vídeos com legendas ocultas", descriptive: "Os vídeos incluídos na publicação têm legendas ocultas" }, "extended-descriptions": "Imagens complexas com descrições detalhadas", "math-as-mathml": { compact: "Matemática em MathML", descriptive: "Fórmulas matemáticas em formato acessível (MathML)" }, "open-captions": { compact: "Vídeos com legendas abertas", descriptive: "Os vídeos incluídos na publicação têm legendas abertas" }, title: "Conteúdo rico", transcript: "Transcrição fornecida", unknown: "Sem informação disponível" }, "ways-of-reading": { "nonvisual-reading": { "alt-text": { compact: "Contém texto alternativo", descriptive: "Inclui descrições alternativas de texto para imagens" }, "no-metadata": "Sem informação sobre leitura não visual", none: { compact: "Não legível em leitura em voz alta ou braille dinâmico", descriptive: "O conteúdo não é legível em voz alta ou através de braille dinâmico" }, "not-fully": { compact: "Parcialmente legível em leitura em voz alta ou braille dinâmico", descriptive: "Nem todo o conteúdo é legível em voz alta ou através de braille dinâmico" }, readable: { compact: "Totalmente legível em leitura em voz alta ou braille dinâmico", descriptive: "Todo o conteúdo pode ser lido em voz alta ou através de braille dinâmico" } }, "prerecorded-audio": { complementary: { compact: "Contém clipes de áudio pré-gravados", descriptive: "O conteúdo contém clipes de áudio pré-gravados incorporados" }, "no-metadata": "Sem informação sobre áudio pré-gravado", only: { compact: "Apenas áudio pré-gravado", descriptive: "A publicação é apenas áudio e não possui alternativa em texto" }, synchronized: { compact: "Áudio pré-gravado sincronizado com texto", descriptive: "Todo o conteúdo está disponível como áudio pré-gravado sincronizado com texto" } }, title: "Formas de leitura", "visual-adjustments": { modifiable: { compact: "Aspeto personalizável", descriptive: "O aspeto do texto e o layout da página podem ser modificados de acordo com as capacidades do sistema de leitura (tipo e tamanho de letra, espaçamento entre parágrafos, frases, palavras e letras, bem como a cor de fundo e do texto)" }, unknown: "Sem informação sobre personalização do aspeto", unmodifiable: { compact: "Aspeto não ajustável", descriptive: "O texto e o layout da página não podem ser modificados, uma vez que a experiência de leitura é semelhante à versão impressa, mas os sistemas de leitura ainda podem oferecer opções de ampliação" } } } } }, altIdentifier_one: "identificador alternativo", altIdentifier_other: "identificadores alternativos", artist_one: "artista", artist_other: "artistas", author_one: "autor", author_other: "autores", collection_one: "coleção", collection_other: "coleções", colorist_one: "colorista", colorist_other: "coloristas", contributor_one: "colaborador", contributor_other: "colaboradores", description: "descrição", duration: "duração", editor_one: "editor", editor_other: "editores", identifier_one: "identificador", identifier_other: "identificadores", illustrator_one: "ilustrador", illustrator_other: "ilustradores", imprint_one: "selo editorial", imprint_other: "selos editoriais", inker_one: "arte-finalista", inker_other: "arte-finalistas", language_one: "idioma", language_other: "idiomas", letterer_one: "letrista", letterer_other: "letristas", modified: "data de modificação", narrator_one: "narrador", narrator_other: "narradores", numberOfPages: "número de páginas", penciler_one: "desenhador", penciler_other: "desenhadores", published: "data de publicação", publisher_one: "editora", publisher_other: "editoras", series_one: "série", series_other: "séries", subject_one: "tema", subject_other: "temas", subtitle: "subtítulo", title: "título", translator_one: "tradutor", translator_other: "tradutores" } }, a = {
2
+ publication: e
3
+ };
4
+ export {
5
+ a as default,
6
+ e as publication
7
+ };
@@ -0,0 +1,7 @@
1
+ const e = { metadata: { accessibility: { "display-guide": { "accessibility-summary": { "no-metadata": "Information saknas", "publisher-contact": "För mer information om den här publikationens tillgänglighet, kontakta utgivaren: ", title: "Kompletterande information om tillgänglighet" }, "additional-accessibility-information": { aria: { compact: "Innehåller ARIA-roller", descriptive: "Innehållet har försetts med ARIA-roller för att tydliggöra strukturen och underlätta navigering" }, "audio-descriptions": "Syntolkning", braille: "Punktskrift", "color-not-sole-means-of-conveying-information": "Betydelse uttrycks aldrig enbart med färg", "dyslexia-readability": "Förbättrad läsbarhet för personer med dyslexi", "full-ruby-annotations": "Fullständig ruby-annotering", "high-contrast-between-foreground-and-background-audio": "Hög kontrast mellan förgrundsljud och bakgrundsljud", "high-contrast-between-text-and-background": "Hög kontrast mellan text och bakgrund", "large-print": "Storstil", "page-breaks": { compact: "Innehåller sidnummer", descriptive: "Innehåller sidnummer från tryckt förlaga" }, "ruby-annotations": "Viss ruby-annotering", "sign-language": "Teckenspråk", "tactile-graphics": { compact: "Innehåller taktila bilder", descriptive: "Taktila bilder har lagts till för att tillgängliggöra visuella element för personer med synnedsättning" }, "tactile-objects": "Taktila 3D-objekt", "text-to-speech-hinting": "Innehåller uttalsinstruktioner för talsyntes", title: "Ytterligare tillgänglighetsinformation", "ultra-high-contrast-between-text-and-background": "Extra hög kontrast mellan text och bakgrund", "visible-page-numbering": "Synlig sidnumrering", "without-background-sounds": "Utan bakgrundsljud" }, conformance: { a: { compact: "Publikationen uppfyller tillgänglighetskrav på en grundläggande nivå", descriptive: "Publikationen anger att den uppfyller standarderna EPUB Accessibility och WCAG 2 nivå A" }, aa: { compact: "Publikationen uppfyller tillgänglighetskrav på en vedertagen nivå", descriptive: "Publikationen anger att den uppfyller standarderna EPUB Accessibility och WCAG 2 nivå AA" }, aaa: { compact: "Publikationen uppfyller tillgänglighetskrav utöver en vedertagen nivå", descriptive: "Publikationen anger att den uppfyller standarderna EPUB Accessibility och WCAG 2 nivå AAA" }, certifier: "Publikationen är certifierad av ", "certifier-credentials": "Certifierarens märkning är ", details: { "certification-info": "Publikationen certifierades ", "certifier-report": "Se certifieringsrapporten för mer information", claim: "Publikationen anger att den uppfyller kraven enligt", "epub-accessibility-1-0": "EPUB Accessibility 1.0", "epub-accessibility-1-1": "EPUB Accessibility 1.1", "level-a": "nivå A", "level-aa": "nivå AA", "level-aaa": "nivå AAA", "wcag-2-0": { compact: "WCAG 2.0", descriptive: "Web Content Accessibility Guidelines (WCAG) 2.0" }, "wcag-2-1": { compact: "WCAG 2.1", descriptive: "Web Content Accessibility Guidelines (WCAG) 2.1" }, "wcag-2-2": { compact: "WCAG 2.2", descriptive: "Web Content Accessibility Guidelines (WCAG) 2.2" } }, "details-title": "Detaljerad information om tillgänglighetskrav", no: "Information saknas", title: "Tillgänglighetskrav", "unknown-standard": "Det går inte att avgöra om publikationen uppfyller vedertagna tillgänglighetskrav" }, hazards: { flashing: { compact: "Blinkande innehåll", descriptive: "Publikationen har blinkande innehåll som kan vara skadligt för ljuskänsliga personer" }, "flashing-none": { compact: "Inget blinkande innehåll", descriptive: "Publikationen har inget blinkande innehåll" }, "flashing-unknown": { compact: "Förekomst av blinkande innehåll är okänd", descriptive: "Förekomst av blinkande innehåll är okänd" }, motion: { compact: "Rörelsesimulering", descriptive: "Publikationen innehåller rörelsesimulering som skulle kunna orsaka illamående" }, "motion-none": { compact: "Ingen rörelsesimulering", descriptive: "Publikationen innehåller ingen rörelsesimulering" }, "motion-unknown": { compact: "Förekomst av rörelsesimulering är okänd", descriptive: "Förekomst av rörelsesimulering är okänd" }, "no-metadata": "Information saknas", none: { compact: "Inga risker", descriptive: "Publikationen innehåller inga risker" }, sound: { compact: "Ljud", descriptive: "Publikationen innehåller ljud som kan orsaka obehag" }, "sound-none": { compact: "Inget ljud som kan orsaka obehag", descriptive: "Publikationen innehåller inget ljud som kan orsaka obehag" }, "sound-unknown": { compact: "Förekomst av ljud som kan orsaka obehag är okänd", descriptive: "Förekomst av ljud som kan orsaka obehag är okänd" }, title: "Risker", unknown: "Förekomst av risker är okänd" }, "legal-considerations": { exempt: { compact: "Åberopar ett undantag från vissa lagstadgade tillgänglighetskrav", descriptive: "Publikationen åberopar ett undantag från vissa lagstadgade tillgänglighetskrav" }, "no-metadata": "Information saknas", title: "Juridiska aspekter" }, navigation: { index: { compact: "Register", descriptive: "Register med länkar till innehållet" }, "no-metadata": "Information saknas", "page-navigation": { compact: "Gå till sida", descriptive: "Sidindelning för navigering enligt sidnummer i tryckt förlaga" }, structural: { compact: "Rubriker", descriptive: "Navigerbara element såsom rubriker eller tabeller" }, title: "Navigering", toc: { compact: "Innehållsförteckning", descriptive: "Innehållsförteckning med länkar till alla kapitel" } }, "rich-content": { "accessible-chemistry-as-latex": { compact: "Kemiska formler i LaTeX", descriptive: "Kemiska formler i tillgängligt format (LaTeX)" }, "accessible-chemistry-as-mathml": { compact: "Kemiska formler i MathML", descriptive: "Kemiska formler i tillgängligt format (MathML)" }, "accessible-math-as-latex": { compact: "Matematik som LaTeX", descriptive: "Matematiska formler i tillgängligt format (LaTeX)" }, "accessible-math-described": "Innehåller textbeskrivningar av matematik", "closed-captions": { compact: "Videoklipp har undertext som kan sättas på/stängas av", descriptive: "Videoklipp som ingår i publikationen har undertext som kan sättas på och stängas av (stängda undertexter)" }, "extended-descriptions": "Informationsrika bilder har utökade bildbeskrivningar", "math-as-mathml": { compact: "Matematik som MathML", descriptive: "Matematiska formler i tillgängligt format (MathML)" }, "open-captions": { compact: "Videoklipp har undertext som inte kan stängas av", descriptive: "Videoklipp som ingår i publikationen har undertext som inte kan stängas av (öppna undertexter)" }, title: "Berikat innehåll", transcript: "Innehåller transkriptioner", unknown: "Information saknas" }, "ways-of-reading": { "nonvisual-reading": { "alt-text": { compact: "Har textalternativ (alt-texter)", descriptive: "Har textalternativ (alt-texter) till bilder" }, "no-metadata": "Information om icke-visuell läsbarhet saknas", none: { compact: "Kan inte läsas med uppläsningsfunktion eller punktskriftsskärm", descriptive: "Innehållet går inte att läsa med uppläsningsfunktion eller punktskriftsskärm" }, "not-fully": { compact: "Inte läsbart i sin helhet med uppläsningsfunktion eller punktskriftsskärm", descriptive: "Allt innehåll går inte att läsa med uppläsningsfunktion eller punktskriftsskärm" }, readable: { compact: "Kan läsas med uppläsningsfunktion eller punktskriftsskärm", descriptive: "Hela innehållet går att läsa med uppläsningsfunktion eller punktskriftsskärm" } }, "prerecorded-audio": { complementary: { compact: "Förinspelade ljudklipp", descriptive: "Innehåller förinspelade ljudklipp" }, "no-metadata": "Information om förinspelat ljud saknas", only: { compact: "Endast förinspelat ljud", descriptive: "Bok med ljud utan textalternativ" }, synchronized: { compact: "Förinspelat ljud synkroniserat med texten", descriptive: "Hela innehållet finns tillgängligt som förinspelat ljud synkroniserat med texten" } }, title: "Olika sätt att läsa", "visual-adjustments": { modifiable: { compact: "Utseendet kan justeras", descriptive: "Det går att justera text och layout i den utsträckning som läsprogrammet tillåter, till exempel typsnitt, storlek på text, avstånd mellan rader och stycken samt färg på text och bakgrund" }, unknown: "Information om möjlighet att justera utseende saknas", unmodifiable: { compact: "Utseendet kan inte justeras", descriptive: "Text- och sidlayout kan inte justeras eftersom presentationen liknar en tryckt version, men läsprogram kan ha funktioner för att zooma in" } } } } }, altIdentifier_one: "alternativ identifierare", altIdentifier_other: "alternativa identifierare", artist_one: "konstnär", artist_other: "konstnärer", author_one: "författare", author_other: "författare", collection_one: "samling", collection_other: "samlingar", colorist_one: "kolorist", colorist_other: "kolorister", contributor_one: "medverkande", contributor_other: "medverkande", description: "beskrivning", duration: "speltid", editor_one: "redaktör", editor_other: "redaktörer", identifier_one: "identifierare", identifier_other: "identifierare", illustrator_one: "illustratör", illustrator_other: "illustratörer", imprint_one: "imprint", imprint_other: "imprint", inker_one: "tuschare", inker_other: "tuschare", language_one: "språk", language_other: "språk", letterer_one: "textare", letterer_other: "textare", modified: "ändringsdatum", narrator_one: "berättarröst", narrator_other: "berättarröster", numberOfPages: "sidantal", penciler_one: "tecknare", penciler_other: "tecknare", published: "publikationsdatum", publisher_one: "förlag", publisher_other: "förlag", series_one: "serie", series_other: "serier", subject_one: "ämne", subject_other: "ämnen", subtitle: "undertitel", title: "titel", translator_one: "översättare", translator_other: "översättare" } }, t = {
2
+ publication: e
3
+ };
4
+ export {
5
+ t as default,
6
+ e as publication
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator",
3
- "version": "2.2.7",
3
+ "version": "2.2.9",
4
4
  "type": "module",
5
5
  "description": "Next generation SDK for publications in Web Apps",
6
6
  "author": "readium",
@@ -45,7 +45,8 @@
45
45
  "node": ">=18"
46
46
  },
47
47
  "scripts": {
48
- "build": "node scripts/generate-css-selector.js && tsc && vite build",
48
+ "clean": "rimraf types dist",
49
+ "build": "pnpm clean && node scripts/generate-css-selector.js && tsc && vite build",
49
50
  "generate:css-selector": "node scripts/generate-css-selector.js"
50
51
  },
51
52
  "devDependencies": {
@@ -56,6 +57,7 @@
56
57
  "@types/path-browserify": "^1.0.3",
57
58
  "css-selector-generator": "^3.8.0",
58
59
  "path-browserify": "^1.0.1",
60
+ "rimraf": "^6.1.2",
59
61
  "tslib": "^2.8.1",
60
62
  "typescript": "^5.9.3",
61
63
  "typescript-plugin-css-modules": "^5.2.0",
@@ -0,0 +1,13 @@
1
+ (function() {
2
+ if(window.onload) window.onload = new Proxy(window.onload, {
3
+ apply: function(target, receiver, args) {
4
+ if(!window._readium_blockEvents) {
5
+ Reflect.apply(target, receiver, args);
6
+ return;
7
+ }
8
+ _readium_blockedEvents.push([
9
+ 0, target, receiver, args
10
+ ]);
11
+ }
12
+ });
13
+ })();
@@ -0,0 +1,65 @@
1
+ // Note: we aren't blocking some of the events right now to try and be as nonintrusive as possible.
2
+ // For a more comprehensive implementation, see https://github.com/hackademix/noscript/blob/3a83c0e4a506f175e38b0342dad50cdca3eae836/src/content/syncFetchPolicy.js#L142
3
+ // The snippet of code at the beginning of this source is an attempt at defence against JS using persistent storage
4
+ (function() {
5
+ const noop = () => {}, emptyObj = {}, emptyPromise = () => Promise.resolve(void 0), fakeStorage = {
6
+ getItem: noop,
7
+ setItem: noop,
8
+ removeItem: noop,
9
+ clear: noop,
10
+ key: noop,
11
+ length: 0
12
+ };
13
+
14
+ ["localStorage", "sessionStorage"].forEach((e) => Object.defineProperty(window, e, {
15
+ get: () => fakeStorage,
16
+ configurable: !0
17
+ }));
18
+
19
+ Object.defineProperty(document, "cookie", {
20
+ get: () => "",
21
+ set: noop,
22
+ configurable: !0
23
+ });
24
+
25
+ Object.defineProperty(window, "indexedDB", {
26
+ get: () => {},
27
+ configurable: !0
28
+ });
29
+
30
+ Object.defineProperty(window, "caches", {
31
+ get: () => emptyObj,
32
+ configurable: !0
33
+ });
34
+
35
+ Object.defineProperty(navigator, "storage", {
36
+ get: () => ({
37
+ persist: emptyPromise,
38
+ persisted: emptyPromise,
39
+ estimate: () => Promise.resolve({quota: 0, usage: 0})
40
+ }),
41
+ configurable: !0
42
+ });
43
+
44
+ Object.defineProperty(navigator, "serviceWorker", {
45
+ get: () => ({
46
+ register: emptyPromise,
47
+ getRegistration: emptyPromise,
48
+ ready: emptyPromise()
49
+ }),
50
+ configurable: !0
51
+ });
52
+
53
+ window._readium_blockedEvents = [];
54
+ window._readium_blockEvents = true;
55
+ window._readium_eventBlocker = (e) => {
56
+ if(!window._readium_blockEvents) return;
57
+ e.preventDefault();
58
+ e.stopImmediatePropagation();
59
+ _readium_blockedEvents.push([
60
+ 1, e, e.currentTarget || e.target
61
+ ]);
62
+ };
63
+ window.addEventListener("DOMContentLoaded", window._readium_eventBlocker, true);
64
+ window.addEventListener("load", window._readium_eventBlocker, true);
65
+ })();
@@ -0,0 +1,4 @@
1
+ // WebPub-specific setup - no execution blocking needed
2
+ window._readium_blockedEvents = [];
3
+ window._readium_blockEvents = false; // WebPub doesn't need event blocking
4
+ window._readium_eventBlocker = null;
@@ -14,12 +14,16 @@ import { EpubPreferencesEditor } from "./preferences/EpubPreferencesEditor";
14
14
  import { ReadiumCSS } from "./css/ReadiumCSS";
15
15
  import { RSProperties, UserProperties } from "./css/Properties";
16
16
  import { getContentWidth } from "../helpers/dimensions";
17
+ import { Injector } from "../injection/Injector";
18
+ import { createReadiumEpubRules } from "../injection/epubInjectables";
19
+ import { IInjectablesConfig } from "../injection/Injectable";
17
20
 
18
21
  export type ManagerEventKey = "zoom";
19
22
 
20
23
  export interface EpubNavigatorConfiguration {
21
24
  preferences: IEpubPreferences;
22
25
  defaults: IEpubDefaults;
26
+ injectables?: IInjectablesConfig;
23
27
  }
24
28
 
25
29
  export interface EpubNavigatorListeners {
@@ -65,6 +69,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
65
69
  private _settings: EpubSettings;
66
70
  private _css: ReadiumCSS;
67
71
  private _preferencesEditor: EpubPreferencesEditor | null = null;
72
+ private readonly _injector: Injector | null = null;
68
73
 
69
74
  private resizeObserver: ResizeObserver;
70
75
 
@@ -106,6 +111,15 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
106
111
  this._layout = EpubNavigator.determineLayout(pub, !!this._settings.scroll);
107
112
  this.currentProgression = pub.metadata.effectiveReadingProgression;
108
113
 
114
+ // Combine Readium rules with user-provided injectables
115
+ const readiumRules = createReadiumEpubRules(pub.metadata);
116
+ const userConfig = configuration.injectables || { rules: [], allowedDomains: [] };
117
+
118
+ this._injector = new Injector({
119
+ rules: [...readiumRules, ...userConfig.rules],
120
+ allowedDomains: userConfig.allowedDomains
121
+ });
122
+
109
123
  // We use a resizeObserver cos’ the container parent may not be the width of
110
124
  // the document/window e.g. app using a docking system with left and right panels.
111
125
  // If we observe this.container, that won’t obviously work since we set its width.
@@ -136,14 +150,24 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
136
150
  if (!this.positions?.length)
137
151
  this.positions = await this.pub.positionsFromManifest();
138
152
  if(this._layout === Layout.fixed) {
139
- this.framePool = new FXLFramePoolManager(this.container, this.positions, this.pub);
153
+ this.framePool = new FXLFramePoolManager(
154
+ this.container,
155
+ this.positions,
156
+ this.pub,
157
+ this._injector
158
+ );
140
159
  this.framePool.listener = (key: CommsEventKey | ManagerEventKey, data: unknown) => {
141
160
  this.eventListener(key, data);
142
161
  }
143
162
  } else {
144
163
  await this.updateCSS(false);
145
164
  const cssProperties = this.compileCSSProperties(this._css);
146
- this.framePool = new FramePoolManager(this.container, this.positions, cssProperties);
165
+ this.framePool = new FramePoolManager(
166
+ this.container,
167
+ this.positions,
168
+ cssProperties,
169
+ this._injector
170
+ );
147
171
  }
148
172
 
149
173
  if(this.currentLocation === undefined)
@@ -1,86 +1,6 @@
1
1
  import { MediaType } from "@readium/shared";
2
2
  import { Link, Publication } from "@readium/shared";
3
-
4
- // Readium CSS imports
5
- // The "?inline" query is to prevent some bundlers from injecting these into the page (e.g. vite)
6
- // @ts-ignore
7
- import readiumCSSAfter from "@readium/css/css/dist/ReadiumCSS-after.css?inline";
8
- // @ts-ignore
9
- import readiumCSSBefore from "@readium/css/css/dist/ReadiumCSS-before.css?inline";
10
- // @ts-ignore
11
- import readiumCSSDefault from "@readium/css/css/dist/ReadiumCSS-default.css?inline";
12
-
13
- // Import the pre-built CSS selector generator
14
- // This has to be injected because you need to be in the iframe's context for it to work properly
15
- import cssSelectorGeneratorContent from "../../dom/_readium_cssSelectorGenerator.js?raw";
16
-
17
- // Utilities
18
- const blobify = (source: string, type: string) => URL.createObjectURL(new Blob([source], { type }));
19
- const stripJS = (source: string) => source.replace(/\/\/.*/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\n/g, "").replace(/\s+/g, " ");
20
- const stripCSS = (source: string) => source.replace(/\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '').replace(/ {2,}/g, ' ')
21
- // Fully resolve absolute local URLs created by bundlers since it's going into a blob
22
- .replace(/url\((?!(https?:)?\/\/)("?)\/([^\)]+)/g, `url($2${window.location.origin}/$3`);
23
- const scriptify = (doc: Document, source: string) => {
24
- const s = doc.createElement("script");
25
- s.dataset.readium = "true";
26
- s.src = source.startsWith("blob:") ? source : blobify(source, "text/javascript");
27
- return s;
28
- }
29
- const styleify = (doc: Document, source: string) => {
30
- const s = doc.createElement("link");
31
- s.dataset.readium = "true";
32
- s.rel = "stylesheet";
33
- s.type = "text/css";
34
- s.href = source.startsWith("blob:") ? source : blobify(source, "text/css");
35
- return s;
36
- }
37
-
38
- type CacheFunction = () => string;
39
- const resourceBlobCache = new Map<string, string>();
40
- const cached = (key: string, cacher: CacheFunction) => {
41
- if(resourceBlobCache.has(key)) return resourceBlobCache.get(key)!;
42
- const value = cacher();
43
- resourceBlobCache.set(key, value);
44
- return value;
45
- };
46
-
47
- const cssSelectorGenerator = (doc: Document) => scriptify(doc, cached("css-selector-generator", () => blobify(
48
- cssSelectorGeneratorContent,
49
- "text/javascript"
50
- )));
51
-
52
- // Note: we aren't blocking some of the events right now to try and be as nonintrusive as possible.
53
- // For a more comprehensive implementation, see https://github.com/hackademix/noscript/blob/3a83c0e4a506f175e38b0342dad50cdca3eae836/src/content/syncFetchPolicy.js#L142
54
- // The snippet of code at the beginning of this source is an attempt at defence against JS using persistent storage
55
- const rBefore = (doc: Document) => scriptify(doc, cached("JS-Before", () => blobify(stripJS(`
56
- const noop=()=>{},emptyObj={},emptyPromise=()=>Promise.resolve(void 0),fakeStorage={getItem:noop,setItem:noop,removeItem:noop,clear:noop,key:noop,length:0};["localStorage","sessionStorage"].forEach((e=>Object.defineProperty(window,e,{get:()=>fakeStorage,configurable:!0}))),Object.defineProperty(document,"cookie",{get:()=>"",set:noop,configurable:!0}),Object.defineProperty(window,"indexedDB",{get:()=>{},configurable:!0}),Object.defineProperty(window,"caches",{get:()=>emptyObj,configurable:!0}),Object.defineProperty(navigator,"storage",{get:()=>({persist:emptyPromise,persisted:emptyPromise,estimate:()=>Promise.resolve({quota:0,usage:0})}),configurable:!0}),Object.defineProperty(navigator,"serviceWorker",{get:()=>({register:emptyPromise,getRegistration:emptyPromise,ready:emptyPromise()}),configurable:!0});
57
-
58
- window._readium_blockedEvents = [];
59
- window._readium_blockEvents = true;
60
- window._readium_eventBlocker = (e) => {
61
- if(!window._readium_blockEvents) return;
62
- e.preventDefault();
63
- e.stopImmediatePropagation();
64
- _readium_blockedEvents.push([
65
- 1, e, e.currentTarget || e.target
66
- ]);
67
- };
68
- window.addEventListener("DOMContentLoaded", window._readium_eventBlocker, true);
69
- window.addEventListener("load", window._readium_eventBlocker, true);`
70
- ), "text/javascript")));
71
- const rAfter = (doc: Document) => scriptify(doc, cached("JS-After", () => blobify(stripJS(`
72
- if(window.onload) window.onload = new Proxy(window.onload, {
73
- apply: function(target, receiver, args) {
74
- if(!window._readium_blockEvents) {
75
- Reflect.apply(target, receiver, args);
76
- return;
77
- }
78
- _readium_blockedEvents.push([
79
- 0, target, receiver, args
80
- ]);
81
- }
82
- });`
83
- ), "text/javascript")));
3
+ import { Injector } from "../../injection/Injector";
84
4
 
85
5
  const csp = (domains: string[]) => {
86
6
  const d = domains.join(" ");
@@ -105,12 +25,22 @@ export default class FrameBlobBuider {
105
25
  private readonly burl: string;
106
26
  private readonly pub: Publication;
107
27
  private readonly cssProperties?: { [key: string]: string };
108
-
109
- constructor(pub: Publication, baseURL: string, item: Link, cssProperties?: { [key: string]: string }) {
28
+ private readonly injector: Injector | null = null;
29
+
30
+ constructor(
31
+ pub: Publication,
32
+ baseURL: string,
33
+ item: Link,
34
+ options: {
35
+ cssProperties?: { [key: string]: string };
36
+ injector?: Injector | null;
37
+ }
38
+ ) {
110
39
  this.pub = pub;
111
40
  this.item = item;
112
41
  this.burl = item.toURL(baseURL) || "";
113
- this.cssProperties = cssProperties;
42
+ this.cssProperties = options.cssProperties;
43
+ this.injector = options.injector ?? null;
114
44
  }
115
45
 
116
46
  public async build(fxl = false): Promise<string> {
@@ -128,15 +58,23 @@ export default class FrameBlobBuider {
128
58
  // Load the HTML resource
129
59
  const txt = await this.pub.get(this.item).readAsString();
130
60
  if(!txt) throw new Error(`Failed reading item ${this.item.href}`);
61
+
131
62
  const doc = new DOMParser().parseFromString(
132
63
  txt,
133
64
  this.item.mediaType.string as DOMParserSupportedType
134
65
  );
66
+
135
67
  const perror = doc.querySelector("parsererror");
136
- if(perror) {
68
+ if (perror) {
137
69
  const details = perror.querySelector("div");
138
70
  throw new Error(`Failed parsing item ${this.item.href}: ${details?.textContent || perror.textContent}`);
139
71
  }
72
+
73
+ // Apply resource injections if injection service is provided
74
+ if (this.injector) {
75
+ await this.injector.injectForDocument(doc, this.item);
76
+ }
77
+
140
78
  return this.finalizeDOM(doc, this.pub.baseURL, this.burl, this.item.mediaType, fxl, this.cssProperties);
141
79
  }
142
80
 
@@ -151,29 +89,6 @@ export default class FrameBlobBuider {
151
89
  return this.finalizeDOM(doc, this.pub.baseURL, this.burl, this.item.mediaType, true);
152
90
  }
153
91
 
154
- // Has JS that may have side-effects when the document is loaded, without any user interaction
155
- private hasExecutable(doc: Document): boolean {
156
- // This is not a 100% comprehensive check of all possibilities for JS execution,
157
- // but it covers what the prevention scripts cover. Other possibilities include:
158
- // - <iframe> src
159
- // - <img> with onload/onerror
160
- // - <meta http-equiv="refresh" content="xxx">
161
- return (
162
- !!doc.querySelector("script") || // Any <script> elements
163
- !!doc.querySelector("body[onload]:not(body[onload=''])") // <body> that executes JS on load
164
- );
165
- }
166
-
167
- private hasStyle(doc: Document): boolean {
168
- if(
169
- doc.querySelector("link[rel='stylesheet']") || // Any CSS link
170
- doc.querySelector("style") || // Any <style> element
171
- doc.querySelector("[style]:not([style=''])") // Any element with style attribute set
172
- ) return true;
173
-
174
- return false;
175
- }
176
-
177
92
  private setProperties(cssProperties: { [key: string]: string }, doc: Document) {
178
93
  for (const key in cssProperties) {
179
94
  const value = cssProperties[key];
@@ -184,22 +99,20 @@ export default class FrameBlobBuider {
184
99
  private finalizeDOM(doc: Document, root: string | undefined, base: string | undefined, mediaType: MediaType, fxl = false, cssProperties?: { [key: string]: string }): string {
185
100
  if(!doc) return "";
186
101
 
187
- // Inject styles
188
- if(!fxl) {
189
- // Readium CSS Before
190
- const rcssBefore = styleify(doc, cached("ReadiumCSS-before", () => blobify(stripCSS(readiumCSSBefore), "text/css")));
191
- doc.head.firstChild ? doc.head.firstChild.before(rcssBefore) : doc.head.appendChild(rcssBefore);
192
-
193
- // Readium CSS defaults
194
- if(!this.hasStyle(doc))
195
- rcssBefore.after(styleify(doc, cached("ReadiumCSS-default", () => blobify(stripCSS(readiumCSSDefault), "text/css"))))
102
+ // Get allowed domains from injector if it exists
103
+ const allowedDomains = this.injector?.getAllowedDomains?.() || [];
104
+
105
+ // Always include the root domain if provided
106
+ const domains = [...new Set([
107
+ ...(root ? [root] : []),
108
+ ...allowedDomains
109
+ ])].filter(Boolean);
196
110
 
197
- // Readium CSS After
198
- doc.head.appendChild(styleify(doc, cached("ReadiumCSS-after", () => blobify(stripCSS(readiumCSSAfter), "text/css"))));
111
+ // CSS and script injection is now handled by the Injector system
199
112
 
200
- if (cssProperties) {
201
- this.setProperties(cssProperties, doc);
202
- }
113
+ // Apply CSS properties if provided (only for reflowable)
114
+ if (cssProperties && !fxl) {
115
+ this.setProperties(cssProperties, doc);
203
116
  }
204
117
 
205
118
  // Set all <img> elements to high priority
@@ -264,20 +177,13 @@ export default class FrameBlobBuider {
264
177
  doc.head.firstChild!.before(b);
265
178
  }
266
179
 
267
- // Inject script to prevent in-publication scripts from executing until we want them to
268
- const hasExecutable = this.hasExecutable(doc);
269
- if (hasExecutable) doc.head.firstChild!.before(rBefore(doc));
270
- doc.head.firstChild!.before(cssSelectorGenerator(doc)); // CSS selector utility
271
- if (hasExecutable) doc.head.appendChild(rAfter(doc)); // Another execution prevention script
272
-
273
- // Add CSP
180
+ // Add CSP with allowed domains
274
181
  const meta = doc.createElement("meta");
275
182
  meta.httpEquiv = "Content-Security-Policy";
276
- meta.content = csp(root ? [root] : []);
183
+ meta.content = csp(domains);
277
184
  meta.dataset.readium = "true";
278
185
  doc.head.firstChild!.before(meta);
279
186
 
280
-
281
187
  // Make blob from doc
282
188
  return URL.createObjectURL(
283
189
  new Blob([new XMLSerializer().serializeToString(doc)], {
@@ -2,6 +2,7 @@ import { ModuleName } from "@readium/navigator-html-injectables";
2
2
  import { Locator, Publication } from "@readium/shared";
3
3
  import FrameBlobBuider from "./FrameBlobBuilder";
4
4
  import { FrameManager } from "./FrameManager";
5
+ import { Injector } from "../../injection/Injector";
5
6
 
6
7
  const UPPER_BOUNDARY = 5;
7
8
  const LOWER_BOUNDARY = 3;
@@ -16,11 +17,18 @@ export class FramePoolManager {
16
17
  private readonly inprogress: Map<string, Promise<void>> = new Map();
17
18
  private pendingUpdates: Map<string, { inPool: boolean }> = new Map();
18
19
  private currentBaseURL: string | undefined;
20
+ private readonly injector: Injector | null = null;
19
21
 
20
- constructor(container: HTMLElement, positions: Locator[], cssProperties?: { [key: string]: string }) {
22
+ constructor(
23
+ container: HTMLElement,
24
+ positions: Locator[],
25
+ cssProperties?: { [key: string]: string },
26
+ injector?: Injector | null
27
+ ) {
21
28
  this.container = container;
22
29
  this.positions = positions;
23
30
  this.currentCssProperties = cssProperties;
31
+ this.injector = injector ?? null;
24
32
  }
25
33
 
26
34
  async destroy() {
@@ -47,7 +55,13 @@ export class FramePoolManager {
47
55
  this.pool.clear();
48
56
 
49
57
  // Revoke all blobs
50
- this.blobs.forEach(v => URL.revokeObjectURL(v));
58
+ this.blobs.forEach(v => {
59
+ this.injector?.releaseBlobUrl?.(v);
60
+ URL.revokeObjectURL(v);
61
+ });
62
+
63
+ // Clean up injector if it exists
64
+ this.injector?.dispose();
51
65
 
52
66
  // Empty container of elements
53
67
  this.container.childNodes.forEach(v => {
@@ -90,7 +104,10 @@ export class FramePoolManager {
90
104
  // Check if base URL of publication has changed
91
105
  if(this.currentBaseURL !== undefined && pub.baseURL !== this.currentBaseURL) {
92
106
  // Revoke all blobs
93
- this.blobs.forEach(v => URL.revokeObjectURL(v));
107
+ this.blobs.forEach(v => {
108
+ this.injector?.releaseBlobUrl?.(v);
109
+ URL.revokeObjectURL(v);
110
+ });
94
111
  this.blobs.clear();
95
112
  }
96
113
  this.currentBaseURL = pub.baseURL;
@@ -103,13 +120,17 @@ export class FramePoolManager {
103
120
  // when navigating backwards, where paginated will go the
104
121
  // start of the resource instead of the end due to the
105
122
  // corrupted width ColumnSnapper (injectables) gets on init
106
- this.blobs.forEach(v => URL.revokeObjectURL(v));
123
+ this.blobs.forEach(v => {
124
+ this.injector?.releaseBlobUrl?.(v);
125
+ URL.revokeObjectURL(v);
126
+ });
107
127
  this.blobs.clear();
108
128
  this.pendingUpdates.clear();
109
129
  }
110
130
  if(this.pendingUpdates.has(href) && this.pendingUpdates.get(href)?.inPool === false) {
111
131
  const url = this.blobs.get(href);
112
132
  if(url) {
133
+ this.injector?.releaseBlobUrl?.(url);
113
134
  URL.revokeObjectURL(url);
114
135
  this.blobs.delete(href);
115
136
  this.pendingUpdates.delete(href);
@@ -129,7 +150,15 @@ export class FramePoolManager {
129
150
  const itm = pub.readingOrder.findWithHref(href);
130
151
  if(!itm) return; // TODO throw?
131
152
  if(!this.blobs.has(href)) {
132
- const blobBuilder = new FrameBlobBuider(pub, this.currentBaseURL || "", itm, this.currentCssProperties);
153
+ const blobBuilder = new FrameBlobBuider(
154
+ pub,
155
+ this.currentBaseURL || "",
156
+ itm,
157
+ {
158
+ cssProperties: this.currentCssProperties,
159
+ injector: this.injector
160
+ }
161
+ );
133
162
  const blobURL = await blobBuilder.build();
134
163
  this.blobs.set(href, blobURL);
135
164
  }