@readium/navigator-html-injectables 2.4.0-beta.1 → 2.4.0-beta.2
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 +545 -541
- package/dist/index.umd.cjs +12 -12
- package/package.json +1 -1
- package/src/helpers/document.ts +9 -3
- package/src/modules/snapper/CJKVerticalSnapper.ts +20 -9
- package/src/modules/snapper/ColumnSnapper.ts +18 -19
- package/types/src/helpers/document.d.ts +1 -0
- package/types/src/modules/snapper/CJKVerticalSnapper.d.ts +1 -0
- package/types/src/modules/snapper/ColumnSnapper.d.ts +1 -0
package/package.json
CHANGED
package/src/helpers/document.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { ReadiumWindow } from "./dom.ts";
|
|
2
2
|
|
|
3
3
|
export function isRTL(wnd: ReadiumWindow): boolean {
|
|
4
|
-
// Check
|
|
5
|
-
|
|
6
|
-
const dir = wnd.document.body.dir || wnd.document.documentElement.dir;
|
|
4
|
+
// Check documentElement first, then fall back to body.
|
|
5
|
+
const dir = wnd.document.documentElement.dir || wnd.document.body.dir;
|
|
7
6
|
return dir.toLowerCase() === "rtl";
|
|
8
7
|
}
|
|
9
8
|
|
|
9
|
+
export function isVerticalLR(wnd: ReadiumWindow): boolean {
|
|
10
|
+
// Check documentElement first, then fall back to body.
|
|
11
|
+
const writingMode = wnd.getComputedStyle(wnd.document.documentElement).writingMode
|
|
12
|
+
|| wnd.getComputedStyle(wnd.document.body).writingMode;
|
|
13
|
+
return writingMode === 'vertical-lr';
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
export function getColumnCountPerScreen(wnd: ReadiumWindow): number {
|
|
11
17
|
return parseInt(
|
|
12
18
|
wnd.getComputedStyle(
|
|
@@ -4,7 +4,7 @@ import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/
|
|
|
4
4
|
import { ModuleName } from "../ModuleLibrary.ts";
|
|
5
5
|
import { Snapper } from "./Snapper.ts";
|
|
6
6
|
import { rangeFromLocator } from "../../helpers/locator.ts";
|
|
7
|
-
import { forceWebkitRecalc } from "../../helpers/document.ts";
|
|
7
|
+
import { forceWebkitRecalc, isVerticalLR } from "../../helpers/document.ts";
|
|
8
8
|
import { PatternAnalyzer } from "../../protection/PatternAnalyzer.ts";
|
|
9
9
|
import { SCROLL_PROTECTION_CONFIG } from "../../protection/config.ts";
|
|
10
10
|
import { BaseSuspiciousActivityEvent } from "../Peripherals.ts";
|
|
@@ -54,6 +54,10 @@ export class CJKVerticalSnapper extends Snapper {
|
|
|
54
54
|
private lastScrollLeft = 0;
|
|
55
55
|
private isResizing = false;
|
|
56
56
|
private resizeDebounce: number | null = null;
|
|
57
|
+
// true for writing-mode: vertical-lr (Traditional Mongolian).
|
|
58
|
+
// vertical-lr uses non-negative scrollLeft like LTR horizontal, so
|
|
59
|
+
// absolute position writes must be positive, not negative.
|
|
60
|
+
private verticalLR = false;
|
|
57
61
|
|
|
58
62
|
private doc() {
|
|
59
63
|
return this.wnd.document.scrollingElement as HTMLElement;
|
|
@@ -150,6 +154,7 @@ export class CJKVerticalSnapper extends Snapper {
|
|
|
150
154
|
this.initialScrollHandled = false;
|
|
151
155
|
this.lastScrollLeft = 0;
|
|
152
156
|
this.isResizing = false;
|
|
157
|
+
this.verticalLR = isVerticalLR(wnd);
|
|
153
158
|
if (this.resizeDebounce) {
|
|
154
159
|
this.wnd.clearTimeout(this.resizeDebounce);
|
|
155
160
|
this.resizeDebounce = null;
|
|
@@ -193,10 +198,13 @@ export class CJKVerticalSnapper extends Snapper {
|
|
|
193
198
|
comms.register("force_webkit_recalc", CJKVerticalSnapper.moduleName, () => {
|
|
194
199
|
forceWebkitRecalc(this.wnd);
|
|
195
200
|
const current = this.doc().scrollLeft;
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
// Nudge by ±1 then restore to force a style recalculation.
|
|
202
|
+
// vertical-lr: scrollLeft ≥ 0, nudge toward 0 when possible.
|
|
203
|
+
// vertical-rl: scrollLeft ≤ 0, nudge toward 0 when possible.
|
|
204
|
+
if (this.verticalLR) {
|
|
205
|
+
this.doc().scrollLeft = current > 1 ? current - 1 : current + 1;
|
|
198
206
|
} else {
|
|
199
|
-
this.doc().scrollLeft = current <
|
|
207
|
+
this.doc().scrollLeft = current < -1 ? current + 1 : current - 1;
|
|
200
208
|
}
|
|
201
209
|
this.doc().scrollLeft = current;
|
|
202
210
|
});
|
|
@@ -211,8 +219,10 @@ export class CJKVerticalSnapper extends Snapper {
|
|
|
211
219
|
return;
|
|
212
220
|
}
|
|
213
221
|
this.wnd.requestAnimationFrame(() => {
|
|
214
|
-
// position 0 = start (scrollLeft=0), position 1 = end
|
|
215
|
-
|
|
222
|
+
// position 0 = start (scrollLeft=0), position 1 = end.
|
|
223
|
+
// vertical-lr: scrollLeft is positive; vertical-rl: negative.
|
|
224
|
+
const target = this.scrollable() * position;
|
|
225
|
+
this.doc().scrollLeft = this.verticalLR ? target : -target;
|
|
216
226
|
this.reportProgress();
|
|
217
227
|
deselect(this.wnd);
|
|
218
228
|
ack(true);
|
|
@@ -263,11 +273,12 @@ export class CJKVerticalSnapper extends Snapper {
|
|
|
263
273
|
ack(true);
|
|
264
274
|
});
|
|
265
275
|
|
|
266
|
-
// go_end = document end
|
|
267
|
-
//
|
|
276
|
+
// go_end = document end.
|
|
277
|
+
// vertical-lr: scrollLeft = +scrollable (rightmost column).
|
|
278
|
+
// vertical-rl: scrollLeft = -scrollable (leftmost column).
|
|
268
279
|
comms.register("go_end", CJKVerticalSnapper.moduleName, (_, ack) => {
|
|
269
280
|
if (Math.abs(this.doc().scrollLeft) === this.scrollable()) return ack(false);
|
|
270
|
-
this.doc().scrollLeft = -this.scrollable();
|
|
281
|
+
this.doc().scrollLeft = this.verticalLR ? this.scrollable() : -this.scrollable();
|
|
271
282
|
this.reportProgress();
|
|
272
283
|
ack(true);
|
|
273
284
|
});
|
|
@@ -36,6 +36,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
36
36
|
private lastTurnTime: number = 0;
|
|
37
37
|
private wnd!: ReadiumWindow;
|
|
38
38
|
private comms!: Comms;
|
|
39
|
+
private rtl = false;
|
|
39
40
|
private doc() { return this.wnd.document.scrollingElement as HTMLElement; }
|
|
40
41
|
private scrollOffset() {
|
|
41
42
|
// The reason we do this is because when the document is transformed (translate3d),
|
|
@@ -47,7 +48,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
snapOffset(offset: number) {
|
|
50
|
-
const value = offset + (
|
|
51
|
+
const value = offset + (this.rtl ? -1 : 1);
|
|
51
52
|
return value - (value % this.wnd.innerWidth);
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -66,9 +67,8 @@ export class ColumnSnapper extends Snapper {
|
|
|
66
67
|
* scrollLeft may be negative depending on the browser — Math.abs() normalizes it.
|
|
67
68
|
*/
|
|
68
69
|
private normScroll(): number {
|
|
69
|
-
const rtl = isRTL(this.wnd);
|
|
70
70
|
const raw = this.doc().scrollLeft || this.alreadyScrollLeft;
|
|
71
|
-
return rtl ? Math.abs(raw) : Math.max(0, this.wnd.scrollX > 0 ? this.wnd.scrollX : raw);
|
|
71
|
+
return this.rtl ? Math.abs(raw) : Math.max(0, this.wnd.scrollX > 0 ? this.wnd.scrollX : raw);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
reportProgress() {
|
|
@@ -97,7 +97,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
97
97
|
// LTR: end is on the right → bounce-r. RTL: start is on the right (norm≈0),
|
|
98
98
|
// end is on the left → bounce direction flips based on which boundary we're at.
|
|
99
99
|
const atStart = this.normScroll() < 5;
|
|
100
|
-
const bounceClass =
|
|
100
|
+
const bounceClass = this.rtl
|
|
101
101
|
? (atStart ? "readium-bounce-r" : "readium-bounce-l")
|
|
102
102
|
: "readium-bounce-r";
|
|
103
103
|
doc.classList.add(bounceClass);
|
|
@@ -125,7 +125,6 @@ export class ColumnSnapper extends Snapper {
|
|
|
125
125
|
|
|
126
126
|
// Snaps the current offset to the page width.
|
|
127
127
|
snapCurrentOffset(smooth=false, noprogress=false) {
|
|
128
|
-
const rtl = isRTL(this.wnd);
|
|
129
128
|
const doc = this.doc();
|
|
130
129
|
const columnCount = getColumnCountPerScreen(this.wnd);
|
|
131
130
|
const maxScroll = this.cachedScrollWidth - this.wnd.innerWidth;
|
|
@@ -137,14 +136,14 @@ export class ColumnSnapper extends Snapper {
|
|
|
137
136
|
// Forward drag: LTR = drag left (cdo > 0), RTL = drag right (cdo < 0).
|
|
138
137
|
// Normalize so that positive = moving forward in reading order.
|
|
139
138
|
const cdo = this.dragOffset();
|
|
140
|
-
const normalizedCdo = rtl ? -cdo : cdo;
|
|
139
|
+
const normalizedCdo = this.rtl ? -cdo : cdo;
|
|
141
140
|
const hurdle = (this.wnd.innerWidth / 3) * (normalizedCdo > 0 ? 2 : 1);
|
|
142
141
|
|
|
143
142
|
const snappedNorm = Math.min(maxScroll, Math.max(0, this.snapNormOffset(currentNorm + hurdle)));
|
|
144
143
|
|
|
145
144
|
// Convert back to signed scrollLeft for the actual DOM write.
|
|
146
|
-
const so = rtl ? -snappedNorm : snappedNorm;
|
|
147
|
-
const currentScrollLeft = rtl ? -currentNorm : currentNorm;
|
|
145
|
+
const so = this.rtl ? -snappedNorm : snappedNorm;
|
|
146
|
+
const currentScrollLeft = this.rtl ? -currentNorm : currentNorm;
|
|
148
147
|
|
|
149
148
|
const direction = so > currentScrollLeft ? "right" : "left";
|
|
150
149
|
this.checkSuspiciousSnap(direction, Math.abs(so - currentScrollLeft));
|
|
@@ -227,12 +226,11 @@ export class ColumnSnapper extends Snapper {
|
|
|
227
226
|
if(this.touchState === ScrollTouchState.MOVE) {
|
|
228
227
|
// Get the horizontal drag distance
|
|
229
228
|
const dragOffset = this.dragOffset();
|
|
230
|
-
const rtl = isRTL(this.wnd);
|
|
231
229
|
|
|
232
230
|
// Normalize: distance from document start, forward drag is positive.
|
|
233
231
|
const norm = this.normScroll();
|
|
234
232
|
const maxScroll = this.cachedScrollWidth - this.wnd.innerWidth;
|
|
235
|
-
const normalizedDrag = rtl ? -dragOffset : dragOffset;
|
|
233
|
+
const normalizedDrag = this.rtl ? -dragOffset : dragOffset;
|
|
236
234
|
|
|
237
235
|
if(this.cachedScrollWidth <= this.wnd.innerWidth) {
|
|
238
236
|
// Only a single page, meaning any swipe triggers next/prev
|
|
@@ -245,7 +243,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
245
243
|
this.comms.send("no_less", undefined);
|
|
246
244
|
} else if((maxScroll - norm) < 5 && normalizedDrag > 5) {
|
|
247
245
|
// At document end, tried to go forward
|
|
248
|
-
this.alreadyScrollLeft = rtl ? -maxScroll : maxScroll;
|
|
246
|
+
this.alreadyScrollLeft = this.rtl ? -maxScroll : maxScroll;
|
|
249
247
|
this.comms.send("no_more", undefined);
|
|
250
248
|
}
|
|
251
249
|
|
|
@@ -279,8 +277,8 @@ export class ColumnSnapper extends Snapper {
|
|
|
279
277
|
|
|
280
278
|
// For RTL, scrollLeft is in [-(scrollWidth - innerWidth), 0].
|
|
281
279
|
// For LTR, scrollLeft is in [0, scrollWidth - innerWidth].
|
|
282
|
-
const minScrollLeft =
|
|
283
|
-
const maxScrollLeft =
|
|
280
|
+
const minScrollLeft = this.rtl ? -(this.cachedScrollWidth - this.wnd.innerWidth) : 0;
|
|
281
|
+
const maxScrollLeft = this.rtl ? 0 : (this.cachedScrollWidth - this.wnd.innerWidth);
|
|
284
282
|
|
|
285
283
|
if(newpos < minScrollLeft) {
|
|
286
284
|
this.overscroll = newpos;
|
|
@@ -330,6 +328,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
330
328
|
mount(wnd: ReadiumWindow, comms: Comms): boolean {
|
|
331
329
|
this.wnd = wnd;
|
|
332
330
|
this.comms = comms;
|
|
331
|
+
this.rtl = isRTL(wnd);
|
|
333
332
|
if(!super.mount(wnd, comms)) return false;
|
|
334
333
|
|
|
335
334
|
wnd.navigator.epubReadingSystem && (wnd.navigator.epubReadingSystem.layoutStyle = "paginated");
|
|
@@ -451,7 +450,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
451
450
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
452
451
|
const scrollable = this.cachedScrollWidth - wnd.innerWidth;
|
|
453
452
|
const norm = scrollable * position;
|
|
454
|
-
if (
|
|
453
|
+
if (this.rtl) {
|
|
455
454
|
this.doc().scrollLeft = -this.snapNormOffset(norm);
|
|
456
455
|
} else {
|
|
457
456
|
this.doc().scrollLeft = this.snapOffset(norm);
|
|
@@ -469,7 +468,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
469
468
|
return;
|
|
470
469
|
}
|
|
471
470
|
this.wnd.requestAnimationFrame(() => {
|
|
472
|
-
if (
|
|
471
|
+
if (this.rtl) {
|
|
473
472
|
this.doc().scrollLeft = -this.snapNormOffset(element.getBoundingClientRect().left + wnd.scrollX);
|
|
474
473
|
} else {
|
|
475
474
|
this.doc().scrollLeft = this.snapOffset(element.getBoundingClientRect().left + wnd.scrollX);
|
|
@@ -504,7 +503,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
504
503
|
return;
|
|
505
504
|
}
|
|
506
505
|
this.wnd.requestAnimationFrame(() => {
|
|
507
|
-
if (
|
|
506
|
+
if (this.rtl) {
|
|
508
507
|
this.doc().scrollLeft = -this.snapNormOffset(r.getBoundingClientRect().left + wnd.scrollX);
|
|
509
508
|
} else {
|
|
510
509
|
this.doc().scrollLeft = this.snapOffset(r.getBoundingClientRect().left + wnd.scrollX);
|
|
@@ -519,7 +518,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
519
518
|
this.wnd.requestAnimationFrame(() => {
|
|
520
519
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
521
520
|
let snappedFinal: number;
|
|
522
|
-
if (
|
|
521
|
+
if (this.rtl) {
|
|
523
522
|
// RTL: end is the leftmost position, using normalized snapping first
|
|
524
523
|
snappedFinal = -this.snapNormOffset(this.cachedScrollWidth - wnd.innerWidth);
|
|
525
524
|
} else {
|
|
@@ -548,7 +547,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
548
547
|
this.wnd.requestAnimationFrame(() => {
|
|
549
548
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
550
549
|
let change: boolean;
|
|
551
|
-
if (
|
|
550
|
+
if (this.rtl) {
|
|
552
551
|
// RTL: go_prev = toward document start = decreasing norm = scrollLeft toward 0
|
|
553
552
|
change = rtlScrollToNorm(this.normScroll() - wnd.innerWidth);
|
|
554
553
|
} else {
|
|
@@ -567,7 +566,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
567
566
|
this.wnd.requestAnimationFrame(() => {
|
|
568
567
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
569
568
|
let change: boolean;
|
|
570
|
-
if (
|
|
569
|
+
if (this.rtl) {
|
|
571
570
|
// RTL: go_next = toward document end = increasing norm = scrollLeft more negative
|
|
572
571
|
change = rtlScrollToNorm(this.normScroll() + wnd.innerWidth);
|
|
573
572
|
} else {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReadiumWindow } from "./dom.ts";
|
|
2
2
|
export declare function isRTL(wnd: ReadiumWindow): boolean;
|
|
3
|
+
export declare function isVerticalLR(wnd: ReadiumWindow): boolean;
|
|
3
4
|
export declare function getColumnCountPerScreen(wnd: ReadiumWindow): number;
|
|
4
5
|
/**
|
|
5
6
|
* Returns the "content height" of an element, which is its clientHeight
|