@vaadin-component-factory/vcf-pdf-viewer 3.1.0 → 3.2.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.
@@ -12,16 +12,22 @@
12
12
  * See the License for the specific language governing permissions and
13
13
  * limitations under the License.
14
14
  */
15
- const CSS_UNITS = 96.0 / 72.0;
15
+
16
16
  const DEFAULT_SCALE_VALUE = "auto";
17
17
  const DEFAULT_SCALE = 1.0;
18
+ const DEFAULT_SCALE_DELTA = 1.1;
18
19
  const MIN_SCALE = 0.1;
19
20
  const MAX_SCALE = 10.0;
20
21
  const UNKNOWN_SCALE = 0;
21
22
  const MAX_AUTO_SCALE = 1.25;
22
23
  const SCROLLBAR_PADDING = 40;
23
24
  const VERTICAL_PADDING = 5;
24
- const LOADINGBAR_END_OFFSET_VAR = "--loadingBar-end-offset";
25
+ const RenderingStates = {
26
+ INITIAL: 0,
27
+ RUNNING: 1,
28
+ PAUSED: 2,
29
+ FINISHED: 3
30
+ };
25
31
  const PresentationModeState = {
26
32
  UNKNOWN: 0,
27
33
  NORMAL: 1,
@@ -37,21 +43,18 @@ const SidebarView = {
37
43
  ATTACHMENTS: 3,
38
44
  LAYERS: 4
39
45
  };
40
- const RendererType = {
41
- CANVAS: "canvas",
42
- SVG: "svg"
43
- };
44
46
  const TextLayerMode = {
45
47
  DISABLE: 0,
46
48
  ENABLE: 1,
47
- ENABLE_ENHANCE: 2
49
+ ENABLE_PERMISSIONS: 2
48
50
  };
49
51
  const ScrollMode = {
50
52
  UNKNOWN: -1,
51
53
  VERTICAL: 0,
52
54
  // Default value.
53
55
  HORIZONTAL: 1,
54
- WRAPPED: 2
56
+ WRAPPED: 2,
57
+ PAGE: 3
55
58
  };
56
59
  const SpreadMode = {
57
60
  UNKNOWN: -1,
@@ -59,109 +62,112 @@ const SpreadMode = {
59
62
  // Default value.
60
63
  ODD: 1,
61
64
  EVEN: 2
62
- }; // Used by `PDFViewerApplication`, and by the API unit-tests.
65
+ };
66
+ const CursorTool = {
67
+ SELECT: 0,
68
+ // The default value.
69
+ HAND: 1,
70
+ ZOOM: 2
71
+ };
63
72
 
73
+ // Used by `PDFViewerApplication`, and by the API unit-tests.
64
74
  const AutoPrintRegExp = /\bprint\s*\(/;
75
+
65
76
  /**
66
- * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
67
- * @returns {Object} The object with horizontal (sx) and vertical (sy)
68
- * scales. The scaled property is set to false if scaling is
69
- * not required, true otherwise.
77
+ * Scale factors for the canvas, necessary with HiDPI displays.
70
78
  */
79
+ class OutputScale {
80
+ constructor() {
81
+ const pixelRatio = window.devicePixelRatio || 1;
71
82
 
72
- function getOutputScale(ctx) {
73
- const devicePixelRatio = window.devicePixelRatio || 1;
74
- const backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
75
- const pixelRatio = devicePixelRatio / backingStoreRatio;
76
- return {
77
- sx: pixelRatio,
78
- sy: pixelRatio,
79
- scaled: pixelRatio !== 1
80
- };
83
+ /**
84
+ * @type {number} Horizontal scale.
85
+ */
86
+ this.sx = pixelRatio;
87
+
88
+ /**
89
+ * @type {number} Vertical scale.
90
+ */
91
+ this.sy = pixelRatio;
92
+ }
93
+
94
+ /**
95
+ * @type {boolean} Returns `true` when scaling is required, `false` otherwise.
96
+ */
97
+ get scaled() {
98
+ return this.sx !== 1 || this.sy !== 1;
99
+ }
81
100
  }
101
+
82
102
  /**
83
103
  * Scrolls specified element into view of its parent.
84
- * @param {Object} element - The element to be visible.
85
- * @param {Object} spot - An object with optional top and left properties,
104
+ * @param {HTMLElement} element - The element to be visible.
105
+ * @param {Object} [spot] - An object with optional top and left properties,
86
106
  * specifying the offset from the top left edge.
107
+ * @param {number} [spot.left]
108
+ * @param {number} [spot.top]
87
109
  * @param {boolean} [scrollMatches] - When scrolling search results into view,
88
110
  * ignore elements that either: Contains marked content identifiers,
89
111
  * or have the CSS-rule `overflow: hidden;` set. The default value is `false`.
90
112
  */
91
-
92
-
93
113
  function scrollIntoView(element, spot, scrollMatches = false) {
94
114
  // Assuming offsetParent is available (it's not available when viewer is in
95
115
  // hidden iframe or object). We have to scroll: if the offsetParent is not set
96
116
  // producing the error. See also animationStarted.
97
117
  let parent = element.offsetParent;
98
-
99
118
  if (!parent) {
100
119
  console.error("offsetParent is not set -- cannot scroll");
101
120
  return;
102
121
  }
103
-
104
122
  let offsetY = element.offsetTop + element.clientTop;
105
123
  let offsetX = element.offsetLeft + element.clientLeft;
106
-
107
124
  while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || scrollMatches && (parent.classList.contains("markedContent") || getComputedStyle(parent).overflow === "hidden")) {
108
125
  offsetY += parent.offsetTop;
109
126
  offsetX += parent.offsetLeft;
110
127
  parent = parent.offsetParent;
111
-
112
128
  if (!parent) {
113
129
  return; // no need to scroll
114
130
  }
115
131
  }
116
-
117
132
  if (spot) {
118
133
  if (spot.top !== undefined) {
119
134
  offsetY += spot.top;
120
135
  }
121
-
122
136
  if (spot.left !== undefined) {
123
137
  offsetX += spot.left;
124
138
  parent.scrollLeft = offsetX;
125
139
  }
126
140
  }
127
-
128
141
  parent.scrollTop = offsetY;
129
142
  }
143
+
130
144
  /**
131
145
  * Helper function to start monitoring the scroll event and converting them into
132
146
  * PDF.js friendly one: with scroll debounce and scroll direction.
133
147
  */
134
-
135
-
136
148
  function watchScroll(viewAreaElement, callback) {
137
149
  const debounceScroll = function (evt) {
138
150
  if (rAF) {
139
151
  return;
140
- } // schedule an invocation of scroll for next animation frame.
141
-
142
-
152
+ }
153
+ // schedule an invocation of scroll for next animation frame.
143
154
  rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
144
155
  rAF = null;
145
156
  const currentX = viewAreaElement.scrollLeft;
146
157
  const lastX = state.lastX;
147
-
148
158
  if (currentX !== lastX) {
149
159
  state.right = currentX > lastX;
150
160
  }
151
-
152
161
  state.lastX = currentX;
153
162
  const currentY = viewAreaElement.scrollTop;
154
163
  const lastY = state.lastY;
155
-
156
164
  if (currentY !== lastY) {
157
165
  state.down = currentY > lastY;
158
166
  }
159
-
160
167
  state.lastY = currentY;
161
168
  callback(state);
162
169
  });
163
170
  };
164
-
165
171
  const state = {
166
172
  right: true,
167
173
  down: true,
@@ -173,24 +179,35 @@ function watchScroll(viewAreaElement, callback) {
173
179
  viewAreaElement.addEventListener("scroll", debounceScroll, true);
174
180
  return state;
175
181
  }
182
+
176
183
  /**
177
- * Helper function to parse query string (e.g. ?param1=value&parm2=...).
184
+ * Helper function to parse query string (e.g. ?param1=value&param2=...).
185
+ * @param {string} query
186
+ * @returns {Map}
178
187
  */
179
-
180
-
181
188
  function parseQueryString(query) {
182
- const parts = query.split("&");
183
- const params = Object.create(null);
184
-
185
- for (let i = 0, ii = parts.length; i < ii; ++i) {
186
- const param = parts[i].split("=");
187
- const key = param[0].toLowerCase();
188
- const value = param.length > 1 ? param[1] : null;
189
- params[decodeURIComponent(key)] = decodeURIComponent(value);
189
+ const params = new Map();
190
+ for (const [key, value] of new URLSearchParams(query)) {
191
+ params.set(key.toLowerCase(), value);
190
192
  }
191
-
192
193
  return params;
193
194
  }
195
+ const InvisibleCharsRegExp = /[\x00-\x1F]/g;
196
+
197
+ /**
198
+ * @param {string} str
199
+ * @param {boolean} [replaceInvisible]
200
+ */
201
+ function removeNullCharacters(str, replaceInvisible = false) {
202
+ if (!InvisibleCharsRegExp.test(str)) {
203
+ return str;
204
+ }
205
+ if (replaceInvisible) {
206
+ return str.replaceAll(InvisibleCharsRegExp, m => m === "\x00" ? "" : " ");
207
+ }
208
+ return str.replaceAll("\x00", "");
209
+ }
210
+
194
211
  /**
195
212
  * Use binary search to find the index of the first item in a given array which
196
213
  * passes a given condition. The items are expected to be sorted in the sense
@@ -200,34 +217,27 @@ function parseQueryString(query) {
200
217
  * @returns {number} Index of the first array element to pass the test,
201
218
  * or |items.length| if no such element exists.
202
219
  */
203
-
204
-
205
- function binarySearchFirstItem(items, condition) {
206
- let minIndex = 0;
220
+ function binarySearchFirstItem(items, condition, start = 0) {
221
+ let minIndex = start;
207
222
  let maxIndex = items.length - 1;
208
-
209
223
  if (maxIndex < 0 || !condition(items[maxIndex])) {
210
224
  return items.length;
211
225
  }
212
-
213
226
  if (condition(items[minIndex])) {
214
227
  return minIndex;
215
228
  }
216
-
217
229
  while (minIndex < maxIndex) {
218
230
  const currentIndex = minIndex + maxIndex >> 1;
219
231
  const currentItem = items[currentIndex];
220
-
221
232
  if (condition(currentItem)) {
222
233
  maxIndex = currentIndex;
223
234
  } else {
224
235
  minIndex = currentIndex + 1;
225
236
  }
226
237
  }
227
-
228
- return minIndex;
229
- /* === maxIndex */
238
+ return minIndex; /* === maxIndex */
230
239
  }
240
+
231
241
  /**
232
242
  * Approximates float number as a fraction using Farey sequence (max order
233
243
  * of 8).
@@ -235,39 +245,32 @@ function binarySearchFirstItem(items, condition) {
235
245
  * @returns {Array} Estimated fraction: the first array item is a numerator,
236
246
  * the second one is a denominator.
237
247
  */
238
-
239
-
240
248
  function approximateFraction(x) {
241
249
  // Fast paths for int numbers or their inversions.
242
250
  if (Math.floor(x) === x) {
243
251
  return [x, 1];
244
252
  }
245
-
246
253
  const xinv = 1 / x;
247
254
  const limit = 8;
248
-
249
255
  if (xinv > limit) {
250
256
  return [1, limit];
251
257
  } else if (Math.floor(xinv) === xinv) {
252
258
  return [1, xinv];
253
259
  }
254
-
255
- const x_ = x > 1 ? xinv : x; // a/b and c/d are neighbours in Farey sequence.
256
-
260
+ const x_ = x > 1 ? xinv : x;
261
+ // a/b and c/d are neighbours in Farey sequence.
257
262
  let a = 0,
258
- b = 1,
259
- c = 1,
260
- d = 1; // Limiting search to order 8.
261
-
263
+ b = 1,
264
+ c = 1,
265
+ d = 1;
266
+ // Limiting search to order 8.
262
267
  while (true) {
263
268
  // Generating next term in sequence (order of q).
264
269
  const p = a + c,
265
- q = b + d;
266
-
270
+ q = b + d;
267
271
  if (q > limit) {
268
272
  break;
269
273
  }
270
-
271
274
  if (x_ <= p / q) {
272
275
  c = p;
273
276
  d = q;
@@ -276,38 +279,45 @@ function approximateFraction(x) {
276
279
  b = q;
277
280
  }
278
281
  }
279
-
280
- let result; // Select closest of the neighbours to x.
281
-
282
+ let result;
283
+ // Select closest of the neighbours to x.
282
284
  if (x_ - a / b < c / d - x_) {
283
285
  result = x_ === x ? [a, b] : [b, a];
284
286
  } else {
285
287
  result = x_ === x ? [c, d] : [d, c];
286
288
  }
287
-
288
289
  return result;
289
290
  }
290
-
291
291
  function roundToDivide(x, div) {
292
292
  const r = x % div;
293
293
  return r === 0 ? x : Math.round(x - r + div);
294
294
  }
295
+
295
296
  /**
296
- * Gets the size of the specified page, converted from PDF units to inches.
297
- * @param {Object} An Object containing the properties: {Array} `view`,
298
- * {number} `userUnit`, and {number} `rotate`.
299
- * @returns {Object} An Object containing the properties: {number} `width`
300
- * and {number} `height`, given in inches.
297
+ * @typedef {Object} GetPageSizeInchesParameters
298
+ * @property {number[]} view
299
+ * @property {number} userUnit
300
+ * @property {number} rotate
301
301
  */
302
302
 
303
+ /**
304
+ * @typedef {Object} PageSize
305
+ * @property {number} width - In inches.
306
+ * @property {number} height - In inches.
307
+ */
303
308
 
309
+ /**
310
+ * Gets the size of the specified page, converted from PDF units to inches.
311
+ * @param {GetPageSizeInchesParameters} params
312
+ * @returns {PageSize}
313
+ */
304
314
  function getPageSizeInches({
305
315
  view,
306
316
  userUnit,
307
317
  rotate
308
318
  }) {
309
- const [x1, y1, x2, y2] = view; // We need to take the page rotation into account as well.
310
-
319
+ const [x1, y1, x2, y2] = view;
320
+ // We need to take the page rotation into account as well.
311
321
  const changeOrientation = rotate % 180 !== 0;
312
322
  const width = (x2 - x1) / 72 * userUnit;
313
323
  const height = (y2 - y1) / 72 * userUnit;
@@ -316,6 +326,7 @@ function getPageSizeInches({
316
326
  height: changeOrientation ? width : height
317
327
  };
318
328
  }
329
+
319
330
  /**
320
331
  * Helper function for getVisibleElements.
321
332
  *
@@ -327,8 +338,6 @@ function getPageSizeInches({
327
338
  * this will be the first element in the first partially visible row in
328
339
  * `views`, although sometimes it goes back one row further.)
329
340
  */
330
-
331
-
332
341
  function backtrackBeforeAllVisibleElements(index, views, top) {
333
342
  // binarySearchFirstItem's assumption is that the input is ordered, with only
334
343
  // one index where the conditions flips from false to true: [false ...,
@@ -339,11 +348,14 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
339
348
  // So there is no guarantee that the binary search yields the index of the
340
349
  // first visible element. It could have been any of the other visible elements
341
350
  // that were preceded by a hidden element.
351
+
342
352
  // Of course, if either this element or the previous (hidden) element is also
343
353
  // the first element, there's nothing to worry about.
344
354
  if (index < 2) {
345
355
  return index;
346
- } // That aside, the possible cases are represented below.
356
+ }
357
+
358
+ // That aside, the possible cases are represented below.
347
359
  //
348
360
  // **** = fully hidden
349
361
  // A*B* = mix of partially visible and/or hidden pages
@@ -361,15 +373,13 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
361
373
  // ABCD = mix of fully and/or partially visible pages
362
374
  //
363
375
  // (4) Binary search could only have returned A.
376
+
364
377
  // Initially assume that we need to find the beginning of the current row
365
378
  // (case 1, 2, or 4), which means finding a page that is above the current
366
379
  // page's top. If the found page is partially visible, we're definitely not in
367
380
  // case 3, and this assumption is correct.
368
-
369
-
370
381
  let elt = views[index].div;
371
382
  let pageTop = elt.offsetTop + elt.clientTop;
372
-
373
383
  if (pageTop >= top) {
374
384
  // The found page is fully visible, so we're actually either in case 3 or 4,
375
385
  // and unfortunately we can't tell the difference between them without
@@ -378,7 +388,9 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
378
388
  // in the previous row, so use its top instead.
379
389
  elt = views[index - 1].div;
380
390
  pageTop = elt.offsetTop + elt.clientTop;
381
- } // Now we backtrack to the first page that still has its bottom below
391
+ }
392
+
393
+ // Now we backtrack to the first page that still has its bottom below
382
394
  // `pageTop`, which is the top of a page in the first visible row (unless
383
395
  // we're in case 4, in which case it's the row before that).
384
396
  // `index` is found by binary search, so the page at `index - 1` is
@@ -386,23 +398,19 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
386
398
  // `index - 2`. (However, if this loop terminates on its first iteration,
387
399
  // which is the case when pages are stacked vertically, `index` should remain
388
400
  // unchanged, so we use a distinct loop variable.)
389
-
390
-
391
401
  for (let i = index - 2; i >= 0; --i) {
392
402
  elt = views[i].div;
393
-
394
403
  if (elt.offsetTop + elt.clientTop + elt.clientHeight <= pageTop) {
395
404
  // We have reached the previous row, so stop now.
396
405
  // This loop is expected to terminate relatively quickly because the
397
406
  // number of pages per row is expected to be small.
398
407
  break;
399
408
  }
400
-
401
409
  index = i;
402
410
  }
403
-
404
411
  return index;
405
412
  }
413
+
406
414
  /**
407
415
  * @typedef {Object} GetVisibleElementsParameters
408
416
  * @property {HTMLElement} scrollEl - A container that can possibly scroll.
@@ -435,11 +443,9 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
435
443
  * rendering canvas. Earlier and later refer to index in `views`, not page
436
444
  * layout.)
437
445
  *
438
- * @param {GetVisibleElementsParameters}
446
+ * @param {GetVisibleElementsParameters} params
439
447
  * @returns {Object} `{ first, last, views: [{ id, x, y, view, percent }] }`
440
448
  */
441
-
442
-
443
449
  function getVisibleElements({
444
450
  scrollEl,
445
451
  views,
@@ -448,9 +454,11 @@ function getVisibleElements({
448
454
  rtl = false
449
455
  }) {
450
456
  const top = scrollEl.scrollTop,
451
- bottom = top + scrollEl.clientHeight;
457
+ bottom = top + scrollEl.clientHeight;
452
458
  const left = scrollEl.scrollLeft,
453
- right = left + scrollEl.clientWidth; // Throughout this "generic" function, comments will assume we're working with
459
+ right = left + scrollEl.clientWidth;
460
+
461
+ // Throughout this "generic" function, comments will assume we're working with
454
462
  // PDF document pages, which is the most important and complex case. In this
455
463
  // case, the visible elements we're actually interested is the page canvas,
456
464
  // which is contained in a wrapper which adds no padding/border/margin, which
@@ -460,25 +468,24 @@ function getVisibleElements({
460
468
  // offsetLeft/Top (which includes margin) and adding clientLeft/Top (which is
461
469
  // the border). Adding clientWidth/Height gets us the bottom-right corner of
462
470
  // the padding edge.
463
-
464
471
  function isElementBottomAfterViewTop(view) {
465
472
  const element = view.div;
466
473
  const elementBottom = element.offsetTop + element.clientTop + element.clientHeight;
467
474
  return elementBottom > top;
468
475
  }
469
-
470
476
  function isElementNextAfterViewHorizontally(view) {
471
477
  const element = view.div;
472
478
  const elementLeft = element.offsetLeft + element.clientLeft;
473
479
  const elementRight = elementLeft + element.clientWidth;
474
480
  return rtl ? elementLeft < right : elementRight > left;
475
481
  }
476
-
477
482
  const visible = [],
478
- numViews = views.length;
479
- let firstVisibleElementInd = binarySearchFirstItem(views, horizontal ? isElementNextAfterViewHorizontally : isElementBottomAfterViewTop); // Please note the return value of the `binarySearchFirstItem` function when
480
- // no valid element is found (hence the `firstVisibleElementInd` check below).
483
+ ids = new Set(),
484
+ numViews = views.length;
485
+ let firstVisibleElementInd = binarySearchFirstItem(views, horizontal ? isElementNextAfterViewHorizontally : isElementBottomAfterViewTop);
481
486
 
487
+ // Please note the return value of the `binarySearchFirstItem` function when
488
+ // no valid element is found (hence the `firstVisibleElementInd` check below).
482
489
  if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews && !horizontal) {
483
490
  // In wrapped scrolling (or vertical scrolling with spreads), with some page
484
491
  // sizes, isElementBottomAfterViewTop doesn't satisfy the binary search
@@ -486,7 +493,9 @@ function getVisibleElements({
486
493
  // pages with bottoms below. This function detects and corrects that error;
487
494
  // see it for more comments.
488
495
  firstVisibleElementInd = backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top);
489
- } // lastEdge acts as a cutoff for us to stop looping, because we know all
496
+ }
497
+
498
+ // lastEdge acts as a cutoff for us to stop looping, because we know all
490
499
  // subsequent pages will be hidden.
491
500
  //
492
501
  // When using wrapped scrolling or vertical scrolling with spreads, we can't
@@ -494,20 +503,16 @@ function getVisibleElements({
494
503
  // the tops of subsequent pages on the same row could still be visible. In
495
504
  // horizontal scrolling, we don't have that issue, so we can stop as soon as
496
505
  // we pass `right`, without needing the code below that handles the -1 case.
497
-
498
-
499
506
  let lastEdge = horizontal ? right : -1;
500
-
501
507
  for (let i = firstVisibleElementInd; i < numViews; i++) {
502
508
  const view = views[i],
503
- element = view.div;
509
+ element = view.div;
504
510
  const currentWidth = element.offsetLeft + element.clientLeft;
505
511
  const currentHeight = element.offsetTop + element.clientTop;
506
512
  const viewWidth = element.clientWidth,
507
- viewHeight = element.clientHeight;
513
+ viewHeight = element.clientHeight;
508
514
  const viewRight = currentWidth + viewWidth;
509
515
  const viewBottom = currentHeight + viewHeight;
510
-
511
516
  if (lastEdge === -1) {
512
517
  // As commented above, this is only needed in non-horizontal cases.
513
518
  // Setting lastEdge to the bottom of the first page that is partially
@@ -519,15 +524,13 @@ function getVisibleElements({
519
524
  } else if ((horizontal ? currentWidth : currentHeight) > lastEdge) {
520
525
  break;
521
526
  }
522
-
523
527
  if (viewBottom <= top || currentHeight >= bottom || viewRight <= left || currentWidth >= right) {
524
528
  continue;
525
529
  }
526
-
527
530
  const hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
528
531
  const hiddenWidth = Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
529
532
  const fractionHeight = (viewHeight - hiddenHeight) / viewHeight,
530
- fractionWidth = (viewWidth - hiddenWidth) / viewWidth;
533
+ fractionWidth = (viewWidth - hiddenWidth) / viewWidth;
531
534
  const percent = fractionHeight * fractionWidth * 100 | 0;
532
535
  visible.push({
533
536
  id: view.id,
@@ -537,145 +540,65 @@ function getVisibleElements({
537
540
  percent,
538
541
  widthPercent: fractionWidth * 100 | 0
539
542
  });
543
+ ids.add(view.id);
540
544
  }
541
-
542
545
  const first = visible[0],
543
- last = visible[visible.length - 1];
544
-
546
+ last = visible.at(-1);
545
547
  if (sortByVisibility) {
546
548
  visible.sort(function (a, b) {
547
549
  const pc = a.percent - b.percent;
548
-
549
550
  if (Math.abs(pc) > 0.001) {
550
551
  return -pc;
551
552
  }
552
-
553
553
  return a.id - b.id; // ensure stability
554
554
  });
555
555
  }
556
-
557
556
  return {
558
557
  first,
559
558
  last,
560
- views: visible
559
+ views: visible,
560
+ ids
561
561
  };
562
562
  }
563
- /**
564
- * Event handler to suppress context menu.
565
- */
566
-
567
-
568
- function noContextMenuHandler(evt) {
569
- evt.preventDefault();
570
- }
571
-
572
563
  function normalizeWheelEventDirection(evt) {
573
564
  let delta = Math.hypot(evt.deltaX, evt.deltaY);
574
565
  const angle = Math.atan2(evt.deltaY, evt.deltaX);
575
-
576
566
  if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
577
567
  // All that is left-up oriented has to change the sign.
578
568
  delta = -delta;
579
569
  }
580
-
581
570
  return delta;
582
571
  }
583
-
584
572
  function normalizeWheelEventDelta(evt) {
573
+ const deltaMode = evt.deltaMode; // Avoid being affected by bug 1392460.
585
574
  let delta = normalizeWheelEventDirection(evt);
586
- const MOUSE_DOM_DELTA_PIXEL_MODE = 0;
587
- const MOUSE_DOM_DELTA_LINE_MODE = 1;
588
575
  const MOUSE_PIXELS_PER_LINE = 30;
589
- const MOUSE_LINES_PER_PAGE = 30; // Converts delta to per-page units
576
+ const MOUSE_LINES_PER_PAGE = 30;
590
577
 
591
- if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) {
578
+ // Converts delta to per-page units
579
+ if (deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
592
580
  delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE;
593
- } else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) {
581
+ } else if (deltaMode === WheelEvent.DOM_DELTA_LINE) {
594
582
  delta /= MOUSE_LINES_PER_PAGE;
595
583
  }
596
-
597
584
  return delta;
598
585
  }
599
-
600
586
  function isValidRotation(angle) {
601
587
  return Number.isInteger(angle) && angle % 90 === 0;
602
588
  }
603
-
604
589
  function isValidScrollMode(mode) {
605
590
  return Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) && mode !== ScrollMode.UNKNOWN;
606
591
  }
607
-
608
592
  function isValidSpreadMode(mode) {
609
593
  return Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) && mode !== SpreadMode.UNKNOWN;
610
594
  }
611
-
612
595
  function isPortraitOrientation(size) {
613
596
  return size.width <= size.height;
614
597
  }
615
598
 
616
- const WaitOnType = {
617
- EVENT: "event",
618
- TIMEOUT: "timeout"
619
- };
620
- /**
621
- * @typedef {Object} WaitOnEventOrTimeoutParameters
622
- * @property {Object} target - The event target, can for example be:
623
- * `window`, `document`, a DOM element, or an {EventBus} instance.
624
- * @property {string} name - The name of the event.
625
- * @property {number} delay - The delay, in milliseconds, after which the
626
- * timeout occurs (if the event wasn't already dispatched).
627
- */
628
-
629
- /**
630
- * Allows waiting for an event or a timeout, whichever occurs first.
631
- * Can be used to ensure that an action always occurs, even when an event
632
- * arrives late or not at all.
633
- *
634
- * @param {WaitOnEventOrTimeoutParameters}
635
- * @returns {Promise} A promise that is resolved with a {WaitOnType} value.
636
- */
637
-
638
- function waitOnEventOrTimeout({
639
- target,
640
- name,
641
- delay = 0
642
- }) {
643
- return new Promise(function (resolve, reject) {
644
- if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
645
- throw new Error("waitOnEventOrTimeout - invalid parameters.");
646
- }
647
-
648
- function handler(type) {
649
- if (target instanceof EventBus) {
650
- target._off(name, eventHandler);
651
- } else {
652
- target.removeEventListener(name, eventHandler);
653
- }
654
-
655
- if (timeout) {
656
- clearTimeout(timeout);
657
- }
658
-
659
- resolve(type);
660
- }
661
-
662
- const eventHandler = handler.bind(null, WaitOnType.EVENT);
663
-
664
- if (target instanceof EventBus) {
665
- target._on(name, eventHandler);
666
- } else {
667
- target.addEventListener(name, eventHandler);
668
- }
669
-
670
- const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
671
- const timeout = setTimeout(timeoutHandler, delay);
672
- });
673
- }
674
599
  /**
675
600
  * Promise that is resolved when DOM window becomes visible.
676
601
  */
677
-
678
-
679
602
  const animationStarted = new Promise(function (resolve) {
680
603
  if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB") && typeof window === "undefined") {
681
604
  // Prevent "ReferenceError: window is not defined" errors when running the
@@ -683,272 +606,73 @@ const animationStarted = new Promise(function (resolve) {
683
606
  setTimeout(resolve, 20);
684
607
  return;
685
608
  }
686
-
687
609
  window.requestAnimationFrame(resolve);
688
610
  });
689
- /**
690
- * NOTE: Only used to support various PDF viewer tests in `mozilla-central`.
691
- */
692
-
693
- function dispatchDOMEvent(eventName, args = null) {
694
- if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("MOZCENTRAL")) {
695
- throw new Error("Not implemented: dispatchDOMEvent");
696
- }
697
-
698
- const details = Object.create(null);
699
-
700
- if ((args === null || args === void 0 ? void 0 : args.length) > 0) {
701
- const obj = args[0];
702
-
703
- for (const key in obj) {
704
- const value = obj[key];
705
-
706
- if (key === "source") {
707
- if (value === window || value === document) {
708
- return; // No need to re-dispatch (already) global events.
709
- }
710
-
711
- continue; // Ignore the `source` property.
712
- }
713
-
714
- details[key] = value;
715
- }
716
- }
717
-
718
- const event = document.createEvent("CustomEvent");
719
- event.initCustomEvent(eventName, true, true, details);
720
- document.dispatchEvent(event);
721
- }
722
- /**
723
- * Simple event bus for an application. Listeners are attached using the `on`
724
- * and `off` methods. To raise an event, the `dispatch` method shall be used.
725
- */
726
-
727
-
728
- class EventBus {
729
- constructor(options) {
730
- this._listeners = Object.create(null);
731
-
732
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) {
733
- this._isInAutomation = (options === null || options === void 0 ? void 0 : options.isInAutomation) === true;
734
- }
735
- }
736
- /**
737
- * @param {string} eventName
738
- * @param {function} listener
739
- * @param {Object} [options]
740
- */
741
-
742
-
743
- on(eventName, listener, options = null) {
744
- this._on(eventName, listener, {
745
- external: true,
746
- once: options === null || options === void 0 ? void 0 : options.once
747
- });
748
- }
749
- /**
750
- * @param {string} eventName
751
- * @param {function} listener
752
- * @param {Object} [options]
753
- */
754
-
755
-
756
- off(eventName, listener, options = null) {
757
- this._off(eventName, listener, {
758
- external: true,
759
- once: options === null || options === void 0 ? void 0 : options.once
760
- });
761
- }
762
-
763
- dispatch(eventName) {
764
- const eventListeners = this._listeners[eventName];
765
-
766
- if (!eventListeners || eventListeners.length === 0) {
767
- if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) && this._isInAutomation) {
768
- const args = Array.prototype.slice.call(arguments, 1);
769
- dispatchDOMEvent(eventName, args);
770
- }
771
-
772
- return;
773
- } // Passing all arguments after the eventName to the listeners.
774
-
775
-
776
- const args = Array.prototype.slice.call(arguments, 1);
777
- let externalListeners; // Making copy of the listeners array in case if it will be modified
778
- // during dispatch.
779
-
780
- for (const {
781
- listener,
782
- external,
783
- once
784
- } of eventListeners.slice(0)) {
785
- if (once) {
786
- this._off(eventName, listener);
787
- }
788
-
789
- if (external) {
790
- (externalListeners || (externalListeners = [])).push(listener);
791
- continue;
792
- }
793
-
794
- listener.apply(null, args);
795
- } // Dispatch any "external" listeners *after* the internal ones, to give the
796
- // viewer components time to handle events and update their state first.
797
-
798
-
799
- if (externalListeners) {
800
- for (const listener of externalListeners) {
801
- listener.apply(null, args);
802
- }
803
-
804
- externalListeners = null;
805
- }
806
-
807
- if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) && this._isInAutomation) {
808
- dispatchDOMEvent(eventName, args);
809
- }
810
- }
811
- /**
812
- * @ignore
813
- */
814
-
815
-
816
- _on(eventName, listener, options = null) {
817
- var _this$_listeners;
818
-
819
- const eventListeners = (_this$_listeners = this._listeners)[eventName] || (_this$_listeners[eventName] = []);
820
- eventListeners.push({
821
- listener,
822
- external: (options === null || options === void 0 ? void 0 : options.external) === true,
823
- once: (options === null || options === void 0 ? void 0 : options.once) === true
824
- });
825
- }
826
- /**
827
- * @ignore
828
- */
829
-
830
-
831
- _off(eventName, listener, options = null) {
832
- const eventListeners = this._listeners[eventName];
833
-
834
- if (!eventListeners) {
835
- return;
836
- }
837
-
838
- for (let i = 0, ii = eventListeners.length; i < ii; i++) {
839
- if (eventListeners[i].listener === listener) {
840
- eventListeners.splice(i, 1);
841
- return;
842
- }
843
- }
844
- }
845
-
846
- }
847
-
611
+ const docStyle = typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB") && typeof document === "undefined" ? null : document.documentElement.style;
848
612
  function clamp(v, min, max) {
849
613
  return Math.min(Math.max(v, min), max);
850
614
  }
851
-
852
615
  class ProgressBar {
853
- constructor(id, {
854
- height,
855
- width,
856
- units
857
- } = {}) {
858
- this.visible = true; // Fetch the sub-elements for later.
859
-
860
- this.div = document.querySelector(id + " .progress"); // Get the loading bar element, so it can be resized to fit the viewer.
861
-
862
- this.bar = this.div.parentNode; // Get options, with sensible defaults.
863
-
864
- this.height = height || 100;
865
- this.width = width || 100;
866
- this.units = units || "%"; // Initialize heights.
867
-
868
- this.div.style.height = this.height + this.units;
869
- this.percent = 0;
616
+ #classList = null;
617
+ #disableAutoFetchTimeout = null;
618
+ #percent = 0;
619
+ #style = null;
620
+ #visible = true;
621
+ constructor(bar) {
622
+ this.#classList = bar.classList;
623
+ this.#style = bar.style;
870
624
  }
871
-
872
- _updateBar() {
873
- if (this._indeterminate) {
874
- this.div.classList.add("indeterminate");
875
- this.div.style.width = this.width + this.units;
876
- return;
877
- }
878
-
879
- this.div.classList.remove("indeterminate");
880
- const progressSize = this.width * this._percent / 100;
881
- this.div.style.width = progressSize + this.units;
882
- }
883
-
884
625
  get percent() {
885
- return this._percent;
626
+ return this.#percent;
886
627
  }
887
-
888
628
  set percent(val) {
889
- this._indeterminate = isNaN(val);
890
- this._percent = clamp(val, 0, 100);
891
-
892
- this._updateBar();
629
+ this.#percent = clamp(val, 0, 100);
630
+ if (isNaN(val)) {
631
+ this.#classList.add("indeterminate");
632
+ return;
633
+ }
634
+ this.#classList.remove("indeterminate");
635
+ this.#style.setProperty("--progressBar-percent", `${this.#percent}%`);
893
636
  }
894
-
895
637
  setWidth(viewer) {
896
638
  if (!viewer) {
897
639
  return;
898
640
  }
899
-
900
641
  const container = viewer.parentNode;
901
642
  const scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
902
-
903
643
  if (scrollbarWidth > 0) {
904
- const doc = document.documentElement;
905
- doc.style.setProperty(LOADINGBAR_END_OFFSET_VAR, `${scrollbarWidth}px`);
644
+ this.#style.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`);
906
645
  }
907
646
  }
908
-
647
+ setDisableAutoFetch(delay = /* ms = */5000) {
648
+ if (isNaN(this.#percent)) {
649
+ return;
650
+ }
651
+ if (this.#disableAutoFetchTimeout) {
652
+ clearTimeout(this.#disableAutoFetchTimeout);
653
+ }
654
+ this.show();
655
+ this.#disableAutoFetchTimeout = setTimeout(() => {
656
+ this.#disableAutoFetchTimeout = null;
657
+ this.hide();
658
+ }, delay);
659
+ }
909
660
  hide() {
910
- if (!this.visible) {
661
+ if (!this.#visible) {
911
662
  return;
912
663
  }
913
-
914
- this.visible = false;
915
- this.bar.classList.add("hidden");
664
+ this.#visible = false;
665
+ this.#classList.add("hidden");
916
666
  }
917
-
918
667
  show() {
919
- if (this.visible) {
668
+ if (this.#visible) {
920
669
  return;
921
670
  }
922
-
923
- this.visible = true;
924
- this.bar.classList.remove("hidden");
671
+ this.#visible = true;
672
+ this.#classList.remove("hidden");
925
673
  }
926
-
927
674
  }
928
- /**
929
- * Moves all elements of an array that satisfy condition to the end of the
930
- * array, preserving the order of the rest.
931
- */
932
-
933
-
934
- function moveToEndOfArray(arr, condition) {
935
- const moved = [],
936
- len = arr.length;
937
- let write = 0;
938
675
 
939
- for (let read = 0; read < len; ++read) {
940
- if (condition(arr[read])) {
941
- moved.push(arr[read]);
942
- } else {
943
- arr[write] = arr[read];
944
- ++write;
945
- }
946
- }
947
-
948
- for (let read = 0; write < len; ++read, ++write) {
949
- arr[write] = moved[read];
950
- }
951
- }
952
676
  /**
953
677
  * Get the active or focused element in current DOM.
954
678
  *
@@ -957,48 +681,50 @@ function moveToEndOfArray(arr, condition) {
957
681
  *
958
682
  * @returns {Element} the truly active or focused element.
959
683
  */
960
-
961
-
962
684
  function getActiveOrFocusedElement() {
963
685
  let curRoot = document;
964
686
  let curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
965
-
966
687
  while ((_curActiveOrFocused = curActiveOrFocused) !== null && _curActiveOrFocused !== void 0 && _curActiveOrFocused.shadowRoot) {
967
688
  var _curActiveOrFocused;
968
-
969
689
  curRoot = curActiveOrFocused.shadowRoot;
970
690
  curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
971
691
  }
972
-
973
692
  return curActiveOrFocused;
974
693
  }
694
+
975
695
  /**
976
696
  * Converts API PageLayout values to the format used by `BaseViewer`.
977
- * NOTE: This is supported to the extent that the viewer implements the
978
- * necessary Scroll/Spread modes (since SinglePage, TwoPageLeft,
979
- * and TwoPageRight all suggests using non-continuous scrolling).
980
- * @param {string} mode - The API PageLayout value.
981
- * @returns {number} A value from {SpreadMode}.
697
+ * @param {string} layout - The API PageLayout value.
698
+ * @returns {Object}
982
699
  */
983
-
984
-
985
- function apiPageLayoutToSpreadMode(layout) {
700
+ function apiPageLayoutToViewerModes(layout) {
701
+ let scrollMode = ScrollMode.VERTICAL,
702
+ spreadMode = SpreadMode.NONE;
986
703
  switch (layout) {
987
704
  case "SinglePage":
705
+ scrollMode = ScrollMode.PAGE;
706
+ break;
988
707
  case "OneColumn":
989
- return SpreadMode.NONE;
990
-
991
- case "TwoColumnLeft":
708
+ break;
992
709
  case "TwoPageLeft":
993
- return SpreadMode.ODD;
994
-
995
- case "TwoColumnRight":
710
+ scrollMode = ScrollMode.PAGE;
711
+ /* falls through */
712
+ case "TwoColumnLeft":
713
+ spreadMode = SpreadMode.ODD;
714
+ break;
996
715
  case "TwoPageRight":
997
- return SpreadMode.EVEN;
716
+ scrollMode = ScrollMode.PAGE;
717
+ /* falls through */
718
+ case "TwoColumnRight":
719
+ spreadMode = SpreadMode.EVEN;
720
+ break;
998
721
  }
999
-
1000
- return SpreadMode.NONE; // Default value.
722
+ return {
723
+ scrollMode,
724
+ spreadMode
725
+ };
1001
726
  }
727
+
1002
728
  /**
1003
729
  * Converts API PageMode values to the format used by `PDFSidebar`.
1004
730
  * NOTE: There's also a "FullScreen" parameter which is not possible to support,
@@ -1007,27 +733,30 @@ function apiPageLayoutToSpreadMode(layout) {
1007
733
  * @param {string} mode - The API PageMode value.
1008
734
  * @returns {number} A value from {SidebarView}.
1009
735
  */
1010
-
1011
-
1012
736
  function apiPageModeToSidebarView(mode) {
1013
737
  switch (mode) {
1014
738
  case "UseNone":
1015
739
  return SidebarView.NONE;
1016
-
1017
740
  case "UseThumbs":
1018
741
  return SidebarView.THUMBS;
1019
-
1020
742
  case "UseOutlines":
1021
743
  return SidebarView.OUTLINE;
1022
-
1023
744
  case "UseAttachments":
1024
745
  return SidebarView.ATTACHMENTS;
1025
-
1026
746
  case "UseOC":
1027
747
  return SidebarView.LAYERS;
1028
748
  }
1029
-
1030
749
  return SidebarView.NONE; // Default value.
1031
750
  }
751
+ function toggleCheckedBtn(button, toggle, view = null) {
752
+ button.classList.toggle("toggled", toggle);
753
+ button.setAttribute("aria-checked", toggle);
754
+ view === null || view === void 0 ? void 0 : view.classList.toggle("hidden", !toggle);
755
+ }
756
+ function toggleExpandedBtn(button, toggle, view = null) {
757
+ button.classList.toggle("toggled", toggle);
758
+ button.setAttribute("aria-expanded", toggle);
759
+ view === null || view === void 0 ? void 0 : view.classList.toggle("hidden", !toggle);
760
+ }
1032
761
 
1033
- export { AutoPrintRegExp, CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, EventBus, MAX_AUTO_SCALE, MAX_SCALE, MIN_SCALE, PresentationModeState, ProgressBar, RendererType, SCROLLBAR_PADDING, ScrollMode, SidebarView, SpreadMode, TextLayerMode, UNKNOWN_SCALE, VERTICAL_PADDING, WaitOnType, animationStarted, apiPageLayoutToSpreadMode, apiPageModeToSidebarView, approximateFraction, backtrackBeforeAllVisibleElements, binarySearchFirstItem, getActiveOrFocusedElement, getOutputScale, getPageSizeInches, getVisibleElements, isPortraitOrientation, isValidRotation, isValidScrollMode, isValidSpreadMode, moveToEndOfArray, noContextMenuHandler, normalizeWheelEventDelta, normalizeWheelEventDirection, parseQueryString, roundToDivide, scrollIntoView, waitOnEventOrTimeout, watchScroll };
762
+ export { AutoPrintRegExp, CursorTool, DEFAULT_SCALE, DEFAULT_SCALE_DELTA, DEFAULT_SCALE_VALUE, MAX_AUTO_SCALE, MAX_SCALE, MIN_SCALE, OutputScale, PresentationModeState, ProgressBar, RenderingStates, SCROLLBAR_PADDING, ScrollMode, SidebarView, SpreadMode, TextLayerMode, UNKNOWN_SCALE, VERTICAL_PADDING, animationStarted, apiPageLayoutToViewerModes, apiPageModeToSidebarView, approximateFraction, backtrackBeforeAllVisibleElements, binarySearchFirstItem, docStyle, getActiveOrFocusedElement, getPageSizeInches, getVisibleElements, isPortraitOrientation, isValidRotation, isValidScrollMode, isValidSpreadMode, normalizeWheelEventDelta, normalizeWheelEventDirection, parseQueryString, removeNullCharacters, roundToDivide, scrollIntoView, toggleCheckedBtn, toggleExpandedBtn, watchScroll };