@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator-html-injectables",
3
- "version": "2.4.0-beta.1",
3
+ "version": "2.4.0-beta.2",
4
4
  "type": "module",
5
5
  "description": "An embeddable solution for connecting frames of HTML publications with a Readium Navigator",
6
6
  "author": "readium",
@@ -1,12 +1,18 @@
1
1
  import { ReadiumWindow } from "./dom.ts";
2
2
 
3
3
  export function isRTL(wnd: ReadiumWindow): boolean {
4
- // Check body first (author-declared direction), then fall back to the
5
- // root element where we inject dir="rtl" for RTL publications.
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
- if (Math.abs(current) > 1) {
197
- this.doc().scrollLeft = current < 0 ? current + 1 : current - 1;
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 < 0 ? current - 1 : current + 1;
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 (scrollLeft=-scrollable)
215
- this.doc().scrollLeft = -(this.scrollable() * position);
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 = left edge. Write -scrollable; browsers normalize
267
- // the sign internally so this works regardless of their scrollLeft convention.
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 + (isRTL(this.wnd) ? -1 : 1);
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 = isRTL(this.wnd)
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 = isRTL(this.wnd) ? -(this.cachedScrollWidth - this.wnd.innerWidth) : 0;
283
- const maxScrollLeft = isRTL(this.wnd) ? 0 : (this.cachedScrollWidth - this.wnd.innerWidth);
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 (isRTL(wnd)) {
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 (isRTL(wnd)) {
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 (isRTL(wnd)) {
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 (isRTL(wnd)) {
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 (isRTL(wnd)) {
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 (isRTL(wnd)) {
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
@@ -46,6 +46,7 @@ export declare class CJKVerticalSnapper extends Snapper {
46
46
  private lastScrollLeft;
47
47
  private isResizing;
48
48
  private resizeDebounce;
49
+ private verticalLR;
49
50
  private doc;
50
51
  /** Total horizontally scrollable distance (magnitude). */
51
52
  private scrollable;
@@ -19,6 +19,7 @@ export declare class ColumnSnapper extends Snapper {
19
19
  private lastTurnTime;
20
20
  private wnd;
21
21
  private comms;
22
+ private rtl;
22
23
  private doc;
23
24
  private scrollOffset;
24
25
  snapOffset(offset: number): number;