ngx-doc-viewer 15.0.0 → 21.0.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/README.md CHANGED
@@ -37,6 +37,8 @@ export class AppModule {}
37
37
  ></ngx-doc-viewer>
38
38
  ```
39
39
 
40
+ The component now shows an internal loading overlay while external viewers such as Google Docs Viewer and Office Online are initializing, so viewer switches do not appear as a blank panel.
41
+
40
42
  To
41
43
 
42
44
  #### API:
@@ -53,6 +55,75 @@ Input:
53
55
  - popout: adds an overlay over googles popout button or office popout and menu which disables just this button/menu but keeps giving the possibility to select text. The popout button is still visible (for google during a few seconds) but not clickable.
54
56
  - popout-hide: see popup, instead of an transparent overlay a white one. This really hides the button but you'll see a white block while loading for the google viewer.
55
57
  - overrideLocalhost: documents from the assets folder are not publicly available and therefor won't show in an external viewer (google, office). If the site is already published to public server, then pass that url and if will replace localhost by the other url. Like: overrideLocalhost="https://angular-doc-viewer.firebaseapp.com/"
58
+ - loadingText: fallback text shown in the built-in loading overlay. Defaults to `Loading document...`
59
+ - errorTextOverride: fallback text shown in the built-in error overlay. Defaults to the runtime error message.
60
+ - retryButtonText: text used by the built-in retry button in the default error overlay. Defaults to `Retry`
61
+ - googleFinalRetryDelay: waits this many milliseconds and retries Google once more before showing the error overlay. Defaults to `0`
62
+ - officeAutoRetry: automatically retries the Office viewer once after `officeRetryDelay`. Defaults to `false`
63
+ - officeRetryDelay: delay in milliseconds before the one-time Office auto retry. Defaults to `3000`
64
+ - officeReloadButtonText: text shown in the persistent Office reload button. Defaults to `↻`
65
+ - officeReloadButtonTitle: tooltip/title for the persistent Office reload button. Defaults to `Reload document`
66
+ - secondaryActionText: optional text for a built-in secondary error action button, for example `Open source` or `Download`
67
+ - secondaryActionMode: controls the built-in secondary action behavior. Supported values: `open` or `download`. Defaults to `open`
68
+
69
+ For custom loading markup in Angular, project an `ng-template` named `loadingContent`.
70
+ The template receives `$implicit` and `state` with:
71
+ - `viewer`
72
+ - `url`
73
+ - `phase`
74
+ - `errorText`
75
+ - `retry()`
76
+ - `actionUrl`
77
+
78
+ ```html
79
+ <ngx-doc-viewer [url]="doc" viewer="office">
80
+ <ng-template #loadingContent let-state>
81
+ <div style="display:flex;gap:8px;align-items:center;">
82
+ <span class="spinner"></span>
83
+ <span>Preparing {{ state.viewer }} preview...</span>
84
+ </div>
85
+ </ng-template>
86
+ </ngx-doc-viewer>
87
+ ```
88
+
89
+ To replace the persistent Office reload control, project an `ng-template` named `officeReloadContent`:
90
+
91
+ ```html
92
+ <ngx-doc-viewer [url]="doc" viewer="office">
93
+ <ng-template #officeReloadContent let-state>
94
+ <span>Reload</span>
95
+ </ng-template>
96
+ </ngx-doc-viewer>
97
+ ```
98
+
99
+ For custom error markup in Angular, project an `ng-template` named `errorContent`.
100
+ It receives the same context and can call `retry()` directly:
101
+
102
+ ```html
103
+ <ngx-doc-viewer [url]="doc" viewer="office">
104
+ <ng-template #errorContent let-state>
105
+ <div style="text-align:center;">
106
+ <div>Preview unavailable for {{ state.viewer }}.</div>
107
+ <div>{{ state.actionUrl }}</div>
108
+ <button type="button" (click)="state.retry()">Retry</button>
109
+ <a [href]="state.actionUrl" target="_blank" rel="noreferrer">Open source</a>
110
+ </div>
111
+ </ng-template>
112
+ </ngx-doc-viewer>
113
+ ```
114
+
115
+ If you want to keep the default error layout but replace just the actions area, project an `ng-template` named `errorActions`:
116
+
117
+ ```html
118
+ <ngx-doc-viewer [url]="doc" viewer="office" retryButtonText="Try again">
119
+ <ng-template #errorActions let-state>
120
+ <div style="margin-top: 14px; display: flex; gap: 8px; justify-content: center;">
121
+ <button type="button" (click)="state.retry()">Try again</button>
122
+ <a [href]="state.actionUrl" target="_blank" rel="noreferrer">Open source</a>
123
+ </div>
124
+ </ng-template>
125
+ </ngx-doc-viewer>
126
+ ```
56
127
 
57
128
  There are some issues loading document in the google viewer. See: https://stackoverflow.com/questions/40414039/google-docs-viewer-returning-204-responses-no-longer-working-alternatives. If loading pdf's and Word documents, seems to work now with this hack let me know via a Github issue.
58
129
 
@@ -61,7 +132,16 @@ There are some issues loading document in the google viewer. See: https://stacko
61
132
  - googleMaxChecks = 5 | max number of retries
62
133
  Output:
63
134
 
64
- - loaded: google only, notifies when iframe is loaded. Can be used to show progress while loading
135
+ - loaded: emitted when the current iframe is ready. Can be used to hook into custom loading or telemetry flows.
136
+ - loading: emitted when the viewer enters the `loading` phase. Payload includes `viewer`, `url`, `phase`, `errorText`, `retry()`, and `actionUrl`.
137
+ - error: emitted when the viewer enters the `error` phase. Payload includes `viewer`, `url`, `phase`, `errorText`, `retry()`, and `actionUrl`.
138
+ - phaseChange: emitted whenever the internal phase changes to `idle`, `loading`, `ready`, or `error`.
139
+
140
+ ### Recent behavior improvements
141
+
142
+ - External viewer switches now remount the iframe cleanly when changing between viewers such as Google and Office.
143
+ - A built-in loading overlay is shown while remote viewers are loading.
144
+ - Mammoth rendering now refreshes correctly when switching from an iframe-based viewer back to inline document rendering.
65
145
 
66
146
  ### File type support
67
147
 
@@ -0,0 +1,517 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, TemplateRef, ViewChildren, ContentChild, Input, Output, Component, NgModule } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import { getViewerDetails, isLocalFile, replaceLocalUrl, getDocxToHtml, googleCheckSubscription, getViewerRecoveryPlan, iframeIsLoaded } from 'docviewhelper';
6
+ export * from 'docviewhelper';
7
+ import * as i1 from '@angular/platform-browser';
8
+
9
+ class NgxDocViewerComponent {
10
+ domSanitizer;
11
+ ngZone;
12
+ cdr;
13
+ loaded = new EventEmitter();
14
+ loading = new EventEmitter();
15
+ error = new EventEmitter();
16
+ phaseChange = new EventEmitter();
17
+ url = '';
18
+ queryParams = '';
19
+ viewerUrl = '';
20
+ googleCheckInterval = 3000;
21
+ googleMaxChecks = 5;
22
+ googleFinalRetryDelay = 0;
23
+ disableContent = 'none';
24
+ googleCheckContentLoaded = true;
25
+ viewer = 'google';
26
+ overrideLocalhost = '';
27
+ loadingText = 'Loading document...';
28
+ errorTextOverride = '';
29
+ retryButtonText = 'Retry';
30
+ officeAutoRetry = false;
31
+ officeRetryDelay = 3000;
32
+ officeReloadButtonText = '↻';
33
+ officeReloadButtonTitle = 'Reload document';
34
+ secondaryActionText = '';
35
+ secondaryActionMode = 'open';
36
+ loadingTemplate;
37
+ errorTemplate;
38
+ errorActionsTemplate;
39
+ officeReloadTemplate;
40
+ iframes = undefined;
41
+ fullUrl = undefined;
42
+ externalViewer = false;
43
+ docHtml = '';
44
+ configuredViewer = 'google';
45
+ showIframe = true;
46
+ renderPhase = 'idle';
47
+ errorText = '';
48
+ failedUrl = '';
49
+ retryNonce = 0;
50
+ loadingTemplateContext = this.createTemplateContext();
51
+ errorTemplateContext = this.createTemplateContext();
52
+ checkIFrameSubscription = undefined;
53
+ loadVersion = 0;
54
+ externalLoadTimeoutId = undefined;
55
+ googleFinalRetryTimeoutId = undefined;
56
+ googleFinalRetriedSourceKey = undefined;
57
+ currentGoogleSourceKey = undefined;
58
+ officeRetryTimeoutId = undefined;
59
+ officeAutoRetriedSourceKey = undefined;
60
+ currentOfficeSourceKey = undefined;
61
+ lastEmittedPhase = undefined;
62
+ constructor(domSanitizer, ngZone, cdr) {
63
+ this.domSanitizer = domSanitizer;
64
+ this.ngZone = ngZone;
65
+ this.cdr = cdr;
66
+ }
67
+ ngOnDestroy() {
68
+ this.clearExternalLoadTimeout();
69
+ this.clearGoogleFinalRetry();
70
+ this.clearOfficeRetry();
71
+ if (this.checkIFrameSubscription) {
72
+ this.checkIFrameSubscription.unsubscribe();
73
+ }
74
+ }
75
+ async ngOnChanges(changes) {
76
+ if (changes &&
77
+ changes['retryNonce'] &&
78
+ changes['retryNonce'].firstChange) {
79
+ return;
80
+ }
81
+ if (changes &&
82
+ changes['viewer'] &&
83
+ (changes['viewer'].firstChange ||
84
+ changes['viewer'].currentValue !== changes['viewer'].previousValue)) {
85
+ if (this.viewer !== 'google' &&
86
+ this.viewer !== 'office' &&
87
+ this.viewer !== 'mammoth' &&
88
+ this.viewer !== 'pdf' &&
89
+ this.viewer !== 'url') {
90
+ console.error(`Unsupported viewer: '${this.viewer}'. Supported viewers: google, office, mammoth and pdf`);
91
+ }
92
+ this.configuredViewer = this.viewer;
93
+ this.updateTemplateContexts();
94
+ }
95
+ if ((changes['url'] &&
96
+ changes['url'].currentValue !== changes['url'].previousValue) ||
97
+ (changes['viewer'] &&
98
+ changes['viewer'].currentValue !== changes['viewer'].previousValue) ||
99
+ (changes['viewerUrl'] &&
100
+ changes['viewerUrl'].currentValue !==
101
+ changes['viewerUrl'].previousValue)) {
102
+ let viewerDetails = getViewerDetails(this.url, this.configuredViewer, this.queryParams, this.viewerUrl);
103
+ const loadVersion = ++this.loadVersion;
104
+ this.clearExternalLoadTimeout();
105
+ this.externalViewer = viewerDetails.externalViewer;
106
+ if (viewerDetails.externalViewer &&
107
+ this.overrideLocalhost &&
108
+ isLocalFile(this.url)) {
109
+ const newUrl = replaceLocalUrl(this.url, this.overrideLocalhost);
110
+ viewerDetails = getViewerDetails(newUrl, this.configuredViewer, this.queryParams, this.viewerUrl);
111
+ }
112
+ const officeSourceKey = this.configuredViewer === 'office' ? viewerDetails.url : undefined;
113
+ if (officeSourceKey !== this.currentOfficeSourceKey) {
114
+ this.officeAutoRetriedSourceKey = undefined;
115
+ }
116
+ this.currentOfficeSourceKey = officeSourceKey;
117
+ const googleSourceKey = this.configuredViewer === 'google' ? viewerDetails.url : undefined;
118
+ if (googleSourceKey !== this.currentGoogleSourceKey) {
119
+ this.googleFinalRetriedSourceKey = undefined;
120
+ }
121
+ this.currentGoogleSourceKey = googleSourceKey;
122
+ this.docHtml = '';
123
+ this.errorText = '';
124
+ this.failedUrl = this.url;
125
+ this.clearGoogleFinalRetry();
126
+ this.clearOfficeRetry();
127
+ this.updateTemplateContexts();
128
+ if (this.checkIFrameSubscription) {
129
+ this.checkIFrameSubscription.unsubscribe();
130
+ }
131
+ if (!this.url) {
132
+ this.fullUrl = undefined;
133
+ this.showIframe = false;
134
+ this.externalViewer = false;
135
+ this.setRenderPhase('idle');
136
+ this.updateTemplateContexts();
137
+ }
138
+ else if (viewerDetails.externalViewer ||
139
+ this.configuredViewer === 'url' ||
140
+ this.configuredViewer === 'pdf') {
141
+ this.setRenderPhase('loading');
142
+ const iframeUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(viewerDetails.url);
143
+ this.fullUrl = undefined;
144
+ this.showIframe = false;
145
+ this.cdr.detectChanges();
146
+ this.fullUrl = iframeUrl;
147
+ this.showIframe = true;
148
+ this.updateTemplateContexts();
149
+ this.scheduleExternalLoadTimeout(loadVersion);
150
+ this.cdr.detectChanges();
151
+ this.scheduleViewerRecovery();
152
+ }
153
+ else if (this.configuredViewer === 'mammoth') {
154
+ this.setRenderPhase('loading');
155
+ this.externalViewer = false;
156
+ this.fullUrl = undefined;
157
+ this.showIframe = false;
158
+ try {
159
+ const docHtml = await getDocxToHtml(this.url);
160
+ if (loadVersion !== this.loadVersion) {
161
+ return;
162
+ }
163
+ this.ngZone.run(() => {
164
+ this.docHtml = docHtml;
165
+ this.setRenderPhase('ready');
166
+ this.updateTemplateContexts();
167
+ this.cdr.detectChanges();
168
+ });
169
+ }
170
+ catch (error) {
171
+ if (loadVersion !== this.loadVersion) {
172
+ return;
173
+ }
174
+ this.ngZone.run(() => {
175
+ this.errorText =
176
+ error instanceof Error ? error.message : 'Unable to load document.';
177
+ this.setRenderPhase('error');
178
+ this.updateTemplateContexts();
179
+ this.cdr.detectChanges();
180
+ });
181
+ }
182
+ }
183
+ }
184
+ }
185
+ retryLoad() {
186
+ this.retryNonce += 1;
187
+ const retryVersion = ++this.loadVersion;
188
+ this.clearExternalLoadTimeout();
189
+ this.clearGoogleFinalRetry();
190
+ this.clearOfficeRetry();
191
+ this.errorText = '';
192
+ this.setRenderPhase(this.url ? 'loading' : 'idle');
193
+ this.updateTemplateContexts();
194
+ if (!this.url) {
195
+ return;
196
+ }
197
+ if (this.configuredViewer === 'mammoth') {
198
+ this.docHtml = '';
199
+ void this.reloadMammoth(retryVersion);
200
+ return;
201
+ }
202
+ if (this.configuredViewer === 'google' ||
203
+ this.configuredViewer === 'office' ||
204
+ this.configuredViewer === 'url' ||
205
+ this.configuredViewer === 'pdf') {
206
+ const details = getViewerDetails(this.url, this.configuredViewer, this.queryParams, this.viewerUrl);
207
+ const finalUrl = details.externalViewer && this.overrideLocalhost && isLocalFile(this.url)
208
+ ? getViewerDetails(replaceLocalUrl(this.url, this.overrideLocalhost), this.configuredViewer, this.queryParams, this.viewerUrl).url
209
+ : details.url;
210
+ this.fullUrl = undefined;
211
+ this.showIframe = false;
212
+ this.cdr.detectChanges();
213
+ this.fullUrl =
214
+ this.domSanitizer.bypassSecurityTrustResourceUrl(finalUrl);
215
+ this.showIframe = true;
216
+ this.scheduleExternalLoadTimeout(retryVersion);
217
+ this.scheduleViewerRecovery();
218
+ this.updateTemplateContexts();
219
+ this.cdr.detectChanges();
220
+ }
221
+ }
222
+ reloadIframe(iframe) {
223
+ this.checkIFrameSubscription = googleCheckSubscription();
224
+ this.checkIFrameSubscription.subscribe(iframe, this.googleCheckInterval, this.googleMaxChecks);
225
+ }
226
+ scheduleViewerRecovery() {
227
+ const recoveryPlan = getViewerRecoveryPlan({
228
+ viewer: this.configuredViewer,
229
+ googleCheckContentLoaded: this.googleCheckContentLoaded,
230
+ googleFinalRetryDelay: this.googleFinalRetryDelay,
231
+ officeAutoRetry: this.officeAutoRetry,
232
+ });
233
+ for (const mode of recoveryPlan.modes) {
234
+ if (mode === 'google-probe') {
235
+ this.scheduleGoogleRecovery();
236
+ }
237
+ if (mode === 'google-final-retry') {
238
+ continue;
239
+ }
240
+ if (mode === 'office-auto-retry') {
241
+ this.scheduleOfficeRetry();
242
+ }
243
+ }
244
+ }
245
+ scheduleGoogleRecovery() {
246
+ if (this.configuredViewer !== 'google' ||
247
+ !this.googleCheckContentLoaded) {
248
+ return;
249
+ }
250
+ // see:
251
+ // https://stackoverflow.com/questions/40414039/google-docs-viewer-returning-204-responses-no-longer-working-alternatives
252
+ // hack to reload iframe if it's not loaded.
253
+ this.ngZone.runOutsideAngular(() => {
254
+ window.setTimeout(() => {
255
+ const iframe = this.iframes?.first?.nativeElement;
256
+ if (iframe) {
257
+ this.reloadIframe(iframe);
258
+ }
259
+ }, 0);
260
+ });
261
+ }
262
+ iframeLoaded() {
263
+ const iframe = this.iframes?.first?.nativeElement;
264
+ if (iframe && iframeIsLoaded(iframe)) {
265
+ this.clearExternalLoadTimeout();
266
+ this.clearGoogleFinalRetry();
267
+ this.clearOfficeRetry();
268
+ this.setRenderPhase('ready');
269
+ this.updateTemplateContexts();
270
+ this.loaded.emit(undefined);
271
+ if (this.checkIFrameSubscription) {
272
+ this.checkIFrameSubscription.unsubscribe();
273
+ }
274
+ }
275
+ }
276
+ objectLoaded() {
277
+ this.clearExternalLoadTimeout();
278
+ this.clearGoogleFinalRetry();
279
+ this.clearOfficeRetry();
280
+ this.setRenderPhase('ready');
281
+ this.updateTemplateContexts();
282
+ }
283
+ async reloadMammoth(loadVersion) {
284
+ this.externalViewer = false;
285
+ this.fullUrl = undefined;
286
+ this.showIframe = false;
287
+ try {
288
+ const docHtml = await getDocxToHtml(this.url);
289
+ if (loadVersion !== this.loadVersion) {
290
+ return;
291
+ }
292
+ this.ngZone.run(() => {
293
+ this.docHtml = docHtml;
294
+ this.setRenderPhase('ready');
295
+ this.updateTemplateContexts();
296
+ this.cdr.detectChanges();
297
+ });
298
+ }
299
+ catch (error) {
300
+ if (loadVersion !== this.loadVersion) {
301
+ return;
302
+ }
303
+ this.ngZone.run(() => {
304
+ this.errorText =
305
+ error instanceof Error ? error.message : 'Unable to load document.';
306
+ this.setRenderPhase('error');
307
+ this.updateTemplateContexts();
308
+ this.cdr.detectChanges();
309
+ });
310
+ }
311
+ }
312
+ scheduleExternalLoadTimeout(loadVersion) {
313
+ this.clearExternalLoadTimeout();
314
+ const timeoutMs = this.configuredViewer === 'google'
315
+ ? Math.max(this.googleCheckInterval * this.googleMaxChecks + 2000, 15000)
316
+ : 15000;
317
+ this.externalLoadTimeoutId = window.setTimeout(() => {
318
+ if (loadVersion !== this.loadVersion || this.renderPhase !== 'loading') {
319
+ return;
320
+ }
321
+ if (this.scheduleGoogleFinalRetry()) {
322
+ return;
323
+ }
324
+ this.ngZone.run(() => {
325
+ this.errorText = `The ${this.configuredViewer} viewer did not finish loading in time.`;
326
+ this.setRenderPhase('error');
327
+ this.updateTemplateContexts();
328
+ this.cdr.detectChanges();
329
+ });
330
+ }, timeoutMs);
331
+ }
332
+ clearExternalLoadTimeout() {
333
+ if (this.externalLoadTimeoutId) {
334
+ window.clearTimeout(this.externalLoadTimeoutId);
335
+ this.externalLoadTimeoutId = undefined;
336
+ }
337
+ }
338
+ clearGoogleFinalRetry() {
339
+ if (this.googleFinalRetryTimeoutId) {
340
+ window.clearTimeout(this.googleFinalRetryTimeoutId);
341
+ this.googleFinalRetryTimeoutId = undefined;
342
+ }
343
+ }
344
+ clearOfficeRetry() {
345
+ if (this.officeRetryTimeoutId) {
346
+ window.clearTimeout(this.officeRetryTimeoutId);
347
+ this.officeRetryTimeoutId = undefined;
348
+ }
349
+ }
350
+ scheduleOfficeRetry() {
351
+ if (this.configuredViewer !== 'office' ||
352
+ !this.officeAutoRetry ||
353
+ !this.currentOfficeSourceKey ||
354
+ this.officeAutoRetriedSourceKey === this.currentOfficeSourceKey) {
355
+ return;
356
+ }
357
+ this.clearOfficeRetry();
358
+ this.officeRetryTimeoutId = window.setTimeout(() => {
359
+ if (!this.currentOfficeSourceKey ||
360
+ this.officeAutoRetriedSourceKey === this.currentOfficeSourceKey) {
361
+ return;
362
+ }
363
+ this.officeAutoRetriedSourceKey = this.currentOfficeSourceKey;
364
+ this.ngZone.run(() => this.retryLoad());
365
+ }, this.officeRetryDelay);
366
+ }
367
+ scheduleGoogleFinalRetry() {
368
+ const recoveryPlan = getViewerRecoveryPlan({
369
+ viewer: this.configuredViewer,
370
+ googleCheckContentLoaded: this.googleCheckContentLoaded,
371
+ googleFinalRetryDelay: this.googleFinalRetryDelay,
372
+ officeAutoRetry: this.officeAutoRetry,
373
+ });
374
+ if (!recoveryPlan.modes.includes('google-final-retry') ||
375
+ !this.currentGoogleSourceKey ||
376
+ this.googleFinalRetriedSourceKey === this.currentGoogleSourceKey) {
377
+ return false;
378
+ }
379
+ this.clearGoogleFinalRetry();
380
+ this.googleFinalRetryTimeoutId = window.setTimeout(() => {
381
+ if (!this.currentGoogleSourceKey ||
382
+ this.googleFinalRetriedSourceKey === this.currentGoogleSourceKey) {
383
+ return;
384
+ }
385
+ this.googleFinalRetriedSourceKey = this.currentGoogleSourceKey;
386
+ this.ngZone.run(() => this.retryLoad());
387
+ }, this.googleFinalRetryDelay);
388
+ return true;
389
+ }
390
+ get displayedErrorText() {
391
+ return this.errorTextOverride || this.errorText || 'Unable to load document.';
392
+ }
393
+ updateTemplateContexts() {
394
+ const state = this.createStateContext();
395
+ this.loadingTemplateContext = { $implicit: state, state };
396
+ this.errorTemplateContext = { $implicit: state, state };
397
+ }
398
+ createTemplateContext() {
399
+ const state = this.createStateContext();
400
+ return { $implicit: state, state };
401
+ }
402
+ createStateContext() {
403
+ return {
404
+ viewer: this.configuredViewer,
405
+ url: this.failedUrl || this.url,
406
+ phase: this.renderPhase,
407
+ errorText: this.displayedErrorText,
408
+ retry: () => this.retryLoad(),
409
+ actionUrl: this.failedUrl || this.url,
410
+ };
411
+ }
412
+ setRenderPhase(phase) {
413
+ this.renderPhase = phase;
414
+ this.emitLifecycleIfNeeded();
415
+ }
416
+ emitLifecycleIfNeeded() {
417
+ if (this.lastEmittedPhase === this.renderPhase) {
418
+ return;
419
+ }
420
+ this.lastEmittedPhase = this.renderPhase;
421
+ const state = this.createStateContext();
422
+ this.phaseChange.emit(state);
423
+ if (this.renderPhase === 'loading') {
424
+ this.loading.emit(state);
425
+ }
426
+ if (this.renderPhase === 'error') {
427
+ this.error.emit(state);
428
+ }
429
+ }
430
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NgxDocViewerComponent, deps: [{ token: i1.DomSanitizer }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
431
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.4", type: NgxDocViewerComponent, isStandalone: true, selector: "ngx-doc-viewer", inputs: { url: "url", queryParams: "queryParams", viewerUrl: "viewerUrl", googleCheckInterval: "googleCheckInterval", googleMaxChecks: "googleMaxChecks", googleFinalRetryDelay: "googleFinalRetryDelay", disableContent: "disableContent", googleCheckContentLoaded: "googleCheckContentLoaded", viewer: "viewer", overrideLocalhost: "overrideLocalhost", loadingText: "loadingText", errorTextOverride: "errorTextOverride", retryButtonText: "retryButtonText", officeAutoRetry: "officeAutoRetry", officeRetryDelay: "officeRetryDelay", officeReloadButtonText: "officeReloadButtonText", officeReloadButtonTitle: "officeReloadButtonTitle", secondaryActionText: "secondaryActionText", secondaryActionMode: "secondaryActionMode" }, outputs: { loaded: "loaded", loading: "loading", error: "error", phaseChange: "phaseChange" }, queries: [{ propertyName: "loadingTemplate", first: true, predicate: ["loadingContent"], descendants: true, read: TemplateRef }, { propertyName: "errorTemplate", first: true, predicate: ["errorContent"], descendants: true, read: TemplateRef }, { propertyName: "errorActionsTemplate", first: true, predicate: ["errorActions"], descendants: true, read: TemplateRef }, { propertyName: "officeReloadTemplate", first: true, predicate: ["officeReloadContent"], descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "iframes", predicate: ["iframe"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"container\">\n <div *ngIf=\"renderPhase === 'loading'\" class=\"loading-overlay\">\n <ng-container *ngIf=\"loadingTemplate; else defaultLoadingTemplate\">\n <ng-container *ngTemplateOutlet=\"loadingTemplate; context: loadingTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultLoadingTemplate>{{ loadingText }}</ng-template>\n </div>\n <div *ngIf=\"renderPhase === 'error'\" class=\"error-overlay\">\n <ng-container *ngIf=\"errorTemplate; else defaultErrorTemplate\">\n <ng-container *ngTemplateOutlet=\"errorTemplate; context: errorTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultErrorTemplate>\n <div>\n <div>{{ displayedErrorText }}</div>\n <div *ngIf=\"configuredViewer\" style=\"margin-top: 8px; color: #475569;\">\n Viewer: {{ configuredViewer }}\n </div>\n <div *ngIf=\"failedUrl\" style=\"margin-top: 4px; color: #64748b; font-size: 12px; word-break: break-word;\">\n {{ failedUrl }}\n </div>\n <ng-container *ngIf=\"errorActionsTemplate; else defaultErrorActions\">\n <ng-container *ngTemplateOutlet=\"errorActionsTemplate; context: errorTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultErrorActions>\n <div style=\"margin-top: 14px; display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;\">\n <button\n type=\"button\"\n (click)=\"retryLoad()\"\n style=\"border: 1px solid #fecaca; background: #fff; color: #991b1b; border-radius: 999px; padding: 8px 14px; font: inherit; cursor: pointer;\"\n >\n {{ retryButtonText }}\n </button>\n <a\n *ngIf=\"secondaryActionText && failedUrl\"\n [href]=\"failedUrl\"\n [attr.target]=\"secondaryActionMode === 'open' ? '_blank' : null\"\n [attr.rel]=\"secondaryActionMode === 'open' ? 'noreferrer' : null\"\n [attr.download]=\"secondaryActionMode === 'download' ? '' : null\"\n style=\"border: 1px solid #cbd5e1; background: #fff; color: #334155; border-radius: 999px; padding: 8px 14px; text-decoration: none; font: inherit;\"\n >\n {{ secondaryActionText }}\n </a>\n </div>\n </ng-template>\n </div>\n </ng-template>\n </div>\n\n <ng-container *ngIf=\"!externalViewer\">\n <div *ngIf=\"configuredViewer !== 'pdf'\" class=\"inline-document-shell\">\n <div class=\"inline-document-page\" [innerHTML]=\"docHtml\"></div>\n </div>\n <object\n (load)=\"objectLoaded()\"\n *ngIf=\"fullUrl && configuredViewer === 'pdf'\"\n [data]=\"fullUrl\"\n type=\"application/pdf\"\n width=\"100%\"\n height=\"100%\"\n >\n <p>\n Your browser does not support PDFs.\n <a [href]=\"fullUrl\">Download the PDF</a>.\n </p>\n </object>\n </ng-container>\n\n <button\n *ngIf=\"configuredViewer === 'office' && renderPhase === 'ready'\"\n class=\"office-reload-btn\"\n type=\"button\"\n [attr.title]=\"officeReloadButtonTitle\"\n (click)=\"retryLoad()\"\n >\n <ng-container *ngIf=\"officeReloadTemplate; else defaultOfficeReloadTemplate\">\n <ng-container *ngTemplateOutlet=\"officeReloadTemplate; context: errorTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultOfficeReloadTemplate>{{ officeReloadButtonText }}</ng-template>\n </button>\n\n <ng-container *ngIf=\"externalViewer\">\n <iframe\n (load)=\"iframeLoaded()\"\n *ngIf=\"fullUrl && showIframe && disableContent === 'none'\"\n #iframe\n id=\"iframe-doc-viewer\"\n frameBorder=\"0\"\n [src]=\"fullUrl\"\n ></iframe>\n <div class=\"container\" *ngIf=\"disableContent !== 'none'\">\n <div\n [class.overlay-full]=\"disableContent === 'all'\"\n [class.overlay-popout-google]=\"\n configuredViewer === 'google' &&\n (disableContent === 'popout' || disableContent === 'popout-hide')\n \"\n [class.overlay-popout-office]=\"\n configuredViewer === 'office' &&\n (disableContent === 'popout' || disableContent === 'popout-hide')\n \"\n [style.background-color]=\"\n disableContent === 'popout-hide' ? '#fff' : 'transparent'\n \"\n ></div>\n <iframe\n (load)=\"iframeLoaded()\"\n *ngIf=\"fullUrl && showIframe\"\n #iframe\n id=\"iframe\"\n frameBorder=\"0\"\n [src]=\"fullUrl\"\n ></iframe>\n </div>\n </ng-container>\n</div>\n", styles: [":host{display:block}.container{width:100%;height:100%;position:relative}.inline-document-shell{width:100%;height:100%;overflow:auto;background:linear-gradient(180deg,#eef2f7,#f8fafc);padding:24px}.inline-document-page{max-width:900px;min-height:100%;margin:0 auto;background:#fff;border:1px solid #dbe4f0;border-radius:18px;box-shadow:0 18px 40px -24px #0f172a4d,0 6px 18px -10px #0f172a2e;padding:40px 48px}.inline-document-page,.inline-document-page *{box-sizing:border-box}.inline-document-page{color:#1e293b;font-family:Georgia,Cambria,Times New Roman,Times,serif;font-size:18px;line-height:1.7}.inline-document-page p,.inline-document-page ul,.inline-document-page ol,.inline-document-page blockquote,.inline-document-page table{margin:0 0 1.1em}.inline-document-page h1,.inline-document-page h2,.inline-document-page h3,.inline-document-page h4,.inline-document-page h5,.inline-document-page h6{margin:1.4em 0 .65em;color:#0f172a;line-height:1.25}.inline-document-page h1:first-child,.inline-document-page h2:first-child,.inline-document-page h3:first-child,.inline-document-page p:first-child{margin-top:0}.inline-document-page table{width:100%;border-collapse:collapse}.inline-document-page td,.inline-document-page th{padding:6px 10px;vertical-align:top}.inline-document-page img{max-width:100%;height:auto}.inline-document-page hr{border:0;border-top:1px solid #dbe4f0;margin:1.5em 0}.loading-overlay{position:absolute;inset:0;z-index:1001;display:flex;align-items:center;justify-content:center;background:#ffffffe0;color:#475569;font-size:14px;font-weight:600;letter-spacing:.02em;text-align:center;padding:16px}.error-overlay{position:absolute;inset:0;z-index:1001;display:flex;align-items:center;justify-content:center;background:#fffffff0;color:#991b1b;font-size:14px;font-weight:600;letter-spacing:.02em;text-align:center;padding:16px}.overlay-popout-google{width:40px;height:40px;right:26px;top:11.5px;position:absolute;z-index:1000}.overlay-popout-office{width:100px;height:20px;right:0;bottom:0;position:absolute;z-index:1000}.office-reload-btn{position:absolute;top:8px;right:8px;z-index:1001;width:32px;height:32px;border-radius:50%;border:1px solid rgba(0,0,0,.12);background:#ffffffd9;color:#475569;font-size:16px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:.5;transition:opacity .15s;padding:0}.office-reload-btn:hover{opacity:1}.overlay-full{width:100%;height:100%;right:0;top:0;position:absolute;z-index:1000}iframe{width:100%;height:100%}@media(max-width:768px){.inline-document-shell{padding:12px}.inline-document-page{border-radius:12px;padding:22px 18px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
432
+ }
433
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NgxDocViewerComponent, decorators: [{
434
+ type: Component,
435
+ args: [{ selector: 'ngx-doc-viewer', standalone: true, imports: [CommonModule], template: "<div class=\"container\">\n <div *ngIf=\"renderPhase === 'loading'\" class=\"loading-overlay\">\n <ng-container *ngIf=\"loadingTemplate; else defaultLoadingTemplate\">\n <ng-container *ngTemplateOutlet=\"loadingTemplate; context: loadingTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultLoadingTemplate>{{ loadingText }}</ng-template>\n </div>\n <div *ngIf=\"renderPhase === 'error'\" class=\"error-overlay\">\n <ng-container *ngIf=\"errorTemplate; else defaultErrorTemplate\">\n <ng-container *ngTemplateOutlet=\"errorTemplate; context: errorTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultErrorTemplate>\n <div>\n <div>{{ displayedErrorText }}</div>\n <div *ngIf=\"configuredViewer\" style=\"margin-top: 8px; color: #475569;\">\n Viewer: {{ configuredViewer }}\n </div>\n <div *ngIf=\"failedUrl\" style=\"margin-top: 4px; color: #64748b; font-size: 12px; word-break: break-word;\">\n {{ failedUrl }}\n </div>\n <ng-container *ngIf=\"errorActionsTemplate; else defaultErrorActions\">\n <ng-container *ngTemplateOutlet=\"errorActionsTemplate; context: errorTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultErrorActions>\n <div style=\"margin-top: 14px; display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;\">\n <button\n type=\"button\"\n (click)=\"retryLoad()\"\n style=\"border: 1px solid #fecaca; background: #fff; color: #991b1b; border-radius: 999px; padding: 8px 14px; font: inherit; cursor: pointer;\"\n >\n {{ retryButtonText }}\n </button>\n <a\n *ngIf=\"secondaryActionText && failedUrl\"\n [href]=\"failedUrl\"\n [attr.target]=\"secondaryActionMode === 'open' ? '_blank' : null\"\n [attr.rel]=\"secondaryActionMode === 'open' ? 'noreferrer' : null\"\n [attr.download]=\"secondaryActionMode === 'download' ? '' : null\"\n style=\"border: 1px solid #cbd5e1; background: #fff; color: #334155; border-radius: 999px; padding: 8px 14px; text-decoration: none; font: inherit;\"\n >\n {{ secondaryActionText }}\n </a>\n </div>\n </ng-template>\n </div>\n </ng-template>\n </div>\n\n <ng-container *ngIf=\"!externalViewer\">\n <div *ngIf=\"configuredViewer !== 'pdf'\" class=\"inline-document-shell\">\n <div class=\"inline-document-page\" [innerHTML]=\"docHtml\"></div>\n </div>\n <object\n (load)=\"objectLoaded()\"\n *ngIf=\"fullUrl && configuredViewer === 'pdf'\"\n [data]=\"fullUrl\"\n type=\"application/pdf\"\n width=\"100%\"\n height=\"100%\"\n >\n <p>\n Your browser does not support PDFs.\n <a [href]=\"fullUrl\">Download the PDF</a>.\n </p>\n </object>\n </ng-container>\n\n <button\n *ngIf=\"configuredViewer === 'office' && renderPhase === 'ready'\"\n class=\"office-reload-btn\"\n type=\"button\"\n [attr.title]=\"officeReloadButtonTitle\"\n (click)=\"retryLoad()\"\n >\n <ng-container *ngIf=\"officeReloadTemplate; else defaultOfficeReloadTemplate\">\n <ng-container *ngTemplateOutlet=\"officeReloadTemplate; context: errorTemplateContext\"></ng-container>\n </ng-container>\n <ng-template #defaultOfficeReloadTemplate>{{ officeReloadButtonText }}</ng-template>\n </button>\n\n <ng-container *ngIf=\"externalViewer\">\n <iframe\n (load)=\"iframeLoaded()\"\n *ngIf=\"fullUrl && showIframe && disableContent === 'none'\"\n #iframe\n id=\"iframe-doc-viewer\"\n frameBorder=\"0\"\n [src]=\"fullUrl\"\n ></iframe>\n <div class=\"container\" *ngIf=\"disableContent !== 'none'\">\n <div\n [class.overlay-full]=\"disableContent === 'all'\"\n [class.overlay-popout-google]=\"\n configuredViewer === 'google' &&\n (disableContent === 'popout' || disableContent === 'popout-hide')\n \"\n [class.overlay-popout-office]=\"\n configuredViewer === 'office' &&\n (disableContent === 'popout' || disableContent === 'popout-hide')\n \"\n [style.background-color]=\"\n disableContent === 'popout-hide' ? '#fff' : 'transparent'\n \"\n ></div>\n <iframe\n (load)=\"iframeLoaded()\"\n *ngIf=\"fullUrl && showIframe\"\n #iframe\n id=\"iframe\"\n frameBorder=\"0\"\n [src]=\"fullUrl\"\n ></iframe>\n </div>\n </ng-container>\n</div>\n", styles: [":host{display:block}.container{width:100%;height:100%;position:relative}.inline-document-shell{width:100%;height:100%;overflow:auto;background:linear-gradient(180deg,#eef2f7,#f8fafc);padding:24px}.inline-document-page{max-width:900px;min-height:100%;margin:0 auto;background:#fff;border:1px solid #dbe4f0;border-radius:18px;box-shadow:0 18px 40px -24px #0f172a4d,0 6px 18px -10px #0f172a2e;padding:40px 48px}.inline-document-page,.inline-document-page *{box-sizing:border-box}.inline-document-page{color:#1e293b;font-family:Georgia,Cambria,Times New Roman,Times,serif;font-size:18px;line-height:1.7}.inline-document-page p,.inline-document-page ul,.inline-document-page ol,.inline-document-page blockquote,.inline-document-page table{margin:0 0 1.1em}.inline-document-page h1,.inline-document-page h2,.inline-document-page h3,.inline-document-page h4,.inline-document-page h5,.inline-document-page h6{margin:1.4em 0 .65em;color:#0f172a;line-height:1.25}.inline-document-page h1:first-child,.inline-document-page h2:first-child,.inline-document-page h3:first-child,.inline-document-page p:first-child{margin-top:0}.inline-document-page table{width:100%;border-collapse:collapse}.inline-document-page td,.inline-document-page th{padding:6px 10px;vertical-align:top}.inline-document-page img{max-width:100%;height:auto}.inline-document-page hr{border:0;border-top:1px solid #dbe4f0;margin:1.5em 0}.loading-overlay{position:absolute;inset:0;z-index:1001;display:flex;align-items:center;justify-content:center;background:#ffffffe0;color:#475569;font-size:14px;font-weight:600;letter-spacing:.02em;text-align:center;padding:16px}.error-overlay{position:absolute;inset:0;z-index:1001;display:flex;align-items:center;justify-content:center;background:#fffffff0;color:#991b1b;font-size:14px;font-weight:600;letter-spacing:.02em;text-align:center;padding:16px}.overlay-popout-google{width:40px;height:40px;right:26px;top:11.5px;position:absolute;z-index:1000}.overlay-popout-office{width:100px;height:20px;right:0;bottom:0;position:absolute;z-index:1000}.office-reload-btn{position:absolute;top:8px;right:8px;z-index:1001;width:32px;height:32px;border-radius:50%;border:1px solid rgba(0,0,0,.12);background:#ffffffd9;color:#475569;font-size:16px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:.5;transition:opacity .15s;padding:0}.office-reload-btn:hover{opacity:1}.overlay-full{width:100%;height:100%;right:0;top:0;position:absolute;z-index:1000}iframe{width:100%;height:100%}@media(max-width:768px){.inline-document-shell{padding:12px}.inline-document-page{border-radius:12px;padding:22px 18px}}\n"] }]
436
+ }], ctorParameters: () => [{ type: i1.DomSanitizer }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }], propDecorators: { loaded: [{
437
+ type: Output
438
+ }], loading: [{
439
+ type: Output
440
+ }], error: [{
441
+ type: Output
442
+ }], phaseChange: [{
443
+ type: Output
444
+ }], url: [{
445
+ type: Input
446
+ }], queryParams: [{
447
+ type: Input
448
+ }], viewerUrl: [{
449
+ type: Input
450
+ }], googleCheckInterval: [{
451
+ type: Input
452
+ }], googleMaxChecks: [{
453
+ type: Input
454
+ }], googleFinalRetryDelay: [{
455
+ type: Input
456
+ }], disableContent: [{
457
+ type: Input
458
+ }], googleCheckContentLoaded: [{
459
+ type: Input
460
+ }], viewer: [{
461
+ type: Input
462
+ }], overrideLocalhost: [{
463
+ type: Input
464
+ }], loadingText: [{
465
+ type: Input
466
+ }], errorTextOverride: [{
467
+ type: Input
468
+ }], retryButtonText: [{
469
+ type: Input
470
+ }], officeAutoRetry: [{
471
+ type: Input
472
+ }], officeRetryDelay: [{
473
+ type: Input
474
+ }], officeReloadButtonText: [{
475
+ type: Input
476
+ }], officeReloadButtonTitle: [{
477
+ type: Input
478
+ }], secondaryActionText: [{
479
+ type: Input
480
+ }], secondaryActionMode: [{
481
+ type: Input
482
+ }], loadingTemplate: [{
483
+ type: ContentChild,
484
+ args: ['loadingContent', { read: TemplateRef }]
485
+ }], errorTemplate: [{
486
+ type: ContentChild,
487
+ args: ['errorContent', { read: TemplateRef }]
488
+ }], errorActionsTemplate: [{
489
+ type: ContentChild,
490
+ args: ['errorActions', { read: TemplateRef }]
491
+ }], officeReloadTemplate: [{
492
+ type: ContentChild,
493
+ args: ['officeReloadContent', { read: TemplateRef }]
494
+ }], iframes: [{
495
+ type: ViewChildren,
496
+ args: ['iframe']
497
+ }] } });
498
+
499
+ class NgxDocViewerModule {
500
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NgxDocViewerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
501
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: NgxDocViewerModule, imports: [CommonModule, NgxDocViewerComponent], exports: [NgxDocViewerComponent] });
502
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NgxDocViewerModule, imports: [CommonModule, NgxDocViewerComponent] });
503
+ }
504
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NgxDocViewerModule, decorators: [{
505
+ type: NgModule,
506
+ args: [{
507
+ imports: [CommonModule, NgxDocViewerComponent],
508
+ exports: [NgxDocViewerComponent],
509
+ }]
510
+ }] });
511
+
512
+ /**
513
+ * Generated bundle index. Do not edit.
514
+ */
515
+
516
+ export { NgxDocViewerComponent, NgxDocViewerModule };
517
+ //# sourceMappingURL=ngx-doc-viewer.mjs.map