@vaadin-component-factory/vcf-pdf-viewer 3.1.0 → 4.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.
@@ -1,23 +1,26 @@
1
- import { PolymerElement, html } from '@polymer/polymer/polymer-element';
2
- import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
3
- import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
4
- import '@vaadin/polymer-legacy-adapter/template-renderer.js';
5
- import '@vaadin/text-field';
6
- import '@vaadin/select';
7
- import '@vaadin/item';
8
- import '@vaadin/button';
9
- import '@vaadin/icon';
10
- import '@vaadin/icons';
11
- import '@vaadin/tooltip';
12
-
13
- import * as pdfjsLib from '../pdfjs/dist/pdf';
14
- import * as pdfjsViewer from '../pdfjs/dist/pdf_viewer';
15
- import * as pdfUtils from '../pdfjs/dist/ui_utils'
16
- import * as pdfjsLinkService from '../pdfjs/dist/pdf_link_service';
17
- import * as pdfjsThumbnailViewer from '../pdfjs/dist/pdf_thumbnail_viewer';
18
- import * as pdfjsRenderingQueue from '../pdfjs/dist/pdf_rendering_queue';
19
- import { NullL10n } from '../pdfjs/dist/l10n_utils';
20
- import * as pdfjsWorker from '../pdfjs/dist/worker';
1
+ import { LitElement, html, css } from "lit";
2
+ import { ElementMixin } from "@vaadin/component-base/src/element-mixin";
3
+ import { ThemeDetectionMixin } from "@vaadin/vaadin-themable-mixin/vaadin-theme-detection-mixin";
4
+ import { ResizeMixin, SlotStylesMixin } from "@vaadin/component-base/";
5
+ import { ThemableMixin } from "@vaadin/vaadin-themable-mixin";
6
+
7
+ import "@vaadin/text-field";
8
+ import "@vaadin/select";
9
+ import "@vaadin/item";
10
+ import "@vaadin/button";
11
+ import "@vaadin/icon";
12
+ import "@vaadin/icons";
13
+ import "@vaadin/tooltip";
14
+
15
+ import * as pdfjsLib from "../pdfjs/dist/pdf";
16
+ import * as pdfjsViewer from "../pdfjs/dist/pdf_viewer";
17
+ import * as pdfUtils from "../pdfjs/dist/ui_utils";
18
+ import * as pdfjsLinkService from "../pdfjs/dist/pdf_link_service";
19
+ import * as pdfjsThumbnailViewer from "../pdfjs/dist/pdf_thumbnail_viewer";
20
+ import * as pdfjsRenderingQueue from "../pdfjs/dist/pdf_rendering_queue";
21
+ import { NullL10n } from "../pdfjs/dist/l10n_utils";
22
+ import * as pdfjsWorker from "../pdfjs/dist/worker";
23
+
21
24
  pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
22
25
 
23
26
  /**
@@ -34,904 +37,1017 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
34
37
  * @mixes Vaadin.ThemableMixin
35
38
  * @demo demo/index.html
36
39
  */
37
- class PdfViewerElement extends
38
- ElementMixin(
39
- ThemableMixin(PolymerElement)) {
40
-
41
- static get template() {
42
- return html`
43
- <style>
44
- :host {
45
- display: flex;
46
- flex-direction: column;
47
- width: 100%;
48
- height: 500px;
49
- }
50
-
51
- :host([hidden]) {
52
- display: none !important;
53
- }
54
-
55
- [part~="toolbar"] #currentPage,
56
- [part~="toolbar"] #pageSeparator,
57
- [part~="toolbar"] #totalPages,
58
- [part~="toolbar"] #previousPage,
59
- [part~="toolbar"] #nextPage,
60
- [part~="toolbar"] #zoom,
61
- [part~="toolbar"] #sidebarToggle {
62
- display: none;
63
- }
64
-
65
- [part~="toolbar"].ready #currentPage,
66
- [part~="toolbar"].ready #pageSeparator,
67
- [part~="toolbar"].ready #totalPages,
68
- [part~="toolbar"].ready #previousPage,
69
- [part~="toolbar"].ready #nextPage,
70
- [part~="toolbar"].ready #zoom,
71
- [part~="toolbar"].ready #sidebarToggle {
72
- display: inherit;
73
- }
74
-
75
- [part~="outer-container"] {
76
- width: 100%;
77
- height: 100%;
78
- }
79
-
80
- [part~="main-container"] {
81
- position: absolute;
82
- top: 0;
83
- right: 0;
84
- bottom: 0;
85
- left: 0;
86
- min-width: 320px;
87
- }
88
-
89
- [part~="viewer-container"] {
90
- position: absolute;
91
- flex: 1;
92
- overflow: auto;
93
- width: 100%;
94
- height: -moz-calc(100% - 45px); /* Firefox */
95
- height: -webkit-calc(100% - 45px); /* Chrome, Safari */
96
- height: calc(100% - 45px); /*all other browsers */
97
- }
98
-
99
- [part~="sidebar-container"] {
100
- position: absolute;
101
- width: 200px;
102
- top: 45px;
103
- bottom: 0;
104
- visibility: hidden;
105
- height: -moz-calc(100% - 45px); /* Firefox */
106
- height: -webkit-calc(100% - 45px); /* Chrome, Safari */
107
- height: calc(100% - 45px); /*all other browsers */
108
- z-index: 100;
109
- }
110
-
111
- [part~="sidebar-content"] {
112
- position: absolute;
113
- top: 0;
114
- bottom: 0;
115
- overflow: auto;
116
- width: 100%;
117
- background-color: rgba(0, 0, 0, 0.1);
118
- }
119
-
120
- [part~="thumbnail-view"] {
121
- position: absolute;
122
- width: calc(100% - 60px);
123
- top: 0;
124
- bottom: 0;
125
- padding: 10px 30px 0;
126
- overflow: auto;
127
- }
128
-
129
- [part~="toolbar"] {
130
- height: 44px;
131
- }
132
-
133
- .page {
134
- position: relative;
135
- margin: 0 auto;
136
- }
137
-
138
- .textLayer {
139
- position: absolute;
140
- left: 0;
141
- top: 0;
142
- right: 0;
143
- bottom: 0;
144
- overflow: hidden;
145
- line-height: 1;
146
- }
147
-
148
- .textLayer > span {
149
- color: transparent;
150
- position: absolute;
151
- white-space: pre;
152
- cursor: text;
153
- -webkit-transform-origin: 0% 0%;
154
- transform-origin: 0% 0%;
155
- }
156
-
157
- .textLayer .highlight {
158
- margin: -1px;
159
- padding: 1px;
160
- }
161
-
162
- .textLayer .highlight.begin {
163
- border-radius: 4px 0 0 4px;
164
- }
165
-
166
- .textLayer .highlight.end {
167
- border-radius: 0 4px 4px 0;
168
- }
169
-
170
- .textLayer .highlight.middle {
171
- border-radius: 0;
172
- }
173
-
174
- .textLayer .endOfContent {
175
- display: block;
176
- position: absolute;
177
- left: 0;
178
- top: 100%;
179
- right: 0;
180
- bottom: 0;
181
- z-index: -1;
182
- cursor: default;
183
- -webkit-user-select: none;
184
- -moz-user-select: none;
185
- -ms-user-select: none;
186
- user-select: none;
187
- }
188
-
189
- .textLayer .endOfContent.active {
190
- top: 0;
191
- }
192
-
193
- #header {
194
- display: flex;
195
- flex-direction: row;
196
- align-items: baseline;
197
- }
198
-
199
- ::slotted(#currentPage) {
200
- align-self: baseline;
201
- }
202
-
203
- #outerContainer.sidebarOpen #viewerContainer {
204
- transition-property: left;
205
- left: 200px;
206
- width: -moz-calc(100% - 200px); /* Firefox */
207
- width: -webkit-calc(100% - 200px); /* Chrome, Safari */
208
- width: calc(100% - 200px); /*all other browsers */
209
- }
210
-
211
- #outerContainer.sidebarOpen #sidebarContainer {
212
- visibility: visible;
213
- }
214
-
215
- .thumbnail {
216
- margin: 0 10px 5px;
217
- }
218
-
219
- .thumbnailImage {
220
- border: 1px solid rgba(0, 0, 0, 0);
221
- box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
222
- opacity: 1.0;
223
- z-index: 99;
224
- background-color: rgba(255, 255, 255, 1);
225
- background-clip: content-box;
226
- }
227
-
228
- .thumbnailSelectionRing {
229
- border-radius: 2px;
230
- padding: 7px;
231
- }
232
-
233
- .thumbnail.selected > .thumbnailSelectionRing {
234
- background-color: rgba(0, 0, 0, 0.15);
235
- }
236
-
237
- ::slotted(#sidebarToggle) {
238
- margin-left: -10px;
239
- margin-right: 15px;
240
- border: 2px solid;
241
- border-color: rgba(0, 0, 0, 0.5);
242
- width: 40px;
243
- }
244
-
245
- ::slotted(#nextPage), ::slotted(#previousPage) {
246
- width: 30px;
247
- margin: 0;
248
- }
249
-
250
- [part~="toolbar"].ready ::slotted(.toolbar-zoom.hide-zoom) {
251
- display: none;
252
- }
253
-
254
- </style>
255
-
256
- <div id="outerContainer" part="outer-container" >
257
- <div id="sidebarContainer" part="sidebar-container">
258
- <div id="sidebarContent" part="sidebar-content">
259
- <div id="thumbnailView" part="thumbnail-view"></div>
260
- </div>
261
- </div>
262
- <div id="mainContainer" part="main-container">
263
- <div id="toolbar" part="toolbar">
264
- <slot name="sidebar-toggle-button-slot"></slot>
265
- <span id="title" part="toolbar-text toolbar-title">{{__title}}</span>
266
- <slot name="toolbar-zoom-slot"></slot>
267
- <div part="toolbar-pages">
268
- <slot name="toolbar-current-page-slot"></slot>
269
- <span id="pageSeparator" part="toolbar-text toolbar-page-separator">/</span>
270
- <span id="totalPages" part="toolbar-text toolbar-total-pages">{{__totalPages}}</span>
271
- <slot name="previous-page-button-slot"></slot>
272
- <slot name="next-page-button-slot"></slot>
273
- </div>
274
- <slot></slot>
275
- </div>
276
-
277
- <div id="viewerContainer" part="viewer-container" tabindex="0">
278
- <div id="viewer" part="viewer"></div>
279
- </div>
280
- </div>
281
- </div>
40
+ export class PdfViewerElement extends ResizeMixin(
41
+ SlotStylesMixin(ThemeDetectionMixin(ThemableMixin(ElementMixin(LitElement)))),
42
+ ) {
43
+ static get styles() {
44
+ return css`
45
+ :host {
46
+ display: flex;
47
+ flex-direction: column;
48
+ width: 100%;
49
+ height: 500px;
50
+ }
51
+
52
+ :host([hidden]) {
53
+ display: none !important;
54
+ }
55
+
56
+ [part~="toolbar"] #currentPage,
57
+ [part~="toolbar"] #pageSeparator,
58
+ [part~="toolbar"] #totalPages,
59
+ [part~="toolbar"] #previousPage,
60
+ [part~="toolbar"] #nextPage,
61
+ [part~="toolbar"] #zoom,
62
+ [part~="toolbar"] #sidebarToggle {
63
+ display: none;
64
+ }
65
+
66
+ [part~="toolbar"].ready #currentPage,
67
+ [part~="toolbar"].ready #pageSeparator,
68
+ [part~="toolbar"].ready #totalPages,
69
+ [part~="toolbar"].ready #previousPage,
70
+ [part~="toolbar"].ready #nextPage,
71
+ [part~="toolbar"].ready #zoom,
72
+ [part~="toolbar"].ready #sidebarToggle {
73
+ display: inherit;
74
+ }
75
+
76
+ [part~="outer-container"] {
77
+ width: 100%;
78
+ height: 100%;
79
+ }
80
+
81
+ [part~="main-container"] {
82
+ position: absolute;
83
+ top: 0;
84
+ right: 0;
85
+ bottom: 0;
86
+ left: 0;
87
+ min-width: 320px;
88
+ }
89
+
90
+ [part~="viewer-container"] {
91
+ position: absolute;
92
+ flex: 1;
93
+ overflow: auto;
94
+ width: 100%;
95
+ height: -moz-calc(100% - 45px); /* Firefox */
96
+ height: -webkit-calc(100% - 45px); /* Chrome, Safari */
97
+ height: calc(100% - 45px); /*all other browsers */
98
+ }
99
+
100
+ [part~="sidebar-container"] {
101
+ position: absolute;
102
+ width: 200px;
103
+ top: 45px;
104
+ bottom: 0;
105
+ visibility: hidden;
106
+ height: -moz-calc(100% - 45px); /* Firefox */
107
+ height: -webkit-calc(100% - 45px); /* Chrome, Safari */
108
+ height: calc(100% - 45px); /*all other browsers */
109
+ z-index: 100;
110
+ }
111
+
112
+ [part~="sidebar-content"] {
113
+ position: absolute;
114
+ top: 0;
115
+ bottom: 0;
116
+ overflow: auto;
117
+ width: 100%;
118
+ background-color: rgba(0, 0, 0, 0.1);
119
+ }
120
+
121
+ [part~="thumbnail-view"] {
122
+ position: absolute;
123
+ width: calc(100% - 60px);
124
+ top: 0;
125
+ bottom: 0;
126
+ padding: 10px 30px 0;
127
+ overflow: auto;
128
+ }
129
+
130
+ [part~="toolbar"] {
131
+ height: 44px;
132
+ }
133
+
134
+ .page {
135
+ position: relative;
136
+ margin: 0 auto;
137
+ }
138
+
139
+ .textLayer {
140
+ position: absolute;
141
+ left: 0;
142
+ top: 0;
143
+ right: 0;
144
+ bottom: 0;
145
+ overflow: hidden;
146
+ line-height: 1;
147
+ }
148
+
149
+ .textLayer > span {
150
+ color: transparent;
151
+ position: absolute;
152
+ white-space: pre;
153
+ cursor: text;
154
+ -webkit-transform-origin: 0% 0%;
155
+ transform-origin: 0% 0%;
156
+ }
157
+
158
+ .textLayer .highlight {
159
+ margin: -1px;
160
+ padding: 1px;
161
+ }
162
+
163
+ .textLayer .highlight.begin {
164
+ border-radius: 4px 0 0 4px;
165
+ }
166
+
167
+ .textLayer .highlight.end {
168
+ border-radius: 0 4px 4px 0;
169
+ }
170
+
171
+ .textLayer .highlight.middle {
172
+ border-radius: 0;
173
+ }
174
+
175
+ .textLayer .endOfContent {
176
+ display: block;
177
+ position: absolute;
178
+ left: 0;
179
+ top: 100%;
180
+ right: 0;
181
+ bottom: 0;
182
+ z-index: -1;
183
+ cursor: default;
184
+ -webkit-user-select: none;
185
+ -moz-user-select: none;
186
+ -ms-user-select: none;
187
+ user-select: none;
188
+ }
189
+
190
+ .textLayer .endOfContent.active {
191
+ top: 0;
192
+ }
193
+
194
+ #header {
195
+ display: flex;
196
+ flex-direction: row;
197
+ align-items: baseline;
198
+ }
199
+
200
+ ::slotted(#currentPage) {
201
+ align-self: baseline;
202
+ }
203
+
204
+ #outerContainer.sidebarOpen #viewerContainer {
205
+ transition-property: left;
206
+ left: 200px;
207
+ width: -moz-calc(100% - 200px); /* Firefox */
208
+ width: -webkit-calc(100% - 200px); /* Chrome, Safari */
209
+ width: calc(100% - 200px); /*all other browsers */
210
+ }
211
+
212
+ #outerContainer.sidebarOpen #sidebarContainer {
213
+ visibility: visible;
214
+ }
215
+
216
+ .thumbnail {
217
+ margin: 0 10px 5px;
218
+ }
219
+
220
+ .thumbnailImage {
221
+ border: 1px solid rgba(0, 0, 0, 0);
222
+ box-shadow:
223
+ 0 0 0 1px rgba(0, 0, 0, 0.5),
224
+ 0 2px 8px rgba(0, 0, 0, 0.3);
225
+ opacity: 1;
226
+ z-index: 99;
227
+ background-color: rgba(255, 255, 255, 1);
228
+ background-clip: content-box;
229
+ }
230
+
231
+ .thumbnailSelectionRing {
232
+ border-radius: 2px;
233
+ padding: 7px;
234
+ }
235
+
236
+ .thumbnail.selected > .thumbnailSelectionRing {
237
+ background-color: rgba(0, 0, 0, 0.15);
238
+ }
239
+
240
+ [part~="toolbar"].ready ::slotted(.toolbar-zoom.hide-zoom) {
241
+ display: none;
242
+ }
282
243
  `;
283
- }
284
-
285
- static get is() {
286
- return 'vcf-pdf-viewer';
287
- }
288
-
289
- static get version() {
290
- return '3.1.0';
291
- }
292
-
293
- static get properties() {
294
- return {
295
- /**
296
- * You can set a pdf file that you want to render with src. Note that regular cross
297
- * site scripting (XSS) rules apply. This means that the file should be on the same
298
- * server as where the component is run, or that the server where the file is on should
299
- * be configured to allow loading files from other sites.
300
- */
301
- src: {
302
- type: String,
303
- observer: '__srcChanged'
304
- },
305
-
306
- /**
307
- * The viewer, which takes care of rendering content into a DOM element.
308
- */
309
- __viewer: Object,
310
-
311
- /**
312
- * The viewer for thumbnails.
313
- */
314
- __thumbnailViewer: Object,
315
-
316
- /**
317
- * The link service.
318
- */
319
- __linkService: Object,
320
-
321
- /**
322
- * A represenentation of a document that has been read in.
323
- */
324
- __document: Object,
325
- /**
326
- * The title for the PDF shown in the toolbar of component. It uses both the file name and
327
- * the title in the PDF metadata if available.
328
- */
329
- __title: {
330
- type: String,
331
- value: 'PDF'
332
- },
333
- /**
334
- * Relative filename
335
- */
336
- __filename: String,
337
- /**
338
- * The pdf metadata title
339
- */
340
- __pdfTitle: String,
341
- /**
342
- * The level of zoom on the document.
343
- * Allowed values are
344
- * - Number, for zoom percentage. Eg. 1.5 means 150% zoom
345
- * - 'auto', default value
346
- * - 'page-fit', fit a full page into component
347
- */
348
- zoom: {
349
- type: String,
350
- value: 'auto'
351
- },
352
- /**
353
- * The current page visible viewed right now
354
- */
355
- currentPage: {
356
- type: String,
357
- value: "1"
358
- },
359
- /**
360
- * Total amount of pages in an opened document
361
- */
362
- __totalPages: Number,
363
-
364
- /**
365
- * Loading state
366
- */
367
- __loading: {
368
- type: Boolean,
369
- value: true
370
- },
244
+ }
371
245
 
372
- /**
373
- * Whether sidebar is open after loading or not
374
- */
375
- __sidebarOpen: {
376
- type: Boolean,
377
- value: false
378
- },
379
-
380
- /**
381
- * Flag to indicate if toolbar should only show filename as title
382
- */
383
- toolbarOnlyFilename: {
384
- type: Boolean,
385
- value: false
386
- },
387
-
388
- /**
389
- * Property to define auto zoom label
390
- */
391
- autoZoomOptionLabel: {
392
- type: String,
393
- value: "Automatic zoom"
394
- },
395
-
396
- /**
397
- * Property to define page fit zoom label
398
- */
399
- fitZoomOptionLabel: {
400
- type: String,
401
- value: "Page fit"
402
- },
403
-
404
- /**
405
- * Property to define a custom title for the viewer
406
- */
407
- customTitle: {
408
- type: String,
409
- value: ""
410
- },
411
-
412
- /**
413
- * Renders interactive form elements in the annotation layer (html) if true,
414
- * renders values of form elements directly onto the canvas if false
415
- */
416
- renderInteractiveForms: {
417
- type: Boolean,
418
- value: true
419
- },
420
-
421
- /**
422
- * Allows to hide the zoom dropdown. By default it's always shown.
423
- */
424
- hideZoom: {
425
- type: Boolean,
426
- value: false
427
- },
428
-
429
- __zoomItems: {
430
- computed: '__computeZoomItems(autoZoomOptionLabel, fitZoomOptionLabel)'
431
- },
432
-
433
- /**
434
- * Property to define a custom tooltip for the sidebar toggle button
435
- */
436
- sidebarToggleTooltip: {
437
- type: String,
438
- value: ""
439
- },
440
-
441
- /**
442
- * Property to define a custom tooltip for the previous page button
443
- */
444
- previousPageTooltip: {
445
- type: String,
446
- value: ""
447
- },
448
-
449
- /**
450
- * Property to define a custom tooltip for the next page button
451
- */
452
- nextPageTooltip: {
453
- type: String,
454
- value: ""
455
- },
456
- };
457
- }
458
-
459
- __createToolbarButton() {
460
- const icon = document.createElement('vaadin-icon');
461
- icon.setAttribute('slot', 'prefix');
462
-
463
- const tooltip = document.createElement('vaadin-tooltip');
464
- tooltip.setAttribute('slot', 'tooltip');
465
-
466
- const button = document.createElement('vaadin-button');
467
- button.classList.add('toolbar-button');
468
- button.setAttribute('theme', 'icon');
469
-
470
- button.appendChild(icon);
471
- button.appendChild(tooltip);
472
- return button;
473
- }
474
-
475
- /**
476
- * Adds toggle button to the toolbar slot named "sidebar-toggle-button-slot".
477
- */
478
- _createSideBarToggleButton() {
479
- const button = this.__createToolbarButton();
480
- const icon = button.querySelector('vaadin-icon');
481
- icon.classList.add('toggle-button-icon')
482
- button.querySelector('vaadin-tooltip').setAttribute('text', this.sidebarToggleTooltip);
483
- button.setAttribute('slot', 'sidebar-toggle-button-slot');
484
- button.setAttribute('id','sidebarToggle');
485
- button.setAttribute('aria-label', 'Sidebar toggle');
486
- button.addEventListener('click', () => {
487
- this.__toogleSidebar();
488
- if(this.$.outerContainer.classList.contains('sidebarOpen')) {
489
- icon.classList.add('sidebarOpen');
490
- } else {
491
- icon.classList.remove('sidebarOpen');
492
- }
493
- });
494
- this.appendChild(button);
495
- }
246
+ get slotStyles() {
247
+ const tag = "vcf-pdf-viewer";
248
+ const lumo = '[data-application-theme="lumo"]';
496
249
 
497
250
  /**
498
- * Adds previous page button to the toolbar slot named "previous-page-button-slot".
251
+ * These rules target a <vaadin-text-field> element with a child element
252
+ * having the 'toggle-button' class name. We can't use `::slotted()` as
253
+ * the toggle button is not a direct child of the month picker element.
254
+ * Also for `vaadin-popover` we can't use `::part()` after `::slotted()`.
499
255
  */
500
- _createPreviousPageButton(){
501
- const button = this.__createToolbarButton();
502
- button.querySelector('vaadin-icon').classList.add('previous-page-button-icon')
503
- button.querySelector('vaadin-tooltip').setAttribute('text', this.previousPageTooltip);
504
- button.setAttribute('slot', 'previous-page-button-slot');
505
- button.setAttribute('id', 'previousPage');
506
- button.setAttribute('aria-label', 'Previous page');
507
- button.addEventListener('click', () => this.__previousPage());
508
- this.appendChild(button);
509
- }
510
-
511
- /**
512
- * Adds next page button to the toolbar slot named "next-page-button-slot".
513
- */
514
- _createNextPageButton() {
515
- const button = this.__createToolbarButton();
516
- button.querySelector('vaadin-icon').classList.add('next-page-button-icon')
517
- button.querySelector('vaadin-tooltip').setAttribute('text', this.nextPageTooltip);
518
- button.setAttribute('slot', 'next-page-button-slot');
519
- button.setAttribute('id', 'nextPage');
520
- button.setAttribute('aria-label', 'Next page');
521
- button.addEventListener('click', () => this.__nextPage());
522
- this.appendChild(button);
523
- }
524
-
525
- /**
526
- * Adds current page text field to the toolbar slot named "toolbar-current-page-slot".
527
- */
528
- _createCurrentPageTextField() {
529
- const textField = document.createElement('vaadin-text-field');
530
- textField.setAttribute('slot', 'toolbar-current-page-slot');
531
- textField.setAttribute('id', 'currentPage');
532
- textField.classList.add('toolbar-current-page');
533
- textField.setAttribute('value', this.currentPage);
534
- textField.addEventListener('change', () => this.__pageChange());
535
- this.appendChild(textField);
536
- }
537
-
538
- /**
539
- * Adds zoom select to the toolbar slot named "toolbar-zoom-slot".
540
- */
541
- _createZoomSelect() {
542
- const select = document.createElement('vaadin-select');
543
- select.setAttribute('slot', 'toolbar-zoom-slot');
544
- select.setAttribute('id', 'zoom');
545
- select.classList.add('toolbar-zoom');
546
- select.setAttribute('value', this.zoom);
547
- select.items = this.__zoomItems;
548
- select.addEventListener('value-changed', (e) => this.__zoomChanged(e.detail.value));
549
- if(this.hideZoom) {
550
- select.classList.add('hide-zoom');
551
- } else {
552
- select.classList.remove('hide-zoom');
256
+ return [
257
+ `
258
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button vaadin-icon {
259
+ display: none;
553
260
  }
554
- this.appendChild(select);
555
- }
556
-
557
- __computeZoomItems(autoZoomOptionLabel, fitZoomOptionLabel) {
558
- return [
559
- { label: autoZoomOptionLabel, value:'auto' },
560
- { label: fitZoomOptionLabel, value:'page-fit' },
561
- { label: '50%', value:'0.5' },
562
- { label: '75%', value:'0.75' },
563
- { label: '100%', value:'1.0' },
564
- { label: '125%', value:'1.25' },
565
- { label: '150%', value:'1.5' },
566
- { label: '200%', value:'2.0' },
567
- { label: '300%', value:'3.0' },
568
- { label: '400%', value:'4.0' }
569
- ]
570
- }
571
-
572
- static get observers() {
573
- return [
574
- '__setTitle(__pdfTitle, __filename)'
575
- ];
576
- }
577
-
578
- __setTitle(pdfTitle, filename) {
579
- if(this.customTitle){
580
- this.__title = this.customTitle;
581
- } else if(this.__viewer && this.toolbarOnlyFilename && filename) {
582
- this.__title = filename;
583
- } else if (pdfTitle && filename) {
584
- this.__title = pdfTitle + ' - ' + filename;
585
- } else if (pdfTitle) {
586
- this.__title = pdfTitle;
587
- } else if (filename) {
588
- this.__title = filename;
589
- } else {
590
- this.__title = 'PDF';
261
+
262
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button {
263
+ height: var(--vaadin-icon-size, 1.5lh);
264
+ width: var(--vaadin-icon-size, 1.5lh);
265
+ padding: 0;
266
+ justify-content: flex-start;
267
+ box-sizing: content-box;
591
268
  }
592
- }
593
-
594
- _addToolbarButtons() {
595
- this._createSideBarToggleButton();
596
- this._createZoomSelect();
597
- this._createCurrentPageTextField();
598
- this._createPreviousPageButton();
599
- this._createNextPageButton();
600
- }
601
-
602
- ready() {
603
- super.ready();
604
-
605
- this._addToolbarButtons();
606
-
607
- this.$.viewerContainer.addEventListener('focus', e => this.__setFocused(true), true);
608
- this.$.viewerContainer.addEventListener('blur', e => this.__setFocused(false), true);
609
- this.$.viewerContainer.addEventListener('mousedown', e => {
610
- this._mousedown = true;
611
- const mouseUpListener = () => {
612
- this._mousedown = false;
613
- document.removeEventListener('mouseup', mouseUpListener);
614
- };
615
- document.addEventListener('mouseup', mouseUpListener);
616
- });
617
-
618
- // options
619
- const eventBus = new pdfUtils.EventBus();
620
- this.__linkService = new pdfjsLinkService.PDFLinkService({
621
- eventBus,
622
- });
623
- var pdfRenderingQueue = new pdfjsRenderingQueue.PDFRenderingQueue();
624
- var l10n = NullL10n;
625
-
626
- // pdfViewer
627
- this.__viewer = new pdfjsViewer.PDFViewer({
628
- container: this.$.viewerContainer,
629
- textLayerMode: 2,
630
- viewer: this.$.viewer,
631
- eventBus: eventBus,
632
- linkService: this.__linkService,
633
- renderingQueue: pdfRenderingQueue,
634
- l10n: l10n,
635
- renderInteractiveForms: this.renderInteractiveForms
636
- });
637
-
638
- this.__linkService.setViewer(this.__viewer);
639
- pdfRenderingQueue.setViewer(this.__viewer);
640
-
641
- // thumbnailViewer
642
- this.__thumbnailViewer = new pdfjsThumbnailViewer.PDFThumbnailViewer({
643
- container: this.$.thumbnailView,
644
- eventBus: eventBus,
645
- linkService: this.__linkService,
646
- renderingQueue: pdfRenderingQueue,
647
- l10n: l10n
648
- })
649
-
650
- pdfRenderingQueue.setThumbnailViewer(this.__thumbnailViewer);
651
-
652
- // listeners
653
- eventBus.on('pagesinit', () => {
654
- this.__viewer.currentScaleValue = this.zoom;
655
- this.__loading = false;
656
- this.__updateThumbnailViewer();
657
- if(this.__sidebarOpen){
658
- this.__openSidebar();
659
- } else {
660
- this.__closeSidebar();
661
- }
662
- this.__viewer.currentPage = this.setCurrentPage();
663
- });
664
- eventBus.on('pagechanging', (event) => {
665
- this.__updateCurrentPageValue(event.pageNumber);
666
- this.__updatePageNumberStates();
667
- if(this.__thumbnailViewer && this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled){
668
- this.__thumbnailViewer.scrollThumbnailIntoView(this.currentPage);
669
- }
670
- this.querySelector('#currentPage').value = this.currentPage;
671
- });
672
-
673
- this.__resizeObserver = new ResizeObserver(() => {
674
- requestAnimationFrame(() => this.__recalculateSizes());
675
- });
676
-
677
- this.__resizeObserver.observe(this);
678
- }
679
-
680
- connectedCallback() {
681
- super.connectedCallback();
682
- this.__recalculateSizes();
683
- }
684
-
685
- __updateCurrentPageValue(pageNumber){
686
- this.currentPage = "" + pageNumber;
687
- this.dispatchEvent(new CustomEvent('currentPage-changed'));
688
- }
689
-
690
- __recalculateSizes() {
691
- if (this.offsetWidth < 600) {
692
- this.classList.add('small-size');
693
- this.$.toolbar.classList.add('small-size');
694
- } else {
695
- this.classList.remove('small-size');
696
- this.$.toolbar.classList.remove('small-size');
269
+
270
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button::before {
271
+ content: '';
272
+ flex: none;
273
+ background: var(--vaadin-input-field-button-text-color, var(--vaadin-text-color-secondary));
274
+ cursor: var(--vaadin-clickable-cursor);
275
+ touch-action: manipulation;
276
+ -webkit-tap-highlight-color: transparent;
277
+ -webkit-user-select: none;
278
+ user-select: none;
279
+ height: var(--vaadin-icon-size, 1.5lh);
280
+ width: var(--vaadin-icon-size, 1.5lh);
281
+ mask-size: var(--vaadin-icon-visual-size, 100%);
282
+ mask-position: 50%;
283
+ mask-repeat: no-repeat;
697
284
  }
698
- }
699
- __setFocused(focused) {
700
- if (focused) {
701
- this.$.viewerContainer.setAttribute('focused', '');
702
- if (!this._mousedown) {
703
- this.$.viewerContainer.setAttribute('focus-ring', '');
704
- }
705
- } else {
706
- this.$.viewerContainer.removeAttribute('focused');
707
- this.$.viewerContainer.removeAttribute('focus-ring');
285
+
286
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button#sidebarToggle::before {
287
+ mask-image: var(--pdf-viewer-toggle-button-icon-closed);
708
288
  }
709
- }
710
-
711
- __open(src) {
712
- // Is there already a document loaded?
713
- if (this.__document) {
714
- // We need to close the current document
715
- return this.__close().then(() => {
716
- // and start over with opening the new one
717
- return this.__open(src);
718
- });
289
+
290
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button#sidebarToggle:has(vaadin-icon.sidebarOpen)::before {
291
+ mask-image: var(--pdf-viewer-toggle-button-icon-open);
719
292
  }
720
- if (!src) {
721
- // No file given, show nothing.
722
- return;
293
+
294
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button#previousPage::before {
295
+ mask-image: var(--pdf-viewer-previous-page-button-icon);
723
296
  }
724
- this.__setFilename(src);
725
- this.__document = pdfjsLib.getDocument(new URL(src, document.baseURI).href);
726
- return this.__document.promise.then((pdfDocument) => {
727
- // Document loaded, specifying document for the viewer.
728
- this.__thumbnailViewer.setDocument(pdfDocument);
729
- this.__viewer.setDocument(pdfDocument);
730
- this.__linkService.setDocument(pdfDocument);
731
-
732
- this.$.toolbar.classList.add('ready');
733
- this.__totalPages = pdfDocument.numPages;
734
- this.__updatePageNumberStates();
735
- this.__setPdfTitleFromMetadata(pdfDocument).then(() => {
736
- this.dispatchEvent(new CustomEvent('document-loaded', {
737
- detail: {
738
- document: pdfDocument
739
- }
740
- }));
741
- });
742
- }, function (exception) {
743
- console.error(exception && exception.message);
744
- });
745
- }
746
-
747
- __srcChanged(newSrc) {
748
- this.__open(newSrc);
749
- }
750
-
751
- /**
752
- * Closes opened PDF document.
753
- * @returns {Promise} - Returns the promise, which is resolved when all
754
- * destruction is completed.
755
- */
756
- __close() {
757
- this.$.toolbar.classList.remove('ready');
758
- this.__filename = 'PDF';
759
- if (!this.__document) {
760
- return Promise.resolve();
297
+
298
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button#nextPage::before {
299
+ mask-image: var(--pdf-viewer-next-page-button-icon);
761
300
  }
762
-
763
- var promise = this.__document.destroy();
764
- if (this.__document) {
765
- this.__document = null;
766
- this.__viewer.setDocument(null);
767
- this.__thumbnailViewer.setDocument(null);
768
- this.__linkService.setDocument(null);
301
+
302
+ ${tag}${lumo} vaadin-button[theme~="icon"] {
303
+ padding-left: 6px;
304
+ padding-right: 6px;
769
305
  }
770
- return promise;
306
+
307
+ ${tag}${lumo} vaadin-button.toolbar-button#sidebarToggle {
308
+ border: 2px solid;
309
+ border-color: rgba(0, 0, 0, 0.5);
310
+ }
311
+ `,
312
+ ];
313
+ }
314
+
315
+ render() {
316
+ return html`
317
+ <div id="outerContainer" part="outer-container">
318
+ <div id="sidebarContainer" part="sidebar-container">
319
+ <div id="sidebarContent" part="sidebar-content">
320
+ <div id="thumbnailView" part="thumbnail-view"></div>
321
+ </div>
322
+ </div>
323
+ <div id="mainContainer" part="main-container">
324
+ <div id="toolbar" part="toolbar">
325
+ <slot name="sidebar-toggle-button-slot"></slot>
326
+ <span id="title" part="toolbar-text toolbar-title"
327
+ >${this.__title}</span
328
+ >
329
+ <slot name="toolbar-zoom-slot"></slot>
330
+ <div part="toolbar-pages">
331
+ <slot name="toolbar-current-page-slot"></slot>
332
+ <span
333
+ id="pageSeparator"
334
+ part="toolbar-text toolbar-page-separator"
335
+ >/</span
336
+ >
337
+ <span id="totalPages" part="toolbar-text toolbar-total-pages"
338
+ >${this.__totalPages}</span
339
+ >
340
+ <slot name="previous-page-button-slot"></slot>
341
+ <slot name="next-page-button-slot"></slot>
342
+ </div>
343
+ <slot></slot>
344
+ </div>
345
+
346
+ <div id="viewerContainer" part="viewer-container">
347
+ <div id="viewer" part="viewer"></div>
348
+ </div>
349
+ </div>
350
+ </div>
351
+ `;
352
+ }
353
+
354
+ static get is() {
355
+ return "vcf-pdf-viewer";
356
+ }
357
+
358
+ static get version() {
359
+ return "4.0.0";
360
+ }
361
+
362
+ static get properties() {
363
+ return {
364
+ /**
365
+ * You can set a pdf file that you want to render with src. Note that regular cross
366
+ * site scripting (XSS) rules apply. This means that the file should be on the same
367
+ * server as where the component is run, or that the server where the file is on should
368
+ * be configured to allow loading files from other sites.
369
+ */
370
+ src: { type: String },
371
+
372
+ /**
373
+ * The viewer, which takes care of rendering content into a DOM element.
374
+ */
375
+ __viewer: Object,
376
+
377
+ /**
378
+ * The viewer for thumbnails.
379
+ */
380
+ __thumbnailViewer: Object,
381
+
382
+ /**
383
+ * The link service.
384
+ */
385
+ __linkService: Object,
386
+
387
+ /**
388
+ * A represenentation of a document that has been read in.
389
+ */
390
+ __document: Object,
391
+ /**
392
+ * The title for the PDF shown in the toolbar of component. It uses both the file name and
393
+ * the title in the PDF metadata if available.
394
+ */
395
+ __title: { type: String },
396
+ /**
397
+ * Relative filename
398
+ */
399
+ __filename: String,
400
+ /**
401
+ * The pdf metadata title
402
+ */
403
+ __pdfTitle: String,
404
+ /**
405
+ * The level of zoom on the document.
406
+ * Allowed values are
407
+ * - Number, for zoom percentage. Eg. 1.5 means 150% zoom
408
+ * - 'auto', default value
409
+ * - 'page-fit', fit a full page into component
410
+ */
411
+ zoom: { type: String },
412
+ /**
413
+ * The current page visible viewed right now
414
+ */
415
+ currentPage: { type: String },
416
+ /**
417
+ * Total amount of pages in an opened document
418
+ */
419
+ __totalPages: Number,
420
+
421
+ /**
422
+ * Loading state
423
+ */
424
+ __loading: { type: Boolean },
425
+
426
+ /**
427
+ * Whether sidebar is open after loading or not
428
+ */
429
+ __sidebarOpen: { type: Boolean },
430
+
431
+ /**
432
+ * Flag to indicate if toolbar should only show filename as title
433
+ */
434
+ toolbarOnlyFilename: { type: Boolean },
435
+
436
+ /**
437
+ * Property to define auto zoom label
438
+ */
439
+ autoZoomOptionLabel: { type: String },
440
+
441
+ /**
442
+ * Property to define page fit zoom label
443
+ */
444
+ fitZoomOptionLabel: { type: String },
445
+
446
+ /**
447
+ * Property to define a custom title for the viewer
448
+ */
449
+ customTitle: { type: String },
450
+
451
+ /**
452
+ * Renders interactive form elements in the annotation layer (html) if true,
453
+ * renders values of form elements directly onto the canvas if false
454
+ */
455
+ renderInteractiveForms: { type: Boolean },
456
+
457
+ /**
458
+ * Allows to hide the zoom dropdown. By default it's always shown.
459
+ */
460
+ hideZoom: { type: Boolean },
461
+
462
+ /**
463
+ * Property to define a custom tooltip for the sidebar toggle button
464
+ */
465
+ sidebarToggleTooltip: { type: String },
466
+
467
+ /**
468
+ * Property to define a custom tooltip for the previous page button
469
+ */
470
+ previousPageTooltip: { type: String },
471
+
472
+ /**
473
+ * Property to define a custom tooltip for the next page button
474
+ */
475
+ nextPageTooltip: { type: String },
476
+ };
477
+ }
478
+
479
+ get __zoomItems() {
480
+ return this.__computeZoomItems(
481
+ this.autoZoomOptionLabel,
482
+ this.fitZoomOptionLabel,
483
+ );
484
+ }
485
+
486
+ constructor() {
487
+ super();
488
+ this.__title = "PDF";
489
+ this.zoom = "auto";
490
+ this.currentPage = "1";
491
+ this.__loading = true;
492
+ this.__sidebarOpen = false;
493
+ this.toolbarOnlyFilename = false;
494
+ this.autoZoomOptionLabel = "Automatic zoom";
495
+ this.fitZoomOptionLabel = "Page fit";
496
+ this.customTitle = "";
497
+ this.renderInteractiveForms = true;
498
+ this.hideZoom = false;
499
+ this.sidebarToggleTooltip = "";
500
+ this.previousPageTooltip = "";
501
+ this.nextPageTooltip = "";
502
+ }
503
+
504
+ willUpdate(changedProperties) {
505
+ super.willUpdate(changedProperties);
506
+
507
+ // Compute filename if src changes
508
+ if (changedProperties.has("src")) {
509
+ this.__setFilename(this.src);
771
510
  }
772
511
 
773
- __setFilename(src) {
774
- let filename = pdfjsLib.getFilenameFromUrl(src) || src;
775
- try {
776
- filename = decodeURIComponent(filename);
777
- } catch (e) {
778
- // decodeURIComponent may throw URIError,
779
- // fall back to using the unprocessed url in that case
512
+ // Compute title before rendering to avoid setting reactive properties during update
513
+ if (
514
+ changedProperties.has("src") ||
515
+ changedProperties.has("customTitle") ||
516
+ changedProperties.has("toolbarOnlyFilename")
517
+ ) {
518
+ this.__setTitle(this.__pdfTitle, this.__filename);
519
+ }
520
+ }
521
+
522
+ updated(changedProperties) {
523
+ super.updated(changedProperties);
524
+
525
+ // Defer property synchronization to avoid "update during update" warning
526
+ // This ensures these calls happen after the current update cycle completes
527
+ Promise.resolve().then(() => {
528
+ if (changedProperties.has("src")) {
529
+ this.__srcChanged(this.src);
530
+ }
531
+ if (changedProperties.has("zoom")) {
532
+ const zoomSelect = this.querySelector("#zoom");
533
+ if (zoomSelect && zoomSelect.value !== this.zoom) {
534
+ zoomSelect.value = this.zoom;
780
535
  }
781
- this.__filename = filename;
536
+ this.__zoomChanged(this.zoom);
537
+ }
538
+ });
539
+ }
540
+
541
+ __createToolbarButton() {
542
+ const icon = document.createElement("vaadin-icon");
543
+ icon.setAttribute("slot", "prefix");
544
+
545
+ const tooltip = document.createElement("vaadin-tooltip");
546
+ tooltip.setAttribute("slot", "tooltip");
547
+
548
+ const button = document.createElement("vaadin-button");
549
+ button.classList.add("toolbar-button");
550
+ button.setAttribute("theme", "icon tertiary contrast");
551
+
552
+ button.appendChild(icon);
553
+ button.appendChild(tooltip);
554
+ return button;
555
+ }
556
+
557
+ /**
558
+ * Adds toggle button to the toolbar slot named "sidebar-toggle-button-slot".
559
+ */
560
+ _createSideBarToggleButton() {
561
+ const button = this.__createToolbarButton();
562
+ const icon = button.querySelector("vaadin-icon");
563
+ icon.classList.add("toggle-button-icon");
564
+ button
565
+ .querySelector("vaadin-tooltip")
566
+ .setAttribute("text", this.sidebarToggleTooltip);
567
+ button.setAttribute("slot", "sidebar-toggle-button-slot");
568
+ button.setAttribute("id", "sidebarToggle");
569
+ button.setAttribute("aria-label", "Sidebar toggle");
570
+ button.addEventListener("click", () => {
571
+ this.__toogleSidebar();
572
+ if (this._outerContainer.classList.contains("sidebarOpen")) {
573
+ icon.classList.add("sidebarOpen");
574
+ } else {
575
+ icon.classList.remove("sidebarOpen");
576
+ }
577
+ });
578
+ this.appendChild(button);
579
+ }
580
+
581
+ /**
582
+ * Adds previous page button to the toolbar slot named "previous-page-button-slot".
583
+ */
584
+ _createPreviousPageButton() {
585
+ const button = this.__createToolbarButton();
586
+ button
587
+ .querySelector("vaadin-icon")
588
+ .classList.add("previous-page-button-icon");
589
+ button
590
+ .querySelector("vaadin-tooltip")
591
+ .setAttribute("text", this.previousPageTooltip);
592
+ button.setAttribute("slot", "previous-page-button-slot");
593
+ button.setAttribute("id", "previousPage");
594
+ button.setAttribute("aria-label", "Previous page");
595
+ button.addEventListener("click", () => this.__previousPage());
596
+ this.appendChild(button);
597
+ }
598
+
599
+ /**
600
+ * Adds next page button to the toolbar slot named "next-page-button-slot".
601
+ */
602
+ _createNextPageButton() {
603
+ const button = this.__createToolbarButton();
604
+ button.querySelector("vaadin-icon").classList.add("next-page-button-icon");
605
+ button
606
+ .querySelector("vaadin-tooltip")
607
+ .setAttribute("text", this.nextPageTooltip);
608
+ button.setAttribute("slot", "next-page-button-slot");
609
+ button.setAttribute("id", "nextPage");
610
+ button.setAttribute("aria-label", "Next page");
611
+ button.addEventListener("click", () => this.__nextPage());
612
+ this.appendChild(button);
613
+ }
614
+
615
+ /**
616
+ * Adds current page text field to the toolbar slot named "toolbar-current-page-slot".
617
+ */
618
+ _createCurrentPageTextField() {
619
+ const textField = document.createElement("vaadin-text-field");
620
+ textField.setAttribute("slot", "toolbar-current-page-slot");
621
+ textField.setAttribute("id", "currentPage");
622
+ textField.classList.add("toolbar-current-page");
623
+ textField.setAttribute("value", this.currentPage);
624
+ textField.addEventListener("change", () => this.__pageChange());
625
+ this.appendChild(textField);
626
+ }
627
+
628
+ /**
629
+ * Adds zoom select to the toolbar slot named "toolbar-zoom-slot".
630
+ */
631
+ _createZoomSelect() {
632
+ const select = document.createElement("vaadin-select");
633
+ select.setAttribute("slot", "toolbar-zoom-slot");
634
+ select.setAttribute("id", "zoom");
635
+ select.classList.add("toolbar-zoom");
636
+ select.setAttribute("value", this.zoom);
637
+ select.items = this.__zoomItems;
638
+ select.addEventListener("value-changed", (e) => {
639
+ if (e.detail.value !== this.zoom) {
640
+ this.__zoomChanged(e.detail.value);
641
+ }
642
+ });
643
+ if (this.hideZoom) {
644
+ select.classList.add("hide-zoom");
645
+ } else {
646
+ select.classList.remove("hide-zoom");
782
647
  }
783
-
784
- __setPdfTitleFromMetadata(pdfDocument) {
785
- return pdfDocument.getMetadata().then((data) => {
786
- let pdfTitle;
787
- const metadata = data.metadata;
788
- if (metadata && metadata.has('dc:title')) {
789
- const title = metadata.get('dc:title');
790
- // Ghostscript sometimes returns 'Untitled', so prevent setting the
791
- // title to 'Untitled'.
792
- if (title !== 'Untitled') {
793
- pdfTitle = title;
794
- }
795
- }
796
-
797
- const info = data.info;
798
- if (!pdfTitle && info && info['Title']) {
799
- pdfTitle = info['Title'];
800
- }
801
- this.__pdfTitle = pdfTitle;
648
+ this.appendChild(select);
649
+ }
650
+
651
+ __computeZoomItems(autoZoomOptionLabel, fitZoomOptionLabel) {
652
+ return [
653
+ { label: autoZoomOptionLabel, value: "auto" },
654
+ { label: fitZoomOptionLabel, value: "page-fit" },
655
+ { label: "50%", value: "0.5" },
656
+ { label: "75%", value: "0.75" },
657
+ { label: "100%", value: "1.0" },
658
+ { label: "125%", value: "1.25" },
659
+ { label: "150%", value: "1.5" },
660
+ { label: "200%", value: "2.0" },
661
+ { label: "300%", value: "3.0" },
662
+ { label: "400%", value: "4.0" },
663
+ ];
664
+ }
665
+
666
+ __setTitle(pdfTitle, filename) {
667
+ if (this.customTitle) {
668
+ this.__title = this.customTitle;
669
+ } else if (this.__viewer && this.toolbarOnlyFilename && filename) {
670
+ this.__title = filename;
671
+ } else if (pdfTitle && filename) {
672
+ this.__title = pdfTitle + " - " + filename;
673
+ } else if (pdfTitle) {
674
+ this.__title = pdfTitle;
675
+ } else if (filename) {
676
+ this.__title = filename;
677
+ } else {
678
+ this.__title = "PDF";
679
+ }
680
+ }
681
+
682
+ _addToolbarButtons() {
683
+ this._createSideBarToggleButton();
684
+ this._createZoomSelect();
685
+ this._createCurrentPageTextField();
686
+ this._createPreviousPageButton();
687
+ this._createNextPageButton();
688
+ }
689
+
690
+ firstUpdated() {
691
+ this._toolbar = this.shadowRoot.querySelector("#toolbar");
692
+ this._viewerContainer = this.shadowRoot.querySelector("#viewerContainer");
693
+ this._outerContainer = this.shadowRoot.querySelector("#outerContainer");
694
+ this._viewer = this.shadowRoot.querySelector("#viewer");
695
+ this._thumbnailView = this.shadowRoot.querySelector("#thumbnailView");
696
+
697
+ this._addToolbarButtons();
698
+
699
+ this._viewerContainer.addEventListener(
700
+ "focus",
701
+ (e) => this.__setFocused(true),
702
+ true,
703
+ );
704
+ this._viewerContainer.addEventListener(
705
+ "blur",
706
+ (e) => this.__setFocused(false),
707
+ true,
708
+ );
709
+ this._viewerContainer.addEventListener("mousedown", (e) => {
710
+ this._mousedown = true;
711
+ const mouseUpListener = () => {
712
+ this._mousedown = false;
713
+ document.removeEventListener("mouseup", mouseUpListener);
714
+ };
715
+ document.addEventListener("mouseup", mouseUpListener);
716
+ });
717
+
718
+ // options
719
+ const eventBus = new pdfUtils.EventBus();
720
+
721
+ // Defer initialization of reactive controller properties to avoid immediate update request
722
+ // that triggers "update scheduled after update" warning.
723
+ Promise.resolve().then(() => {
724
+ this.__linkService = new pdfjsLinkService.PDFLinkService({
725
+ eventBus,
726
+ });
727
+ var pdfRenderingQueue = new pdfjsRenderingQueue.PDFRenderingQueue();
728
+ var l10n = NullL10n;
729
+
730
+ // pdfViewer
731
+ this.__viewer = new pdfjsViewer.PDFViewer({
732
+ container: this._viewerContainer,
733
+ textLayerMode: 2,
734
+ viewer: this._viewer,
735
+ eventBus: eventBus,
736
+ linkService: this.__linkService,
737
+ renderingQueue: pdfRenderingQueue,
738
+ l10n: l10n,
739
+ renderInteractiveForms: this.renderInteractiveForms,
740
+ });
741
+
742
+ this.__linkService.setViewer(this.__viewer);
743
+ pdfRenderingQueue.setViewer(this.__viewer);
744
+
745
+ // thumbnailViewer
746
+ this.__thumbnailViewer = new pdfjsThumbnailViewer.PDFThumbnailViewer({
747
+ container: this._thumbnailView,
748
+ eventBus: eventBus,
749
+ linkService: this.__linkService,
750
+ renderingQueue: pdfRenderingQueue,
751
+ l10n: l10n,
752
+ });
753
+
754
+ pdfRenderingQueue.setThumbnailViewer(this.__thumbnailViewer);
755
+
756
+ // Initialize listeners after viewer creation
757
+ this.__initListeners(eventBus);
758
+ });
759
+ }
760
+
761
+ __initListeners(eventBus) {
762
+ // listeners
763
+ eventBus.on("pagesinit", () => {
764
+ this.__viewer.currentScaleValue = this.zoom;
765
+ this.__loading = false;
766
+ this.__updateThumbnailViewer();
767
+ if (this.__sidebarOpen) {
768
+ this.__openSidebar();
769
+ } else {
770
+ this.__closeSidebar();
771
+ }
772
+ this.__viewer.currentPage = this.setCurrentPage();
773
+ });
774
+ eventBus.on("pagechanging", (event) => {
775
+ this.__updateCurrentPageValue(event.pageNumber);
776
+ this.__updatePageNumberStates();
777
+ if (
778
+ this.__thumbnailViewer &&
779
+ this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled
780
+ ) {
781
+ this.__thumbnailViewer.scrollThumbnailIntoView(this.currentPage);
782
+ }
783
+ this.querySelector("#currentPage").value = this.currentPage;
784
+ });
785
+ }
786
+
787
+ __updateCurrentPageValue(pageNumber) {
788
+ this.currentPage = "" + pageNumber;
789
+ this.dispatchEvent(new CustomEvent("currentPage-changed"));
790
+ }
791
+
792
+ __setFocused(focused) {
793
+ if (focused) {
794
+ this._viewerContainer.setAttribute("focused", "");
795
+ if (!this._mousedown) {
796
+ this._viewerContainer.setAttribute("focus-ring", "");
797
+ }
798
+ } else {
799
+ this._viewerContainer.removeAttribute("focused");
800
+ this._viewerContainer.removeAttribute("focus-ring");
801
+ }
802
+ }
803
+
804
+ __open(src) {
805
+ // Is there already a document loaded?
806
+ if (this.__document) {
807
+ // We need to close the current document
808
+ return this.__close().then(() => {
809
+ // and start over with opening the new one
810
+ return this.__open(src);
811
+ });
812
+ }
813
+ if (!src) {
814
+ // No file given, show nothing.
815
+ return;
816
+ }
817
+ this.__setFilename(src);
818
+ this.__document = pdfjsLib.getDocument(new URL(src, document.baseURI).href);
819
+ return this.__document.promise.then(
820
+ (pdfDocument) => {
821
+ // Document loaded, specifying document for the viewer.
822
+ this.__thumbnailViewer.setDocument(pdfDocument);
823
+ this.__viewer.setDocument(pdfDocument);
824
+ this.__linkService.setDocument(pdfDocument);
825
+
826
+ this._toolbar.classList.add("ready");
827
+ this.__totalPages = pdfDocument.numPages;
828
+ this.__updatePageNumberStates();
829
+ this.__setPdfTitleFromMetadata(pdfDocument).then(() => {
830
+ this.dispatchEvent(
831
+ new CustomEvent("document-loaded", {
832
+ detail: {
833
+ document: pdfDocument,
834
+ },
835
+ }),
836
+ );
802
837
  });
838
+ },
839
+ function (exception) {
840
+ console.error(exception && exception.message);
841
+ },
842
+ );
843
+ }
844
+
845
+ __srcChanged(newSrc) {
846
+ this.__open(newSrc);
847
+ }
848
+
849
+ /**
850
+ * Closes opened PDF document.
851
+ * @returns {Promise} - Returns the promise, which is resolved when all
852
+ * destruction is completed.
853
+ */
854
+ __close() {
855
+ this._toolbar.classList.remove("ready");
856
+ this.__filename = "PDF";
857
+ if (!this.__document) {
858
+ return Promise.resolve();
803
859
  }
804
860
 
805
- __updatePageNumberStates() {
806
- this.querySelector('#previousPage').disabled = (this.currentPage === "1");
807
- this.querySelector('#nextPage').disabled = (this.currentPage === "" + this.__totalPages);
861
+ var promise = this.__document.destroy();
862
+ if (this.__document) {
863
+ this.__document = null;
864
+ this.__viewer.setDocument(null);
865
+ this.__thumbnailViewer.setDocument(null);
866
+ this.__linkService.setDocument(null);
808
867
  }
809
-
810
- __zoomChanged(value) {
811
- if (!this.__viewer || this.__loading) {
812
- return;
813
- }
814
- // This logs error 'TextLayerBuilder._bindEvents: `this.cancel()` should have
815
- // been called when the page was reset, or rendering cancelled.'
816
- //
817
- // There is a problem deep inside pdfjs viewer that causes an console.error()
818
- // to be logged, but the component still works. It seems to be due to
819
- // webcomponents/shadow dom messing with
820
- // TODO: Fix the issue so that we get rid of the error in log
821
- this.__viewer.currentScaleValue = value;
822
- this.__viewer.forceRendering();
868
+ return promise;
869
+ }
870
+
871
+ __setFilename(src) {
872
+ let filename = pdfjsLib.getFilenameFromUrl(src) || src;
873
+ try {
874
+ filename = decodeURIComponent(filename);
875
+ } catch (e) {
876
+ // decodeURIComponent may throw URIError,
877
+ // fall back to using the unprocessed url in that case
823
878
  }
824
-
825
- __pageChange(event) {
826
- const currentPageValue = this.querySelector('#currentPage').value;
827
- let pageNumber = parseInt(currentPageValue, 10);
828
- if (isNaN(pageNumber)) {
829
- pageNumber = this.__viewer.currentPageNumber;
830
- this.querySelector('#currentPage').value = "" + pageNumber;
831
- }
832
- if (pageNumber < 1) {
833
- pageNumber = 1;
879
+ this.__filename = filename;
880
+ }
881
+
882
+ __setPdfTitleFromMetadata(pdfDocument) {
883
+ return pdfDocument.getMetadata().then((data) => {
884
+ let pdfTitle;
885
+ const metadata = data.metadata;
886
+ if (metadata && metadata.has("dc:title")) {
887
+ const title = metadata.get("dc:title");
888
+ // Ghostscript sometimes returns 'Untitled', so prevent setting the
889
+ // title to 'Untitled'.
890
+ if (title !== "Untitled") {
891
+ pdfTitle = title;
834
892
  }
835
- if (pageNumber > this.__totalPages) {
836
- pageNumber = this.__totalPages;
837
- }
838
- this.__viewer.currentPageNumber = pageNumber;
893
+ }
894
+
895
+ const info = data.info;
896
+ if (!pdfTitle && info && info["Title"]) {
897
+ pdfTitle = info["Title"];
898
+ }
899
+ this.__pdfTitle = pdfTitle;
900
+ });
901
+ }
902
+
903
+ __updatePageNumberStates() {
904
+ this.querySelector("#previousPage").disabled = this.currentPage === "1";
905
+ this.querySelector("#nextPage").disabled =
906
+ this.currentPage === "" + this.__totalPages;
907
+ }
908
+
909
+ __zoomChanged(value) {
910
+ if (!this.__viewer || this.__loading) {
911
+ return;
839
912
  }
840
-
841
- setCurrentPage(value) {
842
- if (value != undefined) {
843
- this.querySelector('#currentPage').value = "" + value;
844
- }
845
- this.__pageChange();
913
+ // This logs error 'TextLayerBuilder._bindEvents: `this.cancel()` should have
914
+ // been called when the page was reset, or rendering cancelled.'
915
+ //
916
+ // There is a problem deep inside pdfjs viewer that causes an console.error()
917
+ // to be logged, but the component still works. It seems to be due to
918
+ // webcomponents/shadow dom messing with
919
+ // TODO: Fix the issue so that we get rid of the error in log
920
+ this.__viewer.currentScaleValue = value;
921
+ this.__viewer.forceRendering();
922
+ }
923
+
924
+ __pageChange(event) {
925
+ const currentPageValue = this.querySelector("#currentPage").value;
926
+ let pageNumber = parseInt(currentPageValue, 10);
927
+ if (isNaN(pageNumber)) {
928
+ pageNumber = this.__viewer.currentPageNumber;
929
+ this.querySelector("#currentPage").value = "" + pageNumber;
846
930
  }
847
-
848
- _getPage() {
849
- return this.__viewer.currentPageNumber;
931
+ if (pageNumber < 1) {
932
+ pageNumber = 1;
850
933
  }
851
-
852
- __previousPage() {
853
- this.__viewer.currentPageNumber--;
934
+ if (pageNumber > this.__totalPages) {
935
+ pageNumber = this.__totalPages;
854
936
  }
937
+ this.__viewer.currentPageNumber = pageNumber;
938
+ }
855
939
 
856
- __nextPage() {
857
- this.__viewer.currentPageNumber++;
940
+ setCurrentPage(value) {
941
+ if (value != undefined) {
942
+ this.querySelector("#currentPage").value = "" + value;
858
943
  }
859
-
860
- __toogleSidebar() {
861
- if (this.$.outerContainer.classList.length == 0) {
862
- this.__openSidebar();
863
- } else {
864
- this.__closeSidebar();
865
- }
944
+ this.__pageChange();
945
+ }
946
+
947
+ _getPage() {
948
+ return this.__viewer.currentPageNumber;
949
+ }
950
+
951
+ __previousPage() {
952
+ this.__viewer.currentPageNumber--;
953
+ }
954
+
955
+ __nextPage() {
956
+ this.__viewer.currentPageNumber++;
957
+ }
958
+
959
+ __toogleSidebar() {
960
+ if (this._outerContainer.classList.length == 0) {
961
+ this.__openSidebar();
962
+ } else {
963
+ this.__closeSidebar();
866
964
  }
867
-
868
- __openSidebar() {
869
- if(!this.__thumbnailViewer ||this.__loading){
870
- this.__sidebarOpen = true;
871
- } else {
872
- this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled = true;
873
- this.__updateThumbnailViewer();
874
- this.$.outerContainer.classList.add('sidebarOpen');
875
- }
965
+ }
966
+
967
+ __openSidebar() {
968
+ if (!this.__thumbnailViewer || this.__loading) {
969
+ this.__sidebarOpen = true;
970
+ } else {
971
+ this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled = true;
972
+ this.__updateThumbnailViewer();
973
+ this._outerContainer.classList.add("sidebarOpen");
876
974
  }
877
-
878
- __closeSidebar() {
879
- if(!this.__thumbnailViewer || this.__loading){
880
- this.__sidebarOpen = false;
881
- } else {
882
- this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled = false;
883
- this.$.outerContainer.classList.remove('sidebarOpen');
884
- }
975
+ }
976
+
977
+ __closeSidebar() {
978
+ if (!this.__thumbnailViewer || this.__loading) {
979
+ this.__sidebarOpen = false;
980
+ } else {
981
+ this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled = false;
982
+ this._outerContainer.classList.remove("sidebarOpen");
885
983
  }
886
-
887
- __updateThumbnailViewer() {
888
- const pagesCount = this.__totalPages;
889
- for (let i = 0; i < pagesCount; i++) {
890
- const pageView = this.__viewer.getPageView(i);
891
- if (pageView.renderingState === pdfjsRenderingQueue.RenderingStates.FINISHED) {
892
- const thumbnailView = this.__thumbnailViewer.getThumbnail(i);
893
- thumbnailView.setImage(pageView);
894
- } else {
895
- this.__thumbnailViewer.renderingQueue.renderHighestPriority();
896
- }
897
- }
898
- var component = this;
899
- for (let i = 0; i < this.__thumbnailViewer._thumbnails.length; i++) {
900
- const thumbnailView = this.__thumbnailViewer.getThumbnail(i);
901
- thumbnailView.anchor.onclick = function () {
902
- const id = thumbnailView.id;
903
- thumbnailView.linkService.goToPage(id);
904
- component.dispatchEvent(new CustomEvent('thumbnail-clicked', {
905
- detail: {
906
- source: component,
907
- pageNumber: id
908
- }
909
- }));
910
- return false;
911
- };
912
- }
913
- if(this.__thumbnailViewer && this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled){
914
- this.__thumbnailViewer.scrollThumbnailIntoView(this.currentPage);
915
- }
984
+ }
985
+
986
+ __updateThumbnailViewer() {
987
+ const pagesCount = this.__totalPages;
988
+ for (let i = 0; i < pagesCount; i++) {
989
+ const pageView = this.__viewer.getPageView(i);
990
+ if (
991
+ pageView.renderingState === pdfjsRenderingQueue.RenderingStates.FINISHED
992
+ ) {
993
+ const thumbnailView = this.__thumbnailViewer.getThumbnail(i);
994
+ thumbnailView.setImage(pageView);
995
+ } else {
996
+ this.__thumbnailViewer.renderingQueue.renderHighestPriority();
997
+ }
916
998
  }
917
-
918
- /**
919
- * Rotates document clockwise.
920
- */
921
- rotateCw() {
922
- let delta = 90;
923
- this.__viewer.pagesRotation += delta;
924
- this.__viewer.forceRendering();
999
+ var component = this;
1000
+ for (let i = 0; i < this.__thumbnailViewer._thumbnails.length; i++) {
1001
+ const thumbnailView = this.__thumbnailViewer.getThumbnail(i);
1002
+ thumbnailView.anchor.onclick = function () {
1003
+ const id = thumbnailView.id;
1004
+ thumbnailView.linkService.goToPage(id);
1005
+ component.dispatchEvent(
1006
+ new CustomEvent("thumbnail-clicked", {
1007
+ detail: {
1008
+ source: component,
1009
+ pageNumber: id,
1010
+ },
1011
+ }),
1012
+ );
1013
+ return false;
1014
+ };
925
1015
  }
926
-
927
- /**
928
- * Rotates document counterclockwise.
929
- */
930
- rotateCcw() {
931
- let delta = -90;
932
- this.__viewer.pagesRotation += delta;
933
- this.__viewer.forceRendering();
1016
+ if (
1017
+ this.__thumbnailViewer &&
1018
+ this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled
1019
+ ) {
1020
+ this.__thumbnailViewer.scrollThumbnailIntoView(this.currentPage);
934
1021
  }
1022
+ }
1023
+
1024
+ _onResize() {
1025
+ if (this.offsetWidth < 600) {
1026
+ this.classList.add("small-size");
1027
+ this._toolbar.classList.add("small-size");
1028
+ } else {
1029
+ this.classList.remove("small-size");
1030
+ this._toolbar.classList.remove("small-size");
1031
+ }
1032
+ }
1033
+
1034
+ /**
1035
+ * Rotates document clockwise.
1036
+ */
1037
+ rotateCw() {
1038
+ let delta = 90;
1039
+ this.__viewer.pagesRotation += delta;
1040
+ this.__viewer.forceRendering();
1041
+ }
1042
+
1043
+ /**
1044
+ * Rotates document counterclockwise.
1045
+ */
1046
+ rotateCcw() {
1047
+ let delta = -90;
1048
+ this.__viewer.pagesRotation += delta;
1049
+ this.__viewer.forceRendering();
1050
+ }
935
1051
  }
936
1052
 
937
1053
  customElements.define(PdfViewerElement.is, PdfViewerElement);
@@ -940,6 +1056,3 @@ customElements.define(PdfViewerElement.is, PdfViewerElement);
940
1056
  * @namespace Vaadin
941
1057
  */
942
1058
  window.Vaadin.PdfViewerElement = PdfViewerElement;
943
-
944
-
945
-