@prose-reader/core 1.130.0 → 1.132.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.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { takeUntil, Subject, combineLatest, switchMap, fromEvent, take, map as map$1, from, of, merge, EMPTY, withLatestFrom, NEVER, Observable, 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 as delay$1, identity, timer, skip as skip$1, exhaustMap, first as first$1, lastValueFrom, endWith, defer, reduce, concatMap, forkJoin, catchError as catchError$1 } from "rxjs";
2
- import { startWith, map, shareReplay, tap, pairwise, switchMap as switchMap$1, take as take$1, distinctUntilChanged, takeUntil as takeUntil$1, first, filter, delay, 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";
1
+ import { takeUntil, Subject, combineLatest, switchMap, fromEvent, take, map as map$1, from, of, merge, EMPTY, withLatestFrom, NEVER, Observable, scheduled, animationFrameScheduler, defer, distinctUntilChanged as distinctUntilChanged$1, tap as tap$1, throttleTime, finalize, startWith as startWith$1, debounceTime as debounceTime$1, BehaviorSubject, combineLatestWith, shareReplay as shareReplay$1, switchScan, forkJoin, scan, ReplaySubject, filter as filter$1, share, mergeMap, delay, identity, timer, skip as skip$1, exhaustMap, first as first$1, lastValueFrom, endWith, catchError, reduce, concatMap } from "rxjs";
2
+ import { startWith, map, shareReplay, tap, pairwise, switchMap as switchMap$1, take as take$1, distinctUntilChanged, takeUntil as takeUntil$1, first, filter, debounceTime, skip, withLatestFrom as withLatestFrom$1, share as share$1, mergeMap as mergeMap$1, catchError as catchError$1 } from "rxjs/operators";
3
+ import { shallowMergeIfDefined, isShallowEqual, detectMimeTypeFromName, parseContentType, getParentPath } 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);
@@ -406,6 +406,25 @@ const deferNextResult = (stream) => {
406
406
  return stream;
407
407
  };
408
408
  };
409
+ function idle() {
410
+ return new Observable((observer) => {
411
+ if (window.requestIdleCallback) {
412
+ const handle = window.requestIdleCallback(() => {
413
+ observer.next();
414
+ observer.complete();
415
+ });
416
+ return () => cancelIdleCallback(handle);
417
+ }
418
+ const timeout = setTimeout(() => {
419
+ observer.next();
420
+ observer.complete();
421
+ }, 1);
422
+ return () => clearTimeout(timeout);
423
+ });
424
+ }
425
+ function deferIdle(callback) {
426
+ return defer(() => idle().pipe(switchMap$1(callback)));
427
+ }
409
428
  const fixReflowable = (reader) => {
410
429
  reader.hookManager.register(
411
430
  `item.onAfterLayout`,
@@ -449,6 +468,8 @@ let SettingsManager$1 = class SettingsManager2 extends SettingsManagerOverload {
449
468
  pageHorizontalMargin,
450
469
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
451
470
  pageVerticalMargin,
471
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
472
+ layoutLayerTransition,
452
473
  ...rest
453
474
  } = settings;
454
475
  return rest;
@@ -457,18 +478,25 @@ let SettingsManager$1 = class SettingsManager2 extends SettingsManagerOverload {
457
478
  return {
458
479
  layoutAutoResize: "container",
459
480
  pageHorizontalMargin: 24,
460
- pageVerticalMargin: 24
481
+ pageVerticalMargin: 24,
482
+ layoutLayerTransition: true
461
483
  };
462
484
  }
463
485
  };
464
486
  const layoutEnhancer = (next) => (options) => {
465
- const { pageHorizontalMargin, pageVerticalMargin, layoutAutoResize } = options;
487
+ const {
488
+ pageHorizontalMargin,
489
+ pageVerticalMargin,
490
+ layoutAutoResize,
491
+ layoutLayerTransition
492
+ } = options;
466
493
  const reader = next(options);
467
494
  const settingsManager = new SettingsManager$1(
468
495
  {
469
496
  pageHorizontalMargin,
470
497
  pageVerticalMargin,
471
- layoutAutoResize
498
+ layoutAutoResize,
499
+ layoutLayerTransition
472
500
  },
473
501
  reader.settings
474
502
  );
@@ -532,7 +560,9 @@ const layoutEnhancer = (next) => (options) => {
532
560
  reader.hookManager.register(`item.onDocumentCreated`, ({ layers }) => {
533
561
  layers.forEach(({ element }) => {
534
562
  element.style.opacity = `0`;
535
- element.style.transition = `opacity 300ms`;
563
+ if (settingsManager.values.layoutLayerTransition) {
564
+ element.style.transition = `opacity 300ms`;
565
+ }
536
566
  });
537
567
  });
538
568
  reader.hookManager.register(`item.onBeforeLayout`, ({ item }) => {
@@ -545,11 +575,6 @@ const layoutEnhancer = (next) => (options) => {
545
575
  });
546
576
  const revealItemOnReady$ = reader.spineItemsObserver.itemIsReady$.pipe(
547
577
  filter(({ isReady }) => isReady),
548
- /**
549
- * Make sure that even if the document was loaded instantly, the layout is applied first
550
- * and therefore the transition played. eg: pdf are loaded before attached to the dom.
551
- */
552
- delay(1, animationFrameScheduler),
553
578
  tap(({ item }) => {
554
579
  item.renderer.layers.forEach(({ element }) => {
555
580
  element.style.opacity = `1`;
@@ -585,7 +610,22 @@ const layoutEnhancer = (next) => (options) => {
585
610
  }),
586
611
  takeUntil$1(reader.$.destroy$)
587
612
  ).subscribe();
588
- merge(revealItemOnReady$, movingSafePan$, layoutOnContainerResize$).pipe(takeUntil$1(reader.$.destroy$)).subscribe();
613
+ const updateSpineItemClassName$ = reader.spineItemsObserver.itemIsReady$.pipe(
614
+ tap(({ item, isReady }) => {
615
+ const className = `prose-spineItem-ready`;
616
+ if (isReady) {
617
+ item.containerElement.classList.add(className);
618
+ } else {
619
+ item.containerElement.classList.remove(className);
620
+ }
621
+ })
622
+ );
623
+ merge(
624
+ updateSpineItemClassName$,
625
+ revealItemOnReady$,
626
+ movingSafePan$,
627
+ layoutOnContainerResize$
628
+ ).pipe(takeUntil$1(reader.$.destroy$)).subscribe();
589
629
  return {
590
630
  ...reader,
591
631
  destroy: () => {
@@ -1474,17 +1514,116 @@ const trackPaginationInfo = (reader) => {
1474
1514
  return { paginationInfo$, getPaginationInfo: () => currentValue.value };
1475
1515
  };
1476
1516
  const NAMESPACE$6 = `paginationEnhancer`;
1517
+ const consolidate = (item, reader) => {
1518
+ var _a;
1519
+ let itemPageIndex = (_a = item.meta) == null ? void 0 : _a.itemPageIndex;
1520
+ const { itemIndex } = reader.cfi.parseCfi(item.cfi);
1521
+ const spineItem = reader.spineItemsManager.get(itemIndex);
1522
+ if (!spineItem) return of({ ...item, meta: { ...item.meta, itemIndex } });
1523
+ if (spineItem.item.renditionLayout === `pre-paginated`) {
1524
+ itemPageIndex = 0;
1525
+ }
1526
+ return idle().pipe(
1527
+ withLatestFrom(spineItem.isReady$),
1528
+ map$1(([, isSpineItemReady]) => {
1529
+ var _a2, _b, _c;
1530
+ let range = void 0;
1531
+ const { node: startNode, offset: startOffset } = reader.cfi.resolveCfi({ cfi: item.cfi }) ?? {};
1532
+ if (spineItem.item.renditionLayout !== `pre-paginated` && startNode) {
1533
+ itemPageIndex = reader.spine.locator.spineItemLocator.getSpineItemPageIndexFromNode(
1534
+ startNode,
1535
+ startOffset ?? 0,
1536
+ spineItem
1537
+ ) ?? itemPageIndex;
1538
+ }
1539
+ if (startNode && item.endCfi) {
1540
+ const { node: endNode, offset: endOffset } = reader.cfi.resolveCfi({ cfi: item.cfi }) ?? {};
1541
+ if (endNode && isSpineItemReady) {
1542
+ range = (_a2 = startNode == null ? void 0 : startNode.ownerDocument) == null ? void 0 : _a2.createRange();
1543
+ range == null ? void 0 : range.setStart(startNode, startOffset ?? 0);
1544
+ range == null ? void 0 : range.setEnd(endNode, endOffset ?? 0);
1545
+ }
1546
+ }
1547
+ let absolutePageIndex = (_b = item.meta) == null ? void 0 : _b.absolutePageIndex;
1548
+ if (itemPageIndex !== void 0) {
1549
+ absolutePageIndex = reader.spine.locator.getAbsolutePageIndexFromPageIndex({
1550
+ pageIndex: itemPageIndex,
1551
+ spineItemOrId: spineItem
1552
+ }) ?? ((_c = item.meta) == null ? void 0 : _c.absolutePageIndex);
1553
+ }
1554
+ return {
1555
+ ...item,
1556
+ meta: {
1557
+ ...item.meta,
1558
+ range,
1559
+ itemIndex,
1560
+ startNode,
1561
+ startOffset,
1562
+ absolutePageIndex,
1563
+ itemPageIndex
1564
+ }
1565
+ };
1566
+ })
1567
+ );
1568
+ };
1569
+ const createLocator = (reader) => (resources) => {
1570
+ return deferIdle(() => {
1571
+ if (!resources.length)
1572
+ return of({ isStale: false, data: resources });
1573
+ const StaleSymbol = Symbol("stale");
1574
+ const markStale$ = reader.layout$.pipe(
1575
+ startWith$1(null),
1576
+ map$1(() => StaleSymbol)
1577
+ );
1578
+ const consolidate$ = reader.layout$.pipe(
1579
+ debounceTime$1(10),
1580
+ startWith$1(null),
1581
+ switchScan((acc$) => {
1582
+ return forkJoin(
1583
+ acc$.map(
1584
+ (resource) => deferIdle(() => consolidate(resource, reader)).pipe(
1585
+ map$1((value) => value)
1586
+ )
1587
+ )
1588
+ );
1589
+ }, resources)
1590
+ );
1591
+ const run$ = merge(markStale$, consolidate$).pipe(
1592
+ scan(
1593
+ (acc, value) => {
1594
+ if (value === StaleSymbol) {
1595
+ return {
1596
+ ...acc,
1597
+ isStale: true
1598
+ };
1599
+ }
1600
+ return {
1601
+ isStale: false,
1602
+ data: value
1603
+ };
1604
+ },
1605
+ {
1606
+ isStale: true,
1607
+ data: resources
1608
+ }
1609
+ )
1610
+ );
1611
+ return run$;
1612
+ });
1613
+ };
1477
1614
  Report.namespace(NAMESPACE$6);
1478
1615
  const paginationEnhancer = (next) => (options) => {
1479
1616
  const reader = next(options);
1480
1617
  const { paginationInfo$, getPaginationInfo } = trackPaginationInfo(reader);
1481
1618
  paginationInfo$.pipe(takeUntil(reader.$.destroy$)).subscribe();
1619
+ const locate = createLocator(reader);
1482
1620
  return {
1483
1621
  ...reader,
1484
1622
  pagination: {
1485
1623
  ...reader.pagination,
1486
1624
  getState: () => getPaginationInfo(),
1487
- state$: paginationInfo$
1625
+ state$: paginationInfo$,
1626
+ locate
1488
1627
  }
1489
1628
  };
1490
1629
  };
@@ -3753,7 +3892,7 @@ class ViewportNavigator extends DestroyableClass {
3753
3892
  * anything for x ms while we effectively adjust. We want it to be immediate.
3754
3893
  * However when user is repeatedly turning page, we can improve smoothness by delaying a bit the adjustment
3755
3894
  */
3756
- currentEvent.shouldAnimate ? delay$1(1, animationFrameScheduler) : identity,
3895
+ currentEvent.shouldAnimate ? delay(1, animationFrameScheduler) : identity,
3757
3896
  tap$1((data) => {
3758
3897
  const element2 = this.viewportElement$.getValue();
3759
3898
  const noAdjustmentNeeded = false;
@@ -3789,7 +3928,7 @@ class ViewportNavigator extends DestroyableClass {
3789
3928
  this.setViewportPosition(data.position);
3790
3929
  }
3791
3930
  }),
3792
- currentEvent.shouldAnimate ? delay$1(animationDuration / 2, animationFrameScheduler) : identity,
3931
+ currentEvent.shouldAnimate ? delay(animationDuration / 2, animationFrameScheduler) : identity,
3793
3932
  tap$1((data) => {
3794
3933
  const element2 = this.viewportElement$.getValue();
3795
3934
  if (pageTurnAnimation === `fade`) {
@@ -3797,7 +3936,7 @@ class ViewportNavigator extends DestroyableClass {
3797
3936
  element2.style.setProperty(`opacity`, `1`);
3798
3937
  }
3799
3938
  }),
3800
- currentEvent.shouldAnimate ? delay$1(animationDuration / 2, animationFrameScheduler) : identity,
3939
+ currentEvent.shouldAnimate ? delay(animationDuration / 2, animationFrameScheduler) : identity,
3801
3940
  tap$1((data) => {
3802
3941
  if (pageTurnAnimation === `fade`) {
3803
3942
  this.setViewportPosition(data.position);
@@ -4382,19 +4521,6 @@ const withUrlInfo = ({ navigationResolver }) => (stream) => {
4382
4521
  })
4383
4522
  );
4384
4523
  };
4385
- const withPaginationInfo = () => (stream) => {
4386
- return stream.pipe(
4387
- map$1(({ navigation, pagination, ...rest }) => {
4388
- return {
4389
- navigation: {
4390
- ...navigation,
4391
- paginationBeginCfi: pagination.beginCfi
4392
- },
4393
- ...rest
4394
- };
4395
- })
4396
- );
4397
- };
4398
4524
  const withCfiPosition = ({ navigationResolver }) => (stream) => {
4399
4525
  return stream.pipe(
4400
4526
  map$1((params) => {
@@ -4490,6 +4616,52 @@ const withSpineItem = ({
4490
4616
  })
4491
4617
  );
4492
4618
  };
4619
+ const withPaginationInfo = () => (stream) => {
4620
+ return stream.pipe(
4621
+ map$1(({ navigation, pagination, ...rest }) => {
4622
+ return {
4623
+ navigation: {
4624
+ ...navigation,
4625
+ paginationBeginCfi: pagination.beginCfi
4626
+ },
4627
+ ...rest
4628
+ };
4629
+ })
4630
+ );
4631
+ };
4632
+ const consolidateWithPagination = (context, navigation$, spine) => context.bridgeEvent.pagination$.pipe(
4633
+ withLatestFrom(navigation$),
4634
+ filter$1(
4635
+ ([pagination, navigation]) => pagination.navigationId === navigation.id
4636
+ ),
4637
+ /**
4638
+ * We only register the pagination cfi IF the spine item is ready.
4639
+ * Otherwise we might save something incomplete and thus restore
4640
+ * the user to an invalid location.
4641
+ */
4642
+ switchMap(([pagination, navigation]) => {
4643
+ const spineItem = spine.spineItemsManager.get(navigation.spineItem);
4644
+ return ((spineItem == null ? void 0 : spineItem.isReady$.pipe(first$1())) ?? of(false)).pipe(
4645
+ filter$1((isReady) => isReady),
4646
+ map$1(() => ({
4647
+ pagination,
4648
+ navigation
4649
+ }))
4650
+ );
4651
+ }),
4652
+ withPaginationInfo(),
4653
+ distinctUntilChanged$1(
4654
+ (prev, curr) => prev.navigation.paginationBeginCfi === curr.navigation.paginationBeginCfi
4655
+ ),
4656
+ map$1(
4657
+ ({ navigation }) => ({
4658
+ ...navigation,
4659
+ meta: {
4660
+ triggeredBy: "pagination"
4661
+ }
4662
+ })
4663
+ )
4664
+ );
4493
4665
  const NAMESPACE$1 = `navigation/InternalNavigator`;
4494
4666
  const report = Report.namespace(NAMESPACE$1);
4495
4667
  class InternalNavigator extends DestroyableClass {
@@ -4613,7 +4785,12 @@ class InternalNavigator extends DestroyableClass {
4613
4785
  const navigationUpateFromLayout$ = layoutHasChanged$.pipe(
4614
4786
  switchMap(() => {
4615
4787
  return of(null).pipe(
4616
- switchMap(() => isUserLocked$.pipe(filter$1((isLocked) => !isLocked))),
4788
+ switchMap(
4789
+ () => isUserLocked$.pipe(
4790
+ filter$1((isLocked) => !isLocked),
4791
+ first$1()
4792
+ )
4793
+ ),
4617
4794
  map$1(
4618
4795
  () => ({
4619
4796
  ...this.navigationSubject.getValue(),
@@ -4678,29 +4855,10 @@ class InternalNavigator extends DestroyableClass {
4678
4855
  map$1(({ navigation }) => navigation),
4679
4856
  share()
4680
4857
  );
4681
- const navigationUpdateOnPaginationUpdate$ = context.bridgeEvent.pagination$.pipe(
4682
- withLatestFrom(this.navigationSubject),
4683
- filter$1(
4684
- ([pagination, navigation]) => pagination.navigationId === navigation.id
4685
- ),
4686
- map$1(([pagination, navigation]) => ({
4687
- pagination,
4688
- navigation: {
4689
- ...navigation
4690
- }
4691
- })),
4692
- withPaginationInfo(),
4693
- distinctUntilChanged$1(
4694
- (prev, curr) => prev.navigation.paginationBeginCfi === curr.navigation.paginationBeginCfi
4695
- ),
4696
- map$1(
4697
- ({ navigation }) => ({
4698
- ...navigation,
4699
- meta: {
4700
- triggeredBy: "pagination"
4701
- }
4702
- })
4703
- )
4858
+ const navigationUpdateOnPaginationUpdate$ = consolidateWithPagination(
4859
+ context,
4860
+ this.navigationSubject,
4861
+ spine
4704
4862
  );
4705
4863
  const navigationUpdate$ = merge(
4706
4864
  navigationRestored$,
@@ -5173,9 +5331,7 @@ class PaginationController extends DestroyableClass {
5173
5331
  })
5174
5332
  );
5175
5333
  const updateCfi$ = updatePagination$.pipe(
5176
- switchMap(() => this.context.bridgeEvent.viewportState$),
5177
- filter$1((state) => state === "free"),
5178
- switchMap(() => timer(500, animationFrameScheduler)),
5334
+ waitForSwitch(this.context.bridgeEvent.viewportFree$),
5179
5335
  tap$1(() => {
5180
5336
  const {
5181
5337
  beginSpineItemIndex,
@@ -5205,14 +5361,9 @@ class PaginationController extends DestroyableClass {
5205
5361
  merge(updatePagination$, updateCfi$).pipe(takeUntil(this.destroy$)).subscribe();
5206
5362
  }
5207
5363
  }
5208
- const generateCfiFromRange = ({
5209
- startNode,
5210
- start,
5211
- end,
5212
- endNode
5213
- }, item) => {
5214
- const startCFI = generateCfi(startNode, start, item);
5215
- const endCFI = generateCfi(endNode, end, item);
5364
+ const generateCfiFromRange = (range, item) => {
5365
+ const startCFI = generateCfi(range.startContainer, range.startOffset, item);
5366
+ const endCFI = generateCfi(range.endContainer, range.endOffset, item);
5216
5367
  return { start: startCFI, end: endCFI };
5217
5368
  };
5218
5369
  const defaultGetResource = (item) => new URL(item.href);
@@ -5342,6 +5493,29 @@ class DocumentRenderer extends DestroyableClass {
5342
5493
  unload() {
5343
5494
  this.triggerSubject.next({ type: `unload` });
5344
5495
  }
5496
+ renderHeadless() {
5497
+ const releaseSubject = new Subject();
5498
+ return defer(() => this.onRenderHeadless({ release: releaseSubject })).pipe(
5499
+ endWith(void 0),
5500
+ first$1(),
5501
+ map$1((doc) => {
5502
+ if (!doc) return void 0;
5503
+ return {
5504
+ doc,
5505
+ release: () => {
5506
+ releaseSubject.next(void 0);
5507
+ }
5508
+ };
5509
+ }),
5510
+ finalize(() => {
5511
+ releaseSubject.complete();
5512
+ }),
5513
+ catchError((e) => {
5514
+ Report.error(e);
5515
+ return of(void 0);
5516
+ })
5517
+ );
5518
+ }
5345
5519
  destroy() {
5346
5520
  super.destroy();
5347
5521
  this.stateSubject.complete();
@@ -5366,6 +5540,9 @@ class DefaultRenderer extends DocumentRenderer {
5366
5540
  onLayout() {
5367
5541
  return of(void 0);
5368
5542
  }
5543
+ onRenderHeadless() {
5544
+ return EMPTY;
5545
+ }
5369
5546
  }
5370
5547
  class SpineItem extends DestroyableClass {
5371
5548
  constructor(item, parentElement, context, settings, hookManager, index) {
@@ -5497,20 +5674,29 @@ class SpineItem extends DestroyableClass {
5497
5674
  }),
5498
5675
  share$1()
5499
5676
  );
5500
- this.layout$ = layoutProcess$.pipe(
5501
- filter((event) => event.type === `end`),
5502
- map((event) => event.data),
5503
- share$1()
5504
- );
5505
5677
  this.isReady$ = layoutProcess$.pipe(
5506
5678
  withLatestFrom$1(this.renderer.isLoaded$),
5507
5679
  map(([event, loaded]) => !!(event.type === `end` && loaded)),
5508
5680
  startWith(false),
5509
5681
  distinctUntilChanged(),
5510
- shareReplay({ refCount: true })
5682
+ shareReplay({ refCount: true, bufferSize: 1 })
5683
+ );
5684
+ this.layout$ = layoutProcess$.pipe(
5685
+ filter((event) => event.type === `end`),
5686
+ map((event) => event.data),
5687
+ share$1()
5511
5688
  );
5512
5689
  this.needsLayout$ = merge(this.unloaded$, this.loaded$);
5513
- merge(this.layout$, this.isReady$).pipe(takeUntil$1(this.destroy$)).subscribe();
5690
+ merge(
5691
+ /**
5692
+ * @important
5693
+ * The order is important here. We want to ensure the isReady value
5694
+ * is set before dispatching the layout event. Elements reacting
5695
+ * to layout changes may rely on the isReady value.
5696
+ */
5697
+ this.isReady$,
5698
+ this.layout$
5699
+ ).pipe(takeUntil$1(this.destroy$)).subscribe();
5514
5700
  }
5515
5701
  get element() {
5516
5702
  return this.containerElement;
@@ -6359,7 +6545,9 @@ class SpineLayout extends DestroyableClass {
6359
6545
  }),
6360
6546
  share()
6361
6547
  );
6362
- this.info$ = this.layout$.pipe(shareReplay$1({ refCount: true }));
6548
+ this.info$ = this.layout$.pipe(
6549
+ shareReplay$1({ refCount: true, bufferSize: 1 })
6550
+ );
6363
6551
  merge(this.layout$, this.info$).pipe(takeUntil(this.destroy$)).subscribe();
6364
6552
  }
6365
6553
  layout() {
@@ -6835,7 +7023,7 @@ const createResourcesManager = (context) => {
6835
7023
  switchMap$1(([db, blob]) => {
6836
7024
  return from(db.put(`${uniqueID}_${item.id}`, blob));
6837
7025
  }),
6838
- catchError((error) => {
7026
+ catchError$1((error) => {
6839
7027
  Report.error(error);
6840
7028
  return EMPTY;
6841
7029
  })
@@ -6863,7 +7051,7 @@ const createResourcesManager = (context) => {
6863
7051
  })
6864
7052
  )
6865
7053
  ),
6866
- catchError((error) => {
7054
+ catchError$1((error) => {
6867
7055
  Report.error(error);
6868
7056
  return EMPTY;
6869
7057
  })
@@ -6971,6 +7159,9 @@ class ImageRenderer extends DocumentRenderer {
6971
7159
  height
6972
7160
  });
6973
7161
  }
7162
+ onRenderHeadless() {
7163
+ return EMPTY;
7164
+ }
6974
7165
  }
6975
7166
  const mediaEnhancer = (next) => (options) => {
6976
7167
  const reader = next({
@@ -7209,6 +7400,7 @@ const createLoadingElementContainer = (containerElement, context) => {
7209
7400
  top: 0;
7210
7401
  color: rgb(202, 202, 202);
7211
7402
  background-color: white;
7403
+ z-index: 1;
7212
7404
  `;
7213
7405
  return loadingElement;
7214
7406
  };
@@ -7277,11 +7469,15 @@ const loadingEnhancer = (next) => (options) => {
7277
7469
  );
7278
7470
  const updateEntriesVisibility$ = (entries) => reader.spineItemsObserver.itemIsReady$.pipe(
7279
7471
  tap(({ item, isReady }) => {
7280
- var _a;
7472
+ var _a, _b;
7281
7473
  (_a = entries[item.item.id]) == null ? void 0 : _a.style.setProperty(
7282
7474
  `visibility`,
7283
7475
  isReady ? `hidden` : `visible`
7284
7476
  );
7477
+ (_b = entries[item.item.id]) == null ? void 0 : _b.style.setProperty(
7478
+ `z-index`,
7479
+ isReady ? `0` : `1`
7480
+ );
7285
7481
  })
7286
7482
  );
7287
7483
  const loadingItems$ = reader.spineItemsManager.items$.pipe(
@@ -7574,7 +7770,7 @@ const attachFrameSrc = ({
7574
7770
  }
7575
7771
  }),
7576
7772
  map$1(() => frameElement),
7577
- catchError$1((e) => {
7773
+ catchError((e) => {
7578
7774
  Report.error(
7579
7775
  `Error while trying to fetch or load resource for item ${item.id}`
7580
7776
  );
@@ -7997,8 +8193,6 @@ const renderReflowable = ({
7997
8193
  if ((frameElement == null ? void 0 : frameElement.contentDocument) && (frameElement == null ? void 0 : frameElement.contentWindow)) {
7998
8194
  let contentWidth = pageWidth;
7999
8195
  let contentHeight = pageHeight;
8000
- frameElement == null ? void 0 : frameElement.style.setProperty(`visibility`, `visible`);
8001
- frameElement == null ? void 0 : frameElement.style.setProperty(`opacity`, `1`);
8002
8196
  if (viewportDimensions == null ? void 0 : viewportDimensions.hasViewport) {
8003
8197
  upsertCSS(
8004
8198
  frameElement,
@@ -8094,6 +8288,51 @@ const renderReflowable = ({
8094
8288
  latestContentHeightWhenLoaded: newLatestContentHeightWhenLoaded
8095
8289
  };
8096
8290
  };
8291
+ const loadMedias = ({
8292
+ settings,
8293
+ item,
8294
+ context
8295
+ }) => (stream) => stream.pipe(
8296
+ switchMap((frameElement) => {
8297
+ var _a;
8298
+ const images = ((_a = frameElement.contentDocument) == null ? void 0 : _a.getElementsByTagName("img")) || [];
8299
+ const imageObservables = Array.from(images).map((img) => {
8300
+ var _a2;
8301
+ const originalSrc = img.getAttribute("src");
8302
+ const spineItemUriParentPath = getParentPath(item.href);
8303
+ const foundItem = (_a2 = context.manifest) == null ? void 0 : _a2.items.find(({ href }) => {
8304
+ return `${spineItemUriParentPath}/${originalSrc}`.endsWith(
8305
+ `${href}`
8306
+ );
8307
+ });
8308
+ if (!foundItem) return of(null);
8309
+ const resourceHandler = new ResourceHandler(foundItem, settings);
8310
+ return from(resourceHandler.getResource()).pipe(
8311
+ mergeMap(
8312
+ (resource) => resource instanceof Response ? from(resource.blob()) : of(void 0)
8313
+ ),
8314
+ tap$1((blob) => {
8315
+ if (blob) {
8316
+ const blobUrl = URL.createObjectURL(blob);
8317
+ img.src = blobUrl;
8318
+ }
8319
+ })
8320
+ );
8321
+ });
8322
+ return combineLatest(imageObservables).pipe(map$1(() => frameElement));
8323
+ })
8324
+ );
8325
+ const unloadMedias = (frameElement) => {
8326
+ var _a;
8327
+ const images = Array.from(
8328
+ ((_a = frameElement == null ? void 0 : frameElement.contentDocument) == null ? void 0 : _a.getElementsByTagName("img")) || []
8329
+ );
8330
+ images.forEach((img) => {
8331
+ if (img.src.startsWith("blob:")) {
8332
+ URL.revokeObjectURL(img.src);
8333
+ }
8334
+ });
8335
+ };
8097
8336
  class HtmlRenderer extends DocumentRenderer {
8098
8337
  constructor() {
8099
8338
  super(...arguments);
@@ -8125,10 +8364,16 @@ class HtmlRenderer extends DocumentRenderer {
8125
8364
  this.containerElement.appendChild(frameElement2);
8126
8365
  }),
8127
8366
  waitForFrameLoad,
8367
+ loadMedias({
8368
+ context: this.context,
8369
+ item: this.item,
8370
+ settings: this.settings
8371
+ }),
8128
8372
  waitForFrameReady
8129
8373
  );
8130
8374
  }
8131
8375
  onUnload() {
8376
+ unloadMedias(this.getFrameElement());
8132
8377
  this.layers.forEach((layer) => layer.element.remove());
8133
8378
  this.layers = [];
8134
8379
  return EMPTY;
@@ -8172,6 +8417,34 @@ class HtmlRenderer extends DocumentRenderer {
8172
8417
  this.latestContentHeightWhenLoaded = latestContentHeightWhenLoaded;
8173
8418
  return of(rest);
8174
8419
  }
8420
+ onRenderHeadless() {
8421
+ return from(this.resourcesHandler.fetchResource()).pipe(
8422
+ switchMap((resource) => {
8423
+ if (resource instanceof Response) {
8424
+ const contentType = resource.headers.get("content-type") ?? "";
8425
+ const parsableContentTypes = [
8426
+ `application/xhtml+xml`,
8427
+ `application/xml`,
8428
+ `text/html`,
8429
+ `text/xml`
8430
+ ];
8431
+ if (parsableContentTypes.includes(contentType)) {
8432
+ return from(resource.text()).pipe(
8433
+ map$1((text) => {
8434
+ const domParser = new DOMParser();
8435
+ const doc = domParser.parseFromString(
8436
+ text,
8437
+ contentType
8438
+ );
8439
+ return doc;
8440
+ })
8441
+ );
8442
+ }
8443
+ }
8444
+ return of(void 0);
8445
+ })
8446
+ );
8447
+ }
8175
8448
  getFrameElement() {
8176
8449
  var _a;
8177
8450
  const frame = (_a = this.layers[0]) == null ? void 0 : _a.element;
@@ -8248,7 +8521,7 @@ const getRangeFromSelection = (anchor, focus) => {
8248
8521
  }
8249
8522
  return range;
8250
8523
  };
8251
- const createRangeFromSelection = ({
8524
+ const createOrderedRangeFromSelection = ({
8252
8525
  selection,
8253
8526
  spineItem
8254
8527
  }) => {
@@ -8280,7 +8553,7 @@ class SelectionTracker extends DestroyableClass {
8280
8553
  * The selection is still valid during the event even if it will
8281
8554
  * be discarded. The timeout make sure to detect this edge case.
8282
8555
  */
8283
- delay$1(0),
8556
+ delay(0),
8284
8557
  map$1((event) => {
8285
8558
  const selection = document2.getSelection();
8286
8559
  return selection && !selection.isCollapsed ? [event, selection] : void 0;
@@ -8374,7 +8647,7 @@ const selectionEnhancer = (next) => (options) => {
8374
8647
  selectionAfterPointerUp$,
8375
8648
  lastSelectionOnPointerdown$,
8376
8649
  getSelection: () => selectionSubject.getValue(),
8377
- createRangeFromSelection
8650
+ createOrderedRangeFromSelection
8378
8651
  },
8379
8652
  destroy: () => {
8380
8653
  selectionSubject.complete();
@@ -8434,11 +8707,16 @@ export {
8434
8707
  SettingsManager3 as SettingsManager,
8435
8708
  SpineItem,
8436
8709
  createReaderWithEnhancers as createReader,
8710
+ deferIdle,
8711
+ deferNextResult,
8437
8712
  getAttributeValueFromString,
8438
8713
  getFrameViewportInfo,
8714
+ idle,
8439
8715
  injectCSS,
8440
8716
  isHtmlElement,
8441
8717
  isShallowEqual2 as isShallowEqual,
8718
+ mapKeysTo,
8719
+ observeResize,
8442
8720
  removeCSS,
8443
8721
  upsertCSS,
8444
8722
  waitForFrameLoad,