diff --git a/reader.js b/reader.js index 00b40e5..b291167 100644 --- a/reader.js +++ b/reader.js @@ -1,7 +1,6 @@ import { View, getPosition } from './view.js' import { createTOCView } from './ui/tree.js' import { createMenu } from './ui/menu.js' -import { createPopover } from './ui/popover.js' const isZip = async file => { const arr = new Uint8Array(await file.slice(0, 4).arrayBuffer()) @@ -245,8 +244,6 @@ class Reader { switch (obj.type) { case 'loaded': this.#onLoaded(obj); break case 'relocated': this.#onRelocated(obj); break - case 'reference': this.#onReference(obj); break - case 'external-link': globalThis.open(obj.uri, '_blank'); break } } #handleKeydown(event) { @@ -269,31 +266,6 @@ class Reader { slider.title = `${percent} ยท ${loc}` if (tocItem?.href) this.#tocView?.setCurrentHref?.(tocItem.href) } - #onReference(obj) { - const { content, element } = obj - const { point, dir } = getPosition(element) - const iframe = document.createElement('iframe') - iframe.sandbox = 'allow-same-origin' - iframe.srcdoc = content - iframe.onload = () => { - const doc = iframe.contentDocument - doc.documentElement.style.colorScheme = 'light dark' - doc.body.style.margin = '18px' - } - Object.assign(iframe.style, { - border: '0', - width: '100%', - height: '100%', - }) - const { popover, arrow, overlay } = createPopover(300, 250, point, dir) - overlay.style.zIndex = 3 - popover.style.zIndex = 3 - arrow.style.zIndex = 3 - popover.append(iframe) - document.body.append(overlay) - document.body.append(popover) - document.body.append(arrow) - } } const open = async file => { diff --git a/ui/popover.js b/ui/popover.js deleted file mode 100644 index 9e803ea..0000000 --- a/ui/popover.js +++ /dev/null @@ -1,63 +0,0 @@ -const clamp = (min, max, x) => Math.min(max, Math.max(min, x)) - -const arrowHeight = 10 -const arrowWidth = 20 -const radius = 6 - -const createSVGElement = tag => - document.createElementNS('http://www.w3.org/2000/svg', tag) - -const createArrow = down => { - const h = arrowHeight + 1 - const svg = createSVGElement('svg') - svg.setAttribute('aria-hidden', 'true') - svg.setAttribute('width', arrowWidth) - svg.setAttribute('height', arrowHeight) - const polygon = createSVGElement('polygon') - polygon.setAttribute('points', down - ? `0 ${h}, ${arrowWidth / 2} 0, ${arrowWidth} ${h}` - : `0 0, ${arrowWidth / 2} ${h}, ${arrowWidth} 0`) - svg.classList.add(down ? 'popover-arrow-down' : 'popover-arrow-up') - svg.append(polygon) - return svg -} - -export const createPopover = (width, height, { x, y }, dir) => { - const down = dir === 'down' - const fullHeight = height + arrowHeight - const overlay = document.createElement('div') - Object.assign(overlay.style, { - position: 'absolute', - left: '0', - top: '0', - width: '100vw', - height: '100vh', - }) - const arrow = createArrow(down) - const top = clamp(0, window.innerHeight - (down ? height : fullHeight), - down ? y + arrowHeight : y - fullHeight) - Object.assign(arrow.style, { - position: 'absolute', - left: `${clamp(radius, window.innerWidth - arrowWidth - radius, - x - arrowWidth / 2)}px`, - top: `${down ? top - arrowHeight : top + height}px`, - }) - const popover = document.createElement('div') - popover.setAttribute('role', 'dialog') - popover.classList.add('popover') - Object.assign(popover.style, { - position: 'absolute', - boxSizing: 'border-box', - overflow: 'hidden', - left: `${clamp(0, window.innerWidth - width, x - width / 2)}px`, - top: `${top}px`, - width: `${width}px`, - height: `${height}px`, - }) - overlay.addEventListener('click', () => { - overlay.parentNode.removeChild(overlay) - arrow.parentNode.removeChild(arrow) - popover.parentNode.removeChild(popover) - }) - return { popover, arrow, overlay } -} diff --git a/view.js b/view.js index 431d286..1bb0b2d 100644 --- a/view.js +++ b/view.js @@ -66,15 +66,6 @@ export const getPosition = target => { return start.point.y > window.innerHeight - end.point.y ? start : end } -// https://www.w3.org/TR/epub-ssv-11/ -const SSV = Object.fromEntries(Array.from(Object.entries({ - isRef: ['annoref', 'biblioref', 'glossref', 'noteref'], - isLink: ['backlink'], - isNote: ['annotation', 'note', 'footnote', 'endnote', 'rearnote'], -}), ([k, v]) => [k, el => - el.getAttributeNS('http://www.idpf.org/2007/ops', 'type') - ?.split(/s/)?.some(t => v.includes(t))])) - export class View { #sectionProgress #tocProgress @@ -150,8 +141,12 @@ export class View { doc.documentElement.dir ||= this.isCJK ? '' : this.textDirection this.renderer.setStyle?.(this.#css) + this.handleLinks(doc, index, this.emit) - // set handlers for links + this.emit?.({ type: 'loaded', doc, index }) + } + handleLinks(doc, index, emit) { + const { book } = this const section = book.sections[index] for (const a of doc.querySelectorAll('a[href]')) a.addEventListener('click', e => { @@ -159,25 +154,13 @@ export class View { const href = a.getAttribute('href') const uri = section?.resolveHref?.(href) ?? href if (book?.isExternal?.(uri)) - this.emit?.({ type: 'external-link', uri }) - else if (SSV.isRef(a)) { - const { index, anchor } = book.resolveHref(uri) - Promise.resolve(book.sections[index].createDocument()) - .then(doc => [anchor(doc), doc.contentType]) - .then(([el, type]) => - [el?.innerHTML, type, SSV.isNote(el)]) - .then(([content, contentType, isNote]) => content - ? this.emit?.({ - type: 'reference', - href: isNote ? null : uri, - content, contentType, element: a, - }) : null) + Promise.resolve(emit?.({ type: 'external-link', a, uri })) + .then(x => x ? null : window.open(uri, '_blank')) + .catch(e => console.error(e)) + else Promise.resolve(emit?.({ type: 'link', a, uri })) + .then(x => x ? null : this.goTo(uri)) .catch(e => console.error(e)) - return - } else this.goTo(uri) }) - - this.emit?.({ type: 'loaded', doc, index }) } async addAnnotation(annotation, remove) { const { value } = annotation