@prose-reader/core 1.127.0 → 1.129.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,5 +1,5 @@
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 as delay$1, 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, delay, debounceTime, skip, withLatestFrom as withLatestFrom$1, share as share$1, mergeMap as mergeMap$1, catchError } from "rxjs/operators";
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
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) => {
@@ -112,6 +112,78 @@ let SettingsManager$2 = class SettingsManager extends SettingsManagerOverload {
112
112
  };
113
113
  }
114
114
  };
115
+ const getAttributeValueFromString = (string, key) => {
116
+ const regExp = new RegExp(key + `\\s*=\\s*([0-9.]+)`, `i`);
117
+ const match = string.match(regExp) || [];
118
+ const firstMatch = match[1] || `0`;
119
+ return match && parseFloat(firstMatch) || 0;
120
+ };
121
+ const injectCSS = (frameElement, id, style, prepend) => {
122
+ if (frameElement && frameElement.contentDocument && frameElement.contentDocument.head) {
123
+ const userStyle = document.createElement(`style`);
124
+ userStyle.id = id;
125
+ userStyle.innerHTML = style;
126
+ if (prepend) {
127
+ frameElement.contentDocument.head.prepend(userStyle);
128
+ } else {
129
+ frameElement.contentDocument.head.appendChild(userStyle);
130
+ }
131
+ }
132
+ };
133
+ const removeCSS = (frameElement, id) => {
134
+ if (frameElement && frameElement.contentDocument && frameElement.contentDocument.head) {
135
+ const styleElement = frameElement.contentDocument.getElementById(id);
136
+ if (styleElement) {
137
+ styleElement.remove();
138
+ }
139
+ }
140
+ };
141
+ const upsertCSS = (frameElement, id, style, prepend) => {
142
+ if (!frameElement) return;
143
+ removeCSS(frameElement, id);
144
+ injectCSS(frameElement, id, style, prepend);
145
+ };
146
+ const getFrameViewportInfo = (frame) => {
147
+ if (frame && (frame == null ? void 0 : frame.contentDocument)) {
148
+ const doc = frame.contentDocument;
149
+ const viewportMetaElement = doc.querySelector(`meta[name='viewport']`);
150
+ if (viewportMetaElement) {
151
+ const viewportContent = viewportMetaElement.getAttribute(`content`);
152
+ if (viewportContent) {
153
+ const width = getAttributeValueFromString(viewportContent, `width`);
154
+ const height = getAttributeValueFromString(viewportContent, `height`);
155
+ if (width > 0 && height > 0) {
156
+ return {
157
+ hasViewport: true,
158
+ width,
159
+ height
160
+ };
161
+ } else {
162
+ return { hasViewport: true };
163
+ }
164
+ }
165
+ }
166
+ }
167
+ return { hasViewport: false };
168
+ };
169
+ const waitForFrameLoad = (stream) => stream.pipe(
170
+ switchMap(
171
+ (frame) => fromEvent(frame, `load`).pipe(
172
+ take(1),
173
+ map$1(() => frame)
174
+ )
175
+ )
176
+ );
177
+ const waitForFrameReady = (stream) => stream.pipe(
178
+ switchMap(
179
+ (frame) => {
180
+ var _a;
181
+ return from(((_a = frame == null ? void 0 : frame.contentDocument) == null ? void 0 : _a.fonts.ready) || of(void 0)).pipe(
182
+ map$1(() => frame)
183
+ );
184
+ }
185
+ )
186
+ );
115
187
  const fontsEnhancer = (next) => (options) => {
116
188
  const { fontScale, lineHeight, fontWeight, fontJustification } = options;
117
189
  const changes$ = new Subject();
@@ -144,9 +216,12 @@ const fontsEnhancer = (next) => (options) => {
144
216
  `;
145
217
  const applyChangeToSpineItems = (requireLayout) => {
146
218
  reader.spineItemsManager.items.forEach((item) => {
147
- var _a;
148
219
  if (item.item.renditionLayout !== `pre-paginated`) {
149
- (_a = item.upsertCSS) == null ? void 0 : _a.call(item, `prose-reader-fonts`, getStyle());
220
+ item.renderer.layers.forEach((layer) => {
221
+ if (layer.element instanceof HTMLIFrameElement) {
222
+ upsertCSS(layer.element, `prose-reader-fonts`, getStyle());
223
+ }
224
+ });
150
225
  }
151
226
  });
152
227
  if (requireLayout) {
@@ -156,7 +231,11 @@ const fontsEnhancer = (next) => (options) => {
156
231
  reader.hookManager.register(`item.onDocumentLoad`, ({ itemId }) => {
157
232
  const item = reader.spineItemsManager.get(itemId);
158
233
  if ((item == null ? void 0 : item.item.renditionLayout) !== `pre-paginated`) {
159
- item == null ? void 0 : item.upsertCSS(`prose-reader-fonts`, getStyle());
234
+ item == null ? void 0 : item.renderer.layers.forEach((layer) => {
235
+ if (layer.element instanceof HTMLIFrameElement) {
236
+ upsertCSS(layer.element, `prose-reader-fonts`, getStyle());
237
+ }
238
+ });
160
239
  }
161
240
  });
162
241
  const shouldRequireLayout = (source) => source.pipe(
@@ -266,7 +345,7 @@ const createMovingSafePan$ = (reader) => {
266
345
  );
267
346
  const resetLockViewportFree$ = createResetLock$(
268
347
  reader.viewportFree$
269
- ).pipe(take(1));
348
+ ).pipe(take$1(1));
270
349
  const pageTurnMode$ = reader.settings.values$.pipe(
271
350
  map(() => reader.settings.values.computedPageTurnMode),
272
351
  distinctUntilChanged()
@@ -314,78 +393,19 @@ const waitForSwitch = (waitForStream) => (stream) => stream.pipe(
314
393
  )
315
394
  )
316
395
  );
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
- }
396
+ const deferNextResult = (stream) => {
397
+ let value;
398
+ const sub = stream.subscribe((result) => {
399
+ value = { result };
400
+ });
401
+ return () => {
402
+ sub.unsubscribe();
403
+ if (value) {
404
+ return of(value.result);
367
405
  }
368
- }
369
- return { hasViewport: false };
406
+ return stream;
407
+ };
370
408
  };
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
- );
389
409
  const fixReflowable = (reader) => {
390
410
  reader.hookManager.register(
391
411
  `item.onAfterLayout`,
@@ -479,28 +499,33 @@ const layoutEnhancer = (next) => (options) => {
479
499
  columnWidth = columnHeight;
480
500
  columnGap = pageVerticalMargin2 * 2;
481
501
  }
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;
502
+ spineItem == null ? void 0 : spineItem.renderer.layers.forEach((layer) => {
503
+ if (layer.element instanceof HTMLIFrameElement) {
504
+ upsertCSS(
505
+ layer.element,
506
+ `prose-layout-enhancer-css`,
507
+ `
508
+ body {
509
+ width: ${width}px !important;
510
+ margin: ${pageVerticalMargin2}px ${pageHorizontalMargin2}px !important;
511
+ column-gap: ${columnGap}px !important;
512
+ column-width: ${columnWidth}px !important;
513
+ height: ${columnHeight}px !important;
514
+ }
515
+ img, video, audio, object, svg {
516
+ max-width: ${columnWidth}px !important;
517
+ max-height: ${columnHeight}px !important;
518
+ }
519
+ table {
520
+ max-width: ${columnWidth}px !important;
521
+ }
522
+ td {
523
+ max-width: ${columnWidth}px;
524
+ }
525
+ `
526
+ );
501
527
  }
502
- `
503
- );
528
+ });
504
529
  }
505
530
  });
506
531
  fixReflowable(reader);
@@ -1350,66 +1375,79 @@ const trackTotalPages = (reader) => {
1350
1375
  );
1351
1376
  return totalPages$;
1352
1377
  };
1353
- const mapPaginationInfoToExtendedInfo = (reader) => (paginationInfo, chaptersInfo) => {
1378
+ const mapPaginationInfoToExtendedInfo = (reader, paginationInfo, chaptersInfo) => {
1354
1379
  const context = reader.context;
1355
1380
  const beginItem = paginationInfo.beginSpineItemIndex !== void 0 ? reader.spineItemsManager.get(paginationInfo.beginSpineItemIndex) : void 0;
1356
1381
  const endItem = paginationInfo.endSpineItemIndex !== void 0 ? reader.spineItemsManager.get(paginationInfo.endSpineItemIndex) : void 0;
1357
- return {
1358
- ...paginationInfo,
1359
- beginChapterInfo: beginItem ? chaptersInfo[beginItem.item.id] : void 0,
1360
- // chapterIndex: number;
1361
- // pages: number;
1362
- // pageIndexInBook: number;
1363
- // pageIndexInChapter: number;
1364
- // pagesOfChapter: number;
1365
- // pagePercentageInChapter: number;
1366
- // offsetPercentageInChapter: number;
1367
- // domIndex: number;
1368
- // charOffset: number;
1369
- // serializeString?: string;
1370
- beginSpineItemReadingDirection: beginItem == null ? void 0 : beginItem.readingDirection,
1371
- endChapterInfo: endItem ? chaptersInfo[endItem.item.id] : void 0,
1372
- endSpineItemReadingDirection: endItem == null ? void 0 : endItem.readingDirection,
1373
- // spineItemReadingDirection: focusedSpineItem?.getReadingDirection(),
1374
- /**
1375
- * This percentage is based of the weight (kb) of every items and the number of pages.
1376
- * It is not accurate but gives a general good idea of the overall progress.
1377
- * It is recommended to use this progress only for reflow books. For pre-paginated books
1378
- * the number of pages and current index can be used instead since 1 page = 1 chapter.
1379
- */
1380
- percentageEstimateOfBook: endItem ? reader.progression.getPercentageEstimate(
1381
- context,
1382
- paginationInfo.endSpineItemIndex ?? 0,
1383
- paginationInfo.endNumberOfPagesInSpineItem,
1384
- paginationInfo.endPageIndexInSpineItem || 0,
1385
- reader.navigation.getNavigation().position,
1386
- endItem
1387
- ) : 0,
1388
- isUsingSpread: context.state.isUsingSpreadMode ?? false
1389
- // hasNextChapter: (reader.spine.spineItemIndex || 0) < (manifest.readingOrder.length - 1),
1390
- // hasPreviousChapter: (reader.spine.spineItemIndex || 0) < (manifest.readingOrder.length - 1),
1391
- // numberOfSpineItems: context.manifest?.readingOrder.length,
1392
- };
1382
+ const endItemProgression$ = endItem ? reader.progression.getPercentageEstimate(
1383
+ context,
1384
+ paginationInfo.endSpineItemIndex ?? 0,
1385
+ paginationInfo.endNumberOfPagesInSpineItem,
1386
+ paginationInfo.endPageIndexInSpineItem || 0,
1387
+ reader.navigation.getNavigation().position,
1388
+ endItem
1389
+ ) : of(0);
1390
+ return endItemProgression$.pipe(
1391
+ map$1((percentageEstimateOfBook) => ({
1392
+ ...paginationInfo,
1393
+ beginChapterInfo: beginItem ? chaptersInfo[beginItem.item.id] : void 0,
1394
+ // chapterIndex: number;
1395
+ // pages: number;
1396
+ // pageIndexInBook: number;
1397
+ // pageIndexInChapter: number;
1398
+ // pagesOfChapter: number;
1399
+ // pagePercentageInChapter: number;
1400
+ // offsetPercentageInChapter: number;
1401
+ // domIndex: number;
1402
+ // charOffset: number;
1403
+ // serializeString?: string;
1404
+ beginSpineItemReadingDirection: beginItem == null ? void 0 : beginItem.readingDirection,
1405
+ endChapterInfo: endItem ? chaptersInfo[endItem.item.id] : void 0,
1406
+ endSpineItemReadingDirection: endItem == null ? void 0 : endItem.readingDirection,
1407
+ // spineItemReadingDirection: focusedSpineItem?.getReadingDirection(),
1408
+ /**
1409
+ * This percentage is based of the weight (kb) of every items and the number of pages.
1410
+ * It is not accurate but gives a general good idea of the overall progress.
1411
+ * It is recommended to use this progress only for reflow books. For pre-paginated books
1412
+ * the number of pages and current index can be used instead since 1 page = 1 chapter.
1413
+ */
1414
+ percentageEstimateOfBook,
1415
+ isUsingSpread: context.state.isUsingSpreadMode ?? false
1416
+ // hasNextChapter: (reader.spine.spineItemIndex || 0) < (manifest.readingOrder.length - 1),
1417
+ // hasPreviousChapter: (reader.spine.spineItemIndex || 0) < (manifest.readingOrder.length - 1),
1418
+ // numberOfSpineItems: context.manifest?.readingOrder.length,
1419
+ }))
1420
+ );
1393
1421
  };
1394
1422
  const trackPaginationInfo = (reader) => {
1395
1423
  const chaptersInfo$ = trackChapterInfo(reader);
1396
1424
  const totalPages$ = trackTotalPages(reader);
1397
1425
  const currentValue = new BehaviorSubject({
1398
1426
  ...reader.pagination.getState(),
1399
- ...mapPaginationInfoToExtendedInfo(reader)(
1400
- reader.pagination.getState(),
1401
- getChaptersInfo(reader)
1402
- ),
1427
+ beginChapterInfo: void 0,
1428
+ beginCfi: void 0,
1429
+ beginPageIndexInSpineItem: void 0,
1430
+ isUsingSpread: false,
1403
1431
  beginAbsolutePageIndex: 0,
1404
1432
  endAbsolutePageIndex: 0,
1405
- numberOfTotalPages: 0
1433
+ numberOfTotalPages: 0,
1434
+ beginSpineItemReadingDirection: void 0,
1435
+ beginSpineItemIndex: void 0,
1436
+ endCfi: void 0,
1437
+ endChapterInfo: void 0,
1438
+ endSpineItemReadingDirection: void 0,
1439
+ percentageEstimateOfBook: 0
1406
1440
  });
1407
1441
  const extandedBasePagination$ = reader.pagination.state$.pipe(
1408
1442
  combineLatestWith(chaptersInfo$),
1409
- map$1(([info, chaptersInfo]) => ({
1410
- ...info,
1411
- ...mapPaginationInfoToExtendedInfo(reader)(info, chaptersInfo)
1412
- })),
1443
+ switchMap(
1444
+ ([info, chaptersInfo]) => mapPaginationInfoToExtendedInfo(reader, info, chaptersInfo).pipe(
1445
+ map$1((extendedInfo) => ({
1446
+ ...info,
1447
+ ...extendedInfo
1448
+ }))
1449
+ )
1450
+ ),
1413
1451
  distinctUntilChanged$1(isShallowEqual)
1414
1452
  );
1415
1453
  const paginationInfo$ = combineLatest([
@@ -1508,15 +1546,22 @@ const themeEnhancer = (next) => (options) => {
1508
1546
  };
1509
1547
  const applyChangeToSpineItem = () => {
1510
1548
  reader.spineItemsManager.items.forEach((item) => {
1511
- var _a;
1512
- (_a = item.upsertCSS) == null ? void 0 : _a.call(item, `prose-reader-theme`, getStyle());
1549
+ item.renderer.layers.forEach((layer) => {
1550
+ if (layer.element instanceof HTMLIFrameElement) {
1551
+ upsertCSS(layer.element, `prose-reader-theme`, getStyle());
1552
+ }
1553
+ });
1513
1554
  applyChangeToSpineItemElement({ container: item.element });
1514
1555
  });
1515
1556
  };
1516
1557
  reader.hookManager.register(`item.onDocumentLoad`, ({ itemId }) => {
1517
1558
  const item = reader.spineItemsManager.get(itemId);
1518
1559
  if ((item == null ? void 0 : item.item.renditionLayout) !== "pre-paginated") {
1519
- item == null ? void 0 : item.upsertCSS(`prose-reader-theme`, getStyle());
1560
+ item == null ? void 0 : item.renderer.layers.forEach((layer) => {
1561
+ if (layer.element instanceof HTMLIFrameElement) {
1562
+ upsertCSS(layer.element, `prose-reader-theme`, getStyle());
1563
+ }
1564
+ });
1520
1565
  }
1521
1566
  });
1522
1567
  reader.spineItemsManager.items$.pipe(
@@ -1923,7 +1968,8 @@ const isHtmlElement = (element) => {
1923
1968
  function createRangeOrCaretFromPoint(doc, startX, startY) {
1924
1969
  if (`caretPositionFromPoint` in doc) {
1925
1970
  return doc.caretPositionFromPoint(startX, startY);
1926
- } else if (typeof doc.caretRangeFromPoint !== `undefined`) {
1971
+ } else if ("caretRangeFromPoint" in doc && // @ts-expect-error limited availability
1972
+ typeof doc.caretRangeFromPoint !== `undefined`) {
1927
1973
  return doc.caretRangeFromPoint(startX, startY);
1928
1974
  }
1929
1975
  }
@@ -5070,7 +5116,7 @@ class PaginationController extends DestroyableClass {
5070
5116
  threshold: 0.5
5071
5117
  });
5072
5118
  return this.context.bridgeEvent.navigationUnlocked$.pipe(
5073
- take$1(1),
5119
+ take(1),
5074
5120
  withLatestFrom(this.context.bridgeEvent.navigation$),
5075
5121
  tap$1(([, navigation]) => {
5076
5122
  const { position } = navigation;
@@ -5169,14 +5215,35 @@ const generateCfiFromRange = ({
5169
5215
  const endCFI = generateCfi(endNode, end, item);
5170
5216
  return { start: startCFI, end: endCFI };
5171
5217
  };
5172
- class DocumentRenderer {
5218
+ const defaultGetResource = (item) => new URL(item.href);
5219
+ class ResourceHandler {
5220
+ constructor(item, settings) {
5221
+ this.item = item;
5222
+ this.settings = settings;
5223
+ }
5224
+ async getResource() {
5225
+ var _a, _b;
5226
+ const resource = await lastValueFrom(
5227
+ ((_b = (_a = this.settings.values).getResource) == null ? void 0 : _b.call(_a, this.item)) ?? of(void 0)
5228
+ );
5229
+ return resource ?? defaultGetResource(this.item);
5230
+ }
5231
+ async fetchResource() {
5232
+ const resource = await this.getResource();
5233
+ if (resource instanceof Response) return resource;
5234
+ if (resource instanceof URL) return fetch(resource);
5235
+ return resource;
5236
+ }
5237
+ }
5238
+ class DocumentRenderer extends DestroyableClass {
5173
5239
  constructor(params) {
5240
+ super();
5174
5241
  this.triggerSubject = new Subject();
5175
5242
  this.stateSubject = new BehaviorSubject(`idle`);
5176
5243
  this.unload$ = this.triggerSubject.pipe(
5177
5244
  withLatestFrom(this.stateSubject),
5178
5245
  filter$1(
5179
- ([trigger, state]) => trigger === `unload` && state !== "idle" && state !== "unloading"
5246
+ ([trigger, state]) => trigger.type === `unload` && state !== "idle" && state !== "unloading"
5180
5247
  ),
5181
5248
  map$1(() => void 0),
5182
5249
  share()
@@ -5184,21 +5251,26 @@ class DocumentRenderer {
5184
5251
  this.load$ = this.triggerSubject.pipe(
5185
5252
  withLatestFrom(this.stateSubject),
5186
5253
  filter$1(
5187
- ([trigger, state]) => trigger === `load` && state !== "ready" && state !== "loaded" && state !== "loading"
5254
+ ([trigger, state]) => trigger.type === `load` && state !== "loaded" && state !== "loading"
5188
5255
  ),
5189
5256
  map$1(() => void 0),
5190
5257
  share()
5191
5258
  );
5192
5259
  this.layers = [];
5193
- this.destroy$ = this.triggerSubject.pipe(
5194
- filter$1((trigger) => trigger === `destroy`)
5195
- );
5196
5260
  this.context = params.context;
5197
5261
  this.settings = params.settings;
5198
5262
  this.hookManager = params.hookManager;
5199
5263
  this.item = params.item;
5200
5264
  this.containerElement = params.containerElement;
5201
5265
  this.resourcesHandler = params.resourcesHandler;
5266
+ const unloadTrigger$ = this.triggerSubject.pipe(
5267
+ withLatestFrom(this.stateSubject),
5268
+ filter$1(
5269
+ ([trigger, state]) => trigger.type === `unload` && state !== "idle" && state !== "unloading"
5270
+ ),
5271
+ map$1(() => void 0),
5272
+ share()
5273
+ );
5202
5274
  this.load$.pipe(
5203
5275
  switchMap(() => {
5204
5276
  this.stateSubject.next(`loading`);
@@ -5232,13 +5304,12 @@ class DocumentRenderer {
5232
5304
  }),
5233
5305
  tap$1(() => {
5234
5306
  this.stateSubject.next(`loaded`);
5235
- this.stateSubject.next(`ready`);
5236
5307
  }),
5237
- takeUntil(merge(this.destroy$, this.unload$))
5308
+ takeUntil(merge(this.destroy$, unloadTrigger$))
5238
5309
  );
5239
5310
  })
5240
5311
  ).subscribe();
5241
- this.unload$.pipe(
5312
+ const unload$ = unloadTrigger$.pipe(
5242
5313
  switchMap(() => {
5243
5314
  this.stateSubject.next(`unloading`);
5244
5315
  return this.context.bridgeEvent.viewportFree$.pipe(
@@ -5247,29 +5318,32 @@ class DocumentRenderer {
5247
5318
  this.hookManager.destroy(`item.onDocumentLoad`, this.item.id);
5248
5319
  }),
5249
5320
  switchMap(() => {
5250
- const unload$ = this.onUnload().pipe(endWith(null), first$1());
5251
- return unload$;
5321
+ const onUnload$ = this.onUnload().pipe(endWith(null), first$1());
5322
+ return onUnload$;
5252
5323
  }),
5253
5324
  tap$1(() => {
5254
5325
  this.stateSubject.next(`idle`);
5255
5326
  }),
5256
- takeUntil(merge(this.destroy$, this.load$))
5327
+ takeUntil(this.load$)
5257
5328
  );
5258
5329
  })
5259
- ).subscribe();
5330
+ );
5331
+ merge(unload$).pipe(takeUntil(this.destroy$)).subscribe();
5260
5332
  }
5261
5333
  get state$() {
5262
5334
  return this.stateSubject;
5263
5335
  }
5336
+ get isLoaded$() {
5337
+ return this.state$.pipe(map$1((state) => state === `loaded`));
5338
+ }
5264
5339
  load() {
5265
- this.triggerSubject.next(`load`);
5340
+ this.triggerSubject.next({ type: `load` });
5266
5341
  }
5267
5342
  unload() {
5268
- this.triggerSubject.next(`unload`);
5343
+ this.triggerSubject.next({ type: `unload` });
5269
5344
  }
5270
5345
  destroy() {
5271
- this.triggerSubject.next(`destroy`);
5272
- this.triggerSubject.complete();
5346
+ super.destroy();
5273
5347
  this.stateSubject.complete();
5274
5348
  }
5275
5349
  get writingMode() {
@@ -5289,39 +5363,21 @@ class DefaultRenderer extends DocumentRenderer {
5289
5363
  onLoadDocument() {
5290
5364
  return EMPTY;
5291
5365
  }
5292
- layout() {
5293
- return void 0;
5294
- }
5295
- }
5296
- const defaultGetResource = (item) => new URL(item.href);
5297
- class ResourceHandler {
5298
- constructor(item, settings) {
5299
- this.item = item;
5300
- this.settings = settings;
5301
- }
5302
- async getResource() {
5303
- var _a, _b;
5304
- const resource = await lastValueFrom(
5305
- ((_b = (_a = this.settings.values).getResource) == null ? void 0 : _b.call(_a, this.item)) ?? of(void 0)
5306
- );
5307
- return resource ?? defaultGetResource(this.item);
5308
- }
5309
- async fetchResource() {
5310
- const resource = await this.getResource();
5311
- if (resource instanceof Response) return resource;
5312
- if (resource instanceof URL) return fetch(resource);
5313
- return resource;
5366
+ onLayout() {
5367
+ return of(void 0);
5314
5368
  }
5315
5369
  }
5316
- class SpineItem {
5370
+ class SpineItem extends DestroyableClass {
5317
5371
  constructor(item, parentElement, context, settings, hookManager, index) {
5318
5372
  var _a, _b;
5373
+ super();
5319
5374
  this.item = item;
5320
5375
  this.parentElement = parentElement;
5321
5376
  this.context = context;
5322
5377
  this.settings = settings;
5323
5378
  this.hookManager = hookManager;
5324
5379
  this.index = index;
5380
+ this.layoutTriggerSubject = new Subject();
5325
5381
  this.adjustPositionOfElement = ({
5326
5382
  right,
5327
5383
  left,
@@ -5353,31 +5409,10 @@ class SpineItem {
5353
5409
  return (_e = (_d = frameElement.contentDocument) == null ? void 0 : _d.querySelector(selector)) == null ? void 0 : _e.getBoundingClientRect();
5354
5410
  }
5355
5411
  };
5356
- this.layout = ({
5357
- blankPagePosition,
5358
- minimumWidth,
5359
- spreadPosition
5360
- }) => {
5361
- this.hookManager.execute(`item.onBeforeLayout`, void 0, {
5362
- blankPagePosition,
5363
- item: this.item,
5364
- minimumWidth
5365
- });
5366
- const { height, width } = this.renderer.layout({
5367
- blankPagePosition,
5368
- minPageSpread: minimumWidth / this.context.getPageSize().width,
5369
- spreadPosition
5370
- }) ?? { width: 0, height: 0 };
5371
- const minHeight = Math.max(height, this.context.getPageSize().height);
5372
- const minWidth = Math.max(width, minimumWidth);
5373
- this.containerElement.style.width = `${minWidth}px`;
5374
- this.containerElement.style.height = `${minHeight}px`;
5375
- this.hookManager.execute(`item.onAfterLayout`, void 0, {
5376
- blankPagePosition,
5377
- item: this.item,
5378
- minimumWidth
5379
- });
5380
- return { width: minWidth, height: minHeight };
5412
+ this.layout = (params) => {
5413
+ const nextResult = deferNextResult(this.layout$.pipe(first()));
5414
+ this.layoutTriggerSubject.next(params);
5415
+ return nextResult();
5381
5416
  };
5382
5417
  this.load = () => this.renderer.load();
5383
5418
  this.unload = () => {
@@ -5395,16 +5430,14 @@ class SpineItem {
5395
5430
  return normalizedValues;
5396
5431
  };
5397
5432
  this.destroy = () => {
5398
- this.destroySubject$.next();
5433
+ super.destroy();
5399
5434
  this.containerElement.remove();
5400
- this.destroySubject$.complete();
5401
5435
  this.renderer.destroy();
5402
5436
  };
5403
5437
  this.isUsingVerticalWriting = () => {
5404
5438
  var _a2;
5405
5439
  return !!((_a2 = this.renderer.writingMode) == null ? void 0 : _a2.startsWith(`vertical`));
5406
5440
  };
5407
- this.destroySubject$ = new Subject();
5408
5441
  this.containerElement = createContainerElement(
5409
5442
  parentElement,
5410
5443
  item,
@@ -5422,17 +5455,62 @@ class SpineItem {
5422
5455
  resourcesHandler: this.resourcesHandler
5423
5456
  };
5424
5457
  this.renderer = rendererFactory ? rendererFactory(rendererParams) : new DefaultRenderer(rendererParams);
5425
- const contentLayoutChange$ = merge(
5426
- this.unloaded$.pipe(map(() => ({ isFirstLayout: false }))),
5427
- this.ready$.pipe(map(() => ({ isFirstLayout: true })))
5458
+ const layoutProcess$ = this.layoutTriggerSubject.pipe(
5459
+ switchMap$1(({ blankPagePosition, minimumWidth, spreadPosition }) => {
5460
+ this.hookManager.execute(`item.onBeforeLayout`, void 0, {
5461
+ blankPagePosition,
5462
+ item: this.item,
5463
+ minimumWidth
5464
+ });
5465
+ const layout$ = defer(
5466
+ () => this.renderer.onLayout({
5467
+ blankPagePosition,
5468
+ minPageSpread: minimumWidth / this.context.getPageSize().width,
5469
+ minimumWidth,
5470
+ spreadPosition
5471
+ })
5472
+ );
5473
+ return merge(
5474
+ of({ type: "start" }),
5475
+ layout$.pipe(
5476
+ map((dims) => {
5477
+ const { height, width } = dims ?? { height: 0, width: 0 };
5478
+ const minHeight = Math.max(
5479
+ height,
5480
+ this.context.getPageSize().height
5481
+ );
5482
+ const minWidth = Math.max(width, minimumWidth);
5483
+ this.containerElement.style.width = `${minWidth}px`;
5484
+ this.containerElement.style.height = `${minHeight}px`;
5485
+ this.hookManager.execute(`item.onAfterLayout`, void 0, {
5486
+ blankPagePosition,
5487
+ item: this.item,
5488
+ minimumWidth
5489
+ });
5490
+ return {
5491
+ type: "end",
5492
+ data: { width: minWidth, height: minHeight }
5493
+ };
5494
+ })
5495
+ )
5496
+ );
5497
+ }),
5498
+ share$1()
5428
5499
  );
5429
- this.contentLayout$ = contentLayoutChange$.pipe(
5430
- withLatestFrom$1(this.isReady$),
5431
- map(([data, isReady]) => ({
5432
- isFirstLayout: data.isFirstLayout,
5433
- isReady
5434
- }))
5500
+ this.layout$ = layoutProcess$.pipe(
5501
+ filter((event) => event.type === `end`),
5502
+ map((event) => event.data),
5503
+ share$1()
5504
+ );
5505
+ this.isReady$ = layoutProcess$.pipe(
5506
+ withLatestFrom$1(this.renderer.isLoaded$),
5507
+ map(([event, loaded]) => !!(event.type === `end` && loaded)),
5508
+ startWith(false),
5509
+ distinctUntilChanged(),
5510
+ shareReplay({ refCount: true })
5435
5511
  );
5512
+ this.needsLayout$ = merge(this.unloaded$, this.loaded$);
5513
+ merge(this.layout$, this.isReady$).pipe(takeUntil$1(this.destroy$)).subscribe();
5436
5514
  }
5437
5515
  get element() {
5438
5516
  return this.containerElement;
@@ -5448,15 +5526,6 @@ class SpineItem {
5448
5526
  get readingDirection() {
5449
5527
  return this.renderer.readingDirection;
5450
5528
  }
5451
- get isReady() {
5452
- return this.renderer.state$.getValue() === "ready";
5453
- }
5454
- get ready$() {
5455
- return this.renderer.state$.pipe(
5456
- distinctUntilChanged(),
5457
- filter((state) => state === "ready")
5458
- );
5459
- }
5460
5529
  get loaded$() {
5461
5530
  return this.renderer.state$.pipe(
5462
5531
  distinctUntilChanged(),
@@ -5475,22 +5544,6 @@ class SpineItem {
5475
5544
  )
5476
5545
  );
5477
5546
  }
5478
- get isReady$() {
5479
- return this.renderer.state$.pipe(map((state) => state === "ready"));
5480
- }
5481
- /**
5482
- * Helper that will inject CSS into the document frame.
5483
- *
5484
- * @important
5485
- * The document needs to be detected as a frame.
5486
- */
5487
- upsertCSS(id, style, prepend) {
5488
- this.renderer.layers.forEach((layer) => {
5489
- if (layer.element instanceof HTMLIFrameElement) {
5490
- upsertCSS(layer.element, id, style, prepend);
5491
- }
5492
- });
5493
- }
5494
5547
  }
5495
5548
  const createContainerElement = (containerElement, item, hookManager) => {
5496
5549
  const element = containerElement.ownerDocument.createElement(`div`);
@@ -6071,6 +6124,103 @@ const convertSpinePositionToLayoutPosition = ({
6071
6124
  right: position.x + pageSize.width
6072
6125
  };
6073
6126
  };
6127
+ const layoutItem = ({
6128
+ horizontalOffset,
6129
+ verticalOffset,
6130
+ context,
6131
+ spineItemsManager,
6132
+ isGloballyPrePaginated,
6133
+ settings,
6134
+ index,
6135
+ item,
6136
+ newItemLayoutInformation
6137
+ }) => {
6138
+ let minimumWidth = context.getPageSize().width;
6139
+ let blankPagePosition = `none`;
6140
+ const itemStartOnNewScreen = horizontalOffset % context.state.visibleAreaRect.width === 0;
6141
+ const isLastItem = index === spineItemsManager.items.length - 1;
6142
+ if (context.state.isUsingSpreadMode) {
6143
+ if (!isGloballyPrePaginated && item.item.renditionLayout === `reflowable` && !isLastItem) {
6144
+ minimumWidth = context.getPageSize().width * 2;
6145
+ }
6146
+ if (!isGloballyPrePaginated && item.item.renditionLayout === `reflowable` && isLastItem && itemStartOnNewScreen) {
6147
+ minimumWidth = context.getPageSize().width * 2;
6148
+ }
6149
+ const lastItemStartOnNewScreenInAPrepaginatedBook = itemStartOnNewScreen && isLastItem && isGloballyPrePaginated;
6150
+ if (item.item.pageSpreadRight && itemStartOnNewScreen && !context.isRTL()) {
6151
+ blankPagePosition = `before`;
6152
+ minimumWidth = context.getPageSize().width * 2;
6153
+ } else if (item.item.pageSpreadLeft && itemStartOnNewScreen && context.isRTL()) {
6154
+ blankPagePosition = `before`;
6155
+ minimumWidth = context.getPageSize().width * 2;
6156
+ } else if (lastItemStartOnNewScreenInAPrepaginatedBook) {
6157
+ if (context.isRTL()) {
6158
+ blankPagePosition = `before`;
6159
+ } else {
6160
+ blankPagePosition = `after`;
6161
+ }
6162
+ minimumWidth = context.getPageSize().width * 2;
6163
+ }
6164
+ }
6165
+ const itemLayout$ = item.layout({
6166
+ minimumWidth,
6167
+ blankPagePosition,
6168
+ spreadPosition: context.state.isUsingSpreadMode ? itemStartOnNewScreen ? context.isRTL() ? `right` : `left` : context.isRTL() ? `left` : `right` : `none`
6169
+ });
6170
+ return itemLayout$.pipe(
6171
+ map$1(({ width, height }) => {
6172
+ if (settings.values.computedPageTurnDirection === `vertical`) {
6173
+ const currentValidEdgeYForVerticalPositioning = itemStartOnNewScreen ? verticalOffset : verticalOffset - context.state.visibleAreaRect.height;
6174
+ const currentValidEdgeXForVerticalPositioning = itemStartOnNewScreen ? 0 : horizontalOffset;
6175
+ if (context.isRTL()) {
6176
+ item.adjustPositionOfElement({
6177
+ top: currentValidEdgeYForVerticalPositioning,
6178
+ left: currentValidEdgeXForVerticalPositioning
6179
+ });
6180
+ } else {
6181
+ item.adjustPositionOfElement({
6182
+ top: currentValidEdgeYForVerticalPositioning,
6183
+ left: currentValidEdgeXForVerticalPositioning
6184
+ });
6185
+ }
6186
+ const newEdgeX = width + currentValidEdgeXForVerticalPositioning;
6187
+ const newEdgeY = height + currentValidEdgeYForVerticalPositioning;
6188
+ newItemLayoutInformation.push({
6189
+ left: currentValidEdgeXForVerticalPositioning,
6190
+ right: newEdgeX,
6191
+ top: currentValidEdgeYForVerticalPositioning,
6192
+ bottom: newEdgeY,
6193
+ height,
6194
+ width,
6195
+ x: currentValidEdgeXForVerticalPositioning,
6196
+ y: currentValidEdgeYForVerticalPositioning
6197
+ });
6198
+ return {
6199
+ horizontalOffset: newEdgeX,
6200
+ verticalOffset: newEdgeY
6201
+ };
6202
+ }
6203
+ item.adjustPositionOfElement(
6204
+ context.isRTL() ? { right: horizontalOffset, top: 0 } : { left: horizontalOffset, top: 0 }
6205
+ );
6206
+ const left = context.isRTL() ? context.state.visibleAreaRect.width - horizontalOffset - width : horizontalOffset;
6207
+ newItemLayoutInformation.push({
6208
+ right: context.isRTL() ? context.state.visibleAreaRect.width - horizontalOffset : horizontalOffset + width,
6209
+ left,
6210
+ x: left,
6211
+ top: verticalOffset,
6212
+ bottom: height,
6213
+ height,
6214
+ width,
6215
+ y: verticalOffset
6216
+ });
6217
+ return {
6218
+ horizontalOffset: horizontalOffset + width,
6219
+ verticalOffset: 0
6220
+ };
6221
+ })
6222
+ );
6223
+ };
6074
6224
  const NAMESPACE = `SpineLayout`;
6075
6225
  class SpineLayout extends DestroyableClass {
6076
6226
  constructor(spineItemsManager, context, settings) {
@@ -6082,8 +6232,8 @@ class SpineLayout extends DestroyableClass {
6082
6232
  this.layoutSubject = new Subject();
6083
6233
  spineItemsManager.items$.pipe(
6084
6234
  switchMap((items) => {
6085
- const itemsLayout$ = items.map(
6086
- (spineItem) => spineItem.contentLayout$.pipe(
6235
+ const needsLayouts$ = items.map(
6236
+ (spineItem) => spineItem.needsLayout$.pipe(
6087
6237
  tap$1(() => {
6088
6238
  this.layout();
6089
6239
  })
@@ -6104,12 +6254,60 @@ class SpineLayout extends DestroyableClass {
6104
6254
  })
6105
6255
  )
6106
6256
  );
6107
- return merge(...itemsLayout$, ...writingModeUpdate$);
6257
+ return merge(...needsLayouts$, ...writingModeUpdate$);
6108
6258
  })
6109
6259
  ).pipe(takeUntil(this.destroy$)).subscribe();
6260
+ const layoutInProgress = new BehaviorSubject(false);
6110
6261
  this.layout$ = this.layoutSubject.pipe(
6111
- map$1((hasChanged) => ({ hasChanged })),
6112
- startWith$1({ hasChanged: true }),
6262
+ debounceTime$1(50),
6263
+ // queue layout until previous layout is done
6264
+ exhaustMap(
6265
+ () => layoutInProgress.pipe(
6266
+ filter$1((value) => !value),
6267
+ first$1()
6268
+ )
6269
+ ),
6270
+ exhaustMap(() => {
6271
+ layoutInProgress.next(true);
6272
+ const manifest = this.context.manifest;
6273
+ const newItemLayoutInformation = [];
6274
+ const isGloballyPrePaginated = (manifest == null ? void 0 : manifest.renditionLayout) === `pre-paginated`;
6275
+ return from(this.spineItemsManager.items).pipe(
6276
+ reduce(
6277
+ (acc$, item, index) => acc$.pipe(
6278
+ concatMap(
6279
+ ({ horizontalOffset, verticalOffset }) => layoutItem({
6280
+ context: this.context,
6281
+ horizontalOffset,
6282
+ index,
6283
+ isGloballyPrePaginated,
6284
+ item,
6285
+ settings: this.settings,
6286
+ spineItemsManager: this.spineItemsManager,
6287
+ verticalOffset,
6288
+ newItemLayoutInformation
6289
+ })
6290
+ )
6291
+ ),
6292
+ of({ horizontalOffset: 0, verticalOffset: 0 })
6293
+ ),
6294
+ concatMap((layout$) => layout$),
6295
+ map$1(() => {
6296
+ const hasChanged = this.itemLayoutInformation.length !== newItemLayoutInformation.length || this.itemLayoutInformation.some(
6297
+ (old, index) => !isShallowEqual(old, newItemLayoutInformation[index])
6298
+ );
6299
+ this.itemLayoutInformation = newItemLayoutInformation;
6300
+ Report.log(NAMESPACE, `layout`, {
6301
+ hasChanged,
6302
+ itemLayoutInformation: this.itemLayoutInformation
6303
+ });
6304
+ return { hasChanged };
6305
+ }),
6306
+ finalize(() => {
6307
+ layoutInProgress.next(false);
6308
+ })
6309
+ );
6310
+ }),
6113
6311
  map$1(({ hasChanged }) => {
6114
6312
  const items = spineItemsManager.items;
6115
6313
  const spineItemsPagesAbsolutePositions = items.map((item) => {
@@ -6159,116 +6357,13 @@ class SpineLayout extends DestroyableClass {
6159
6357
  pages
6160
6358
  };
6161
6359
  }),
6162
- shareReplay$1(1)
6360
+ share()
6163
6361
  );
6362
+ this.info$ = this.layout$.pipe(shareReplay$1({ refCount: true }));
6363
+ merge(this.layout$, this.info$).pipe(takeUntil(this.destroy$)).subscribe();
6164
6364
  }
6165
- /**
6166
- * @todo
6167
- * move this logic to the spine
6168
- *
6169
- * @todo
6170
- * make sure to check how many times it is being called and try to reduce number of layouts
6171
- * it is called eery time an item is being unload (which can adds up quickly for big books)
6172
- */
6173
6365
  layout() {
6174
- const manifest = this.context.manifest;
6175
- const newItemLayoutInformation = [];
6176
- const isGloballyPrePaginated = (manifest == null ? void 0 : manifest.renditionLayout) === `pre-paginated`;
6177
- this.spineItemsManager.items.reduce(
6178
- ({ horizontalOffset, verticalOffset }, item, index) => {
6179
- let minimumWidth = this.context.getPageSize().width;
6180
- let blankPagePosition = `none`;
6181
- const itemStartOnNewScreen = horizontalOffset % this.context.state.visibleAreaRect.width === 0;
6182
- const isLastItem = index === this.spineItemsManager.items.length - 1;
6183
- if (this.context.state.isUsingSpreadMode) {
6184
- if (!isGloballyPrePaginated && item.item.renditionLayout === `reflowable` && !isLastItem) {
6185
- minimumWidth = this.context.getPageSize().width * 2;
6186
- }
6187
- if (!isGloballyPrePaginated && item.item.renditionLayout === `reflowable` && isLastItem && itemStartOnNewScreen) {
6188
- minimumWidth = this.context.getPageSize().width * 2;
6189
- }
6190
- const lastItemStartOnNewScreenInAPrepaginatedBook = itemStartOnNewScreen && isLastItem && isGloballyPrePaginated;
6191
- if (item.item.pageSpreadRight && itemStartOnNewScreen && !this.context.isRTL()) {
6192
- blankPagePosition = `before`;
6193
- minimumWidth = this.context.getPageSize().width * 2;
6194
- } else if (item.item.pageSpreadLeft && itemStartOnNewScreen && this.context.isRTL()) {
6195
- blankPagePosition = `before`;
6196
- minimumWidth = this.context.getPageSize().width * 2;
6197
- } else if (lastItemStartOnNewScreenInAPrepaginatedBook) {
6198
- if (this.context.isRTL()) {
6199
- blankPagePosition = `before`;
6200
- } else {
6201
- blankPagePosition = `after`;
6202
- }
6203
- minimumWidth = this.context.getPageSize().width * 2;
6204
- }
6205
- }
6206
- const { width, height } = item.layout({
6207
- minimumWidth,
6208
- blankPagePosition,
6209
- spreadPosition: this.context.state.isUsingSpreadMode ? itemStartOnNewScreen ? this.context.isRTL() ? `right` : `left` : this.context.isRTL() ? `left` : `right` : `none`
6210
- });
6211
- if (this.settings.values.computedPageTurnDirection === `vertical`) {
6212
- const currentValidEdgeYForVerticalPositioning = itemStartOnNewScreen ? verticalOffset : verticalOffset - this.context.state.visibleAreaRect.height;
6213
- const currentValidEdgeXForVerticalPositioning = itemStartOnNewScreen ? 0 : horizontalOffset;
6214
- if (this.context.isRTL()) {
6215
- item.adjustPositionOfElement({
6216
- top: currentValidEdgeYForVerticalPositioning,
6217
- left: currentValidEdgeXForVerticalPositioning
6218
- });
6219
- } else {
6220
- item.adjustPositionOfElement({
6221
- top: currentValidEdgeYForVerticalPositioning,
6222
- left: currentValidEdgeXForVerticalPositioning
6223
- });
6224
- }
6225
- const newEdgeX = width + currentValidEdgeXForVerticalPositioning;
6226
- const newEdgeY = height + currentValidEdgeYForVerticalPositioning;
6227
- newItemLayoutInformation.push({
6228
- left: currentValidEdgeXForVerticalPositioning,
6229
- right: newEdgeX,
6230
- top: currentValidEdgeYForVerticalPositioning,
6231
- bottom: newEdgeY,
6232
- height,
6233
- width,
6234
- x: currentValidEdgeXForVerticalPositioning,
6235
- y: currentValidEdgeYForVerticalPositioning
6236
- });
6237
- return {
6238
- horizontalOffset: newEdgeX,
6239
- verticalOffset: newEdgeY
6240
- };
6241
- }
6242
- item.adjustPositionOfElement(
6243
- this.context.isRTL() ? { right: horizontalOffset, top: 0 } : { left: horizontalOffset, top: 0 }
6244
- );
6245
- const left = this.context.isRTL() ? this.context.state.visibleAreaRect.width - horizontalOffset - width : horizontalOffset;
6246
- newItemLayoutInformation.push({
6247
- right: this.context.isRTL() ? this.context.state.visibleAreaRect.width - horizontalOffset : horizontalOffset + width,
6248
- left,
6249
- x: left,
6250
- top: verticalOffset,
6251
- bottom: height,
6252
- height,
6253
- width,
6254
- y: verticalOffset
6255
- });
6256
- return {
6257
- horizontalOffset: horizontalOffset + width,
6258
- verticalOffset: 0
6259
- };
6260
- },
6261
- { horizontalOffset: 0, verticalOffset: 0 }
6262
- );
6263
- const hasLayoutChanges = this.itemLayoutInformation.length !== newItemLayoutInformation.length || this.itemLayoutInformation.some(
6264
- (old, index) => !isShallowEqual(old, newItemLayoutInformation[index])
6265
- );
6266
- this.itemLayoutInformation = newItemLayoutInformation;
6267
- Report.log(NAMESPACE, `layout`, {
6268
- hasLayoutChanges,
6269
- itemLayoutInformation: this.itemLayoutInformation
6270
- });
6271
- this.layoutSubject.next(hasLayoutChanges);
6366
+ this.layoutSubject.next(void 0);
6272
6367
  }
6273
6368
  /**
6274
6369
  * It's important to not use x,y since we need the absolute position of each element. Otherwise x,y would be relative to
@@ -6851,7 +6946,7 @@ class ImageRenderer extends DocumentRenderer {
6851
6946
  this.layers = [];
6852
6947
  return EMPTY;
6853
6948
  }
6854
- layout({
6949
+ onLayout({
6855
6950
  spreadPosition
6856
6951
  }) {
6857
6952
  var _a, _b;
@@ -6871,10 +6966,10 @@ class ImageRenderer extends DocumentRenderer {
6871
6966
  element.style.width = `${width}px`;
6872
6967
  element.style.objectPosition = spreadPosition === "left" ? `right` : spreadPosition === `right` ? `left` : `center`;
6873
6968
  }
6874
- return {
6969
+ return of({
6875
6970
  width,
6876
6971
  height
6877
- };
6972
+ });
6878
6973
  }
6879
6974
  }
6880
6975
  const mediaEnhancer = (next) => (options) => {
@@ -6970,36 +7065,41 @@ const mediaEnhancer = (next) => (options) => {
6970
7065
  const progressionEnhancer = (next) => (options) => {
6971
7066
  const reader = next(options);
6972
7067
  const getPercentageEstimate = (context, currentSpineIndex, numberOfPages, pageIndex, currentPosition, currentItem) => {
6973
- var _a, _b, _c, _d, _e, _f;
6974
- const isGloballyPrePaginated = ((_a = context.manifest) == null ? void 0 : _a.renditionLayout) === `pre-paginated`;
6975
- const readingOrderLength = ((_b = context.manifest) == null ? void 0 : _b.spineItems.length) || 0;
6976
- const estimateBeforeThisItem = ((_c = context.manifest) == null ? void 0 : _c.spineItems.slice(0, currentSpineIndex).reduce((acc, item) => acc + (item.progressionWeight ?? 0), 0)) || 0;
6977
- const currentItemWeight = ((_e = (_d = context.manifest) == null ? void 0 : _d.spineItems[currentSpineIndex]) == null ? void 0 : _e.progressionWeight) || 0;
6978
- let progressWithinThisItem = (pageIndex + 1) * (currentItemWeight / numberOfPages);
6979
- if (!isGloballyPrePaginated && currentItem.item.renditionLayout === `reflowable` && !currentItem.isReady) {
6980
- progressWithinThisItem = 0;
6981
- }
6982
- let totalProgress = estimateBeforeThisItem + progressWithinThisItem;
6983
- if (((_f = context.manifest) == null ? void 0 : _f.renditionFlow) === `scrolled-continuous`) {
6984
- if (currentItem.isReady) {
6985
- progressWithinThisItem = getScrollPercentageWithinItem(
6986
- context,
6987
- currentPosition,
6988
- currentItem
6989
- );
6990
- } else {
6991
- progressWithinThisItem = 0;
6992
- }
6993
- totalProgress = getTotalProgressFromPercentages(
6994
- estimateBeforeThisItem,
6995
- currentItemWeight,
6996
- progressWithinThisItem
6997
- );
6998
- }
6999
- if (currentSpineIndex === readingOrderLength - 1 && pageIndex === numberOfPages - 1 && totalProgress > 0.99) {
7000
- return 1;
7001
- }
7002
- return totalProgress;
7068
+ return currentItem.isReady$.pipe(
7069
+ first$1(),
7070
+ map$1((itemIsReady) => {
7071
+ var _a, _b, _c, _d, _e, _f;
7072
+ const isGloballyPrePaginated = ((_a = context.manifest) == null ? void 0 : _a.renditionLayout) === `pre-paginated`;
7073
+ const readingOrderLength = ((_b = context.manifest) == null ? void 0 : _b.spineItems.length) || 0;
7074
+ const estimateBeforeThisItem = ((_c = context.manifest) == null ? void 0 : _c.spineItems.slice(0, currentSpineIndex).reduce((acc, item) => acc + (item.progressionWeight ?? 0), 0)) || 0;
7075
+ const currentItemWeight = ((_e = (_d = context.manifest) == null ? void 0 : _d.spineItems[currentSpineIndex]) == null ? void 0 : _e.progressionWeight) || 0;
7076
+ let progressWithinThisItem = (pageIndex + 1) * (currentItemWeight / numberOfPages);
7077
+ if (!isGloballyPrePaginated && currentItem.item.renditionLayout === `reflowable` && !itemIsReady) {
7078
+ progressWithinThisItem = 0;
7079
+ }
7080
+ let totalProgress = estimateBeforeThisItem + progressWithinThisItem;
7081
+ if (((_f = context.manifest) == null ? void 0 : _f.renditionFlow) === `scrolled-continuous`) {
7082
+ if (itemIsReady) {
7083
+ progressWithinThisItem = getScrollPercentageWithinItem(
7084
+ context,
7085
+ currentPosition,
7086
+ currentItem
7087
+ );
7088
+ } else {
7089
+ progressWithinThisItem = 0;
7090
+ }
7091
+ totalProgress = getTotalProgressFromPercentages(
7092
+ estimateBeforeThisItem,
7093
+ currentItemWeight,
7094
+ progressWithinThisItem
7095
+ );
7096
+ }
7097
+ if (currentSpineIndex === readingOrderLength - 1 && pageIndex === numberOfPages - 1 && totalProgress > 0.99) {
7098
+ return 1;
7099
+ }
7100
+ return totalProgress;
7101
+ })
7102
+ );
7003
7103
  };
7004
7104
  const getTotalProgressFromPercentages = (estimateBeforeThisItem, currentItemWeight, progressWithinThisItem) => {
7005
7105
  return estimateBeforeThisItem + currentItemWeight * progressWithinThisItem;
@@ -7052,19 +7152,24 @@ const accessibilityEnhancer = (next) => (options) => {
7052
7152
  if (!(frame instanceof HTMLIFrameElement)) return;
7053
7153
  const item = reader.spineItemsManager.get(itemId);
7054
7154
  if (!item) return;
7055
- item.upsertCSS(
7056
- `prose-reader-accessibility`,
7057
- `
7058
- :focus-visible {
7059
- ${/*
7060
- Some epubs remove the outline, this is not good practice since it reduce accessibility.
7061
- We will try to restore it by force.
7062
- */
7063
- ``}
7064
- outline: -webkit-focus-ring-color auto 1px;
7155
+ item.renderer.layers.forEach((layer) => {
7156
+ if (layer.element instanceof HTMLIFrameElement) {
7157
+ upsertCSS(
7158
+ layer.element,
7159
+ `prose-reader-accessibility`,
7160
+ `
7161
+ :focus-visible {
7162
+ ${/*
7163
+ Some epubs remove the outline, this is not good practice since it reduce accessibility.
7164
+ We will try to restore it by force.
7165
+ */
7166
+ ``}
7167
+ outline: -webkit-focus-ring-color auto 1px;
7168
+ }
7169
+ `
7170
+ );
7065
7171
  }
7066
- `
7067
- );
7172
+ });
7068
7173
  const links = (_b = frame.contentDocument) == null ? void 0 : _b.body.querySelectorAll(`a`);
7069
7174
  links == null ? void 0 : links.forEach((link) => {
7070
7175
  observer.observe(link);
@@ -8028,7 +8133,7 @@ class HtmlRenderer extends DocumentRenderer {
8028
8133
  this.layers = [];
8029
8134
  return EMPTY;
8030
8135
  }
8031
- layout({
8136
+ onLayout({
8032
8137
  minPageSpread,
8033
8138
  blankPagePosition,
8034
8139
  spreadPosition
@@ -8036,10 +8141,10 @@ class HtmlRenderer extends DocumentRenderer {
8036
8141
  var _a;
8037
8142
  const { width: pageWidth, height: pageHeight } = this.context.getPageSize();
8038
8143
  const frameElement = this.getFrameElement();
8039
- if (!frameElement) return { width: pageWidth, height: pageHeight };
8144
+ if (!frameElement) return of({ width: pageWidth, height: pageHeight });
8040
8145
  const isUsingVerticalWriting = !!((_a = this.writingMode) == null ? void 0 : _a.startsWith(`vertical`));
8041
8146
  if (this.item.renditionLayout === `pre-paginated`) {
8042
- return renderPrePaginated({
8147
+ const dims = renderPrePaginated({
8043
8148
  blankPagePosition,
8044
8149
  enableTouch: this.settings.values.computedPageTurnMode !== `scrollable`,
8045
8150
  frameElement,
@@ -8049,6 +8154,7 @@ class HtmlRenderer extends DocumentRenderer {
8049
8154
  pageWidth,
8050
8155
  spreadPosition
8051
8156
  });
8157
+ return of(dims);
8052
8158
  }
8053
8159
  const { latestContentHeightWhenLoaded, ...rest } = renderReflowable({
8054
8160
  pageHeight,
@@ -8064,7 +8170,7 @@ class HtmlRenderer extends DocumentRenderer {
8064
8170
  enableTouch: this.settings.values.computedPageTurnMode !== `scrollable`
8065
8171
  });
8066
8172
  this.latestContentHeightWhenLoaded = latestContentHeightWhenLoaded;
8067
- return rest;
8173
+ return of(rest);
8068
8174
  }
8069
8175
  getFrameElement() {
8070
8176
  var _a;
@@ -8146,7 +8252,6 @@ const createRangeFromSelection = ({
8146
8252
  selection,
8147
8253
  spineItem
8148
8254
  }) => {
8149
- if (!spineItem.isReady) return void 0;
8150
8255
  const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
8151
8256
  if (!anchorNode || !focusNode) {
8152
8257
  return void 0;