@prose-reader/core 1.140.0 → 1.142.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.
@@ -175,27 +175,43 @@ export declare const createReaderWithEnhancers: (options: Partial<import('./sett
175
175
  } & {
176
176
  selection: {
177
177
  selection$: import('rxjs').Observable<{
178
- document: Document;
178
+ itemIndex: number;
179
+ type: "change";
179
180
  selection: Selection;
181
+ } | {
180
182
  itemIndex: number;
183
+ type: "over";
184
+ event: Event;
185
+ selection: Selection;
181
186
  } | undefined>;
182
187
  selectionStart$: import('rxjs').Observable<boolean>;
183
188
  selectionEnd$: import('rxjs').Observable<void>;
184
- selectionOver$: import('rxjs').Observable<[Event, {
185
- document: Document;
189
+ selectionOver$: import('rxjs').Observable<{
190
+ itemIndex: number;
191
+ type: "over";
192
+ event: Event;
193
+ selection: Selection;
194
+ }>;
195
+ lastSelectionOnPointerdown$: import('rxjs').Observable<{
196
+ itemIndex: number;
197
+ type: "change";
186
198
  selection: Selection;
199
+ } | {
187
200
  itemIndex: number;
188
- } | undefined]>;
189
- lastSelectionOnPointerdown$: import('rxjs').Observable<({
190
- document: Document;
201
+ type: "over";
202
+ event: Event;
191
203
  selection: Selection;
204
+ } | undefined>;
205
+ getSelection: () => {
192
206
  itemIndex: number;
193
- } | undefined) | undefined>;
194
- getSelection: () => ({
195
- document: Document;
207
+ type: "change";
196
208
  selection: Selection;
209
+ } | {
197
210
  itemIndex: number;
198
- } | undefined) | undefined;
211
+ type: "over";
212
+ event: Event;
213
+ selection: Selection;
214
+ } | undefined;
199
215
  createOrderedRangeFromSelection: (params: {
200
216
  selection: {
201
217
  anchorNode?: Node | null;
@@ -1,7 +1,7 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { DestroyableClass } from '../../utils/DestroyableClass';
3
- export declare class SelectionTracker extends DestroyableClass {
3
+ export declare class FrameSelectionTracker extends DestroyableClass {
4
4
  selectionChange$: Observable<Selection | null>;
5
5
  selectionOver$: Observable<readonly [Event, Selection]>;
6
- constructor(document: Document);
6
+ constructor(frame: HTMLIFrameElement);
7
7
  }
@@ -1,19 +1,26 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { EnhancerOutput, RootEnhancer } from '../types/enhancer';
3
3
  import { SpineItem } from '../..';
4
- type SelectionValue = {
5
- document: Document;
4
+ type SelectionChange = {
5
+ itemIndex: number;
6
+ type: "change";
6
7
  selection: Selection;
8
+ };
9
+ type SelectionOver = {
7
10
  itemIndex: number;
8
- } | undefined;
11
+ type: "over";
12
+ event: Event;
13
+ selection: Selection;
14
+ };
15
+ type SelectionValue = SelectionChange | SelectionOver | undefined;
9
16
  export declare const selectionEnhancer: <InheritOptions, InheritOutput extends EnhancerOutput<RootEnhancer>>(next: (options: InheritOptions) => InheritOutput) => (options: InheritOptions) => InheritOutput & {
10
17
  selection: {
11
18
  selection$: Observable<SelectionValue>;
12
19
  selectionStart$: Observable<boolean>;
13
20
  selectionEnd$: Observable<void>;
14
- selectionOver$: Observable<[Event, SelectionValue]>;
15
- lastSelectionOnPointerdown$: Observable<SelectionValue | undefined>;
16
- getSelection: () => SelectionValue | undefined;
21
+ selectionOver$: Observable<SelectionOver>;
22
+ lastSelectionOnPointerdown$: Observable<SelectionValue>;
23
+ getSelection: () => SelectionValue;
17
24
  createOrderedRangeFromSelection: (params: {
18
25
  selection: {
19
26
  anchorNode?: Node | null;
@@ -0,0 +1,9 @@
1
+ import { SpineItem } from '../../spineItem/SpineItem';
2
+ export declare const trackSpineItemSelection: (spineItem: SpineItem) => import('rxjs').Observable<{
3
+ type: "change";
4
+ selection: Selection;
5
+ } | {
6
+ type: "over";
7
+ event: Event;
8
+ selection: Selection;
9
+ } | undefined>;
package/dist/index.js CHANGED
@@ -89,6 +89,15 @@ function idle() {
89
89
  function deferIdle(callback) {
90
90
  return defer(() => idle().pipe(switchMap(callback)));
91
91
  }
92
+ const observeMutation = (target, options) => {
93
+ return new Observable((subscriber) => {
94
+ const observer = new MutationObserver((mutations) => {
95
+ subscriber.next(mutations);
96
+ });
97
+ observer.observe(target, options);
98
+ return () => observer.disconnect();
99
+ });
100
+ };
92
101
  class SettingsManagerOverload {
93
102
  constructor(initialSettings, settingsManager) {
94
103
  this.settingsManager = settingsManager;
@@ -194,7 +203,7 @@ const getAttributeValueFromString = (string, key) => {
194
203
  };
195
204
  const injectCSS = (frameElement, id, style, prepend) => {
196
205
  if (frameElement && frameElement.contentDocument && frameElement.contentDocument.head) {
197
- const userStyle = document.createElement(`style`);
206
+ const userStyle = frameElement.contentDocument.createElement(`style`);
198
207
  userStyle.id = id;
199
208
  userStyle.innerHTML = style;
200
209
  if (prepend) {
@@ -212,7 +221,7 @@ const removeCSS = (frameElement, id) => {
212
221
  }
213
222
  }
214
223
  };
215
- const upsertCSS = (frameElement, id, style, prepend) => {
224
+ const upsertCSSToFrame = (frameElement, id, style, prepend) => {
216
225
  if (!frameElement) return;
217
226
  removeCSS(frameElement, id);
218
227
  injectCSS(frameElement, id, style, prepend);
@@ -293,7 +302,7 @@ const fontsEnhancer = (next) => (options) => {
293
302
  if (item.renditionLayout !== `pre-paginated`) {
294
303
  const frame = item.renderer.getDocumentFrame();
295
304
  if (frame) {
296
- upsertCSS(frame, `prose-reader-fonts`, getStyle());
305
+ upsertCSSToFrame(frame, `prose-reader-fonts`, getStyle());
297
306
  }
298
307
  }
299
308
  });
@@ -306,7 +315,7 @@ const fontsEnhancer = (next) => (options) => {
306
315
  if ((item == null ? void 0 : item.renditionLayout) !== `pre-paginated`) {
307
316
  const frame = item == null ? void 0 : item.renderer.getDocumentFrame();
308
317
  if (frame) {
309
- upsertCSS(frame, `prose-reader-fonts`, getStyle());
318
+ upsertCSSToFrame(frame, `prose-reader-fonts`, getStyle());
310
319
  }
311
320
  }
312
321
  });
@@ -531,7 +540,7 @@ const layoutEnhancer = (next) => (options) => {
531
540
  }
532
541
  const frame = spineItem == null ? void 0 : spineItem.renderer.getDocumentFrame();
533
542
  if (frame) {
534
- upsertCSS(
543
+ upsertCSSToFrame(
535
544
  frame,
536
545
  `prose-layout-enhancer-css`,
537
546
  `
@@ -1695,7 +1704,7 @@ const themeEnhancer = (next) => (options) => {
1695
1704
  reader.spineItemsManager.items.forEach((item) => {
1696
1705
  const frame = item.renderer.getDocumentFrame();
1697
1706
  if (frame) {
1698
- upsertCSS(frame, `prose-reader-theme`, getStyle());
1707
+ upsertCSSToFrame(frame, `prose-reader-theme`, getStyle());
1699
1708
  }
1700
1709
  applyChangeToSpineItemElement({ container: item.element });
1701
1710
  });
@@ -1705,7 +1714,7 @@ const themeEnhancer = (next) => (options) => {
1705
1714
  if ((item == null ? void 0 : item.renditionLayout) !== "pre-paginated") {
1706
1715
  const frame = item == null ? void 0 : item.renderer.getDocumentFrame();
1707
1716
  if (frame) {
1708
- upsertCSS(frame, `prose-reader-theme`, getStyle());
1717
+ upsertCSSToFrame(frame, `prose-reader-theme`, getStyle());
1709
1718
  }
1710
1719
  }
1711
1720
  });
@@ -5561,10 +5570,25 @@ class DocumentRenderer extends DestroyableClass {
5561
5570
  layout(params) {
5562
5571
  return defer(() => this.onLayout(params)).pipe(
5563
5572
  map$1((dims) => {
5564
- const { height, width } = dims ?? this.lastLayoutDims ?? { height: 0, width: 0 };
5565
- const minHeight = Math.max(height, this.context.getPageSize().height);
5566
- const minWidth = Math.max(width, params.minimumWidth);
5567
- this.lastLayoutDims = { height: minHeight, width: minWidth };
5573
+ var _a, _b;
5574
+ const isPrepaginated = this.item.renditionLayout === `pre-paginated` || !((_a = this.context.manifest) == null ? void 0 : _a.renditionLayout) && ((_b = this.context.manifest) == null ? void 0 : _b.renditionLayout) === `pre-paginated`;
5575
+ if (dims) {
5576
+ const { height, width } = dims;
5577
+ this.lastLayoutDims = { height, width };
5578
+ return this.lastLayoutDims;
5579
+ }
5580
+ if (isPrepaginated) {
5581
+ this.lastLayoutDims = {
5582
+ height: this.context.getPageSize().height,
5583
+ width: this.context.getPageSize().width
5584
+ };
5585
+ } else {
5586
+ this.lastLayoutDims = {
5587
+ height: this.context.getPageSize().height,
5588
+ width: this.context.getPageSize().width,
5589
+ ...this.lastLayoutDims
5590
+ };
5591
+ }
5568
5592
  return this.lastLayoutDims;
5569
5593
  })
5570
5594
  );
@@ -6514,7 +6538,7 @@ class SpineLayout extends DestroyableClass {
6514
6538
  layoutInProgress.next(true);
6515
6539
  const manifest = this.context.manifest;
6516
6540
  const newItemLayoutInformation = [];
6517
- const isGloballyPrePaginated = (manifest == null ? void 0 : manifest.renditionLayout) === `pre-paginated`;
6541
+ const isGloballyPrePaginated = isFullyPrePaginated(manifest) ?? false;
6518
6542
  return from(this.spineItemsManager.items).pipe(
6519
6543
  reduce(
6520
6544
  (acc$, item, index) => acc$.pipe(
@@ -7403,7 +7427,7 @@ const accessibilityEnhancer = (next) => (options) => {
7403
7427
  if (!item) return;
7404
7428
  const frame = item.renderer.getDocumentFrame();
7405
7429
  if (!frame) return;
7406
- upsertCSS(
7430
+ upsertCSSToFrame(
7407
7431
  frame,
7408
7432
  `prose-reader-accessibility`,
7409
7433
  `
@@ -7965,7 +7989,7 @@ const renderPrePaginated = ({
7965
7989
  },
7966
7990
  viewportDimensions
7967
7991
  );
7968
- upsertCSS(frameElement, `prose-reader-css`, cssLink);
7992
+ upsertCSSToFrame(frameElement, `prose-reader-css`, cssLink);
7969
7993
  if (viewportDimensions) {
7970
7994
  staticLayout$1(frameElement, {
7971
7995
  width: viewportDimensions.width ?? 1,
@@ -8246,7 +8270,7 @@ const renderReflowable = ({
8246
8270
  let contentWidth = pageWidth;
8247
8271
  let contentHeight = pageHeight;
8248
8272
  if (viewportDimensions == null ? void 0 : viewportDimensions.hasViewport) {
8249
- upsertCSS(
8273
+ upsertCSSToFrame(
8250
8274
  frameElement,
8251
8275
  `prose-reader-html-renderer-framce-css`,
8252
8276
  buildStyleForViewportFrame()
@@ -8281,7 +8305,7 @@ const renderReflowable = ({
8281
8305
  pageWidth
8282
8306
  })
8283
8307
  );
8284
- upsertCSS(frameElement, `prose-reader-css`, frameStyle, true);
8308
+ upsertCSSToFrame(frameElement, `prose-reader-css`, frameStyle, true);
8285
8309
  if (isUsingVerticalWriting) {
8286
8310
  const pages = Math.ceil(
8287
8311
  frameElement.contentDocument.documentElement.scrollHeight / pageHeight
@@ -8597,92 +8621,136 @@ const createOrderedRangeFromSelection = ({
8597
8621
  return void 0;
8598
8622
  }
8599
8623
  };
8600
- class SelectionTracker extends DestroyableClass {
8601
- constructor(document2) {
8624
+ class FrameSelectionTracker extends DestroyableClass {
8625
+ constructor(frame) {
8626
+ var _a;
8602
8627
  super();
8603
- this.selectionChange$ = fromEvent(document2, "selectionchange").pipe(
8604
- map$1(() => document2.getSelection())
8605
- );
8606
- this.selectionOver$ = fromEvent(document2, "pointerdown").pipe(
8607
- switchMap$1(
8608
- () => merge(
8609
- fromEvent(document2, "pointerup"),
8610
- fromEvent(document2, "pointercancel"),
8611
- fromEvent(document2, "contextmenu")
8612
- ).pipe(
8613
- first$1(),
8614
- /**
8615
- * The selection is still valid during the event even if it will
8616
- * be discarded. The timeout make sure to detect this edge case.
8617
- */
8618
- delay(0),
8619
- map$1((event) => {
8620
- const selection = document2.getSelection();
8621
- return selection && !selection.isCollapsed ? [event, selection] : void 0;
8622
- }),
8623
- filter$1(isDefined)
8628
+ const frameDoc = frame.contentDocument || ((_a = frame.contentWindow) == null ? void 0 : _a.document);
8629
+ if (!frameDoc) {
8630
+ this.selectionChange$ = NEVER;
8631
+ this.selectionOver$ = NEVER;
8632
+ } else {
8633
+ const frameDocMutation$ = observeMutation(frameDoc.body, {
8634
+ childList: true,
8635
+ subtree: true
8636
+ }).pipe(
8637
+ filter$1(
8638
+ (mutations) => !!mutations.find((mutation) => {
8639
+ return mutation.type === "childList" && mutation.removedNodes.length;
8640
+ })
8624
8641
  )
8625
- )
8626
- );
8642
+ );
8643
+ const iframeDestroyed$ = !frame.parentElement ? of(null) : observeMutation(frame.parentElement, {
8644
+ childList: true
8645
+ }).pipe(
8646
+ filter$1(
8647
+ (mutation) => !!mutation.find(
8648
+ (mutation2) => Array.from(mutation2.removedNodes).includes(frame)
8649
+ )
8650
+ )
8651
+ );
8652
+ this.selectionChange$ = merge(
8653
+ fromEvent(frameDoc, "selectionchange"),
8654
+ frameDocMutation$
8655
+ ).pipe(
8656
+ map$1(() => frameDoc.getSelection()),
8657
+ takeUntil(merge(iframeDestroyed$, this.destroy$)),
8658
+ endWith(null)
8659
+ );
8660
+ this.selectionOver$ = fromEvent(frameDoc, "pointerdown").pipe(
8661
+ switchMap$1(
8662
+ () => merge(
8663
+ fromEvent(frameDoc, "pointerup"),
8664
+ fromEvent(frameDoc, "pointercancel"),
8665
+ fromEvent(frameDoc, "contextmenu")
8666
+ ).pipe(
8667
+ first$1(),
8668
+ /**
8669
+ * The selection is still valid during the event even if it will
8670
+ * be discarded. The timeout make sure to detect this edge case.
8671
+ */
8672
+ delay(0),
8673
+ map$1((event) => {
8674
+ const selection = frameDoc.getSelection();
8675
+ return selection && !selection.isCollapsed ? [event, selection] : void 0;
8676
+ }),
8677
+ filter$1(isDefined)
8678
+ )
8679
+ ),
8680
+ takeUntil(merge(iframeDestroyed$, this.destroy$))
8681
+ );
8682
+ }
8627
8683
  }
8628
8684
  }
8685
+ const trackSpineItemSelection = (spineItem) => spineItem.loaded$.pipe(
8686
+ switchMap$1(() => {
8687
+ var _a;
8688
+ const frame = spineItem.renderer.getDocumentFrame();
8689
+ const frameDoc = (frame == null ? void 0 : frame.contentDocument) || ((_a = frame == null ? void 0 : frame.contentWindow) == null ? void 0 : _a.document);
8690
+ if (!frame || !frameDoc) return NEVER;
8691
+ const selectionTracker = new FrameSelectionTracker(frame);
8692
+ return merge(
8693
+ selectionTracker.selectionChange$.pipe(
8694
+ map$1((selection) => {
8695
+ if (selection == null ? void 0 : selection.toString()) {
8696
+ return {
8697
+ type: "change",
8698
+ selection
8699
+ };
8700
+ } else {
8701
+ return void 0;
8702
+ }
8703
+ })
8704
+ ),
8705
+ selectionTracker.selectionOver$.pipe(
8706
+ map$1(([event, selection]) => {
8707
+ return {
8708
+ type: "over",
8709
+ event,
8710
+ selection
8711
+ };
8712
+ })
8713
+ )
8714
+ ).pipe(
8715
+ takeUntil(spineItem.unloaded$),
8716
+ endWith(void 0),
8717
+ finalize(() => {
8718
+ selectionTracker.destroy();
8719
+ })
8720
+ );
8721
+ }),
8722
+ distinctUntilChanged$1()
8723
+ );
8629
8724
  const selectionEnhancer = (next) => (options) => {
8630
8725
  const reader = next(options);
8631
- const selectionSubject = new BehaviorSubject(
8632
- void 0
8633
- );
8634
- const selectionOverSubject = new Subject();
8635
- reader.hookManager.register(
8636
- `item.onDocumentLoad`,
8637
- ({ itemId, destroy$, destroy }) => {
8638
- var _a;
8639
- const item = reader.spineItemsManager.get(itemId);
8640
- const frame = item == null ? void 0 : item.renderer.getDocumentFrame();
8641
- const itemIndex = reader.spineItemsManager.getSpineItemIndex(itemId) ?? 0;
8642
- if (frame) {
8643
- const frameDoc = frame.contentDocument || ((_a = frame.contentWindow) == null ? void 0 : _a.document);
8644
- if (frameDoc) {
8645
- const selectionTracker = new SelectionTracker(frameDoc);
8646
- merge(
8647
- selectionTracker.selectionChange$.pipe(
8648
- tap$1((selection) => {
8649
- if (selection == null ? void 0 : selection.toString()) {
8650
- selectionSubject.next({
8651
- document: frameDoc,
8652
- selection,
8653
- itemIndex
8654
- });
8655
- } else {
8656
- selectionSubject.next(void 0);
8657
- }
8658
- })
8659
- ),
8660
- selectionTracker.selectionOver$.pipe(
8661
- tap$1(([event, selection]) => {
8662
- selectionOverSubject.next([
8663
- event,
8664
- {
8665
- document: frameDoc,
8666
- selection,
8667
- itemIndex
8668
- }
8669
- ]);
8670
- })
8671
- )
8672
- ).pipe(takeUntil(destroy$)).subscribe();
8673
- destroy(() => {
8674
- selectionTracker.destroy();
8675
- });
8676
- }
8677
- }
8678
- }
8679
- );
8680
- const selection$ = selectionSubject.pipe(
8726
+ let lasSelection = void 0;
8727
+ const trackedSelection$ = reader.spineItemsManager.items$.pipe(
8728
+ switchMap$1((spineItems) => {
8729
+ const instances = spineItems.map((spineItem) => {
8730
+ const itemIndex = reader.spineItemsManager.getSpineItemIndex(spineItem) ?? 0;
8731
+ return trackSpineItemSelection(spineItem).pipe(
8732
+ map$1((entry) => {
8733
+ if (!entry) return void 0;
8734
+ return {
8735
+ ...entry,
8736
+ itemIndex
8737
+ };
8738
+ })
8739
+ );
8740
+ });
8741
+ return merge(...instances);
8742
+ }),
8681
8743
  distinctUntilChanged$1(),
8682
- shareReplay$1(1),
8683
- takeUntil(reader.$.destroy$)
8744
+ tap$1((value) => {
8745
+ lasSelection = value;
8746
+ }),
8747
+ shareReplay$1({ refCount: true, bufferSize: 1 })
8684
8748
  );
8685
- const selectionStart$ = selectionSubject.pipe(
8749
+ const selection$ = trackedSelection$.pipe(
8750
+ filter$1((selection) => (selection == null ? void 0 : selection.type) === "change" || !selection),
8751
+ share()
8752
+ );
8753
+ const selectionStart$ = trackedSelection$.pipe(
8686
8754
  map$1((selection) => !!selection),
8687
8755
  distinctUntilChanged$1(),
8688
8756
  filter$1((isSelecting) => isSelecting),
@@ -8694,15 +8762,18 @@ const selectionEnhancer = (next) => (options) => {
8694
8762
  filter$1((selection) => !selection),
8695
8763
  share()
8696
8764
  );
8697
- const selectionOver$ = selectionOverSubject.asObservable();
8765
+ const selectionOver$ = trackedSelection$.pipe(
8766
+ filter$1((selection) => (selection == null ? void 0 : selection.type) === "over"),
8767
+ share()
8768
+ );
8698
8769
  const lastSelectionOnPointerdown$ = reader.context.containerElement$.pipe(
8699
8770
  switchMap$1((container) => fromEvent(container, "pointerdown")),
8700
8771
  withLatestFrom(selection$),
8701
8772
  map$1(([, selection]) => selection),
8702
8773
  startWith$1(void 0),
8703
- shareReplay$1(1),
8704
- takeUntil(reader.$.destroy$)
8774
+ shareReplay$1({ refCount: true, bufferSize: 1 })
8705
8775
  );
8776
+ merge(selection$, lastSelectionOnPointerdown$).pipe(takeUntil(reader.$.destroy$)).subscribe();
8706
8777
  return {
8707
8778
  ...reader,
8708
8779
  selection: {
@@ -8711,12 +8782,8 @@ const selectionEnhancer = (next) => (options) => {
8711
8782
  selectionEnd$,
8712
8783
  selectionOver$,
8713
8784
  lastSelectionOnPointerdown$,
8714
- getSelection: () => selectionSubject.getValue(),
8785
+ getSelection: () => lasSelection,
8715
8786
  createOrderedRangeFromSelection
8716
- },
8717
- destroy: () => {
8718
- selectionSubject.complete();
8719
- reader.destroy();
8720
8787
  }
8721
8788
  };
8722
8789
  };
@@ -8781,9 +8848,10 @@ export {
8781
8848
  isHtmlElement,
8782
8849
  isShallowEqual2 as isShallowEqual,
8783
8850
  mapKeysTo,
8851
+ observeMutation,
8784
8852
  observeResize,
8785
8853
  removeCSS,
8786
- upsertCSS,
8854
+ upsertCSSToFrame,
8787
8855
  waitForFrameLoad,
8788
8856
  waitForFrameReady,
8789
8857
  waitForSwitch