@prose-reader/core 1.117.0 → 1.119.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.
Files changed (66) hide show
  1. package/dist/cfi/generate/generateCfiForSpineItemPage.d.ts +1 -1
  2. package/dist/cfi/generate/getItemAnchor.d.ts +1 -1
  3. package/dist/cfi/generate/getRootCfi.d.ts +1 -1
  4. package/dist/cfi/lookup/resolveCfi.d.ts +2 -236
  5. package/dist/context/Context.d.ts +1 -1
  6. package/dist/createReaderWithEnhancer.d.ts +11 -245
  7. package/dist/enhancers/events/normalizeEventForViewport.d.ts +2 -1
  8. package/dist/enhancers/events/translateFramePositionIntoPage.d.ts +12 -0
  9. package/dist/enhancers/html/enhancer.d.ts +2 -0
  10. package/dist/enhancers/html/renderer/HtmlRenderer.d.ts +26 -0
  11. package/dist/{spineItem/frame/loader → enhancers/html/renderer}/attachFrameSrc.d.ts +4 -2
  12. package/dist/{spineItem/frame → enhancers/html/renderer}/createHtmlPageFromResource.d.ts +1 -1
  13. package/dist/enhancers/html/renderer/prePaginated/renderPrePaginated.d.ts +31 -0
  14. package/dist/enhancers/html/renderer/reflowable/renderReflowable.d.ts +22 -0
  15. package/dist/enhancers/loading/constants.d.ts +2 -0
  16. package/dist/enhancers/loading/createLoadingElement.d.ts +7 -0
  17. package/dist/enhancers/{loadingEnhancer.d.ts → loading/loadingEnhancer.d.ts} +3 -3
  18. package/dist/enhancers/media/ImageRenderer.d.ts +15 -0
  19. package/dist/enhancers/media/media.d.ts +2 -0
  20. package/dist/enhancers/navigation/resolvers/getNavigationForLeftOrTopPage.d.ts +1 -1
  21. package/dist/enhancers/navigation/resolvers/getNavigationForRightOrBottomPage.d.ts +1 -1
  22. package/dist/enhancers/navigation/resolvers/getSpineItemPositionForLeftPage.d.ts +1 -1
  23. package/dist/enhancers/navigation/resolvers/getSpineItemPositionForRightPage.d.ts +1 -1
  24. package/dist/enhancers/progression.d.ts +1 -1
  25. package/dist/hooks/types.d.ts +19 -9
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.js +2314 -2181
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.umd.cjs +3010 -2877
  30. package/dist/index.umd.cjs.map +1 -1
  31. package/dist/navigation/Navigator.d.ts +6 -6
  32. package/dist/navigation/resolvers/NavigationResolver.d.ts +1 -1
  33. package/dist/navigation/resolvers/getNavigationForSpineItemPage.d.ts +1 -1
  34. package/dist/navigation/resolvers/getNavigationFromSpineItemPosition.d.ts +1 -1
  35. package/dist/reader.d.ts +3 -237
  36. package/dist/settings/SettingsManager.d.ts +2 -1
  37. package/dist/settings/types.d.ts +9 -1
  38. package/dist/spine/SpineItemsManager.d.ts +6 -591
  39. package/dist/spine/SpineItemsObserver.d.ts +2 -1
  40. package/dist/spine/SpineLayout.d.ts +1 -1
  41. package/dist/spine/locator/SpineLocator.d.ts +5 -473
  42. package/dist/spine/locator/getAbsolutePageIndexFromPageIndex.d.ts +1 -1
  43. package/dist/spine/locator/getSpineInfoFromAbsolutePageIndex.d.ts +2 -118
  44. package/dist/spine/locator/getSpineItemFromPosition.d.ts +1 -118
  45. package/dist/spineItem/DocumentRenderer.d.ts +48 -0
  46. package/dist/spineItem/ResourceHandler.d.ts +15 -0
  47. package/dist/spineItem/SpineItem.d.ts +64 -0
  48. package/dist/spineItem/locationResolver.d.ts +1 -1
  49. package/dist/spineItem/navigationResolver.d.ts +1 -1
  50. package/dist/utils/frames.d.ts +15 -0
  51. package/package.json +3 -3
  52. package/dist/enhancers/media.d.ts +0 -2
  53. package/dist/spineItem/commonSpineItem.d.ts +0 -128
  54. package/dist/spineItem/createSpineItem.d.ts +0 -130
  55. package/dist/spineItem/frame/FrameItem.d.ts +0 -43
  56. package/dist/spineItem/frame/loader/configureFrame.d.ts +0 -9
  57. package/dist/spineItem/frame/loader/loadFrame.d.ts +0 -11
  58. package/dist/spineItem/frame/loader/loader.d.ts +0 -22
  59. package/dist/spineItem/frame/loader/unloadFrame.d.ts +0 -8
  60. package/dist/spineItem/frame/loader/waitForFrameLoad.d.ts +0 -2
  61. package/dist/spineItem/frame/loader/waitForFrameReady.d.ts +0 -2
  62. package/dist/spineItem/prePaginatedSpineItem.d.ts +0 -129
  63. package/dist/spineItem/reflowable/ReflowableSpineItems.d.ts +0 -128
  64. package/dist/spineItem/styles/getStyleForViewportDocument.d.ts +0 -1
  65. /package/dist/{spineItem/frame/loader → enhancers/html/renderer}/createFrameElement.d.ts +0 -0
  66. /package/dist/{spineItem → enhancers/html/renderer}/reflowable/styles.d.ts +0 -0
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { takeUntil, Subject, combineLatest, map as map$1, switchMap, merge, EMPTY, fromEvent, withLatestFrom, NEVER, Observable, of, scheduled, animationFrameScheduler, distinctUntilChanged as distinctUntilChanged$1, tap as tap$1, throttleTime, finalize, startWith as startWith$1, debounceTime as debounceTime$1, BehaviorSubject, combineLatestWith, shareReplay as shareReplay$1, ReplaySubject, filter as filter$1, share, mergeMap, delay, identity, timer, skip as skip$1, exhaustMap, first as first$1, take as take$1, from, catchError, forkJoin } from "rxjs";
2
- import { startWith, map, shareReplay, tap, pairwise, switchMap as switchMap$1, take, distinctUntilChanged, takeUntil as takeUntil$1, first, filter, debounceTime, skip, withLatestFrom as withLatestFrom$1, exhaustMap as exhaustMap$1, ignoreElements, endWith, defaultIfEmpty, share as share$1, mergeMap as mergeMap$1, catchError as catchError$1 } from "rxjs/operators";
3
- import { shallowMergeIfDefined, isShallowEqual, parseContentType, detectMimeTypeFromName } from "@prose-reader/shared";
1
+ import { takeUntil, Subject, combineLatest, map as map$1, switchMap, merge, EMPTY, fromEvent, withLatestFrom, NEVER, Observable, of, scheduled, animationFrameScheduler, take as take$1, from, distinctUntilChanged as distinctUntilChanged$1, tap as tap$1, throttleTime, finalize, startWith as startWith$1, debounceTime as debounceTime$1, BehaviorSubject, combineLatestWith, shareReplay as shareReplay$1, ReplaySubject, filter as filter$1, share, mergeMap, delay, identity, timer, skip as skip$1, exhaustMap, first as first$1, endWith, lastValueFrom, forkJoin, catchError as catchError$1 } from "rxjs";
2
+ import { startWith, map, shareReplay, tap, pairwise, switchMap as switchMap$1, take, distinctUntilChanged, takeUntil as takeUntil$1, first, filter, debounceTime, skip, withLatestFrom as withLatestFrom$1, share as share$1, mergeMap as mergeMap$1, catchError } from "rxjs/operators";
3
+ import { shallowMergeIfDefined, isShallowEqual, detectMimeTypeFromName, parseContentType } from "@prose-reader/shared";
4
4
  import { isShallowEqual as isShallowEqual2 } from "@prose-reader/shared";
5
5
  const chromeEnhancer = (next) => (options) => {
6
6
  const reader = next(options);
@@ -13,9 +13,11 @@ const chromeEnhancer = (next) => (options) => {
13
13
  };
14
14
  containerElement.addEventListener(`scroll`, onScroll);
15
15
  });
16
- reader.hookManager.register(`item.onLoad`, ({ frame }) => {
17
- var _a;
18
- (_a = frame.contentDocument) == null ? void 0 : _a.body.setAttribute(`tabindex`, `-1`);
16
+ reader.hookManager.register(`item.onDocumentLoad`, ({ layers }) => {
17
+ var _a, _b;
18
+ const frame = (_a = layers[0]) == null ? void 0 : _a.element;
19
+ if (!(frame instanceof HTMLIFrameElement)) return;
20
+ (_b = frame.contentDocument) == null ? void 0 : _b.body.setAttribute(`tabindex`, `-1`);
19
21
  });
20
22
  return reader;
21
23
  };
@@ -142,21 +144,19 @@ const fontsEnhancer = (next) => (options) => {
142
144
  `;
143
145
  const applyChangeToSpineItems = (requireLayout) => {
144
146
  reader.spineItemsManager.items.forEach((item) => {
145
- var _a, _b, _c, _d;
147
+ var _a;
146
148
  if (item.item.renditionLayout !== `pre-paginated`) {
147
- (_b = (_a = item.frame).removeStyle) == null ? void 0 : _b.call(_a, `prose-reader-fonts`);
148
- (_d = (_c = item.frame).addStyle) == null ? void 0 : _d.call(_c, `prose-reader-fonts`, getStyle());
149
+ (_a = item.upsertCSS) == null ? void 0 : _a.call(item, `prose-reader-fonts`, getStyle());
149
150
  }
150
151
  });
151
152
  if (requireLayout) {
152
153
  reader.layout();
153
154
  }
154
155
  };
155
- reader.hookManager.register(`item.onLoad`, ({ itemId }) => {
156
+ reader.hookManager.register(`item.onDocumentLoad`, ({ itemId }) => {
156
157
  const item = reader.spineItemsManager.get(itemId);
157
158
  if ((item == null ? void 0 : item.item.renditionLayout) !== `pre-paginated`) {
158
- item == null ? void 0 : item.frame.removeStyle(`prose-reader-fonts`);
159
- item == null ? void 0 : item.frame.addStyle(`prose-reader-fonts`, getStyle());
159
+ item == null ? void 0 : item.upsertCSS(`prose-reader-fonts`, getStyle());
160
160
  }
161
161
  });
162
162
  const shouldRequireLayout = (source) => source.pipe(
@@ -211,10 +211,12 @@ const hotkeysEnhancer = (next) => (options) => {
211
211
  switchMap(
212
212
  (spineItems) => merge(
213
213
  ...spineItems.map(
214
- (item) => item.$.loaded$.pipe(
215
- switchMap(
216
- (iframe) => (iframe == null ? void 0 : iframe.contentDocument) ? navigateOnKey(iframe.contentDocument) : EMPTY
217
- )
214
+ (item) => item.loaded$.pipe(
215
+ switchMap(() => {
216
+ var _a;
217
+ const element = (_a = item.renderer.layers[0]) == null ? void 0 : _a.element;
218
+ return element instanceof HTMLIFrameElement && (element == null ? void 0 : element.contentDocument) ? navigateOnKey(element.contentDocument) : EMPTY;
219
+ })
218
220
  )
219
221
  )
220
222
  )
@@ -312,19 +314,94 @@ const waitForSwitch = (waitForStream) => (stream) => stream.pipe(
312
314
  )
313
315
  )
314
316
  );
317
+ const getAttributeValueFromString = (string, key) => {
318
+ const regExp = new RegExp(key + `\\s*=\\s*([0-9.]+)`, `i`);
319
+ const match = string.match(regExp) || [];
320
+ const firstMatch = match[1] || `0`;
321
+ return match && parseFloat(firstMatch) || 0;
322
+ };
323
+ const injectCSS = (frameElement, id, style, prepend) => {
324
+ if (frameElement && frameElement.contentDocument && frameElement.contentDocument.head) {
325
+ const userStyle = document.createElement(`style`);
326
+ userStyle.id = id;
327
+ userStyle.innerHTML = style;
328
+ if (prepend) {
329
+ frameElement.contentDocument.head.prepend(userStyle);
330
+ } else {
331
+ frameElement.contentDocument.head.appendChild(userStyle);
332
+ }
333
+ }
334
+ };
335
+ const removeCSS = (frameElement, id) => {
336
+ if (frameElement && frameElement.contentDocument && frameElement.contentDocument.head) {
337
+ const styleElement = frameElement.contentDocument.getElementById(id);
338
+ if (styleElement) {
339
+ styleElement.remove();
340
+ }
341
+ }
342
+ };
343
+ const upsertCSS = (frameElement, id, style, prepend) => {
344
+ if (!frameElement) return;
345
+ removeCSS(frameElement, id);
346
+ injectCSS(frameElement, id, style, prepend);
347
+ };
348
+ const getFrameViewportInfo = (frame) => {
349
+ if (frame && (frame == null ? void 0 : frame.contentDocument)) {
350
+ const doc = frame.contentDocument;
351
+ const viewportMetaElement = doc.querySelector(`meta[name='viewport']`);
352
+ if (viewportMetaElement) {
353
+ const viewportContent = viewportMetaElement.getAttribute(`content`);
354
+ if (viewportContent) {
355
+ const width = getAttributeValueFromString(viewportContent, `width`);
356
+ const height = getAttributeValueFromString(viewportContent, `height`);
357
+ if (width > 0 && height > 0) {
358
+ return {
359
+ hasViewport: true,
360
+ width,
361
+ height
362
+ };
363
+ } else {
364
+ return { hasViewport: true };
365
+ }
366
+ }
367
+ }
368
+ }
369
+ return { hasViewport: false };
370
+ };
371
+ const waitForFrameLoad = (stream) => stream.pipe(
372
+ switchMap(
373
+ (frame) => fromEvent(frame, `load`).pipe(
374
+ take$1(1),
375
+ map$1(() => frame)
376
+ )
377
+ )
378
+ );
379
+ const waitForFrameReady = (stream) => stream.pipe(
380
+ switchMap(
381
+ (frame) => {
382
+ var _a;
383
+ return from(((_a = frame == null ? void 0 : frame.contentDocument) == null ? void 0 : _a.fonts.ready) || of(void 0)).pipe(
384
+ map$1(() => frame)
385
+ );
386
+ }
387
+ )
388
+ );
315
389
  const fixReflowable = (reader) => {
316
390
  reader.hookManager.register(
317
391
  `item.onAfterLayout`,
318
392
  ({ item, blankPagePosition, minimumWidth }) => {
393
+ var _a, _b;
319
394
  const spineItem = reader.spineItemsManager.get(item.id);
320
- if (!((spineItem == null ? void 0 : spineItem.item.renditionLayout) === `reflowable`)) return;
321
- const { viewportDimensions } = (spineItem == null ? void 0 : spineItem.getViewPortInformation()) ?? {};
395
+ const element = (_a = spineItem == null ? void 0 : spineItem.renderer.layers[0]) == null ? void 0 : _a.element;
396
+ if (!((spineItem == null ? void 0 : spineItem.item.renditionLayout) === `reflowable`) || !(element instanceof HTMLIFrameElement))
397
+ return;
398
+ const { hasViewport } = getFrameViewportInfo(element);
322
399
  const { width: pageWidth } = reader.context.getPageSize();
323
- const frameElement = spineItem == null ? void 0 : spineItem.frame.element;
324
- if (viewportDimensions) {
400
+ const frameElement = (_b = spineItem == null ? void 0 : spineItem.renderer.layers[0]) == null ? void 0 : _b.element;
401
+ if (hasViewport) {
325
402
  const spineManagerWantAFullWidthItem = pageWidth < minimumWidth;
326
403
  const noBlankPageAsked = blankPagePosition === `none`;
327
- if (noBlankPageAsked && spineManagerWantAFullWidthItem) {
404
+ if (noBlankPageAsked && spineManagerWantAFullWidthItem && frameElement instanceof HTMLIFrameElement) {
328
405
  frameElement == null ? void 0 : frameElement.style.setProperty(
329
406
  `left`,
330
407
  reader.context.isRTL() ? `75%` : `25%`
@@ -378,57 +455,77 @@ const layoutEnhancer = (next) => (options) => {
378
455
  reader.hookManager.register(`onViewportOffsetAdjust`, () => {
379
456
  let hasRedrawn = false;
380
457
  reader.spineItemsManager.items.forEach((item) => {
381
- const frame = item.frame.element;
458
+ var _a;
459
+ const frame = (_a = item.renderer.layers[0]) == null ? void 0 : _a.element;
382
460
  if (!hasRedrawn && frame) {
383
461
  void frame.getBoundingClientRect().left;
384
462
  hasRedrawn = true;
385
463
  }
386
464
  });
387
465
  });
388
- reader.hookManager.register(
389
- `item.onLayoutBeforeMeasurement`,
390
- ({ itemIndex, minimumWidth, isImageType }) => {
391
- const item = reader.spineItemsManager.get(itemIndex);
392
- const frame = item == null ? void 0 : item.frame;
393
- const { pageHorizontalMargin: pageHorizontalMargin2 = 0, pageVerticalMargin: pageVerticalMargin2 = 0 } = settingsManager.values;
394
- const pageSize = reader.context.getPageSize();
395
- if ((item == null ? void 0 : item.item.renditionLayout) === `reflowable` && (frame == null ? void 0 : frame.isReady) && !isImageType() && !frame.getViewportDimensions()) {
396
- let columnWidth = pageSize.width - pageHorizontalMargin2 * 2;
397
- const columnHeight = pageSize.height - pageVerticalMargin2 * 2;
398
- let width = pageSize.width - pageHorizontalMargin2 * 2;
399
- let columnGap = pageHorizontalMargin2 * 2;
400
- if (frame.isUsingVerticalWriting()) {
401
- width = minimumWidth - pageHorizontalMargin2 * 2;
402
- columnWidth = columnHeight;
403
- columnGap = pageVerticalMargin2 * 2;
404
- }
405
- frame == null ? void 0 : frame.removeStyle(`prose-layout-enhancer-css`);
406
- frame == null ? void 0 : frame.addStyle(
407
- `prose-layout-enhancer-css`,
408
- `
409
- body {
410
- width: ${width}px !important;
411
- margin: ${pageVerticalMargin2}px ${pageHorizontalMargin2}px !important;
412
- column-gap: ${columnGap}px !important;
413
- column-width: ${columnWidth}px !important;
414
- height: ${columnHeight}px !important;
415
- }
416
- img, video, audio, object, svg {
417
- max-width: ${columnWidth}px !important;
418
- max-height: ${columnHeight}px !important;
419
- }
420
- table {
421
- max-width: ${columnWidth}px !important;
422
- }
423
- td {
424
- max-width: ${columnWidth}px;
425
- }
426
- `
427
- );
466
+ reader.hookManager.register(`item.onBeforeLayout`, ({ item }) => {
467
+ const spineItem = reader.spineItemsManager.get(item.id);
468
+ const mimeType = item.mediaType ?? detectMimeTypeFromName(item.href);
469
+ const isImageType = !!(mimeType == null ? void 0 : mimeType.startsWith(`image/`));
470
+ const { pageHorizontalMargin: pageHorizontalMargin2 = 0, pageVerticalMargin: pageVerticalMargin2 = 0 } = settingsManager.values;
471
+ const pageSize = reader.context.getPageSize();
472
+ if ((spineItem == null ? void 0 : spineItem.item.renditionLayout) === `reflowable` && !isImageType) {
473
+ let columnWidth = pageSize.width - pageHorizontalMargin2 * 2;
474
+ const columnHeight = pageSize.height - pageVerticalMargin2 * 2;
475
+ let width = pageSize.width - pageHorizontalMargin2 * 2;
476
+ let columnGap = pageHorizontalMargin2 * 2;
477
+ if (spineItem.isUsingVerticalWriting()) {
478
+ width = pageSize.width - pageHorizontalMargin2 * 2;
479
+ columnWidth = columnHeight;
480
+ columnGap = pageVerticalMargin2 * 2;
428
481
  }
482
+ spineItem == null ? void 0 : spineItem.upsertCSS(
483
+ `prose-layout-enhancer-css`,
484
+ `
485
+ body {
486
+ width: ${width}px !important;
487
+ margin: ${pageVerticalMargin2}px ${pageHorizontalMargin2}px !important;
488
+ column-gap: ${columnGap}px !important;
489
+ column-width: ${columnWidth}px !important;
490
+ height: ${columnHeight}px !important;
491
+ }
492
+ img, video, audio, object, svg {
493
+ max-width: ${columnWidth}px !important;
494
+ max-height: ${columnHeight}px !important;
495
+ }
496
+ table {
497
+ max-width: ${columnWidth}px !important;
498
+ }
499
+ td {
500
+ max-width: ${columnWidth}px;
501
+ }
502
+ `
503
+ );
429
504
  }
430
- );
505
+ });
431
506
  fixReflowable(reader);
507
+ reader.hookManager.register(`item.onDocumentCreated`, ({ layers }) => {
508
+ layers.forEach(({ element }) => {
509
+ element.style.opacity = `0`;
510
+ element.style.transition = `opacity 300ms`;
511
+ });
512
+ });
513
+ reader.hookManager.register(`item.onBeforeLayout`, ({ item }) => {
514
+ const spineItem = reader.spineItemsManager.get(item.id);
515
+ spineItem == null ? void 0 : spineItem.renderer.layers.forEach(({ element }) => {
516
+ if (reader.settings.values.computedPageTurnMode !== `scrollable`) {
517
+ element.setAttribute(`tab-index`, `0`);
518
+ }
519
+ });
520
+ });
521
+ reader.hookManager.register(`item.onAfterLayout`, ({ item }) => {
522
+ const spineItem = reader.spineItemsManager.get(item.id);
523
+ if (spineItem == null ? void 0 : spineItem.isReady) {
524
+ spineItem == null ? void 0 : spineItem.renderer.layers.forEach(({ element }) => {
525
+ element.style.opacity = `1`;
526
+ });
527
+ }
528
+ });
432
529
  const observeContainerResize = (container) => new Observable((observer) => {
433
530
  const resizeObserver = new ResizeObserver(() => {
434
531
  observer.next();
@@ -573,7 +670,10 @@ const linksEnhancer = (next) => (options) => {
573
670
  }
574
671
  return false;
575
672
  };
576
- reader.hookManager.register(`item.onLoad`, ({ frame }) => {
673
+ reader.hookManager.register(`item.onDocumentLoad`, ({ layers }) => {
674
+ var _a;
675
+ const frame = (_a = layers[0]) == null ? void 0 : _a.element;
676
+ if (!(frame instanceof HTMLIFrameElement)) return;
577
677
  if (frame == null ? void 0 : frame.contentDocument) {
578
678
  Array.from(frame.contentDocument.querySelectorAll(`a`)).forEach(
579
679
  (element) => element.addEventListener(`click`, (e) => {
@@ -1404,16 +1504,14 @@ const themeEnhancer = (next) => (options) => {
1404
1504
  };
1405
1505
  const applyChangeToSpineItem = () => {
1406
1506
  reader.spineItemsManager.items.forEach((item) => {
1407
- var _a, _b, _c, _d;
1408
- (_b = (_a = item.frame).removeStyle) == null ? void 0 : _b.call(_a, `prose-reader-theme`);
1409
- (_d = (_c = item.frame).addStyle) == null ? void 0 : _d.call(_c, `prose-reader-theme`, getStyle());
1507
+ var _a;
1508
+ (_a = item.upsertCSS) == null ? void 0 : _a.call(item, `prose-reader-theme`, getStyle());
1410
1509
  applyChangeToSpineItemElement({ container: item.element });
1411
1510
  });
1412
1511
  };
1413
- reader.hookManager.register(`item.onLoad`, ({ itemId }) => {
1512
+ reader.hookManager.register(`item.onDocumentLoad`, ({ itemId }) => {
1414
1513
  const item = reader.spineItemsManager.get(itemId);
1415
- item == null ? void 0 : item.frame.removeStyle(`prose-reader-theme`);
1416
- item == null ? void 0 : item.frame.addStyle(`prose-reader-theme`, getStyle());
1514
+ item == null ? void 0 : item.upsertCSS(`prose-reader-theme`, getStyle());
1417
1515
  });
1418
1516
  reader.spineItemsManager.items$.pipe(
1419
1517
  tap(
@@ -2119,10 +2217,10 @@ const createSpineItemLocator = ({
2119
2217
  return void 0;
2120
2218
  };
2121
2219
  const getFirstNodeOrRangeAtPage = (pageIndex, spineItem) => {
2122
- var _a, _b;
2220
+ var _a, _b, _c;
2123
2221
  const pageSize = context.getPageSize();
2124
- const frame = (_a = spineItem.frame) == null ? void 0 : _a.element;
2125
- if (((_b = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _b.document) && // very important because it is being used by next functions
2222
+ const frame = (_b = (_a = spineItem.renderer) == null ? void 0 : _a.layers[0]) == null ? void 0 : _b.element;
2223
+ if (frame instanceof HTMLIFrameElement && ((_c = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _c.document) && // very important because it is being used by next functions
2126
2224
  frame.contentWindow.document.body !== null) {
2127
2225
  const { x: left, y: top } = getSpineItemPositionFromPageIndex({
2128
2226
  pageIndex,
@@ -3335,22 +3433,25 @@ const resolveCfi = ({
3335
3433
  if (!spineItem) return void 0;
3336
3434
  const { cleanedCfi, offset } = extractProseMetadataFromCfi(cfi);
3337
3435
  const cfiHandler = new CfiHandler(cleanedCfi, {});
3338
- const doc = (_b = (_a = spineItem.frame.element) == null ? void 0 : _a.contentWindow) == null ? void 0 : _b.document;
3339
- if (doc) {
3340
- try {
3341
- const { node, offset: resolvedOffset } = cfiHandler.resolve(doc, {});
3342
- return {
3343
- node,
3344
- offset: offset ?? resolvedOffset,
3345
- spineItemIndex,
3346
- spineItem
3347
- };
3348
- } catch (e) {
3349
- Report.error(e);
3350
- return {
3351
- spineItemIndex,
3352
- spineItem
3353
- };
3436
+ const rendererElement = (_a = spineItem.renderer.layers[0]) == null ? void 0 : _a.element;
3437
+ if (rendererElement instanceof HTMLIFrameElement) {
3438
+ const doc = (_b = rendererElement.contentWindow) == null ? void 0 : _b.document;
3439
+ if (doc) {
3440
+ try {
3441
+ const { node, offset: resolvedOffset } = cfiHandler.resolve(doc, {});
3442
+ return {
3443
+ node,
3444
+ offset: offset ?? resolvedOffset,
3445
+ spineItemIndex,
3446
+ spineItem
3447
+ };
3448
+ } catch (e) {
3449
+ Report.error(e);
3450
+ return {
3451
+ spineItemIndex,
3452
+ spineItem
3453
+ };
3454
+ }
3354
3455
  }
3355
3456
  }
3356
3457
  return {
@@ -3551,7 +3652,8 @@ class ViewportNavigator extends DestroyableClass {
3551
3652
  if (settings.values.computedPageTurnMode === `scrollable`) {
3552
3653
  element.style.removeProperty(`transform`);
3553
3654
  element.style.removeProperty(`transition`);
3554
- element.style.overflow = `scroll`;
3655
+ element.style.overflowY = `scroll`;
3656
+ element.style.overflowX = `hidden`;
3555
3657
  } else {
3556
3658
  element.style.removeProperty(`overflow`);
3557
3659
  element.style.removeProperty(`overflowY`);
@@ -4703,8 +4805,9 @@ const createNavigator = ({
4703
4805
  getElement: () => element$.getValue()
4704
4806
  };
4705
4807
  };
4706
- class SettingsManager3 {
4808
+ class SettingsManager3 extends DestroyableClass {
4707
4809
  constructor(initialSettings) {
4810
+ super();
4708
4811
  const settingsWithDefaults = {
4709
4812
  ...this.getDefaultSettings(),
4710
4813
  ...initialSettings
@@ -4750,6 +4853,7 @@ class SettingsManager3 {
4750
4853
  return this._settings$;
4751
4854
  }
4752
4855
  destroy() {
4856
+ super.destroy();
4753
4857
  this.outputSettingsUpdateSubject.complete();
4754
4858
  }
4755
4859
  }
@@ -4903,7 +5007,7 @@ class HookManager {
4903
5007
  const getItemAnchor = (spineItem) => `|[prose~anchor~${encodeURIComponent(spineItem.item.id)}]`;
4904
5008
  const getRootCfi = (spineItem) => {
4905
5009
  const itemAnchor = getItemAnchor(spineItem);
4906
- return `epubcfi(/0${itemAnchor}) `;
5010
+ return `epubcfi(/0${itemAnchor})`;
4907
5011
  };
4908
5012
  const generateCfiForSpineItemPage = Report.measurePerformance(
4909
5013
  `getCfi`,
@@ -4918,16 +5022,16 @@ const generateCfiForSpineItemPage = Report.measurePerformance(
4918
5022
  pageIndex,
4919
5023
  spineItem
4920
5024
  );
4921
- const doc = (_b = (_a = spineItem.frame.element) == null ? void 0 : _a.contentWindow) == null ? void 0 : _b.document;
5025
+ const rendererElement = (_a = spineItem.renderer.layers[0]) == null ? void 0 : _a.element;
4922
5026
  const itemAnchor = getItemAnchor(spineItem);
4923
5027
  const offset = `|[prose~offset~${(nodeOrRange == null ? void 0 : nodeOrRange.offset) || 0}]`;
4924
- if (nodeOrRange && doc) {
5028
+ if (nodeOrRange && rendererElement instanceof HTMLIFrameElement && ((_b = rendererElement.contentWindow) == null ? void 0 : _b.document)) {
4925
5029
  const cfiString = CfiHandler.generate(
4926
5030
  nodeOrRange.node,
4927
5031
  0,
4928
5032
  `${itemAnchor}${offset}`
4929
5033
  );
4930
- return cfiString;
5034
+ return cfiString.trim();
4931
5035
  }
4932
5036
  return getRootCfi(spineItem);
4933
5037
  }
@@ -5116,1679 +5220,368 @@ class SpineItemsManager extends DestroyableClass {
5116
5220
  this.orderedSpineItemsSubject.value.forEach((item) => item.destroy());
5117
5221
  }
5118
5222
  }
5119
- const getAttributeValueFromString = (string, key) => {
5120
- const regExp = new RegExp(key + `\\s*=\\s*([0-9.]+)`, `i`);
5121
- const match = string.match(regExp) || [];
5122
- const firstMatch = match[1] || `0`;
5123
- return match && parseFloat(firstMatch) || 0;
5124
- };
5125
- const getIntrinsicDimensionsFromBase64Img = (data) => new Promise((resolve, reject) => {
5126
- const image = new Image();
5127
- image.src = data;
5128
- image.onload = () => {
5129
- resolve({ height: image.naturalHeight, width: image.naturalWidth });
5130
- };
5131
- image.onerror = reject;
5132
- });
5133
- const createHtmlPageFromResource = async (resourceResponse, item) => {
5134
- if (typeof resourceResponse === `string`) return resourceResponse;
5135
- const contentType = parseContentType(resourceResponse.headers.get(`Content-Type`) || ``) || detectMimeTypeFromName(item.href);
5136
- if ([`image/jpg`, `image/jpeg`, `image/png`, `image/webp`].some(
5137
- (mime) => mime === contentType
5138
- )) {
5139
- const data = await getBase64FromBlob(await resourceResponse.blob());
5140
- const { height, width } = await getIntrinsicDimensionsFromBase64Img(data);
5141
- return `
5142
- <html>
5143
- <head>
5144
- ${item.renditionLayout !== `reflowable` ? `<meta name="viewport" content="width=${width}, height=${height}">` : ``}
5145
- </head>
5146
- <body style="margin: 0px;" tab-index="-1;">
5147
- <img
5148
- src="${data}"
5149
- style="max-width: 100%;height:100%;object-fit:contain;"
5150
- >
5151
- </body>
5152
- </html>
5153
- `;
5154
- }
5155
- if ([`text/plain`].some((mime) => mime === contentType)) {
5156
- const data = await resourceResponse.text();
5157
- return `
5158
- <!DOCTYPE html>
5159
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
5160
- <head>
5161
- <style>
5162
- pre {
5163
- white-space: pre;
5164
- white-space: pre-wrap;
5165
- word-wrap: break-word;
5166
- }
5167
- </style>
5168
- </head>
5169
- <body>
5170
- <pre>${data}</pre>
5171
- </body>
5172
- </html>
5173
- `;
5174
- }
5175
- const content = await resourceResponse.text();
5176
- return content;
5177
- };
5178
- const attachFrameSrc = ({
5179
- settings,
5180
- item
5223
+ const getSpineItemFromPosition = ({
5224
+ position,
5225
+ spineItemsManager,
5226
+ spineLayout,
5227
+ settings
5181
5228
  }) => {
5182
- const getHtmlFromResource = (response) => createHtmlPageFromResource(response, item);
5183
- return (stream) => stream.pipe(
5184
- switchMap((frameElement) => {
5185
- const { fetchResource } = settings.values;
5186
- if (!fetchResource && item.href.startsWith(window.location.origin) && // we have an encoding and it's a valid html
5187
- (item.mediaType && [
5188
- `application/xhtml+xml`,
5189
- `application/xml`,
5190
- `text/html`,
5191
- `text/xml`
5192
- ].includes(item.mediaType) || // no encoding ? then try to detect html
5193
- !item.mediaType && ITEM_EXTENSION_VALID_FOR_FRAME_SRC.some(
5194
- (extension) => item.href.endsWith(extension)
5195
- ))) {
5196
- frameElement == null ? void 0 : frameElement.setAttribute(`src`, item.href);
5197
- return of(frameElement);
5198
- } else {
5199
- const fetchFn = fetchResource || (() => fetch(item.href));
5200
- return from(fetchFn(item)).pipe(
5201
- mergeMap((response) => getHtmlFromResource(response)),
5202
- tap$1((htmlDoc) => {
5203
- if (htmlDoc) {
5204
- const blob = new Blob([htmlDoc], { type: "text/html" });
5205
- const blobURL = URL.createObjectURL(blob);
5206
- frameElement == null ? void 0 : frameElement.setAttribute(`src`, blobURL);
5207
- }
5208
- }),
5209
- map$1(() => frameElement),
5210
- catchError((e) => {
5211
- Report.error(
5212
- `Error while trying to fetch or load resource for item ${item.id}`
5213
- );
5214
- console.error(e);
5215
- return of(frameElement);
5216
- })
5217
- );
5218
- }
5219
- })
5220
- );
5229
+ const spineItem = spineItemsManager.items.find((item) => {
5230
+ const { left, right, bottom, top } = spineLayout.getAbsolutePositionOf(item);
5231
+ const isWithinXAxis = position.x >= left && position.x < right;
5232
+ if (settings.values.computedPageTurnDirection === `horizontal`) {
5233
+ return isWithinXAxis;
5234
+ } else {
5235
+ return isWithinXAxis && position.y >= top && position.y < bottom;
5236
+ }
5237
+ });
5238
+ if (position.x === 0 && !spineItem) {
5239
+ return spineItemsManager.items[0];
5240
+ }
5241
+ return spineItem;
5221
5242
  };
5222
- const configureFrame = ({
5223
- settings,
5224
- item,
5225
- hookManager
5243
+ const isItemVisibleByThresholdForPosition = ({
5244
+ itemHeight,
5245
+ itemWidth,
5246
+ visibleWidthOfItem,
5247
+ visibleHeightOfItem,
5248
+ threshold
5226
5249
  }) => {
5227
- return (stream) => stream.pipe(
5228
- // We don't need sandbox since we are actually already allowing too much to the iframe
5229
- // frame.setAttribute(`sandbox`, `allow-same-origin allow-scripts`)
5230
- mergeMap((frame) => {
5231
- var _a;
5232
- const body = (_a = frame.contentDocument) == null ? void 0 : _a.body;
5233
- if (!body) {
5234
- Report.error(`Something went wrong on iframe load ${item.id}`);
5235
- return EMPTY;
5236
- }
5237
- frame.setAttribute(`role`, `main`);
5238
- if ((frame == null ? void 0 : frame.contentDocument) && body) ;
5239
- if (settings.values.computedPageTurnMode !== `scrollable`) {
5240
- frame.setAttribute(`tab-index`, `0`);
5241
- }
5242
- const hookResults = hookManager.execute(`item.onLoad`, item.id, {
5243
- itemId: item.id,
5244
- frame
5245
- }).filter(
5246
- (result) => result instanceof Observable
5247
- );
5248
- return combineLatest([of(null), ...hookResults]).pipe(map$1(() => frame));
5249
- })
5250
- );
5250
+ const visibleWidthRatioOfSpineItem = visibleWidthOfItem / itemWidth;
5251
+ const visibleHeightRatioOfSpineItem = visibleHeightOfItem / itemHeight;
5252
+ const isItemVisibleEnough = visibleWidthRatioOfSpineItem >= threshold && visibleHeightRatioOfSpineItem >= threshold;
5253
+ return isItemVisibleEnough;
5251
5254
  };
5252
- const createFrameElement = Report.measurePerformance(
5253
- `SpineItemFrame createFrame`,
5254
- Infinity,
5255
- () => {
5256
- const frame = document.createElement(`iframe`);
5257
- frame.frameBorder = `no`;
5258
- frame.tabIndex = 0;
5259
- frame.setAttribute(
5260
- `sandbox`,
5261
- `
5262
- allow-same-origin
5263
- allow-scripts
5264
- allow-top-navigation-to-custom-protocols
5265
- `
5266
- );
5267
- frame.scrolling = `no`;
5268
- frame.style.cssText = `
5269
- visibility: hidden;
5270
- overflow: hidden;
5271
- background-color: transparent;
5272
- border: 0px none transparent;
5273
- padding: 0px;
5274
- transition: opacity 300ms;
5275
- opacity: 0;
5276
- `;
5277
- return frame;
5278
- }
5279
- );
5280
- const waitForFrameLoad = (stream) => stream.pipe(
5281
- switchMap(
5282
- (frame) => fromEvent(frame, `load`).pipe(
5283
- take$1(1),
5284
- map$1(() => frame)
5285
- )
5286
- )
5287
- );
5288
- const loadFrame = ({
5289
- settings,
5290
- item,
5291
- hookManager,
5292
- element,
5293
- onFrameElement,
5255
+ const isItemVisibleOnScreenByThresholdForPosition = ({
5256
+ visibleWidthOfItem,
5257
+ visibleHeightOfItem,
5258
+ threshold,
5294
5259
  context
5295
5260
  }) => {
5296
- const frameElement = createFrameElement();
5297
- return of(frameElement).pipe(
5298
- tap$1((frame) => {
5299
- element.appendChild(frame);
5300
- onFrameElement(frame);
5301
- }),
5302
- attachFrameSrc({ settings, item }),
5303
- waitForSwitch(context.bridgeEvent.viewportFree$),
5304
- waitForFrameLoad,
5305
- waitForSwitch(context.bridgeEvent.viewportFree$),
5306
- configureFrame({
5307
- item,
5308
- settings,
5309
- hookManager
5310
- })
5311
- );
5261
+ const widthRatioOfSpaceTakenInScreen = visibleWidthOfItem / context.state.visibleAreaRect.width;
5262
+ const heightRatioOfSpaceTakenInScreen = visibleHeightOfItem / context.state.visibleAreaRect.height;
5263
+ const isItemVisibleEnoughOnScreen = heightRatioOfSpaceTakenInScreen >= threshold && widthRatioOfSpaceTakenInScreen >= threshold;
5264
+ return isItemVisibleEnoughOnScreen;
5312
5265
  };
5313
- const unloadFrame = ({
5314
- item,
5315
- hookManager,
5316
- frameElement,
5266
+ const getItemVisibilityForPosition = ({
5267
+ itemPosition: {
5268
+ bottom,
5269
+ left,
5270
+ right,
5271
+ top,
5272
+ width: itemWidth,
5273
+ height: itemHeight
5274
+ },
5275
+ threshold,
5276
+ viewportPosition,
5277
+ restrictToScreen,
5317
5278
  context
5318
5279
  }) => {
5319
- return of(null).pipe(
5320
- waitForSwitch(context.bridgeEvent.viewportFree$),
5321
- tap$1(() => {
5322
- hookManager.destroy(`item.onLoad`, item.id);
5323
- frameElement == null ? void 0 : frameElement.remove();
5324
- })
5280
+ const viewportLeft = viewportPosition.x;
5281
+ const viewportRight = viewportPosition.x + (context.state.visibleAreaRect.width - 1);
5282
+ const viewportTop = viewportPosition.y;
5283
+ const viewportBottom = Math.max(
5284
+ viewportPosition.y + (context.state.visibleAreaRect.height - 1),
5285
+ 0
5286
+ );
5287
+ const visibleWidthOfItem = Math.max(
5288
+ 0,
5289
+ Math.min(right, viewportRight) - Math.max(left, viewportLeft)
5325
5290
  );
5291
+ const visibleHeightOfItem = Math.max(
5292
+ 0,
5293
+ Math.min(bottom, viewportBottom) - Math.max(top, viewportTop)
5294
+ );
5295
+ const itemIsOnTheOuterEdge = visibleWidthOfItem <= 0 || visibleHeightOfItem <= 0;
5296
+ if (itemIsOnTheOuterEdge) return { visible: false };
5297
+ const isItemVisibleEnoughOnScreen = isItemVisibleOnScreenByThresholdForPosition({
5298
+ threshold,
5299
+ visibleHeightOfItem,
5300
+ visibleWidthOfItem,
5301
+ context
5302
+ });
5303
+ if (restrictToScreen) {
5304
+ return { visible: isItemVisibleEnoughOnScreen };
5305
+ }
5306
+ const isItemVisibleEnough = isItemVisibleByThresholdForPosition({
5307
+ itemHeight,
5308
+ itemWidth,
5309
+ threshold,
5310
+ visibleHeightOfItem,
5311
+ visibleWidthOfItem
5312
+ });
5313
+ return {
5314
+ visible: isItemVisibleEnough || isItemVisibleEnoughOnScreen
5315
+ };
5326
5316
  };
5327
- const waitForFrameReady = (stream) => stream.pipe(
5328
- switchMap(
5329
- (frame) => {
5330
- var _a;
5331
- return from(((_a = frame == null ? void 0 : frame.contentDocument) == null ? void 0 : _a.fonts.ready) || of(void 0));
5332
- }
5333
- )
5334
- );
5335
- const createLoader = ({
5336
- item,
5337
- parent,
5317
+ const getVisibleSpineItemsFromPosition = ({
5318
+ position,
5319
+ threshold,
5320
+ restrictToScreen,
5321
+ spineItemsManager,
5338
5322
  context,
5339
5323
  settings,
5340
- hookManager
5324
+ spineLayout
5341
5325
  }) => {
5342
- const destroySubject$ = new Subject();
5343
- const stateSubject = new BehaviorSubject("idle");
5344
- const loadSubject = new Subject();
5345
- const unloadSubject = new Subject();
5346
- const frameElementSubject = new BehaviorSubject(void 0);
5347
- const unloadFrame$ = unloadSubject.pipe(
5348
- withLatestFrom$1(stateSubject),
5349
- filter(([, state]) => state !== "unloading" && state !== "idle"),
5350
- exhaustMap$1(
5351
- () => context.bridgeEvent.viewportFree$.pipe(
5352
- first(),
5353
- switchMap$1(
5354
- () => unloadFrame({
5355
- hookManager,
5356
- item,
5357
- frameElement: frameElementSubject.getValue(),
5358
- context
5359
- })
5360
- ),
5361
- tap(() => {
5362
- frameElementSubject.next(void 0);
5363
- }),
5364
- ignoreElements(),
5365
- startWith("loading"),
5366
- endWith("success"),
5367
- defaultIfEmpty("idle")
5368
- )
5369
- ),
5370
- startWith("idle"),
5371
- distinctUntilChanged(),
5372
- share$1()
5373
- );
5374
- const unloaded$ = unloadFrame$.pipe(filter((state) => state === "success"));
5375
- const unloading$ = unloadFrame$.pipe(filter((state) => state === "loading"));
5376
- const loadFrame$ = loadSubject.pipe(
5377
- exhaustMap$1(() => {
5378
- const preventFurtherLoad$ = NEVER;
5379
- return merge(
5380
- preventFurtherLoad$,
5381
- context.bridgeEvent.viewportFree$.pipe(
5382
- first(),
5383
- switchMap$1(
5384
- () => loadFrame({
5385
- element: parent,
5386
- hookManager,
5387
- item,
5388
- onFrameElement: (element) => {
5389
- frameElementSubject.next(element);
5390
- },
5391
- settings,
5392
- context
5393
- })
5394
- ),
5395
- map((frame) => ({ state: "success", frame })),
5396
- startWith({ state: "loading" }),
5397
- defaultIfEmpty({ state: "idle" })
5398
- )
5399
- ).pipe(takeUntil$1(unloaded$));
5400
- }),
5401
- startWith({ state: "idle" }),
5402
- share$1()
5403
- );
5404
- const loading$ = loadFrame$.pipe(filter(({ state }) => state === "loading"));
5405
- const loaded$ = loadFrame$.pipe(
5406
- filter((state) => state.state === "success"),
5407
- map(({ frame }) => frame)
5408
- );
5409
- const frameIsReady$ = loaded$.pipe(
5410
- switchMap$1(
5411
- (frame) => of(frame).pipe(waitForFrameReady, takeUntil$1(unloadSubject))
5412
- ),
5413
- share$1()
5414
- );
5415
- const ready$ = frameIsReady$;
5416
- const state$ = merge(
5417
- unloaded$.pipe(map(() => "idle")),
5418
- unloading$.pipe(map(() => "unloading")),
5419
- loaded$.pipe(map(() => "loaded")),
5420
- loading$.pipe(map(() => "loading")),
5421
- ready$.pipe(map(() => "ready"))
5422
- ).pipe(
5423
- startWith("idle"),
5424
- tap((state) => stateSubject.next(state)),
5425
- shareReplay(1)
5326
+ const fallbackSpineItem = getSpineItemFromPosition({
5327
+ position,
5328
+ settings,
5329
+ spineItemsManager,
5330
+ spineLayout
5331
+ }) || spineItemsManager.get(0);
5332
+ const spineItemsVisible = spineItemsManager.items.reduce(
5333
+ (acc, spineItem) => {
5334
+ const itemPosition = spineLayout.getAbsolutePositionOf(spineItem);
5335
+ const { visible } = getItemVisibilityForPosition({
5336
+ itemPosition,
5337
+ threshold,
5338
+ viewportPosition: position,
5339
+ restrictToScreen,
5340
+ context
5341
+ });
5342
+ if (visible) {
5343
+ return [...acc, spineItem];
5344
+ }
5345
+ return acc;
5346
+ },
5347
+ []
5426
5348
  );
5427
- const isReady$ = state$.pipe(map((state) => state === "ready"));
5428
- state$.pipe(takeUntil$1(destroySubject$)).subscribe();
5349
+ const beginItem = spineItemsVisible[0] ?? fallbackSpineItem;
5350
+ const endItem = spineItemsVisible[spineItemsVisible.length - 1] ?? beginItem;
5351
+ if (!beginItem || !endItem) return void 0;
5352
+ const beginItemIndex = spineItemsManager.getSpineItemIndex(beginItem);
5353
+ const endItemIndex = spineItemsManager.getSpineItemIndex(endItem);
5429
5354
  return {
5430
- load: () => loadSubject.next(),
5431
- unload: () => unloadSubject.next(),
5432
- destroy: () => {
5433
- unloadSubject.next();
5434
- unloadSubject.complete();
5435
- loadSubject.complete();
5436
- frameElementSubject.complete();
5437
- destroySubject$.next();
5438
- destroySubject$.complete();
5439
- stateSubject.complete();
5440
- },
5441
- get state() {
5442
- return stateSubject.getValue();
5443
- },
5444
- get element() {
5445
- return frameElementSubject.getValue();
5446
- },
5447
- isReady$,
5448
- ready$,
5449
- loaded$,
5450
- unloaded$,
5451
- element$: frameElementSubject.asObservable()
5355
+ beginIndex: beginItemIndex ?? 0,
5356
+ endIndex: endItemIndex ?? 0
5452
5357
  };
5453
5358
  };
5454
- class FrameItem extends DestroyableClass {
5455
- constructor(parent, item, context, settings, hookManager) {
5456
- super();
5457
- this.parent = parent;
5458
- this.item = item;
5459
- this.context = context;
5460
- this.settings = settings;
5461
- this.hookManager = hookManager;
5462
- this.getViewportDimensions = () => {
5463
- const frame = this.loader.element;
5464
- if (frame && (frame == null ? void 0 : frame.contentDocument)) {
5465
- const doc = frame.contentDocument;
5466
- const viewPortMeta = doc.querySelector(`meta[name='viewport']`);
5467
- if (viewPortMeta) {
5468
- const viewPortMetaInfos = viewPortMeta.getAttribute(`content`);
5469
- if (viewPortMetaInfos) {
5470
- const width = getAttributeValueFromString(viewPortMetaInfos, `width`);
5471
- const height = getAttributeValueFromString(
5472
- viewPortMetaInfos,
5473
- `height`
5474
- );
5475
- if (width > 0 && height > 0) {
5476
- return {
5477
- width,
5478
- height
5479
- };
5480
- } else {
5481
- return void 0;
5482
- }
5483
- }
5484
- }
5485
- }
5486
- return void 0;
5487
- };
5488
- this.getWritingMode = () => {
5489
- var _a;
5490
- return (_a = this.getComputedStyleAfterLoad()) == null ? void 0 : _a.writingMode;
5491
- };
5492
- this.isUsingVerticalWriting = () => {
5493
- var _a;
5494
- return !!((_a = this.getWritingMode()) == null ? void 0 : _a.startsWith(`vertical`));
5495
- };
5496
- this.getHtmlFromResource = (response) => {
5497
- return createHtmlPageFromResource(response, this.item);
5498
- };
5499
- this.staticLayout = (size) => {
5500
- const frame = this.loader.element;
5501
- if (frame) {
5502
- frame.style.width = `${size.width}px`;
5503
- frame.style.height = `${size.height}px`;
5504
- if (this.settings.values.computedPageTurnMode !== `scrollable`) {
5505
- frame.setAttribute(`tab-index`, `0`);
5506
- }
5507
- }
5508
- };
5509
- this.destroy = () => {
5510
- super.destroy();
5511
- this.loader.destroy();
5512
- };
5513
- this.loader = createLoader({
5514
- context,
5515
- hookManager,
5516
- item,
5517
- parent,
5518
- settings
5519
- });
5520
- this.contentLayoutChange$ = merge(
5521
- this.loader.unloaded$.pipe(map(() => ({ isFirstLayout: false }))),
5522
- this.ready$.pipe(map(() => ({ isFirstLayout: true })))
5523
- );
5524
- }
5525
- // @todo optimize
5526
- getComputedStyleAfterLoad() {
5527
- var _a, _b;
5528
- const frame = this.loader.element;
5529
- const body = (_a = frame == null ? void 0 : frame.contentDocument) == null ? void 0 : _a.body;
5530
- if (body) {
5531
- return (_b = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _b.getComputedStyle(body);
5532
- }
5533
- }
5534
- get element() {
5535
- return this.loader.element;
5536
- }
5537
- get unloaded$() {
5538
- return this.loader.unloaded$;
5539
- }
5540
- get loaded$() {
5541
- return this.loader.loaded$;
5542
- }
5543
- get ready$() {
5544
- return this.loader.ready$;
5545
- }
5546
- get isReady$() {
5547
- return this.loader.isReady$;
5548
- }
5549
- get isLoaded() {
5550
- return this.loader.state === "loaded" || this.loader.state === "ready";
5551
- }
5552
- get isReady() {
5553
- return this.loader.state === "ready";
5554
- }
5555
- load() {
5556
- this.loader.load();
5557
- }
5558
- unload() {
5559
- this.loader.unload();
5560
- }
5561
- addStyle(id, style, prepend) {
5562
- const frameElement = this.loader.element;
5563
- if (frameElement && frameElement.contentDocument && frameElement.contentDocument.head) {
5564
- const userStyle = document.createElement(`style`);
5565
- userStyle.id = id;
5566
- userStyle.innerHTML = style;
5567
- if (prepend) {
5568
- frameElement.contentDocument.head.prepend(userStyle);
5569
- } else {
5570
- frameElement.contentDocument.head.appendChild(userStyle);
5571
- }
5572
- }
5573
- }
5574
- removeStyle(id) {
5575
- const frameElement = this.loader.element;
5576
- if (frameElement && frameElement.contentDocument && frameElement.contentDocument.head) {
5577
- const styleElement = frameElement.contentDocument.getElementById(id);
5578
- if (styleElement) {
5579
- styleElement.remove();
5580
- }
5581
- }
5582
- }
5583
- }
5584
- const createFingerTracker = () => {
5585
- const fingerPositionInIframe = { x: void 0, y: void 0 };
5586
- const subject = new Subject();
5587
- let isMouseDown = false;
5588
- const track = (frame) => {
5589
- var _a, _b, _c;
5590
- fingerPositionInIframe.x = void 0;
5591
- fingerPositionInIframe.y = void 0;
5592
- (_a = frame.contentDocument) == null ? void 0 : _a.addEventListener(`mousedown`, (e) => {
5593
- isMouseDown = true;
5594
- fingerPositionInIframe.x = e.x;
5595
- fingerPositionInIframe.y = e.y;
5596
- subject.next({ event: `fingermove`, data: { x: e.x, y: e.y } });
5597
- });
5598
- (_b = frame.contentDocument) == null ? void 0 : _b.addEventListener(`mouseup`, () => {
5599
- isMouseDown = false;
5600
- fingerPositionInIframe.x = void 0;
5601
- fingerPositionInIframe.y = void 0;
5602
- subject.next({ event: `fingerout`, data: void 0 });
5603
- });
5604
- (_c = frame.contentDocument) == null ? void 0 : _c.addEventListener(`mousemove`, (e) => {
5605
- if (isMouseDown) {
5606
- subject.next({ event: `fingermove`, data: { x: e.x, y: e.y } });
5607
- }
5608
- });
5609
- };
5359
+ const getSpinePositionFromSpineItemPosition = ({
5360
+ spineItemPosition,
5361
+ itemLayout: { left, top }
5362
+ }) => {
5610
5363
  return {
5611
- track,
5612
- getFingerPositionInIframe() {
5613
- return fingerPositionInIframe.x === void 0 || fingerPositionInIframe.y === void 0 ? void 0 : fingerPositionInIframe;
5614
- },
5615
- destroy: () => {
5616
- },
5617
- $: subject.asObservable()
5364
+ x: left + spineItemPosition.x,
5365
+ y: top + spineItemPosition.y
5618
5366
  };
5619
5367
  };
5620
- const createSelectionTracker = () => {
5621
- let isSelecting = false;
5622
- let frame;
5623
- const subject = new Subject();
5624
- const mouseUpEvents = [`mouseup`, `pointerup`];
5625
- const track = (frameToTrack) => {
5626
- var _a, _b;
5627
- frame = frameToTrack;
5628
- mouseUpEvents.forEach((eventName) => {
5629
- var _a2;
5630
- (_a2 = frameToTrack.contentWindow) == null ? void 0 : _a2.addEventListener(eventName, () => {
5631
- isSelecting = false;
5368
+ const getSpineInfoFromAbsolutePageIndex = ({
5369
+ absolutePageIndex,
5370
+ spineLayout,
5371
+ spineItemsManager,
5372
+ context,
5373
+ settings
5374
+ }) => {
5375
+ const items = spineItemsManager.items;
5376
+ const { found, currentAbsolutePage } = items.reduce(
5377
+ (acc, item) => {
5378
+ if (acc.found) return acc;
5379
+ const itemLayout = spineLayout.getAbsolutePositionOf(item);
5380
+ const numberOfPages = getSpineItemNumberOfPages({
5381
+ isUsingVerticalWriting: !!item.isUsingVerticalWriting(),
5382
+ itemHeight: itemLayout.height,
5383
+ itemWidth: itemLayout.width,
5384
+ context,
5385
+ settings
5632
5386
  });
5633
- });
5634
- (_a = frameToTrack.contentDocument) == null ? void 0 : _a.addEventListener(`selectionchange`, () => {
5635
- var _a2;
5636
- subject.next({
5637
- event: `selectionchange`,
5638
- data: ((_a2 = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _a2.getSelection()) || null
5387
+ const possiblePageIndex = absolutePageIndex - acc.currentAbsolutePage;
5388
+ const currentAbsolutePage2 = acc.currentAbsolutePage + numberOfPages;
5389
+ if (possiblePageIndex <= numberOfPages - 1) {
5390
+ return {
5391
+ ...acc,
5392
+ currentAbsolutePage: currentAbsolutePage2,
5393
+ found: { item, pageIndex: possiblePageIndex }
5394
+ };
5395
+ }
5396
+ return {
5397
+ ...acc,
5398
+ currentAbsolutePage: currentAbsolutePage2
5399
+ };
5400
+ },
5401
+ { currentAbsolutePage: 0 }
5402
+ );
5403
+ if (found) {
5404
+ return {
5405
+ spineItem: found.item,
5406
+ pageIndex: found.pageIndex,
5407
+ itemIndex: spineItemsManager.getSpineItemIndex(found.item) ?? 0,
5408
+ currentAbsolutePage
5409
+ };
5410
+ }
5411
+ return void 0;
5412
+ };
5413
+ const getAbsolutePageIndexFromPageIndex = ({
5414
+ pageIndex,
5415
+ spineItemOrId,
5416
+ spineLayout,
5417
+ spineItemsManager,
5418
+ context,
5419
+ settings
5420
+ }) => {
5421
+ const items = spineItemsManager.items;
5422
+ const spineItem = spineItemsManager.get(spineItemOrId);
5423
+ if (!spineItem) return void 0;
5424
+ const { currentAbsolutePage } = items.reduce(
5425
+ (acc, item) => {
5426
+ if (acc.found) return acc;
5427
+ const itemLayout = spineLayout.getAbsolutePositionOf(item);
5428
+ const numberOfPages = getSpineItemNumberOfPages({
5429
+ isUsingVerticalWriting: !!item.isUsingVerticalWriting(),
5430
+ itemHeight: itemLayout.height,
5431
+ itemWidth: itemLayout.width,
5432
+ context,
5433
+ settings
5639
5434
  });
5640
- });
5641
- (_b = frameToTrack.contentWindow) == null ? void 0 : _b.addEventListener(`selectstart`, () => {
5642
- isSelecting = true;
5643
- });
5644
- };
5645
- const destroy = () => {
5646
- };
5647
- return {
5648
- track,
5649
- destroy,
5650
- isSelecting: () => isSelecting,
5651
- getSelection: () => {
5652
- var _a;
5653
- const selection = (_a = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _a.getSelection();
5654
- if (!(selection == null ? void 0 : selection.anchorNode) || selection.type === `None` || selection.type === `Caret`)
5655
- return void 0;
5656
- return selection;
5435
+ if (spineItem === item) {
5436
+ if (pageIndex <= numberOfPages - 1) {
5437
+ return {
5438
+ currentAbsolutePage: acc.currentAbsolutePage + pageIndex,
5439
+ found: true
5440
+ };
5441
+ }
5442
+ }
5443
+ return {
5444
+ ...acc,
5445
+ currentAbsolutePage: acc.currentAbsolutePage + numberOfPages
5446
+ };
5657
5447
  },
5658
- $: subject.asObservable()
5659
- };
5448
+ { currentAbsolutePage: 0, found: false }
5449
+ );
5450
+ return currentAbsolutePage;
5660
5451
  };
5661
- const createCommonSpineItem = ({
5662
- item,
5452
+ const createSpineLocator = ({
5453
+ spineItemsManager,
5663
5454
  context,
5664
- parentElement,
5455
+ spineItemLocator,
5665
5456
  settings,
5666
- hookManager,
5667
- index
5457
+ spineLayout
5668
5458
  }) => {
5669
- const destroySubject$ = new Subject();
5670
- const containerElement = createContainerElement(
5671
- parentElement,
5672
- item,
5673
- hookManager
5674
- );
5675
- const overlayElement = createOverlayElement(parentElement, item);
5676
- const fingerTracker = createFingerTracker();
5677
- const selectionTracker = createSelectionTracker();
5678
- const frameItem = new FrameItem(
5679
- containerElement,
5680
- item,
5681
- context,
5682
- settings,
5683
- hookManager
5459
+ const getSpineItemPositionFromSpinePosition = Report.measurePerformance(
5460
+ `getSpineItemPositionFromSpinePosition`,
5461
+ 10,
5462
+ (position, spineItem) => {
5463
+ const { left, top } = spineLayout.getAbsolutePositionOf(spineItem);
5464
+ return {
5465
+ /**
5466
+ * when using spread the item could be on the right and therefore will be negative
5467
+ * @example
5468
+ * 400 (position = viewport), page of 200
5469
+ * 400 - 600 = -200.
5470
+ * However we can assume we are at 0, because we in fact can see the beginning of the item
5471
+ */
5472
+ x: Math.max(position.x - left, 0),
5473
+ y: Math.max(position.y - top, 0)
5474
+ };
5475
+ },
5476
+ { disable: true }
5684
5477
  );
5685
- containerElement.appendChild(overlayElement);
5686
- parentElement.appendChild(containerElement);
5687
- const getElementDimensions = () => {
5688
- const rect = containerElement.getBoundingClientRect();
5689
- const normalizedValues = {
5690
- ...rect,
5691
- // we want to round to first decimal because it's possible to have half pixel
5692
- // however browser engine can also gives back x.yyyy based on their precision
5693
- width: Math.round(rect.width * 10) / 10,
5694
- height: Math.round(rect.height * 10) / 10
5695
- };
5696
- return normalizedValues;
5697
- };
5698
- const isImageType = () => {
5699
- const mimeType = item.mediaType ?? detectMimeTypeFromName(item.href);
5700
- return !!(mimeType == null ? void 0 : mimeType.startsWith(`image/`));
5701
- };
5702
- const injectStyle = (cssText) => {
5703
- frameItem.removeStyle(`prose-reader-css`);
5704
- frameItem.addStyle(`prose-reader-css`, cssText);
5705
- };
5706
- const adjustPositionOfElement = ({
5707
- right,
5708
- left,
5709
- top
5710
- }) => {
5711
- if (right !== void 0) {
5712
- containerElement.style.right = `${right}px`;
5713
- } else {
5714
- containerElement.style.removeProperty(`right`);
5715
- }
5716
- if (left !== void 0) {
5717
- containerElement.style.left = `${left}px`;
5718
- } else {
5719
- containerElement.style.removeProperty(`left`);
5720
- }
5721
- if (top !== void 0) {
5722
- containerElement.style.top = `${top}px`;
5723
- } else {
5724
- containerElement.style.removeProperty(`top`);
5725
- }
5726
- };
5727
- const getViewPortInformation = () => {
5728
- const { width: pageWidth, height: pageHeight } = context.getPageSize();
5729
- const viewportDimensions = frameItem.getViewportDimensions();
5730
- const frameElement = frameItem.element;
5731
- if (containerElement && (frameElement == null ? void 0 : frameElement.contentDocument) && (frameElement == null ? void 0 : frameElement.contentWindow) && viewportDimensions) {
5732
- const computedWidthScale = pageWidth / viewportDimensions.width;
5733
- const computedScale = Math.min(
5734
- computedWidthScale,
5735
- pageHeight / viewportDimensions.height
5736
- );
5737
- return { computedScale, computedWidthScale, viewportDimensions };
5738
- }
5739
- };
5740
- const load = () => frameItem.load();
5741
- const unload = () => frameItem.unload();
5742
- const getBoundingRectOfElementFromSelector = (selector) => {
5743
- var _a, _b, _c, _d;
5744
- const frameElement = frameItem.element;
5745
- if (frameElement && selector) {
5746
- if (selector.startsWith(`#`)) {
5747
- return (_b = (_a = frameElement.contentDocument) == null ? void 0 : _a.getElementById(selector.replace(`#`, ``))) == null ? void 0 : _b.getBoundingClientRect();
5748
- }
5749
- return (_d = (_c = frameElement.contentDocument) == null ? void 0 : _c.querySelector(selector)) == null ? void 0 : _d.getBoundingClientRect();
5750
- }
5478
+ const getSpinePositionFromSpineItem = (spineItem) => {
5479
+ return getSpinePositionFromSpineItemPosition({
5480
+ spineItemPosition: { x: 0, y: 0 },
5481
+ itemLayout: spineLayout.getAbsolutePositionOf(spineItem)
5482
+ });
5751
5483
  };
5752
- const getDimensionsForPaginatedContent = () => {
5753
- const pageSize = context.getPageSize();
5754
- const pageWidth = pageSize.width;
5755
- const columnHeight = pageSize.height;
5756
- const columnWidth = pageWidth;
5757
- return { columnHeight, columnWidth };
5484
+ const getSpineItemFromIframe = (iframe) => {
5485
+ return spineItemsManager.items.find((item) => {
5486
+ var _a;
5487
+ return ((_a = item.renderer.layers[0]) == null ? void 0 : _a.element) === iframe;
5488
+ });
5758
5489
  };
5759
- const getDimensionsForReflowableContent = (isUsingVerticalWriting, minimumWidth) => {
5760
- const pageSize = context.getPageSize();
5761
- const horizontalMargin = 0;
5762
- const verticalMargin = 0;
5763
- let columnWidth = pageSize.width - horizontalMargin * 2;
5764
- const columnHeight = pageSize.height - verticalMargin * 2;
5765
- let width = pageSize.width - horizontalMargin * 2;
5766
- if (isUsingVerticalWriting) {
5767
- width = minimumWidth - horizontalMargin * 2;
5768
- columnWidth = columnHeight;
5490
+ const getSpineItemPageIndexFromNode = (node, offset, spineItemOrIndex) => {
5491
+ if (typeof spineItemOrIndex === `number`) {
5492
+ const spineItem = spineItemsManager.get(spineItemOrIndex);
5493
+ return spineItem ? spineItemLocator.getSpineItemPageIndexFromNode(
5494
+ node,
5495
+ offset || 0,
5496
+ spineItem
5497
+ ) : void 0;
5769
5498
  }
5770
- return {
5771
- columnHeight,
5772
- columnWidth,
5773
- // horizontalMargin,
5774
- // verticalMargin,
5775
- width
5776
- };
5499
+ return spineItemLocator.getSpineItemPageIndexFromNode(
5500
+ node,
5501
+ offset || 0,
5502
+ spineItemOrIndex
5503
+ );
5777
5504
  };
5778
- const layout = ({
5779
- height,
5780
- width,
5781
- blankPagePosition,
5782
- minimumWidth
5505
+ const getVisiblePagesFromViewportPosition = ({
5506
+ position,
5507
+ threshold,
5508
+ spineItem,
5509
+ restrictToScreen
5783
5510
  }) => {
5784
- containerElement.style.width = `${width}px`;
5785
- containerElement.style.height = `${height}px`;
5786
- hookManager.execute(`item.onAfterLayout`, void 0, {
5787
- blankPagePosition,
5788
- item,
5789
- minimumWidth
5511
+ const { height, width } = spineItem.getElementDimensions();
5512
+ const numberOfPages = spineItemLocator.getSpineItemNumberOfPages({
5513
+ isUsingVerticalWriting: !!spineItem.isUsingVerticalWriting(),
5514
+ itemHeight: height,
5515
+ itemWidth: width
5790
5516
  });
5791
- };
5792
- const translateFramePositionIntoPage = (position) => {
5793
- var _a, _b;
5794
- const { left = 0, top = 0 } = ((_a = frameItem.element) == null ? void 0 : _a.getBoundingClientRect()) || {};
5795
- const computedScale = ((_b = getViewPortInformation()) == null ? void 0 : _b.computedScale) ?? 1;
5796
- const adjustedX = position.clientX * computedScale + left;
5797
- const adjustedY = position.clientY * computedScale + top;
5517
+ const pages = Array.from(Array(numberOfPages)).map((_, index) => {
5518
+ const spineItemPosition = spineItemLocator.getSpineItemPositionFromPageIndex({
5519
+ pageIndex: index,
5520
+ isUsingVerticalWriting: !!spineItem.isUsingVerticalWriting(),
5521
+ itemLayout: spineItem.getElementDimensions()
5522
+ });
5523
+ const spinePosition = getSpinePositionFromSpineItemPosition({
5524
+ spineItemPosition,
5525
+ itemLayout: spineLayout.getAbsolutePositionOf(spineItem)
5526
+ });
5527
+ return {
5528
+ index,
5529
+ absolutePosition: {
5530
+ width: context.getPageSize().width,
5531
+ height: context.getPageSize().height,
5532
+ left: spinePosition.x,
5533
+ top: spinePosition.y,
5534
+ bottom: spinePosition.y + context.getPageSize().height,
5535
+ right: spinePosition.x + context.getPageSize().width
5536
+ }
5537
+ };
5538
+ });
5539
+ const pagesVisible = pages.reduce(
5540
+ (acc, { absolutePosition, index }) => {
5541
+ const { visible } = getItemVisibilityForPosition({
5542
+ context,
5543
+ viewportPosition: position,
5544
+ restrictToScreen,
5545
+ threshold,
5546
+ itemPosition: absolutePosition
5547
+ });
5548
+ if (visible) {
5549
+ return [...acc, index];
5550
+ }
5551
+ return acc;
5552
+ },
5553
+ []
5554
+ );
5555
+ const beginPageIndex = pagesVisible[0];
5556
+ const endPageIndex = pagesVisible[pagesVisible.length - 1] ?? beginPageIndex;
5557
+ if (beginPageIndex === void 0 || endPageIndex === void 0)
5558
+ return void 0;
5798
5559
  return {
5799
- clientX: adjustedX,
5800
- clientY: adjustedY
5560
+ beginPageIndex,
5561
+ endPageIndex
5801
5562
  };
5802
5563
  };
5803
- const getResource = async () => {
5804
- const fetchResource = settings.values.fetchResource;
5805
- const lastFetch = (_) => {
5806
- if (fetchResource) {
5807
- return fetchResource(item);
5808
- }
5809
- return fetch(item.href);
5564
+ const isPositionWithinSpineItem = (position, spineItem) => {
5565
+ const { bottom, left, right, top } = spineLayout.getAbsolutePositionOf(spineItem);
5566
+ return position.x >= left && position.x <= right && position.y <= bottom && position.y >= top;
5567
+ };
5568
+ const getSafeSpineItemPositionFromUnsafeSpineItemPosition = (unsafePosition, spineItem) => {
5569
+ const { height, width } = spineLayout.getAbsolutePositionOf(spineItem);
5570
+ return {
5571
+ x: Math.min(Math.max(0, unsafePosition.x), width),
5572
+ y: Math.min(Math.max(0, unsafePosition.y), height)
5810
5573
  };
5811
- return await lastFetch();
5812
5574
  };
5813
- const executeOnLayoutBeforeMeasurementHook = (options) => hookManager.execute("item.onLayoutBeforeMeasurement", void 0, {
5814
- itemIndex: index,
5815
- isImageType,
5816
- ...options
5817
- });
5818
- const contentLayout$ = frameItem.contentLayoutChange$.pipe(
5819
- withLatestFrom$1(frameItem.isReady$),
5820
- map(([data, isReady]) => ({
5821
- isFirstLayout: data.isFirstLayout,
5822
- isReady
5823
- }))
5824
- );
5825
5575
  return {
5826
- item,
5827
- layout,
5828
- overlayElement,
5829
- adjustPositionOfElement,
5830
- getElementDimensions,
5831
- getHtmlFromResource: frameItem.getHtmlFromResource,
5832
- getResource,
5833
- translateFramePositionIntoPage,
5834
- injectStyle,
5835
- load,
5836
- unload,
5837
- frame: frameItem,
5838
- element: containerElement,
5839
- getBoundingRectOfElementFromSelector,
5840
- getViewPortInformation,
5841
- isImageType,
5842
- isReady: () => frameItem.isReady,
5843
- destroy: () => {
5844
- destroySubject$.next();
5845
- containerElement.remove();
5846
- frameItem == null ? void 0 : frameItem.destroy();
5847
- fingerTracker.destroy();
5848
- selectionTracker.destroy();
5849
- destroySubject$.complete();
5850
- },
5851
- /**
5852
- * @important
5853
- * Do not use this value for layout and navigation. It will be in possible conflict
5854
- * with the global reading direction. A book should not mix them anyway. A page can have
5855
- * a different reading direction for style reason but it should be in theory pre-paginated.
5856
- * For example an english page in a japanese manga. That's expected and will
5857
- * be confined to a single page.
5858
- */
5859
- get readingDirection() {
5860
- var _a;
5861
- const writingMode = this.frame.getWritingMode();
5862
- if (writingMode === `vertical-rl`) {
5863
- return `rtl`;
5864
- }
5865
- const direction = (_a = this.frame.getComputedStyleAfterLoad()) == null ? void 0 : _a.direction;
5866
- if ([`ltr`, `rtl`].includes(direction || ``))
5867
- return direction;
5868
- return void 0;
5869
- },
5870
- isUsingVerticalWriting: () => {
5871
- var _a;
5872
- return (_a = frameItem.getWritingMode()) == null ? void 0 : _a.startsWith(`vertical`);
5873
- },
5874
- executeOnLayoutBeforeMeasurementHook,
5875
- selectionTracker,
5876
- fingerTracker,
5877
- getDimensionsForReflowableContent,
5878
- getDimensionsForPaginatedContent,
5879
- $: {
5880
- contentLayout$,
5881
- loaded$: frameItem.loaded$,
5882
- isReady$: frameItem.isReady$
5883
- }
5884
- };
5885
- };
5886
- const createContainerElement = (containerElement, item, hookManager) => {
5887
- const element = containerElement.ownerDocument.createElement(`div`);
5888
- element.classList.add(`spineItem`);
5889
- element.classList.add(`spineItem-${item.renditionLayout}`);
5890
- element.style.cssText = `
5891
- position: absolute;
5892
- overflow: hidden;
5893
- `;
5894
- hookManager.execute("item.onBeforeContainerCreated", void 0, { element });
5895
- return element;
5896
- };
5897
- const createOverlayElement = (containerElement, item) => {
5898
- const element = containerElement.ownerDocument.createElement(`div`);
5899
- element.classList.add(`spineItemOverlay`);
5900
- element.classList.add(`spineItemOverlay-${item.renditionLayout}`);
5901
- element.style.cssText = `
5902
- position: absolute;
5903
- width: 100%;
5904
- height: 100%;
5905
- pointer-events: none;
5906
- background-color: transparent;
5907
- `;
5908
- return element;
5909
- };
5910
- const getStyleForViewportDocument = () => {
5911
- return `
5912
- body {
5913
- margin: 0;
5914
- }
5915
- }
5916
- `;
5917
- };
5918
- const createPrePaginatedSpineItem = ({
5919
- item,
5920
- context,
5921
- containerElement,
5922
- settings,
5923
- hookManager,
5924
- index
5925
- }) => {
5926
- const commonSpineItem = createCommonSpineItem({
5927
- context,
5928
- item,
5929
- parentElement: containerElement,
5930
- settings,
5931
- hookManager,
5932
- index
5933
- });
5934
- const spineItemFrame = commonSpineItem.frame;
5935
- const layout = ({
5936
- blankPagePosition,
5937
- minimumWidth,
5938
- spreadPosition
5939
- }) => {
5940
- const { width: pageWidth, height: pageHeight } = context.getPageSize();
5941
- const { viewportDimensions, computedScale = 1 } = commonSpineItem.getViewPortInformation() ?? {};
5942
- const visibleArea = context.state.visibleAreaRect;
5943
- const frameElement = spineItemFrame.element;
5944
- if ((spineItemFrame == null ? void 0 : spineItemFrame.isLoaded) && (frameElement == null ? void 0 : frameElement.contentDocument) && (frameElement == null ? void 0 : frameElement.contentWindow)) {
5945
- const contentWidth = pageWidth;
5946
- const contentHeight = visibleArea.height + context.state.calculatedInnerMargin;
5947
- const cssLink = buildDocumentStyle(
5948
- {
5949
- ...commonSpineItem.getDimensionsForPaginatedContent(),
5950
- enableTouch: settings.values.computedPageTurnMode !== `scrollable`,
5951
- spreadPosition
5952
- },
5953
- viewportDimensions
5954
- );
5955
- frameElement == null ? void 0 : frameElement.style.setProperty(`visibility`, `visible`);
5956
- frameElement == null ? void 0 : frameElement.style.setProperty(`opacity`, `1`);
5957
- if (viewportDimensions) {
5958
- commonSpineItem.injectStyle(cssLink);
5959
- spineItemFrame.staticLayout({
5960
- width: viewportDimensions.width,
5961
- height: viewportDimensions.height
5962
- });
5963
- frameElement == null ? void 0 : frameElement.style.setProperty(`position`, `absolute`);
5964
- frameElement == null ? void 0 : frameElement.style.setProperty(`top`, `50%`);
5965
- if (spreadPosition === `left`) {
5966
- frameElement == null ? void 0 : frameElement.style.setProperty(`right`, `0`);
5967
- frameElement == null ? void 0 : frameElement.style.removeProperty(`left`);
5968
- } else if (blankPagePosition === `before` && context.isRTL()) {
5969
- frameElement == null ? void 0 : frameElement.style.setProperty(`right`, `50%`);
5970
- frameElement == null ? void 0 : frameElement.style.removeProperty(`left`);
5971
- } else if (spreadPosition === `right`) {
5972
- frameElement == null ? void 0 : frameElement.style.setProperty(`left`, `0`);
5973
- frameElement == null ? void 0 : frameElement.style.removeProperty(`right`);
5974
- } else {
5975
- frameElement == null ? void 0 : frameElement.style.setProperty(
5976
- `left`,
5977
- blankPagePosition === `before` ? context.isRTL() ? `25%` : `75%` : blankPagePosition === `after` ? context.isRTL() ? `75%` : `25%` : `50%`
5978
- );
5979
- frameElement == null ? void 0 : frameElement.style.removeProperty(`right`);
5980
- }
5981
- const transformTranslateX = spreadPosition !== `none` ? `0` : `-50%`;
5982
- const transformOriginX = spreadPosition === `right` && blankPagePosition !== `before` ? `left` : spreadPosition === `left` || blankPagePosition === `before` && context.isRTL() ? `right` : `center`;
5983
- frameElement == null ? void 0 : frameElement.style.setProperty(
5984
- `transform`,
5985
- `translate(${transformTranslateX}, -50%) scale(${computedScale})`
5986
- );
5987
- frameElement == null ? void 0 : frameElement.style.setProperty(
5988
- `transform-origin`,
5989
- `${transformOriginX} center`
5990
- );
5991
- commonSpineItem.executeOnLayoutBeforeMeasurementHook({ minimumWidth });
5992
- } else {
5993
- commonSpineItem.injectStyle(cssLink);
5994
- spineItemFrame.staticLayout({
5995
- width: contentWidth,
5996
- height: contentHeight
5997
- });
5998
- if (blankPagePosition === `before`) {
5999
- if (context.isRTL()) {
6000
- frameElement == null ? void 0 : frameElement.style.setProperty(`margin-right`, `${pageWidth}px`);
6001
- } else {
6002
- frameElement == null ? void 0 : frameElement.style.setProperty(`margin-left`, `${pageWidth}px`);
6003
- }
6004
- } else {
6005
- frameElement == null ? void 0 : frameElement.style.removeProperty(`margin-left`);
6006
- frameElement == null ? void 0 : frameElement.style.removeProperty(`margin-right`);
6007
- }
6008
- }
6009
- commonSpineItem.executeOnLayoutBeforeMeasurementHook({ minimumWidth });
6010
- commonSpineItem.layout({
6011
- width: minimumWidth,
6012
- height: contentHeight,
6013
- blankPagePosition,
6014
- minimumWidth
6015
- });
6016
- return { width: minimumWidth, height: contentHeight };
6017
- } else {
6018
- commonSpineItem.executeOnLayoutBeforeMeasurementHook({ minimumWidth });
6019
- commonSpineItem.layout({
6020
- width: minimumWidth,
6021
- height: pageHeight,
6022
- blankPagePosition,
6023
- minimumWidth
6024
- });
6025
- }
6026
- return { width: minimumWidth, height: pageHeight };
6027
- };
6028
- return {
6029
- ...commonSpineItem,
6030
- layout
6031
- };
6032
- };
6033
- const buildDocumentStyle = ({
6034
- columnWidth,
6035
- enableTouch,
6036
- spreadPosition
6037
- }, viewportDimensions) => {
6038
- return `
6039
- ${getStyleForViewportDocument()}
6040
- body {
6041
- ${!viewportDimensions ? `
6042
- display: flex;
6043
- justify-content: ${spreadPosition === `left` ? `flex-end` : spreadPosition === `right` ? `flex-start` : `center`};
6044
- ` : ``}
6045
- }
6046
- ${/*
6047
- might be html * but it does mess up things like figure if so.
6048
- check accessible_epub_3
6049
- */
6050
- ``}
6051
- html, body {
6052
- height: 100%;
6053
- width: 100%;
6054
- }
6055
- ${/*
6056
- This one is important for preventing 100% img to resize above
6057
- current width. Especially needed for cbz conversion
6058
- */
6059
- ``}
6060
- html, body {
6061
- -max-width: ${columnWidth}px !important;
6062
- }
6063
- ${/*
6064
- * @see https://hammerjs.github.io/touch-action/
6065
- * It needs to be disabled when using free scroll
6066
- */
6067
- ``}
6068
- html, body {
6069
- ${enableTouch ? `
6070
- touch-action: none
6071
- ` : ``}
6072
- }
6073
- ${/*
6074
- prevent drag of image instead of touch on firefox
6075
- */
6076
- ``}
6077
- img {
6078
- user-select: none;
6079
- -webkit-user-drag: none;
6080
- -khtml-user-drag: none;
6081
- -moz-user-drag: none;
6082
- -o-user-drag: none;
6083
- user-drag: none;
6084
- ${/*
6085
- prevent weird overflow or margin. Try `block` if `flex` has weird behavior
6086
- */
6087
- ``}
6088
- display: flex;
6089
- ${/*
6090
- If the document does not have viewport, we cannot scale anything inside.
6091
- This should never happens with a valid epub document however it will happens if
6092
- we load .jpg, .png, etc directly in the iframe. This is expected, in this case we force
6093
- the inner content to display correctly.
6094
- */
6095
- ``}
6096
- ${!viewportDimensions ? `
6097
- -width: 100%;
6098
- max-width: 100%;
6099
- height:100%;
6100
- object-fit:contain;
6101
- ` : ``}
6102
- }
6103
- `;
6104
- };
6105
- const buildStyleForViewportFrame = () => {
6106
- return `
6107
- ${getStyleForViewportDocument()}
6108
- html {
6109
- width: 100%;
6110
- height: 100%;
6111
- }
6112
- body {
6113
- width: 100%;
6114
- height: 100%;
6115
- margin: 0;
6116
- }
6117
- ${/*
6118
- * @see https://hammerjs.github.io/touch-action/
6119
- */
6120
- ``}
6121
- html, body {
6122
- touch-action: none;
6123
- }
6124
- `;
6125
- };
6126
- const buildStyleForReflowableImageOnly = ({
6127
- isScrollable,
6128
- enableTouch
6129
- }) => {
6130
- return `
6131
- ${/*
6132
- * @see https://hammerjs.github.io/touch-action/
6133
- */
6134
- ``}
6135
- html, body {
6136
- width: 100%;
6137
- margin: 0;
6138
- padding: 0;
6139
- ${enableTouch ? `
6140
- touch-action: none
6141
- ` : ``}
6142
- }
6143
- ${isScrollable ? `
6144
- img {
6145
- height: auto !important;
6146
- margin: 0;
6147
- padding: 0;
6148
- box-sizing: border-box;
6149
- ${// we make sure img spread on entire screen
6150
- ``}
6151
- width: 100%;
6152
- ${/**
6153
- * line break issue
6154
- * @see https://stackoverflow.com/questions/37869020/image-not-taking-up-the-full-height-of-container
6155
- */
6156
- ``}
6157
- display: block;
6158
- }
6159
- ` : ``}
6160
- `;
6161
- };
6162
- const buildStyleWithMultiColumn = ({
6163
- width,
6164
- columnHeight,
6165
- columnWidth
6166
- }) => {
6167
- return `
6168
- parsererror {
6169
- display: none !important;
6170
- }
6171
- ${/*
6172
- might be html * but it does mess up things like figure if so.
6173
- check accessible_epub_3
6174
- */
6175
- ``}
6176
- html, body {
6177
- margin: 0;
6178
- padding: 0 !important;
6179
- -max-width: ${columnWidth}px !important;
6180
- }
6181
- ${/*
6182
- body {
6183
- height: ${columnHeight}px !important;
6184
- width: ${columnWidth}px !important;
6185
- -margin-left: ${horizontalMargin}px !important;
6186
- -margin-right: ${horizontalMargin}px !important;
6187
- -margin: ${verticalMargin}px ${horizontalMargin}px !important;
6188
- -padding-top: ${horizontalMargin}px !important;
6189
- -padding-bottom: ${horizontalMargin}px !important;
6190
- }
6191
- */
6192
- ``}
6193
- body {
6194
- padding: 0 !important;
6195
- width: ${width}px !important;
6196
- height: ${columnHeight}px !important;
6197
- overflow-y: hidden;
6198
- column-gap: 0px !important;
6199
- column-width: ${columnWidth}px !important;
6200
- column-fill: auto !important;
6201
- word-wrap: break-word;
6202
- box-sizing: border-box;
6203
- }
6204
- body {
6205
- margin: 0;
6206
- }
6207
- body:focus-visible {
6208
- ${/*
6209
- we make sure that there are no outline when we focus something inside the iframe
6210
- */
6211
- ``}
6212
- outline: none;
6213
- }
6214
- ${/*
6215
- * @see https://hammerjs.github.io/touch-action/
6216
- */
6217
- ``}
6218
- html, body {
6219
- touch-action: none;
6220
- }
6221
- ${/*
6222
- this messes up hard, be careful with this
6223
- */
6224
- ``}
6225
- * {
6226
- -max-width: ${columnWidth}px !important;
6227
- }
6228
- ${/*
6229
- this is necessary to have a proper calculation when determining size
6230
- of iframe content. If an img is using something like width:100% it would expand to
6231
- the size of the original image and potentially gives back a wrong size (much larger)
6232
- @see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Columns/Handling_Overflow_in_Multicol
6233
- */
6234
- ``}
6235
- img, video, audio, object, svg {
6236
- max-width: 100%;
6237
- max-width: ${columnWidth}px !important;
6238
- max-height: ${columnHeight}px !important;
6239
- -pointer-events: none;
6240
- -webkit-column-break-inside: avoid;
6241
- page-break-inside: avoid;
6242
- break-inside: avoid;
6243
- }
6244
- figure {
6245
- d-max-width: ${columnWidth}px !important;
6246
- }
6247
- img {
6248
- object-fit: contain;
6249
- break-inside: avoid;
6250
- box-sizing: border-box;
6251
- d-max-width: ${columnWidth}px !important;
6252
- }
6253
- ${/*
6254
- img, video, audio, object, svg {
6255
- max-height: ${columnHeight}px !important;
6256
- box-sizing: border-box;
6257
- object-fit: contain;
6258
- -webkit-column-break-inside: avoid;
6259
- page-break-inside: avoid;
6260
- break-inside: avoid;
6261
- }
6262
- */
6263
- ``}
6264
- table {
6265
- max-width: ${columnWidth}px !important;
6266
- table-layout: fixed;
6267
- }
6268
- td {
6269
- max-width: ${columnWidth}px;
6270
- }
6271
- `;
6272
- };
6273
- const createReflowableSpineItem = ({
6274
- item,
6275
- context,
6276
- containerElement,
6277
- settings,
6278
- hookManager,
6279
- index
6280
- }) => {
6281
- const commonSpineItem = createCommonSpineItem({
6282
- context,
6283
- item,
6284
- parentElement: containerElement,
6285
- settings,
6286
- hookManager,
6287
- index
6288
- });
6289
- const spineItemFrame = commonSpineItem.frame;
6290
- let latestContentHeightWhenLoaded;
6291
- const layout = ({
6292
- blankPagePosition,
6293
- minimumWidth
6294
- }) => {
6295
- var _a, _b, _c, _d, _e, _f, _g;
6296
- const { width: pageWidth, height: pageSizeHeight } = context.getPageSize();
6297
- const continuousScrollableReflowableItem = ((_a = context.manifest) == null ? void 0 : _a.renditionLayout) === "reflowable" && ((_b = context.manifest) == null ? void 0 : _b.renditionFlow) === "scrolled-continuous" && commonSpineItem.item.renditionLayout === "reflowable";
6298
- const pageHeight = continuousScrollableReflowableItem ? Math.min(400, pageSizeHeight) : pageSizeHeight;
6299
- (_c = spineItemFrame.element) == null ? void 0 : _c.style.setProperty(`width`, `${pageWidth}px`);
6300
- if (!continuousScrollableReflowableItem) {
6301
- (_d = spineItemFrame.element) == null ? void 0 : _d.style.setProperty(`height`, `${pageHeight}px`);
6302
- }
6303
- const { viewportDimensions, computedScale = 1 } = commonSpineItem.getViewPortInformation() ?? {};
6304
- const visibleArea = context.state.visibleAreaRect;
6305
- const frameElement = spineItemFrame.element;
6306
- const isGloballyPrePaginated = ((_e = context.manifest) == null ? void 0 : _e.renditionLayout) === `pre-paginated`;
6307
- if ((spineItemFrame == null ? void 0 : spineItemFrame.isLoaded) && (frameElement == null ? void 0 : frameElement.contentDocument) && (frameElement == null ? void 0 : frameElement.contentWindow)) {
6308
- let contentWidth = pageWidth;
6309
- let contentHeight = visibleArea.height + context.state.calculatedInnerMargin;
6310
- frameElement == null ? void 0 : frameElement.style.setProperty(`visibility`, `visible`);
6311
- frameElement == null ? void 0 : frameElement.style.setProperty(`opacity`, `1`);
6312
- if (viewportDimensions) {
6313
- commonSpineItem.injectStyle(buildStyleForViewportFrame());
6314
- commonSpineItem.executeOnLayoutBeforeMeasurementHook({ minimumWidth });
6315
- spineItemFrame.staticLayout({
6316
- width: viewportDimensions.width,
6317
- height: viewportDimensions.height
6318
- });
6319
- frameElement == null ? void 0 : frameElement.style.setProperty(`position`, `absolute`);
6320
- frameElement == null ? void 0 : frameElement.style.setProperty(`top`, `50%`);
6321
- frameElement == null ? void 0 : frameElement.style.setProperty(
6322
- `left`,
6323
- blankPagePosition === `before` ? context.isRTL() ? `25%` : `75%` : blankPagePosition === `after` ? context.isRTL() ? `75%` : `25%` : `50%`
6324
- );
6325
- frameElement == null ? void 0 : frameElement.style.setProperty(
6326
- `transform`,
6327
- `translate(-50%, -50%) scale(${computedScale})`
6328
- );
6329
- frameElement == null ? void 0 : frameElement.style.setProperty(`transform-origin`, `center center`);
6330
- } else {
6331
- const frameStyle = commonSpineItem.isImageType() ? buildStyleForReflowableImageOnly({
6332
- isScrollable: ((_f = context.manifest) == null ? void 0 : _f.renditionFlow) === `scrolled-continuous`,
6333
- enableTouch: settings.values.computedPageTurnMode !== `scrollable`
6334
- }) : buildStyleWithMultiColumn(
6335
- commonSpineItem.getDimensionsForReflowableContent(
6336
- spineItemFrame.isUsingVerticalWriting(),
6337
- minimumWidth
6338
- )
6339
- );
6340
- commonSpineItem.injectStyle(frameStyle);
6341
- commonSpineItem.executeOnLayoutBeforeMeasurementHook({ minimumWidth });
6342
- if (spineItemFrame.isUsingVerticalWriting()) {
6343
- const pages = Math.ceil(
6344
- frameElement.contentDocument.documentElement.scrollHeight / pageHeight
6345
- );
6346
- contentHeight = pages * pageHeight;
6347
- spineItemFrame.staticLayout({
6348
- width: minimumWidth,
6349
- height: contentHeight
6350
- });
6351
- } else if (((_g = context.manifest) == null ? void 0 : _g.renditionFlow) === `scrolled-continuous`) {
6352
- contentHeight = frameElement.contentDocument.body.scrollHeight;
6353
- latestContentHeightWhenLoaded = contentHeight;
6354
- spineItemFrame.staticLayout({
6355
- width: minimumWidth,
6356
- height: contentHeight
6357
- });
6358
- } else {
6359
- const pages = Math.ceil(
6360
- frameElement.contentDocument.documentElement.scrollWidth / pageWidth
6361
- );
6362
- if (isGloballyPrePaginated) {
6363
- contentWidth = pageWidth;
6364
- } else {
6365
- contentWidth = pages * pageWidth;
6366
- }
6367
- spineItemFrame.staticLayout({
6368
- width: contentWidth,
6369
- height: contentHeight
6370
- });
6371
- }
6372
- }
6373
- const isFillingAllScreen = contentWidth % minimumWidth === 0;
6374
- if (!isFillingAllScreen) {
6375
- contentWidth = contentWidth + pageWidth;
6376
- if (context.isRTL() && !commonSpineItem.isUsingVerticalWriting()) {
6377
- frameElement == null ? void 0 : frameElement.style.setProperty(`margin-left`, `${pageWidth}px`);
6378
- }
6379
- } else {
6380
- frameElement == null ? void 0 : frameElement.style.setProperty(`margin-left`, `0px`);
6381
- }
6382
- commonSpineItem.layout({
6383
- width: contentWidth,
6384
- height: contentHeight,
6385
- blankPagePosition,
6386
- minimumWidth
6387
- });
6388
- return { width: contentWidth, height: contentHeight };
6389
- } else {
6390
- commonSpineItem.executeOnLayoutBeforeMeasurementHook({ minimumWidth });
6391
- }
6392
- const height = latestContentHeightWhenLoaded || pageHeight;
6393
- commonSpineItem.layout({
6394
- width: minimumWidth,
6395
- height,
6396
- blankPagePosition,
6397
- minimumWidth
6398
- });
6399
- return { width: minimumWidth, height };
6400
- };
6401
- return {
6402
- ...commonSpineItem,
6403
- layout
6404
- };
6405
- };
6406
- const createSpineItem = ({
6407
- item,
6408
- context,
6409
- containerElement,
6410
- settings,
6411
- hookManager,
6412
- index
6413
- }) => {
6414
- if (item.renditionLayout === `pre-paginated`) {
6415
- return createPrePaginatedSpineItem({
6416
- item,
6417
- context,
6418
- containerElement,
6419
- settings,
6420
- hookManager,
6421
- index
6422
- });
6423
- }
6424
- return createReflowableSpineItem({
6425
- item,
6426
- context,
6427
- containerElement,
6428
- settings,
6429
- hookManager,
6430
- index
6431
- });
6432
- };
6433
- const getSpineItemFromPosition = ({
6434
- position,
6435
- spineItemsManager,
6436
- spineLayout,
6437
- settings
6438
- }) => {
6439
- const spineItem = spineItemsManager.items.find((item) => {
6440
- const { left, right, bottom, top } = spineLayout.getAbsolutePositionOf(item);
6441
- const isWithinXAxis = position.x >= left && position.x < right;
6442
- if (settings.values.computedPageTurnDirection === `horizontal`) {
6443
- return isWithinXAxis;
6444
- } else {
6445
- return isWithinXAxis && position.y >= top && position.y < bottom;
6446
- }
6447
- });
6448
- if (position.x === 0 && !spineItem) {
6449
- return spineItemsManager.items[0];
6450
- }
6451
- return spineItem;
6452
- };
6453
- const isItemVisibleByThresholdForPosition = ({
6454
- itemHeight,
6455
- itemWidth,
6456
- visibleWidthOfItem,
6457
- visibleHeightOfItem,
6458
- threshold
6459
- }) => {
6460
- const visibleWidthRatioOfSpineItem = visibleWidthOfItem / itemWidth;
6461
- const visibleHeightRatioOfSpineItem = visibleHeightOfItem / itemHeight;
6462
- const isItemVisibleEnough = visibleWidthRatioOfSpineItem >= threshold && visibleHeightRatioOfSpineItem >= threshold;
6463
- return isItemVisibleEnough;
6464
- };
6465
- const isItemVisibleOnScreenByThresholdForPosition = ({
6466
- visibleWidthOfItem,
6467
- visibleHeightOfItem,
6468
- threshold,
6469
- context
6470
- }) => {
6471
- const widthRatioOfSpaceTakenInScreen = visibleWidthOfItem / context.state.visibleAreaRect.width;
6472
- const heightRatioOfSpaceTakenInScreen = visibleHeightOfItem / context.state.visibleAreaRect.height;
6473
- const isItemVisibleEnoughOnScreen = heightRatioOfSpaceTakenInScreen >= threshold && widthRatioOfSpaceTakenInScreen >= threshold;
6474
- return isItemVisibleEnoughOnScreen;
6475
- };
6476
- const getItemVisibilityForPosition = ({
6477
- itemPosition: {
6478
- bottom,
6479
- left,
6480
- right,
6481
- top,
6482
- width: itemWidth,
6483
- height: itemHeight
6484
- },
6485
- threshold,
6486
- viewportPosition,
6487
- restrictToScreen,
6488
- context
6489
- }) => {
6490
- const viewportLeft = viewportPosition.x;
6491
- const viewportRight = viewportPosition.x + (context.state.visibleAreaRect.width - 1);
6492
- const viewportTop = viewportPosition.y;
6493
- const viewportBottom = Math.max(
6494
- viewportPosition.y + (context.state.visibleAreaRect.height - 1),
6495
- 0
6496
- );
6497
- const visibleWidthOfItem = Math.max(
6498
- 0,
6499
- Math.min(right, viewportRight) - Math.max(left, viewportLeft)
6500
- );
6501
- const visibleHeightOfItem = Math.max(
6502
- 0,
6503
- Math.min(bottom, viewportBottom) - Math.max(top, viewportTop)
6504
- );
6505
- const itemIsOnTheOuterEdge = visibleWidthOfItem <= 0 || visibleHeightOfItem <= 0;
6506
- if (itemIsOnTheOuterEdge) return { visible: false };
6507
- const isItemVisibleEnoughOnScreen = isItemVisibleOnScreenByThresholdForPosition({
6508
- threshold,
6509
- visibleHeightOfItem,
6510
- visibleWidthOfItem,
6511
- context
6512
- });
6513
- if (restrictToScreen) {
6514
- return { visible: isItemVisibleEnoughOnScreen };
6515
- }
6516
- const isItemVisibleEnough = isItemVisibleByThresholdForPosition({
6517
- itemHeight,
6518
- itemWidth,
6519
- threshold,
6520
- visibleHeightOfItem,
6521
- visibleWidthOfItem
6522
- });
6523
- return {
6524
- visible: isItemVisibleEnough || isItemVisibleEnoughOnScreen
6525
- };
6526
- };
6527
- const getVisibleSpineItemsFromPosition = ({
6528
- position,
6529
- threshold,
6530
- restrictToScreen,
6531
- spineItemsManager,
6532
- context,
6533
- settings,
6534
- spineLayout
6535
- }) => {
6536
- const fallbackSpineItem = getSpineItemFromPosition({
6537
- position,
6538
- settings,
6539
- spineItemsManager,
6540
- spineLayout
6541
- }) || spineItemsManager.get(0);
6542
- const spineItemsVisible = spineItemsManager.items.reduce(
6543
- (acc, spineItem) => {
6544
- const itemPosition = spineLayout.getAbsolutePositionOf(spineItem);
6545
- const { visible } = getItemVisibilityForPosition({
6546
- itemPosition,
6547
- threshold,
6548
- viewportPosition: position,
6549
- restrictToScreen,
6550
- context
6551
- });
6552
- if (visible) {
6553
- return [...acc, spineItem];
6554
- }
6555
- return acc;
6556
- },
6557
- []
6558
- );
6559
- const beginItem = spineItemsVisible[0] ?? fallbackSpineItem;
6560
- const endItem = spineItemsVisible[spineItemsVisible.length - 1] ?? beginItem;
6561
- if (!beginItem || !endItem) return void 0;
6562
- const beginItemIndex = spineItemsManager.getSpineItemIndex(beginItem);
6563
- const endItemIndex = spineItemsManager.getSpineItemIndex(endItem);
6564
- return {
6565
- beginIndex: beginItemIndex ?? 0,
6566
- endIndex: endItemIndex ?? 0
6567
- };
6568
- };
6569
- const getSpinePositionFromSpineItemPosition = ({
6570
- spineItemPosition,
6571
- itemLayout: { left, top }
6572
- }) => {
6573
- return {
6574
- x: left + spineItemPosition.x,
6575
- y: top + spineItemPosition.y
6576
- };
6577
- };
6578
- const getSpineInfoFromAbsolutePageIndex = ({
6579
- absolutePageIndex,
6580
- spineLayout,
6581
- spineItemsManager,
6582
- context,
6583
- settings
6584
- }) => {
6585
- const items = spineItemsManager.items;
6586
- const { found, currentAbsolutePage } = items.reduce(
6587
- (acc, item) => {
6588
- if (acc.found) return acc;
6589
- const itemLayout = spineLayout.getAbsolutePositionOf(item);
6590
- const numberOfPages = getSpineItemNumberOfPages({
6591
- isUsingVerticalWriting: !!item.isUsingVerticalWriting(),
6592
- itemHeight: itemLayout.height,
6593
- itemWidth: itemLayout.width,
6594
- context,
6595
- settings
6596
- });
6597
- const possiblePageIndex = absolutePageIndex - acc.currentAbsolutePage;
6598
- const currentAbsolutePage2 = acc.currentAbsolutePage + numberOfPages;
6599
- if (possiblePageIndex <= numberOfPages - 1) {
6600
- return {
6601
- ...acc,
6602
- currentAbsolutePage: currentAbsolutePage2,
6603
- found: { item, pageIndex: possiblePageIndex }
6604
- };
6605
- }
6606
- return {
6607
- ...acc,
6608
- currentAbsolutePage: currentAbsolutePage2
6609
- };
6610
- },
6611
- { currentAbsolutePage: 0 }
6612
- );
6613
- if (found) {
6614
- return {
6615
- spineItem: found.item,
6616
- pageIndex: found.pageIndex,
6617
- itemIndex: spineItemsManager.getSpineItemIndex(found.item) ?? 0,
6618
- currentAbsolutePage
6619
- };
6620
- }
6621
- return void 0;
6622
- };
6623
- const getAbsolutePageIndexFromPageIndex = ({
6624
- pageIndex,
6625
- spineItemOrId,
6626
- spineLayout,
6627
- spineItemsManager,
6628
- context,
6629
- settings
6630
- }) => {
6631
- const items = spineItemsManager.items;
6632
- const spineItem = spineItemsManager.get(spineItemOrId);
6633
- if (!spineItem) return void 0;
6634
- const { currentAbsolutePage } = items.reduce(
6635
- (acc, item) => {
6636
- if (acc.found) return acc;
6637
- const itemLayout = spineLayout.getAbsolutePositionOf(item);
6638
- const numberOfPages = getSpineItemNumberOfPages({
6639
- isUsingVerticalWriting: !!item.isUsingVerticalWriting(),
6640
- itemHeight: itemLayout.height,
6641
- itemWidth: itemLayout.width,
6642
- context,
6643
- settings
6644
- });
6645
- if (spineItem === item) {
6646
- if (pageIndex <= numberOfPages - 1) {
6647
- return {
6648
- currentAbsolutePage: acc.currentAbsolutePage + pageIndex,
6649
- found: true
6650
- };
6651
- }
6652
- }
6653
- return {
6654
- ...acc,
6655
- currentAbsolutePage: acc.currentAbsolutePage + numberOfPages
6656
- };
6657
- },
6658
- { currentAbsolutePage: 0, found: false }
6659
- );
6660
- return currentAbsolutePage;
6661
- };
6662
- const createSpineLocator = ({
6663
- spineItemsManager,
6664
- context,
6665
- spineItemLocator,
6666
- settings,
6667
- spineLayout
6668
- }) => {
6669
- const getSpineItemPositionFromSpinePosition = Report.measurePerformance(
6670
- `getSpineItemPositionFromSpinePosition`,
6671
- 10,
6672
- (position, spineItem) => {
6673
- const { left, top } = spineLayout.getAbsolutePositionOf(spineItem);
6674
- return {
6675
- /**
6676
- * when using spread the item could be on the right and therefore will be negative
6677
- * @example
6678
- * 400 (position = viewport), page of 200
6679
- * 400 - 600 = -200.
6680
- * However we can assume we are at 0, because we in fact can see the beginning of the item
6681
- */
6682
- x: Math.max(position.x - left, 0),
6683
- y: Math.max(position.y - top, 0)
6684
- };
6685
- },
6686
- { disable: true }
6687
- );
6688
- const getSpinePositionFromSpineItem = (spineItem) => {
6689
- return getSpinePositionFromSpineItemPosition({
6690
- spineItemPosition: { x: 0, y: 0 },
6691
- itemLayout: spineLayout.getAbsolutePositionOf(spineItem)
6692
- });
6693
- };
6694
- const getSpineItemFromIframe = (iframe) => {
6695
- return spineItemsManager.items.find((item) => item.frame.element === iframe);
6696
- };
6697
- const getSpineItemPageIndexFromNode = (node, offset, spineItemOrIndex) => {
6698
- if (typeof spineItemOrIndex === `number`) {
6699
- const spineItem = spineItemsManager.get(spineItemOrIndex);
6700
- return spineItem ? spineItemLocator.getSpineItemPageIndexFromNode(
6701
- node,
6702
- offset || 0,
6703
- spineItem
6704
- ) : void 0;
6705
- }
6706
- return spineItemLocator.getSpineItemPageIndexFromNode(
6707
- node,
6708
- offset || 0,
6709
- spineItemOrIndex
6710
- );
6711
- };
6712
- const getVisiblePagesFromViewportPosition = ({
6713
- position,
6714
- threshold,
6715
- spineItem,
6716
- restrictToScreen
6717
- }) => {
6718
- const { height, width } = spineItem.getElementDimensions();
6719
- const numberOfPages = spineItemLocator.getSpineItemNumberOfPages({
6720
- isUsingVerticalWriting: !!spineItem.isUsingVerticalWriting(),
6721
- itemHeight: height,
6722
- itemWidth: width
6723
- });
6724
- const pages = Array.from(Array(numberOfPages)).map((_, index) => {
6725
- const spineItemPosition = spineItemLocator.getSpineItemPositionFromPageIndex({
6726
- pageIndex: index,
6727
- isUsingVerticalWriting: !!spineItem.isUsingVerticalWriting(),
6728
- itemLayout: spineItem.getElementDimensions()
6729
- });
6730
- const spinePosition = getSpinePositionFromSpineItemPosition({
6731
- spineItemPosition,
6732
- itemLayout: spineLayout.getAbsolutePositionOf(spineItem)
6733
- });
6734
- return {
6735
- index,
6736
- absolutePosition: {
6737
- width: context.getPageSize().width,
6738
- height: context.getPageSize().height,
6739
- left: spinePosition.x,
6740
- top: spinePosition.y,
6741
- bottom: spinePosition.y + context.getPageSize().height,
6742
- right: spinePosition.x + context.getPageSize().width
6743
- }
6744
- };
6745
- });
6746
- const pagesVisible = pages.reduce(
6747
- (acc, { absolutePosition, index }) => {
6748
- const { visible } = getItemVisibilityForPosition({
6749
- context,
6750
- viewportPosition: position,
6751
- restrictToScreen,
6752
- threshold,
6753
- itemPosition: absolutePosition
6754
- });
6755
- if (visible) {
6756
- return [...acc, index];
6757
- }
6758
- return acc;
6759
- },
6760
- []
6761
- );
6762
- const beginPageIndex = pagesVisible[0];
6763
- const endPageIndex = pagesVisible[pagesVisible.length - 1] ?? beginPageIndex;
6764
- if (beginPageIndex === void 0 || endPageIndex === void 0)
6765
- return void 0;
6766
- return {
6767
- beginPageIndex,
6768
- endPageIndex
6769
- };
6770
- };
6771
- const isPositionWithinSpineItem = (position, spineItem) => {
6772
- const { bottom, left, right, top } = spineLayout.getAbsolutePositionOf(spineItem);
6773
- return position.x >= left && position.x <= right && position.y <= bottom && position.y >= top;
6774
- };
6775
- const getSafeSpineItemPositionFromUnsafeSpineItemPosition = (unsafePosition, spineItem) => {
6776
- const { height, width } = spineLayout.getAbsolutePositionOf(spineItem);
6777
- return {
6778
- x: Math.min(Math.max(0, unsafePosition.x), width),
6779
- y: Math.min(Math.max(0, unsafePosition.y), height)
6780
- };
6781
- };
6782
- return {
6783
- getSpinePositionFromSpineItemPosition: ({
6784
- spineItem,
6785
- spineItemPosition
6786
- }) => {
6787
- const itemLayout = spineLayout.getAbsolutePositionOf(spineItem);
6788
- return getSpinePositionFromSpineItemPosition({
6789
- itemLayout,
6790
- spineItemPosition
6791
- });
5576
+ getSpinePositionFromSpineItemPosition: ({
5577
+ spineItem,
5578
+ spineItemPosition
5579
+ }) => {
5580
+ const itemLayout = spineLayout.getAbsolutePositionOf(spineItem);
5581
+ return getSpinePositionFromSpineItemPosition({
5582
+ itemLayout,
5583
+ spineItemPosition
5584
+ });
6792
5585
  },
6793
5586
  getAbsolutePageIndexFromPageIndex: (params) => getAbsolutePageIndexFromPageIndex({
6794
5587
  ...params,
@@ -6899,7 +5692,7 @@ class SpineItemsObserver extends DestroyableClass {
6899
5692
  this.itemIsReady$ = this.spineItemsManager.items$.pipe(
6900
5693
  switchMap((items) => {
6901
5694
  const itemsIsReady$ = items.map(
6902
- (item) => item.$.isReady$.pipe(map$1((isReady) => ({ item, isReady })))
5695
+ (item) => item.isReady$.pipe(map$1((isReady) => ({ item, isReady })))
6903
5696
  );
6904
5697
  return merge(...itemsIsReady$);
6905
5698
  }),
@@ -6916,6 +5709,14 @@ class SpineItemsObserver extends DestroyableClass {
6916
5709
  }),
6917
5710
  share()
6918
5711
  );
5712
+ this.itemLoaded$ = this.spineItemsManager.items$.pipe(
5713
+ switchMap((items) => {
5714
+ const itemsIsReady$ = items.map(
5715
+ (item) => item.loaded$.pipe(map$1(() => item))
5716
+ );
5717
+ return merge(...itemsIsReady$);
5718
+ })
5719
+ );
6919
5720
  }
6920
5721
  }
6921
5722
  const convertSpinePositionToLayoutPosition = ({
@@ -6945,14 +5746,14 @@ class SpineLayout extends DestroyableClass {
6945
5746
  spineItemsManager.items$.pipe(
6946
5747
  switchMap((items) => {
6947
5748
  const itemsLayout$ = items.map(
6948
- (spineItem) => spineItem.$.contentLayout$.pipe(
5749
+ (spineItem) => spineItem.contentLayout$.pipe(
6949
5750
  tap$1(() => {
6950
5751
  this.layout();
6951
5752
  })
6952
5753
  )
6953
5754
  );
6954
5755
  const writingModeUpdate$ = items.map(
6955
- (spineItem) => spineItem.$.loaded$.pipe(
5756
+ (spineItem) => spineItem.loaded$.pipe(
6956
5757
  tap$1(() => {
6957
5758
  if (spineItem.isUsingVerticalWriting()) {
6958
5759
  this.context.update({
@@ -7157,6 +5958,423 @@ class SpineLayout extends DestroyableClass {
7157
5958
  this.layoutSubject.complete();
7158
5959
  }
7159
5960
  }
5961
+ const createFingerTracker = () => {
5962
+ const fingerPositionInIframe = { x: void 0, y: void 0 };
5963
+ const subject = new Subject();
5964
+ let isMouseDown = false;
5965
+ const track = (frame) => {
5966
+ var _a, _b, _c;
5967
+ fingerPositionInIframe.x = void 0;
5968
+ fingerPositionInIframe.y = void 0;
5969
+ (_a = frame.contentDocument) == null ? void 0 : _a.addEventListener(`mousedown`, (e) => {
5970
+ isMouseDown = true;
5971
+ fingerPositionInIframe.x = e.x;
5972
+ fingerPositionInIframe.y = e.y;
5973
+ subject.next({ event: `fingermove`, data: { x: e.x, y: e.y } });
5974
+ });
5975
+ (_b = frame.contentDocument) == null ? void 0 : _b.addEventListener(`mouseup`, () => {
5976
+ isMouseDown = false;
5977
+ fingerPositionInIframe.x = void 0;
5978
+ fingerPositionInIframe.y = void 0;
5979
+ subject.next({ event: `fingerout`, data: void 0 });
5980
+ });
5981
+ (_c = frame.contentDocument) == null ? void 0 : _c.addEventListener(`mousemove`, (e) => {
5982
+ if (isMouseDown) {
5983
+ subject.next({ event: `fingermove`, data: { x: e.x, y: e.y } });
5984
+ }
5985
+ });
5986
+ };
5987
+ return {
5988
+ track,
5989
+ getFingerPositionInIframe() {
5990
+ return fingerPositionInIframe.x === void 0 || fingerPositionInIframe.y === void 0 ? void 0 : fingerPositionInIframe;
5991
+ },
5992
+ destroy: () => {
5993
+ },
5994
+ $: subject.asObservable()
5995
+ };
5996
+ };
5997
+ const createSelectionTracker = () => {
5998
+ let isSelecting = false;
5999
+ let frame;
6000
+ const subject = new Subject();
6001
+ const mouseUpEvents = [`mouseup`, `pointerup`];
6002
+ const track = (frameToTrack) => {
6003
+ var _a, _b;
6004
+ frame = frameToTrack;
6005
+ mouseUpEvents.forEach((eventName) => {
6006
+ var _a2;
6007
+ (_a2 = frameToTrack.contentWindow) == null ? void 0 : _a2.addEventListener(eventName, () => {
6008
+ isSelecting = false;
6009
+ });
6010
+ });
6011
+ (_a = frameToTrack.contentDocument) == null ? void 0 : _a.addEventListener(`selectionchange`, () => {
6012
+ var _a2;
6013
+ subject.next({
6014
+ event: `selectionchange`,
6015
+ data: ((_a2 = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _a2.getSelection()) || null
6016
+ });
6017
+ });
6018
+ (_b = frameToTrack.contentWindow) == null ? void 0 : _b.addEventListener(`selectstart`, () => {
6019
+ isSelecting = true;
6020
+ });
6021
+ };
6022
+ const destroy = () => {
6023
+ };
6024
+ return {
6025
+ track,
6026
+ destroy,
6027
+ isSelecting: () => isSelecting,
6028
+ getSelection: () => {
6029
+ var _a;
6030
+ const selection = (_a = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _a.getSelection();
6031
+ if (!(selection == null ? void 0 : selection.anchorNode) || selection.type === `None` || selection.type === `Caret`)
6032
+ return void 0;
6033
+ return selection;
6034
+ },
6035
+ $: subject.asObservable()
6036
+ };
6037
+ };
6038
+ class DocumentRenderer {
6039
+ constructor(context, settings, hookManager, item, containerElement, resourcesHandler) {
6040
+ this.context = context;
6041
+ this.settings = settings;
6042
+ this.hookManager = hookManager;
6043
+ this.item = item;
6044
+ this.containerElement = containerElement;
6045
+ this.resourcesHandler = resourcesHandler;
6046
+ this.stateSubject = new BehaviorSubject(`idle`);
6047
+ this.triggerSubject = new Subject();
6048
+ this.layers = [];
6049
+ this.unload$ = this.triggerSubject.pipe(
6050
+ withLatestFrom(this.stateSubject),
6051
+ filter$1(
6052
+ ([trigger, state]) => trigger === `unload` && state !== "idle" && state !== "unloading"
6053
+ ),
6054
+ map$1(() => void 0),
6055
+ share()
6056
+ );
6057
+ this.load$ = this.triggerSubject.pipe(
6058
+ withLatestFrom(this.stateSubject),
6059
+ filter$1(
6060
+ ([trigger, state]) => trigger === `load` && state !== "ready" && state !== "loaded" && state !== "loading"
6061
+ ),
6062
+ map$1(() => void 0),
6063
+ share()
6064
+ );
6065
+ this.destroy$ = this.triggerSubject.pipe(
6066
+ filter$1((trigger) => trigger === `destroy`)
6067
+ );
6068
+ this.stateSubject.subscribe((state) => {
6069
+ console.log(`FOOO`, item.id, `state`, state);
6070
+ });
6071
+ this.load$.pipe(
6072
+ switchMap(() => {
6073
+ this.stateSubject.next(`loading`);
6074
+ const createDocument$ = this.onCreateDocument().pipe(
6075
+ endWith(null),
6076
+ first$1()
6077
+ );
6078
+ return createDocument$.pipe(
6079
+ tap$1(() => {
6080
+ this.hookManager.execute(`item.onDocumentCreated`, this.item.id, {
6081
+ itemId: this.item.id,
6082
+ layers: this.layers
6083
+ });
6084
+ }),
6085
+ switchMap(() => {
6086
+ const loadDocument$ = this.onLoadDocument().pipe(
6087
+ endWith(null),
6088
+ first$1()
6089
+ );
6090
+ return loadDocument$;
6091
+ }),
6092
+ waitForSwitch(this.context.bridgeEvent.viewportFree$),
6093
+ switchMap(() => {
6094
+ const hookResults = this.hookManager.execute(`item.onDocumentLoad`, this.item.id, {
6095
+ itemId: this.item.id,
6096
+ layers: this.layers
6097
+ }).filter(
6098
+ (result) => result instanceof Observable
6099
+ );
6100
+ return combineLatest([of(null), ...hookResults]).pipe(first$1());
6101
+ }),
6102
+ tap$1(() => {
6103
+ this.stateSubject.next(`loaded`);
6104
+ this.stateSubject.next(`ready`);
6105
+ }),
6106
+ takeUntil(merge(this.destroy$, this.unload$))
6107
+ );
6108
+ })
6109
+ ).subscribe();
6110
+ this.unload$.pipe(
6111
+ switchMap(() => {
6112
+ this.stateSubject.next(`unloading`);
6113
+ return this.context.bridgeEvent.viewportFree$.pipe(
6114
+ first$1(),
6115
+ tap$1(() => {
6116
+ this.hookManager.destroy(`item.onDocumentLoad`, this.item.id);
6117
+ }),
6118
+ switchMap(() => {
6119
+ const unload$ = this.onUnload().pipe(endWith(null), first$1());
6120
+ return unload$;
6121
+ }),
6122
+ tap$1(() => {
6123
+ this.stateSubject.next(`idle`);
6124
+ }),
6125
+ takeUntil(merge(this.destroy$, this.load$))
6126
+ );
6127
+ })
6128
+ ).subscribe();
6129
+ }
6130
+ get state$() {
6131
+ return this.stateSubject;
6132
+ }
6133
+ load() {
6134
+ this.triggerSubject.next(`load`);
6135
+ }
6136
+ unload() {
6137
+ this.triggerSubject.next(`unload`);
6138
+ }
6139
+ destroy() {
6140
+ this.triggerSubject.next(`destroy`);
6141
+ this.triggerSubject.complete();
6142
+ this.stateSubject.complete();
6143
+ }
6144
+ get writingMode() {
6145
+ return void 0;
6146
+ }
6147
+ get readingDirection() {
6148
+ return void 0;
6149
+ }
6150
+ }
6151
+ class DefaultRenderer extends DocumentRenderer {
6152
+ onUnload() {
6153
+ return EMPTY;
6154
+ }
6155
+ onCreateDocument() {
6156
+ return EMPTY;
6157
+ }
6158
+ onLoadDocument() {
6159
+ return EMPTY;
6160
+ }
6161
+ layout() {
6162
+ return void 0;
6163
+ }
6164
+ }
6165
+ const defaultGetResource = (item) => new URL(item.href);
6166
+ class ResourceHandler {
6167
+ constructor(item, settings) {
6168
+ this.item = item;
6169
+ this.settings = settings;
6170
+ }
6171
+ async getResource() {
6172
+ var _a, _b;
6173
+ const resource = await lastValueFrom(
6174
+ ((_b = (_a = this.settings.values).getResource) == null ? void 0 : _b.call(_a, this.item)) ?? of(void 0)
6175
+ );
6176
+ return resource ?? defaultGetResource(this.item);
6177
+ }
6178
+ async fetchResource() {
6179
+ const resource = await this.getResource();
6180
+ if (resource instanceof Response) return resource;
6181
+ if (resource instanceof URL) return fetch(resource);
6182
+ return resource;
6183
+ }
6184
+ }
6185
+ class SpineItem {
6186
+ constructor(item, parentElement, context, settings, hookManager, index) {
6187
+ var _a, _b;
6188
+ this.item = item;
6189
+ this.parentElement = parentElement;
6190
+ this.context = context;
6191
+ this.settings = settings;
6192
+ this.hookManager = hookManager;
6193
+ this.index = index;
6194
+ this.adjustPositionOfElement = ({
6195
+ right,
6196
+ left,
6197
+ top
6198
+ }) => {
6199
+ if (right !== void 0) {
6200
+ this.containerElement.style.right = `${right}px`;
6201
+ } else {
6202
+ this.containerElement.style.removeProperty(`right`);
6203
+ }
6204
+ if (left !== void 0) {
6205
+ this.containerElement.style.left = `${left}px`;
6206
+ } else {
6207
+ this.containerElement.style.removeProperty(`left`);
6208
+ }
6209
+ if (top !== void 0) {
6210
+ this.containerElement.style.top = `${top}px`;
6211
+ } else {
6212
+ this.containerElement.style.removeProperty(`top`);
6213
+ }
6214
+ };
6215
+ this.getBoundingRectOfElementFromSelector = (selector) => {
6216
+ var _a2, _b2, _c, _d, _e;
6217
+ const frameElement = (_a2 = this.renderer.layers[0]) == null ? void 0 : _a2.element;
6218
+ if (frameElement && frameElement instanceof HTMLIFrameElement && selector) {
6219
+ if (selector.startsWith(`#`)) {
6220
+ return (_c = (_b2 = frameElement.contentDocument) == null ? void 0 : _b2.getElementById(selector.replace(`#`, ``))) == null ? void 0 : _c.getBoundingClientRect();
6221
+ }
6222
+ return (_e = (_d = frameElement.contentDocument) == null ? void 0 : _d.querySelector(selector)) == null ? void 0 : _e.getBoundingClientRect();
6223
+ }
6224
+ };
6225
+ this.layout = ({
6226
+ blankPagePosition,
6227
+ minimumWidth,
6228
+ spreadPosition
6229
+ }) => {
6230
+ this.hookManager.execute(`item.onBeforeLayout`, void 0, {
6231
+ blankPagePosition,
6232
+ item: this.item,
6233
+ minimumWidth
6234
+ });
6235
+ const { height, width } = this.renderer.layout({
6236
+ blankPagePosition,
6237
+ minPageSpread: minimumWidth / this.context.getPageSize().width,
6238
+ spreadPosition
6239
+ }) ?? { width: 0, height: 0 };
6240
+ const minHeight = Math.max(height, this.context.getPageSize().height);
6241
+ const minWidth = Math.max(width, minimumWidth);
6242
+ this.containerElement.style.width = `${minWidth}px`;
6243
+ this.containerElement.style.height = `${minHeight}px`;
6244
+ this.hookManager.execute(`item.onAfterLayout`, void 0, {
6245
+ blankPagePosition,
6246
+ item: this.item,
6247
+ minimumWidth
6248
+ });
6249
+ return { width: minWidth, height: minHeight };
6250
+ };
6251
+ this.load = () => this.renderer.load();
6252
+ this.unload = () => {
6253
+ this.renderer.unload();
6254
+ };
6255
+ this.getElementDimensions = () => {
6256
+ const rect = this.containerElement.getBoundingClientRect();
6257
+ const normalizedValues = {
6258
+ ...rect,
6259
+ // we want to round to first decimal because it's possible to have half pixel
6260
+ // however browser engine can also gives back x.yyyy based on their precision
6261
+ width: Math.round(rect.width * 10) / 10,
6262
+ height: Math.round(rect.height * 10) / 10
6263
+ };
6264
+ return normalizedValues;
6265
+ };
6266
+ this.destroy = () => {
6267
+ this.destroySubject$.next();
6268
+ this.containerElement.remove();
6269
+ this.fingerTracker.destroy();
6270
+ this.selectionTracker.destroy();
6271
+ this.destroySubject$.complete();
6272
+ this.renderer.destroy();
6273
+ };
6274
+ this.isUsingVerticalWriting = () => {
6275
+ var _a2;
6276
+ return !!((_a2 = this.renderer.writingMode) == null ? void 0 : _a2.startsWith(`vertical`));
6277
+ };
6278
+ this.destroySubject$ = new Subject();
6279
+ this.containerElement = createContainerElement(
6280
+ parentElement,
6281
+ item,
6282
+ hookManager
6283
+ );
6284
+ this.fingerTracker = createFingerTracker();
6285
+ this.selectionTracker = createSelectionTracker();
6286
+ parentElement.appendChild(this.containerElement);
6287
+ const RendererClass = ((_b = (_a = this.settings.values).getRenderer) == null ? void 0 : _b.call(_a, item)) ?? DefaultRenderer;
6288
+ this.resourcesHandler = new ResourceHandler(item, this.settings);
6289
+ this.renderer = new RendererClass(
6290
+ context,
6291
+ settings,
6292
+ hookManager,
6293
+ item,
6294
+ this.containerElement,
6295
+ this.resourcesHandler
6296
+ );
6297
+ const contentLayoutChange$ = merge(
6298
+ this.unloaded$.pipe(map(() => ({ isFirstLayout: false }))),
6299
+ this.ready$.pipe(map(() => ({ isFirstLayout: true })))
6300
+ );
6301
+ this.contentLayout$ = contentLayoutChange$.pipe(
6302
+ withLatestFrom$1(this.isReady$),
6303
+ map(([data, isReady]) => ({
6304
+ isFirstLayout: data.isFirstLayout,
6305
+ isReady
6306
+ }))
6307
+ );
6308
+ }
6309
+ get element() {
6310
+ return this.containerElement;
6311
+ }
6312
+ /**
6313
+ * @important
6314
+ * Do not use this value for layout and navigation. It will be in possible conflict
6315
+ * with the global reading direction. A book should not mix them anyway. A page can have
6316
+ * a different reading direction for style reason but it should be in theory pre-paginated.
6317
+ * For example an english page in a japanese manga. That's expected and will
6318
+ * be confined to a single page.
6319
+ */
6320
+ get readingDirection() {
6321
+ return this.renderer.readingDirection;
6322
+ }
6323
+ get isReady() {
6324
+ return this.renderer.state$.getValue() === "ready";
6325
+ }
6326
+ get ready$() {
6327
+ return this.renderer.state$.pipe(
6328
+ distinctUntilChanged(),
6329
+ filter((state) => state === "ready")
6330
+ );
6331
+ }
6332
+ get loaded$() {
6333
+ return this.renderer.state$.pipe(
6334
+ distinctUntilChanged(),
6335
+ filter((state) => state === "loaded")
6336
+ );
6337
+ }
6338
+ get unloaded$() {
6339
+ return this.renderer.state$.pipe(
6340
+ distinctUntilChanged(),
6341
+ filter((state) => state !== "idle"),
6342
+ switchMap$1(
6343
+ () => this.renderer.state$.pipe(
6344
+ filter((state) => state === `idle`),
6345
+ first()
6346
+ )
6347
+ )
6348
+ );
6349
+ }
6350
+ get isReady$() {
6351
+ return this.renderer.state$.pipe(map((state) => state === "ready"));
6352
+ }
6353
+ /**
6354
+ * Helper that will inject CSS into the document frame.
6355
+ *
6356
+ * @important
6357
+ * The document needs to be detected as a frame.
6358
+ */
6359
+ upsertCSS(id, style, prepend) {
6360
+ this.renderer.layers.forEach((layer) => {
6361
+ if (layer.element instanceof HTMLIFrameElement) {
6362
+ upsertCSS(layer.element, id, style, prepend);
6363
+ }
6364
+ });
6365
+ }
6366
+ }
6367
+ const createContainerElement = (containerElement, item, hookManager) => {
6368
+ const element = containerElement.ownerDocument.createElement(`div`);
6369
+ element.classList.add(`spineItem`);
6370
+ element.classList.add(`spineItem-${item.renditionLayout}`);
6371
+ element.style.cssText = `
6372
+ position: absolute;
6373
+ overflow: hidden;
6374
+ `;
6375
+ hookManager.execute("item.onBeforeContainerCreated", void 0, { element });
6376
+ return element;
6377
+ };
7160
6378
  class Spine extends DestroyableClass {
7161
6379
  constructor(parentElement$, context, pagination, spineItemsManager, spineItemLocator, settings, hookManager) {
7162
6380
  super();
@@ -7196,14 +6414,14 @@ class Spine extends DestroyableClass {
7196
6414
  tap((manifest) => {
7197
6415
  this.spineItemsManager.destroyItems();
7198
6416
  const spineItems = manifest.spineItems.map(
7199
- (resource, index) => createSpineItem({
7200
- item: resource,
7201
- containerElement: this.elementSubject.getValue(),
7202
- context: this.context,
7203
- settings: this.settings,
7204
- hookManager: this.hookManager,
6417
+ (resource, index) => new SpineItem(
6418
+ resource,
6419
+ this.elementSubject.getValue(),
6420
+ this.context,
6421
+ this.settings,
6422
+ this.hookManager,
7205
6423
  index
7206
- })
6424
+ )
7207
6425
  );
7208
6426
  this.spineItemsManager.addMany(spineItems);
7209
6427
  })
@@ -7581,492 +6799,1403 @@ const createResourcesManager = (context) => {
7581
6799
  if (cacheData) {
7582
6800
  return new Response(cacheData, { status: 200 });
7583
6801
  }
7584
- const data = fetchResource && await fetchResource(item) || await fetch(item.href);
7585
- cache(item, data.clone());
7586
- return data;
7587
- };
7588
- const cache = (itemIndexOrId, data) => {
7589
- cache$.next({ id: itemIndexOrId, data });
6802
+ const data = fetchResource && await fetchResource(item) || await fetch(item.href);
6803
+ cache(item, data.clone());
6804
+ return data;
6805
+ };
6806
+ const cache = (itemIndexOrId, data) => {
6807
+ cache$.next({ id: itemIndexOrId, data });
6808
+ };
6809
+ cache$.asObservable().pipe(
6810
+ mergeMap$1(({ id, data }) => {
6811
+ const item = retrieveItem(id);
6812
+ if (!item) return EMPTY;
6813
+ return from(
6814
+ forkJoin([openDatabase(`prose-reader`), from(data.blob())])
6815
+ ).pipe(
6816
+ switchMap$1(([db, blob]) => {
6817
+ return from(db.put(`${uniqueID}_${item.id}`, blob));
6818
+ }),
6819
+ catchError((error) => {
6820
+ Report.error(error);
6821
+ return EMPTY;
6822
+ })
6823
+ );
6824
+ }),
6825
+ takeUntil$1(context.destroy$)
6826
+ ).subscribe();
6827
+ const onLoad$ = context.manifest$.pipe(
6828
+ tap(() => {
6829
+ uniqueID = Date.now().toString();
6830
+ })
6831
+ );
6832
+ merge(onLoad$).pipe(
6833
+ switchMap$1(() => {
6834
+ Report.log(`Cleanup up old cache...`);
6835
+ return from(openDatabase(`prose-reader`)).pipe(
6836
+ switchMap$1(
6837
+ (db) => from(db.keys()).pipe(
6838
+ map(
6839
+ (keys) => keys.filter((key) => !key.toString().startsWith(uniqueID))
6840
+ ),
6841
+ switchMap$1((keysToRemove) => {
6842
+ const promises = keysToRemove.map((key) => db.remove(key));
6843
+ return from(Promise.all(promises));
6844
+ })
6845
+ )
6846
+ ),
6847
+ catchError((error) => {
6848
+ Report.error(error);
6849
+ return EMPTY;
6850
+ })
6851
+ );
6852
+ }),
6853
+ takeUntil$1(context.destroy$)
6854
+ ).subscribe();
6855
+ const destroy = () => {
6856
+ cache$.complete();
6857
+ };
6858
+ return {
6859
+ get,
6860
+ destroy
6861
+ };
6862
+ };
6863
+ const resourcesEnhancer = (next) => (options) => {
6864
+ const reader = next(options);
6865
+ const resourceManager = createResourcesManager(reader.context);
6866
+ const destroy = () => {
6867
+ resourceManager.destroy();
6868
+ reader.destroy();
6869
+ };
6870
+ return {
6871
+ ...reader,
6872
+ // $: {
6873
+ // ...reader.$,
6874
+ // errors$: merge(reader.$.errors$, errorsSubject$.asObservable())
6875
+ // },
6876
+ destroy
6877
+ // load,
6878
+ };
6879
+ };
6880
+ class ImageRenderer extends DocumentRenderer {
6881
+ getImageElement() {
6882
+ var _a;
6883
+ const element = (_a = this.layers[0]) == null ? void 0 : _a.element;
6884
+ if (!(element instanceof HTMLImageElement)) return void 0;
6885
+ return element;
6886
+ }
6887
+ onCreateDocument() {
6888
+ return from(this.resourcesHandler.getResource()).pipe(
6889
+ switchMap((responseOrUrl) => {
6890
+ const imgElement = this.containerElement.ownerDocument.createElement(`img`);
6891
+ this.layers = [{ element: imgElement }];
6892
+ imgElement.style.objectFit = `contain`;
6893
+ imgElement.style.userSelect = `none`;
6894
+ if (responseOrUrl instanceof URL) {
6895
+ return of(responseOrUrl.href);
6896
+ } else if (responseOrUrl instanceof Response) {
6897
+ return from(responseOrUrl.blob()).pipe(
6898
+ map$1((blob) => {
6899
+ return URL.createObjectURL(blob);
6900
+ })
6901
+ );
6902
+ }
6903
+ throw new Error(`Invalid resource`);
6904
+ }),
6905
+ tap$1((src) => {
6906
+ const element = this.getImageElement();
6907
+ if (element) {
6908
+ element.src = src;
6909
+ }
6910
+ })
6911
+ );
6912
+ }
6913
+ onLoadDocument() {
6914
+ const imageElement = this.getImageElement();
6915
+ if (!imageElement) throw new Error(`invalid element`);
6916
+ this.containerElement.appendChild(imageElement);
6917
+ return fromEvent(imageElement, `load`);
6918
+ }
6919
+ onUnload() {
6920
+ const imageElement = this.getImageElement();
6921
+ if (imageElement) {
6922
+ URL.revokeObjectURL(imageElement.src);
6923
+ }
6924
+ this.layers.forEach(({ element }) => {
6925
+ element.remove();
6926
+ });
6927
+ this.layers = [];
6928
+ return EMPTY;
6929
+ }
6930
+ layout({
6931
+ spreadPosition
6932
+ }) {
6933
+ var _a, _b;
6934
+ const element = this.getImageElement();
6935
+ const continuousScrollableReflowableItem = ((_a = this.context.manifest) == null ? void 0 : _a.renditionLayout) === "reflowable" && ((_b = this.context.manifest) == null ? void 0 : _b.renditionFlow) === "scrolled-continuous";
6936
+ const { height: pageHeight, width: pageWidth } = this.context.getPageSize();
6937
+ let height = pageHeight;
6938
+ const width = pageWidth;
6939
+ if (element) {
6940
+ const naturalWidth = element.naturalWidth;
6941
+ const naturalHeight = element.naturalHeight;
6942
+ const ratio = naturalWidth / naturalHeight;
6943
+ if (continuousScrollableReflowableItem) {
6944
+ height = pageWidth / ratio;
6945
+ }
6946
+ element.style.height = `${height}px`;
6947
+ element.style.width = `${width}px`;
6948
+ element.style.objectPosition = spreadPosition === "left" ? `right` : spreadPosition === `right` ? `left` : `center`;
6949
+ }
6950
+ return {
6951
+ width,
6952
+ height
6953
+ };
6954
+ }
6955
+ }
6956
+ const mediaEnhancer = (next) => (options) => {
6957
+ const reader = next({
6958
+ ...options,
6959
+ getRenderer(item) {
6960
+ var _a;
6961
+ const MaybeRenderer = (_a = options.getRenderer) == null ? void 0 : _a.call(options, item);
6962
+ const mimeType = item.mediaType ?? detectMimeTypeFromName(item.href);
6963
+ const isImageType = !!(mimeType == null ? void 0 : mimeType.startsWith(`image/`));
6964
+ if (!MaybeRenderer && isImageType) {
6965
+ return ImageRenderer;
6966
+ }
6967
+ return MaybeRenderer;
6968
+ }
6969
+ });
6970
+ const frameObserver = new IntersectionObserver(
6971
+ (entries) => {
6972
+ entries.forEach((entry) => {
6973
+ var _a;
6974
+ const frame = entry.target;
6975
+ const audios = Array.from(
6976
+ ((_a = frame.contentDocument) == null ? void 0 : _a.body.getElementsByTagName(`audio`)) || []
6977
+ );
6978
+ if (!entry.isIntersecting) {
6979
+ audios.forEach((audioElement) => {
6980
+ audioElement.pause();
6981
+ audioElement.currentTime = 0;
6982
+ });
6983
+ } else {
6984
+ audios.forEach((audioElement) => {
6985
+ if (audioElement.hasAttribute(`autoplay`) && audioElement.paused && audioElement.readyState >= 2) {
6986
+ audioElement.play().catch(console.error);
6987
+ }
6988
+ });
6989
+ }
6990
+ });
6991
+ },
6992
+ {
6993
+ threshold: 0.01
6994
+ }
6995
+ );
6996
+ const elementObserver = new IntersectionObserver(
6997
+ (entries) => {
6998
+ entries.forEach((entry) => {
6999
+ if (entry.target.tagName === `video`) {
7000
+ const video = entry.target;
7001
+ if (!entry.isIntersecting) {
7002
+ video.pause();
7003
+ video.currentTime = 0;
7004
+ } else {
7005
+ if (video.hasAttribute(`autoplay`)) {
7006
+ if (video.paused && video.readyState >= 2) {
7007
+ video.play().catch(console.error);
7008
+ }
7009
+ }
7010
+ }
7011
+ }
7012
+ });
7013
+ },
7014
+ {
7015
+ threshold: 0.5
7016
+ }
7017
+ );
7018
+ reader.hookManager.register(
7019
+ `item.onDocumentLoad`,
7020
+ ({ layers, destroy: destroy2 }) => {
7021
+ var _a, _b;
7022
+ const frame = (_a = layers[0]) == null ? void 0 : _a.element;
7023
+ if (!(frame instanceof HTMLIFrameElement)) return;
7024
+ frameObserver.observe(frame);
7025
+ const videos = (_b = frame.contentDocument) == null ? void 0 : _b.body.getElementsByTagName(`video`);
7026
+ const unobserveElements = Array.from(videos || []).map((element) => {
7027
+ elementObserver.observe(element);
7028
+ return () => elementObserver.unobserve(element);
7029
+ });
7030
+ destroy2(() => {
7031
+ frameObserver.unobserve(frame);
7032
+ unobserveElements.forEach((unobserve) => unobserve());
7033
+ });
7034
+ }
7035
+ );
7036
+ const destroy = () => {
7037
+ frameObserver.disconnect();
7038
+ elementObserver.disconnect();
7039
+ reader.destroy();
7040
+ };
7041
+ return {
7042
+ ...reader,
7043
+ destroy
7044
+ };
7045
+ };
7046
+ const progressionEnhancer = (next) => (options) => {
7047
+ const reader = next(options);
7048
+ const getPercentageEstimate = (context, currentSpineIndex, numberOfPages, pageIndex, currentPosition, currentItem) => {
7049
+ var _a, _b, _c, _d, _e, _f;
7050
+ const isGloballyPrePaginated = ((_a = context.manifest) == null ? void 0 : _a.renditionLayout) === `pre-paginated`;
7051
+ const readingOrderLength = ((_b = context.manifest) == null ? void 0 : _b.spineItems.length) || 0;
7052
+ const estimateBeforeThisItem = ((_c = context.manifest) == null ? void 0 : _c.spineItems.slice(0, currentSpineIndex).reduce((acc, item) => acc + (item.progressionWeight ?? 0), 0)) || 0;
7053
+ const currentItemWeight = ((_e = (_d = context.manifest) == null ? void 0 : _d.spineItems[currentSpineIndex]) == null ? void 0 : _e.progressionWeight) || 0;
7054
+ let progressWithinThisItem = (pageIndex + 1) * (currentItemWeight / numberOfPages);
7055
+ if (!isGloballyPrePaginated && currentItem.item.renditionLayout === `reflowable` && !currentItem.isReady) {
7056
+ progressWithinThisItem = 0;
7057
+ }
7058
+ let totalProgress = estimateBeforeThisItem + progressWithinThisItem;
7059
+ if (((_f = context.manifest) == null ? void 0 : _f.renditionFlow) === `scrolled-continuous`) {
7060
+ if (currentItem.isReady) {
7061
+ progressWithinThisItem = getScrollPercentageWithinItem(
7062
+ context,
7063
+ currentPosition,
7064
+ currentItem
7065
+ );
7066
+ } else {
7067
+ progressWithinThisItem = 0;
7068
+ }
7069
+ totalProgress = getTotalProgressFromPercentages(
7070
+ estimateBeforeThisItem,
7071
+ currentItemWeight,
7072
+ progressWithinThisItem
7073
+ );
7074
+ }
7075
+ if (currentSpineIndex === readingOrderLength - 1 && pageIndex === numberOfPages - 1 && totalProgress > 0.99) {
7076
+ return 1;
7077
+ }
7078
+ return totalProgress;
7079
+ };
7080
+ const getTotalProgressFromPercentages = (estimateBeforeThisItem, currentItemWeight, progressWithinThisItem) => {
7081
+ return estimateBeforeThisItem + currentItemWeight * progressWithinThisItem;
7082
+ };
7083
+ const getScrollPercentageWithinItem = (context, currentPosition, currentItem) => {
7084
+ const { height, width } = currentItem.getElementDimensions();
7085
+ const { top, left } = reader.spine.spineLayout.getAbsolutePositionOf(currentItem);
7086
+ if (reader.settings.values.computedPageTurnDirection === `vertical`) {
7087
+ return Math.max(
7088
+ 0,
7089
+ Math.min(
7090
+ 1,
7091
+ (currentPosition.y - top + context.state.visibleAreaRect.height) / height
7092
+ )
7093
+ );
7094
+ } else {
7095
+ return Math.max(
7096
+ 0,
7097
+ Math.min(
7098
+ 1,
7099
+ (currentPosition.x - left + context.state.visibleAreaRect.width) / width
7100
+ )
7101
+ );
7102
+ }
7103
+ };
7104
+ return {
7105
+ ...reader,
7106
+ progression: {
7107
+ getPercentageEstimate,
7108
+ getScrollPercentageWithinItem
7109
+ }
7110
+ };
7111
+ };
7112
+ const accessibilityEnhancer = (next) => (options) => {
7113
+ const reader = next(options);
7114
+ const observer = new IntersectionObserver((entries) => {
7115
+ entries.forEach((entry) => {
7116
+ if (entry.isIntersecting) {
7117
+ entry.target.removeAttribute(`tab-index`);
7118
+ } else {
7119
+ entry.target.setAttribute(`tab-index`, `-1`);
7120
+ }
7121
+ });
7122
+ }, {});
7123
+ reader.hookManager.register(
7124
+ `item.onDocumentLoad`,
7125
+ ({ itemId, layers, destroy }) => {
7126
+ var _a, _b;
7127
+ const frame = (_a = layers[0]) == null ? void 0 : _a.element;
7128
+ if (!(frame instanceof HTMLIFrameElement)) return;
7129
+ const item = reader.spineItemsManager.get(itemId);
7130
+ if (!item) return;
7131
+ item.upsertCSS(
7132
+ `prose-reader-accessibility`,
7133
+ `
7134
+ :focus-visible {
7135
+ ${/*
7136
+ Some epubs remove the outline, this is not good practice since it reduce accessibility.
7137
+ We will try to restore it by force.
7138
+ */
7139
+ ``}
7140
+ outline: -webkit-focus-ring-color auto 1px;
7141
+ }
7142
+ `
7143
+ );
7144
+ const links = (_b = frame.contentDocument) == null ? void 0 : _b.body.querySelectorAll(`a`);
7145
+ links == null ? void 0 : links.forEach((link) => {
7146
+ observer.observe(link);
7147
+ });
7148
+ destroy(() => {
7149
+ links == null ? void 0 : links.forEach((link) => {
7150
+ observer.unobserve(link);
7151
+ });
7152
+ });
7153
+ }
7154
+ );
7155
+ return {
7156
+ ...reader
7590
7157
  };
7591
- cache$.asObservable().pipe(
7592
- mergeMap$1(({ id, data }) => {
7593
- const item = retrieveItem(id);
7594
- if (!item) return EMPTY;
7595
- return from(
7596
- forkJoin([openDatabase(`prose-reader`), from(data.blob())])
7597
- ).pipe(
7598
- switchMap$1(([db, blob]) => {
7599
- return from(db.put(`${uniqueID}_${item.id}`, blob));
7600
- }),
7601
- catchError$1((error) => {
7602
- Report.error(error);
7603
- return EMPTY;
7604
- })
7158
+ };
7159
+ navigator.userAgent.indexOf(``) > -1 && navigator.userAgent.indexOf(`Chrome`) <= -1;
7160
+ const webkitEnhancer = (createReader2) => (options) => {
7161
+ const reader = createReader2(options);
7162
+ return reader;
7163
+ };
7164
+ const HTML_PREFIX = `${HTML_PREFIX$1}-enhancer-loading`;
7165
+ const CONTAINER_HTML_PREFIX = `${HTML_PREFIX}-container`;
7166
+ const createLoadingElementContainer = (containerElement, context) => {
7167
+ const loadingElement = containerElement.ownerDocument.createElement(`div`);
7168
+ loadingElement.classList.add(CONTAINER_HTML_PREFIX);
7169
+ loadingElement.style.cssText = `
7170
+ height: 100%;
7171
+ width: 100%;
7172
+ max-width: ${context.state.visibleAreaRect.width}px;
7173
+ text-align: center;
7174
+ display: flex;
7175
+ justify-content: center;
7176
+ align-items: center;
7177
+ flex-direction: column;
7178
+ position: absolute;
7179
+ left: 0;
7180
+ top: 0;
7181
+ color: rgb(202, 202, 202);
7182
+ background-color: white;
7183
+ `;
7184
+ return loadingElement;
7185
+ };
7186
+ const defaultLoadingElementCreate = ({
7187
+ container,
7188
+ item
7189
+ }) => {
7190
+ const logoElement = container.ownerDocument.createElement(`div`);
7191
+ logoElement.innerText = `prose`;
7192
+ logoElement.style.cssText = `
7193
+ font-size: 4em;
7194
+ `;
7195
+ const detailsElement = container.ownerDocument.createElement(`div`);
7196
+ detailsElement.innerText = `loading ${item.id}`;
7197
+ detailsElement.style.cssText = `
7198
+ font-size: 1.2em;
7199
+ text-overflow: ellipsis;
7200
+ white-space: nowrap;
7201
+ overflow: hidden;
7202
+ max-width: 300px;
7203
+ width: 80%;
7204
+ `;
7205
+ container.appendChild(logoElement);
7206
+ container.appendChild(detailsElement);
7207
+ return container;
7208
+ };
7209
+ const loadingEnhancer = (next) => (options) => {
7210
+ const { loadingElementCreate = defaultLoadingElementCreate } = options;
7211
+ const reader = next(options);
7212
+ const createEntries$ = (items) => of(
7213
+ items.reduce((acc, { item, element }) => {
7214
+ const alreadyExistingElement = element.querySelector(
7215
+ `.${CONTAINER_HTML_PREFIX}`
7605
7216
  );
7606
- }),
7607
- takeUntil$1(context.destroy$)
7608
- ).subscribe();
7609
- const onLoad$ = context.manifest$.pipe(
7610
- tap(() => {
7611
- uniqueID = Date.now().toString();
7217
+ if (alreadyExistingElement instanceof HTMLElement)
7218
+ return {
7219
+ ...acc,
7220
+ [item.id]: alreadyExistingElement
7221
+ };
7222
+ const loadingElementContainer = loadingElementCreate({
7223
+ container: createLoadingElementContainer(element, reader.context),
7224
+ item
7225
+ });
7226
+ element.appendChild(loadingElementContainer);
7227
+ return {
7228
+ ...acc,
7229
+ [item.id]: loadingElementContainer
7230
+ };
7231
+ }, {})
7232
+ );
7233
+ const updateEntriesLayout$ = (entries) => combineLatest([reader.layout$, reader.theme.$.theme$]).pipe(
7234
+ map(([, theme]) => ({
7235
+ width: reader.context.state.visibleAreaRect.width,
7236
+ theme
7237
+ })),
7238
+ distinctUntilChanged(isShallowEqual),
7239
+ tap(({ width, theme }) => {
7240
+ Object.values(entries).forEach((element) => {
7241
+ element.style.setProperty(`max-width`, `${width}px`);
7242
+ element.style.setProperty(
7243
+ `color`,
7244
+ theme === `sepia` ? `#939393` : `rgb(202, 202, 202)`
7245
+ );
7246
+ });
7612
7247
  })
7613
7248
  );
7614
- merge(onLoad$).pipe(
7615
- switchMap$1(() => {
7616
- Report.log(`Cleanup up old cache...`);
7617
- return from(openDatabase(`prose-reader`)).pipe(
7618
- switchMap$1(
7619
- (db) => from(db.keys()).pipe(
7620
- map(
7621
- (keys) => keys.filter((key) => !key.toString().startsWith(uniqueID))
7622
- ),
7623
- switchMap$1((keysToRemove) => {
7624
- const promises = keysToRemove.map((key) => db.remove(key));
7625
- return from(Promise.all(promises));
7626
- })
7627
- )
7628
- ),
7629
- catchError$1((error) => {
7630
- Report.error(error);
7631
- return EMPTY;
7632
- })
7249
+ const updateEntriesVisibility$ = (entries) => reader.spineItemsObserver.itemIsReady$.pipe(
7250
+ tap(({ item, isReady }) => {
7251
+ var _a;
7252
+ (_a = entries[item.item.id]) == null ? void 0 : _a.style.setProperty(
7253
+ `visibility`,
7254
+ isReady ? `hidden` : `visible`
7633
7255
  );
7634
- }),
7635
- takeUntil$1(context.destroy$)
7256
+ })
7257
+ );
7258
+ const loadingItems$ = reader.spineItemsManager.items$.pipe(
7259
+ switchMap$1((items) => createEntries$(items)),
7260
+ shareReplay(1),
7261
+ takeUntil$1(reader.context.destroy$)
7262
+ );
7263
+ loadingItems$.pipe(
7264
+ switchMap$1(
7265
+ (entries) => merge(
7266
+ updateEntriesLayout$(entries),
7267
+ updateEntriesVisibility$(entries)
7268
+ )
7269
+ ),
7270
+ takeUntil$1(reader.$.destroy$)
7636
7271
  ).subscribe();
7637
- const destroy = () => {
7638
- cache$.complete();
7639
- };
7640
7272
  return {
7641
- get,
7642
- destroy
7273
+ ...reader,
7274
+ loading: {
7275
+ $: {
7276
+ items$: loadingItems$
7277
+ }
7278
+ }
7643
7279
  };
7644
7280
  };
7645
- const resourcesEnhancer = (next) => (options) => {
7646
- const reader = next(options);
7647
- const resourceManager = createResourcesManager(reader.context);
7648
- const destroy = () => {
7649
- resourceManager.destroy();
7650
- reader.destroy();
7651
- };
7281
+ const translateFramePositionIntoPage = ({
7282
+ position,
7283
+ frameElement,
7284
+ pageWidth,
7285
+ pageHeight
7286
+ }) => {
7287
+ const {
7288
+ height: viewportHeight = pageHeight,
7289
+ width: viewportWidth = pageWidth
7290
+ } = getFrameViewportInfo(frameElement);
7291
+ const computedWidthScale = pageWidth / viewportWidth;
7292
+ const computedScale = Math.min(
7293
+ computedWidthScale,
7294
+ pageHeight / viewportHeight
7295
+ );
7296
+ const { left = 0, top = 0 } = (frameElement == null ? void 0 : frameElement.getBoundingClientRect()) || {};
7297
+ const adjustedX = position.clientX * computedScale + left;
7298
+ const adjustedY = position.clientY * computedScale + top;
7652
7299
  return {
7653
- ...reader,
7654
- // $: {
7655
- // ...reader.$,
7656
- // errors$: merge(reader.$.errors$, errorsSubject$.asObservable())
7657
- // },
7658
- destroy
7659
- // load,
7300
+ clientX: adjustedX,
7301
+ clientY: adjustedY
7660
7302
  };
7661
7303
  };
7662
- const mediaEnhancer = (next) => (options) => {
7304
+ const normalizeEventForViewport = (event, iframeOriginalEvent, locator, context) => {
7305
+ var _a, _b;
7306
+ const originalFrame = (_a = iframeOriginalEvent == null ? void 0 : iframeOriginalEvent.view) == null ? void 0 : _a.frameElement;
7307
+ if (!iframeOriginalEvent || !originalFrame) return event;
7308
+ const spineItem = locator.getSpineItemFromIframe(originalFrame);
7309
+ const frameElement = (_b = spineItem == null ? void 0 : spineItem.renderer.layers[0]) == null ? void 0 : _b.element;
7310
+ const { height: pageHeight, width: pageWidth } = context.getPageSize();
7311
+ if (!spineItem || !(frameElement instanceof HTMLIFrameElement)) return event;
7312
+ if (isPointerEvent(event)) {
7313
+ const { clientX, clientY } = translateFramePositionIntoPage({
7314
+ position: event,
7315
+ frameElement,
7316
+ pageHeight,
7317
+ pageWidth
7318
+ });
7319
+ const newEvent = new PointerEvent(event.type, {
7320
+ ...event,
7321
+ pointerId: event.pointerId,
7322
+ clientX,
7323
+ clientY
7324
+ });
7325
+ Object.defineProperty(newEvent, `target`, {
7326
+ value: iframeOriginalEvent.target,
7327
+ enumerable: true
7328
+ });
7329
+ return newEvent;
7330
+ }
7331
+ if (isMouseEvent(event)) {
7332
+ const { clientX, clientY } = translateFramePositionIntoPage({
7333
+ position: event,
7334
+ frameElement,
7335
+ pageHeight,
7336
+ pageWidth
7337
+ });
7338
+ const newEvent = new MouseEvent(event.type, {
7339
+ ...event,
7340
+ clientX,
7341
+ clientY
7342
+ });
7343
+ Object.defineProperty(newEvent, `target`, {
7344
+ value: iframeOriginalEvent.target,
7345
+ enumerable: true
7346
+ });
7347
+ return newEvent;
7348
+ }
7349
+ if (isTouchEvent(event)) {
7350
+ const touches = Array.from(event.touches).map((touch) => {
7351
+ const { clientX, clientY } = translateFramePositionIntoPage({
7352
+ position: touch,
7353
+ frameElement,
7354
+ pageHeight,
7355
+ pageWidth
7356
+ });
7357
+ return new Touch({
7358
+ identifier: touch.identifier,
7359
+ target: touch.target,
7360
+ clientX,
7361
+ clientY
7362
+ });
7363
+ });
7364
+ const newEvent = new TouchEvent(event.type, {
7365
+ touches,
7366
+ changedTouches: touches,
7367
+ targetTouches: touches
7368
+ });
7369
+ Object.defineProperty(newEvent, `target`, {
7370
+ value: iframeOriginalEvent.target,
7371
+ enumerable: true
7372
+ });
7373
+ return newEvent;
7374
+ }
7375
+ return event;
7376
+ };
7377
+ const pointerEvents = [
7378
+ `pointercancel`,
7379
+ `pointerdown`,
7380
+ `pointerenter`,
7381
+ `pointerleave`,
7382
+ `pointermove`,
7383
+ `pointerout`,
7384
+ `pointerover`,
7385
+ `pointerup`
7386
+ ];
7387
+ const passthroughEvents = [
7388
+ ...pointerEvents
7389
+ /*, ...mouseEvents*/
7390
+ ];
7391
+ const eventsEnhancer = (next) => (options) => {
7663
7392
  const reader = next(options);
7664
- const frameObserver = new IntersectionObserver(
7665
- (entries) => {
7666
- entries.forEach((entry) => {
7667
- var _a;
7668
- const frame = entry.target;
7669
- const audios = Array.from(
7670
- ((_a = frame.contentDocument) == null ? void 0 : _a.body.getElementsByTagName(`audio`)) || []
7671
- );
7672
- if (!entry.isIntersecting) {
7673
- audios.forEach((audioElement) => {
7674
- audioElement.pause();
7675
- audioElement.currentTime = 0;
7676
- });
7677
- } else {
7678
- audios.forEach((audioElement) => {
7679
- if (audioElement.hasAttribute(`autoplay`) && audioElement.paused && audioElement.readyState >= 2) {
7680
- audioElement.play().catch(console.error);
7681
- }
7682
- });
7683
- }
7393
+ reader.hookManager.register(
7394
+ `item.onDocumentLoad`,
7395
+ ({ destroy, layers, itemId }) => {
7396
+ var _a;
7397
+ const frame = (_a = layers[0]) == null ? void 0 : _a.element;
7398
+ if (!(frame instanceof HTMLIFrameElement)) return;
7399
+ const item = reader.spineItemsManager.get(itemId);
7400
+ if (!item) return;
7401
+ const unregister = passthroughEvents.map((event) => {
7402
+ var _a2;
7403
+ const listener = (e) => {
7404
+ var _a3;
7405
+ let convertedEvent = e;
7406
+ if (isPointerEvent(e)) {
7407
+ convertedEvent = new PointerEvent(e.type, e);
7408
+ }
7409
+ if (convertedEvent !== e) {
7410
+ const normalizedEvent = normalizeEventForViewport(
7411
+ convertedEvent,
7412
+ e,
7413
+ reader.spine.locator,
7414
+ reader.context
7415
+ );
7416
+ (_a3 = reader.context.state.containerElement) == null ? void 0 : _a3.dispatchEvent(
7417
+ normalizedEvent
7418
+ );
7419
+ }
7420
+ };
7421
+ (_a2 = frame.contentDocument) == null ? void 0 : _a2.addEventListener(event, listener);
7422
+ return () => {
7423
+ var _a3;
7424
+ (_a3 = frame.contentDocument) == null ? void 0 : _a3.removeEventListener(event, listener);
7425
+ };
7426
+ });
7427
+ item.selectionTracker.track(frame);
7428
+ item.fingerTracker.track(frame);
7429
+ destroy(() => {
7430
+ unregister.forEach((cb) => cb());
7684
7431
  });
7685
- },
7686
- {
7687
- threshold: 0.01
7688
7432
  }
7689
7433
  );
7690
- const elementObserver = new IntersectionObserver(
7691
- (entries) => {
7692
- entries.forEach((entry) => {
7693
- if (entry.target.tagName === `video`) {
7694
- const video = entry.target;
7695
- if (!entry.isIntersecting) {
7696
- video.pause();
7697
- video.currentTime = 0;
7698
- } else {
7699
- if (video.hasAttribute(`autoplay`)) {
7700
- if (video.paused && video.readyState >= 2) {
7701
- video.play().catch(console.error);
7702
- }
7434
+ return reader;
7435
+ };
7436
+ const createFrameElement = Report.measurePerformance(
7437
+ `SpineItemFrame createFrame`,
7438
+ Infinity,
7439
+ () => {
7440
+ const frame = document.createElement(`iframe`);
7441
+ frame.frameBorder = `no`;
7442
+ frame.tabIndex = 0;
7443
+ frame.setAttribute(
7444
+ `sandbox`,
7445
+ `
7446
+ allow-same-origin
7447
+ allow-scripts
7448
+ allow-top-navigation-to-custom-protocols
7449
+ `
7450
+ );
7451
+ frame.style.cssText = `
7452
+ overflow: hidden;
7453
+ background-color: transparent;
7454
+ border: 0px none transparent;
7455
+ padding: 0px;
7456
+ `;
7457
+ frame.setAttribute(`role`, `main`);
7458
+ return frame;
7459
+ }
7460
+ );
7461
+ const getIntrinsicDimensionsFromBase64Img = (data) => new Promise((resolve, reject) => {
7462
+ const image = new Image();
7463
+ image.src = data;
7464
+ image.onload = () => {
7465
+ resolve({ height: image.naturalHeight, width: image.naturalWidth });
7466
+ };
7467
+ image.onerror = reject;
7468
+ });
7469
+ const createHtmlPageFromResource = async (resourceResponse, item) => {
7470
+ if (typeof resourceResponse === `string`) return resourceResponse;
7471
+ const contentType = parseContentType(resourceResponse.headers.get(`Content-Type`) || ``) || detectMimeTypeFromName(item.href);
7472
+ if ([`image/jpg`, `image/jpeg`, `image/png`, `image/webp`].some(
7473
+ (mime) => mime === contentType
7474
+ )) {
7475
+ const data = await getBase64FromBlob(await resourceResponse.blob());
7476
+ const { height, width } = await getIntrinsicDimensionsFromBase64Img(data);
7477
+ return `
7478
+ <html>
7479
+ <head>
7480
+ ${item.renditionLayout !== `reflowable` ? `<meta name="viewport" content="width=${width}, height=${height}">` : ``}
7481
+ </head>
7482
+ <body style="margin: 0px;" tab-index="-1;">
7483
+ <img
7484
+ src="${data}"
7485
+ style="max-width: 100%;height:100%;object-fit:contain;"
7486
+ >
7487
+ </body>
7488
+ </html>
7489
+ `;
7490
+ }
7491
+ if ([`text/plain`].some((mime) => mime === contentType)) {
7492
+ const data = await resourceResponse.text();
7493
+ return `
7494
+ <!DOCTYPE html>
7495
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
7496
+ <head>
7497
+ <style>
7498
+ pre {
7499
+ white-space: pre;
7500
+ white-space: pre-wrap;
7501
+ word-wrap: break-word;
7703
7502
  }
7503
+ </style>
7504
+ </head>
7505
+ <body>
7506
+ <pre>${data}</pre>
7507
+ </body>
7508
+ </html>
7509
+ `;
7510
+ }
7511
+ const content = await resourceResponse.text();
7512
+ return content;
7513
+ };
7514
+ const attachFrameSrc = ({
7515
+ item,
7516
+ resourcesHandler
7517
+ }) => {
7518
+ const getHtmlFromResource = (response) => createHtmlPageFromResource(response, item);
7519
+ return (stream) => stream.pipe(
7520
+ switchMap((frameElement) => {
7521
+ return from(resourcesHandler.getResource()).pipe(
7522
+ switchMap((resource) => {
7523
+ if (resource instanceof URL && item.href.startsWith(window.location.origin) && // we have an encoding and it's a valid html
7524
+ (item.mediaType && [
7525
+ `application/xhtml+xml`,
7526
+ `application/xml`,
7527
+ `text/html`,
7528
+ `text/xml`
7529
+ ].includes(item.mediaType) || // no encoding ? then try to detect html
7530
+ !item.mediaType && ITEM_EXTENSION_VALID_FOR_FRAME_SRC.some(
7531
+ (extension) => item.href.endsWith(extension)
7532
+ ))) {
7533
+ frameElement == null ? void 0 : frameElement.setAttribute(`src`, item.href);
7534
+ return of(frameElement);
7535
+ } else {
7536
+ const resourceResponse$ = resource instanceof URL ? from(resourcesHandler.fetchResource()) : resource instanceof Response ? of(resource) : EMPTY;
7537
+ return resourceResponse$.pipe(
7538
+ filter$1((response) => response instanceof Response),
7539
+ switchMap((response) => from(getHtmlFromResource(response))),
7540
+ tap$1((htmlDoc) => {
7541
+ if (htmlDoc) {
7542
+ const blob = new Blob([htmlDoc], { type: "text/html" });
7543
+ const blobURL = URL.createObjectURL(blob);
7544
+ frameElement == null ? void 0 : frameElement.setAttribute(`src`, blobURL);
7545
+ }
7546
+ }),
7547
+ map$1(() => frameElement),
7548
+ catchError$1((e) => {
7549
+ Report.error(
7550
+ `Error while trying to fetch or load resource for item ${item.id}`
7551
+ );
7552
+ console.error(e);
7553
+ return of(frameElement);
7554
+ })
7555
+ );
7704
7556
  }
7705
- }
7706
- });
7707
- },
7708
- {
7709
- threshold: 0.5
7710
- }
7557
+ })
7558
+ );
7559
+ })
7711
7560
  );
7712
- reader.hookManager.register(`item.onLoad`, ({ frame, destroy: destroy2 }) => {
7713
- var _a;
7714
- frameObserver.observe(frame);
7715
- const videos = (_a = frame.contentDocument) == null ? void 0 : _a.body.getElementsByTagName(`video`);
7716
- const unobserveElements = Array.from(videos || []).map((element) => {
7717
- elementObserver.observe(element);
7718
- return () => elementObserver.unobserve(element);
7719
- });
7720
- destroy2(() => {
7721
- frameObserver.unobserve(frame);
7722
- unobserveElements.forEach((unobserve) => unobserve());
7723
- });
7724
- });
7725
- const destroy = () => {
7726
- frameObserver.disconnect();
7727
- elementObserver.disconnect();
7728
- reader.destroy();
7729
- };
7730
- return {
7731
- ...reader,
7732
- destroy
7733
- };
7734
7561
  };
7735
- const progressionEnhancer = (next) => (options) => {
7736
- const reader = next(options);
7737
- const getPercentageEstimate = (context, currentSpineIndex, numberOfPages, pageIndex, currentPosition, currentItem) => {
7738
- var _a, _b, _c, _d, _e, _f;
7739
- const isGloballyPrePaginated = ((_a = context.manifest) == null ? void 0 : _a.renditionLayout) === `pre-paginated`;
7740
- const readingOrderLength = ((_b = context.manifest) == null ? void 0 : _b.spineItems.length) || 0;
7741
- const estimateBeforeThisItem = ((_c = context.manifest) == null ? void 0 : _c.spineItems.slice(0, currentSpineIndex).reduce((acc, item) => acc + (item.progressionWeight ?? 0), 0)) || 0;
7742
- const currentItemWeight = ((_e = (_d = context.manifest) == null ? void 0 : _d.spineItems[currentSpineIndex]) == null ? void 0 : _e.progressionWeight) || 0;
7743
- let progressWithinThisItem = (pageIndex + 1) * (currentItemWeight / numberOfPages);
7744
- if (!isGloballyPrePaginated && currentItem.item.renditionLayout === `reflowable` && !currentItem.isReady()) {
7745
- progressWithinThisItem = 0;
7562
+ const getStyleForViewportDocument = () => {
7563
+ return `
7564
+ body {
7565
+ margin: 0;
7566
+ }
7567
+ }
7568
+ `;
7569
+ };
7570
+ const getViewPortInformation = ({
7571
+ pageHeight,
7572
+ pageWidth,
7573
+ frameElement
7574
+ }) => {
7575
+ const viewportDimensions = getFrameViewportInfo(frameElement);
7576
+ if ((frameElement == null ? void 0 : frameElement.contentDocument) && frameElement.contentWindow && viewportDimensions) {
7577
+ const computedWidthScale = pageWidth / (viewportDimensions.width ?? 1);
7578
+ const computedScale = Math.min(
7579
+ computedWidthScale,
7580
+ pageHeight / (viewportDimensions.height ?? 1)
7581
+ );
7582
+ return { computedScale, computedWidthScale, viewportDimensions };
7583
+ }
7584
+ };
7585
+ const buildDocumentStyle = ({
7586
+ columnWidth,
7587
+ enableTouch,
7588
+ spreadPosition
7589
+ }, viewportDimensions) => {
7590
+ return `
7591
+ ${getStyleForViewportDocument()}
7592
+ body {
7593
+ ${!viewportDimensions ? `
7594
+ display: flex;
7595
+ justify-content: ${spreadPosition === `left` ? `flex-end` : spreadPosition === `right` ? `flex-start` : `center`};
7596
+ ` : ``}
7597
+ }
7598
+ ${/*
7599
+ might be html * but it does mess up things like figure if so.
7600
+ check accessible_epub_3
7601
+ */
7602
+ ``}
7603
+ html, body {
7604
+ height: 100%;
7605
+ width: 100%;
7606
+ }
7607
+ ${/*
7608
+ This one is important for preventing 100% img to resize above
7609
+ current width. Especially needed for cbz conversion
7610
+ */
7611
+ ``}
7612
+ html, body {
7613
+ -max-width: ${columnWidth}px !important;
7614
+ }
7615
+ ${/*
7616
+ * @see https://hammerjs.github.io/touch-action/
7617
+ * It needs to be disabled when using free scroll
7618
+ */
7619
+ ``}
7620
+ html, body {
7621
+ ${enableTouch ? `
7622
+ touch-action: none
7623
+ ` : ``}
7624
+ }
7625
+ ${/*
7626
+ prevent drag of image instead of touch on firefox
7627
+ */
7628
+ ``}
7629
+ img {
7630
+ user-select: none;
7631
+ -webkit-user-drag: none;
7632
+ -khtml-user-drag: none;
7633
+ -moz-user-drag: none;
7634
+ -o-user-drag: none;
7635
+ user-drag: none;
7636
+ ${/*
7637
+ prevent weird overflow or margin. Try `block` if `flex` has weird behavior
7638
+ */
7639
+ ``}
7640
+ display: flex;
7641
+ ${/*
7642
+ If the document does not have viewport, we cannot scale anything inside.
7643
+ This should never happens with a valid epub document however it will happens if
7644
+ we load .jpg, .png, etc directly in the iframe. This is expected, in this case we force
7645
+ the inner content to display correctly.
7646
+ */
7647
+ ``}
7648
+ ${!viewportDimensions ? `
7649
+ -width: 100%;
7650
+ max-width: 100%;
7651
+ height:100%;
7652
+ object-fit:contain;
7653
+ ` : ``}
7654
+ }
7655
+ `;
7656
+ };
7657
+ const getDimensionsForPaginatedContent = ({
7658
+ pageHeight: columnHeight,
7659
+ pageWidth
7660
+ }) => {
7661
+ const columnWidth = pageWidth;
7662
+ return { columnHeight, columnWidth };
7663
+ };
7664
+ const staticLayout$1 = (frameElement, size) => {
7665
+ frameElement.style.width = `${size.width}px`;
7666
+ frameElement.style.height = `${size.height}px`;
7667
+ };
7668
+ const renderPrePaginated = ({
7669
+ minPageSpread,
7670
+ blankPagePosition,
7671
+ spreadPosition,
7672
+ pageHeight,
7673
+ pageWidth,
7674
+ frameElement,
7675
+ isRTL,
7676
+ enableTouch
7677
+ }) => {
7678
+ const minimumWidth = minPageSpread * pageWidth;
7679
+ if ((frameElement == null ? void 0 : frameElement.contentDocument) && (frameElement == null ? void 0 : frameElement.contentWindow)) {
7680
+ const { viewportDimensions, computedScale = 1 } = getViewPortInformation({ frameElement, pageHeight, pageWidth }) ?? {};
7681
+ const contentWidth = pageWidth;
7682
+ const contentHeight = pageHeight;
7683
+ const cssLink = buildDocumentStyle(
7684
+ {
7685
+ ...getDimensionsForPaginatedContent({ pageHeight, pageWidth }),
7686
+ enableTouch,
7687
+ spreadPosition
7688
+ },
7689
+ viewportDimensions
7690
+ );
7691
+ upsertCSS(frameElement, `prose-reader-css`, cssLink);
7692
+ if (viewportDimensions) {
7693
+ staticLayout$1(frameElement, {
7694
+ width: viewportDimensions.width ?? 1,
7695
+ height: viewportDimensions.height ?? 1
7696
+ });
7697
+ } else {
7698
+ staticLayout$1(frameElement, {
7699
+ width: contentWidth,
7700
+ height: contentHeight
7701
+ });
7746
7702
  }
7747
- let totalProgress = estimateBeforeThisItem + progressWithinThisItem;
7748
- if (((_f = context.manifest) == null ? void 0 : _f.renditionFlow) === `scrolled-continuous`) {
7749
- if (currentItem.isReady()) {
7750
- progressWithinThisItem = getScrollPercentageWithinItem(
7751
- context,
7752
- currentPosition,
7753
- currentItem
7754
- );
7703
+ if (viewportDimensions) {
7704
+ frameElement == null ? void 0 : frameElement.style.setProperty(`position`, `absolute`);
7705
+ frameElement == null ? void 0 : frameElement.style.setProperty(`top`, `50%`);
7706
+ if (spreadPosition === `left`) {
7707
+ frameElement == null ? void 0 : frameElement.style.setProperty(`right`, `0`);
7708
+ frameElement == null ? void 0 : frameElement.style.removeProperty(`left`);
7709
+ } else if (blankPagePosition === `before` && isRTL) {
7710
+ frameElement == null ? void 0 : frameElement.style.setProperty(`right`, `50%`);
7711
+ frameElement == null ? void 0 : frameElement.style.removeProperty(`left`);
7712
+ } else if (spreadPosition === `right`) {
7713
+ frameElement == null ? void 0 : frameElement.style.setProperty(`left`, `0`);
7714
+ frameElement == null ? void 0 : frameElement.style.removeProperty(`right`);
7755
7715
  } else {
7756
- progressWithinThisItem = 0;
7716
+ frameElement == null ? void 0 : frameElement.style.setProperty(
7717
+ `left`,
7718
+ blankPagePosition === `before` ? isRTL ? `25%` : `75%` : blankPagePosition === `after` ? isRTL ? `75%` : `25%` : `50%`
7719
+ );
7720
+ frameElement == null ? void 0 : frameElement.style.removeProperty(`right`);
7757
7721
  }
7758
- totalProgress = getTotalProgressFromPercentages(
7759
- estimateBeforeThisItem,
7760
- currentItemWeight,
7761
- progressWithinThisItem
7722
+ const transformTranslateX = spreadPosition !== `none` ? `0` : `-50%`;
7723
+ const transformOriginX = spreadPosition === `right` && blankPagePosition !== `before` ? `left` : spreadPosition === `left` || blankPagePosition === `before` && isRTL ? `right` : `center`;
7724
+ frameElement == null ? void 0 : frameElement.style.setProperty(
7725
+ `transform`,
7726
+ `translate(${transformTranslateX}, -50%) scale(${computedScale})`
7762
7727
  );
7763
- }
7764
- if (currentSpineIndex === readingOrderLength - 1 && pageIndex === numberOfPages - 1 && totalProgress > 0.99) {
7765
- return 1;
7766
- }
7767
- return totalProgress;
7768
- };
7769
- const getTotalProgressFromPercentages = (estimateBeforeThisItem, currentItemWeight, progressWithinThisItem) => {
7770
- return estimateBeforeThisItem + currentItemWeight * progressWithinThisItem;
7771
- };
7772
- const getScrollPercentageWithinItem = (context, currentPosition, currentItem) => {
7773
- const { height, width } = currentItem.getElementDimensions();
7774
- const { top, left } = reader.spine.spineLayout.getAbsolutePositionOf(currentItem);
7775
- if (reader.settings.values.computedPageTurnDirection === `vertical`) {
7776
- return Math.max(
7777
- 0,
7778
- Math.min(
7779
- 1,
7780
- (currentPosition.y - top + context.state.visibleAreaRect.height) / height
7781
- )
7728
+ frameElement == null ? void 0 : frameElement.style.setProperty(
7729
+ `transform-origin`,
7730
+ `${transformOriginX} center`
7782
7731
  );
7783
7732
  } else {
7784
- return Math.max(
7785
- 0,
7786
- Math.min(
7787
- 1,
7788
- (currentPosition.x - left + context.state.visibleAreaRect.width) / width
7789
- )
7790
- );
7791
- }
7792
- };
7793
- return {
7794
- ...reader,
7795
- progression: {
7796
- getPercentageEstimate,
7797
- getScrollPercentageWithinItem
7733
+ if (blankPagePosition === `before`) {
7734
+ if (isRTL) {
7735
+ frameElement == null ? void 0 : frameElement.style.setProperty(`margin-right`, `${pageWidth}px`);
7736
+ } else {
7737
+ frameElement == null ? void 0 : frameElement.style.setProperty(`margin-left`, `${pageWidth}px`);
7738
+ }
7739
+ } else {
7740
+ frameElement == null ? void 0 : frameElement.style.removeProperty(`margin-left`);
7741
+ frameElement == null ? void 0 : frameElement.style.removeProperty(`margin-right`);
7742
+ }
7798
7743
  }
7799
- };
7744
+ return { width: minimumWidth, height: contentHeight };
7745
+ }
7746
+ return { width: minimumWidth, height: pageHeight };
7800
7747
  };
7801
- const accessibilityEnhancer = (next) => (options) => {
7802
- const reader = next(options);
7803
- const observer = new IntersectionObserver((entries) => {
7804
- entries.forEach((entry) => {
7805
- if (entry.isIntersecting) {
7806
- entry.target.removeAttribute(`tab-index`);
7807
- } else {
7808
- entry.target.setAttribute(`tab-index`, `-1`);
7748
+ const buildStyleForViewportFrame = () => {
7749
+ return `
7750
+ ${getStyleForViewportDocument()}
7751
+ html {
7752
+ width: 100%;
7753
+ height: 100%;
7809
7754
  }
7810
- });
7811
- }, {});
7812
- reader.hookManager.register(`item.onLoad`, ({ itemId, frame, destroy }) => {
7813
- var _a;
7814
- const item = reader.spineItemsManager.get(itemId);
7815
- if (!item) return;
7816
- item.frame.addStyle(
7817
- `prose-reader-accessibility`,
7818
- `
7819
- :focus-visible {
7820
- ${/*
7821
- Some epubs remove the outline, this is not good practice since it reduce accessibility.
7822
- We will try to restore it by force.
7823
- */
7824
- ``}
7825
- outline: -webkit-focus-ring-color auto 1px;
7826
- }
7827
- `
7828
- );
7829
- const links = (_a = frame.contentDocument) == null ? void 0 : _a.body.querySelectorAll(`a`);
7830
- links == null ? void 0 : links.forEach((link) => {
7831
- observer.observe(link);
7832
- });
7833
- destroy(() => {
7834
- links == null ? void 0 : links.forEach((link) => {
7835
- observer.unobserve(link);
7836
- });
7837
- });
7838
- });
7839
- return {
7840
- ...reader
7841
- };
7755
+ body {
7756
+ width: 100%;
7757
+ height: 100%;
7758
+ margin: 0;
7759
+ }
7760
+ ${/*
7761
+ * @see https://hammerjs.github.io/touch-action/
7762
+ */
7763
+ ``}
7764
+ html, body {
7765
+ touch-action: none;
7766
+ }
7767
+ `;
7842
7768
  };
7843
- navigator.userAgent.indexOf(``) > -1 && navigator.userAgent.indexOf(`Chrome`) <= -1;
7844
- const webkitEnhancer = (createReader2) => (options) => {
7845
- const reader = createReader2(options);
7846
- return reader;
7769
+ const buildStyleForReflowableImageOnly = ({
7770
+ isScrollable,
7771
+ enableTouch
7772
+ }) => {
7773
+ return `
7774
+ ${/*
7775
+ * @see https://hammerjs.github.io/touch-action/
7776
+ */
7777
+ ``}
7778
+ html, body {
7779
+ width: 100%;
7780
+ margin: 0;
7781
+ padding: 0;
7782
+ ${enableTouch ? `
7783
+ touch-action: none
7784
+ ` : ``}
7785
+ }
7786
+ ${isScrollable ? `
7787
+ img {
7788
+ height: auto !important;
7789
+ margin: 0;
7790
+ padding: 0;
7791
+ box-sizing: border-box;
7792
+ ${// we make sure img spread on entire screen
7793
+ ``}
7794
+ width: 100%;
7795
+ ${/**
7796
+ * line break issue
7797
+ * @see https://stackoverflow.com/questions/37869020/image-not-taking-up-the-full-height-of-container
7798
+ */
7799
+ ``}
7800
+ display: block;
7801
+ }
7802
+ ` : ``}
7803
+ `;
7847
7804
  };
7848
- const HTML_PREFIX = `${HTML_PREFIX$1}-enhancer-loading`;
7849
- const CONTAINER_HTML_PREFIX = `${HTML_PREFIX}-container`;
7850
- const loadingEnhancer = (next) => (options) => {
7851
- const { loadingElementCreate = defaultLoadingElementCreate } = options;
7852
- const reader = next(options);
7853
- const createEntries$ = (items) => of(
7854
- items.reduce((acc, { item, element }) => {
7855
- const alreadyExistingElement = element.querySelector(
7856
- `.${CONTAINER_HTML_PREFIX}`
7857
- );
7858
- if (alreadyExistingElement instanceof HTMLElement)
7859
- return {
7860
- ...acc,
7861
- [item.id]: alreadyExistingElement
7862
- };
7863
- const loadingElementContainer = loadingElementCreate({
7864
- container: createLoadingElementContainer(element, reader.context),
7865
- item
7866
- });
7867
- element.appendChild(loadingElementContainer);
7868
- return {
7869
- ...acc,
7870
- [item.id]: loadingElementContainer
7871
- };
7872
- }, {})
7873
- );
7874
- const updateEntriesLayout$ = (entries) => combineLatest([reader.layout$, reader.theme.$.theme$]).pipe(
7875
- map(([, theme]) => ({
7876
- width: reader.context.state.visibleAreaRect.width,
7877
- theme
7878
- })),
7879
- distinctUntilChanged(isShallowEqual),
7880
- tap(({ width, theme }) => {
7881
- Object.values(entries).forEach((element) => {
7882
- element.style.setProperty(`max-width`, `${width}px`);
7883
- element.style.setProperty(
7884
- `color`,
7885
- theme === `sepia` ? `#939393` : `rgb(202, 202, 202)`
7886
- );
7887
- });
7888
- })
7889
- );
7890
- const updateEntriesVisibility$ = (entries) => reader.spineItemsObserver.itemIsReady$.pipe(
7891
- tap(({ item, isReady }) => {
7892
- var _a;
7893
- (_a = entries[item.item.id]) == null ? void 0 : _a.style.setProperty(
7894
- `visibility`,
7895
- isReady ? `hidden` : `visible`
7896
- );
7897
- })
7898
- );
7899
- const loadingItems$ = reader.spineItemsManager.items$.pipe(
7900
- switchMap$1((items) => createEntries$(items)),
7901
- shareReplay(1),
7902
- takeUntil$1(reader.context.destroy$)
7903
- );
7904
- loadingItems$.pipe(
7905
- switchMap$1(
7906
- (entries) => merge(
7907
- updateEntriesLayout$(entries),
7908
- updateEntriesVisibility$(entries)
7909
- )
7910
- ),
7911
- takeUntil$1(reader.$.destroy$)
7912
- ).subscribe();
7913
- return {
7914
- ...reader,
7915
- loading: {
7916
- $: {
7917
- items$: loadingItems$
7805
+ const buildStyleWithMultiColumn = ({
7806
+ width,
7807
+ columnHeight,
7808
+ columnWidth
7809
+ }) => {
7810
+ return `
7811
+ parsererror {
7812
+ display: none !important;
7813
+ }
7814
+ ${/*
7815
+ might be html * but it does mess up things like figure if so.
7816
+ check accessible_epub_3
7817
+ */
7818
+ ``}
7819
+ html, body {
7820
+ margin: 0;
7821
+ padding: 0 !important;
7822
+ -max-width: ${columnWidth}px !important;
7823
+ }
7824
+ ${/*
7825
+ body {
7826
+ height: ${columnHeight}px !important;
7827
+ width: ${columnWidth}px !important;
7828
+ -margin-left: ${horizontalMargin}px !important;
7829
+ -margin-right: ${horizontalMargin}px !important;
7830
+ -margin: ${verticalMargin}px ${horizontalMargin}px !important;
7831
+ -padding-top: ${horizontalMargin}px !important;
7832
+ -padding-bottom: ${horizontalMargin}px !important;
7833
+ }
7834
+ */
7835
+ ``}
7836
+ body {
7837
+ padding: 0 !important;
7838
+ width: ${width}px !important;
7839
+ height: ${columnHeight}px !important;
7840
+ overflow-y: hidden;
7841
+ column-gap: 0px !important;
7842
+ column-width: ${columnWidth}px !important;
7843
+ column-fill: auto !important;
7844
+ word-wrap: break-word;
7845
+ box-sizing: border-box;
7846
+ }
7847
+ body {
7848
+ margin: 0;
7849
+ }
7850
+ body:focus-visible {
7851
+ ${/*
7852
+ we make sure that there are no outline when we focus something inside the iframe
7853
+ */
7854
+ ``}
7855
+ outline: none;
7856
+ }
7857
+ ${/*
7858
+ * @see https://hammerjs.github.io/touch-action/
7859
+ */
7860
+ ``}
7861
+ html, body {
7862
+ touch-action: none;
7863
+ }
7864
+ ${/*
7865
+ this messes up hard, be careful with this
7866
+ */
7867
+ ``}
7868
+ * {
7869
+ -max-width: ${columnWidth}px !important;
7870
+ }
7871
+ ${/*
7872
+ this is necessary to have a proper calculation when determining size
7873
+ of iframe content. If an img is using something like width:100% it would expand to
7874
+ the size of the original image and potentially gives back a wrong size (much larger)
7875
+ @see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Columns/Handling_Overflow_in_Multicol
7876
+ */
7877
+ ``}
7878
+ img, video, audio, object, svg {
7879
+ max-width: 100%;
7880
+ max-width: ${columnWidth}px !important;
7881
+ max-height: ${columnHeight}px !important;
7882
+ -pointer-events: none;
7883
+ -webkit-column-break-inside: avoid;
7884
+ page-break-inside: avoid;
7885
+ break-inside: avoid;
7886
+ }
7887
+ figure {
7888
+ d-max-width: ${columnWidth}px !important;
7889
+ }
7890
+ img {
7891
+ object-fit: contain;
7892
+ break-inside: avoid;
7893
+ box-sizing: border-box;
7894
+ d-max-width: ${columnWidth}px !important;
7918
7895
  }
7896
+ ${/*
7897
+ img, video, audio, object, svg {
7898
+ max-height: ${columnHeight}px !important;
7899
+ box-sizing: border-box;
7900
+ object-fit: contain;
7901
+ -webkit-column-break-inside: avoid;
7902
+ page-break-inside: avoid;
7903
+ break-inside: avoid;
7919
7904
  }
7905
+ */
7906
+ ``}
7907
+ table {
7908
+ max-width: ${columnWidth}px !important;
7909
+ table-layout: fixed;
7910
+ }
7911
+ td {
7912
+ max-width: ${columnWidth}px;
7913
+ }
7914
+ `;
7915
+ };
7916
+ const getDimensionsForReflowableContent = ({
7917
+ isUsingVerticalWriting,
7918
+ minimumWidth,
7919
+ pageHeight,
7920
+ pageWidth
7921
+ }) => {
7922
+ const horizontalMargin = 0;
7923
+ const verticalMargin = 0;
7924
+ let columnWidth = pageWidth - horizontalMargin * 2;
7925
+ const columnHeight = pageHeight - verticalMargin * 2;
7926
+ let width = pageWidth - horizontalMargin * 2;
7927
+ if (isUsingVerticalWriting) {
7928
+ width = minimumWidth - horizontalMargin * 2;
7929
+ columnWidth = columnHeight;
7930
+ }
7931
+ return {
7932
+ columnHeight,
7933
+ columnWidth,
7934
+ width
7920
7935
  };
7921
7936
  };
7922
- const createLoadingElementContainer = (containerElement, context) => {
7923
- const loadingElement = containerElement.ownerDocument.createElement(`div`);
7924
- loadingElement.classList.add(CONTAINER_HTML_PREFIX);
7925
- loadingElement.style.cssText = `
7926
- height: 100%;
7927
- width: 100%;
7928
- max-width: ${context.state.visibleAreaRect.width}px;
7929
- text-align: center;
7930
- display: flex;
7931
- justify-content: center;
7932
- align-items: center;
7933
- flex-direction: column;
7934
- position: absolute;
7935
- left: 0;
7936
- top: 0;
7937
- color: rgb(202, 202, 202);
7938
- -background-color: white;
7939
- `;
7940
- return loadingElement;
7937
+ const staticLayout = (frameElement, size) => {
7938
+ frameElement.style.width = `${size.width}px`;
7939
+ frameElement.style.height = `${size.height}px`;
7941
7940
  };
7942
- const defaultLoadingElementCreate = ({
7943
- container,
7944
- item
7941
+ const renderReflowable = ({
7942
+ pageHeight: pageSizeHeight,
7943
+ pageWidth,
7944
+ frameElement,
7945
+ manifest,
7946
+ latestContentHeightWhenLoaded,
7947
+ minPageSpread,
7948
+ isRTL,
7949
+ blankPagePosition,
7950
+ isImageType,
7951
+ enableTouch,
7952
+ isUsingVerticalWriting
7945
7953
  }) => {
7946
- const logoElement = container.ownerDocument.createElement(`div`);
7947
- logoElement.innerText = `prose`;
7948
- logoElement.style.cssText = `
7949
- font-size: 4em;
7950
- `;
7951
- const detailsElement = container.ownerDocument.createElement(`div`);
7952
- detailsElement.innerText = `loading ${item.id}`;
7953
- detailsElement.style.cssText = `
7954
- font-size: 1.2em;
7955
- text-overflow: ellipsis;
7956
- white-space: nowrap;
7957
- overflow: hidden;
7958
- max-width: 300px;
7959
- width: 80%;
7960
- `;
7961
- container.appendChild(logoElement);
7962
- container.appendChild(detailsElement);
7963
- return container;
7954
+ const minimumWidth = minPageSpread * pageWidth;
7955
+ let newLatestContentHeightWhenLoaded = latestContentHeightWhenLoaded;
7956
+ const continuousScrollableReflowableItem = (manifest == null ? void 0 : manifest.renditionLayout) === "reflowable" && (manifest == null ? void 0 : manifest.renditionFlow) === "scrolled-continuous";
7957
+ const pageHeight = continuousScrollableReflowableItem ? Math.min(400, pageSizeHeight) : pageSizeHeight;
7958
+ frameElement == null ? void 0 : frameElement.style.setProperty(`width`, `${pageWidth}px`);
7959
+ if (!continuousScrollableReflowableItem) {
7960
+ frameElement == null ? void 0 : frameElement.style.setProperty(`height`, `${pageHeight}px`);
7961
+ }
7962
+ const { viewportDimensions, computedScale = 1 } = getViewPortInformation({
7963
+ frameElement,
7964
+ pageHeight,
7965
+ pageWidth
7966
+ }) ?? {};
7967
+ const isGloballyPrePaginated = (manifest == null ? void 0 : manifest.renditionLayout) === `pre-paginated`;
7968
+ if ((frameElement == null ? void 0 : frameElement.contentDocument) && (frameElement == null ? void 0 : frameElement.contentWindow)) {
7969
+ let contentWidth = pageWidth;
7970
+ let contentHeight = pageHeight;
7971
+ frameElement == null ? void 0 : frameElement.style.setProperty(`visibility`, `visible`);
7972
+ frameElement == null ? void 0 : frameElement.style.setProperty(`opacity`, `1`);
7973
+ if (viewportDimensions == null ? void 0 : viewportDimensions.hasViewport) {
7974
+ upsertCSS(
7975
+ frameElement,
7976
+ `prose-reader-html-renderer-framce-css`,
7977
+ buildStyleForViewportFrame()
7978
+ );
7979
+ staticLayout(
7980
+ frameElement,
7981
+ {
7982
+ width: viewportDimensions.width ?? 1,
7983
+ height: viewportDimensions.height ?? 1
7984
+ }
7985
+ );
7986
+ frameElement == null ? void 0 : frameElement.style.setProperty(`position`, `absolute`);
7987
+ frameElement == null ? void 0 : frameElement.style.setProperty(`top`, `50%`);
7988
+ frameElement == null ? void 0 : frameElement.style.setProperty(
7989
+ `left`,
7990
+ blankPagePosition === `before` ? isRTL ? `25%` : `75%` : blankPagePosition === `after` ? isRTL ? `75%` : `25%` : `50%`
7991
+ );
7992
+ frameElement == null ? void 0 : frameElement.style.setProperty(
7993
+ `transform`,
7994
+ `translate(-50%, -50%) scale(${computedScale})`
7995
+ );
7996
+ frameElement == null ? void 0 : frameElement.style.setProperty(`transform-origin`, `center center`);
7997
+ } else {
7998
+ const frameStyle = isImageType ? buildStyleForReflowableImageOnly({
7999
+ isScrollable: (manifest == null ? void 0 : manifest.renditionFlow) === `scrolled-continuous`,
8000
+ enableTouch
8001
+ }) : buildStyleWithMultiColumn(
8002
+ getDimensionsForReflowableContent({
8003
+ isUsingVerticalWriting,
8004
+ minimumWidth,
8005
+ pageHeight,
8006
+ pageWidth
8007
+ })
8008
+ );
8009
+ upsertCSS(frameElement, `prose-reader-css`, frameStyle, true);
8010
+ if (isUsingVerticalWriting) {
8011
+ const pages = Math.ceil(
8012
+ frameElement.contentDocument.documentElement.scrollHeight / pageHeight
8013
+ );
8014
+ contentHeight = pages * pageHeight;
8015
+ staticLayout(
8016
+ frameElement,
8017
+ {
8018
+ width: minimumWidth,
8019
+ height: contentHeight
8020
+ }
8021
+ );
8022
+ } else if ((manifest == null ? void 0 : manifest.renditionFlow) === `scrolled-continuous`) {
8023
+ contentHeight = frameElement.contentDocument.body.scrollHeight;
8024
+ newLatestContentHeightWhenLoaded = contentHeight;
8025
+ staticLayout(
8026
+ frameElement,
8027
+ {
8028
+ width: minimumWidth,
8029
+ height: contentHeight
8030
+ }
8031
+ );
8032
+ } else {
8033
+ const pages = Math.ceil(
8034
+ frameElement.contentDocument.documentElement.scrollWidth / pageWidth
8035
+ );
8036
+ if (isGloballyPrePaginated) {
8037
+ contentWidth = pageWidth;
8038
+ } else {
8039
+ contentWidth = pages * pageWidth;
8040
+ }
8041
+ staticLayout(
8042
+ frameElement,
8043
+ {
8044
+ width: contentWidth,
8045
+ height: contentHeight
8046
+ }
8047
+ );
8048
+ }
8049
+ }
8050
+ const isFillingAllScreen = contentWidth % minimumWidth === 0;
8051
+ if (!isFillingAllScreen) {
8052
+ contentWidth = contentWidth + pageWidth;
8053
+ if (isRTL && !isUsingVerticalWriting) {
8054
+ frameElement == null ? void 0 : frameElement.style.setProperty(`margin-left`, `${pageWidth}px`);
8055
+ }
8056
+ } else {
8057
+ frameElement == null ? void 0 : frameElement.style.setProperty(`margin-left`, `0px`);
8058
+ }
8059
+ return { width: contentWidth, height: contentHeight };
8060
+ }
8061
+ const height = newLatestContentHeightWhenLoaded || pageHeight;
8062
+ return {
8063
+ width: minimumWidth,
8064
+ height,
8065
+ latestContentHeightWhenLoaded: newLatestContentHeightWhenLoaded
8066
+ };
7964
8067
  };
7965
- const normalizeEventForViewport = (event, iframeOriginalEvent, locator) => {
7966
- var _a;
7967
- const originalFrame = (_a = iframeOriginalEvent == null ? void 0 : iframeOriginalEvent.view) == null ? void 0 : _a.frameElement;
7968
- if (!iframeOriginalEvent || !originalFrame) return event;
7969
- const spineItem = locator.getSpineItemFromIframe(originalFrame);
7970
- if (!spineItem) return event;
7971
- if (isPointerEvent(event)) {
7972
- const { clientX, clientY } = spineItem.translateFramePositionIntoPage(event);
7973
- const newEvent = new PointerEvent(event.type, {
7974
- ...event,
7975
- pointerId: event.pointerId,
7976
- clientX,
7977
- clientY
7978
- });
7979
- Object.defineProperty(newEvent, `target`, {
7980
- value: iframeOriginalEvent.target,
7981
- enumerable: true
7982
- });
7983
- return newEvent;
8068
+ class HtmlRenderer extends DocumentRenderer {
8069
+ constructor(context, settings, hookManager, item, containerElement, resourcesHandler) {
8070
+ super(
8071
+ context,
8072
+ settings,
8073
+ hookManager,
8074
+ item,
8075
+ containerElement,
8076
+ resourcesHandler
8077
+ );
8078
+ this.isImageType = () => {
8079
+ const mimeType = this.item.mediaType ?? detectMimeTypeFromName(this.item.href);
8080
+ return !!(mimeType == null ? void 0 : mimeType.startsWith(`image/`));
8081
+ };
7984
8082
  }
7985
- if (isMouseEvent(event)) {
7986
- const { clientX, clientY } = spineItem.translateFramePositionIntoPage(event);
7987
- const newEvent = new MouseEvent(event.type, {
7988
- ...event,
7989
- clientX,
7990
- clientY
7991
- });
7992
- Object.defineProperty(newEvent, `target`, {
7993
- value: iframeOriginalEvent.target,
7994
- enumerable: true
7995
- });
7996
- return newEvent;
8083
+ onCreateDocument() {
8084
+ const frameElement = createFrameElement();
8085
+ this.layers = [
8086
+ {
8087
+ element: frameElement
8088
+ }
8089
+ ];
8090
+ return EMPTY;
8091
+ }
8092
+ onLoadDocument() {
8093
+ const frameElement = this.getFrameElement();
8094
+ if (!frameElement) throw new Error(`invalid frame`);
8095
+ return of(frameElement).pipe(
8096
+ attachFrameSrc({
8097
+ item: this.item,
8098
+ resourcesHandler: this.resourcesHandler,
8099
+ settings: this.settings
8100
+ }),
8101
+ waitForSwitch(this.context.bridgeEvent.viewportFree$),
8102
+ tap$1((frameElement2) => {
8103
+ this.containerElement.appendChild(frameElement2);
8104
+ }),
8105
+ waitForFrameLoad,
8106
+ waitForFrameReady
8107
+ );
7997
8108
  }
7998
- if (isTouchEvent(event)) {
7999
- const touches = Array.from(event.touches).map((touch) => {
8000
- const { clientX, clientY } = spineItem.translateFramePositionIntoPage(touch);
8001
- return new Touch({
8002
- identifier: touch.identifier,
8003
- target: touch.target,
8004
- clientX,
8005
- clientY
8109
+ onUnload() {
8110
+ this.layers.forEach((layer) => layer.element.remove());
8111
+ this.layers = [];
8112
+ return EMPTY;
8113
+ }
8114
+ layout({
8115
+ minPageSpread,
8116
+ blankPagePosition,
8117
+ spreadPosition
8118
+ }) {
8119
+ var _a;
8120
+ const { width: pageWidth, height: pageHeight } = this.context.getPageSize();
8121
+ const frameElement = this.getFrameElement();
8122
+ if (!frameElement) return { width: pageWidth, height: pageHeight };
8123
+ const isUsingVerticalWriting = !!((_a = this.writingMode) == null ? void 0 : _a.startsWith(`vertical`));
8124
+ if (this.item.renditionLayout === `pre-paginated`) {
8125
+ return renderPrePaginated({
8126
+ blankPagePosition,
8127
+ enableTouch: this.settings.values.computedPageTurnMode !== `scrollable`,
8128
+ frameElement,
8129
+ isRTL: this.context.isRTL(),
8130
+ minPageSpread,
8131
+ pageHeight,
8132
+ pageWidth,
8133
+ spreadPosition
8006
8134
  });
8135
+ }
8136
+ const { latestContentHeightWhenLoaded, ...rest } = renderReflowable({
8137
+ pageHeight,
8138
+ pageWidth,
8139
+ frameElement,
8140
+ manifest: this.context.manifest,
8141
+ blankPagePosition,
8142
+ isUsingVerticalWriting,
8143
+ isRTL: this.context.isRTL(),
8144
+ latestContentHeightWhenLoaded: this.latestContentHeightWhenLoaded,
8145
+ minPageSpread,
8146
+ isImageType: this.isImageType(),
8147
+ enableTouch: this.settings.values.computedPageTurnMode !== `scrollable`
8007
8148
  });
8008
- const newEvent = new TouchEvent(event.type, {
8009
- touches,
8010
- changedTouches: touches,
8011
- targetTouches: touches
8012
- });
8013
- Object.defineProperty(newEvent, `target`, {
8014
- value: iframeOriginalEvent.target,
8015
- enumerable: true
8016
- });
8017
- return newEvent;
8149
+ this.latestContentHeightWhenLoaded = latestContentHeightWhenLoaded;
8150
+ return rest;
8018
8151
  }
8019
- return event;
8020
- };
8021
- const pointerEvents = [
8022
- `pointercancel`,
8023
- `pointerdown`,
8024
- `pointerenter`,
8025
- `pointerleave`,
8026
- `pointermove`,
8027
- `pointerout`,
8028
- `pointerover`,
8029
- `pointerup`
8030
- ];
8031
- const passthroughEvents = [
8032
- ...pointerEvents
8033
- /*, ...mouseEvents*/
8034
- ];
8035
- const eventsEnhancer = (next) => (options) => {
8036
- const reader = next(options);
8037
- reader.hookManager.register(`item.onLoad`, ({ destroy, frame, itemId }) => {
8038
- const item = reader.spineItemsManager.get(itemId);
8039
- if (!item) return;
8040
- const unregister = passthroughEvents.map((event) => {
8152
+ getFrameElement() {
8153
+ var _a;
8154
+ const frame = (_a = this.layers[0]) == null ? void 0 : _a.element;
8155
+ if (!(frame instanceof HTMLIFrameElement)) return;
8156
+ return frame;
8157
+ }
8158
+ // @todo optimize
8159
+ getComputedStyleAfterLoad() {
8160
+ var _a, _b;
8161
+ const frame = this.getFrameElement();
8162
+ const body = (_a = frame == null ? void 0 : frame.contentDocument) == null ? void 0 : _a.body;
8163
+ if (body) {
8164
+ return (_b = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _b.getComputedStyle(body);
8165
+ }
8166
+ }
8167
+ get writingMode() {
8168
+ var _a;
8169
+ return (_a = this.getComputedStyleAfterLoad()) == null ? void 0 : _a.writingMode;
8170
+ }
8171
+ get readingDirection() {
8172
+ var _a;
8173
+ const writingMode = this.writingMode;
8174
+ if (writingMode === `vertical-rl`) {
8175
+ return `rtl`;
8176
+ }
8177
+ const direction = (_a = this.getComputedStyleAfterLoad()) == null ? void 0 : _a.direction;
8178
+ switch (direction) {
8179
+ case `ltr`:
8180
+ case `inherit`:
8181
+ case `initial`: {
8182
+ return `ltr`;
8183
+ }
8184
+ case `rtl`:
8185
+ return `rtl`;
8186
+ default:
8187
+ return void 0;
8188
+ }
8189
+ }
8190
+ }
8191
+ const htmlEnhancer = (next) => (options) => {
8192
+ const reader = next({
8193
+ ...options,
8194
+ getRenderer(item) {
8041
8195
  var _a;
8042
- const listener = (e) => {
8043
- var _a2;
8044
- let convertedEvent = e;
8045
- if (isPointerEvent(e)) {
8046
- convertedEvent = new PointerEvent(e.type, e);
8047
- }
8048
- if (convertedEvent !== e) {
8049
- const normalizedEvent = normalizeEventForViewport(
8050
- convertedEvent,
8051
- e,
8052
- reader.spine.locator
8053
- );
8054
- (_a2 = reader.context.state.containerElement) == null ? void 0 : _a2.dispatchEvent(
8055
- normalizedEvent
8056
- );
8057
- }
8058
- };
8059
- (_a = frame.contentDocument) == null ? void 0 : _a.addEventListener(event, listener);
8060
- return () => {
8061
- var _a2;
8062
- (_a2 = frame.contentDocument) == null ? void 0 : _a2.removeEventListener(event, listener);
8063
- };
8064
- });
8065
- item.selectionTracker.track(frame);
8066
- item.fingerTracker.track(frame);
8067
- destroy(() => {
8068
- unregister.forEach((cb) => cb());
8069
- });
8196
+ const MaybeRenderer = (_a = options.getRenderer) == null ? void 0 : _a.call(options, item);
8197
+ return MaybeRenderer ?? HtmlRenderer;
8198
+ }
8070
8199
  });
8071
8200
  return reader;
8072
8201
  };
@@ -8089,8 +8218,10 @@ const createReaderWithEnhancers = (
8089
8218
  paginationEnhancer(
8090
8219
  progressionEnhancer(
8091
8220
  eventsEnhancer(
8092
- // __
8093
- createReader
8221
+ htmlEnhancer(
8222
+ // __
8223
+ createReader
8224
+ )
8094
8225
  )
8095
8226
  )
8096
8227
  )
@@ -8110,8 +8241,10 @@ const createReaderWithEnhancers = (
8110
8241
  )
8111
8242
  );
8112
8243
  export {
8244
+ DocumentRenderer,
8113
8245
  HookManager,
8114
8246
  Report,
8247
+ ResourceHandler,
8115
8248
  SettingsManager3 as SettingsManager,
8116
8249
  createReaderWithEnhancers as createReader,
8117
8250
  isHtmlElement,