diff --git a/src/dom/common/lib/nodes.ts b/src/dom/common/lib/nodes.ts index a4ce4b69..f0d20583 100644 --- a/src/dom/common/lib/nodes.ts +++ b/src/dom/common/lib/nodes.ts @@ -58,3 +58,14 @@ export function getContainingBlock(element: Element): Element | null { } return null; } + +export function iterateWalker(walker: TreeWalker | NodeIterator): Iterable { + return { + [Symbol.iterator]: function* () { + let node: Node | null; + while ((node = walker.nextNode())) { + yield node; + } + } + }; +} diff --git a/src/dom/common/lib/range.ts b/src/dom/common/lib/range.ts index 6c62cab5..7dc1d490 100644 --- a/src/dom/common/lib/range.ts +++ b/src/dom/common/lib/range.ts @@ -1,4 +1,5 @@ import { isFirefox, isWin } from "../../../common/lib/utilities"; +import { iterateWalker } from "./nodes"; /** * Wraps the properties of a Range object in a static structure so that they don't change when the DOM changes. @@ -73,8 +74,7 @@ export function moveRangeEndsIntoTextNodes(range: Range): Range { if (!endNode || endNode.nodeType !== Node.TEXT_NODE) { // Get the last text node inside the container/child let walker = doc.createTreeWalker(endNode || range.endContainer, NodeFilter.SHOW_TEXT); - let node; - while ((node = walker.nextNode())) { + for (let node of iterateWalker(walker)) { endNode = node; } } diff --git a/src/dom/common/lib/selector.ts b/src/dom/common/lib/selector.ts index 5c78b1d9..5399c5ce 100644 --- a/src/dom/common/lib/selector.ts +++ b/src/dom/common/lib/selector.ts @@ -1,4 +1,5 @@ import { moveRangeEndsIntoTextNodes } from "./range"; +import { iterateWalker } from "./nodes"; // We generate and support a very limited subset of the Web Annotation Data Model: // https://www.w3.org/TR/annotation-model/#selectors @@ -72,8 +73,7 @@ export function textPositionFromRange(range: Range, root: Element): TextPosition type: 'TextPositionSelector' }; let pos = 0; - let node: Node | null = null; - while ((node = iter.nextNode())) { + for (let node of iterateWalker(iter)) { if (node === range.startContainer) { selector.start = pos + range.startOffset; } @@ -94,8 +94,7 @@ export function textPositionToRange(selector: TextPositionSelector, root: Elemen let iter = root.ownerDocument.createNodeIterator(root, NodeFilter.SHOW_TEXT); let range = root.ownerDocument.createRange(); let pos = 0; - let node: Node | null = null; - while ((node = iter.nextNode())) { + for (let node of iterateWalker(iter)) { if (!node.nodeValue) { continue; } diff --git a/src/dom/epub/flow.ts b/src/dom/epub/flow.ts index b588f9f7..a3ea0977 100644 --- a/src/dom/epub/flow.ts +++ b/src/dom/epub/flow.ts @@ -395,6 +395,11 @@ export class PaginatedFlow extends AbstractFlow { this._iframeDocument.body.classList.remove('flow-mode-paginated'); } + private get _spreadWidth(): number { + return this._sectionsContainer.offsetWidth + + parseFloat(getComputedStyle(this._sectionsContainer).columnGap); + } + get currentSectionIndex(): number { return this._currentSectionIndex; } @@ -437,8 +442,7 @@ export class PaginatedFlow extends AbstractFlow { if (options?.block === 'center') { x += rect.width / 2; } - let gap = parseFloat(getComputedStyle(this._sectionsContainer).columnGap); - let spreadWidth = this._sectionsContainer.offsetWidth + gap; + let spreadWidth = this._spreadWidth; this._sectionsContainer.scrollTo({ left: Math.floor(x / spreadWidth) * spreadWidth, top: 0 @@ -495,9 +499,8 @@ export class PaginatedFlow extends AbstractFlow { this._onViewUpdate(); return; } - let gap = parseFloat(getComputedStyle(this._sectionsContainer).columnGap); this._sectionsContainer.scrollBy({ - left: -this._sectionsContainer.offsetWidth - gap, + left: -this._spreadWidth, behavior: 'auto' // TODO 'smooth' once annotation positioning is fixed }); this._onViewUpdate(); @@ -508,9 +511,8 @@ export class PaginatedFlow extends AbstractFlow { this.navigateToNextSection(); return; } - let gap = parseFloat(getComputedStyle(this._sectionsContainer).columnGap); this._sectionsContainer.scrollBy({ - left: this._sectionsContainer.offsetWidth + gap, + left: this._spreadWidth, behavior: 'auto' // TODO 'smooth' once annotation positioning is fixed }); this._onViewUpdate(); diff --git a/src/dom/epub/section-renderer.ts b/src/dom/epub/section-renderer.ts index 1bd984d6..209fa307 100644 --- a/src/dom/epub/section-renderer.ts +++ b/src/dom/epub/section-renderer.ts @@ -1,5 +1,5 @@ import Section from "epubjs/types/section"; -import { getPotentiallyVisibleTextNodes } from "../common/lib/nodes"; +import { getPotentiallyVisibleTextNodes, iterateWalker } from "../common/lib/nodes"; import { sanitizeAndRender, StyleScoper @@ -118,9 +118,8 @@ class SectionRenderer { ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; }); - let node = null; let bestRange = null; - while ((node = iter.nextNode())) { + for (let node of iterateWalker(iter)) { let range = this._document.createRange(); if (node.nodeType == Node.ELEMENT_NODE) { range.selectNode(node); diff --git a/src/dom/snapshot/snapshot-view.ts b/src/dom/snapshot/snapshot-view.ts index 541bdcf5..04320369 100644 --- a/src/dom/snapshot/snapshot-view.ts +++ b/src/dom/snapshot/snapshot-view.ts @@ -23,7 +23,8 @@ import DOMView, { } from "../common/dom-view"; import { getUniqueSelectorContaining } from "../common/lib/unique-selector"; import { - getVisibleTextNodes + getVisibleTextNodes, + iterateWalker } from "../common/lib/nodes"; import DefaultFindProcessor, { createSearchContext } from "../common/find"; @@ -211,8 +212,7 @@ class SnapshotView extends DOMView { private _getSortIndex(range: Range) { let iter = this._iframeDocument.createNodeIterator(this._iframeDocument.documentElement, NodeFilter.SHOW_TEXT); let count = 0; - let node: Node | null; - while ((node = iter.nextNode())) { + for (let node of iterateWalker(iter)) { if (range.startContainer.contains(node)) { return String(count + range.startOffset).padStart(8, '0'); }