Skip to content

Commit

Permalink
1.3.0 (#87)
Browse files Browse the repository at this point in the history
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Hadrien Gardeur <hadrien.gardeur@gmail.com>
Co-authored-by: Jiminy Panoz <JayPanoz@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Nov 22, 2024
1 parent 1eb6035 commit 587c788
Show file tree
Hide file tree
Showing 23 changed files with 635 additions and 270 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

Next generation SDK for publications in Web Apps

## Install
## Usage

Three packages are made available by this repository, which are published on NPM.
They are:
- [@readium/shared](https://www.npmjs.com/package/@readium/shared)
- [@readium/navigator-html-injectables](https://www.npmjs.com/package/@readium/navigator-html-injectables)
- [@readium/navigator](https://www.npmjs.com/package/@readium/navigator)

# Development

You need `pnpm` installed as this is a monorepo using workspaces.

Expand Down
20 changes: 20 additions & 0 deletions navigator-html-injectables/CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.3.0] - 2024-11-22

### Added

- (`epubReadingSystem`)[https://www.w3.org/TR/epub-rs-33/#app-epubReadingSystem] is available to publications.

### Fixed

- Ensure CSS-only locator ranges are correct, especially relevant when using the `go_text` command (#77).

### Changed

- The `ColumnSnapper` and `ScrollSnapper` report progress that now includes a reference progression – a.k.a. progression by a ReadiumWindow viewport – in addition to the current one (#62).
4 changes: 2 additions & 2 deletions navigator-html-injectables/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@readium/navigator-html-injectables",
"version": "1.2.0",
"version": "1.3.0",
"type": "module",
"description": "An embeddable solution for connecting frames of HTML publications with a Readium Navigator",
"author": "readium",
Expand Down Expand Up @@ -53,6 +53,6 @@
"css-selector-generator": "^3.6.4",
"tslib": "^2.6.1",
"typescript": "^5.4.5",
"vite": "^4.4.9"
"vite": "^4.5.5"
}
}
8 changes: 8 additions & 0 deletions navigator-html-injectables/src/helpers/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import type { getCssSelector } from "css-selector-generator";

type BlockedEventData = [0, Function, any[], any[]] | [1, Event, EventTarget];

export interface EPUBReadingSystem {
name: string;
version: string;
layoutStyle: "paginated" | "scrolling"; // Technically, more are allowed
hasFeature: (feature: string, version?: string) => boolean;
}

// This is what is injected into the HTML documents
export interface ReadiumWindow extends Window {
_readium_blockEvents: boolean;
Expand All @@ -13,6 +20,7 @@ export interface ReadiumWindow extends Window {
_readium_cssSelectorGenerator: {
getCssSelector: typeof getCssSelector;
};
navigator: Navigator & { epubReadingSystem: EPUBReadingSystem };
}

export function deselect(wnd: ReadiumWindow) {
Expand Down
108 changes: 55 additions & 53 deletions navigator-html-injectables/src/helpers/locator.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,69 @@
import { Locator } from "@readium/shared";
import { TextQuoteAnchor } from "../vendor/hypothesis/anchoring/types";

function isReplacedLikeElement(element: Element): boolean {
const tagName = element.tagName.toUpperCase();
return tagName === "IMG" || tagName === "VIDEO" || tagName === "AUDIO" || tagName === "IFRAME" || tagName === "OBJECT" || tagName === "EMBED" || tagName === "CANVAS";
}

// Based on the kotlin-toolkit code
export function rangeFromLocator(doc: Document, locator: Locator) {
try {
let locations = locator.locations;
let text = locator.text;
if (text && text.highlight) {
let root;
if (locations && locations.getCssSelector()) {
root = doc.querySelector(locations.getCssSelector()!);
}
if (!root) {
root = doc.body;
}

let anchor = new TextQuoteAnchor(root, text.highlight, {
prefix: text.before,
suffix: text.after,
});
try {
return anchor.toRange();
} catch (error) {
// We don't watch to "crash" when the quote is not found
console.warn("Quote not found:", anchor);
return null;
}
}

if (locations) {
var element = null;

if (!element && locations.getCssSelector()) {
element = doc.querySelector(locations.getCssSelector()!);
}

if (!element && locations.fragments) {
for (const htmlId of locations.fragments) {
element = doc.getElementById(htmlId);
if (element) {
break;
const locations = locator.locations;
const text = locator.text;
if (text && text.highlight) {
let root;
if (locations && locations.getCssSelector()) {
root = doc.querySelector(locations.getCssSelector()!);
}
if (!root) {
root = doc.body;
}

const anchor = new TextQuoteAnchor(root, text.highlight, {
prefix: text.before,
suffix: text.after,
});
try {
return anchor.toRange();
} catch (error) {
// We don't watch to "crash" when the quote is not found
console.warn("Quote not found:", anchor);
return null;
}
}
}

if (element) {
let range = doc.createRange();

// This is a special case where the node is
// a single element with no children. Not sure
// yet how effective this is yet, may remove in future.
if(element.childNodes.length === 0) {
range.selectNodeContents(element);
return range;
}
if (locations) {
let element = null;

range.setStartBefore(element);
range.setEndAfter(element);
return range;
if (!element && locations.getCssSelector()) {
element = doc.querySelector(locations.getCssSelector()!);
}

if (!element && locations.fragments) {
for (const htmlId of locations.fragments) {
element = doc.getElementById(htmlId);
if (element) {
break;
}
}
}

if (element) {
const range = doc.createRange();

if (element.childNodes.length === 0 || isReplacedLikeElement(element)) {
range.selectNode(element);
return range;
}

range.setStartBefore(element);
range.setEndAfter(element);
return range;
}
}
}
} catch (e) {
console.error(e);
console.error(e);
}
return null;
}
}
2 changes: 2 additions & 0 deletions navigator-html-injectables/src/modules/setup/FixedSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export class FixedSetup extends Setup {
mount(wnd: ReadiumWindow, comms: Comms): boolean {
if(!super.mount(wnd, comms)) return false;

wnd.navigator.epubReadingSystem.layoutStyle = "paginated"; // TODO: what if we support scrolling?

const style = wnd.document.createElement("style");
style.id = FIXED_STYLE_ID;
style.dataset.readium = "true";
Expand Down
31 changes: 30 additions & 1 deletion navigator-html-injectables/src/modules/setup/Setup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Comms } from "../../comms/comms";
import { ReadiumWindow } from "../../helpers/dom";
import { ReadiumWindow, EPUBReadingSystem } from "../../helpers/dom";
import { Module } from "../Module";
import { ModuleName } from "../ModuleLibrary";

Expand Down Expand Up @@ -72,6 +72,35 @@ export abstract class Setup extends Module {
false
);

// Add reading system property to navigator
Reflect.defineProperty(wnd.navigator, "epubReadingSystem", {
value: {
name: "readium-ts-toolkit",
version: import.meta.env.PACKAGE_VERSION,
hasFeature: (feature: string, _version = "") => {
switch (feature) {
case "dom-manipulation":
return true;
case "layout-changes":
return true;
case "touch-events":
return true;
case "mouse-events":
return true;
case "keyboard-events":
return true;
case "spine-scripting":
return true;
case "embedded-web-content":
return true;
default:
return false;
}
}
} as EPUBReadingSystem,
writable: false
});

// Add all currently active animations and cancel them
if("getAnimations" in wnd.document) {
wnd.document.getAnimations().forEach((a) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class ColumnSnapper extends Snapper {
}

reportProgress() {
this.comms.send("progress", this.wnd.scrollX / this.cachedScrollWidth);
this.comms.send("progress", { progress: this.wnd.scrollX / this.cachedScrollWidth, reference: this.wnd.innerWidth / this.doc().scrollWidth });
}

private shakeTimeout = 0;
Expand Down Expand Up @@ -229,6 +229,8 @@ export class ColumnSnapper extends Snapper {
this.comms = comms;
if(!super.mount(wnd, comms)) return false;

wnd.navigator.epubReadingSystem.layoutStyle = "paginated";

// Add styling to hide the scrollbar
const d = wnd.document.createElement("style");
d.dataset.readium = "true";
Expand Down
21 changes: 13 additions & 8 deletions navigator-html-injectables/src/modules/snapper/ScrollSnapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ export class ScrollSnapper extends Snapper {
customElements.define("anchor-observer", AnchorObserver);
}

private reportProgress(progress: number) {
this.comms.send("progress", progress);
private reportProgress(data: { progress: number, reference: number }) {
this.comms.send("progress", data);
}

mount(wnd: ReadiumWindow, comms: Comms): boolean {
this.wnd = wnd;
this.comms = comms;

wnd.navigator.epubReadingSystem.layoutStyle = "scrolling";

// Add styling to hide the scrollbar
const style = wnd.document.createElement("style");
style.dataset.readium = "true";
Expand Down Expand Up @@ -66,7 +68,7 @@ export class ScrollSnapper extends Snapper {

this.wnd.requestAnimationFrame(() => {
this.doc().scrollTop = this.doc().offsetHeight * position;
this.reportProgress(position);
this.reportProgress({ progress: position, reference: this.wnd.innerHeight / this.doc().scrollHeight });
deselect(this.wnd);
ack(true);
});
Expand All @@ -80,7 +82,8 @@ export class ScrollSnapper extends Snapper {
}
this.wnd.requestAnimationFrame(() => {
this.doc().scrollTop = element.getBoundingClientRect().top + wnd.scrollY - wnd.innerHeight / 2;
this.reportProgress(this.doc().scrollTop / this.doc().offsetHeight);
const progress = this.doc().scrollTop / this.doc().offsetHeight;
this.reportProgress({ progress: progress, reference: this.wnd.innerHeight / this.doc().scrollHeight });
deselect(this.wnd);
ack(true);
});
Expand Down Expand Up @@ -111,7 +114,8 @@ export class ScrollSnapper extends Snapper {
}
this.wnd.requestAnimationFrame(() => {
this.doc().scrollTop = r.getBoundingClientRect().top + wnd.scrollY - wnd.innerHeight / 2;
this.reportProgress(this.doc().scrollTop / this.doc().offsetHeight);
const progress = this.doc().scrollTop / this.doc().offsetHeight
this.reportProgress({ progress: progress, reference: this.wnd.innerHeight / this.doc().scrollHeight });
deselect(this.wnd);
ack(true);
});
Expand All @@ -120,14 +124,14 @@ export class ScrollSnapper extends Snapper {
comms.register("go_start", ScrollSnapper.moduleName, (_, ack) => {
if (this.doc().scrollTop === 0) return ack(false);
this.doc().scrollTop = 0;
this.reportProgress(0);
this.reportProgress({ progress: 0, reference: this.wnd.innerHeight / this.doc().scrollHeight });
ack(true);
});

comms.register("go_end", ScrollSnapper.moduleName, (_, ack) => {
if (this.doc().scrollTop === 0) return ack(false);
this.doc().scrollTop = 0;
this.reportProgress(0);
this.reportProgress({ progress: 0, reference: this.wnd.innerHeight / this.doc().scrollHeight });
ack(true);
})

Expand All @@ -142,7 +146,8 @@ export class ScrollSnapper extends Snapper {
], ScrollSnapper.moduleName, (_, ack) => ack(false));

comms.register("focus", ScrollSnapper.moduleName, (_, ack) => {
this.reportProgress(this.doc().scrollTop / this.doc().offsetHeight);
const progress = this.doc().scrollTop / this.doc().offsetHeight
this.reportProgress({ progress: progress, reference: this.wnd.innerHeight / this.doc().scrollHeight });
ack(true);
});

Expand Down
3 changes: 2 additions & 1 deletion navigator-html-injectables/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"forceConsistentCasingInFileNames": true,
// emit only .d.ts
"noEmit": false,
"emitDeclarationOnly": true
"emitDeclarationOnly": true,
"types": ["vite/client"]
},
}
5 changes: 5 additions & 0 deletions navigator-html-injectables/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { resolve } from "path";
import { defineConfig } from "vite";
import packageJson from "./package.json";

export default defineConfig({
build: {
Expand All @@ -8,5 +9,9 @@ export default defineConfig({
name: "navigator-html-injectables",
fileName: "index"
}
},
define: {
"import.meta.env.PACKAGE_NAME": JSON.stringify(packageJson.name),
"import.meta.env.PACKAGE_VERSION": JSON.stringify(packageJson.version),
}
});
13 changes: 13 additions & 0 deletions navigator/CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.3.0] - 2024-11-22

### Changed

- `findNearestPosition` in the (`EPUBNavigator`)[https://github.com/readium/ts-toolkit/blob/develop/navigator/src/epub/EpubNavigator.ts] has been replaced by `findLastPositionInProgressionRange` and `findNearestPositions` (#62).
- The Readium CSS dependency is now on the latest 1.x version, and added as a dependency using the `@readium/css` NPM package.
Loading

0 comments on commit 587c788

Please sign in to comment.