wickes-css2 2.113.0 → 2.117.0-develop.1

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.
Files changed (233) hide show
  1. package/Readme.md +1 -1
  2. package/build/css/category-main.css +1 -1
  3. package/build/css/components/checkout-payment-details-v2.css +1 -1
  4. package/build/css/components/checkout-payment-repayment-terms.css +1 -1
  5. package/build/css/components/price-block-v2-critical.css +1 -1
  6. package/build/css/components/price-block-v2.css +1 -1
  7. package/build/css/kitchen-plp-main.css +1 -1
  8. package/build/css/main.css +1 -1
  9. package/build/css/my-account-main-v2.css +1 -1
  10. package/build/css/my-account-main.css +1 -1
  11. package/build/css/pages/checkout-new.css +1 -1
  12. package/build/css/pages/checkout.css +1 -1
  13. package/build/css/pages/page_checkout_delivery-new.css +1 -1
  14. package/build/css/pages/page_product-details-v2-critical.css +1 -1
  15. package/build/css/pages/page_product-details-v2.css +1 -1
  16. package/build/css/pages/page_product-details.css +1 -1
  17. package/build/css/pages/page_products-list-combined.css +1 -1
  18. package/build/css/pages/page_products-list_v2.css +1 -1
  19. package/build/css/pages/tradePro-signup.css +1 -1
  20. package/build/css/pdp-main-before-combine.css +1 -1
  21. package/build/css/pdp-main-critical.css +1 -1
  22. package/build/css/pdp-main-non-critical.css +1 -1
  23. package/build/css/pdp-main.css +1 -1
  24. package/build/css/plp-main.css +1 -1
  25. package/build/css/store-locator-main.css +1 -1
  26. package/build/img/file-pdf-solid-full.svg +3 -0
  27. package/build/img/lorem_ipsum.pdf +150 -0
  28. package/build/js/account-members.min.js +1 -1
  29. package/build/js/add-project-list-id.min.js +1 -1
  30. package/build/js/address-book.min.js +1 -1
  31. package/build/js/basket.min.js +2 -2
  32. package/build/js/bloomreach-widget.min.js +1 -1
  33. package/build/js/bundle.min.js +1 -1
  34. package/build/js/change-password.min.js +1 -1
  35. package/build/js/checkout.min.js +2 -2
  36. package/build/js/emulation.min.js +400 -192
  37. package/build/js/general.bundle.min.js +1 -1
  38. package/build/js/gift-cards.min.js +1 -1
  39. package/build/js/kitchen/kitchen-plp.min.js +1 -1
  40. package/build/js/merged-checkout.min.js +2 -2
  41. package/build/js/package-bulk-add.min.js +1 -0
  42. package/build/js/page/bloomreach-widget/bloomreach-recommendation-carousel-widget.js +7 -6
  43. package/build/js/page/bloomreach-widget/bloomreach-widget-core.js +47 -27
  44. package/build/js/page/bloomreach-widget/bloomreach-widget.js +5 -0
  45. package/build/js/page/bloomreach-widget/complete-your-project/view.js +11 -6
  46. package/build/js/page/bloomreach-widget/complete-your-project/widget.js +0 -1
  47. package/build/js/page/bloomreach-widget/similar-products-widget.js +17 -0
  48. package/build/js/page/plp-load-more.js +81 -66
  49. package/build/js/page/ripple/bulk-add.js +71 -0
  50. package/build/js/page/ripple/package-bulk-add.js +43 -0
  51. package/build/js/page/ripple/postcode-checker.js +83 -0
  52. package/build/js/page/ripple/thumb-gallery.js +236 -0
  53. package/build/js/page/utils/validation.js +1 -1
  54. package/build/js/pdf-preview.min.js +1 -0
  55. package/build/js/pdp.bundle.min.js +2 -2
  56. package/build/js/personal-details.min.js +1 -1
  57. package/build/js/plp.bundle.min.js +1 -1
  58. package/build/js/postcode-checker.min.js +1 -0
  59. package/build/js/project-list.min.js +2 -2
  60. package/build/js/quiz.min.js +1 -1
  61. package/build/js/track-my-order.min.js +1 -1
  62. package/package.json +1 -1
  63. package/src/badges-ripple.html +120 -0
  64. package/src/badges.html +9 -18
  65. package/src/components/bloomreach/complete-card.hbs +1 -1
  66. package/src/components/bloomreach/why-not-add-card.hbs +26 -7
  67. package/src/components/bloomreach/why-not-add.hbs +1 -1
  68. package/src/components/fulfilment-accordions.hbs +1 -1
  69. package/src/components/image-accordion.hbs +18 -0
  70. package/src/components/kitchen/price.hbs +49 -29
  71. package/src/components/kitchen/strip-stickers.hbs +9 -3
  72. package/src/components/modal-capture.hbs +1 -1
  73. package/src/components/modal-id-type.hbs +271 -0
  74. package/src/components/modal-pdf-preview.hbs +70 -0
  75. package/src/components/package-card.hbs +30 -0
  76. package/src/components/partner-signup-form.hbs +1 -231
  77. package/src/components/pdp-price-actions.hbs +17 -0
  78. package/src/components/postcode-checker.hbs +24 -0
  79. package/src/components/price-badges.hbs +12 -10
  80. package/src/components/price-block-v2.hbs +289 -199
  81. package/src/components/product-main-info-v2.hbs +2 -0
  82. package/src/components/suite-includes.hbs +66 -0
  83. package/src/components/suite-product-card.hbs +47 -0
  84. package/src/components/thumb-gallery.hbs +23 -0
  85. package/src/data/data_badges.json +143 -0
  86. package/src/data/data_bathroom-plp.json +814 -0
  87. package/src/data/data_suite-includes-products.json +134 -0
  88. package/src/img/file-pdf-solid-full.svg +3 -0
  89. package/src/img/lorem_ipsum.pdf +150 -0
  90. package/src/js/components/banner-placement-manager.js +25 -9
  91. package/src/js/components/general/image-accordion.js +54 -0
  92. package/src/js/components/general/switchVat.js +8 -1
  93. package/src/js/components/package-card.js +69 -0
  94. package/src/js/components/store-locator-cards.js +76 -0
  95. package/src/js/emulation/banner-placement-manager.js +219 -188
  96. package/src/js/emulation/bloomreach-widget-calculations.js +1 -1
  97. package/src/js/emulation/mock.js +66 -34
  98. package/src/js/emulation/plp-load-more.js +13 -4
  99. package/src/js/emulation/postcode-checker.js +11 -0
  100. package/src/js/emulation/similar-products-loading.js +22 -0
  101. package/src/js/emulation/tradepro-id-pdf.js +182 -0
  102. package/src/js/libs/pdf-preview.js +1177 -0
  103. package/src/js/page/bloomreach-widget/bloomreach-recommendation-carousel-widget.js +7 -6
  104. package/src/js/page/bloomreach-widget/bloomreach-widget-core.js +47 -27
  105. package/src/js/page/bloomreach-widget/bloomreach-widget.js +5 -0
  106. package/src/js/page/bloomreach-widget/complete-your-project/view.js +11 -6
  107. package/src/js/page/bloomreach-widget/complete-your-project/widget.js +0 -1
  108. package/src/js/page/bloomreach-widget/similar-products-widget.js +17 -0
  109. package/src/js/page/plp-load-more.js +81 -66
  110. package/src/js/page/ripple/bulk-add.js +71 -0
  111. package/src/js/page/ripple/package-bulk-add.js +43 -0
  112. package/src/js/page/ripple/postcode-checker.js +83 -0
  113. package/src/js/page/ripple/thumb-gallery.js +236 -0
  114. package/src/js/page/utils/validation.js +1 -1
  115. package/src/page_bathroom-plp-vat.html +81 -0
  116. package/src/page_bathroom-plp-without-load-more.html +83 -0
  117. package/src/page_bathroom-plp.html +98 -0
  118. package/src/page_checkout_confirmation-billie.html +5 -231
  119. package/src/page_checkout_confirmation-clearpay.html +5 -231
  120. package/src/page_checkout_confirmation-new-apple.html +5 -231
  121. package/src/page_checkout_confirmation-new-google.html +5 -231
  122. package/src/page_checkout_confirmation-new-klarna.html +5 -231
  123. package/src/page_checkout_confirmation-new-paypal.html +5 -231
  124. package/src/page_checkout_delivery-details-v2-calendar-not-loaded.html +167 -0
  125. package/src/page_checkout_payment-details-v2-asm.html +2 -3
  126. package/src/page_checkout_payment-details-v2.html +2 -3
  127. package/src/page_checkout_payment-details_with-card-loader.html +2 -3
  128. package/src/page_checkout_payment-details_with-klarna-loader.html +2 -3
  129. package/src/page_checkout_payment-details_with-loader-v2.html +2 -3
  130. package/src/page_klarna_payment-details_with-klarna.html +2 -3
  131. package/src/page_payment-details-billie-registered-only.html +2 -3
  132. package/src/page_payment-details-billie-unregistered-only.html +2 -3
  133. package/src/page_payment-details-with-billie.html +2 -3
  134. package/src/page_payment-details-with-clearpay.html +2 -3
  135. package/src/page_payment-details-with-gift-card.html +2 -3
  136. package/src/page_payment-details_with_apple-pay.html +0 -1
  137. package/src/page_payment-details_with_google-pay.html +2 -3
  138. package/src/page_pdf-preview-example.html +955 -0
  139. package/src/page_product-details-banner.html +1 -3
  140. package/src/page_product-details-big-wins-disabled-delivery.html +1 -3
  141. package/src/page_product-details-big-wins-non-registered.html +1 -3
  142. package/src/page_product-details-big-wins-out-nearest.html +1 -3
  143. package/src/page_product-details-big-wins-unhappy.html +1 -3
  144. package/src/page_product-details-big-wins.html +1 -3
  145. package/src/page_product-details-billie.html +1 -3
  146. package/src/page_product-details-calculator.html +1 -3
  147. package/src/page_product-details-cashback-v2.html +1 -3
  148. package/src/page_product-details-clearpay.html +1 -3
  149. package/src/page_product-details-cnc-error-notification.html +1 -3
  150. package/src/page_product-details-cnc-success-notification-vat.html +1 -4
  151. package/src/page_product-details-cnc-success-notification.html +1 -3
  152. package/src/page_product-details-default-store.html +1 -3
  153. package/src/page_product-details-energy-efficiency.html +1 -3
  154. package/src/page_product-details-flooring-with-pr-placeholder.html +1 -3
  155. package/src/page_product-details-flooring.html +1 -3
  156. package/src/page_product-details-gallery-actual.html +1 -3
  157. package/src/page_product-details-mfe-calculator.html +1 -3
  158. package/src/page_product-details-mtm-doors.html +1 -3
  159. package/src/page_product-details-only-clearpay.html +1 -3
  160. package/src/page_product-details-paint-mixing.html +1 -3
  161. package/src/page_product-details-ripple-gallery-1-image.html +120 -0
  162. package/src/page_product-details-ripple-gallery-missing-image.html +121 -0
  163. package/src/page_product-details-ripple-gallery.html +121 -0
  164. package/src/page_product-details-saved-pl.html +1 -3
  165. package/src/page_product-details-suite-includes-banners.html +139 -0
  166. package/src/page_product-details-suite-includes-billie.html +128 -0
  167. package/src/page_product-details-suite-includes-klarna.html +120 -0
  168. package/src/page_product-details-suite-includes-vat.html +126 -0
  169. package/src/page_product-details-suite-includes.html +122 -0
  170. package/src/page_product-details-tile-steps-klarna-widget-with-method-ids.html +1 -3
  171. package/src/page_product-details-tile-steps-logout.html +1 -3
  172. package/src/page_product-details-tile-steps-no-new-project-list.html +1 -3
  173. package/src/page_product-details-tile-steps-with-klarna.html +1 -3
  174. package/src/page_product-details-tile-steps.html +1 -3
  175. package/src/page_product-details-v2.html +1 -3
  176. package/src/page_product-details-vat.html +1 -3
  177. package/src/page_product-details-wisdom-mcfc-registered.html +1 -3
  178. package/src/page_product-details-wisdom-mcfc.html +1 -3
  179. package/src/page_product-details-wisdom-oos-registered.html +1 -3
  180. package/src/page_product-details-wisdom-oos.html +1 -3
  181. package/src/page_product-details-wisdom-registered.html +1 -3
  182. package/src/page_product-details-wisdom.html +1 -3
  183. package/src/page_product-details-with-favourite.html +1 -3
  184. package/src/page_product-details-with-global-search-v2.html +2 -3
  185. package/src/page_product-details-with-similar-products-widget.html +132 -0
  186. package/src/page_product-details-with-why-not-add-single.html +1 -0
  187. package/src/page_product-details_no-previous-default.html +1 -3
  188. package/src/page_tradePro-checkout-summary-klarna.html +4 -231
  189. package/src/page_tradePro-checkout-summary.html +4 -231
  190. package/src/page_tradePro-signup.html +5 -0
  191. package/src/scss/common/_loading-button.scss +3 -1
  192. package/src/scss/components/_date-selection-add-new-styles.scss +53 -3
  193. package/src/scss/components/_gift-cards.scss +1 -1
  194. package/src/scss/components/_id-type-modal.scss +39 -0
  195. package/src/scss/components/_image-accordion.scss +54 -0
  196. package/src/scss/components/_kitchen-card.scss +1 -0
  197. package/src/scss/components/_map.scss +38 -0
  198. package/src/scss/components/_package-card.scss +65 -0
  199. package/src/scss/components/_package-plp.scss +88 -0
  200. package/src/scss/components/_pdf-preview-modal.scss +122 -0
  201. package/src/scss/components/_postcode-checker.scss +115 -0
  202. package/src/scss/components/_price-badges-critical.scss +10 -0
  203. package/src/scss/components/_price-badges.scss +10 -0
  204. package/src/scss/components/_price-block-critical.scss +1 -0
  205. package/src/scss/components/_price-block.scss +1 -0
  206. package/src/scss/components/_suite-includes.scss +141 -0
  207. package/src/scss/components/_suite-product-card.scss +98 -0
  208. package/src/scss/components/_thumb-gallery-critical.scss +35 -0
  209. package/src/scss/components/_thumb-gallery.scss +200 -0
  210. package/src/scss/components/bloomreach/_why-not-add.scss +3 -1
  211. package/src/scss/components/checkout-payment-details-v2.scss +44 -20
  212. package/src/scss/components/checkout-payment-repayment-terms.scss +0 -1
  213. package/src/scss/components/kitchen/_price.scss +10 -0
  214. package/src/scss/components/kitchen/_strip-stickers.scss +46 -0
  215. package/src/scss/components/price-block-v2-critical.scss +62 -0
  216. package/src/scss/components/price-block-v2.scss +62 -0
  217. package/src/scss/pages/_checkout-tradepro.scss +1 -0
  218. package/src/scss/pages/_checkout_delivery-details.scss +16 -104
  219. package/src/scss/pages/page_checkout_delivery-new.scss +252 -164
  220. package/src/scss/pages/page_product-details-v2-critical.scss +216 -109
  221. package/src/scss/pages/page_product-details-v2.scss +168 -107
  222. package/src/scss/pages/page_product-details.scss +11 -0
  223. package/src/scss/pages/page_products-list-combined.scss +8 -6
  224. package/src/scss/pages/page_products-list_v2.scss +8 -6
  225. package/src/scss/pages/tradePro-signup.scss +1 -0
  226. package/src/scss/pdp-main-critical.scss +1 -0
  227. package/src/scss/pdp-main.scss +6 -0
  228. package/src/scss/plp-main.scss +5 -0
  229. package/src/sitemap.html +30 -6
  230. package/build/css/components/kitchen/strip-stickers.css +0 -1
  231. package/build/css/components/strip-stickers.css +0 -1
  232. package/src/scss/components/kitchen/strip-stickers.scss +0 -18
  233. package/src/scss/components/strip-stickers.scss +0 -18
@@ -0,0 +1,1177 @@
1
+ /* eslint-env amd */
2
+ /*!
3
+ * PdfPreview - a small, dependency-free PDF preview with pagination.
4
+ *
5
+ * Renders a PDF into a container as one canvas per page, and wires up optional
6
+ * navigation controls. Brings no styling of its own: the consumer supplies the
7
+ * markup, the classes, and the CSS.
8
+ *
9
+ * pdf.js is the only dependency and is fetched from a CDN on first use, unless
10
+ * an instance of it is passed in or already present on the page.
11
+ *
12
+ * Quick start
13
+ * -----------
14
+ * <div id="preview">
15
+ * <button data-pdf-preview-prev>Prev</button>
16
+ * <input data-pdf-preview-page>
17
+ * / <span data-pdf-preview-total></span>
18
+ * <button data-pdf-preview-next>Next</button>
19
+ * <div data-pdf-preview-pages></div>
20
+ * </div>
21
+ *
22
+ * var preview = new PdfPreview('#preview');
23
+ * preview.load(fileOrBlobOrUrl);
24
+ *
25
+ * Constructing against an element that already has an instance returns that
26
+ * instance, so this is safe to call repeatedly. Options are read on first
27
+ * construction only; call destroy() first to rebuild with different ones.
28
+ *
29
+ * Declarative usage
30
+ * -----------------
31
+ * <div data-pdf-preview data-pdf-preview-src="doc.pdf"> ... </div>
32
+ * PdfPreview.initAll();
33
+ *
34
+ * Thumbnails
35
+ * ----------
36
+ * One page drawn to an offscreen canvas and handed back as a data URL, for use
37
+ * as the src of an <img>. No root element and no preview needed.
38
+ *
39
+ * PdfPreview.thumbnail(fileOrBlobOrUrl, { width: 120 }).then(function (thumb) {
40
+ * img.src = thumb.dataUrl;
41
+ * });
42
+ *
43
+ * PdfPreview.thumbnailInto(img, fileOrBlobOrUrl, { width: 120 });
44
+ *
45
+ * <img data-pdf-thumbnail data-pdf-thumbnail-src="doc.pdf" alt="First page">
46
+ * PdfPreview.initThumbnails();
47
+ *
48
+ * A preview that already has a document open can make one from it, without
49
+ * reading and parsing the file a second time:
50
+ *
51
+ * preview.getThumbnail({ width: 120 }).then(...);
52
+ *
53
+ * Thumbnail options
54
+ * -----------------
55
+ * width number width to draw the page at in CSS px, default 160
56
+ * height number height to draw the page at in CSS px, default none.
57
+ * With both, the page is fitted inside the box and
58
+ * keeps its aspect ratio
59
+ * page number page to draw, default 1, clamped to the document
60
+ * maxPixelRatio number as above, default 2
61
+ * type string image format, default 'image/jpeg'
62
+ * quality number quality for lossy formats, 0 to 1, default 0.85
63
+ * background string colour drawn behind the page, default '#ffffff'
64
+ * pdfjsLib, pdfjsUrl, pdfjsWorkerUrl as above
65
+ *
66
+ * Thumbnail events (dispatched on the <img>, bubbling)
67
+ * ----------------------------------------------------
68
+ * pdfthumbnail:loaded detail: { dataUrl, width, height, page, pageCount }
69
+ * pdfthumbnail:error detail: { error }
70
+ *
71
+ * Out of the box it injects a handful of base styles so the pages stack inside a
72
+ * scrolling viewport. These go in at the top of <head>, so any stylesheet on the
73
+ * page overrides them, and `injectStyles: false` turns them off entirely.
74
+ *
75
+ * Options
76
+ * -------
77
+ * scale number page size relative to the PDF's own size, used when
78
+ * fitWidth is false (or the container has no width
79
+ * yet), default 1.5
80
+ * pageClass string extra class set on every page canvas, default ''
81
+ * src string URL loaded immediately
82
+ * fitWidth boolean size pages to the container width, default true
83
+ * maxPixelRatio number cap on the device pixel ratio pages are rendered
84
+ * at, to bound memory on dense screens, default 2
85
+ * height number|string max-height of the scrolling viewport, any
86
+ * CSS length, default '50vh'. 'none' removes
87
+ * the cap. Bare numbers are treated as px.
88
+ * spacing number|string gap between pages and inset around them,
89
+ * default 16px. Bare numbers are treated as px.
90
+ * injectStyles boolean add the base layout styles, default true
91
+ * pdfjsLib object existing pdf.js instance, skips the CDN fetch
92
+ * pdfjsUrl string pdf.js script URL
93
+ * pdfjsWorkerUrl string pdf.js worker URL
94
+ * selectors object overrides for the markup hooks below
95
+ *
96
+ * Events (dispatched on the root element, all bubbling)
97
+ * -----------------------------------------------------
98
+ * pdfpreview:loaded detail: { pageCount }
99
+ * pdfpreview:pagechange detail: { page }
100
+ * pdfpreview:error detail: { error }
101
+ */
102
+ (function (root, factory) {
103
+ if (typeof define === 'function' && define.amd) {
104
+ define([], factory);
105
+ } else if (typeof module === 'object' && module.exports) {
106
+ module.exports = factory();
107
+ } else {
108
+ root.PdfPreview = factory();
109
+ }
110
+ })(typeof self !== 'undefined' ? self : this, function () {
111
+ 'use strict';
112
+
113
+ var DEFAULTS = {
114
+ scale: 1.5,
115
+ pageClass: '',
116
+ src: null,
117
+ fitWidth: true,
118
+ maxPixelRatio: 2,
119
+ height: '50vh',
120
+ spacing: null,
121
+ injectStyles: true,
122
+ pdfjsLib: null,
123
+ pdfjsUrl: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js',
124
+ pdfjsWorkerUrl: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js',
125
+ };
126
+
127
+ // Thumbnails share the pdf.js plumbing with previews but nothing else, so
128
+ // they get their own defaults rather than overloading the ones above.
129
+ var THUMBNAIL_DEFAULTS = {
130
+ width: 160,
131
+ height: null,
132
+ page: 1,
133
+ maxPixelRatio: DEFAULTS.maxPixelRatio,
134
+ // JPEG keeps the data URL small, which matters when it lives in an
135
+ // attribute. PDF pages are opaque, so there is no transparency to lose.
136
+ type: 'image/jpeg',
137
+ quality: 0.85,
138
+ background: '#ffffff',
139
+ pdfjsLib: DEFAULTS.pdfjsLib,
140
+ pdfjsUrl: DEFAULTS.pdfjsUrl,
141
+ pdfjsWorkerUrl: DEFAULTS.pdfjsWorkerUrl,
142
+ };
143
+
144
+ var SELECTORS = {
145
+ root: '[data-pdf-preview]',
146
+ pages: '[data-pdf-preview-pages]',
147
+ first: '[data-pdf-preview-first]',
148
+ prev: '[data-pdf-preview-prev]',
149
+ next: '[data-pdf-preview-next]',
150
+ last: '[data-pdf-preview-last]',
151
+ pageInput: '[data-pdf-preview-page]',
152
+ pageTotal: '[data-pdf-preview-total]',
153
+ thumbnail: '[data-pdf-thumbnail]',
154
+ };
155
+
156
+ // Classes the library puts on its own elements. Prefixed to stay clear of
157
+ // whatever the consumer names things.
158
+ var CLASSES = {
159
+ pages: 'pdfp-pages',
160
+ page: 'pdfp-page',
161
+ pageAuto: 'pdfp-page--auto',
162
+ };
163
+
164
+ var STYLE_ID = 'pdf-preview-base-styles';
165
+
166
+ /**
167
+ * The minimum needed for a usable document view: a scrolling viewport with
168
+ * pages stacked inside it. Decoration is left to the consumer, so this is
169
+ * inserted at the top of <head> and any stylesheet on the page outranks it.
170
+ */
171
+ var BASE_STYLES =
172
+ '.' +
173
+ CLASSES.pages +
174
+ '{overflow-y:auto;overflow-x:hidden;max-height:var(--pdfp-height);scrollbar-gutter:stable;' +
175
+ 'padding:var(--pdfp-spacing,16px);-webkit-overflow-scrolling:touch}' +
176
+ '.' +
177
+ CLASSES.page +
178
+ '{display:block;width:100%;height:auto;margin:0 auto}' +
179
+ // Canvases are rendered at a multiple of their display size for sharp
180
+ // output on dense screens, so natural-size pages need their CSS width
181
+ // set explicitly. The library supplies it as --pdfp-page-width.
182
+ '.' +
183
+ CLASSES.pageAuto +
184
+ '{width:var(--pdfp-page-width,auto);max-width:100%}' +
185
+ '.' +
186
+ CLASSES.page +
187
+ '+.' +
188
+ CLASSES.page +
189
+ '{margin-top:var(--pdfp-spacing,16px)}';
190
+
191
+ // How long after the last scroll event a programmatic scroll is treated as
192
+ // finished, and the safety net for when it produces no scroll events at all.
193
+ var SETTLE_DELAY = 150;
194
+ var SETTLE_TIMEOUT = 700;
195
+
196
+ // Largest canvas iOS Safari will draw (4096 x 4096); bigger ones render blank.
197
+ var MAX_CANVAS_PIXELS = 16777216;
198
+
199
+ var INSTANCE_KEY = '__pdfPreview';
200
+ var THUMBNAIL_KEY = '__pdfThumbnail';
201
+ var pdfjsPromise = null;
202
+ var stylesInjected = false;
203
+
204
+ function noop() {
205
+ // Deliberately empty: used to mark a rejection as handled.
206
+ }
207
+
208
+ function injectBaseStyles() {
209
+ if (stylesInjected || document.getElementById(STYLE_ID)) {
210
+ stylesInjected = true;
211
+
212
+ return;
213
+ }
214
+
215
+ var style = document.createElement('style');
216
+
217
+ style.id = STYLE_ID;
218
+ style.appendChild(document.createTextNode(BASE_STYLES));
219
+ document.head.insertBefore(style, document.head.firstChild);
220
+ stylesInjected = true;
221
+ }
222
+
223
+ function assign(target) {
224
+ for (var i = 1; i < arguments.length; i++) {
225
+ var source = arguments[i];
226
+
227
+ if (!source) {
228
+ continue;
229
+ }
230
+
231
+ for (var key in source) {
232
+ if (
233
+ Object.prototype.hasOwnProperty.call(source, key) &&
234
+ source[key] !== undefined
235
+ ) {
236
+ target[key] = source[key];
237
+ }
238
+ }
239
+ }
240
+
241
+ return target;
242
+ }
243
+
244
+ function smoothScrollSupported() {
245
+ return 'scrollBehavior' in document.documentElement.style;
246
+ }
247
+
248
+ function emit(element, name, detail) {
249
+ var event;
250
+
251
+ try {
252
+ event = new CustomEvent(name, { detail: detail, bubbles: true });
253
+ } catch (error) {
254
+ // IE11 and other engines without the CustomEvent constructor.
255
+ event = document.createEvent('CustomEvent');
256
+ event.initCustomEvent(name, true, false, detail);
257
+ }
258
+
259
+ element.dispatchEvent(event);
260
+ }
261
+
262
+ /**
263
+ * Scale back a render that would need a canvas bigger than iOS Safari will
264
+ * draw. Shared by pages and thumbnails, so neither can render blank.
265
+ */
266
+ function clampRenderScale(natural, renderScale) {
267
+ var area = natural.width * natural.height * renderScale * renderScale;
268
+
269
+ if (area > MAX_CANVAS_PIXELS) {
270
+ return renderScale * Math.sqrt(MAX_CANVAS_PIXELS / area);
271
+ }
272
+
273
+ return renderScale;
274
+ }
275
+
276
+ function toCssLength(value) {
277
+ if (value === null || value === undefined || value === '') {
278
+ return null;
279
+ }
280
+
281
+ return /^-?\d+(\.\d+)?$/.test(String(value)) ? value + 'px' : String(value);
282
+ }
283
+
284
+ function addClass(existing, name) {
285
+ var current = existing ? String(existing).split(/\s+/) : [];
286
+
287
+ for (var i = 0; i < current.length; i++) {
288
+ if (current[i] === name) {
289
+ return existing;
290
+ }
291
+ }
292
+
293
+ current.push(name);
294
+
295
+ return current.join(' ').replace(/^\s+/, '');
296
+ }
297
+
298
+ function resolveElement(target) {
299
+ if (!target) {
300
+ return null;
301
+ }
302
+
303
+ if (typeof target === 'string') {
304
+ return document.querySelector(target);
305
+ }
306
+
307
+ // Accept a jQuery-like or array-like wrapper.
308
+ if (target.nodeType !== 1 && typeof target.length === 'number') {
309
+ return target[0] || null;
310
+ }
311
+
312
+ return target.nodeType === 1 ? target : null;
313
+ }
314
+
315
+ function readDataOptions(element) {
316
+ var scale = parseFloat(element.getAttribute('data-pdf-preview-scale'));
317
+ var maxPixelRatio = parseFloat(element.getAttribute('data-pdf-preview-max-pixel-ratio'));
318
+
319
+ var fitWidth = element.getAttribute('data-pdf-preview-fit-width');
320
+
321
+ return {
322
+ scale: isNaN(scale) ? undefined : scale,
323
+ maxPixelRatio: isNaN(maxPixelRatio) ? undefined : maxPixelRatio,
324
+ pageClass: element.getAttribute('data-pdf-preview-page-class') || undefined,
325
+ src: element.getAttribute('data-pdf-preview-src') || undefined,
326
+ height: element.getAttribute('data-pdf-preview-height') || undefined,
327
+ spacing: element.getAttribute('data-pdf-preview-spacing') || undefined,
328
+ fitWidth: fitWidth === null ? undefined : fitWidth !== 'false',
329
+ };
330
+ }
331
+
332
+ function loadScript(url) {
333
+ return new Promise(function (resolve, reject) {
334
+ var script = document.createElement('script');
335
+
336
+ script.src = url;
337
+ script.onload = resolve;
338
+ script.onerror = function () {
339
+ reject(new Error('PdfPreview: failed to load ' + url));
340
+ };
341
+ document.head.appendChild(script);
342
+ });
343
+ }
344
+
345
+ function getPdfjs(options) {
346
+ if (options.pdfjsLib) {
347
+ return Promise.resolve(options.pdfjsLib);
348
+ }
349
+
350
+ if (window.pdfjsLib) {
351
+ return Promise.resolve(window.pdfjsLib);
352
+ }
353
+
354
+ if (!pdfjsPromise) {
355
+ pdfjsPromise = loadScript(options.pdfjsUrl).then(function () {
356
+ var lib = window.pdfjsLib || window['pdfjs-dist/build/pdf'];
357
+
358
+ if (!lib) {
359
+ throw new Error('PdfPreview: pdf.js did not register itself');
360
+ }
361
+
362
+ if (options.pdfjsWorkerUrl && lib.GlobalWorkerOptions) {
363
+ lib.GlobalWorkerOptions.workerSrc = options.pdfjsWorkerUrl;
364
+ }
365
+
366
+ window.pdfjsLib = lib;
367
+
368
+ return lib;
369
+ });
370
+ }
371
+
372
+ return pdfjsPromise;
373
+ }
374
+
375
+ /**
376
+ * Scale that fits a page into the requested box. With a width and a height
377
+ * the smaller of the two wins, so the page is contained rather than
378
+ * stretched; the thumbnail then matches the page's own aspect ratio.
379
+ */
380
+ function thumbnailDisplayScale(natural, options) {
381
+ var width = parseFloat(options.width);
382
+ var height = parseFloat(options.height);
383
+ var scale = Infinity;
384
+
385
+ if (width > 0) {
386
+ scale = Math.min(scale, width / natural.width);
387
+ }
388
+
389
+ if (height > 0) {
390
+ scale = Math.min(scale, height / natural.height);
391
+ }
392
+
393
+ return scale === Infinity ? 1 : scale;
394
+ }
395
+
396
+ /**
397
+ * Draw one page to an offscreen canvas and hand it back as a data URL. The
398
+ * canvas is released straight away, so all that is kept is the string.
399
+ */
400
+ function drawThumbnail(page, options) {
401
+ var natural = page.getViewport({ scale: 1 });
402
+ var displayScale = thumbnailDisplayScale(natural, options);
403
+ var pixelRatio = Math.min(window.devicePixelRatio || 1, options.maxPixelRatio);
404
+ var renderScale = clampRenderScale(natural, displayScale * pixelRatio);
405
+ var viewport = page.getViewport({ scale: renderScale });
406
+ var canvas = document.createElement('canvas');
407
+
408
+ canvas.width = Math.floor(viewport.width);
409
+ canvas.height = Math.floor(viewport.height);
410
+
411
+ return page
412
+ .render({
413
+ canvasContext: canvas.getContext('2d'),
414
+ viewport: viewport,
415
+ // pdf.js fills white by default, but a PDF page carries no
416
+ // background of its own, so say it explicitly rather than rely
417
+ // on the default of whichever pdf.js is in play.
418
+ background: options.background,
419
+ })
420
+ .promise.then(function () {
421
+ var dataUrl = canvas.toDataURL(options.type, options.quality);
422
+
423
+ // Zero-sized canvases free their backing store immediately,
424
+ // which matters on iOS where it is capped per page.
425
+ canvas.width = 0;
426
+ canvas.height = 0;
427
+
428
+ return {
429
+ dataUrl: dataUrl,
430
+ width: Math.round(natural.width * displayScale),
431
+ height: Math.round(natural.height * displayScale),
432
+ };
433
+ });
434
+ }
435
+
436
+ function thumbnailFromDocument(doc, options) {
437
+ var requested = parseInt(options.page, 10);
438
+ var pageNumber = Math.min(Math.max(isNaN(requested) ? 1 : requested, 1), doc.numPages);
439
+
440
+ return doc.getPage(pageNumber).then(function (page) {
441
+ return drawThumbnail(page, options).then(function (thumbnail) {
442
+ thumbnail.page = pageNumber;
443
+ thumbnail.pageCount = doc.numPages;
444
+
445
+ return thumbnail;
446
+ });
447
+ });
448
+ }
449
+
450
+ function readThumbnailDataOptions(element) {
451
+ var width = parseFloat(element.getAttribute('data-pdf-thumbnail-width'));
452
+ var height = parseFloat(element.getAttribute('data-pdf-thumbnail-height'));
453
+ var page = parseInt(element.getAttribute('data-pdf-thumbnail-page'), 10);
454
+ var quality = parseFloat(element.getAttribute('data-pdf-thumbnail-quality'));
455
+
456
+ return {
457
+ src: element.getAttribute('data-pdf-thumbnail-src') || undefined,
458
+ width: isNaN(width) ? undefined : width,
459
+ height: isNaN(height) ? undefined : height,
460
+ page: isNaN(page) ? undefined : page,
461
+ type: element.getAttribute('data-pdf-thumbnail-type') || undefined,
462
+ quality: isNaN(quality) ? undefined : quality,
463
+ };
464
+ }
465
+
466
+ function PdfPreview(target, options) {
467
+ var element = resolveElement(target);
468
+
469
+ if (!element) {
470
+ throw new Error('PdfPreview: root element not found');
471
+ }
472
+
473
+ // Calling on an already-initialised element returns the existing
474
+ // instance, so it is safe to call repeatedly for the same root.
475
+ if (element[INSTANCE_KEY]) {
476
+ return element[INSTANCE_KEY];
477
+ }
478
+
479
+ // Tolerate being called without `new`.
480
+ if (!(this instanceof PdfPreview)) {
481
+ return new PdfPreview(element, options);
482
+ }
483
+
484
+ this.element = element;
485
+ this.options = assign({}, DEFAULTS, readDataOptions(element), options);
486
+ this.selectors = assign({}, SELECTORS, (options || {}).selectors);
487
+ this.pagesContainer = element.querySelector(this.selectors.pages) || element;
488
+ this.doc = null;
489
+ this.canvases = [];
490
+ this.objectUrl = null;
491
+ // Bumped by every load() and reset(), so async work started by an
492
+ // earlier load can tell it has been superseded and stop touching the DOM.
493
+ this._loadId = 0;
494
+ this._loadingTask = null;
495
+
496
+ element[INSTANCE_KEY] = this;
497
+
498
+ if (this.options.injectStyles) {
499
+ injectBaseStyles();
500
+ this.pagesContainer.className = addClass(this.pagesContainer.className, CLASSES.pages);
501
+ }
502
+
503
+ // Sizing options are handed to the base styles as custom properties
504
+ // rather than set as inline max-height/padding/margin, so a consumer
505
+ // stylesheet can still override the real properties.
506
+ var height = toCssLength(this.options.height);
507
+ var spacing = toCssLength(this.options.spacing);
508
+
509
+ if (this.pagesContainer.style.setProperty) {
510
+ if (height) {
511
+ this.pagesContainer.style.setProperty('--pdfp-height', height);
512
+ }
513
+
514
+ if (spacing) {
515
+ this.pagesContainer.style.setProperty('--pdfp-spacing', spacing);
516
+ }
517
+ }
518
+
519
+ this._bind();
520
+
521
+ if (this.options.src) {
522
+ this.load(this.options.src);
523
+ }
524
+ }
525
+
526
+ PdfPreview.prototype._find = function (selector) {
527
+ return selector ? this.element.querySelector(selector) : null;
528
+ };
529
+
530
+ PdfPreview.prototype._emit = function (name, detail) {
531
+ emit(this.element, name, detail);
532
+ };
533
+
534
+ PdfPreview.prototype._bind = function () {
535
+ var self = this;
536
+
537
+ this._onClick = function (event) {
538
+ var target = event.target;
539
+
540
+ function matches(selector) {
541
+ return selector && target.closest(selector);
542
+ }
543
+
544
+ if (matches(self.selectors.first)) {
545
+ self.goToPage(1);
546
+ } else if (matches(self.selectors.prev)) {
547
+ self.goToPage(self.getCurrentPage() - 1);
548
+ } else if (matches(self.selectors.next)) {
549
+ self.goToPage(self.getCurrentPage() + 1);
550
+ } else if (matches(self.selectors.last)) {
551
+ self.goToPage(self.getPageCount());
552
+ } else {
553
+ return;
554
+ }
555
+
556
+ event.preventDefault();
557
+ };
558
+
559
+ this._onChange = function (event) {
560
+ if (!self.selectors.pageInput || !event.target.closest(self.selectors.pageInput)) {
561
+ return;
562
+ }
563
+
564
+ var value = parseInt(event.target.value, 10);
565
+
566
+ if (!isNaN(value) && value >= 1 && value <= self.getPageCount()) {
567
+ self.goToPage(value);
568
+ } else {
569
+ self._setPageInput(self.getCurrentPage());
570
+ }
571
+ };
572
+
573
+ this._onScroll = function (event) {
574
+ var target = event.target;
575
+
576
+ // Listening in the capture phase on the document sees every scroll
577
+ // on the page, since scroll events do not bubble. Only the ones that
578
+ // move the pages matter: the pages container itself when it is the
579
+ // scrolling viewport, or an ancestor (the window, a modal) when the
580
+ // pages sit in normal flow.
581
+ if (target !== document && !(target.contains && target.contains(self.pagesContainer))) {
582
+ return;
583
+ }
584
+
585
+ if (self._scrollLocked) {
586
+ // A programmatic scroll is running, so keep showing the
587
+ // destination page and wait for it to settle rather than
588
+ // reporting every page it travels past.
589
+ self._lockPageSync(SETTLE_DELAY);
590
+
591
+ return;
592
+ }
593
+
594
+ if (self._scrollFrame) {
595
+ return;
596
+ }
597
+
598
+ self._scrollFrame = requestAnimationFrame(function () {
599
+ self._scrollFrame = null;
600
+ self._setPageInput(self.getCurrentPage());
601
+ });
602
+ };
603
+
604
+ this.element.addEventListener('click', this._onClick);
605
+ this.element.addEventListener('change', this._onChange);
606
+ document.addEventListener('scroll', this._onScroll, true);
607
+ };
608
+
609
+ /** Whether the pages container is itself the scrolling viewport. */
610
+ PdfPreview.prototype._isScrollViewport = function () {
611
+ return this.pagesContainer.scrollHeight > this.pagesContainer.clientHeight;
612
+ };
613
+
614
+ /**
615
+ * Stop scroll events from writing to the page input until things have
616
+ * settled. Called again on every scroll event so the lock lasts as long as
617
+ * the animation does. Once settled, the page asked for by goToPage() is kept
618
+ * even if it could not be scrolled fully into place (e.g. the second-to-last
619
+ * page when the last one is short); the next user scroll re-syncs.
620
+ */
621
+ PdfPreview.prototype._lockPageSync = function (delay) {
622
+ var self = this;
623
+
624
+ this._scrollLocked = true;
625
+ clearTimeout(this._scrollTimer);
626
+
627
+ this._scrollTimer = setTimeout(function () {
628
+ self._scrollLocked = false;
629
+ self._setPageInput(self._targetPage || self.getCurrentPage());
630
+ self._targetPage = null;
631
+ }, delay);
632
+ };
633
+
634
+ PdfPreview.prototype._setPageInput = function (page) {
635
+ var input = this._find(this.selectors.pageInput);
636
+
637
+ if (input && String(input.value) !== String(page)) {
638
+ input.value = page;
639
+ this._emit('pdfpreview:pagechange', { page: page });
640
+ }
641
+ };
642
+
643
+ PdfPreview.prototype._setPageTotal = function (total) {
644
+ var element = this._find(this.selectors.pageTotal);
645
+
646
+ if (element) {
647
+ element.textContent = total;
648
+ }
649
+ };
650
+
651
+ PdfPreview.prototype._isCurrentLoad = function (token) {
652
+ return token === this._loadId;
653
+ };
654
+
655
+ /** Width available to a page inside the pages container, in CSS px. */
656
+ PdfPreview.prototype._contentWidth = function () {
657
+ var container = this.pagesContainer;
658
+ var style = window.getComputedStyle(container);
659
+
660
+ return (
661
+ container.clientWidth -
662
+ (parseFloat(style.paddingLeft) || 0) -
663
+ (parseFloat(style.paddingRight) || 0)
664
+ );
665
+ };
666
+
667
+ /**
668
+ * Scale that gives a page its on-screen (CSS) size: the container width
669
+ * when fitting, otherwise the `scale` option. Also used when the container
670
+ * has no width yet, e.g. when loading into a hidden element.
671
+ */
672
+ PdfPreview.prototype._displayScale = function (pageWidth) {
673
+ if (this.options.fitWidth && this._availableWidth > 0) {
674
+ return this._availableWidth / pageWidth;
675
+ }
676
+
677
+ return this.options.scale;
678
+ };
679
+
680
+ PdfPreview.prototype._renderPage = function (num, token) {
681
+ var self = this;
682
+
683
+ return this.doc.getPage(num).then(function (page) {
684
+ if (!self._isCurrentLoad(token)) {
685
+ return;
686
+ }
687
+
688
+ // Draw at display size x device pixel ratio, so the canvas has one
689
+ // backing pixel per screen pixel and stays sharp on dense screens,
690
+ // capped to keep memory in check.
691
+ var natural = page.getViewport({ scale: 1 });
692
+ var displayScale = self._displayScale(natural.width);
693
+ var pixelRatio = Math.min(window.devicePixelRatio || 1, self.options.maxPixelRatio);
694
+ var renderScale = clampRenderScale(natural, displayScale * pixelRatio);
695
+ var viewport = page.getViewport({ scale: renderScale });
696
+ var canvas = document.createElement('canvas');
697
+
698
+ canvas.width = Math.floor(viewport.width);
699
+ canvas.height = Math.floor(viewport.height);
700
+ canvas.setAttribute('data-page', num);
701
+
702
+ if (canvas.style.setProperty) {
703
+ canvas.style.setProperty(
704
+ '--pdfp-page-width',
705
+ Math.round(natural.width * displayScale) + 'px'
706
+ );
707
+ }
708
+
709
+ var className = '';
710
+
711
+ if (self.options.injectStyles) {
712
+ className = addClass(className, CLASSES.page);
713
+
714
+ if (!self.options.fitWidth) {
715
+ className = addClass(className, CLASSES.pageAuto);
716
+ }
717
+ }
718
+
719
+ if (self.options.pageClass) {
720
+ className = addClass(className, self.options.pageClass);
721
+ }
722
+
723
+ if (className) {
724
+ canvas.className = className;
725
+ }
726
+
727
+ self.pagesContainer.appendChild(canvas);
728
+ self.canvases.push(canvas);
729
+
730
+ return page.render({
731
+ canvasContext: canvas.getContext('2d'),
732
+ viewport: viewport,
733
+ }).promise;
734
+ });
735
+ };
736
+
737
+ PdfPreview.prototype._renderFrom = function (num, token) {
738
+ var self = this;
739
+
740
+ if (!this._isCurrentLoad(token) || !this.doc || num > this.doc.numPages) {
741
+ return Promise.resolve();
742
+ }
743
+
744
+ return this._renderPage(num, token).then(function () {
745
+ return self._renderFrom(num + 1, token);
746
+ });
747
+ };
748
+
749
+ /**
750
+ * Render a PDF. Accepts a File, a Blob, or a URL string.
751
+ * Returns a Promise resolving with the pdf.js document, or with undefined
752
+ * when the load is superseded by another load(), reset() or destroy()
753
+ * before it finishes.
754
+ */
755
+ PdfPreview.prototype.load = function (source) {
756
+ var self = this;
757
+
758
+ if (!source) {
759
+ return Promise.reject(new Error('PdfPreview: no source given'));
760
+ }
761
+
762
+ this.reset();
763
+
764
+ var token = ++this._loadId;
765
+
766
+ return getPdfjs(this.options)
767
+ .then(function (pdfjsLib) {
768
+ if (!self._isCurrentLoad(token)) {
769
+ return null;
770
+ }
771
+
772
+ var url = source;
773
+
774
+ if (typeof source !== 'string') {
775
+ self.objectUrl = URL.createObjectURL(source);
776
+ url = self.objectUrl;
777
+ }
778
+
779
+ // isEvalSupported: false stops pdf.js compiling font glyphs into
780
+ // code with new Function(), which a crafted PDF can abuse to run
781
+ // its own script (CVE-2024-4367, fixed in pdf.js 4.2.67). Glyphs
782
+ // are interpreted instead; slightly slower, but safe for any
783
+ // pdf.js version, including one passed in via `pdfjsLib`.
784
+ self._loadingTask = pdfjsLib.getDocument({ url: url, isEvalSupported: false });
785
+
786
+ return self._loadingTask.promise;
787
+ })
788
+ .then(function (doc) {
789
+ if (!doc) {
790
+ return undefined;
791
+ }
792
+
793
+ if (!self._isCurrentLoad(token)) {
794
+ // Resolved after being superseded: free it and bail out.
795
+ doc.destroy();
796
+
797
+ return undefined;
798
+ }
799
+
800
+ self.doc = doc;
801
+ self._setPageTotal(doc.numPages);
802
+
803
+ var input = self._find(self.selectors.pageInput);
804
+
805
+ if (input) {
806
+ input.setAttribute('max', doc.numPages);
807
+ input.value = 1;
808
+ }
809
+
810
+ self._emit('pdfpreview:loaded', { pageCount: doc.numPages });
811
+
812
+ // Measured once per load, so every page is fitted to the same width.
813
+ self._availableWidth = self._contentWidth();
814
+
815
+ return self._renderFrom(1, token).then(function () {
816
+ return self._isCurrentLoad(token) ? doc : undefined;
817
+ });
818
+ })
819
+ .catch(function (error) {
820
+ // Cancelling a load makes pdf.js reject whatever was in flight
821
+ // (worker destroyed, rendering cancelled). That is expected, not
822
+ // an error worth reporting.
823
+ if (!self._isCurrentLoad(token)) {
824
+ return undefined;
825
+ }
826
+
827
+ self._emit('pdfpreview:error', { error: error });
828
+ throw error;
829
+ });
830
+ };
831
+
832
+ /**
833
+ * Remove all rendered pages, cancel any load in progress and release the
834
+ * current document.
835
+ */
836
+ PdfPreview.prototype.reset = function () {
837
+ this._loadId++;
838
+
839
+ if (this._loadingTask) {
840
+ // Also destroys the document it produced, and aborts the fetch and
841
+ // parsing if it has not finished yet.
842
+ this._loadingTask.destroy();
843
+ this._loadingTask = null;
844
+ }
845
+
846
+ while (this.pagesContainer.firstChild) {
847
+ this.pagesContainer.removeChild(this.pagesContainer.firstChild);
848
+ }
849
+
850
+ this.canvases = [];
851
+ this.doc = null;
852
+ clearTimeout(this._scrollTimer);
853
+ cancelAnimationFrame(this._scrollFrame);
854
+ this._scrollFrame = null;
855
+ this._scrollLocked = false;
856
+ this._targetPage = null;
857
+ this._setPageTotal('');
858
+
859
+ var input = this._find(this.selectors.pageInput);
860
+
861
+ if (input) {
862
+ input.value = 1;
863
+ }
864
+
865
+ if (this.objectUrl) {
866
+ URL.revokeObjectURL(this.objectUrl);
867
+ this.objectUrl = null;
868
+ }
869
+ };
870
+
871
+ PdfPreview.prototype.getPageCount = function () {
872
+ return this.doc ? this.doc.numPages : 0;
873
+ };
874
+
875
+ /**
876
+ * Thumbnail of the document this preview already has open, so the file is
877
+ * read and parsed once for both. Resolves with { dataUrl, width, height,
878
+ * page, pageCount }, or with undefined when a later load(), reset() or
879
+ * destroy() takes over while it is being drawn. Rejects when no document is
880
+ * open; use the static PdfPreview.thumbnail() for that case.
881
+ */
882
+ PdfPreview.prototype.getThumbnail = function (options) {
883
+ var self = this;
884
+
885
+ if (!this.doc) {
886
+ return Promise.reject(new Error('PdfPreview: no document loaded'));
887
+ }
888
+
889
+ var settings = assign(
890
+ {},
891
+ THUMBNAIL_DEFAULTS,
892
+ { maxPixelRatio: this.options.maxPixelRatio },
893
+ options
894
+ );
895
+ var token = this._loadId;
896
+
897
+ return thumbnailFromDocument(this.doc, settings).then(
898
+ function (thumbnail) {
899
+ return self._isCurrentLoad(token) ? thumbnail : undefined;
900
+ },
901
+ function (error) {
902
+ // Superseded loads destroy the document mid-render, which
903
+ // rejects. Expected, and not this thumbnail's problem.
904
+ if (!self._isCurrentLoad(token)) {
905
+ return undefined;
906
+ }
907
+
908
+ throw error;
909
+ }
910
+ );
911
+ };
912
+
913
+ /**
914
+ * The page under, or nearest to, the middle of the visible area: the
915
+ * pages container when it scrolls, otherwise the browser window.
916
+ */
917
+ PdfPreview.prototype.getCurrentPage = function () {
918
+ var count = this.canvases.length;
919
+
920
+ if (!count) {
921
+ return 1;
922
+ }
923
+
924
+ var middle;
925
+
926
+ if (this._isScrollViewport()) {
927
+ var container = this.pagesContainer;
928
+
929
+ // A short last page can never reach the middle, so treat the very
930
+ // end of the scroll range as the last page (and the start as the first).
931
+ if (container.scrollTop + container.clientHeight >= container.scrollHeight - 1) {
932
+ return count;
933
+ }
934
+
935
+ if (container.scrollTop <= 0) {
936
+ return 1;
937
+ }
938
+
939
+ var bounds = container.getBoundingClientRect();
940
+
941
+ middle = bounds.top + bounds.height / 2;
942
+ } else {
943
+ middle = window.innerHeight / 2;
944
+ }
945
+
946
+ var nearest = 1;
947
+ var nearestDistance = Infinity;
948
+
949
+ for (var i = 0; i < count; i++) {
950
+ var pageBounds = this.canvases[i].getBoundingClientRect();
951
+
952
+ if (pageBounds.top <= middle && pageBounds.bottom >= middle) {
953
+ return i + 1;
954
+ }
955
+
956
+ // The middle is in the gap between pages (or past either end):
957
+ // fall back to whichever page edge is closest to it.
958
+ var distance = Math.min(
959
+ Math.abs(pageBounds.top - middle),
960
+ Math.abs(pageBounds.bottom - middle)
961
+ );
962
+
963
+ if (distance < nearestDistance) {
964
+ nearestDistance = distance;
965
+ nearest = i + 1;
966
+ }
967
+ }
968
+
969
+ return nearest;
970
+ };
971
+
972
+ PdfPreview.prototype.goToPage = function (page) {
973
+ var canvas = this.canvases[page - 1];
974
+
975
+ if (!canvas || page < 1 || page > this.getPageCount()) {
976
+ return this;
977
+ }
978
+
979
+ var container = this.pagesContainer;
980
+
981
+ // Show the destination straight away and hold it there for the duration
982
+ // of the scroll, so the input does not count through every page on route.
983
+ this._setPageInput(page);
984
+ this._targetPage = page;
985
+ this._lockPageSync(SETTLE_TIMEOUT);
986
+
987
+ if (this._isScrollViewport()) {
988
+ // Scroll the viewport itself rather than using scrollIntoView, which
989
+ // would also scroll every ancestor and drag the page away from the
990
+ // navigation controls.
991
+ var offset = canvas.getBoundingClientRect().top - container.getBoundingClientRect().top;
992
+ var paddingTop = parseFloat(window.getComputedStyle(container).paddingTop) || 0;
993
+ var top = container.scrollTop + offset - paddingTop;
994
+
995
+ if (smoothScrollSupported() && container.scrollTo) {
996
+ container.scrollTo({ top: top, behavior: 'smooth' });
997
+ } else {
998
+ container.scrollTop = top;
999
+ }
1000
+ } else {
1001
+ // Not a scrolling viewport, so the pages sit in normal page flow and
1002
+ // moving the document is the only way to reach them.
1003
+ canvas.scrollIntoView({ behavior: 'smooth', block: 'start' });
1004
+ }
1005
+
1006
+ return this;
1007
+ };
1008
+
1009
+ /** Unbind listeners, clear the container and forget the instance. */
1010
+ PdfPreview.prototype.destroy = function () {
1011
+ this.reset();
1012
+ clearTimeout(this._scrollTimer);
1013
+ this._scrollLocked = false;
1014
+ this.element.removeEventListener('click', this._onClick);
1015
+ this.element.removeEventListener('change', this._onChange);
1016
+ document.removeEventListener('scroll', this._onScroll, true);
1017
+ delete this.element[INSTANCE_KEY];
1018
+ };
1019
+
1020
+ /**
1021
+ * Thumbnail of one page of a PDF, as a data URL for the src of an <img>.
1022
+ * Accepts a File, a Blob or a URL, and needs no preview and no element.
1023
+ * Resolves with { dataUrl, width, height, page, pageCount }, where width
1024
+ * and height are the CSS px the thumbnail is meant to be shown at.
1025
+ *
1026
+ * The document is closed and any object URL revoked before it resolves, so
1027
+ * nothing is held on to but the string.
1028
+ */
1029
+ PdfPreview.thumbnail = function (source, options) {
1030
+ if (!source) {
1031
+ return Promise.reject(new Error('PdfPreview.thumbnail: no source given'));
1032
+ }
1033
+
1034
+ var settings = assign({}, THUMBNAIL_DEFAULTS, options);
1035
+ var objectUrl = null;
1036
+ var loadingTask = null;
1037
+
1038
+ function release() {
1039
+ if (loadingTask) {
1040
+ // Destroys the document it produced along with it.
1041
+ loadingTask.destroy();
1042
+ loadingTask = null;
1043
+ }
1044
+
1045
+ if (objectUrl) {
1046
+ URL.revokeObjectURL(objectUrl);
1047
+ objectUrl = null;
1048
+ }
1049
+ }
1050
+
1051
+ return getPdfjs(settings)
1052
+ .then(function (pdfjsLib) {
1053
+ var url = source;
1054
+
1055
+ if (typeof source !== 'string') {
1056
+ objectUrl = URL.createObjectURL(source);
1057
+ url = objectUrl;
1058
+ }
1059
+
1060
+ // isEvalSupported: false for the same reason as in load().
1061
+ loadingTask = pdfjsLib.getDocument({ url: url, isEvalSupported: false });
1062
+
1063
+ return loadingTask.promise;
1064
+ })
1065
+ .then(function (doc) {
1066
+ return thumbnailFromDocument(doc, settings);
1067
+ })
1068
+ .then(
1069
+ function (thumbnail) {
1070
+ release();
1071
+
1072
+ return thumbnail;
1073
+ },
1074
+ function (error) {
1075
+ release();
1076
+
1077
+ throw error;
1078
+ }
1079
+ );
1080
+ };
1081
+
1082
+ /**
1083
+ * Render a thumbnail straight into an <img>: sets its src, and its width
1084
+ * and height so the tile holds its space before the image arrives. Options
1085
+ * can come from data-pdf-thumbnail-* attributes on the element, with the
1086
+ * ones passed here winning. `source` may be omitted to use
1087
+ * data-pdf-thumbnail-src.
1088
+ *
1089
+ * Dispatches pdfthumbnail:loaded or pdfthumbnail:error on the element. On
1090
+ * failure the element is left untouched, so whatever it already shows (a
1091
+ * generic file icon, say) stays visible.
1092
+ */
1093
+ PdfPreview.thumbnailInto = function (target, source, options) {
1094
+ var element = resolveElement(target);
1095
+
1096
+ if (!element) {
1097
+ return Promise.reject(new Error('PdfPreview.thumbnailInto: element not found'));
1098
+ }
1099
+
1100
+ if (element.tagName !== 'IMG') {
1101
+ return Promise.reject(new Error('PdfPreview.thumbnailInto: expects an <img> element'));
1102
+ }
1103
+
1104
+ var settings = assign({}, readThumbnailDataOptions(element), options);
1105
+
1106
+ return PdfPreview.thumbnail(source || settings.src, settings).then(
1107
+ function (thumbnail) {
1108
+ element.src = thumbnail.dataUrl;
1109
+ element.setAttribute('width', thumbnail.width);
1110
+ element.setAttribute('height', thumbnail.height);
1111
+ emit(element, 'pdfthumbnail:loaded', thumbnail);
1112
+
1113
+ return thumbnail;
1114
+ },
1115
+ function (error) {
1116
+ emit(element, 'pdfthumbnail:error', { error: error });
1117
+
1118
+ throw error;
1119
+ }
1120
+ );
1121
+ };
1122
+
1123
+ /**
1124
+ * Fill every [data-pdf-thumbnail] within an optional scope from its
1125
+ * data-pdf-thumbnail-src. Returns the promises, one per element, in
1126
+ * document order. Elements already handled are skipped, so this is safe to
1127
+ * call again after adding markup.
1128
+ */
1129
+ PdfPreview.initThumbnails = function (scope, options) {
1130
+ var context = resolveElement(scope) || document;
1131
+ var elements = context.querySelectorAll(SELECTORS.thumbnail);
1132
+ var promises = [];
1133
+
1134
+ for (var i = 0; i < elements.length; i++) {
1135
+ var element = elements[i];
1136
+
1137
+ if (!element[THUMBNAIL_KEY]) {
1138
+ element[THUMBNAIL_KEY] = PdfPreview.thumbnailInto(element, null, options);
1139
+ // Failures are reported through pdfthumbnail:error and through
1140
+ // the returned promise. This stops a caller that watches the
1141
+ // events instead from logging an unhandled rejection.
1142
+ element[THUMBNAIL_KEY].catch(noop);
1143
+ }
1144
+
1145
+ promises.push(element[THUMBNAIL_KEY]);
1146
+ }
1147
+
1148
+ return promises;
1149
+ };
1150
+
1151
+ /** Existing instance for an element, or null. */
1152
+ PdfPreview.get = function (target) {
1153
+ var element = resolveElement(target);
1154
+
1155
+ return (element && element[INSTANCE_KEY]) || null;
1156
+ };
1157
+
1158
+ /** Initialise every [data-pdf-preview] within an optional scope. */
1159
+ PdfPreview.initAll = function (scope, options) {
1160
+ var context = resolveElement(scope) || document;
1161
+ var elements = context.querySelectorAll(SELECTORS.root);
1162
+ var instances = [];
1163
+
1164
+ for (var i = 0; i < elements.length; i++) {
1165
+ instances.push(new PdfPreview(elements[i], options));
1166
+ }
1167
+
1168
+ return instances;
1169
+ };
1170
+
1171
+ PdfPreview.defaults = DEFAULTS;
1172
+ PdfPreview.thumbnailDefaults = THUMBNAIL_DEFAULTS;
1173
+ PdfPreview.selectors = SELECTORS;
1174
+ PdfPreview.classes = CLASSES;
1175
+
1176
+ return PdfPreview;
1177
+ });