Skip to content

Commit

Permalink
Merge branch 'dev' into text-deselection-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
chocolatkey authored Mar 7, 2024
2 parents c2c3321 + 012983e commit 3694843
Show file tree
Hide file tree
Showing 10 changed files with 1,577 additions and 1,943 deletions.
44 changes: 25 additions & 19 deletions navigator-html-injectables/src/comms/comms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,33 @@ export class Comms {
}));
}

public register(key: CommsCommandKey, module: string, callback: CommsCallback) {
const listeners = this.registrar.get(key);
if(listeners && listeners.length >= 0) {
const existing = listeners.find(l => l.module === module);
if(existing) throw new Error(`Trying to register another callback for combination of event ${key} and module ${module}`);
listeners.push({
cb: callback,
module
})
this.registrar.set(key, listeners);
} else
this.registrar.set(key, [{
cb: callback,
module
}]);
public register(key: CommsCommandKey | CommsCommandKey[], module: string, callback: CommsCallback) {
if(!Array.isArray(key)) key = [key];
key.forEach(k => {
const listeners = this.registrar.get(k);
if(listeners && listeners.length >= 0) {
const existing = listeners.find(l => l.module === module);
if(existing) throw new Error(`Trying to register another callback for combination of event ${k} and module ${module}`);
listeners.push({
cb: callback,
module
})
this.registrar.set(k, listeners);
} else
this.registrar.set(k, [{
cb: callback,
module
}]);
})
}

public unregister(key: CommsCommandKey, module: string) {
const listeners = this.registrar.get(key);
if(!listeners || listeners.length === 0) return;
listeners.splice(listeners.findIndex(l => l.module === module), 1);
public unregister(key: CommsCommandKey | CommsCommandKey[], module: string) {
if(!Array.isArray(key)) key = [key];
key.forEach(k => {
const listeners = this.registrar.get(k);
if(!listeners || listeners.length === 0) return;
listeners.splice(listeners.findIndex(l => l.module === module), 1);
});
}

public unregisterAll(module: string) {
Expand Down
7 changes: 6 additions & 1 deletion navigator-html-injectables/src/helpers/rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ export function getClientRectsNoOverlap(
range: Range,
doNotMergeHorizontallyAlignedRects: boolean
) {
const clientRects = range.getClientRects();
let clientRects = range.getClientRects();

// Try falling back to the client rects of the common ancestor of the range if it's an HTML Element
if(!clientRects.length)
if(range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE)
clientRects = (range.commonAncestorContainer as HTMLElement).getClientRects();

const tolerance = 1;
const originalRects: Rect[] = [];
Expand Down
35 changes: 29 additions & 6 deletions navigator-html-injectables/src/modules/Decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,16 @@ interface DecorationItem {
container: HTMLElement | undefined;
}

const canNativeHighlight = "Highlight" in window;
const cannotNativeHighlight = ["IMG", "IMAGE", "AUDIO", "VIDEO", "SVG"];

class DecorationGroup {
public readonly items: DecorationItem[] = [];
private lastItemId = 0;
private container: HTMLDivElement | undefined = undefined;
private activateable = false;
public readonly experimentalHighlights: boolean = false;
private readonly notTextFlag: Map<string, boolean> | undefined;

/**
* Creates a DecorationGroup object
Expand All @@ -69,8 +73,9 @@ class DecorationGroup {
private readonly id: string,
private readonly name: string
) {
if ("Highlight" in window) {
if (canNativeHighlight) {
this.experimentalHighlights = true;
this.notTextFlag = new Map<string, boolean>();
}
}

Expand All @@ -94,12 +99,28 @@ class DecorationGroup {
this.comms.log("Can't locate DOM range for decoration", decoration);
return;
}
const ancestor = range.commonAncestorContainer as HTMLElement;
if(ancestor.nodeType !== Node.TEXT_NODE) {
if(cannotNativeHighlight.includes(ancestor.nodeName.toUpperCase())) {
// The common ancestor is an element that definitely cannot be highlighted
this.notTextFlag?.set(id, true);
}
if(ancestor.querySelector(cannotNativeHighlight.join(", ").toLowerCase())) {
// Contains elements that definitely cannot be highlighted as children
this.notTextFlag?.set(id, true);
}
if((ancestor.textContent?.trim() || "").length === 0) {
// No text to be highlighted
this.notTextFlag?.set(id, true);
}
}

const item = {
decoration,
id,
range,
} as DecorationItem;

this.items.push(item);
this.layout(item);
this.renderLayout([item]);
Expand All @@ -120,11 +141,12 @@ class DecorationGroup {
item.container.remove();
item.container = undefined;
}
if (this.experimentalHighlights) {
if (this.experimentalHighlights && !this.notTextFlag?.has(item.id)) {
// Remove highlight from ranges
const mm = ((this.wnd as any).CSS.highlights as Map<string, unknown>).get(this.id) as Set<Range>;
mm?.delete(item.range);
}
this.notTextFlag?.delete(item.id);
}

/**
Expand All @@ -142,6 +164,7 @@ class DecorationGroup {
clear() {
this.clearContainer();
this.items.length = 0;
this.notTextFlag?.clear();
}

/**
Expand Down Expand Up @@ -172,7 +195,7 @@ class DecorationGroup {
* @param item
*/
private layout(item: DecorationItem) {
if (this.experimentalHighlights) {
if (this.experimentalHighlights && !this.notTextFlag?.has(item.id)) {
// Highlight using the new Highlight Web API!
return this.experimentalLayout(item);
}
Expand Down Expand Up @@ -288,12 +311,12 @@ class DecorationGroup {

private currentRender = 0;
private renderLayout(items: DecorationItem[]) {
if(this.experimentalHighlights) return;
this.wnd.cancelAnimationFrame(this.currentRender);
this.currentRender = this.wnd.requestAnimationFrame(() => {
if(!items) return;
items = items.filter(i => this.notTextFlag?.has(i.id));
if(!items || items.length === 0) return;
const groupContainer = this.requireContainer() as HTMLDivElement;
groupContainer.append(...this.items.map(i => i.container).filter(c => !!c) as Node[])
groupContainer.append(...items.map(i => i.container).filter(c => !!c) as Node[])
});
}

Expand Down
40 changes: 6 additions & 34 deletions navigator-html-injectables/src/modules/setup/FixedSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,41 +39,13 @@ export class FixedSetup extends Setup {
ack(true);
})

comms.register("go_progression", FixedSetup.moduleName, (_, ack) => {
ack(true);
});

comms.register("go_start", FixedSetup.moduleName, (_, ack) => {
ack(true);
});

comms.register("go_text", FixedSetup.moduleName, (_, ack) => {
ack(true);
});

comms.register("go_id", FixedSetup.moduleName, (_, ack) => {
ack(true);
});
comms.register("first_visible_locator", FixedSetup.moduleName, (_, ack) => ack(false))

comms.register("go_end", FixedSetup.moduleName, (_, ack) => {
ack(true);
})

comms.register("go_prev", FixedSetup.moduleName, (_, ack) => {
ack(false);
});

comms.register("go_next", FixedSetup.moduleName, (_, ack) => {
ack(false);
});

comms.register("unfocus", FixedSetup.moduleName, (_, ack) => {
ack(true);
});

comms.register("focus", FixedSetup.moduleName, (_, ack) => {
ack(true);
});
comms.register([
"focus", "unfocus", "go_next", "go_prev",
"go_id", "go_end", "go_start", "go_text",
"go_progression"
], FixedSetup.moduleName, (_, ack) => ack(true));

comms.register("activate", FixedSetup.moduleName, (_, ack) => {
this.unblock(wnd as ReadiumWindow);
Expand Down
18 changes: 15 additions & 3 deletions navigator-html-injectables/src/modules/snapper/ColumnSnapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Snapper } from "./Snapper";
import { getColumnCountPerScreen, isRTL, appendVirtualColumnIfNeeded } from "../../helpers/document";
import { easeInOutQuad } from "../../helpers/animation";
import { ModuleName } from "../ModuleLibrary";
import { Locator, LocatorText } from "@readium/shared/src/publication";
import { Locator, LocatorLocations, LocatorText } from "@readium/shared/src/publication";
import { rangeFromLocator } from "../../helpers/locator";
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom";

Expand Down Expand Up @@ -329,12 +329,24 @@ export class ColumnSnapper extends Snapper {
});
});

comms.register("go_text", ColumnSnapper.moduleName, (data, ack) => {
comms.register("go_text", ColumnSnapper.moduleName, (data: unknown | unknown[], ack) => {
let cssSelector = undefined;
if(Array.isArray(data)) {
if(data.length > 1)
// Second element is presumed to be the CSS selector
cssSelector = (data as unknown[])[1] as string;
data = data[0]; // First element will always be the locator text object
}
const text = LocatorText.deserialize(data);
const r = rangeFromLocator(this.wnd.document, new Locator({
href: wnd.location.href,
type: "text/html",
text
text,
locations: cssSelector ? new LocatorLocations({
otherLocations: new Map([
["cssSelector", cssSelector]
])
}) : undefined
}));
if(!r) {
ack(false);
Expand Down
29 changes: 19 additions & 10 deletions navigator-html-injectables/src/modules/snapper/ScrollSnapper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Locator, LocatorText } from "@readium/shared/src/publication";
import { Locator, LocatorLocations, LocatorText } from "@readium/shared/src/publication";
import { Comms } from "../../comms";
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom";
import { AnchorObserver, helperCreateAnchorElements, helperRemoveAnchorElements } from '../../helpers/scrollSnapperHelper';
Expand Down Expand Up @@ -87,11 +87,23 @@ export class ScrollSnapper extends Snapper {
});

comms.register("go_text", ScrollSnapper.moduleName, (data, ack) => {
let cssSelector = undefined;
if(Array.isArray(data)) {
if(data.length > 1)
// Second element is presumed to be the CSS selector
cssSelector = (data as unknown[])[1] as string;
data = data[0]; // First element will always be the locator text object
}
const text = LocatorText.deserialize(data);
const r = rangeFromLocator(this.wnd.document, new Locator({
href: wnd.location.href,
type: "text/html",
text
text,
locations: cssSelector ? new LocatorLocations({
otherLocations: new Map([
["cssSelector", cssSelector]
])
}) : undefined
}));
if(!r) {
ack(false);
Expand Down Expand Up @@ -119,19 +131,16 @@ export class ScrollSnapper extends Snapper {
ack(true);
})

comms.register("go_prev", ScrollSnapper.moduleName, (_, ack) => {
ack(false);
});

comms.register("go_next", ScrollSnapper.moduleName, (_, ack) => {
ack(false);
});

comms.register("unfocus", ScrollSnapper.moduleName, (_, ack) => {
deselect(this.wnd);
ack(true);
});

comms.register([
"go_next",
"go_prev",
], ScrollSnapper.moduleName, (_, ack) => ack(false));

comms.register("focus", ScrollSnapper.moduleName, (_, ack) => {
this.reportProgress(this.doc().scrollTop / this.doc().offsetHeight);
ack(true);
Expand Down
24 changes: 22 additions & 2 deletions navigator/src/epub/EpubNavigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,31 @@ export class EpubNavigator extends VisualNavigator {

private async loadLocator(locator: Locator, cb: (ok: boolean) => void) {
let done = false;
if(locator.text?.highlight)
let cssSelector = (typeof locator.locations.getCssSelector === 'function') && locator.locations.getCssSelector();
if(locator.text?.highlight) {
done = await new Promise<boolean>((res, _) => {
// Attempt to go to a highlighted piece of text in the resource
this._cframes[0]!.msg!.send("go_text", locator.text?.serialize(), (ok) => res(ok));
this._cframes[0]!.msg!.send(
"go_text",
cssSelector ? [
locator.text?.serialize(),
cssSelector // Include CSS selector if it exists
] : locator.text?.serialize(),
(ok) => res(ok)
);
});
} else if(cssSelector) {
done = await new Promise<boolean>((res, _) => {
this._cframes[0]!.msg!.send(
"go_text",
[
"", // No text!
cssSelector // Just CSS selector
],
(ok) => res(ok)
);
});
}
if(done) {
cb(done);
return;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
"navigator-html-injectables",
"shared"
],
"packageManager": "pnpm@7.9.0"
"packageManager": "pnpm@8.15.4"
}
Loading

0 comments on commit 3694843

Please sign in to comment.