diff --git a/.epub/example/OEBPS/scripts/ipub.js b/.epub/example/OEBPS/scripts/ipub.js new file mode 100644 index 0000000..763fb2a --- /dev/null +++ b/.epub/example/OEBPS/scripts/ipub.js @@ -0,0 +1,98 @@ +"use strict"; + +globalThis.addEventListener("load", () => { + console.log("IPUB SCRIPT LOADED"); + + /** @type {Map} */ + const onScreenMap = new Map(); + + const observer = new IntersectionObserver((entries) => { + entries.forEach((e) => { + if (e.intersectionRatio > 0) { + console.debug( + `IntersectionObserver: adding element #${e.target.id} to onScreenMap`, + ); + onScreenMap.set(e.target.id, e.target); + } else { + console.debug( + `IntersectionObserver: removing element #${e.target.id} to onScreenMap`, + ); + onScreenMap.delete(e.target.id); + } + }); + }); + + for (const element of document.querySelectorAll( + `[data-ipub-trigger="on-screen"]`, + )) { + observer.observe(element); + } + + document.addEventListener("scroll", async () => { + for (const [id, element] of onScreenMap) { + const perc = getPercentageInView(element); + console.debug(`Element #${id} is now ${perc}% on screen`); + + const played = element.getAttribute("data-ipub-trigger-played") == "true"; + + if (perc >= 100 && !played) { + await playIpubElement(element); + element.setAttribute("data-ipub-trigger-played", "true"); + } + } + }); +}); + +/** + * @param {Element} element + */ +async function playIpubElement(element) { + switch (element.tagName) { + case "audio": { + /** @type {HTMLAudioElement} */ + const audio = element; + + await audio.play(); + + break; + } + default: + break; + } +} + +/** + * @param {Element} element + * @returns {number} + */ +function getPercentageInView(element) { + const viewTop = globalThis.pageYOffset; + const viewBottom = viewTop + globalThis.innerHeight; + + const rect = element.getBoundingClientRect(); + const elementTop = rect.y + viewTop; + const elementBottom = rect.y + rect.height + viewTop; + + if (viewTop > elementBottom || viewBottom < elementTop) { + return 0; + } + + if ( + (viewTop < elementTop && viewBottom > elementBottom) || + (elementTop < viewTop && elementBottom > viewBottom) + ) { + return 100; + } + + let inView = rect.height; + + if (elementTop < viewTop) { + inView = rect.height - (globalThis.pageYOffset - elementTop); + } + + if (elementBottom > viewBottom) { + inView = inView - (elementBottom - viewBottom); + } + + return Math.round((inView / globalThis.innerHeight) * 100); +} diff --git a/.epub/example/OEBPS/sections/section0001.xhtml b/.epub/example/OEBPS/sections/section0001.xhtml index 79d7c03..e026502 100644 --- a/.epub/example/OEBPS/sections/section0001.xhtml +++ b/.epub/example/OEBPS/sections/section0001.xhtml @@ -59,6 +59,39 @@ we want the editor to "guess" what controls to provide the user with. --> +
+ + + + + +
+
+ + + +
+
+ + + +
+
+ + + +