@schukai/monster 4.43.2 → 4.43.3

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.
@@ -13,9 +13,9 @@
13
13
  */
14
14
 
15
15
  import {
16
- assembleMethodSymbol,
17
- CustomElement,
18
- registerCustomElement,
16
+ assembleMethodSymbol,
17
+ CustomElement,
18
+ registerCustomElement,
19
19
  } from "../../dom/customelement.mjs";
20
20
  import "../notify/notify.mjs";
21
21
  import { ViewerStyleSheet } from "./stylesheet/viewer.mjs";
@@ -62,711 +62,711 @@ const downloadRevokeSymbol = Symbol("downloadRevoke");
62
62
  * @summary A simple viewer component for PDF, HTML, and images.
63
63
  */
64
64
  class Viewer extends CustomElement {
65
- /**
66
- * This method is called by the `instanceof` operator.
67
- * @return {symbol}
68
- */
69
- static get [instanceSymbol]() {
70
- return Symbol.for("@schukai/monster/components/content/viewer@@instance");
71
- }
72
-
73
- /**
74
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
75
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
76
- *
77
- * The individual configuration values can be found in the table.
78
- *
79
- * @property {Object} templates Template definitions
80
- * @property {string} templates.main Main template
81
- * @property {string} content Content to be displayed in the viewer
82
- * @property {Object} classes Css classes
83
- * @property {string} classes.viewer Css class for the viewer
84
- * @property {Object} renderers Renderers for different media types
85
- * @property {function} renderers.image Function to render image content
86
- * @property {function} renderers.html Function to render HTML content
87
- * @property {function} renderers.pdf Function to render PDF content
88
- * @property {function} renderers.plaintext Function to render plain text content
89
- * @property {function} renderers.markdown Function to render Markdown content
90
- */
91
- get defaults() {
92
- return Object.assign({}, super.defaults, {
93
- templates: {
94
- main: getTemplate(),
95
- },
96
- content: "<slot></slot>",
97
- classes: {
98
- viewer: "",
99
- },
100
- labels: getLabels(),
101
- renderers: {
102
- image: this.setImage,
103
- html: this.setHTML,
104
- pdf: this.setPDF,
105
- download: this.setDownload,
106
- plaintext: this.setPlainText,
107
- markdown: this.setMarkdown,
108
- audio: this.setAudio,
109
- video: this.setVideo,
110
- message: this.setMessage,
111
- },
112
- });
113
- }
114
-
115
- /**
116
- * @private
117
- * @type {symbol}
118
- * @memberof Viewer
119
- * @static
120
- * @description downloadHandlerSymbol is used to store the download handler function.
121
- * This is necessary to remove the event listener when the element is disconnected.
122
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener}
123
- */
124
- disconnectedCallback() {
125
- super.disconnectedCallback?.();
126
- if (this[downloadHandlerSymbol]) {
127
- this.removeEventListener("click", this[downloadHandlerSymbol]);
128
- this[downloadHandlerSymbol] = null;
129
- }
130
- if (this[downloadRevokeSymbol]) {
131
- try {
132
- this[downloadRevokeSymbol]();
133
- } catch {}
134
- this[downloadRevokeSymbol] = null;
135
- }
136
- }
137
-
138
- /**
139
- * Sets the content of an element based on the provided content and media type.
140
- *
141
- * @param {string} content - The content to be set.
142
- * @param {string} [mediaType="text/plain"] - The media type of the content. Defaults to "text/plain" if not specified.
143
- * @return {void} This method does not return a value.
144
- * @throws {Error} Throws an error if shadowRoot is not defined.
145
- */
146
- setContent(content, mediaType = "text/plain") {
147
- if (!this.shadowRoot) {
148
- throw new Error("no shadow-root is defined");
149
- }
150
-
151
- const renderers = this.getOption("renderers");
152
-
153
- const isDataURL = (value) => {
154
- return (
155
- (typeof value === "string" && value.startsWith("data:")) ||
156
- (value instanceof URL && value.protocol === "data:")
157
- );
158
- };
159
-
160
- if (isDataURL(content)) {
161
- try {
162
- const dataUrl = content.toString();
163
- const [header] = dataUrl.split(",");
164
- const [typeSegment] = header.split(";");
165
- mediaType = typeSegment.replace("data:", "") || "text/plain";
166
- } catch (error) {
167
- this.dispatchEvent(
168
- new CustomEvent("viewer-error", {
169
- detail: "Invalid data URL format",
170
- }),
171
- );
172
- return;
173
- }
174
- }
175
-
176
- if (mediaType === undefined || mediaType === null || mediaType === "") {
177
- mediaType = "text/plain";
178
- }
179
-
180
- let mediaTypeObject;
181
-
182
- try {
183
- mediaTypeObject = new parseMediaType(mediaType);
184
- if (!(mediaTypeObject instanceof MediaType)) {
185
- this.dispatchEvent(
186
- new CustomEvent("viewer-error", { detail: "Invalid MediaType" }),
187
- );
188
- return;
189
- }
190
- } catch (error) {
191
- this.dispatchEvent(new CustomEvent("viewer-error", { detail: error }));
192
- return;
193
- }
194
-
195
- const checkRenderer = (renderer, contentType) => {
196
- if (renderers && typeof renderers[renderer] === "function") {
197
- return true;
198
- } else {
199
- this.dispatchEvent(
200
- new CustomEvent("viewer-error", {
201
- detail: `Renderer for ${contentType} not found`,
202
- }),
203
- );
204
- return false;
205
- }
206
- };
207
-
208
- switch (mediaTypeObject.type) {
209
- case "text":
210
- switch (mediaTypeObject.subtype) {
211
- case "html":
212
- if (checkRenderer("html", mediaTypeObject.toString())) {
213
- renderers.html.call(this, content);
214
- }
215
- break;
216
- case "plain":
217
- if (checkRenderer("plaintext", mediaTypeObject.toString())) {
218
- renderers.plaintext.call(this, content);
219
- }
220
- break;
221
- case "markdown":
222
- if (checkRenderer("markdown", mediaTypeObject.toString())) {
223
- this.setMarkdown(content);
224
- }
225
- break;
226
- default:
227
- if (checkRenderer("plaintext", mediaTypeObject.toString())) {
228
- renderers.plaintext.call(this, content);
229
- }
230
- break;
231
- }
232
- break;
233
-
234
- case "application":
235
- switch (mediaTypeObject.subtype) {
236
- case "pdf":
237
- if (checkRenderer("pdf", mediaTypeObject.toString())) {
238
- renderers.pdf.call(this, content);
239
- }
240
- break;
241
-
242
- default:
243
- // Handle octet-stream as a generic binary data type
244
- if (checkRenderer("download", mediaTypeObject.toString())) {
245
- renderers.download.call(this, content);
246
- break;
247
- }
248
-
249
- this.setOption("content", content);
250
- break;
251
- }
252
- break;
253
-
254
- case "message":
255
- switch (mediaTypeObject.subtype) {
256
- case "rfc822":
257
- if (checkRenderer("message", mediaTypeObject.toString())) {
258
- renderers.message.call(this, content);
259
- }
260
- break;
261
-
262
- default:
263
- this.setOption("content", content);
264
- break;
265
- }
266
- break;
267
-
268
- case "audio":
269
- if (checkRenderer(mediaTypeObject.type, mediaTypeObject.toString())) {
270
- renderers[mediaTypeObject.type].call(this, content);
271
- }
272
- break;
273
-
274
- case "video":
275
- if (checkRenderer(mediaTypeObject.type, mediaTypeObject.toString())) {
276
- renderers[mediaTypeObject.type].call(this, content);
277
- }
278
- break;
279
-
280
- case "image":
281
- if (checkRenderer("image", mediaTypeObject.toString())) {
282
- renderers.image.call(this, content);
283
- }
284
- break;
285
-
286
- default:
287
- this.setOption("content", content);
288
- this.dispatchEvent(
289
- new CustomEvent("viewer-error", {
290
- detail: `Unsupported media type: ${mediaTypeObject.toString()}`,
291
- }),
292
- ); // Notify about unsupported media type
293
- return;
294
- }
295
- }
296
-
297
- /**
298
- * Sets the audio content for the viewer. Accepts a Blob, URL, or string and processes it
299
- * to configure audio playback within the viewer. Throws an error if the input type is invalid.
300
- *
301
- * @param {Blob|string} data - The audio content. This can be a Blob, a URL, or a string.
302
- * @return {void} No return value.
303
- */
304
- setAudio(data) {
305
- if (isBlob(data)) {
306
- data = URL.createObjectURL(data);
307
- } else if (isURL(data)) {
308
- // nothing to do
309
- } else if (isString(data)) {
310
- // nothing to do
311
- } else {
312
- this.dispatchEvent(
313
- new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
314
- );
315
- throw new Error("Blob or URL expected");
316
- }
317
-
318
- this.setOption(
319
- "content",
320
- `
65
+ /**
66
+ * This method is called by the `instanceof` operator.
67
+ * @return {symbol}
68
+ */
69
+ static get [instanceSymbol]() {
70
+ return Symbol.for("@schukai/monster/components/content/viewer@@instance");
71
+ }
72
+
73
+ /**
74
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
75
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
76
+ *
77
+ * The individual configuration values can be found in the table.
78
+ *
79
+ * @property {Object} templates Template definitions
80
+ * @property {string} templates.main Main template
81
+ * @property {string} content Content to be displayed in the viewer
82
+ * @property {Object} classes Css classes
83
+ * @property {string} classes.viewer Css class for the viewer
84
+ * @property {Object} renderers Renderers for different media types
85
+ * @property {function} renderers.image Function to render image content
86
+ * @property {function} renderers.html Function to render HTML content
87
+ * @property {function} renderers.pdf Function to render PDF content
88
+ * @property {function} renderers.plaintext Function to render plain text content
89
+ * @property {function} renderers.markdown Function to render Markdown content
90
+ */
91
+ get defaults() {
92
+ return Object.assign({}, super.defaults, {
93
+ templates: {
94
+ main: getTemplate(),
95
+ },
96
+ content: "<slot></slot>",
97
+ classes: {
98
+ viewer: "",
99
+ },
100
+ labels: getLabels(),
101
+ renderers: {
102
+ image: this.setImage,
103
+ html: this.setHTML,
104
+ pdf: this.setPDF,
105
+ download: this.setDownload,
106
+ plaintext: this.setPlainText,
107
+ markdown: this.setMarkdown,
108
+ audio: this.setAudio,
109
+ video: this.setVideo,
110
+ message: this.setMessage,
111
+ },
112
+ });
113
+ }
114
+
115
+ /**
116
+ * @private
117
+ * @type {symbol}
118
+ * @memberof Viewer
119
+ * @static
120
+ * @description downloadHandlerSymbol is used to store the download handler function.
121
+ * This is necessary to remove the event listener when the element is disconnected.
122
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener}
123
+ */
124
+ disconnectedCallback() {
125
+ super.disconnectedCallback?.();
126
+ if (this[downloadHandlerSymbol]) {
127
+ this.removeEventListener("click", this[downloadHandlerSymbol]);
128
+ this[downloadHandlerSymbol] = null;
129
+ }
130
+ if (this[downloadRevokeSymbol]) {
131
+ try {
132
+ this[downloadRevokeSymbol]();
133
+ } catch {}
134
+ this[downloadRevokeSymbol] = null;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Sets the content of an element based on the provided content and media type.
140
+ *
141
+ * @param {string} content - The content to be set.
142
+ * @param {string} [mediaType="text/plain"] - The media type of the content. Defaults to "text/plain" if not specified.
143
+ * @return {void} This method does not return a value.
144
+ * @throws {Error} Throws an error if shadowRoot is not defined.
145
+ */
146
+ setContent(content, mediaType = "text/plain") {
147
+ if (!this.shadowRoot) {
148
+ throw new Error("no shadow-root is defined");
149
+ }
150
+
151
+ const renderers = this.getOption("renderers");
152
+
153
+ const isDataURL = (value) => {
154
+ return (
155
+ (typeof value === "string" && value.startsWith("data:")) ||
156
+ (value instanceof URL && value.protocol === "data:")
157
+ );
158
+ };
159
+
160
+ if (isDataURL(content)) {
161
+ try {
162
+ const dataUrl = content.toString();
163
+ const [header] = dataUrl.split(",");
164
+ const [typeSegment] = header.split(";");
165
+ mediaType = typeSegment.replace("data:", "") || "text/plain";
166
+ } catch (error) {
167
+ this.dispatchEvent(
168
+ new CustomEvent("viewer-error", {
169
+ detail: "Invalid data URL format",
170
+ }),
171
+ );
172
+ return;
173
+ }
174
+ }
175
+
176
+ if (mediaType === undefined || mediaType === null || mediaType === "") {
177
+ mediaType = "text/plain";
178
+ }
179
+
180
+ let mediaTypeObject;
181
+
182
+ try {
183
+ mediaTypeObject = new parseMediaType(mediaType);
184
+ if (!(mediaTypeObject instanceof MediaType)) {
185
+ this.dispatchEvent(
186
+ new CustomEvent("viewer-error", { detail: "Invalid MediaType" }),
187
+ );
188
+ return;
189
+ }
190
+ } catch (error) {
191
+ this.dispatchEvent(new CustomEvent("viewer-error", { detail: error }));
192
+ return;
193
+ }
194
+
195
+ const checkRenderer = (renderer, contentType) => {
196
+ if (renderers && typeof renderers[renderer] === "function") {
197
+ return true;
198
+ } else {
199
+ this.dispatchEvent(
200
+ new CustomEvent("viewer-error", {
201
+ detail: `Renderer for ${contentType} not found`,
202
+ }),
203
+ );
204
+ return false;
205
+ }
206
+ };
207
+
208
+ switch (mediaTypeObject.type) {
209
+ case "text":
210
+ switch (mediaTypeObject.subtype) {
211
+ case "html":
212
+ if (checkRenderer("html", mediaTypeObject.toString())) {
213
+ renderers.html.call(this, content);
214
+ }
215
+ break;
216
+ case "plain":
217
+ if (checkRenderer("plaintext", mediaTypeObject.toString())) {
218
+ renderers.plaintext.call(this, content);
219
+ }
220
+ break;
221
+ case "markdown":
222
+ if (checkRenderer("markdown", mediaTypeObject.toString())) {
223
+ this.setMarkdown(content);
224
+ }
225
+ break;
226
+ default:
227
+ if (checkRenderer("plaintext", mediaTypeObject.toString())) {
228
+ renderers.plaintext.call(this, content);
229
+ }
230
+ break;
231
+ }
232
+ break;
233
+
234
+ case "application":
235
+ switch (mediaTypeObject.subtype) {
236
+ case "pdf":
237
+ if (checkRenderer("pdf", mediaTypeObject.toString())) {
238
+ renderers.pdf.call(this, content);
239
+ }
240
+ break;
241
+
242
+ default:
243
+ // Handle octet-stream as a generic binary data type
244
+ if (checkRenderer("download", mediaTypeObject.toString())) {
245
+ renderers.download.call(this, content);
246
+ break;
247
+ }
248
+
249
+ this.setOption("content", content);
250
+ break;
251
+ }
252
+ break;
253
+
254
+ case "message":
255
+ switch (mediaTypeObject.subtype) {
256
+ case "rfc822":
257
+ if (checkRenderer("message", mediaTypeObject.toString())) {
258
+ renderers.message.call(this, content);
259
+ }
260
+ break;
261
+
262
+ default:
263
+ this.setOption("content", content);
264
+ break;
265
+ }
266
+ break;
267
+
268
+ case "audio":
269
+ if (checkRenderer(mediaTypeObject.type, mediaTypeObject.toString())) {
270
+ renderers[mediaTypeObject.type].call(this, content);
271
+ }
272
+ break;
273
+
274
+ case "video":
275
+ if (checkRenderer(mediaTypeObject.type, mediaTypeObject.toString())) {
276
+ renderers[mediaTypeObject.type].call(this, content);
277
+ }
278
+ break;
279
+
280
+ case "image":
281
+ if (checkRenderer("image", mediaTypeObject.toString())) {
282
+ renderers.image.call(this, content);
283
+ }
284
+ break;
285
+
286
+ default:
287
+ this.setOption("content", content);
288
+ this.dispatchEvent(
289
+ new CustomEvent("viewer-error", {
290
+ detail: `Unsupported media type: ${mediaTypeObject.toString()}`,
291
+ }),
292
+ ); // Notify about unsupported media type
293
+ return;
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Sets the audio content for the viewer. Accepts a Blob, URL, or string and processes it
299
+ * to configure audio playback within the viewer. Throws an error if the input type is invalid.
300
+ *
301
+ * @param {Blob|string} data - The audio content. This can be a Blob, a URL, or a string.
302
+ * @return {void} No return value.
303
+ */
304
+ setAudio(data) {
305
+ if (isBlob(data)) {
306
+ data = URL.createObjectURL(data);
307
+ } else if (isURL(data)) {
308
+ // nothing to do
309
+ } else if (isString(data)) {
310
+ // nothing to do
311
+ } else {
312
+ this.dispatchEvent(
313
+ new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
314
+ );
315
+ throw new Error("Blob or URL expected");
316
+ }
317
+
318
+ this.setOption(
319
+ "content",
320
+ `
321
321
  <audio controls part="audio" style="max-width: 100%">
322
322
  <source src="${data}">
323
323
  </audio>`,
324
- );
325
- }
326
-
327
- /**
328
- * Sets the video content for the viewer. The method accepts a Blob, URL, or string,
329
- * verifies its type, and updates the viewer's content accordingly.
330
- *
331
- * @param {Blob|string} data - The video data to set. It can be a Blob, URL, or string.
332
- * @return {void} This method does not return a value. It updates the viewer's state.
333
- * @throws {Error} Throws an error if the provided data is not a Blob or URL.
334
- */
335
- setVideo(data) {
336
- if (isBlob(data)) {
337
- data = URL.createObjectURL(data);
338
- } else if (isURL(data)) {
339
- // nothing to do
340
- } else if (isString(data)) {
341
- // nothing to do
342
- } else {
343
- this.dispatchEvent(
344
- new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
345
- );
346
- throw new Error("Blob or URL expected");
347
- }
348
-
349
- this.setOption(
350
- "content",
351
- `
324
+ );
325
+ }
326
+
327
+ /**
328
+ * Sets the video content for the viewer. The method accepts a Blob, URL, or string,
329
+ * verifies its type, and updates the viewer's content accordingly.
330
+ *
331
+ * @param {Blob|string} data - The video data to set. It can be a Blob, URL, or string.
332
+ * @return {void} This method does not return a value. It updates the viewer's state.
333
+ * @throws {Error} Throws an error if the provided data is not a Blob or URL.
334
+ */
335
+ setVideo(data) {
336
+ if (isBlob(data)) {
337
+ data = URL.createObjectURL(data);
338
+ } else if (isURL(data)) {
339
+ // nothing to do
340
+ } else if (isString(data)) {
341
+ // nothing to do
342
+ } else {
343
+ this.dispatchEvent(
344
+ new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
345
+ );
346
+ throw new Error("Blob or URL expected");
347
+ }
348
+
349
+ this.setOption(
350
+ "content",
351
+ `
352
352
  <video controls part="video" style="max-width: 100%">
353
353
  <source src="${data}">
354
354
  </video>`,
355
- );
356
- }
357
-
358
- /**
359
- * Renders Markdown content using built-in or custom Markdown parser.
360
- * Overrideable via `customRenderers['text/markdown']`.
361
- *
362
- * @param {string|Blob} data
363
- */
364
- setMarkdown(data) {
365
- if (isBlob(data)) {
366
- blobToText(data)
367
- .then((markdownText) => {
368
- try {
369
- const html = MarkdownToHTML.convert(markdownText);
370
- this.setHTML(html);
371
- } catch (error) {
372
- this.setPlainText(markdownText); // Fallback to plain text if conversion fails
373
- this.dispatchEvent(
374
- new CustomEvent("viewer-error", { detail: error }),
375
- );
376
- }
377
- })
378
- .catch((error) => {
379
- this.dispatchEvent(
380
- new CustomEvent("viewer-error", { detail: error }),
381
- );
382
- throw new Error(error);
383
- });
384
- return;
385
- } else if (isURL(data)) {
386
- getGlobal()
387
- .fetch(data)
388
- .then((response) => {
389
- return response.text();
390
- })
391
- .then((markdownText) => {
392
- try {
393
- const html = MarkdownToHTML.convert(markdownText);
394
- this.setHTML(html);
395
- } catch (error) {
396
- this.setPlainText(markdownText); // Fallback to plain text if conversion fails
397
- this.dispatchEvent(
398
- new CustomEvent("viewer-error", { detail: error }),
399
- );
400
- }
401
- })
402
- .catch((error) => {
403
- this.dispatchEvent(
404
- new CustomEvent("viewer-error", { detail: error }),
405
- );
406
- throw new Error(error);
407
- });
408
- return;
409
- } else if (isString(data)) {
410
- try {
411
- const html = MarkdownToHTML.convert(data);
412
- this.setHTML(html);
413
- } catch (error) {
414
- this.setPlainText(data); // Fallback to plain text if conversion fails
415
- this.dispatchEvent(new CustomEvent("viewer-error", { detail: error }));
416
- }
417
- return;
418
- }
419
-
420
- this.dispatchEvent(
421
- new CustomEvent("viewer-error", { detail: "Blob or string expected" }),
422
- );
423
- throw new Error("Blob or string expected");
424
- }
425
-
426
- /**
427
- * Configures and embeds a PDF document into the application with customizable display settings.
428
- *
429
- * @param {Blob|URL|string} data The PDF data to be embedded. Can be provided as a Blob, URL, or base64 string.
430
- * @param {boolean} [navigation=true] Determines whether the navigation pane is displayed in the PDF viewer.
431
- * @param {boolean} [toolbar=true] Controls the visibility of the toolbar in the PDF viewer.
432
- * @param {boolean} [scrollbar=false] Configures the display of the scrollbar in the PDF viewer.
433
- * @return {void} This method returns nothing but sets the embedded PDF as the content.
434
- */
435
- setPDF(data, navigation = true, toolbar = true, scrollbar = false) {
436
- const hashes =
437
- "#toolbar=" +
438
- (toolbar ? "1" : "0") +
439
- "&navpanes=" +
440
- (navigation ? "1" : "0") +
441
- "&scrollbar=" +
442
- (scrollbar ? "1" : "0");
443
-
444
- let pdfURL = "";
445
- if (isBlob(data)) {
446
- pdfURL = URL.createObjectURL(data);
447
- pdfURL += hashes;
448
- } else if (isURL(data)) {
449
- // check if the url already contains the hashes
450
- if (data?.hash?.indexOf("#") === -1) {
451
- pdfURL = data.toString() + hashes;
452
- } else {
453
- pdfURL = data.toString();
454
- }
455
- } else if (isString(data)) {
456
- //URL.createObjectURL(data);
457
- const blobObj = new Blob([atob(data)], { type: "application/pdf" });
458
- const url = window.URL.createObjectURL(blobObj);
459
-
460
- pdfURL = data;
461
- } else {
462
- this.dispatchEvent(
463
- new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
464
- );
465
- throw new Error("Blob or URL expected");
466
- }
467
-
468
- const html =
469
- '<object part="pdf" data="' +
470
- pdfURL +
471
- '" width="100%" height="100%" type="application/pdf"></object>';
472
-
473
- this.setOption("content", html);
474
- }
475
-
476
- /**
477
- * Sets the download functionality for the viewer.
478
- * @param data
479
- * @param filename
480
- */
481
- setDownload(data, filename = "download") {
482
- let href = "";
483
- let revoke = null;
484
- let suggestedName = filename;
485
-
486
- if (data instanceof File) {
487
- href = URL.createObjectURL(data);
488
- suggestedName = data.name || filename;
489
- revoke = () => URL.revokeObjectURL(href);
490
- } else if (isBlob(data)) {
491
- href = URL.createObjectURL(data);
492
- revoke = () => URL.revokeObjectURL(href);
493
- } else if (
494
- isURL(data) ||
495
- (isString(data) && /^(data:|blob:|https?:)/.test(data))
496
- ) {
497
- href = data.toString();
498
- try {
499
- const u = new URL(href, location.href);
500
- const last = u.pathname.split("/").pop();
501
- if ((!filename || filename === "download") && last) {
502
- suggestedName = decodeURIComponent(last);
503
- }
504
- } catch {}
505
- } else if (isString(data)) {
506
- const blob = new Blob([data], { type: "application/octet-stream" });
507
- href = URL.createObjectURL(blob);
508
- revoke = () => URL.revokeObjectURL(href);
509
- } else {
510
- const msg = "Blob, URL oder String erwartet";
511
- this.dispatchEvent(new CustomEvent("viewer-error", { detail: msg }));
512
- throw new Error(msg);
513
- }
514
-
515
- const button =
516
- `<monster-button data-monster-role="download">` +
517
- this.getOption("labels.download") +
518
- `</monster-button>`;
519
- this.setOption("content", button);
520
-
521
- if (this[downloadHandlerSymbol]) {
522
- this.removeEventListener("click", this[downloadHandlerSymbol]);
523
- }
524
- if (this[downloadRevokeSymbol]) {
525
- try {
526
- this[downloadRevokeSymbol]();
527
- } catch {}
528
- }
529
-
530
- this[downloadHandlerSymbol] = (event) => {
531
- const el = findTargetElementFromEvent(
532
- event,
533
- "data-monster-role",
534
- "download",
535
- );
536
- if (el instanceof Button) {
537
- const a = document.createElement("a");
538
- a.href = href;
539
- a.download = suggestedName;
540
- a.rel = "noopener";
541
- a.style.display = "none";
542
- document.body.appendChild(a);
543
- a.click();
544
- document.body.removeChild(a);
545
- }
546
- };
547
- this[downloadRevokeSymbol] = revoke;
548
-
549
- this.addEventListener("click", this[downloadHandlerSymbol]);
550
- }
551
-
552
- /**
553
- * Sets the content for displaying an email message.
554
- * The data is expected to be an object with a structure containing
555
- * 'to', 'from', 'subject', 'parts', and 'headers'.
556
- * The parts are processed to display plain text and HTML in separate tabs,
557
- * and attachments are listed.
558
- *
559
- * @param {object} emailData - The structured email data.
560
- */
561
- setMessage(emailData) {
562
- if (!emailData || typeof emailData !== "object") {
563
- this.dispatchEvent(
564
- new CustomEvent("viewer-error", {
565
- detail: "Invalid email data provided",
566
- }),
567
- );
568
- return;
569
- }
570
-
571
- this.setOption(
572
- "content",
573
- '<monster-message-content part="message"></monster-message-content>',
574
- );
575
-
576
- setTimeout(() => {
577
- const messageContent = this.shadowRoot.querySelector(
578
- "monster-message-content",
579
- );
580
- if (!messageContent) {
581
- this.dispatchEvent(
582
- new CustomEvent("viewer-error", {
583
- detail: "Message content element not found",
584
- }),
585
- );
586
- return;
587
- }
588
-
589
- messageContent.setMessage(emailData);
590
- }, 100);
591
- }
592
-
593
- /**
594
- * Sets an image for the target by accepting a blob, URL, or string representation of the image.
595
- *
596
- * @param {(Blob|string)} data - The image data, which can be a Blob, a valid URL, or a string representation of the image.
597
- * @return {void} Does not return a value.
598
- */
599
- setImage(data) {
600
- if (isBlob(data)) {
601
- data = URL.createObjectURL(data);
602
- } else if (isURL(data)) {
603
- // nothing to do
604
- } else if (isString(data)) {
605
- // nothing to do
606
- } else {
607
- this.dispatchEvent(
608
- new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
609
- );
610
- throw new Error("Blob or URL expected");
611
- }
612
-
613
- this.setOption(
614
- "content",
615
- `<img style="max-width: 100%" src="${data}" alt="image" part="image" onerror="this.dispatchEvent(new CustomEvent('viewer-error', {detail: 'Image loading error'}));">`,
616
- );
617
- }
618
-
619
- /**
620
- *
621
- * if the data is a string, it is interpreted as HTML.
622
- * if the data is a URL, the HTML is loaded from the url and set as content.
623
- * if the data is an HTMLElement, the outerHTML is used as content.
624
- *
625
- * @param {HTMLElement|URL|string|Blob} data
626
- */
627
- setHTML(data) {
628
- if (data instanceof Blob) {
629
- blobToText(data)
630
- .then((html) => {
631
- this.setOption("content", html);
632
- })
633
- .catch((error) => {
634
- this.dispatchEvent(
635
- new CustomEvent("viewer-error", { detail: error }),
636
- );
637
- throw new Error(error);
638
- });
639
-
640
- return;
641
- } else if (data instanceof HTMLElement) {
642
- data = data.outerHTML;
643
- } else if (isString(data)) {
644
- // nothing to do
645
- } else if (isURL(data)) {
646
- // fetch element
647
- getGlobal()
648
- .fetch(data)
649
- .then((response) => {
650
- return response.text();
651
- })
652
- .then((html) => {
653
- this.setOption("content", html);
654
- })
655
- .catch((error) => {
656
- this.dispatchEvent(
657
- new CustomEvent("viewer-error", { detail: error }),
658
- );
659
- throw new Error(error);
660
- });
661
- } else {
662
- this.dispatchEvent(
663
- new CustomEvent("viewer-error", {
664
- detail: "HTMLElement or string expected",
665
- }),
666
- );
667
- throw new Error("HTMLElement or string expected");
668
- }
669
-
670
- this.setOption("content", data);
671
- }
672
-
673
- /**
674
- * Sets the plain text content by processing the input data, which can be of various types, including Blob,
675
- * HTMLElement, string, or a valid URL. The method extracts and sets the raw text content into a predefined option.
676
- *
677
- * @param {Blob|HTMLElement|string} data - The input data to be processed. It can be a Blob object, an HTMLElement,
678
- * a plain string, or a string formatted as a valid URL. The method determines
679
- * the data type and processes it accordingly.
680
- * @return {void} - This method does not return any value. It processes the content and updates the relevant option
681
- * property.
682
- */
683
- setPlainText(data) {
684
- const mkPreSpan = (text) => {
685
- const pre = document.createElement("pre");
686
- pre.innerText = text;
687
- pre.setAttribute("part", "text");
688
- return pre.outerHTML;
689
- };
690
-
691
- if (data instanceof Blob) {
692
- blobToText(data)
693
- .then((text) => {
694
- const div = document.createElement("div");
695
- div.innerHTML = text;
696
- text = div.innerText;
697
-
698
- this.setOption("content", mkPreSpan(text));
699
- })
700
- .catch((error) => {
701
- this.dispatchEvent(
702
- new CustomEvent("viewer-error", { detail: error }),
703
- );
704
- throw new Error(error);
705
- });
706
-
707
- return;
708
- } else if (data instanceof HTMLElement) {
709
- data = data.outerText;
710
- } else if (isString(data)) {
711
- const div = document.createElement("div");
712
- div.innerHTML = data;
713
- data = div.innerText;
714
- } else if (isURL(data)) {
715
- getGlobal()
716
- .fetch(data)
717
- .then((response) => {
718
- return response.text();
719
- })
720
- .then((text) => {
721
- const div = document.createElement("div");
722
- div.innerHTML = text;
723
- text = div.innerText;
724
-
725
- this.setOption("content", mkPreSpan(text));
726
- })
727
- .catch((error) => {
728
- this.dispatchEvent(
729
- new CustomEvent("viewer-error", { detail: error }),
730
- );
731
- throw new Error(error);
732
- });
733
- } else {
734
- this.dispatchEvent(
735
- new CustomEvent("viewer-error", {
736
- detail: "HTMLElement or string expected",
737
- }),
738
- );
739
- throw new Error("HTMLElement or string expected");
740
- }
741
-
742
- this.setOption("content", mkPreSpan(data));
743
- }
744
-
745
- /**
746
- *
747
- * @return {Viewer}
748
- */
749
- [assembleMethodSymbol]() {
750
- super[assembleMethodSymbol]();
751
-
752
- initControlReferences.call(this);
753
- initEventHandler.call(this);
754
- }
755
-
756
- /**
757
- *
758
- * @return {string}
759
- */
760
- static getTag() {
761
- return "monster-viewer";
762
- }
763
-
764
- /**
765
- * @return {CSSStyleSheet[]}
766
- */
767
- static getCSSStyleSheet() {
768
- return [ViewerStyleSheet];
769
- }
355
+ );
356
+ }
357
+
358
+ /**
359
+ * Renders Markdown content using built-in or custom Markdown parser.
360
+ * Overrideable via `customRenderers['text/markdown']`.
361
+ *
362
+ * @param {string|Blob} data
363
+ */
364
+ setMarkdown(data) {
365
+ if (isBlob(data)) {
366
+ blobToText(data)
367
+ .then((markdownText) => {
368
+ try {
369
+ const html = MarkdownToHTML.convert(markdownText);
370
+ this.setHTML(html);
371
+ } catch (error) {
372
+ this.setPlainText(markdownText); // Fallback to plain text if conversion fails
373
+ this.dispatchEvent(
374
+ new CustomEvent("viewer-error", { detail: error }),
375
+ );
376
+ }
377
+ })
378
+ .catch((error) => {
379
+ this.dispatchEvent(
380
+ new CustomEvent("viewer-error", { detail: error }),
381
+ );
382
+ throw new Error(error);
383
+ });
384
+ return;
385
+ } else if (isURL(data)) {
386
+ getGlobal()
387
+ .fetch(data)
388
+ .then((response) => {
389
+ return response.text();
390
+ })
391
+ .then((markdownText) => {
392
+ try {
393
+ const html = MarkdownToHTML.convert(markdownText);
394
+ this.setHTML(html);
395
+ } catch (error) {
396
+ this.setPlainText(markdownText); // Fallback to plain text if conversion fails
397
+ this.dispatchEvent(
398
+ new CustomEvent("viewer-error", { detail: error }),
399
+ );
400
+ }
401
+ })
402
+ .catch((error) => {
403
+ this.dispatchEvent(
404
+ new CustomEvent("viewer-error", { detail: error }),
405
+ );
406
+ throw new Error(error);
407
+ });
408
+ return;
409
+ } else if (isString(data)) {
410
+ try {
411
+ const html = MarkdownToHTML.convert(data);
412
+ this.setHTML(html);
413
+ } catch (error) {
414
+ this.setPlainText(data); // Fallback to plain text if conversion fails
415
+ this.dispatchEvent(new CustomEvent("viewer-error", { detail: error }));
416
+ }
417
+ return;
418
+ }
419
+
420
+ this.dispatchEvent(
421
+ new CustomEvent("viewer-error", { detail: "Blob or string expected" }),
422
+ );
423
+ throw new Error("Blob or string expected");
424
+ }
425
+
426
+ /**
427
+ * Configures and embeds a PDF document into the application with customizable display settings.
428
+ *
429
+ * @param {Blob|URL|string} data The PDF data to be embedded. Can be provided as a Blob, URL, or base64 string.
430
+ * @param {boolean} [navigation=true] Determines whether the navigation pane is displayed in the PDF viewer.
431
+ * @param {boolean} [toolbar=true] Controls the visibility of the toolbar in the PDF viewer.
432
+ * @param {boolean} [scrollbar=false] Configures the display of the scrollbar in the PDF viewer.
433
+ * @return {void} This method returns nothing but sets the embedded PDF as the content.
434
+ */
435
+ setPDF(data, navigation = true, toolbar = true, scrollbar = false) {
436
+ const hashes =
437
+ "#toolbar=" +
438
+ (toolbar ? "1" : "0") +
439
+ "&navpanes=" +
440
+ (navigation ? "1" : "0") +
441
+ "&scrollbar=" +
442
+ (scrollbar ? "1" : "0");
443
+
444
+ let pdfURL = "";
445
+ if (isBlob(data)) {
446
+ pdfURL = URL.createObjectURL(data);
447
+ pdfURL += hashes;
448
+ } else if (isURL(data)) {
449
+ // check if the url already contains the hashes
450
+ if (data?.hash?.indexOf("#") === -1) {
451
+ pdfURL = data.toString() + hashes;
452
+ } else {
453
+ pdfURL = data.toString();
454
+ }
455
+ } else if (isString(data)) {
456
+ //URL.createObjectURL(data);
457
+ const blobObj = new Blob([atob(data)], { type: "application/pdf" });
458
+ const url = window.URL.createObjectURL(blobObj);
459
+
460
+ pdfURL = data;
461
+ } else {
462
+ this.dispatchEvent(
463
+ new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
464
+ );
465
+ throw new Error("Blob or URL expected");
466
+ }
467
+
468
+ const html =
469
+ '<object part="pdf" data="' +
470
+ pdfURL +
471
+ '" width="100%" height="100%" type="application/pdf"></object>';
472
+
473
+ this.setOption("content", html);
474
+ }
475
+
476
+ /**
477
+ * Sets the download functionality for the viewer.
478
+ * @param data
479
+ * @param filename
480
+ */
481
+ setDownload(data, filename = "download") {
482
+ let href = "";
483
+ let revoke = null;
484
+ let suggestedName = filename;
485
+
486
+ if (data instanceof File) {
487
+ href = URL.createObjectURL(data);
488
+ suggestedName = data.name || filename;
489
+ revoke = () => URL.revokeObjectURL(href);
490
+ } else if (isBlob(data)) {
491
+ href = URL.createObjectURL(data);
492
+ revoke = () => URL.revokeObjectURL(href);
493
+ } else if (
494
+ isURL(data) ||
495
+ (isString(data) && /^(data:|blob:|https?:)/.test(data))
496
+ ) {
497
+ href = data.toString();
498
+ try {
499
+ const u = new URL(href, location.href);
500
+ const last = u.pathname.split("/").pop();
501
+ if ((!filename || filename === "download") && last) {
502
+ suggestedName = decodeURIComponent(last);
503
+ }
504
+ } catch {}
505
+ } else if (isString(data)) {
506
+ const blob = new Blob([data], { type: "application/octet-stream" });
507
+ href = URL.createObjectURL(blob);
508
+ revoke = () => URL.revokeObjectURL(href);
509
+ } else {
510
+ const msg = "Blob, URL oder String erwartet";
511
+ this.dispatchEvent(new CustomEvent("viewer-error", { detail: msg }));
512
+ throw new Error(msg);
513
+ }
514
+
515
+ const button =
516
+ `<monster-button data-monster-role="download">` +
517
+ this.getOption("labels.download") +
518
+ `</monster-button>`;
519
+ this.setOption("content", button);
520
+
521
+ if (this[downloadHandlerSymbol]) {
522
+ this.removeEventListener("click", this[downloadHandlerSymbol]);
523
+ }
524
+ if (this[downloadRevokeSymbol]) {
525
+ try {
526
+ this[downloadRevokeSymbol]();
527
+ } catch {}
528
+ }
529
+
530
+ this[downloadHandlerSymbol] = (event) => {
531
+ const el = findTargetElementFromEvent(
532
+ event,
533
+ "data-monster-role",
534
+ "download",
535
+ );
536
+ if (el instanceof Button) {
537
+ const a = document.createElement("a");
538
+ a.href = href;
539
+ a.download = suggestedName;
540
+ a.rel = "noopener";
541
+ a.style.display = "none";
542
+ document.body.appendChild(a);
543
+ a.click();
544
+ document.body.removeChild(a);
545
+ }
546
+ };
547
+ this[downloadRevokeSymbol] = revoke;
548
+
549
+ this.addEventListener("click", this[downloadHandlerSymbol]);
550
+ }
551
+
552
+ /**
553
+ * Sets the content for displaying an email message.
554
+ * The data is expected to be an object with a structure containing
555
+ * 'to', 'from', 'subject', 'parts', and 'headers'.
556
+ * The parts are processed to display plain text and HTML in separate tabs,
557
+ * and attachments are listed.
558
+ *
559
+ * @param {object} emailData - The structured email data.
560
+ */
561
+ setMessage(emailData) {
562
+ if (!emailData || typeof emailData !== "object") {
563
+ this.dispatchEvent(
564
+ new CustomEvent("viewer-error", {
565
+ detail: "Invalid email data provided",
566
+ }),
567
+ );
568
+ return;
569
+ }
570
+
571
+ this.setOption(
572
+ "content",
573
+ '<monster-message-content part="message"></monster-message-content>',
574
+ );
575
+
576
+ setTimeout(() => {
577
+ const messageContent = this.shadowRoot.querySelector(
578
+ "monster-message-content",
579
+ );
580
+ if (!messageContent) {
581
+ this.dispatchEvent(
582
+ new CustomEvent("viewer-error", {
583
+ detail: "Message content element not found",
584
+ }),
585
+ );
586
+ return;
587
+ }
588
+
589
+ messageContent.setMessage(emailData);
590
+ }, 100);
591
+ }
592
+
593
+ /**
594
+ * Sets an image for the target by accepting a blob, URL, or string representation of the image.
595
+ *
596
+ * @param {(Blob|string)} data - The image data, which can be a Blob, a valid URL, or a string representation of the image.
597
+ * @return {void} Does not return a value.
598
+ */
599
+ setImage(data) {
600
+ if (isBlob(data)) {
601
+ data = URL.createObjectURL(data);
602
+ } else if (isURL(data)) {
603
+ // nothing to do
604
+ } else if (isString(data)) {
605
+ // nothing to do
606
+ } else {
607
+ this.dispatchEvent(
608
+ new CustomEvent("viewer-error", { detail: "Blob or URL expected" }),
609
+ );
610
+ throw new Error("Blob or URL expected");
611
+ }
612
+
613
+ this.setOption(
614
+ "content",
615
+ `<img style="max-width: 100%" src="${data}" alt="image" part="image" onerror="this.dispatchEvent(new CustomEvent('viewer-error', {detail: 'Image loading error'}));">`,
616
+ );
617
+ }
618
+
619
+ /**
620
+ *
621
+ * if the data is a string, it is interpreted as HTML.
622
+ * if the data is a URL, the HTML is loaded from the url and set as content.
623
+ * if the data is an HTMLElement, the outerHTML is used as content.
624
+ *
625
+ * @param {HTMLElement|URL|string|Blob} data
626
+ */
627
+ setHTML(data) {
628
+ if (data instanceof Blob) {
629
+ blobToText(data)
630
+ .then((html) => {
631
+ this.setOption("content", html);
632
+ })
633
+ .catch((error) => {
634
+ this.dispatchEvent(
635
+ new CustomEvent("viewer-error", { detail: error }),
636
+ );
637
+ throw new Error(error);
638
+ });
639
+
640
+ return;
641
+ } else if (data instanceof HTMLElement) {
642
+ data = data.outerHTML;
643
+ } else if (isString(data)) {
644
+ // nothing to do
645
+ } else if (isURL(data)) {
646
+ // fetch element
647
+ getGlobal()
648
+ .fetch(data)
649
+ .then((response) => {
650
+ return response.text();
651
+ })
652
+ .then((html) => {
653
+ this.setOption("content", html);
654
+ })
655
+ .catch((error) => {
656
+ this.dispatchEvent(
657
+ new CustomEvent("viewer-error", { detail: error }),
658
+ );
659
+ throw new Error(error);
660
+ });
661
+ } else {
662
+ this.dispatchEvent(
663
+ new CustomEvent("viewer-error", {
664
+ detail: "HTMLElement or string expected",
665
+ }),
666
+ );
667
+ throw new Error("HTMLElement or string expected");
668
+ }
669
+
670
+ this.setOption("content", data);
671
+ }
672
+
673
+ /**
674
+ * Sets the plain text content by processing the input data, which can be of various types, including Blob,
675
+ * HTMLElement, string, or a valid URL. The method extracts and sets the raw text content into a predefined option.
676
+ *
677
+ * @param {Blob|HTMLElement|string} data - The input data to be processed. It can be a Blob object, an HTMLElement,
678
+ * a plain string, or a string formatted as a valid URL. The method determines
679
+ * the data type and processes it accordingly.
680
+ * @return {void} - This method does not return any value. It processes the content and updates the relevant option
681
+ * property.
682
+ */
683
+ setPlainText(data) {
684
+ const mkPreSpan = (text) => {
685
+ const pre = document.createElement("pre");
686
+ pre.innerText = text;
687
+ pre.setAttribute("part", "text");
688
+ return pre.outerHTML;
689
+ };
690
+
691
+ if (data instanceof Blob) {
692
+ blobToText(data)
693
+ .then((text) => {
694
+ const div = document.createElement("div");
695
+ div.innerHTML = text;
696
+ text = div.innerText;
697
+
698
+ this.setOption("content", mkPreSpan(text));
699
+ })
700
+ .catch((error) => {
701
+ this.dispatchEvent(
702
+ new CustomEvent("viewer-error", { detail: error }),
703
+ );
704
+ throw new Error(error);
705
+ });
706
+
707
+ return;
708
+ } else if (data instanceof HTMLElement) {
709
+ data = data.outerText;
710
+ } else if (isString(data)) {
711
+ const div = document.createElement("div");
712
+ div.innerHTML = data;
713
+ data = div.innerText;
714
+ } else if (isURL(data)) {
715
+ getGlobal()
716
+ .fetch(data)
717
+ .then((response) => {
718
+ return response.text();
719
+ })
720
+ .then((text) => {
721
+ const div = document.createElement("div");
722
+ div.innerHTML = text;
723
+ text = div.innerText;
724
+
725
+ this.setOption("content", mkPreSpan(text));
726
+ })
727
+ .catch((error) => {
728
+ this.dispatchEvent(
729
+ new CustomEvent("viewer-error", { detail: error }),
730
+ );
731
+ throw new Error(error);
732
+ });
733
+ } else {
734
+ this.dispatchEvent(
735
+ new CustomEvent("viewer-error", {
736
+ detail: "HTMLElement or string expected",
737
+ }),
738
+ );
739
+ throw new Error("HTMLElement or string expected");
740
+ }
741
+
742
+ this.setOption("content", mkPreSpan(data));
743
+ }
744
+
745
+ /**
746
+ *
747
+ * @return {Viewer}
748
+ */
749
+ [assembleMethodSymbol]() {
750
+ super[assembleMethodSymbol]();
751
+
752
+ initControlReferences.call(this);
753
+ initEventHandler.call(this);
754
+ }
755
+
756
+ /**
757
+ *
758
+ * @return {string}
759
+ */
760
+ static getTag() {
761
+ return "monster-viewer";
762
+ }
763
+
764
+ /**
765
+ * @return {CSSStyleSheet[]}
766
+ */
767
+ static getCSSStyleSheet() {
768
+ return [ViewerStyleSheet];
769
+ }
770
770
  }
771
771
 
772
772
  /**
@@ -775,12 +775,12 @@ class Viewer extends CustomElement {
775
775
  * @return {boolean}
776
776
  */
777
777
  function isURL(variable) {
778
- try {
779
- new URL(variable);
780
- return true;
781
- } catch (error) {
782
- return false;
783
- }
778
+ try {
779
+ new URL(variable);
780
+ return true;
781
+ } catch (error) {
782
+ return false;
783
+ }
784
784
  }
785
785
 
786
786
  /**
@@ -789,7 +789,7 @@ function isURL(variable) {
789
789
  * @return {boolean}
790
790
  */
791
791
  function isBlob(variable) {
792
- return variable instanceof Blob;
792
+ return variable instanceof Blob;
793
793
  }
794
794
 
795
795
  /**
@@ -798,12 +798,12 @@ function isBlob(variable) {
798
798
  * @return {Promise<unknown>}
799
799
  */
800
800
  function blobToText(blob) {
801
- return new Promise((resolve, reject) => {
802
- const reader = new FileReader();
803
- reader.onloadend = () => resolve(reader.result);
804
- reader.onerror = reject;
805
- reader.readAsText(blob);
806
- });
801
+ return new Promise((resolve, reject) => {
802
+ const reader = new FileReader();
803
+ reader.onloadend = () => resolve(reader.result);
804
+ reader.onerror = reject;
805
+ reader.readAsText(blob);
806
+ });
807
807
  }
808
808
 
809
809
  /**
@@ -812,122 +812,122 @@ function blobToText(blob) {
812
812
  * @throws {Error} no shadow-root is defined
813
813
  */
814
814
  function initControlReferences() {
815
- if (!this.shadowRoot) {
816
- throw new Error("no shadow-root is defined");
817
- }
815
+ if (!this.shadowRoot) {
816
+ throw new Error("no shadow-root is defined");
817
+ }
818
818
 
819
- this[viewerElementSymbol] = this.shadowRoot.getElementById("viewer");
819
+ this[viewerElementSymbol] = this.shadowRoot.getElementById("viewer");
820
820
  }
821
821
 
822
822
  /**
823
823
  * @private
824
824
  */
825
825
  function initEventHandler() {
826
- return this;
826
+ return this;
827
827
  }
828
828
 
829
829
  function getLabels() {
830
- switch (getLocaleOfDocument().language) {
831
- case "de": // German
832
- return {
833
- download: "Herunterladen",
834
- };
835
-
836
- case "es": // Spanish
837
- return {
838
- download: "Descargar",
839
- };
840
-
841
- case "zh": // Mandarin
842
- return {
843
- download: "下载",
844
- };
845
-
846
- case "hi": // Hindi
847
- return {
848
- download: "下载",
849
- };
850
-
851
- case "bn": // Bengali
852
- return {
853
- download: "ডাউনলোড",
854
- };
855
-
856
- case "pt": // Portuguese
857
- return {
858
- download: "Baixar",
859
- };
860
-
861
- case "ru": // Russian
862
- return {
863
- download: "Скачать",
864
- };
865
-
866
- case "ja": // Japanese
867
- return {
868
- download: "ダウンロード",
869
- };
870
-
871
- case "pa": // Western Punjabi
872
- return {
873
- download: "ਡਾਊਨਲੋਡ",
874
- };
875
-
876
- case "mr": // Marathi
877
- return {
878
- download: "डाउनलोड",
879
- };
880
-
881
- case "fr": // French
882
- return {
883
- download: "Télécharger",
884
- };
885
-
886
- case "it": // Italian
887
- return {
888
- download: "Scarica",
889
- };
890
-
891
- case "nl": // Dutch
892
- return {
893
- download: "Downloaden",
894
- };
895
-
896
- case "sv": // Swedish
897
- return {
898
- download: "Ladda ner",
899
- };
900
-
901
- case "pl": // Polish
902
- return {
903
- download: "Ściągnij",
904
- };
905
-
906
- case "da": // Danish
907
- return {
908
- download: "Lad ned",
909
- };
910
-
911
- case "fi": // Finnish
912
- return {
913
- download: "Lataa",
914
- };
915
-
916
- case "no": // Norwegian
917
- return {
918
- download: "Laste ned",
919
- };
920
-
921
- case "cs": // Czech
922
- return {
923
- download: "Stáhnout",
924
- };
925
-
926
- default:
927
- return {
928
- download: "Download",
929
- };
930
- }
830
+ switch (getLocaleOfDocument().language) {
831
+ case "de": // German
832
+ return {
833
+ download: "Herunterladen",
834
+ };
835
+
836
+ case "es": // Spanish
837
+ return {
838
+ download: "Descargar",
839
+ };
840
+
841
+ case "zh": // Mandarin
842
+ return {
843
+ download: "下载",
844
+ };
845
+
846
+ case "hi": // Hindi
847
+ return {
848
+ download: "下载",
849
+ };
850
+
851
+ case "bn": // Bengali
852
+ return {
853
+ download: "ডাউনলোড",
854
+ };
855
+
856
+ case "pt": // Portuguese
857
+ return {
858
+ download: "Baixar",
859
+ };
860
+
861
+ case "ru": // Russian
862
+ return {
863
+ download: "Скачать",
864
+ };
865
+
866
+ case "ja": // Japanese
867
+ return {
868
+ download: "ダウンロード",
869
+ };
870
+
871
+ case "pa": // Western Punjabi
872
+ return {
873
+ download: "ਡਾਊਨਲੋਡ",
874
+ };
875
+
876
+ case "mr": // Marathi
877
+ return {
878
+ download: "डाउनलोड",
879
+ };
880
+
881
+ case "fr": // French
882
+ return {
883
+ download: "Télécharger",
884
+ };
885
+
886
+ case "it": // Italian
887
+ return {
888
+ download: "Scarica",
889
+ };
890
+
891
+ case "nl": // Dutch
892
+ return {
893
+ download: "Downloaden",
894
+ };
895
+
896
+ case "sv": // Swedish
897
+ return {
898
+ download: "Ladda ner",
899
+ };
900
+
901
+ case "pl": // Polish
902
+ return {
903
+ download: "Ściągnij",
904
+ };
905
+
906
+ case "da": // Danish
907
+ return {
908
+ download: "Lad ned",
909
+ };
910
+
911
+ case "fi": // Finnish
912
+ return {
913
+ download: "Lataa",
914
+ };
915
+
916
+ case "no": // Norwegian
917
+ return {
918
+ download: "Laste ned",
919
+ };
920
+
921
+ case "cs": // Czech
922
+ return {
923
+ download: "Stáhnout",
924
+ };
925
+
926
+ default:
927
+ return {
928
+ download: "Download",
929
+ };
930
+ }
931
931
  }
932
932
 
933
933
  /**
@@ -935,8 +935,8 @@ function getLabels() {
935
935
  * @return {string}
936
936
  */
937
937
  function getTemplate() {
938
- // language=HTML
939
- return `
938
+ // language=HTML
939
+ return `
940
940
  <div id="viewer" data-monster-role="viewer" part="viewer"
941
941
  data-monster-replace="path:content"
942
942
  data-monster-attributes="class path:classes.viewer">