@file-viewer/pptx 2.2.9 → 2.4.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/viewer.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { renderPptxPostProcessing } from './chart.js';
2
2
  import { resolvePptxEngineOptions, RECOMMENDED_ZIP_LIMITS } from './options.js';
3
+ import { PptxPresentation } from './presentation.js';
3
4
  import { sanitizePptxCss, sanitizePptxMarkup } from './sanitize.js';
4
5
  import { ensurePptxViewerStyles } from './styles.js';
5
6
  import { createPptxWorker } from './worker.js';
@@ -96,6 +97,13 @@ export class PptxViewer {
96
97
  this.previewThumbnailDataUrl = null;
97
98
  this.slideSize = null;
98
99
  this.slideRecords = [];
100
+ // Total slides received from the worker. The windowed path mirrors this in
101
+ // slideRecords, but the default (non-windowed) path appends slides straight
102
+ // into the content node without creating records, so the deck size has to be
103
+ // counted independently of the virtualization bookkeeping.
104
+ this.totalSlides = 0;
105
+ this.savedScroll = null;
106
+ this.pendingScrollRestore = null;
99
107
  this.slideWindowTarget = null;
100
108
  this.slideWindowListeners = [];
101
109
  this.slideWindowFrame = 0;
@@ -104,6 +112,7 @@ export class PptxViewer {
104
112
  this.mediaRecords = new Map();
105
113
  this.disposed = false;
106
114
  this.completed = false;
115
+ this.presentation = null;
107
116
  this.handleSlideWindowChange = () => this.scheduleSlideWindowUpdate();
108
117
  this.buffer = buffer;
109
118
  this.target = target;
@@ -124,9 +133,111 @@ export class PptxViewer {
124
133
  get thumbnailDataUrl() {
125
134
  return this.previewThumbnailDataUrl;
126
135
  }
136
+ get slideCount() {
137
+ return this.totalSlides;
138
+ }
139
+ get slideDimensions() {
140
+ var _a, _b;
141
+ const width = Number((_a = this.slideSize) === null || _a === void 0 ? void 0 : _a.width);
142
+ const height = Number((_b = this.slideSize) === null || _b === void 0 ? void 0 : _b.height);
143
+ return Number.isFinite(width) && width > 0 && Number.isFinite(height) && height > 0
144
+ ? { width, height }
145
+ : null;
146
+ }
147
+ get presenting() {
148
+ var _a;
149
+ return Boolean((_a = this.presentation) === null || _a === void 0 ? void 0 : _a.active);
150
+ }
151
+ get presentationSlideNumber() {
152
+ var _a, _b;
153
+ return (_b = (_a = this.presentation) === null || _a === void 0 ? void 0 : _a.slideNumber) !== null && _b !== void 0 ? _b : 1;
154
+ }
155
+ get styleRoot() {
156
+ var _a;
157
+ if (this.options.styleRoot) {
158
+ return this.options.styleRoot;
159
+ }
160
+ const documentRef = this.target.ownerDocument || document;
161
+ const root = this.target.getRootNode();
162
+ const ShadowRootCtor = (_a = documentRef.defaultView) === null || _a === void 0 ? void 0 : _a.ShadowRoot;
163
+ return ShadowRootCtor && root instanceof ShadowRootCtor
164
+ ? root
165
+ : documentRef;
166
+ }
167
+ /**
168
+ * Where the slideshow overlay is mounted. It has to share a root with the injected slide styles,
169
+ * or the engine's scoped CSS stops applying once the slides move into the overlay.
170
+ */
171
+ get presentationRoot() {
172
+ const documentRef = this.target.ownerDocument || document;
173
+ const styleRoot = this.styleRoot;
174
+ if (styleRoot !== documentRef) {
175
+ return styleRoot;
176
+ }
177
+ return documentRef.body || documentRef.documentElement;
178
+ }
179
+ /** Force a slide out of the virtualized window so the slideshow can show it immediately. */
180
+ ensureSlideRendered(slideNumber) {
181
+ const record = this.slideRecords.find(item => item.slideNumber === slideNumber);
182
+ if (!record) {
183
+ return null;
184
+ }
185
+ if (!record.rendered) {
186
+ this.renderSlideRecord(record);
187
+ }
188
+ return record.element;
189
+ }
190
+ refreshLayout() {
191
+ this.scheduleResize();
192
+ }
193
+ /**
194
+ * Remember where the deck was scrolled before the slideshow moves the scale
195
+ * box into the overlay. Moving it collapses the scroller and clamps scrollTop
196
+ * to zero, so the position has to be captured up front and restored on exit.
197
+ */
198
+ saveScrollPosition() {
199
+ const view = this.target.ownerDocument.defaultView || window;
200
+ const HTMLElementCtor = view.HTMLElement;
201
+ const scroller = this.slideWindowTarget || this.findSlideWindowTarget();
202
+ if (scroller instanceof HTMLElementCtor) {
203
+ this.savedScroll = { top: scroller.scrollTop, left: scroller.scrollLeft };
204
+ }
205
+ else {
206
+ this.savedScroll = { top: view.scrollY, left: view.scrollX };
207
+ }
208
+ }
209
+ restoreScrollPosition() {
210
+ if (this.savedScroll) {
211
+ this.pendingScrollRestore = this.savedScroll;
212
+ this.savedScroll = null;
213
+ }
214
+ this.scheduleResize();
215
+ }
216
+ emitPresentationChange(state) {
217
+ var _a, _b;
218
+ (_b = (_a = this.options).onPresentationChange) === null || _b === void 0 ? void 0 : _b.call(_a, state);
219
+ }
220
+ async enterPresentation(slideNumber) {
221
+ if (this.disposed || this.totalSlides === 0) {
222
+ return;
223
+ }
224
+ this.presentation || (this.presentation = new PptxPresentation(this, this.options.presentationLabels, this.options.presentationFullscreen));
225
+ await this.presentation.enter(slideNumber);
226
+ }
227
+ exitPresentation() {
228
+ var _a;
229
+ (_a = this.presentation) === null || _a === void 0 ? void 0 : _a.exit();
230
+ }
231
+ async togglePresentation(slideNumber) {
232
+ if (this.presenting) {
233
+ this.exitPresentation();
234
+ return;
235
+ }
236
+ await this.enterPresentation(slideNumber);
237
+ }
127
238
  async open() {
128
239
  ensureZipWithinLimits(this.buffer, this.options);
129
- ensurePptxViewerStyles(this.target.ownerDocument || document, this.options.styleRoot);
240
+ ensurePptxViewerStyles(this.target.ownerDocument || document, this.styleRoot);
130
241
  this.target.replaceChildren(this.scaleBox);
131
242
  this.attachResizeObserver();
132
243
  this.attachSlideWindowListeners();
@@ -138,20 +249,22 @@ export class PptxViewer {
138
249
  this.scheduleResize();
139
250
  }
140
251
  destroy() {
141
- var _a, _b, _c, _d;
252
+ var _a, _b, _c, _d, _e;
142
253
  this.disposed = true;
254
+ (_a = this.presentation) === null || _a === void 0 ? void 0 : _a.destroy();
255
+ this.presentation = null;
143
256
  this.previewThumbnailDataUrl = null;
144
257
  this.releaseCharts();
145
258
  this.releaseMedia();
146
- (_a = this.worker) === null || _a === void 0 ? void 0 : _a.terminate();
259
+ (_b = this.worker) === null || _b === void 0 ? void 0 : _b.terminate();
147
260
  this.worker = null;
148
- (_b = this.resizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
261
+ (_c = this.resizeObserver) === null || _c === void 0 ? void 0 : _c.disconnect();
149
262
  this.resizeObserver = null;
150
263
  if (this.resizeFrame) {
151
- (_c = this.target.ownerDocument.defaultView) === null || _c === void 0 ? void 0 : _c.cancelAnimationFrame(this.resizeFrame);
264
+ (_d = this.target.ownerDocument.defaultView) === null || _d === void 0 ? void 0 : _d.cancelAnimationFrame(this.resizeFrame);
152
265
  }
153
266
  if (this.slideWindowFrame) {
154
- (_d = this.target.ownerDocument.defaultView) === null || _d === void 0 ? void 0 : _d.cancelAnimationFrame(this.slideWindowFrame);
267
+ (_e = this.target.ownerDocument.defaultView) === null || _e === void 0 ? void 0 : _e.cancelAnimationFrame(this.slideWindowFrame);
155
268
  }
156
269
  this.detachSlideWindowListeners();
157
270
  this.target.replaceChildren();
@@ -166,6 +279,7 @@ export class PptxViewer {
166
279
  this.previewThumbnailDataUrl = null;
167
280
  this.slideSize = null;
168
281
  this.slideRecords = [];
282
+ this.totalSlides = 0;
169
283
  this.content.replaceChildren();
170
284
  this.content.dataset.renderState = 'loading';
171
285
  try {
@@ -196,6 +310,7 @@ export class PptxViewer {
196
310
  switch (message.type) {
197
311
  case 'slide': {
198
312
  this.clearThumbnail();
313
+ this.totalSlides += 1;
199
314
  if (this.shouldWindowSlides()) {
200
315
  this.appendWindowedSlide(String(message.data || ''), Number(message.slide_num || 0));
201
316
  }
@@ -215,6 +330,7 @@ export class PptxViewer {
215
330
  const error = message.data;
216
331
  const html = buildSlideErrorHtml(slideNumber, error);
217
332
  this.clearThumbnail();
333
+ this.totalSlides += 1;
218
334
  if (this.shouldWindowSlides()) {
219
335
  this.appendWindowedSlide(html, slideNumber);
220
336
  }
@@ -420,6 +536,19 @@ export class PptxViewer {
420
536
  }
421
537
  const windowOptions = this.getSlideWindowOptions();
422
538
  const indexesToRender = this.getWindowedSlideIndexes(windowOptions);
539
+ // While presenting, the active slide and its neighbour live inside the fixed
540
+ // overlay, outside the scroll-view geometry the windowing code measures. Keep
541
+ // them mounted so a resize cannot unmount the slide that is on screen.
542
+ const presentation = this.presentation;
543
+ if (presentation === null || presentation === void 0 ? void 0 : presentation.active) {
544
+ const activeIndex = this.slideRecords.findIndex(record => record.slideNumber === presentation.slideNumber);
545
+ if (activeIndex >= 0) {
546
+ indexesToRender.add(activeIndex);
547
+ if (activeIndex + 1 < this.slideRecords.length) {
548
+ indexesToRender.add(activeIndex + 1);
549
+ }
550
+ }
551
+ }
423
552
  let changed = false;
424
553
  this.slideRecords.forEach((record, index) => {
425
554
  if (indexesToRender.has(index)) {
@@ -748,15 +877,20 @@ export class PptxViewer {
748
877
  this.resizeFrame = view.requestAnimationFrame(() => this.resize());
749
878
  }
750
879
  resize() {
751
- var _a, _b;
880
+ var _a, _b, _c;
881
+ // While presenting, the slideshow owns the transform of the same nodes.
882
+ if ((_a = this.presentation) === null || _a === void 0 ? void 0 : _a.active) {
883
+ this.presentation.layout();
884
+ return;
885
+ }
752
886
  const slides = this.getMountedSlideElements();
753
- const sizeWidth = Number((_a = this.slideSize) === null || _a === void 0 ? void 0 : _a.width);
887
+ const sizeWidth = Number((_b = this.slideSize) === null || _b === void 0 ? void 0 : _b.width);
754
888
  const fallbackWidth = Number.isFinite(sizeWidth) && sizeWidth > 0 ? sizeWidth : 0;
755
889
  const slideWidth = Math.max(...slides.map(slide => slide.offsetWidth), fallbackWidth);
756
890
  if (!slideWidth) {
757
891
  return;
758
892
  }
759
- const viewportWidth = this.target.clientWidth || ((_b = this.target.parentElement) === null || _b === void 0 ? void 0 : _b.clientWidth) || slideWidth;
893
+ const viewportWidth = this.target.clientWidth || ((_c = this.target.parentElement) === null || _c === void 0 ? void 0 : _c.clientWidth) || slideWidth;
760
894
  this.fitScale = this.options.fitMode === 'none'
761
895
  ? 1
762
896
  : Math.min(1, viewportWidth / slideWidth);
@@ -767,6 +901,22 @@ export class PptxViewer {
767
901
  this.scaleBox.style.width = `${Math.ceil(slideWidth * effectiveScale)}px`;
768
902
  this.scaleBox.style.height = `${Math.ceil(this.content.scrollHeight * effectiveScale)}px`;
769
903
  this.scaleBox.style.minHeight = '';
904
+ // The scale box height is recomputed above; only now can the saved scroll
905
+ // position be applied without the scroller clamping it back to zero.
906
+ if (this.pendingScrollRestore) {
907
+ const { top, left } = this.pendingScrollRestore;
908
+ this.pendingScrollRestore = null;
909
+ const view = this.target.ownerDocument.defaultView || window;
910
+ const HTMLElementCtor = view.HTMLElement;
911
+ const scroller = this.slideWindowTarget || this.findSlideWindowTarget();
912
+ if (scroller instanceof HTMLElementCtor) {
913
+ scroller.scrollTop = top;
914
+ scroller.scrollLeft = left;
915
+ }
916
+ else {
917
+ view.scrollTo(left, top);
918
+ }
919
+ }
770
920
  }
771
921
  getMountedSlideElements() {
772
922
  var _a;