@openpageflip/core 0.0.1 → 0.2.0

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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/options.ts"],"sourcesContent":["/**\n * Public option vocabularies. Plain `as const` objects instead of enums so they survive\n * `isolatedModules`, `erasableSyntaxOnly`, and consumers who only speak string literals.\n */\n\n/** How many pages are visible at once. `auto` picks by container width. */\nexport const Layout = { auto: \"auto\", single: \"single\", spread: \"spread\" } as const;\nexport type Layout = (typeof Layout)[keyof typeof Layout];\n\n/** Reading direction. `rtl` flips the spine to the right for manga and Hebrew/Arabic books. */\nexport const Direction = { ltr: \"ltr\", rtl: \"rtl\" } as const;\nexport type Direction = (typeof Direction)[keyof typeof Direction];\n\n/** Which corner a programmatic flip lifts. */\nexport const FlipCorner = { top: \"top\", bottom: \"bottom\" } as const;\nexport type FlipCorner = (typeof FlipCorner)[keyof typeof FlipCorner];\n\n/** `hard` pages rotate as a rigid sheet (covers); `soft` pages bend along the fold. */\nexport const PageDensity = { soft: \"soft\", hard: \"hard\" } as const;\nexport type PageDensity = (typeof PageDensity)[keyof typeof PageDensity];\n"],"mappings":";;;;;;AAMA,MAAa,SAAS;CAAE,MAAM;CAAQ,QAAQ;CAAU,QAAQ;AAAS;;AAIzE,MAAa,YAAY;CAAE,KAAK;CAAO,KAAK;AAAM;;AAIlD,MAAa,aAAa;CAAE,KAAK;CAAO,QAAQ;AAAS;;AAIzD,MAAa,cAAc;CAAE,MAAM;CAAQ,MAAM;AAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/animation.ts","../src/options.ts","../src/coords.ts","../src/geometry/point.ts","../src/geometry/fold.ts","../src/pagination.ts","../src/controller.ts","../src/events.ts","../src/input.ts","../src/layout.ts","../src/pages.ts","../src/render/dom.ts","../src/book.ts"],"sourcesContent":["/** Time and frame sources, injectable so tests can step animations by hand. */\nexport type Clock = {\n now(): number;\n requestFrame(callback: (time: number) => void): number;\n cancelFrame(handle: number): void;\n};\n\nexport const browserClock: Clock = {\n now: () => performance.now(),\n requestFrame: (callback) => requestAnimationFrame(callback),\n cancelFrame: (handle) => cancelAnimationFrame(handle),\n};\n\nexport type Tween = {\n /** Jump to the end and run the completion callback. */\n finish(): void;\n /** Stop without completing. */\n cancel(): void;\n};\n\nexport type TweenSpec = {\n readonly duration: number;\n readonly easing: (t: number) => number;\n /** Called with eased progress 0..1 once per frame, and once with 1 on finish. */\n readonly onFrame: (progress: number) => void;\n readonly onEnd: () => void;\n};\n\n/** A single time-based animation. Frames are requested only while it runs. */\nexport function startTween(clock: Clock, spec: TweenSpec): Tween {\n const startedAt = clock.now();\n let handle: number | null = null;\n let done = false;\n\n const end = (): void => {\n if (done) return;\n done = true;\n if (handle !== null) clock.cancelFrame(handle);\n handle = null;\n spec.onFrame(1);\n spec.onEnd();\n };\n\n const tick = (time: number): void => {\n handle = null;\n if (done) return;\n // A frame's timestamp can precede the moment the tween started, so clamp from below too.\n const t = spec.duration <= 0 ? 1 : Math.min(1, Math.max(0, (time - startedAt) / spec.duration));\n if (t >= 1) {\n end();\n return;\n }\n spec.onFrame(spec.easing(t));\n handle = clock.requestFrame(tick);\n };\n\n if (spec.duration <= 0) {\n end();\n } else {\n handle = clock.requestFrame(tick);\n }\n\n return {\n finish: end,\n cancel: () => {\n done = true;\n if (handle !== null) clock.cancelFrame(handle);\n handle = null;\n },\n };\n}\n","/**\n * Public option vocabularies. Plain `as const` objects instead of enums so they survive\n * `isolatedModules`, `erasableSyntaxOnly`, and consumers who only speak string literals.\n */\n\n/** How many pages are visible at once. `auto` picks by container width. */\nexport const Layout = { auto: \"auto\", single: \"single\", spread: \"spread\" } as const;\nexport type Layout = (typeof Layout)[keyof typeof Layout];\n\n/** Which corner a programmatic flip lifts. */\nexport const FlipCorner = { top: \"top\", bottom: \"bottom\" } as const;\nexport type FlipCorner = (typeof FlipCorner)[keyof typeof FlipCorner];\n\n/** `hard` pages rotate as a rigid sheet (covers); `soft` pages bend along the fold. */\nexport const PageDensity = { soft: \"soft\", hard: \"hard\" } as const;\nexport type PageDensity = (typeof PageDensity)[keyof typeof PageDensity];\n\n/** Which way a page is turning: `forward` reads on, `back` returns to the previous spread. */\nexport const FlipDirection = { forward: \"forward\", back: \"back\" } as const;\nexport type FlipDirection = (typeof FlipDirection)[keyof typeof FlipDirection];\n\n/** What the book is showing: one page (`portrait`) or a two-page spread (`landscape`). */\nexport const Orientation = { portrait: \"portrait\", landscape: \"landscape\" } as const;\nexport type Orientation = (typeof Orientation)[keyof typeof Orientation];\n\n/** What the book is doing right now. */\nexport const FlipState = {\n /** Nothing in motion. */\n read: \"read\",\n /** A corner is lifted because the pointer hovers over it. */\n foldCorner: \"fold_corner\",\n /** The user is dragging a corner. */\n userFold: \"user_fold\",\n /** A flip animation is running. */\n flipping: \"flipping\",\n} as const;\nexport type FlipState = (typeof FlipState)[keyof typeof FlipState];\n\n/** How the book sizes itself. */\nexport const SizeMode = {\n /** Pages are exactly `width` x `height` CSS pixels. */\n fixed: \"fixed\",\n /** Pages scale to the container, keeping the `width:height` ratio, between `minWidth` and `maxWidth`. */\n stretch: \"stretch\",\n} as const;\nexport type SizeMode = (typeof SizeMode)[keyof typeof SizeMode];\n\n/** When a click or tap turns the page. */\nexport const ClickMode = { anywhere: \"anywhere\", corners: \"corners\", off: \"off\" } as const;\nexport type ClickMode = (typeof ClickMode)[keyof typeof ClickMode];\n\nexport type BookOptions = {\n /** Base page width in CSS pixels. With `size: \"stretch\"` only the `width:height` ratio matters. */\n readonly width: number;\n /** Base page height in CSS pixels. */\n readonly height: number;\n /** @default \"fixed\" */\n readonly size?: SizeMode;\n /** Narrowest single page in `stretch` mode; below twice this the book goes portrait. @default 100 */\n readonly minWidth?: number;\n /** Widest single page in `stretch` mode. @default 2000 */\n readonly maxWidth?: number;\n /** @default \"auto\" */\n readonly layout?: Layout;\n /** Show the first and last pages alone, as hard covers. @default false */\n readonly cover?: boolean;\n /** Zero-based page to open on. @default 0 */\n readonly startPage?: number;\n /** Duration of a full flip in milliseconds. Shorter flips take proportionally less. @default 1000 */\n readonly flipDuration?: number;\n /** Easing for the corner's path, `t` in 0..1. @default linear */\n readonly easing?: (t: number) => number;\n /** @default true */\n readonly shadows?: boolean;\n /** 0 hides shadows, 1 is full strength. @default 1 */\n readonly shadowOpacity?: number;\n /** Size the container to the book (aspect ratio and max width). @default true */\n readonly autoSize?: boolean;\n /** @default \"anywhere\" */\n readonly click?: ClickMode;\n /** Let the pointer drag a corner. @default true */\n readonly drag?: boolean;\n /** Turn the page on a quick horizontal swipe. @default true */\n readonly swipe?: boolean;\n /** Minimum swipe travel in CSS pixels. @default 30 */\n readonly swipeDistance?: number;\n /** Lift a corner when the mouse hovers over it. @default true */\n readonly hoverCorners?: boolean;\n /**\n * Pointer events starting on an element matching this selector never start a flip.\n * `false` turns this off.\n * @default \"a, button, input, textarea, select, [data-opf-no-flip]\"\n */\n readonly ignoreDragOn?: string | false;\n /** Page elements. @default the container's children */\n readonly pages?: Iterable<HTMLElement>;\n};\n\nexport type ResolvedOptions = Required<Omit<BookOptions, \"pages\">>;\n\nconst DEFAULTS: Omit<ResolvedOptions, \"width\" | \"height\"> = {\n size: SizeMode.fixed,\n minWidth: 100,\n maxWidth: 2000,\n layout: Layout.auto,\n cover: false,\n startPage: 0,\n flipDuration: 1000,\n easing: (t) => t,\n shadows: true,\n shadowOpacity: 1,\n autoSize: true,\n click: ClickMode.anywhere,\n drag: true,\n swipe: true,\n swipeDistance: 30,\n hoverCorners: true,\n ignoreDragOn: \"a, button, input, textarea, select, [data-opf-no-flip]\",\n};\n\nfunction isOneOf<T extends string>(vocabulary: Record<string, T>, value: unknown): value is T {\n return Object.values(vocabulary).some((allowed) => allowed === value);\n}\n\n/** Fill in defaults and reject options that could only produce a broken book. */\nexport function resolveOptions(user: BookOptions): ResolvedOptions {\n const { pages: _pages, ...rest } = user;\n const options: ResolvedOptions = { ...DEFAULTS, ...rest };\n\n const positive = (name: \"width\" | \"height\" | \"flipDuration\" | \"minWidth\" | \"maxWidth\") => {\n const value = options[name];\n if (!(Number.isFinite(value) && value > 0)) {\n throw new TypeError(\n `@openpageflip/core: \"${name}\" must be a positive number, got ${String(value)}`,\n );\n }\n };\n positive(\"width\");\n positive(\"height\");\n positive(\"flipDuration\");\n positive(\"minWidth\");\n positive(\"maxWidth\");\n if (options.maxWidth < options.minWidth) {\n throw new TypeError(\n `@openpageflip/core: \"maxWidth\" (${options.maxWidth}) is below \"minWidth\" (${options.minWidth})`,\n );\n }\n if (!isOneOf(SizeMode, options.size))\n throw new TypeError(`@openpageflip/core: unknown \"size\" ${String(options.size)}`);\n if (!isOneOf(Layout, options.layout))\n throw new TypeError(`@openpageflip/core: unknown \"layout\" ${String(options.layout)}`);\n if (!isOneOf(ClickMode, options.click))\n throw new TypeError(`@openpageflip/core: unknown \"click\" ${String(options.click)}`);\n if (!(options.shadowOpacity >= 0 && options.shadowOpacity <= 1)) {\n throw new TypeError(\n `@openpageflip/core: \"shadowOpacity\" must be within 0..1, got ${options.shadowOpacity}`,\n );\n }\n if (!Number.isInteger(options.startPage) || options.startPage < 0) {\n throw new TypeError(\n `@openpageflip/core: \"startPage\" must be a non-negative integer, got ${options.startPage}`,\n );\n }\n if (options.ignoreDragOn !== false) {\n try {\n document.createElement(\"div\").matches(options.ignoreDragOn);\n } catch {\n throw new TypeError(\n `@openpageflip/core: \"ignoreDragOn\" is not a valid selector: ${options.ignoreDragOn}`,\n );\n }\n }\n return options;\n}\n","import type { Point } from \"./geometry/point.ts\";\nimport type { BookRect } from \"./layout.ts\";\nimport { FlipDirection } from \"./options.ts\";\n\n/**\n * Three coordinate spaces meet here: the container (where pointer events land), the book rect,\n * and the active page. Page space has its origin at the page's outer top corner with x growing\n * toward the spine, so a backward flip mirrors x.\n */\n\nexport function containerToBook(pos: Point, rect: BookRect): Point {\n return { x: pos.x - rect.left, y: pos.y - rect.top };\n}\n\nexport function containerToPage(pos: Point, rect: BookRect, direction: FlipDirection): Point {\n const x =\n direction === FlipDirection.forward\n ? pos.x - rect.left - rect.width / 2\n : rect.width / 2 - pos.x + rect.left;\n return { x, y: pos.y - rect.top };\n}\n\nexport function pageToContainer(pos: Point, rect: BookRect, direction: FlipDirection): Point {\n const x =\n direction === FlipDirection.forward\n ? pos.x + rect.left + rect.width / 2\n : rect.width / 2 - pos.x + rect.left;\n return { x, y: pos.y + rect.top };\n}\n","/**\n * Plane geometry primitives for the fold calculation. Pure functions, no DOM.\n *\n * The maths derives from StPageFlip's `Helper` (MIT, Oleg Litovski). Where the original's\n * behaviour is quirky, the quirk is kept and commented, because the renderer's look depends on\n * it and the parity tests in `test/fold.parity.test.ts` hold this module to the original.\n */\n\nexport type Point = { readonly x: number; readonly y: number };\n\n/** A line through two points. Used as an infinite line, not a bounded segment. */\nexport type Segment = readonly [Point, Point];\n\nexport type Rect = {\n readonly left: number;\n readonly top: number;\n readonly width: number;\n readonly height: number;\n};\n\n/** The four corners of a rectangle after rotation, so no longer axis-aligned. */\nexport type RectPoints = {\n readonly topLeft: Point;\n readonly topRight: Point;\n readonly bottomLeft: Point;\n readonly bottomRight: Point;\n};\n\nexport function distance(a: Point, b: Point): number {\n return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);\n}\n\n/** Angle between two lines in radians, via the dot product of their normals. */\nexport function angleBetweenLines(one: Segment, two: Segment): number {\n const a1 = one[0].y - one[1].y;\n const a2 = two[0].y - two[1].y;\n const b1 = one[1].x - one[0].x;\n const b2 = two[1].x - two[0].x;\n return Math.acos(\n (a1 * a2 + b1 * b2) / (Math.sqrt(a1 * a1 + b1 * b1) * Math.sqrt(a2 * a2 + b2 * b2)),\n );\n}\n\n/** Inclusive containment test. */\nexport function isPointInRect(rect: Rect, point: Point): boolean {\n return (\n point.x >= rect.left &&\n point.x <= rect.left + rect.width &&\n point.y >= rect.top &&\n point.y <= rect.top + rect.height\n );\n}\n\n/** Rotate `point` by `angle` radians (clockwise in screen space) and translate by `origin`. */\nexport function rotatePoint(point: Point, origin: Point, angle: number): Point {\n const cos = Math.cos(angle);\n const sin = Math.sin(angle);\n return {\n x: point.x * cos + point.y * sin + origin.x,\n y: point.y * cos - point.x * sin + origin.y,\n };\n}\n\n/**\n * Keep `point` inside the circle around `center`. Returns the very same object when it is\n * already inside, so callers can detect clamping by identity.\n *\n * Quirk kept from the original: when the point is left of the y axis the whole x result is\n * negated (not mirrored around the center), and a degenerate line falls back to `y = radius`.\n */\nexport function clampToCircle(center: Point, radius: number, point: Point): Point {\n if (distance(center, point) <= radius) return point;\n\n const a = center.x;\n const b = center.y;\n const n = point.x;\n const m = point.y;\n\n let x = Math.sqrt((radius ** 2 * (a - n) ** 2) / ((a - n) ** 2 + (b - m) ** 2)) + a;\n if (point.x < 0) x *= -1;\n\n let y = ((x - a) * (b - m)) / (a - n) + b;\n if (a - n + b === 0) y = radius;\n\n return { x, y };\n}\n\n/** Two lines are the same line, so they have no single intersection point. */\nexport const Collinear: unique symbol = Symbol(\"collinear\");\n\n/**\n * Intersection of two infinite lines: a point, `null` when parallel, or `Collinear` when they\n * coincide. The fold calculation treats `Collinear` as \"this pointer position is degenerate\".\n */\nexport function intersectLines(one: Segment, two: Segment): Point | null | typeof Collinear {\n const a1 = one[0].y - one[1].y;\n const a2 = two[0].y - two[1].y;\n const b1 = one[1].x - one[0].x;\n const b2 = two[1].x - two[0].x;\n const c1 = one[0].x * one[1].y - one[1].x * one[0].y;\n const c2 = two[0].x * two[1].y - two[1].x * two[0].y;\n\n const x = -((c1 * b2 - c2 * b1) / (a1 * b2 - a2 * b1));\n const y = -((a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1));\n if (Number.isFinite(x) && Number.isFinite(y)) return { x, y };\n\n const det1 = a1 * c2 - a2 * c1;\n const det2 = b1 * c2 - b2 * c1;\n return Math.abs(det1 - det2) < 0.1 ? Collinear : null;\n}\n\n/** `intersectLines`, additionally dropping a hit that lands outside `bounds`. */\nexport function intersectLinesWithin(\n bounds: Rect,\n one: Segment,\n two: Segment,\n): Point | null | typeof Collinear {\n const hit = intersectLines(one, two);\n if (hit === null || hit === Collinear) return hit;\n return isPointInRect(bounds, hit) ? hit : null;\n}\n","/**\n * The fold: where a page lands when its corner is dragged to a point.\n *\n * A pure function of the drag point and the page size. This is the kernel that StPageFlip\n * users came for; it derives from `FlipCalculation` (MIT, Oleg Litovski) with the same output\n * for the same input, checked by the parity tests. Differences are deliberate: numbers instead\n * of strings for the page size (so fractional layouts stay exact), a result instead of thrown\n * errors for degenerate positions, and no `null` entries inside clip polygons.\n */\nimport { FlipCorner, FlipDirection } from \"../options.ts\";\nimport {\n angleBetweenLines,\n Collinear,\n clampToCircle,\n distance,\n intersectLinesWithin,\n type Point,\n type Rect,\n type RectPoints,\n rotatePoint,\n type Segment,\n} from \"./point.ts\";\n\nexport type FoldInput = {\n readonly direction: FlipDirection;\n readonly corner: FlipCorner;\n readonly pageWidth: number;\n readonly pageHeight: number;\n /** Drag point in active-page coordinates: origin at the page's top-left, x grows toward the outer edge. */\n readonly point: Point;\n};\n\n/** Where the fold line meets the page edges. A `null` edge is not crossed. */\nexport type FoldIntersections = {\n readonly top: Point | null;\n readonly side: Point | null;\n readonly bottom: Point | null;\n};\n\nexport type Fold = {\n /** Rotation of the flipping page in radians, signed by direction. */\n readonly angle: number;\n /** Where the dragged corner ended up after clamping to what paper can do. */\n readonly position: Point;\n /** 0 at rest, 100 fully turned. */\n readonly progress: number;\n /** Corners of the flipping page after rotation. */\n readonly rect: RectPoints;\n readonly intersections: FoldIntersections;\n /** Visible part of the flipping page, as a polygon in page coordinates. */\n readonly flippingClip: readonly Point[];\n /** Part of the page underneath that the fold reveals. */\n readonly bottomClip: readonly Point[];\n /** Where the flipping page's own origin corner sits. */\n readonly activeCorner: Point;\n readonly bottomPagePosition: Point;\n /** Drop-shadow origin and rotation, or `null` when the fold crosses no usable edges. */\n readonly shadow: { readonly start: Point; readonly angle: number } | null;\n};\n\n/**\n * Returns `null` when the point is degenerate (the corner is at rest, or the fold line would\n * coincide with a page edge). Callers keep the previous fold for that frame, as the original did.\n */\nexport function computeFold(input: FoldInput): Fold | null {\n const { direction, corner, pageWidth, pageHeight } = input;\n\n const positioned = resolvePosition(input);\n if (positioned === null) return null;\n const { position, angle, rect } = positioned;\n\n const intersections = intersect(input, position, rect);\n if (intersections === null) return null;\n const { top, side, bottom } = intersections;\n\n const flippingClip: Point[] = [rect.topLeft];\n if (top) flippingClip.push(top);\n let clipBottom = false;\n if (side === null) {\n clipBottom = true;\n } else {\n flippingClip.push(side);\n }\n if (bottom) flippingClip.push(bottom);\n if (clipBottom || corner === FlipCorner.bottom) flippingClip.push(rect.bottomLeft);\n\n const bottomClip: Point[] = [];\n if (top) bottomClip.push(top);\n if (corner === FlipCorner.top) {\n bottomClip.push({ x: pageWidth, y: 0 });\n } else {\n if (top !== null) bottomClip.push({ x: pageWidth, y: 0 });\n bottomClip.push({ x: pageWidth, y: pageHeight });\n }\n if (side !== null) {\n // A side hit right next to the top hit would give the polygon a zero-width sliver.\n if (top === null || distance(side, top) >= 10) bottomClip.push(side);\n } else if (corner === FlipCorner.top) {\n bottomClip.push({ x: pageWidth, y: pageHeight });\n }\n if (bottom) bottomClip.push(bottom);\n if (top) bottomClip.push(top);\n\n const shadowStart = corner === FlipCorner.top ? top : (side ?? top);\n const shadowEnd = shadowStart !== side && side !== null ? side : bottom;\n let shadow: Fold[\"shadow\"] = null;\n if (shadowStart !== null && shadowEnd !== null) {\n const raw = angleBetweenLines(\n [shadowStart, shadowEnd],\n [\n { x: 0, y: 0 },\n { x: pageWidth, y: 0 },\n ],\n );\n shadow = {\n start: shadowStart,\n angle: direction === FlipDirection.forward ? raw : Math.PI - raw,\n };\n }\n\n return {\n angle: direction === FlipDirection.forward ? -angle : angle,\n position,\n progress: Math.abs(((position.x - pageWidth) / (2 * pageWidth)) * 100),\n rect,\n intersections,\n flippingClip,\n bottomClip,\n activeCorner: direction === FlipDirection.forward ? rect.topLeft : rect.topRight,\n bottomPagePosition: direction === FlipDirection.back ? { x: pageWidth, y: 0 } : { x: 0, y: 0 },\n shadow,\n };\n}\n\ntype Positioned = { position: Point; angle: number; rect: RectPoints };\n\n/** Clamp the drag point to what the paper allows and derive the page rotation from it. */\nfunction resolvePosition(input: FoldInput): Positioned | null {\n const { corner, pageWidth, pageHeight } = input;\n\n let position = input.point;\n let geometry = angleAndRect(input, position);\n if (geometry === null) return null;\n\n // The dragged corner cannot get further from the spine than the page is wide.\n const spineNear = corner === FlipCorner.top ? { x: 0, y: 0 } : { x: 0, y: pageHeight };\n const spineFar = corner === FlipCorner.top ? { x: 0, y: pageHeight } : { x: 0, y: 0 };\n const clamped = clampToCircle(spineNear, pageWidth, position);\n if (clamped !== position) {\n position = clamped;\n geometry = angleAndRect(input, position);\n if (geometry === null) return null;\n }\n\n // Once the far corner crosses the spine, pin the drag to the page's opposite corner, kept\n // within the page diagonal. The original guarded this with an identity check that never held,\n // so the reassignment is unconditional here too.\n const crossed = corner === FlipCorner.top ? geometry.rect.bottomRight : geometry.rect.topRight;\n const opposite = corner === FlipCorner.top ? geometry.rect.topLeft : geometry.rect.bottomLeft;\n if (crossed.x <= 0) {\n position = clampToCircle(spineFar, Math.sqrt(pageWidth ** 2 + pageHeight ** 2), opposite);\n geometry = angleAndRect(input, position);\n if (geometry === null) return null;\n }\n\n // The corner is still at rest: nothing to fold.\n if (Math.abs(position.x - pageWidth) < 1 && Math.abs(position.y) < 1) return null;\n\n return { position, ...geometry };\n}\n\nfunction angleAndRect(\n input: FoldInput,\n position: Point,\n): { angle: number; rect: RectPoints } | null {\n const angle = foldAngle(input, position);\n if (angle === null) return null;\n return { angle, rect: pageRect(input, position, angle) };\n}\n\n/** Rotation that puts the page corner at `position`, folded around the crease. */\nfunction foldAngle(input: FoldInput, position: Point): number | null {\n const { corner, pageWidth, pageHeight } = input;\n const left = pageWidth - position.x + 1;\n const top = corner === FlipCorner.bottom ? pageHeight - position.y : position.y;\n\n let angle = 2 * Math.acos(left / Math.sqrt(top * top + left * left));\n if (top < 0) angle = -angle;\n\n // A page folded almost exactly flat onto itself has no usable fold line.\n const flat = Math.PI - angle;\n if (!Number.isFinite(angle) || (flat >= 0 && flat < 0.003)) return null;\n\n return corner === FlipCorner.bottom ? -angle : angle;\n}\n\nfunction pageRect(input: FoldInput, position: Point, angle: number): RectPoints {\n const { corner, pageWidth, pageHeight } = input;\n // For the bottom corner the page is modelled above the origin, so its bottom edge is y = 0.\n const dy = corner === FlipCorner.top ? 0 : -pageHeight;\n return {\n topLeft: rotatePoint({ x: 0, y: dy }, position, angle),\n topRight: rotatePoint({ x: pageWidth, y: dy }, position, angle),\n bottomLeft: rotatePoint({ x: 0, y: dy + pageHeight }, position, angle),\n bottomRight: rotatePoint({ x: pageWidth, y: dy + pageHeight }, position, angle),\n };\n}\n\n/** Where the fold line and the page's far edge cross the page borders. `null` when degenerate. */\nfunction intersect(input: FoldInput, position: Point, rect: RectPoints): FoldIntersections | null {\n const { corner, pageWidth, pageHeight } = input;\n const bounds: Rect = { left: -1, top: -1, width: pageWidth + 2, height: pageHeight + 2 };\n const topEdge: Segment = [\n { x: 0, y: 0 },\n { x: pageWidth, y: 0 },\n ];\n const rightEdge: Segment = [\n { x: pageWidth, y: 0 },\n { x: pageWidth, y: pageHeight },\n ];\n const bottomEdge: Segment = [\n { x: 0, y: pageHeight },\n { x: pageWidth, y: pageHeight },\n ];\n\n const top =\n corner === FlipCorner.top\n ? intersectLinesWithin(bounds, [position, rect.topRight], topEdge)\n : intersectLinesWithin(bounds, [rect.topLeft, rect.topRight], topEdge);\n const side =\n corner === FlipCorner.top\n ? intersectLinesWithin(bounds, [position, rect.bottomLeft], rightEdge)\n : intersectLinesWithin(bounds, [position, rect.topLeft], rightEdge);\n const bottom = intersectLinesWithin(bounds, [rect.bottomLeft, rect.bottomRight], bottomEdge);\n\n if (top === Collinear || side === Collinear || bottom === Collinear) return null;\n return { top, side, bottom };\n}\n","import { FlipDirection, Orientation } from \"./options.ts\";\n\n/** Page indices shown together. Landscape pairs them; portrait shows one at a time. */\nexport type Spread = readonly [number] | readonly [number, number];\n\nexport type Spreads = {\n readonly spreads: readonly Spread[];\n /** Pages that are hard because of where they sit: the cover, and a last page shown alone. */\n readonly hardByPosition: ReadonlySet<number>;\n};\n\nexport function buildSpreads(pageCount: number, orientation: Orientation, cover: boolean): Spreads {\n const hardByPosition = new Set<number>();\n const landscape: Spread[] = [];\n let start = 0;\n if (cover && pageCount > 0) {\n hardByPosition.add(0);\n landscape.push([0]);\n start = 1;\n }\n for (let i = start; i < pageCount; i += 2) {\n if (i < pageCount - 1) {\n landscape.push([i, i + 1]);\n } else {\n landscape.push([i]);\n hardByPosition.add(i);\n }\n }\n const portrait: Spread[] = Array.from({ length: pageCount }, (_, i) => [i] as const);\n return { spreads: orientation === Orientation.portrait ? portrait : landscape, hardByPosition };\n}\n\nexport function spreadIndexOfPage(spreads: readonly Spread[], page: number): number | null {\n const index = spreads.findIndex((spread) => spread[0] === page || spread[1] === page);\n return index === -1 ? null : index;\n}\n\n/** Pages lying flat on the left and right for a spread. */\nexport function staticPages(\n spreads: readonly Spread[],\n orientation: Orientation,\n spreadIndex: number,\n pageCount: number,\n): { left: number | null; right: number | null } {\n const spread = spreads[spreadIndex];\n if (spread === undefined) return { left: null, right: null };\n if (spread.length === 2) return { left: spread[0], right: spread[1] };\n // A lone last page in landscape sits on the left, like the back cover of a closed book.\n if (orientation === Orientation.landscape && spread[0] === pageCount - 1)\n return { left: spread[0], right: null };\n return { left: null, right: spread[0] };\n}\n\n/**\n * The page that lifts (its back face is what the viewer sees mid-flip) and the page revealed\n * underneath it. `null` when there is no spread in that direction.\n */\nexport function flipPages(\n spreads: readonly Spread[],\n orientation: Orientation,\n spreadIndex: number,\n direction: FlipDirection,\n): { flipping: number; bottom: number } | null {\n const forward = direction === FlipDirection.forward;\n if (orientation === Orientation.portrait) {\n const current = spreads[spreadIndex]?.[0];\n const other = spreads[forward ? spreadIndex + 1 : spreadIndex - 1]?.[0];\n if (current === undefined || other === undefined) return null;\n // Portrait shows the current page lifting away or the previous page coming back.\n return forward ? { flipping: current, bottom: other } : { flipping: other, bottom: other };\n }\n const target = spreads[forward ? spreadIndex + 1 : spreadIndex - 1];\n if (target === undefined) return null;\n if (target.length === 1) return { flipping: target[0], bottom: target[0] };\n return forward\n ? { flipping: target[0], bottom: target[1] }\n : { flipping: target[1], bottom: target[0] };\n}\n","/**\n * The headless heart of the book: which spread is open, what a pointer is doing to a corner,\n * and where the flip animation is. It knows nothing about the DOM; it hands `Frame`s to a\n * renderer. The behaviour (corner detection, hover fold, drop thresholds, animation paths)\n * is the original's, so the book feels the same.\n */\nimport { type Clock, startTween, type Tween } from \"./animation.ts\";\nimport { containerToBook, containerToPage } from \"./coords.ts\";\nimport { computeFold, type Fold } from \"./geometry/fold.ts\";\nimport { distance, type Point } from \"./geometry/point.ts\";\nimport type { BookRect, LayoutResult } from \"./layout.ts\";\nimport {\n ClickMode,\n FlipCorner,\n FlipDirection,\n FlipState,\n Orientation,\n PageDensity,\n type ResolvedOptions,\n} from \"./options.ts\";\nimport type { PageModel } from \"./pages.ts\";\nimport {\n buildSpreads,\n flipPages,\n type Spread,\n spreadIndexOfPage,\n staticPages,\n} from \"./pagination.ts\";\n\nexport type ShadowData = {\n readonly pos: Point;\n readonly angle: number;\n readonly width: number;\n readonly opacity: number;\n readonly direction: FlipDirection;\n /** 0..200: the original doubled flip progress for its hard-page shadow curve. */\n readonly progress: number;\n};\n\nexport type FlipFrame = {\n readonly direction: FlipDirection;\n readonly corner: FlipCorner;\n readonly flipping: number;\n readonly bottom: number;\n readonly fold: Fold;\n readonly progress: number;\n /** Rotation about the spine for hard pages, in degrees. */\n readonly hardAngle: number;\n readonly shadow: ShadowData | null;\n};\n\n/** Everything a renderer needs to draw one moment of the book. */\nexport type Frame = {\n readonly rect: BookRect;\n readonly orientation: Orientation;\n readonly left: number | null;\n readonly right: number | null;\n readonly flip: FlipFrame | null;\n};\n\nexport type ControllerHooks = {\n readonly onFrame: (frame: Frame) => void;\n readonly onPage: (page: number) => void;\n readonly onState: (state: FlipState) => void;\n};\n\ntype Session = {\n readonly direction: FlipDirection;\n readonly corner: FlipCorner;\n readonly flipping: number;\n readonly bottom: number;\n readonly pageWidth: number;\n readonly pageHeight: number;\n fold: Fold | null;\n progress: number;\n hardAngle: number;\n shadow: ShadowData | null;\n};\n\n/** Pointer travel before a press counts as a drag rather than a click. */\nconst DRAG_THRESHOLD = 5;\n/** How far a hovered corner lifts. */\nconst HOVER_LIFT = 50;\n/** Animation paths longer than this take the full `flipDuration`; shorter ones scale down. */\nconst FULL_FLIP_LENGTH = 1000;\n\nexport class FlipController {\n private pages: PageModel[];\n private spreads: readonly Spread[] = [];\n private spreadIndex = 0;\n private currentPage = 0;\n private orientation: Orientation;\n private rect: BookRect;\n private left: number | null = null;\n private right: number | null = null;\n\n private state: FlipState = FlipState.read;\n private session: Session | null = null;\n private tween: Tween | null = null;\n /** Settles the promise of the running animation when it is cut short. */\n private settleTween: ((turned: boolean) => void) | null = null;\n\n private pressStart: Point | null = null;\n private dragged = false;\n\n private readonly options: ResolvedOptions;\n private readonly clock: Clock;\n private readonly hooks: ControllerHooks;\n\n constructor(\n options: ResolvedOptions,\n clock: Clock,\n hooks: ControllerHooks,\n pages: PageModel[],\n layout: LayoutResult,\n ) {\n this.options = options;\n this.clock = clock;\n this.hooks = hooks;\n this.pages = pages;\n this.orientation = layout.orientation;\n this.rect = layout.rect;\n this.rebuildSpreads();\n }\n\n // ---- pages and layout ---------------------------------------------------------------------\n\n get page(): number {\n return this.currentPage;\n }\n get pageCount(): number {\n return this.pages.length;\n }\n get currentState(): FlipState {\n return this.state;\n }\n get currentOrientation(): Orientation {\n return this.orientation;\n }\n get bookRect(): BookRect {\n return this.rect;\n }\n\n setPages(pages: PageModel[]): void {\n this.endSession();\n this.pages = pages;\n this.rebuildSpreads();\n this.showPage(Math.min(this.currentPage, Math.max(0, pages.length - 1)));\n }\n\n /** Returns true when the orientation changed, which re-paginates the book. */\n setLayout(layout: LayoutResult): boolean {\n const resized =\n layout.rect.pageWidth !== this.rect.pageWidth || layout.rect.height !== this.rect.height;\n this.rect = layout.rect;\n const orientationChanged = layout.orientation !== this.orientation;\n // A fold is computed for one page size; when that changes mid-flip the fold is dropped.\n if (resized && !orientationChanged) this.endSession();\n if (orientationChanged) {\n this.endSession();\n this.orientation = layout.orientation;\n this.rebuildSpreads();\n }\n this.showPage(this.currentPage);\n return orientationChanged;\n }\n\n private rebuildSpreads(): void {\n const { spreads, hardByPosition } = buildSpreads(\n this.pages.length,\n this.orientation,\n this.options.cover,\n );\n this.spreads = spreads;\n for (const [index, page] of this.pages.entries()) {\n if (hardByPosition.has(index)) {\n page.density = PageDensity.hard;\n page.drawingDensity = PageDensity.hard;\n }\n }\n }\n\n // ---- navigation without animation ---------------------------------------------------------\n\n showPage(page: number): void {\n const index = spreadIndexOfPage(this.spreads, page);\n if (index === null) {\n throw new RangeError(\n `@openpageflip/core: page ${page} is out of range (0..${this.pages.length - 1})`,\n );\n }\n this.spreadIndex = index;\n this.showSpread();\n }\n\n showNext(): void {\n if (this.spreadIndex < this.spreads.length - 1) {\n this.spreadIndex++;\n this.showSpread();\n }\n }\n\n showPrev(): void {\n if (this.spreadIndex > 0) {\n this.spreadIndex--;\n this.showSpread();\n }\n }\n\n private showSpread(): void {\n const { left, right } = staticPages(\n this.spreads,\n this.orientation,\n this.spreadIndex,\n this.pages.length,\n );\n this.left = left;\n this.right = right;\n const spread = this.spreads[this.spreadIndex];\n const page = spread === undefined ? this.currentPage : spread[0];\n const changed = page !== this.currentPage;\n this.currentPage = page;\n this.render();\n // Relayouts and redraws re-show the same spread; only a real change is a flip.\n if (changed) this.hooks.onPage(page);\n }\n\n // ---- animated flips -----------------------------------------------------------------------\n\n flipNext(corner: FlipCorner): Promise<boolean> {\n return this.flipFrom({\n x: this.rect.left + this.rect.pageWidth * 2 - 10,\n y: corner === FlipCorner.top ? 1 : this.rect.height - 2,\n });\n }\n\n flipPrev(corner: FlipCorner): Promise<boolean> {\n return this.flipFrom({\n x: this.rect.left + 10,\n y: corner === FlipCorner.top ? 1 : this.rect.height - 2,\n });\n }\n\n /**\n * Jumps to the spread beside the target without animation, then animates the last turn.\n * The static pages keep showing the current spread until that turn lands.\n */\n flipTo(page: number, corner: FlipCorner): Promise<boolean> {\n // A running flip lands first, so the target is measured from where the book actually is.\n this.tween?.finish();\n const target = spreadIndexOfPage(this.spreads, page);\n if (target === null || target === this.spreadIndex) return Promise.resolve(false);\n if (target > this.spreadIndex) {\n this.spreadIndex = target - 1;\n this.syncCurrentPage();\n return this.flipNext(corner);\n }\n this.spreadIndex = target + 1;\n this.syncCurrentPage();\n return this.flipPrev(corner);\n }\n\n private syncCurrentPage(): void {\n const spread = this.spreads[this.spreadIndex];\n if (spread !== undefined) this.currentPage = spread[0];\n }\n\n /** Full animated flip starting at a container point, as a click would. */\n private flipFrom(containerPos: Point): Promise<boolean> {\n if (this.session !== null) this.tween?.finish();\n const session = this.start(containerPos);\n if (session === null) return Promise.resolve(false);\n\n this.setState(FlipState.flipping);\n const { pageWidth, pageHeight } = session;\n const margin = pageHeight / 10;\n const yStart = session.corner === FlipCorner.bottom ? pageHeight - margin : margin;\n const yDest = session.corner === FlipCorner.bottom ? pageHeight : 0;\n const from = { x: pageWidth - margin, y: yStart };\n this.applyFold(from);\n return this.animateTo(from, { x: -pageWidth, y: yDest }, true, true);\n }\n\n /** Let go of a dragged corner: complete the turn if it crossed the spine, otherwise drop it back. */\n private release(): Promise<boolean> {\n const session = this.session;\n if (session === null || session.fold === null) return Promise.resolve(false);\n const pos = session.fold.position;\n const y = session.corner === FlipCorner.bottom ? session.pageHeight : 0;\n return pos.x <= 0\n ? this.animateTo(pos, { x: -session.pageWidth, y }, true, true)\n : this.animateTo(pos, { x: session.pageWidth, y }, false, true);\n }\n\n /** Resolves with whether the page turned. A cancelled animation resolves with `false`. */\n private animateTo(from: Point, to: Point, turn: boolean, reset: boolean): Promise<boolean> {\n this.tween?.finish();\n const dx = to.x - from.x;\n const dy = to.y - from.y;\n const length = Math.max(Math.abs(dx), Math.abs(dy));\n const duration = Math.min(1, length / FULL_FLIP_LENGTH) * this.options.flipDuration;\n\n return new Promise((resolve) => {\n this.settleTween = resolve;\n this.tween = startTween(this.clock, {\n duration,\n easing: this.options.easing,\n onFrame: (t) => this.applyFold({ x: from.x + dx * t, y: from.y + dy * t }),\n onEnd: () => {\n this.tween = null;\n this.settleTween = null;\n const session = this.session;\n if (session === null) {\n resolve(false);\n return;\n }\n if (turn) {\n if (session.direction === FlipDirection.back) this.showPrev();\n else this.showNext();\n }\n if (reset) {\n this.endSession();\n this.setState(FlipState.read);\n this.render();\n }\n resolve(turn);\n },\n });\n });\n }\n\n // ---- pointer interaction --------------------------------------------------------------------\n\n /** Mouse moving over the book without a button down. */\n hover(containerPos: Point): void {\n if (this.state !== FlipState.read && this.state !== FlipState.foldCorner) return;\n const { pageWidth, height } = this.rect;\n\n if (!this.isOnCorner(containerPos)) {\n if (this.session === null) return;\n this.setState(FlipState.read);\n this.tween?.finish();\n void this.release();\n return;\n }\n\n if (this.session !== null) {\n this.applyFold(containerToPage(containerPos, this.rect, this.session.direction));\n return;\n }\n const session = this.start(containerPos);\n if (session === null) return;\n this.setState(FlipState.foldCorner);\n this.applyFold({ x: pageWidth - 1, y: 1 });\n const yStart = session.corner === FlipCorner.bottom ? height - 1 : 1;\n const yDest = session.corner === FlipCorner.bottom ? height - HOVER_LIFT : HOVER_LIFT;\n void this.animateTo(\n { x: pageWidth - 1, y: yStart },\n { x: pageWidth - HOVER_LIFT, y: yDest },\n false,\n false,\n );\n }\n\n /** The mouse left the book: drop any hovered corner. */\n hoverEnd(): void {\n if (this.state !== FlipState.foldCorner) return;\n this.setState(FlipState.read);\n this.tween?.finish();\n void this.release();\n }\n\n pointerDown(containerPos: Point): void {\n // Pressing during a flip lands it; the press then acts on the settled book.\n if (this.state === FlipState.flipping) this.tween?.finish();\n this.pressStart = containerPos;\n this.dragged = false;\n }\n\n /** A pressed pointer moved. Starts a drag once it travels past the click threshold. */\n pointerDrag(containerPos: Point): void {\n if (this.pressStart === null) return;\n if (!this.dragged && distance(this.pressStart, containerPos) <= DRAG_THRESHOLD) return;\n // A press that travelled is a drag even when dragging is off: releasing it must not click.\n this.dragged = true;\n if (!this.options.drag) return;\n // Direction and corner come from where the press started, so a fast drag across the spine\n // cannot flip the wrong way. (The original decided from the first move instead.)\n const session = this.session ?? this.start(this.pressStart);\n if (session === null) return;\n this.setState(FlipState.userFold);\n this.applyFold(containerToPage(containerPos, this.rect, session.direction));\n }\n\n /** The pointer was released. A press without a drag is a click. */\n pointerUp(containerPos: Point): void {\n if (this.pressStart === null) return;\n this.pressStart = null;\n if (this.dragged) {\n void this.release();\n return;\n }\n this.click(containerPos);\n }\n\n /** The browser took the pointer (a scroll, for instance): drop the corner, no click. */\n pointerCancel(): void {\n if (this.pressStart === null) return;\n this.pressStart = null;\n if (this.dragged) void this.release();\n }\n\n /** A quick horizontal swipe: turn the page the swipe points at. */\n swipe(direction: FlipDirection, corner: FlipCorner): Promise<boolean> {\n this.pressStart = null;\n const session = this.session;\n if (session !== null && session.fold !== null) {\n if (session.direction !== direction) return this.release();\n const y = corner === FlipCorner.bottom ? session.pageHeight : 0;\n return this.animateTo(session.fold.position, { x: -session.pageWidth, y }, true, true);\n }\n return direction === FlipDirection.forward ? this.flipNext(corner) : this.flipPrev(corner);\n }\n\n private click(containerPos: Point): void {\n if (this.options.click === ClickMode.off) return;\n if (this.options.click === ClickMode.corners && !this.isOnCorner(containerPos)) return;\n void this.flipFrom(containerPos);\n }\n\n // ---- the flip session -----------------------------------------------------------------------\n\n /** Decide direction and corner from where the pointer is, and pick the pages that move. */\n private start(containerPos: Point): Session | null {\n this.endSession();\n const bookPos = containerToBook(containerPos, this.rect);\n const direction = this.directionAt(bookPos);\n const corner = bookPos.y >= this.rect.height / 2 ? FlipCorner.bottom : FlipCorner.top;\n\n const canFlip =\n direction === FlipDirection.forward\n ? this.currentPage < this.pages.length - 1\n : this.currentPage >= 1;\n if (!canFlip) return null;\n\n const pair = flipPages(this.spreads, this.orientation, this.spreadIndex, direction);\n if (pair === null) return null;\n\n // A soft page beside a hard one turns as a hard sheet for this flip, so the two move as one.\n if (this.orientation === Orientation.landscape) {\n const flipping = this.pages[pair.flipping];\n const neighbour =\n this.pages[direction === FlipDirection.back ? pair.flipping + 1 : pair.flipping - 1];\n if (\n flipping !== undefined &&\n neighbour !== undefined &&\n flipping.density !== neighbour.density\n ) {\n flipping.drawingDensity = PageDensity.hard;\n neighbour.drawingDensity = PageDensity.hard;\n }\n }\n\n this.session = {\n direction,\n corner,\n flipping: pair.flipping,\n bottom: pair.bottom,\n pageWidth: this.rect.pageWidth,\n pageHeight: this.rect.height,\n fold: null,\n progress: 0,\n hardAngle: 0,\n shadow: null,\n };\n return this.session;\n }\n\n private endSession(): void {\n this.tween?.cancel();\n this.tween = null;\n this.settleTween?.(false);\n this.settleTween = null;\n this.session = null;\n for (const page of this.pages) page.drawingDensity = page.density;\n }\n\n /** Move the lifted corner to a page-space point. Degenerate points keep the previous fold. */\n private applyFold(pagePos: Point): void {\n const session = this.session;\n if (session === null) return;\n const fold = computeFold({\n direction: session.direction,\n corner: session.corner,\n pageWidth: session.pageWidth,\n pageHeight: session.pageHeight,\n point: pagePos,\n });\n if (fold === null) return;\n\n const { progress } = fold;\n session.fold = fold;\n session.progress = progress;\n session.hardAngle =\n (session.direction === FlipDirection.forward ? 90 : -90) * ((200 - progress * 2) / 100);\n session.shadow =\n this.options.shadows && fold.shadow !== null\n ? {\n pos: fold.shadow.start,\n angle: fold.shadow.angle,\n width: ((session.pageWidth * 3) / 4) * (progress / 100),\n opacity: ((100 - progress) * (100 * this.options.shadowOpacity)) / 100 / 100,\n direction: session.direction,\n progress: progress * 2,\n }\n : null;\n this.render();\n }\n\n private directionAt(bookPos: Point): FlipDirection {\n if (this.orientation === Orientation.portrait) {\n // The visible page is the right half; its inner fifth turns back.\n return bookPos.x - this.rect.pageWidth <= this.rect.width / 5\n ? FlipDirection.back\n : FlipDirection.forward;\n }\n return bookPos.x < this.rect.width / 2 ? FlipDirection.back : FlipDirection.forward;\n }\n\n private isOnCorner(containerPos: Point): boolean {\n const { pageWidth, height, width } = this.rect;\n const reach = Math.sqrt(pageWidth ** 2 + height ** 2) / 5;\n const p = containerToBook(containerPos, this.rect);\n return (\n p.x > 0 &&\n p.y > 0 &&\n p.x < width &&\n p.y < height &&\n (p.x < reach || p.x > width - reach) &&\n (p.y < reach || p.y > height - reach)\n );\n }\n\n private setState(state: FlipState): void {\n if (this.state === state) return;\n this.state = state;\n this.hooks.onState(state);\n }\n\n // ---- output -----------------------------------------------------------------------------------\n\n frame(): Frame {\n const session = this.session;\n return {\n rect: this.rect,\n orientation: this.orientation,\n left: this.left,\n right: this.right,\n flip:\n session !== null && session.fold !== null\n ? {\n direction: session.direction,\n corner: session.corner,\n flipping: session.flipping,\n bottom: session.bottom,\n fold: session.fold,\n progress: session.progress,\n hardAngle: session.hardAngle,\n shadow: session.shadow,\n }\n : null,\n };\n }\n\n /** Hand the current frame to the renderer again, after something else touched the DOM. */\n redraw(): void {\n this.render();\n }\n\n private render(): void {\n this.hooks.onFrame(this.frame());\n }\n\n destroy(): void {\n this.endSession();\n }\n}\n","export type Listener<T> = (event: T) => void;\n\nexport type Emitter<Events extends Record<string, unknown>> = {\n on<K extends keyof Events>(\n name: K,\n listener: Listener<Events[K]>,\n options?: { signal?: AbortSignal },\n ): () => void;\n off<K extends keyof Events>(name: K, listener: Listener<Events[K]>): void;\n emit<K extends keyof Events>(name: K, event: Events[K]): void;\n clear(): void;\n};\n\n/** Minimal typed event emitter. `on` returns the unsubscribe function. */\nexport function createEmitter<Events extends Record<string, unknown>>(): Emitter<Events> {\n const listeners = new Map<keyof Events, Set<Listener<never>>>();\n\n const off: Emitter<Events>[\"off\"] = (name, listener) => {\n listeners.get(name)?.delete(listener as Listener<never>);\n };\n\n return {\n on(name, listener, options) {\n let set = listeners.get(name);\n if (set === undefined) {\n set = new Set();\n listeners.set(name, set);\n }\n const unsubscribe = () => off(name, listener);\n if (options?.signal?.aborted) return unsubscribe;\n set.add(listener as Listener<never>);\n options?.signal?.addEventListener(\"abort\", unsubscribe, { once: true });\n return unsubscribe;\n },\n off,\n emit(name, event) {\n const set = listeners.get(name);\n if (set === undefined) return;\n for (const listener of [...set]) (listener as Listener<typeof event>)(event);\n },\n clear() {\n listeners.clear();\n },\n };\n}\n","/**\n * Pointer Events to controller calls. One code path for mouse, touch and pen; the container's\n * `touch-action` decides what the browser keeps (vertical scrolling) and what reaches us.\n */\nimport type { FlipController } from \"./controller.ts\";\nimport type { Point } from \"./geometry/point.ts\";\nimport { FlipCorner, FlipDirection, type ResolvedOptions } from \"./options.ts\";\n\n/** A press shorter than this that travels `swipeDistance` is a swipe rather than a drag. */\nconst SWIPE_TIMEOUT = 250;\n\ntype Press = { readonly id: number; readonly start: Point; readonly startedAt: number };\n\nexport function attachInput(\n container: HTMLElement,\n controller: FlipController,\n options: Pick<ResolvedOptions, \"swipe\" | \"swipeDistance\" | \"hoverCorners\" | \"ignoreDragOn\">,\n): () => void {\n let press: Press | null = null;\n\n const local = (event: PointerEvent): Point => {\n const bounds = container.getBoundingClientRect();\n return { x: event.clientX - bounds.left, y: event.clientY - bounds.top };\n };\n\n const onDown = (event: PointerEvent): void => {\n if (press !== null) return;\n if (event.pointerType === \"mouse\" && event.button !== 0) return;\n if (\n options.ignoreDragOn !== false &&\n event.target instanceof Element &&\n event.target.closest(options.ignoreDragOn) !== null\n ) {\n return;\n }\n const start = local(event);\n press = { id: event.pointerId, start, startedAt: event.timeStamp };\n try {\n container.setPointerCapture(event.pointerId);\n } catch {\n // A synthetic event has no active pointer to capture; nothing is lost.\n }\n controller.pointerDown(start);\n if (event.pointerType === \"mouse\") event.preventDefault();\n };\n\n const onMove = (event: PointerEvent): void => {\n if (press !== null) {\n if (event.pointerId === press.id) controller.pointerDrag(local(event));\n return;\n }\n if (event.pointerType === \"mouse\" && options.hoverCorners) controller.hover(local(event));\n };\n\n const onUp = (event: PointerEvent): void => {\n if (press === null || event.pointerId !== press.id) return;\n const { start, startedAt } = press;\n press = null;\n const end = local(event);\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n const quick = event.timeStamp - startedAt < SWIPE_TIMEOUT;\n if (\n options.swipe &&\n quick &&\n Math.abs(dx) > options.swipeDistance &&\n Math.abs(dy) < options.swipeDistance * 2\n ) {\n const rect = controller.bookRect;\n const corner = start.y - rect.top < rect.height / 2 ? FlipCorner.top : FlipCorner.bottom;\n void controller.swipe(dx > 0 ? FlipDirection.back : FlipDirection.forward, corner);\n return;\n }\n controller.pointerUp(end);\n };\n\n const onCancel = (event: PointerEvent): void => {\n if (press === null || event.pointerId !== press.id) return;\n press = null;\n controller.pointerCancel();\n };\n\n const onLeave = (event: PointerEvent): void => {\n if (press === null && event.pointerType === \"mouse\") controller.hoverEnd();\n };\n\n container.addEventListener(\"pointerdown\", onDown);\n container.addEventListener(\"pointermove\", onMove, { passive: true });\n container.addEventListener(\"pointerup\", onUp);\n container.addEventListener(\"pointercancel\", onCancel);\n container.addEventListener(\"pointerleave\", onLeave);\n\n return () => {\n container.removeEventListener(\"pointerdown\", onDown);\n container.removeEventListener(\"pointermove\", onMove);\n container.removeEventListener(\"pointerup\", onUp);\n container.removeEventListener(\"pointercancel\", onCancel);\n container.removeEventListener(\"pointerleave\", onLeave);\n };\n}\n","import { Layout, Orientation, type ResolvedOptions, SizeMode } from \"./options.ts\";\n\n/** Where the book sits inside its container, in container CSS pixels. */\nexport type BookRect = {\n readonly left: number;\n readonly top: number;\n /** Always two pages wide, even in portrait, where only the right half is visible. */\n readonly width: number;\n readonly height: number;\n readonly pageWidth: number;\n};\n\nexport type LayoutResult = { readonly orientation: Orientation; readonly rect: BookRect };\n\nexport type LayoutOptions = Pick<\n ResolvedOptions,\n \"size\" | \"width\" | \"height\" | \"minWidth\" | \"maxWidth\" | \"layout\"\n>;\n\n/**\n * Page size and orientation for a container. Same arithmetic as the original, so the book lands\n * on the same pixels; `layout` only overrides the \"is the container too narrow\" decision.\n */\nexport function computeLayout(\n containerWidth: number,\n containerHeight: number,\n options: LayoutOptions,\n): LayoutResult {\n const middle = { x: containerWidth / 2, y: containerHeight / 2 };\n const ratio = options.width / options.height;\n const portraitIf = (narrow: boolean): Orientation =>\n options.layout === Layout.single || (options.layout === Layout.auto && narrow)\n ? Orientation.portrait\n : Orientation.landscape;\n\n let orientation: Orientation;\n let pageWidth = options.width;\n let pageHeight = options.height;\n\n if (options.size === SizeMode.stretch) {\n orientation = portraitIf(containerWidth < options.minWidth * 2);\n pageWidth = orientation === Orientation.portrait ? containerWidth : containerWidth / 2;\n if (pageWidth > options.maxWidth) pageWidth = options.maxWidth;\n pageHeight = pageWidth / ratio;\n if (pageHeight > containerHeight) {\n pageHeight = containerHeight;\n pageWidth = pageHeight * ratio;\n }\n } else {\n orientation = portraitIf(containerWidth < pageWidth * 2);\n }\n\n // In portrait the visible page is the right half of the two-page rect, centred in the container.\n const left =\n orientation === Orientation.portrait\n ? middle.x - pageWidth / 2 - pageWidth\n : middle.x - pageWidth;\n\n return {\n orientation,\n rect: {\n left,\n top: middle.y - pageHeight / 2,\n width: pageWidth * 2,\n height: pageHeight,\n pageWidth,\n },\n };\n}\n","import { PageDensity } from \"./options.ts\";\n\nexport type PageModel = {\n readonly element: HTMLElement;\n /** Declared by markup (`data-density=\"hard\"`) or forced by position (covers). */\n density: PageDensity;\n /** Density used while drawing. Differs from `density` for the duration of a flip beside a page of the other kind. */\n drawingDensity: PageDensity;\n};\n\nexport function createPages(\n elements: readonly HTMLElement[],\n hardByPosition: ReadonlySet<number>,\n): PageModel[] {\n return elements.map((element, index) => {\n const density =\n hardByPosition.has(index) || element.dataset[\"density\"] === PageDensity.hard\n ? PageDensity.hard\n : PageDensity.soft;\n return { element, density, drawingDensity: density };\n });\n}\n","/**\n * Draws a `Frame` with plain DOM: absolutely positioned page elements, `clip-path` polygons for\n * soft pages, `rotateY` for hard ones, and four gradient elements for shadows. The style strings\n * are the original's, so a frame lands on the same pixels; writes happen only when a frame is\n * handed over, never on a timer.\n */\n\nimport type { Frame, ShadowData } from \"../controller.ts\";\nimport { pageToContainer } from \"../coords.ts\";\nimport type { Point, RectPoints } from \"../geometry/point.ts\";\nimport { rotatePoint } from \"../geometry/point.ts\";\nimport type { BookRect } from \"../layout.ts\";\nimport {\n FlipDirection,\n Layout,\n Orientation,\n PageDensity,\n type ResolvedOptions,\n SizeMode,\n} from \"../options.ts\";\nimport type { PageModel } from \"../pages.ts\";\n\nconst Z = {\n flat: 1,\n bottom: 3,\n hardShadow: 4,\n flipping: 5,\n hardInnerShadow: 5,\n shadow: 10,\n} as const;\n\nconst CLASS = {\n book: \"opf-book\",\n page: \"opf-page\",\n left: \"opf-page--left\",\n right: \"opf-page--right\",\n flat: \"opf-page--flat\",\n soft: \"opf-page--soft\",\n hard: \"opf-page--hard\",\n shadow: \"opf-shadow\",\n} as const;\n\ntype Side = \"left\" | \"right\";\n\n/**\n * The inline properties this renderer owns on a page element. Every draw sets all of them\n * (clearing the ones it does not use) and touches nothing else, so a page keeps whatever other\n * inline style its author or framework gave it.\n */\nconst PAGE_STYLE = [\n \"display\",\n \"position\",\n \"zIndex\",\n \"left\",\n \"top\",\n \"width\",\n \"height\",\n \"transformOrigin\",\n \"transform\",\n \"clipPath\",\n \"backfaceVisibility\",\n] as const;\ntype PageStyle = Partial<Record<(typeof PAGE_STYLE)[number], string>>;\n\nfunction applyPageStyle(el: HTMLElement, style: PageStyle): void {\n for (const key of PAGE_STYLE) el.style[key] = style[key] ?? \"\";\n}\n\ntype SizingOptions = Pick<\n ResolvedOptions,\n \"autoSize\" | \"size\" | \"width\" | \"height\" | \"minWidth\" | \"maxWidth\" | \"layout\"\n>;\n\ntype Saved = { readonly cssText: string; readonly className: string };\n\nexport class DomRenderer {\n private readonly shadows: Record<\"outer\" | \"inner\" | \"hardOuter\" | \"hardInner\", HTMLDivElement>;\n private pages: readonly PageModel[] = [];\n private saved = new Map<HTMLElement, Saved>();\n /**\n * In portrait a page lifts away from itself: the flat page stays and a mirrored copy folds\n * over it. The copy is inert, has no ids, and lives only for the duration of the flip.\n */\n private clone: { readonly source: HTMLElement; readonly element: HTMLElement } | null = null;\n /** Pages currently hidden inline. A page is hidden once when it leaves the stage, not every frame. */\n private hidden = new Set<number>();\n\n private readonly container: HTMLElement;\n private readonly options: SizingOptions;\n\n constructor(container: HTMLElement, options: SizingOptions) {\n this.container = container;\n this.options = options;\n container.classList.add(CLASS.book);\n const shadow = (name: string): HTMLDivElement => {\n const el = document.createElement(\"div\");\n el.className = `${CLASS.shadow} ${CLASS.shadow}--${name}`;\n el.style.display = \"none\";\n container.append(el);\n return el;\n };\n this.shadows = {\n outer: shadow(\"outer\"),\n inner: shadow(\"inner\"),\n hardOuter: shadow(\"hard-outer\"),\n hardInner: shadow(\"hard-inner\"),\n };\n this.applyContainerSizing();\n }\n\n setPages(pages: readonly PageModel[]): void {\n for (const page of this.pages) {\n if (!pages.some((next) => next.element === page.element)) this.restore(page.element);\n }\n this.pages = pages;\n this.hidden.clear();\n for (const page of pages) {\n if (this.saved.has(page.element)) continue;\n this.saved.set(page.element, {\n cssText: page.element.style.cssText,\n className: page.element.className,\n });\n page.element.classList.add(CLASS.page);\n if (page.element.parentElement !== this.container) this.container.append(page.element);\n }\n }\n\n /** Aspect ratio and width limits on the container, when the book sizes itself. */\n applyContainerSizing(orientation: Orientation = Orientation.landscape): void {\n const { autoSize, size, width, height, minWidth, maxWidth, layout } = this.options;\n if (!autoSize) return;\n // Narrowest: one page unless spreads are forced. Widest: two pages unless single is forced.\n const minAcross = layout === Layout.spread ? 2 : 1;\n const maxAcross = layout === Layout.single ? 1 : 2;\n const style = this.container.style;\n style.width = \"100%\";\n style.minWidth = `${(size === SizeMode.fixed ? width : minWidth) * minAcross}px`;\n style.maxWidth = `${(size === SizeMode.fixed ? width : maxWidth) * maxAcross}px`;\n style.aspectRatio =\n orientation === Orientation.portrait ? `${width} / ${height}` : `${width * 2} / ${height}`;\n }\n\n render(frame: Frame): void {\n const { rect, flip } = frame;\n const active = new Set<number>();\n for (const index of [frame.left, frame.right, flip?.flipping, flip?.bottom]) {\n if (index !== undefined && index !== null) active.add(index);\n }\n // Inline, because a page's own stylesheet (display: flex, say) would beat the class rule.\n for (const [index, page] of this.pages.entries()) {\n if (active.has(index)) {\n this.hidden.delete(index);\n } else if (!this.hidden.has(index)) {\n applyPageStyle(page.element, { display: \"none\" });\n this.hidden.add(index);\n }\n }\n\n const flippingHard =\n flip !== null && this.pages[flip.flipping]?.drawingDensity === PageDensity.hard;\n\n if (frame.orientation !== Orientation.portrait && frame.left !== null) {\n if (flip !== null && flip.direction === FlipDirection.back && flippingHard) {\n this.drawHard(frame.left, \"left\", 180 + flip.hardAngle, Z.flipping, rect);\n } else {\n this.drawFlat(frame.left, \"left\", rect);\n }\n }\n if (frame.right !== null) {\n if (flip !== null && flip.direction === FlipDirection.forward && flippingHard) {\n this.drawHard(frame.right, \"right\", 180 + flip.hardAngle, Z.flipping, rect);\n } else {\n this.drawFlat(frame.right, \"right\", rect);\n }\n }\n\n if (flip === null) {\n this.dropClone();\n this.hideShadows();\n return;\n }\n const liftsFromItself = !flippingHard && flip.flipping === frame.right;\n if (!liftsFromItself) this.dropClone();\n\n const bottomSide: Side = flip.direction === FlipDirection.back ? \"left\" : \"right\";\n if (!(frame.orientation === Orientation.portrait && flip.direction === FlipDirection.back)) {\n if (flippingHard) {\n this.drawHard(flip.bottom, bottomSide, 0, Z.bottom, rect);\n } else {\n this.drawSoft(\n flip.bottom,\n bottomSide,\n flip.fold.bottomClip,\n flip.fold.bottomPagePosition,\n 0,\n flip.direction,\n Z.bottom,\n rect,\n );\n }\n }\n\n const flippingSide: Side =\n flip.direction === FlipDirection.forward && frame.orientation !== Orientation.portrait\n ? \"left\"\n : \"right\";\n if (flippingHard) {\n this.drawHard(flip.flipping, flippingSide, flip.hardAngle, Z.flipping, rect);\n } else {\n this.drawSoft(\n flip.flipping,\n flippingSide,\n flip.fold.flippingClip,\n flip.fold.activeCorner,\n flip.fold.angle,\n flip.direction,\n Z.flipping,\n rect,\n liftsFromItself,\n );\n }\n\n if (flip.shadow === null) {\n this.hideShadows();\n } else if (flippingHard) {\n this.hideSoftShadows();\n this.drawHardShadows(flip.shadow, rect);\n } else {\n this.hideHardShadows();\n this.drawSoftShadows(flip.shadow, flip.fold.rect, rect);\n }\n }\n\n // ---- pages ----------------------------------------------------------------------------------\n\n private element(index: number, side: Side, asClone = false): HTMLElement | null {\n const page = this.pages[index];\n if (page === undefined) return null;\n const el = asClone ? this.cloneOf(page.element) : page.element;\n // Re-asserted on every draw: a framework may have rewritten the class attribute since.\n el.classList.add(CLASS.page);\n el.classList.toggle(CLASS.hard, page.drawingDensity === PageDensity.hard);\n el.classList.toggle(CLASS.soft, page.drawingDensity === PageDensity.soft);\n el.classList.toggle(CLASS.left, side === \"left\");\n el.classList.toggle(CLASS.right, side === \"right\");\n return el;\n }\n\n private cloneOf(source: HTMLElement): HTMLElement {\n if (this.clone?.source === source) return this.clone.element;\n this.dropClone();\n const element = source.cloneNode(true);\n if (!(element instanceof HTMLElement))\n throw new TypeError(\"@openpageflip/core: a page clone is not an element\");\n element.removeAttribute(\"id\");\n for (const el of element.querySelectorAll(\"[id]\")) el.removeAttribute(\"id\");\n element.setAttribute(\"aria-hidden\", \"true\");\n element.inert = true;\n element.dataset[\"opfClone\"] = \"\";\n source.after(element);\n this.clone = { source, element };\n return element;\n }\n\n private dropClone(): void {\n this.clone?.element.remove();\n this.clone = null;\n }\n\n private drawFlat(index: number, side: Side, rect: BookRect): void {\n const el = this.element(index, side);\n if (el === null) return;\n el.classList.add(CLASS.flat);\n const left = side === \"right\" ? rect.left + rect.pageWidth : rect.left;\n applyPageStyle(el, {\n position: \"absolute\",\n display: \"block\",\n height: `${rect.height}px`,\n left: `${left}px`,\n top: `${rect.top}px`,\n width: `${rect.pageWidth}px`,\n zIndex: String(Z.flat),\n });\n }\n\n private drawSoft(\n index: number,\n side: Side,\n area: readonly Point[],\n position: Point,\n angle: number,\n direction: FlipDirection,\n zIndex: number,\n rect: BookRect,\n asClone = false,\n ): void {\n const el = this.element(index, side, asClone);\n if (el === null) return;\n el.classList.remove(CLASS.flat);\n const at = pageToContainer(position, rect, direction);\n const polygon = area\n .map((p) => {\n const local =\n direction === FlipDirection.back\n ? { x: -p.x + position.x, y: p.y - position.y }\n : { x: p.x - position.x, y: p.y - position.y };\n const g = rotatePoint(local, { x: 0, y: 0 }, angle);\n return `${g.x}px ${g.y}px`;\n })\n .join(\", \");\n applyPageStyle(el, {\n position: \"absolute\",\n display: \"block\",\n zIndex: String(zIndex),\n left: \"0\",\n top: \"0\",\n width: `${rect.pageWidth}px`,\n height: `${rect.height}px`,\n transformOrigin: \"0 0\",\n clipPath: `polygon(${polygon})`,\n transform: `translate3d(${at.x}px, ${at.y}px, 0) rotate(${angle}rad)`,\n });\n }\n\n private drawHard(index: number, side: Side, angle: number, zIndex: number, rect: BookRect): void {\n const el = this.element(index, side);\n if (el === null) return;\n el.classList.remove(CLASS.flat);\n const spine = rect.left + rect.width / 2;\n applyPageStyle(el, {\n position: \"absolute\",\n display: \"block\",\n zIndex: String(zIndex),\n left: \"0\",\n top: \"0\",\n width: `${rect.pageWidth}px`,\n height: `${rect.height}px`,\n backfaceVisibility: \"hidden\",\n clipPath: \"none\",\n transformOrigin: side === \"left\" ? `${rect.pageWidth}px 0` : \"0 0\",\n transform:\n side === \"left\"\n ? `translate3d(${rect.left}px, ${rect.top}px, 0) rotateY(${angle}deg)`\n : `translate3d(${spine}px, ${rect.top}px, 0) rotateY(${angle}deg)`,\n });\n }\n\n // ---- shadows --------------------------------------------------------------------------------\n\n private drawSoftShadows(shadow: ShadowData, pageRect: RectPoints, rect: BookRect): void {\n const forward = shadow.direction === FlipDirection.forward;\n const at = pageToContainer(shadow.pos, rect, shadow.direction);\n const angle = shadow.angle + (3 * Math.PI) / 2;\n const polygon = (points: readonly Point[], translate: number): string =>\n points\n .map((p) => {\n const local = forward\n ? { x: p.x - shadow.pos.x, y: p.y - shadow.pos.y }\n : { x: -p.x + shadow.pos.x, y: p.y - shadow.pos.y };\n const g = rotatePoint(local, { x: translate, y: 100 }, angle);\n return `${g.x}px ${g.y}px`;\n })\n .join(\", \");\n\n const outerTranslate = forward ? 0 : shadow.width;\n const outerClip = polygon(\n [\n { x: 0, y: 0 },\n { x: rect.pageWidth, y: 0 },\n { x: rect.pageWidth, y: rect.height },\n { x: 0, y: rect.height },\n ],\n outerTranslate,\n );\n this.shadows.outer.style.cssText = `display: block; z-index: ${Z.shadow}; width: ${shadow.width}px; height: ${rect.height * 2}px; background: linear-gradient(${forward ? \"to right\" : \"to left\"}, rgba(0, 0, 0, ${shadow.opacity}), rgba(0, 0, 0, 0)); transform-origin: ${outerTranslate}px 100px; transform: translate3d(${at.x - outerTranslate}px, ${at.y - 100}px, 0) rotate(${angle}rad); clip-path: polygon(${outerClip});`;\n\n const innerWidth = (shadow.width * 3) / 4;\n const innerTranslate = forward ? innerWidth : 0;\n const innerClip = polygon(\n [pageRect.topLeft, pageRect.topRight, pageRect.bottomRight, pageRect.bottomLeft],\n innerTranslate,\n );\n this.shadows.inner.style.cssText = `display: block; z-index: ${Z.shadow}; width: ${innerWidth}px; height: ${rect.height * 2}px; background: linear-gradient(${forward ? \"to left\" : \"to right\"}, rgba(0, 0, 0, ${shadow.opacity}) 5%, rgba(0, 0, 0, 0.05) 15%, rgba(0, 0, 0, ${shadow.opacity}) 35%, rgba(0, 0, 0, 0) 100%); transform-origin: ${innerTranslate}px 100px; transform: translate3d(${at.x - innerTranslate}px, ${at.y - 100}px, 0) rotate(${angle}rad); clip-path: polygon(${innerClip});`;\n }\n\n private drawHardShadows(shadow: ShadowData, rect: BookRect): void {\n const progress = shadow.progress > 100 ? 200 - shadow.progress : shadow.progress;\n const size = Math.min(rect.pageWidth, ((100 - progress) * (2.5 * rect.pageWidth)) / 100 + 20);\n const spine = rect.left + rect.width / 2;\n const flipped =\n (shadow.direction === FlipDirection.forward && shadow.progress > 100) ||\n (shadow.direction === FlipDirection.back && shadow.progress <= 100);\n const common = `display: block; width: ${size}px; height: ${rect.height}px; left: ${spine}px; top: ${rect.top}px; transform-origin: 0 0;`;\n this.shadows.hardInner.style.cssText = `${common} z-index: ${Z.hardInnerShadow}; background: linear-gradient(to right, rgba(0, 0, 0, ${(shadow.opacity * progress) / 100}) 5%, rgba(0, 0, 0, 0) 100%); transform: translate3d(0, 0, 0)${flipped ? \"\" : \" rotateY(180deg)\"};`;\n this.shadows.hardOuter.style.cssText = `${common} z-index: ${Z.hardShadow}; background: linear-gradient(to left, rgba(0, 0, 0, ${shadow.opacity}) 5%, rgba(0, 0, 0, 0) 100%); transform: translate3d(0, 0, 0)${flipped ? \" rotateY(180deg)\" : \"\"};`;\n }\n\n private hideSoftShadows(): void {\n this.shadows.outer.style.cssText = \"display: none\";\n this.shadows.inner.style.cssText = \"display: none\";\n }\n private hideHardShadows(): void {\n this.shadows.hardOuter.style.cssText = \"display: none\";\n this.shadows.hardInner.style.cssText = \"display: none\";\n }\n private hideShadows(): void {\n this.hideSoftShadows();\n this.hideHardShadows();\n }\n\n // ---- teardown -------------------------------------------------------------------------------\n\n private restore(element: HTMLElement): void {\n const saved = this.saved.get(element);\n if (saved === undefined) return;\n element.style.cssText = saved.cssText;\n element.className = saved.className;\n this.saved.delete(element);\n }\n\n /** Put the container and every page back the way they were found. */\n destroy(): void {\n this.dropClone();\n this.hidden.clear();\n for (const page of this.pages) this.restore(page.element);\n this.pages = [];\n for (const el of Object.values(this.shadows)) el.remove();\n this.container.classList.remove(CLASS.book);\n const style = this.container.style;\n style.width = \"\";\n style.minWidth = \"\";\n style.maxWidth = \"\";\n style.aspectRatio = \"\";\n }\n}\n","import { browserClock, type Clock } from \"./animation.ts\";\nimport { FlipController, type Frame } from \"./controller.ts\";\nimport { createEmitter, type Emitter } from \"./events.ts\";\nimport { attachInput } from \"./input.ts\";\nimport { type BookRect, computeLayout, type LayoutResult } from \"./layout.ts\";\nimport {\n type BookOptions,\n FlipCorner,\n type FlipState,\n type Orientation,\n resolveOptions,\n} from \"./options.ts\";\nimport { createPages } from \"./pages.ts\";\nimport { buildSpreads } from \"./pagination.ts\";\nimport { DomRenderer } from \"./render/dom.ts\";\n\nexport type BookEvents = {\n /** The book is laid out and showing `startPage`. Fires once, after `createBook` returns. */\n init: { readonly page: number; readonly orientation: Orientation };\n /** Pages were replaced with `setPages`. */\n update: { readonly page: number; readonly orientation: Orientation };\n /** A different spread is showing. `page` is the first page of it. */\n flip: { readonly page: number };\n changeState: { readonly state: FlipState };\n changeOrientation: { readonly orientation: Orientation };\n};\n\nexport type Book = {\n /** First page of the open spread, zero-based. */\n readonly page: number;\n readonly pageCount: number;\n readonly orientation: Orientation;\n readonly state: FlipState;\n readonly rect: BookRect;\n\n on: Emitter<BookEvents>[\"on\"];\n off: Emitter<BookEvents>[\"off\"];\n\n /** Animated turns. Resolve with `false` when there is nothing to turn to. */\n flipNext(corner?: FlipCorner): Promise<boolean>;\n flipPrev(corner?: FlipCorner): Promise<boolean>;\n flipTo(page: number, corner?: FlipCorner): Promise<boolean>;\n /** Instant turns. */\n turnTo(page: number): void;\n turnNext(): void;\n turnPrev(): void;\n\n /** Replace the page elements. Keeps the current page where possible. */\n setPages(pages: Iterable<HTMLElement>): void;\n /** Re-measure the container and redraw. Resizes are handled automatically; call this after other layout changes. */\n update(): void;\n /** Redraw the current frame without measuring, after something else rewrote page attributes. Cheap. */\n redraw(): void;\n /** Stop everything, drop listeners and observers, and restore the DOM. */\n destroy(): void;\n};\n\nexport type CreateBookOptions = BookOptions & {\n /** Time and frame source, replaceable in tests. */\n readonly clock?: Clock;\n};\n\nexport function createBook(container: HTMLElement, userOptions: CreateBookOptions): Book {\n const { clock = browserClock, ...bookOptions } = userOptions;\n const options = resolveOptions(bookOptions);\n const elements = Array.from(bookOptions.pages ?? container.children).filter(\n (el): el is HTMLElement => el instanceof HTMLElement,\n );\n if (elements.length === 0) {\n throw new TypeError(\"@openpageflip/core: createBook needs at least one page element\");\n }\n if (options.startPage >= elements.length) {\n throw new TypeError(\n `@openpageflip/core: \"startPage\" ${options.startPage} is out of range for ${elements.length} pages`,\n );\n }\n\n const emitter = createEmitter<BookEvents>();\n const renderer = new DomRenderer(container, options);\n const reducedMotion = matchMedia(\"(prefers-reduced-motion: reduce)\");\n\n const measure = (): LayoutResult => {\n // Orientation depends on width only, and the container's height follows orientation when it\n // sizes itself, so settle the aspect ratio before measuring the height.\n const width = container.clientWidth;\n const { orientation } = computeLayout(width, container.clientHeight, options);\n renderer.applyContainerSizing(orientation);\n return computeLayout(width, container.clientHeight, options);\n };\n\n const buildPages = (els: readonly HTMLElement[]) => {\n // Hard-by-position does not depend on orientation, so landscape is as good as any here.\n const { hardByPosition } = buildSpreads(els.length, \"landscape\", options.cover);\n return createPages(els, hardByPosition);\n };\n\n let pages = buildPages(elements);\n renderer.setPages(pages);\n\n // The controller is headless, so the reduced-motion preference reaches it as a live duration.\n const controller = new FlipController(\n {\n ...options,\n get flipDuration() {\n return reducedMotion.matches ? 0 : options.flipDuration;\n },\n },\n clock,\n {\n onFrame: (frame: Frame) => renderer.render(frame),\n onPage: (page) => emitter.emit(\"flip\", { page }),\n onState: (state) => emitter.emit(\"changeState\", { state }),\n },\n pages,\n measure(),\n );\n\n const relayout = (): void => {\n const layout = measure();\n if (controller.setLayout(layout)) {\n emitter.emit(\"changeOrientation\", { orientation: layout.orientation });\n }\n };\n\n // Relayout can change the container's own height (aspect ratio follows orientation), which\n // would re-enter the observer in the same frame; deferring one frame keeps the loop clean.\n let lastSize = { width: container.clientWidth, height: container.clientHeight };\n let pendingRelayout: number | null = null;\n const observer = new ResizeObserver(() => {\n if (pendingRelayout !== null) return;\n pendingRelayout = clock.requestFrame(() => {\n pendingRelayout = null;\n const size = { width: container.clientWidth, height: container.clientHeight };\n if (size.width === lastSize.width && size.height === lastSize.height) return;\n lastSize = size;\n relayout();\n });\n });\n observer.observe(container);\n\n const detachInput = attachInput(container, controller, options);\n\n controller.showPage(options.startPage);\n queueMicrotask(() =>\n emitter.emit(\"init\", { page: controller.page, orientation: controller.currentOrientation }),\n );\n\n return {\n get page() {\n return controller.page;\n },\n get pageCount() {\n return controller.pageCount;\n },\n get orientation() {\n return controller.currentOrientation;\n },\n get state() {\n return controller.currentState;\n },\n get rect() {\n return controller.bookRect;\n },\n on: emitter.on,\n off: emitter.off,\n flipNext: (corner = FlipCorner.top) => controller.flipNext(corner),\n flipPrev: (corner = FlipCorner.top) => controller.flipPrev(corner),\n flipTo: (page, corner = FlipCorner.top) => controller.flipTo(page, corner),\n turnTo: (page) => controller.showPage(page),\n turnNext: () => controller.showNext(),\n turnPrev: () => controller.showPrev(),\n setPages(next) {\n const els = Array.from(next);\n if (els.length === 0)\n throw new TypeError(\"@openpageflip/core: setPages needs at least one page element\");\n pages = buildPages(els);\n renderer.setPages(pages);\n controller.setPages(pages);\n emitter.emit(\"update\", { page: controller.page, orientation: controller.currentOrientation });\n },\n update: relayout,\n redraw: () => controller.redraw(),\n destroy() {\n observer.disconnect();\n if (pendingRelayout !== null) clock.cancelFrame(pendingRelayout);\n detachInput();\n controller.destroy();\n renderer.destroy();\n emitter.clear();\n },\n };\n}\n"],"mappings":"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"}
package/dist/styles.css CHANGED
@@ -1,22 +1,30 @@
1
1
  /*
2
2
  * @openpageflip/core stylesheet. Import it once: `import "@openpageflip/core/styles.css"`.
3
- * Theming is through the custom properties below; set them on the host element.
3
+ * Pages are positioned by the library with inline styles; these rules give them their 3D
4
+ * stage and their resting state. Style page faces through `.opf-page--hard`, `--soft`,
5
+ * `--left`, `--right` and `--flat`.
4
6
  */
5
7
  .opf-book {
6
- --opf-flip-duration: 800ms;
7
- --opf-shadow-opacity: 1;
8
-
9
8
  position: relative;
10
9
  display: block;
11
10
  box-sizing: border-box;
11
+ perspective: 2000px;
12
12
  /* Vertical page scroll stays native; horizontal drags belong to the book. */
13
13
  touch-action: pan-y;
14
14
  user-select: none;
15
15
  -webkit-user-select: none;
16
16
  }
17
17
 
18
- @media (prefers-reduced-motion: reduce) {
19
- .opf-book {
20
- --opf-flip-duration: 0ms;
21
- }
18
+ /* Box sizing is left to the page's own CSS: a bordered page grows past its nominal size, as in the original. */
19
+ .opf-page {
20
+ display: none;
21
+ position: absolute;
22
+ transform-style: preserve-3d;
23
+ }
24
+
25
+ .opf-shadow {
26
+ position: absolute;
27
+ left: 0;
28
+ top: 0;
29
+ pointer-events: none;
22
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpageflip/core",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "Realistic page-flip effect for HTML content. Framework-agnostic successor to StPageFlip.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,5 +37,8 @@
37
37
  "./package.json": "./package.json"
38
38
  },
39
39
  "unpkg": "./dist/index.iife.js",
40
- "jsdelivr": "./dist/index.iife.js"
40
+ "jsdelivr": "./dist/index.iife.js",
41
+ "devDependencies": {
42
+ "page-flip": "2.0.7"
43
+ }
41
44
  }