@vaadin-component-factory/vcf-pdf-viewer 3.0.2 → 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,886 +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.0.2';
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
- },
371
-
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
- },
244
+ }
403
245
 
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
- }
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;
268
+ }
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;
284
+ }
285
+
286
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button#sidebarToggle::before {
287
+ mask-image: var(--pdf-viewer-toggle-button-icon-closed);
288
+ }
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);
292
+ }
293
+
294
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button#previousPage::before {
295
+ mask-image: var(--pdf-viewer-previous-page-button-icon);
296
+ }
297
+
298
+ ${tag}:not(${lumo}) vaadin-button.toolbar-button#nextPage::before {
299
+ mask-image: var(--pdf-viewer-next-page-button-icon);
300
+ }
301
+
302
+ ${tag}${lumo} vaadin-button[theme~="icon"] {
303
+ padding-left: 6px;
304
+ padding-right: 6px;
305
+ }
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>
556
345
 
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
- ]
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);
570
510
  }
571
511
 
572
- static get observers() {
573
- return [
574
- '__setTitle(__pdfTitle, __filename)'
575
- ];
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);
576
519
  }
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';
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;
591
535
  }
592
- }
593
-
594
- _addToolbarButtons() {
595
- this._createSideBarToggleButton();
596
- this._createZoomSelect();
597
- this._createCurrentPageTextField();
598
- this._createPreviousPageButton();
599
- this._createNextPageButton();
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");
600
647
  }
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();
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";
683
679
  }
684
-
685
- __updateCurrentPageValue(pageNumber){
686
- this.currentPage = "" + pageNumber;
687
- this.dispatchEvent(new CustomEvent('currentPage-changed'));
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");
688
801
  }
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');
697
- }
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
+ });
698
812
  }
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');
708
- }
813
+ if (!src) {
814
+ // No file given, show nothing.
815
+ return;
709
816
  }
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
- });
719
- }
720
- if (!src) {
721
- // No file given, show nothing.
722
- return;
723
- }
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);
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
+ );
744
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();
745
859
  }
746
860
 
747
- __srcChanged(newSrc) {
748
- this.__open(newSrc);
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);
749
867
  }
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();
761
- }
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);
769
- }
770
- return promise;
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
771
878
  }
772
-
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
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;
780
892
  }
781
- this.__filename = filename;
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;
782
912
  }
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;
802
- });
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;
803
930
  }
804
-
805
- __updatePageNumberStates() {
806
- this.querySelector('#previousPage').disabled = (this.currentPage === "1");
807
- this.querySelector('#nextPage').disabled = (this.currentPage === "" + this.__totalPages);
931
+ if (pageNumber < 1) {
932
+ pageNumber = 1;
808
933
  }
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();
934
+ if (pageNumber > this.__totalPages) {
935
+ pageNumber = this.__totalPages;
823
936
  }
937
+ this.__viewer.currentPageNumber = pageNumber;
938
+ }
824
939
 
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;
834
- }
835
- if (pageNumber > this.__totalPages) {
836
- pageNumber = this.__totalPages;
837
- }
838
- this.__viewer.currentPageNumber = pageNumber;
940
+ setCurrentPage(value) {
941
+ if (value != undefined) {
942
+ this.querySelector("#currentPage").value = "" + value;
839
943
  }
840
-
841
- setCurrentPage(value) {
842
- if (value != undefined) {
843
- this.querySelector('#currentPage').value = "" + value;
844
- }
845
- this.__pageChange();
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();
846
964
  }
847
-
848
- _getPage() {
849
- return this.__viewer.currentPageNumber;
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");
850
974
  }
851
-
852
- __previousPage() {
853
- this.__viewer.currentPageNumber--;
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");
854
983
  }
855
-
856
- __nextPage() {
857
- this.__viewer.currentPageNumber++;
858
- }
859
-
860
- __toogleSidebar() {
861
- if (this.$.outerContainer.classList.length == 0) {
862
- this.__openSidebar();
863
- } else {
864
- this.__closeSidebar();
865
- }
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
+ }
866
998
  }
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
- }
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
+ };
876
1015
  }
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
- }
1016
+ if (
1017
+ this.__thumbnailViewer &&
1018
+ this.__thumbnailViewer.renderingQueue.isThumbnailViewEnabled
1019
+ ) {
1020
+ this.__thumbnailViewer.scrollThumbnailIntoView(this.currentPage);
885
1021
  }
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
- }
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");
916
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
+ }
917
1051
  }
918
1052
 
919
1053
  customElements.define(PdfViewerElement.is, PdfViewerElement);
@@ -922,6 +1056,3 @@ customElements.define(PdfViewerElement.is, PdfViewerElement);
922
1056
  * @namespace Vaadin
923
1057
  */
924
1058
  window.Vaadin.PdfViewerElement = PdfViewerElement;
925
-
926
-
927
-