@teipublisher/pb-components 2.26.1-next.3 → 3.0.0-next-4.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 (262) hide show
  1. package/.github/workflows/docker-cypress.yml +54 -0
  2. package/.github/workflows/main.yml +6 -4
  3. package/.github/workflows/node.js.yml +56 -21
  4. package/.github/workflows/release.js.yml +19 -17
  5. package/.releaserc.json +1 -1
  6. package/CHANGELOG.md +346 -11
  7. package/Dockerfile +78 -70
  8. package/README.md +112 -4
  9. package/css/components.css +5 -5
  10. package/css/gridjs/mermaid.min.css +1 -1
  11. package/css/leaflet/Control.Geocoder.css +1 -126
  12. package/css/leaflet/images/layers.png +0 -0
  13. package/css/tify/tify.css +6 -5
  14. package/css/tom-select/tom-select.bootstrap4.min.css +1 -1
  15. package/css/tom-select/tom-select.bootstrap5.min.css +1 -1
  16. package/css/tom-select/tom-select.default.min.css +1 -1
  17. package/css/tom-select/tom-select.default.min.css.map +1 -0
  18. package/css/tom-select/tom-select.min.css +1 -1
  19. package/cypress.config.js +84 -0
  20. package/dist/api.html +1 -1
  21. package/dist/css/design-system.css +607 -0
  22. package/dist/demo/bundle-test.html +4 -3
  23. package/dist/demo/components.css +46 -1
  24. package/dist/demo/design-system.html +710 -0
  25. package/dist/demo/dts-client.html +2 -2
  26. package/dist/demo/pb-autocomplete.html +23 -11
  27. package/dist/demo/pb-autocomplete2.html +66 -55
  28. package/dist/demo/pb-autocomplete3.html +17 -8
  29. package/dist/demo/pb-blacklab-highlight.html +28 -11
  30. package/dist/demo/pb-blacklab-results.html +3 -2
  31. package/dist/demo/pb-browse-docs.html +24 -24
  32. package/dist/demo/pb-browse-docs2.html +3 -3
  33. package/dist/demo/pb-clipboard.html +32 -28
  34. package/dist/demo/pb-code-editor.html +6 -6
  35. package/dist/demo/pb-code-highlight.html +63 -63
  36. package/dist/demo/pb-codepen.html +1 -1
  37. package/dist/demo/pb-collapse.html +1 -1
  38. package/dist/demo/pb-collapse2.html +2 -2
  39. package/dist/demo/pb-combo-box.html +135 -130
  40. package/dist/demo/pb-custom-form.html +64 -55
  41. package/dist/demo/pb-dialog.html +12 -6
  42. package/dist/demo/pb-document.html +1 -1
  43. package/dist/demo/pb-download.html +68 -59
  44. package/dist/demo/pb-drawer.html +67 -46
  45. package/dist/demo/pb-drawer2.html +65 -58
  46. package/dist/demo/pb-edit-app.html +2 -2
  47. package/dist/demo/pb-edit-xml.html +1 -1
  48. package/dist/demo/pb-facsimile-2.html +26 -11
  49. package/dist/demo/pb-facsimile-3.html +25 -10
  50. package/dist/demo/pb-facsimile-dedup-test-2.html +48 -0
  51. package/dist/demo/pb-facsimile-dedup-test.html +48 -0
  52. package/dist/demo/pb-facsimile.html +4 -4
  53. package/dist/demo/pb-formula.html +1 -1
  54. package/dist/demo/pb-grid.html +22 -8
  55. package/dist/demo/pb-highlight.html +2 -2
  56. package/dist/demo/pb-i18n-simple.html +1 -0
  57. package/dist/demo/pb-i18n.html +15 -5
  58. package/dist/demo/pb-image-strip-standalone.html +2 -2
  59. package/dist/demo/pb-image-strip-view.html +2 -2
  60. package/dist/demo/pb-leaflet-map.html +3 -3
  61. package/dist/demo/pb-leaflet-map2.html +2 -2
  62. package/dist/demo/pb-leaflet-map3.html +3 -3
  63. package/dist/demo/pb-link.html +1 -1
  64. package/dist/demo/pb-load.html +2 -6
  65. package/dist/demo/pb-login.html +1 -3
  66. package/dist/demo/pb-manage-odds.html +9 -4
  67. package/dist/demo/pb-markdown.html +1 -1
  68. package/dist/demo/pb-media-query.html +2 -2
  69. package/dist/demo/pb-mei.html +2 -2
  70. package/dist/demo/pb-mei2.html +2 -2
  71. package/dist/demo/pb-message.html +2 -3
  72. package/dist/demo/pb-odd-editor.html +54 -52
  73. package/dist/demo/pb-page-header.html +27 -0
  74. package/dist/demo/pb-popover.html +1 -1
  75. package/dist/demo/pb-print-preview.html +2 -2
  76. package/dist/demo/pb-progress.html +4 -4
  77. package/dist/demo/pb-repeat.html +32 -36
  78. package/dist/demo/pb-search.html +16 -5
  79. package/dist/demo/pb-search2.html +4 -4
  80. package/dist/demo/pb-search3.html +3 -3
  81. package/dist/demo/pb-search4.html +3 -3
  82. package/dist/demo/pb-select-feature.html +4 -4
  83. package/dist/demo/pb-select-feature2.html +4 -4
  84. package/dist/demo/pb-select-feature3.html +2 -2
  85. package/dist/demo/pb-select-i18n.html +58 -53
  86. package/dist/demo/pb-select-odd.html +1 -1
  87. package/dist/demo/pb-select.html +190 -75
  88. package/dist/demo/pb-select2.html +91 -37
  89. package/dist/demo/pb-select3.html +109 -41
  90. package/dist/demo/pb-svg.html +1 -1
  91. package/dist/demo/pb-table-grid.html +26 -15
  92. package/dist/demo/pb-tabs.html +15 -7
  93. package/dist/demo/pb-tify.html +7 -7
  94. package/dist/demo/pb-timeline.html +1 -1
  95. package/dist/demo/pb-timeline2.html +1 -1
  96. package/dist/demo/pb-toggle-feature.html +26 -23
  97. package/dist/demo/pb-toggle-feature2.html +4 -4
  98. package/dist/demo/pb-toggle-feature3.html +2 -2
  99. package/dist/demo/pb-toggle-feature4.html +56 -54
  100. package/dist/demo/pb-version.html +2 -2
  101. package/dist/demo/pb-view.html +78 -40
  102. package/dist/demo/pb-view2.html +69 -46
  103. package/dist/demo/pb-view3.html +53 -48
  104. package/dist/demo/pb-view4.html +70 -49
  105. package/dist/demo/pb-zoom.html +2 -2
  106. package/dist/{es-global-bridge-d8ce175d.js → es-global-bridge-D8ZcUcx_.js} +0 -4
  107. package/dist/focus-mixin-VCsFap6b.js +768 -0
  108. package/dist/images/icons.svg +217 -0
  109. package/dist/jinn-codemirror-DETLdm08.js +1 -0
  110. package/dist/lib/openseadragon.min.js +80 -0
  111. package/dist/lib/openseadragon.min.js.map +1 -0
  112. package/dist/pb-code-editor.js +25 -20
  113. package/dist/pb-component-docs.js +414 -3225
  114. package/dist/pb-components-bundle.js +3046 -4402
  115. package/dist/pb-dialog-tklYGWfc.js +121 -0
  116. package/dist/pb-edit-app.js +208 -107
  117. package/dist/pb-elements.json +716 -249
  118. package/dist/pb-facsimile.js +46 -0
  119. package/dist/pb-i18n-C0NDma4h.js +1 -0
  120. package/dist/pb-leaflet-map.js +23 -23
  121. package/dist/pb-mei.js +152 -134
  122. package/dist/pb-mixin-DHoWQheB.js +1 -0
  123. package/dist/pb-odd-editor.js +1671 -1231
  124. package/dist/pb-tify.js +1 -27
  125. package/dist/unsafe-html-D5VGo9Oq.js +1 -0
  126. package/dist/urls-BEONu_g4.js +1 -0
  127. package/eslint.config.mjs +92 -0
  128. package/gh-pages.js +5 -3
  129. package/i18n/common/en.json +6 -0
  130. package/i18n/common/pl.json +2 -2
  131. package/images/icons.svg +217 -0
  132. package/index.html +0 -5
  133. package/lib/leaflet-src.js.map +1 -0
  134. package/lib/leaflet.markercluster-src.js.map +1 -0
  135. package/lib/openseadragon.min.js +6 -6
  136. package/package.json +56 -81
  137. package/pb-elements.json +716 -249
  138. package/rollup.config.mjs +312 -0
  139. package/src/assets/components.css +5 -5
  140. package/src/assets/design-system.css +607 -0
  141. package/src/authority/airtable.js +20 -21
  142. package/src/authority/anton.js +129 -129
  143. package/src/authority/custom.js +70 -27
  144. package/src/authority/geonames.js +38 -32
  145. package/src/authority/gnd.js +50 -42
  146. package/src/authority/kbga.js +136 -134
  147. package/src/authority/metagrid.js +44 -46
  148. package/src/authority/reconciliation.js +66 -68
  149. package/src/authority/registry.js +4 -4
  150. package/src/docs/demo-utils.js +91 -0
  151. package/src/docs/pb-component-docs.js +287 -147
  152. package/src/docs/pb-component-view.js +380 -273
  153. package/src/docs/pb-components-list.js +115 -51
  154. package/src/docs/pb-demo-snippet.js +199 -174
  155. package/src/dts-client.js +306 -303
  156. package/src/dts-select-endpoint.js +125 -85
  157. package/src/parse-date-service.js +184 -135
  158. package/src/pb-ajax.js +175 -173
  159. package/src/pb-authority-lookup.js +198 -158
  160. package/src/pb-autocomplete.js +731 -313
  161. package/src/pb-blacklab-highlight.js +266 -260
  162. package/src/pb-blacklab-results.js +230 -225
  163. package/src/pb-browse-docs.js +601 -484
  164. package/src/pb-browse.js +68 -65
  165. package/src/pb-clipboard.js +97 -76
  166. package/src/pb-code-editor.js +111 -103
  167. package/src/pb-code-highlight.js +234 -204
  168. package/src/pb-codepen.js +81 -73
  169. package/src/pb-collapse.js +265 -152
  170. package/src/pb-combo-box.js +191 -191
  171. package/src/pb-components-bundle.js +1 -7
  172. package/src/pb-components.js +2 -6
  173. package/src/pb-custom-form.js +230 -141
  174. package/src/pb-dialog.js +99 -63
  175. package/src/pb-document.js +118 -91
  176. package/src/pb-download.js +214 -198
  177. package/src/pb-drawer.js +146 -149
  178. package/src/pb-edit-app.js +471 -240
  179. package/src/pb-edit-xml.js +101 -98
  180. package/src/pb-events.js +126 -107
  181. package/src/pb-facs-link.js +130 -101
  182. package/src/pb-facsimile.js +494 -410
  183. package/src/pb-fetch.js +389 -0
  184. package/src/pb-formula.js +152 -154
  185. package/src/pb-geolocation.js +130 -132
  186. package/src/pb-grid-action.js +59 -56
  187. package/src/pb-grid.js +388 -228
  188. package/src/pb-highlight.js +142 -142
  189. package/src/pb-hotkeys.js +40 -42
  190. package/src/pb-i18n.js +115 -127
  191. package/src/pb-icon-button.js +108 -0
  192. package/src/pb-icon.js +283 -0
  193. package/src/pb-image-strip.js +85 -79
  194. package/src/pb-lang.js +142 -57
  195. package/src/pb-leaflet-map.js +551 -483
  196. package/src/pb-link.js +132 -126
  197. package/src/pb-load.js +495 -428
  198. package/src/pb-login.js +303 -248
  199. package/src/pb-manage-odds.js +384 -338
  200. package/src/pb-map-icon.js +90 -90
  201. package/src/pb-map-layer.js +86 -86
  202. package/src/pb-markdown.js +107 -110
  203. package/src/pb-media-query.js +75 -73
  204. package/src/pb-mei.js +523 -303
  205. package/src/pb-message.js +144 -98
  206. package/src/pb-mixin.js +268 -265
  207. package/src/pb-navigation.js +83 -96
  208. package/src/pb-observable.js +39 -39
  209. package/src/pb-odd-editor.js +1209 -948
  210. package/src/pb-odd-elementspec-editor.js +375 -310
  211. package/src/pb-odd-model-editor.js +1189 -941
  212. package/src/pb-odd-parameter-editor.js +269 -170
  213. package/src/pb-odd-rendition-editor.js +184 -131
  214. package/src/pb-page.js +451 -422
  215. package/src/pb-paginate.js +260 -178
  216. package/src/pb-panel.js +217 -183
  217. package/src/pb-popover-themes.js +16 -9
  218. package/src/pb-popover.js +297 -288
  219. package/src/pb-print-preview.js +128 -128
  220. package/src/pb-progress.js +52 -52
  221. package/src/pb-repeat.js +141 -108
  222. package/src/pb-restricted.js +85 -78
  223. package/src/pb-search.js +258 -230
  224. package/src/pb-select-feature.js +210 -126
  225. package/src/pb-select-odd.js +184 -118
  226. package/src/pb-select-template.js +113 -78
  227. package/src/pb-select.js +330 -229
  228. package/src/pb-split-list.js +181 -176
  229. package/src/pb-svg.js +81 -80
  230. package/src/pb-table-column.js +55 -55
  231. package/src/pb-table-grid.js +334 -205
  232. package/src/pb-tabs.js +238 -61
  233. package/src/pb-tify.js +3331 -126
  234. package/src/pb-timeline.js +394 -255
  235. package/src/pb-toggle-feature.js +196 -188
  236. package/src/pb-upload.js +201 -176
  237. package/src/pb-version.js +22 -34
  238. package/src/pb-view-annotate.js +138 -102
  239. package/src/pb-view.js +1722 -1272
  240. package/src/pb-zoom.js +144 -46
  241. package/src/search-result-service.js +256 -223
  242. package/src/seed-element.js +14 -22
  243. package/src/settings.js +4 -4
  244. package/src/theming.js +98 -91
  245. package/src/urls.js +403 -289
  246. package/src/utils.js +53 -51
  247. package/vite.config.js +86 -0
  248. package/css/pb-styles.css +0 -51
  249. package/dist/iron-form-3b8dcaa7.js +0 -210
  250. package/dist/jinn-codemirror-da0e2d1f.js +0 -1
  251. package/dist/paper-checkbox-515a5284.js +0 -1597
  252. package/dist/paper-icon-button-b1d31571.js +0 -398
  253. package/dist/paper-listbox-a3b7175c.js +0 -1265
  254. package/dist/pb-i18n-0611135a.js +0 -1
  255. package/dist/pb-mixin-b1caa22e.js +0 -158
  256. package/dist/polymer-hack.js +0 -1
  257. package/dist/vaadin-element-mixin-fe4a4883.js +0 -527
  258. package/lib/Control.Geocoder.min.js +0 -2
  259. package/lib/Control.Geocoder.min.js.map +0 -1
  260. package/src/assets/pb-styles.css +0 -51
  261. package/src/pb-light-dom.js +0 -41
  262. package/src/polymer-hack.js +0 -6
@@ -1,5 +1,5 @@
1
- import { LitElement, html, css } from 'lit-element';
2
- import "@lrnwebcomponents/es-global-bridge";
1
+ import { LitElement, html, css } from 'lit';
2
+ import '@lrnwebcomponents/es-global-bridge';
3
3
  import { pbMixin } from './pb-mixin.js';
4
4
  import { resolveURL } from './utils.js';
5
5
 
@@ -24,445 +24,529 @@ import { resolveURL } from './utils.js';
24
24
  * @slot after - use for content which should be shown below the facsimile viewer
25
25
  */
26
26
  export class PbFacsimile extends pbMixin(LitElement) {
27
- static get properties() {
28
- return {
29
- ...super.properties,
30
- /**
31
- * Set to false to prevent the appearance of the default navigation controls.
32
- * Note that if set to false, the customs buttons set by the options
33
- * zoomInButton, zoomOutButton etc, are rendered inactive.
34
- */
35
- showNavigationControl: {
36
- type: Boolean,
37
- attribute: 'show-navigation-control'
38
- },
39
- // Set to true to make the navigator minimap appear.
40
- showNavigator: {
41
- type: Boolean,
42
- attribute: 'show-navigator'
43
- },
44
-
45
- /** If true then the 'previous" and 'next' button is displayed switch between images. */
46
- showSequenceMode: {
47
- type: Boolean,
48
- attribute: 'show-sequence-control'
49
- },
50
-
51
- /** If true then the 'Go home' button is displayed to go back to the original zoom and pan. */
52
- showHomeControl: {
53
- type: Boolean,
54
- attribute: 'show-home-control'
55
- },
56
- /** If true then the 'Toggle full page' button is displayed to switch between full page and normal mode. */
57
- showFullPageControl: {
58
- type: Boolean,
59
- attribute: 'show-full-page-control'
60
- },
61
- /**
62
- * if true shows a 'download' button
63
- */
64
- showDownloadButton:{
65
- type: Boolean,
66
- attribute: 'show-download-control'
67
- },
68
- /**
69
- * Default zoom between: set to 0 to adjust to viewer size.
70
- */
71
- defaultZoomLevel: {
72
- type: Number,
73
- attribute: 'default-zoom-level'
74
- },
75
- /**
76
- * If true then the rotate left/right controls will be displayed
77
- * as part of the standard controls. This is also subject to the
78
- * browser support for rotate (e.g. viewer.drawer.canRotate()).
79
- */
80
- showRotationControl: {
81
- type: Boolean,
82
- attribute: 'show-rotation-control'
83
- },
84
- // Constrain during pan
85
- constrainDuringPan: {
86
- type: Boolean,
87
- attribute: 'contrain-during-pan'
88
- },
89
- /**
90
- * The percentage ( as a number from 0 to 1 ) of the source image
91
- * which must be kept within the viewport.
92
- * If the image is dragged beyond that limit, it will 'bounce'
93
- * back until the minimum visibility ratio is achieved.
94
- * Setting this to 0 and wrapHorizontal ( or wrapVertical )
95
- * to true will provide the effect of an infinitely scrolling viewport.
96
- */
97
- visibilityRatio: {
98
- type: Number,
99
- attribute: 'visibility-ratio'
100
- },
101
- /**
102
- * If set, thumbnails of all images are shown in a reference strip at the
103
- * bottom of the viewer.
104
- */
105
- referenceStrip: {
106
- type: Boolean,
107
- attribute: 'reference-strip'
108
- },
109
- /**
110
- * Size ratio for the reference strip thumbnails. 0.2 by default.
111
- */
112
- referenceStripSizeRatio: {
113
- type: Number,
114
- attribute: 'reference-strip-size-ratio'
115
- },
116
- /**
117
- * Type of the source of the image to display: either 'iiif' or 'image'
118
- * (for simple image links not served via IIIF).
119
- */
120
- type: {
121
- type: String
122
- },
123
- baseUri: {
124
- type: String,
125
- attribute: 'base-uri'
126
- },
127
- /**
128
- * Path pointing to the location of openseadragon user interface images.
129
- */
130
- prefixUrl: {
131
- type: String,
132
- attribute: 'prefix-url'
133
- },
134
- /**
135
- * Array of facsimiles
136
- *
137
- */
138
- facsimiles: {
139
- type: Array
140
- },
141
- /**
142
- * Will be true if images were loaded for display, false if there are no images
143
- * to show.
144
- */
145
- loaded: {
146
- type: Boolean,
147
- reflect: true
148
- },
149
- /**
150
- * CORS (Cross-Origin Resource Sharing) policy - wraps the OSD Viewer option -
151
- * only sensible values are 'anonymous' (default) or 'use-credentials'.
152
- */
153
- crossOriginPolicy:{
154
- type: String,
155
- attribute: 'cors'
156
- }
157
- };
27
+ static get properties() {
28
+ return {
29
+ ...super.properties,
30
+ /**
31
+ * Set to false to prevent the appearance of the default navigation controls.
32
+ * Note that if set to false, the customs buttons set by the options
33
+ * zoomInButton, zoomOutButton etc, are rendered inactive.
34
+ */
35
+ showNavigationControl: {
36
+ type: Boolean,
37
+ attribute: 'show-navigation-control',
38
+ },
39
+ // Set to true to make the navigator minimap appear.
40
+ showNavigator: {
41
+ type: Boolean,
42
+ attribute: 'show-navigator',
43
+ },
44
+
45
+ /** If true then the 'previous" and 'next' button is displayed switch between images. */
46
+ showSequenceMode: {
47
+ type: Boolean,
48
+ attribute: 'show-sequence-control',
49
+ },
50
+
51
+ /** If true then the 'Go home' button is displayed to go back to the original zoom and pan. */
52
+ showHomeControl: {
53
+ type: Boolean,
54
+ attribute: 'show-home-control',
55
+ },
56
+ /** If true then the 'Toggle full page' button is displayed to switch between full page and normal mode. */
57
+ showFullPageControl: {
58
+ type: Boolean,
59
+ attribute: 'show-full-page-control',
60
+ },
61
+ /**
62
+ * if true shows a 'download' button
63
+ */
64
+ showDownloadButton: {
65
+ type: Boolean,
66
+ attribute: 'show-download-control',
67
+ },
68
+ /**
69
+ * Default zoom between: set to 0 to adjust to viewer size.
70
+ */
71
+ defaultZoomLevel: {
72
+ type: Number,
73
+ attribute: 'default-zoom-level',
74
+ },
75
+ /**
76
+ * If true then the rotate left/right controls will be displayed
77
+ * as part of the standard controls. This is also subject to the
78
+ * browser support for rotate (e.g. viewer.drawer.canRotate()).
79
+ */
80
+ showRotationControl: {
81
+ type: Boolean,
82
+ attribute: 'show-rotation-control',
83
+ },
84
+ // Constrain during pan
85
+ constrainDuringPan: {
86
+ type: Boolean,
87
+ attribute: 'contrain-during-pan',
88
+ },
89
+ /**
90
+ * The percentage ( as a number from 0 to 1 ) of the source image
91
+ * which must be kept within the viewport.
92
+ * If the image is dragged beyond that limit, it will 'bounce'
93
+ * back until the minimum visibility ratio is achieved.
94
+ * Setting this to 0 and wrapHorizontal ( or wrapVertical )
95
+ * to true will provide the effect of an infinitely scrolling viewport.
96
+ */
97
+ visibilityRatio: {
98
+ type: Number,
99
+ attribute: 'visibility-ratio',
100
+ },
101
+ /**
102
+ * If set, thumbnails of all images are shown in a reference strip at the
103
+ * bottom of the viewer.
104
+ */
105
+ referenceStrip: {
106
+ type: Boolean,
107
+ attribute: 'reference-strip',
108
+ },
109
+ /**
110
+ * Size ratio for the reference strip thumbnails. 0.2 by default.
111
+ */
112
+ referenceStripSizeRatio: {
113
+ type: Number,
114
+ attribute: 'reference-strip-size-ratio',
115
+ },
116
+ /**
117
+ * Type of the source of the image to display: either 'iiif' or 'image'
118
+ * (for simple image links not served via IIIF).
119
+ */
120
+ type: {
121
+ type: String,
122
+ },
123
+ baseUri: {
124
+ type: String,
125
+ attribute: 'base-uri',
126
+ },
127
+ /**
128
+ * Path pointing to the location of openseadragon user interface images.
129
+ */
130
+ prefixUrl: {
131
+ type: String,
132
+ attribute: 'prefix-url',
133
+ },
134
+ /**
135
+ * Array of facsimiles
136
+ *
137
+ */
138
+ facsimiles: {
139
+ type: Array,
140
+ },
141
+ /**
142
+ * Will be true if images were loaded for display, false if there are no images
143
+ * to show.
144
+ */
145
+ loaded: {
146
+ type: Boolean,
147
+ reflect: true,
148
+ },
149
+ /**
150
+ * CORS (Cross-Origin Resource Sharing) policy - wraps the OSD Viewer option -
151
+ * only sensible values are 'anonymous' (default) or 'use-credentials'.
152
+ */
153
+ crossOriginPolicy: {
154
+ type: String,
155
+ attribute: 'cors',
156
+ },
157
+ };
158
+ }
159
+
160
+ constructor() {
161
+ super();
162
+ this._facsimiles = [];
163
+ this.baseUri = '';
164
+ this.crossOriginPolicy = 'anonymous';
165
+ this.type = 'iiif';
166
+ this.visibilityRatio = 1;
167
+ this.defaultZoomLevel = 0;
168
+ this.sequenceMode = false;
169
+ this.showHomeControl = false;
170
+ this.showNavigator = false;
171
+ this.showNavigationControl = false;
172
+ this.showFullPageControl = false;
173
+ this.showRotationControl = false;
174
+ this.showDownloadButton = false;
175
+ this.constrainDuringPan = false;
176
+ this.referenceStrip = false;
177
+ this.referenceStripSizeRatio = 0.2;
178
+ this.prefixUrl = '../images/openseadragon/';
179
+ this.loaded = false;
180
+ this._facsimileObserverScheduled = false;
181
+ }
182
+
183
+ set facsimiles(facs) {
184
+ this._facsimiles = facs || [];
185
+ this.loaded = this._facsimiles.length > 0;
186
+ this.emitTo('pb-facsimile-status', { status: 'loading' });
187
+ }
188
+
189
+ connectedCallback() {
190
+ super.connectedCallback();
191
+ this.subscribeTo('pb-start-update', this._clearAll.bind(this));
192
+ this.subscribeTo('pb-load-facsimile', e => {
193
+ const { element, order } = e.detail;
194
+ const itemOrder = this._facsimiles.map(item =>
195
+ item.getOrder ? item.getOrder() : Number.POSITIVE_INFINITY,
196
+ );
197
+ const insertAt = itemOrder.reduce((result, next, index) => {
198
+ if (order < next) return result;
199
+ if (order === next) return index;
200
+ return index + 1;
201
+ }, 0);
202
+
203
+ this._facsimiles.splice(insertAt, 0, element);
204
+ this.loaded = this._facsimiles.length > 0;
205
+
206
+ this._scheduleFacsimileObserver();
207
+ });
208
+ this.subscribeTo('pb-show-annotation', this._showAnnotationListener.bind(this));
209
+ }
210
+
211
+ firstUpdated() {
212
+ try {
213
+ const bridge = window.ESGlobalBridge.requestAvailability();
214
+ const path = resolveURL('../lib/openseadragon.min.js');
215
+ // check if OpenSeadragon is already loaded
216
+ if (bridge.imports['openseadragon']) {
217
+ this._initOpenSeadragon();
218
+ return;
219
+ }
220
+ // Wait for OpenSeadragon to load
221
+ window.addEventListener(
222
+ 'es-bridge-openseadragon-loaded',
223
+ this._initOpenSeadragon.bind(this),
224
+ { once: true },
225
+ );
226
+ // load OpenSeadragon
227
+ bridge.load('openseadragon', path);
228
+ } catch (error) {
229
+ console.error(error.message);
158
230
  }
159
-
160
- constructor() {
161
- super();
162
- this._facsimiles = [];
163
- this.baseUri = '';
164
- this.crossOriginPolicy = 'anonymous';
165
- this.type = 'iiif';
166
- this.visibilityRatio = 1;
167
- this.defaultZoomLevel = 0;
168
- this.sequenceMode = false;
169
- this.showHomeControl = false;
170
- this.showNavigator = false;
171
- this.showNavigationControl = false;
172
- this.showFullPageControl = false;
173
- this.showRotationControl = false;
174
- this.showDownloadButton = false;
175
- this.constrainDuringPan = false;
176
- this.referenceStrip = false;
177
- this.referenceStripSizeRatio = 0.2;
178
- this.prefixUrl = '../images/openseadragon/';
179
- this.loaded = false;
180
- }
181
-
182
- set facsimiles(facs) {
183
- this._facsimiles = facs || [];
184
- this.loaded = this._facsimiles.length > 0;
185
- this.emitTo('pb-facsimile-status', { status: 'loading' });
186
- }
187
-
188
- connectedCallback() {
189
- super.connectedCallback();
190
- this.subscribeTo('pb-start-update', this._clearAll.bind(this));
191
- this.subscribeTo('pb-load-facsimile', (e) => {
192
- const { element, order } = e.detail
193
- const itemOrder = this._facsimiles.map(item => item.getOrder ? item.getOrder() : Number.POSITIVE_INFINITY )
194
- const insertAt = itemOrder.reduce((result, next, index) => {
195
- if (order < next) return result;
196
- if (order === next) return index;
197
- return index + 1;
198
- }, 0)
199
-
200
- this._facsimiles.splice(insertAt, 0, element)
201
- this.loaded = this._facsimiles.length > 0;
202
-
203
- this._facsimileObserver()
204
- });
205
- this.subscribeTo('pb-show-annotation', this._showAnnotationListener.bind(this));
231
+ }
232
+
233
+ render() {
234
+ return html`
235
+ <slot name="before"></slot>
236
+ <!-- Openseadragon -->
237
+
238
+ <div id="viewer" part="image"></div>
239
+ <slot name="after"></slot>
240
+ ${this.showDownloadButton ? html`<a id="downloadBtn" title="Download">&#8676;</a>` : ''}
241
+ `;
242
+ }
243
+
244
+ static get styles() {
245
+ return css`
246
+ :host {
247
+ display: flex;
248
+ flex-direction: column;
249
+ position: relative;
250
+ background: transparent;
251
+ }
252
+
253
+ #runtime-overlay {
254
+ border: var(--pb-facsimile-border, 4px solid rgba(0, 0, 128, 0.5));
255
+ }
256
+
257
+ #viewer {
258
+ flex: 1;
259
+ position: relative;
260
+ max-height: var(--pb-facsimile-height, auto);
261
+ width: 100%;
262
+ }
263
+ #downloadBtn {
264
+ position: absolute;
265
+ z-index: 100;
266
+ bottom: 0.25rem;
267
+ width: 1.35rem;
268
+ height: 1.35rem;
269
+ transform: rotate(-90deg);
270
+ cursor: pointer;
271
+ border: thin solid #d7dde8;
272
+ display: flex;
273
+ align-items: center;
274
+ justify-content: center;
275
+ border-radius: 0.75rem;
276
+ background-image: linear-gradient(to left, #fafafa 0%, #d7dde8 51%, #bbbbbb 100%);
277
+ font-size: 1.2rem;
278
+ box-shadow: -2px 1px 5px 0px rgba(0, 0, 0, 0.75);
279
+ }
280
+ #downloadBtn:hover {
281
+ background-image: radial-gradient(white, #efefef);
282
+ }
283
+ `;
284
+ }
285
+
286
+ // Init openseadragon
287
+ _initOpenSeadragon() {
288
+ const prefixUrl = resolveURL(this.prefixUrl + (this.prefixUrl.endsWith('/') ? '' : '/'));
289
+ const options = {
290
+ element: this.shadowRoot.getElementById('viewer'),
291
+ prefixUrl,
292
+ preserveViewport: true,
293
+ showZoomControl: true,
294
+ sequenceMode: this.showSequenceMode,
295
+ showHomeControl: this.showHomeControl,
296
+ showFullPageControl: this.showFullPageControl,
297
+ showNavigator: this.showNavigator,
298
+ showNavigationControl: this.showNavigationControl,
299
+ showRotationControl: this.showRotationControl,
300
+ autoHideControls: false,
301
+ visibilityRatio: 1,
302
+ minZoomLevel: 1,
303
+ defaultZoomLevel: this.defaultZoomLevel,
304
+ constrainDuringPan: true,
305
+ crossOriginPolicy: this.crossOriginPolicy,
306
+ };
307
+
308
+ if (this.referenceStrip) {
309
+ options.showReferenceStrip = true;
310
+ options.referenceStripSizeRatio = this.referenceStripSizeRatio;
206
311
  }
312
+ this.viewer = OpenSeadragon(options);
207
313
 
208
- firstUpdated() {
209
- try{
210
- window.ESGlobalBridge.requestAvailability();
211
- const path = resolveURL('../lib/openseadragon.min.js');
212
- window.ESGlobalBridge.instance.load("openseadragon", path);
213
- window.addEventListener(
214
- "es-bridge-openseadragon-loaded",
215
- this._initOpenSeadragon.bind(this),
216
- { once: true }
217
- );
218
- } catch (error){
219
- console.error(error.message);
220
- }
221
- }
222
-
223
- render() {
224
- return html`
225
- <slot name="before"></slot>
226
- <!-- Openseadragon -->
227
-
228
- <div id="viewer" part="image"></div>
229
- <slot name="after"></slot>
230
- ${this.showDownloadButton ?
231
- html`<a id="downloadBtn" title="Download">&#8676;</a>`:''
232
- }
233
-
234
- `;
314
+ if (this.showFullPageControl) {
315
+ this._overrideFullscreenButton();
235
316
  }
236
317
 
237
- static get styles() {
238
- return css`
239
- :host {
240
- display: flex;
241
- flex-direction: column;
242
- position: relative;
243
- background: transparent;
244
- }
245
-
246
- #runtime-overlay {
247
- border: var(--pb-facsimile-border, 4px solid rgba(0, 0, 128, 0.5));
248
- }
249
-
250
- #viewer {
251
- flex: 1;
252
- position: relative;
253
- max-height: var(--pb-facsimile-height, auto);
254
- width: 100%;
255
- }
256
- #downloadBtn{
257
- position: absolute;
258
- z-index: 100;
259
- bottom:0.25rem;
260
- width:1.35rem;
261
- height:1.35rem;
262
- transform:rotate(-90deg);
263
- cursor: pointer;
264
- border: thin solid #D7DDE8;
265
- display: flex;
266
- align-items: center;
267
- justify-content: center;
268
- border-radius:0.75rem;
269
- background-image:linear-gradient(to left, #fafafa 0%, #D7DDE8 51%, #bbbbbb 100%);
270
- font-size:1.2rem;
271
- box-shadow: -2px 1px 5px 0px rgba(0,0,0,0.75);
272
- }
273
- #downloadBtn:hover{
274
- background-image:radial-gradient( white, #efefef);
275
- }
276
- `;
318
+ this.viewer.addHandler('open', () => {
319
+ this.resetZoom();
320
+ this.emitTo('pb-facsimile-status', { status: 'loaded', facsimiles: this._facsimiles });
321
+ });
322
+ this.viewer.addHandler('open-failed', ev => {
323
+ console.error('<pb-facsimile> open failed: %s', ev.message);
324
+ this.loaded = false;
325
+ this.emitTo('pb-facsimile-status', { status: 'fail' });
326
+ });
327
+
328
+ const download = this.shadowRoot.querySelector('#downloadBtn');
329
+ if (this.showDownloadButton) {
330
+ download.addEventListener('click', ev => {
331
+ ev.preventDefault();
332
+ const currentImage = this.viewer.drawer.canvas.toDataURL('image/png');
333
+ const downloadLink = document.createElement('a');
334
+ downloadLink.href = currentImage;
335
+ downloadLink.download = 'download';
336
+ downloadLink.click();
337
+ });
277
338
  }
278
339
 
279
- // Init openseadragon
280
- _initOpenSeadragon() {
281
- const prefixUrl = resolveURL(this.prefixUrl + (this.prefixUrl.endsWith("/") ? "" : "/"));
282
- const options = {
283
- element: this.shadowRoot.getElementById('viewer'),
284
- prefixUrl,
285
- preserveViewport: true,
286
- showZoomControl: true,
287
- sequenceMode: this.showSequenceMode,
288
- showHomeControl: this.showHomeControl,
289
- showFullPageControl: this.showFullPageControl,
290
- showNavigator: this.showNavigator,
291
- showNavigationControl: this.showNavigationControl,
292
- showRotationControl: this.showRotationControl,
293
- autoHideControls: false,
294
- visibilityRatio: 1,
295
- minZoomLevel: 1,
296
- defaultZoomLevel: this.defaultZoomLevel,
297
- constrainDuringPan: true,
298
- crossOriginPolicy: this.crossOriginPolicy
299
- };
300
-
301
- if (this.referenceStrip) {
302
- options.showReferenceStrip = true;
303
- options.referenceStripSizeRatio = this.referenceStripSizeRatio;
304
- }
305
- this.viewer = OpenSeadragon(options);
306
-
307
- this.viewer.addHandler('open', () => {
308
- this.resetZoom();
309
- this.emitTo('pb-facsimile-status', { status: 'loaded', facsimiles: this._facsimiles });
310
- });
311
- this.viewer.addHandler('open-failed', (ev) => {
312
- console.error('<pb-facsimile> open failed: %s', ev.message);
313
- this.loaded = false;
314
- this.emitTo('pb-facsimile-status', { status: 'fail' });
315
- });
316
-
317
- const download = this.shadowRoot.querySelector('#downloadBtn');
318
- if(this.showDownloadButton){
319
- download.addEventListener('click', (ev) => {
320
- ev.preventDefault();
321
- const currentImage = this.viewer.drawer.canvas.toDataURL("image/png");
322
- const downloadLink = document.createElement('a');
323
- downloadLink.href = currentImage;
324
- downloadLink.download = 'download';
325
- downloadLink.click();
326
- });
327
- }
328
-
329
- /*
340
+ /*
330
341
  handling of full-screen view requires to hide/unhide the content of body to allow full screen viewer
331
342
  to full-page functionality. Standard OSD completely deletes all body children disconnecting all event-handlers
332
343
  that have been there. This solution just uses style.display to hide/show. Former display value of pb-page
333
344
  will be preserved.
334
345
  */
335
- this.ownerPage = document.querySelector('pb-page');
336
- if(this.ownerPage){
337
- this.pbPageDisplay = window.getComputedStyle(this.ownerPage).getPropertyValue('display');
338
- this.viewer.addHandler('full-screen', (ev) => {
339
- if(ev.fullScreen){
340
- this.ownerPage.style.display = 'none';
341
- }else{
342
- this.viewer.clearOverlays();
343
- this.emitTo('pb-refresh');
344
- this.ownerPage.style.display = this.pbPageDisplay;
345
- }
346
- });
346
+ this.ownerPage = document.querySelector('pb-page');
347
+ if (this.ownerPage) {
348
+ this.pbPageDisplay = window.getComputedStyle(this.ownerPage).getPropertyValue('display');
349
+ this.viewer.addHandler('full-screen', ev => {
350
+ if (ev.fullScreen) {
351
+ this.ownerPage.style.display = 'none';
352
+ } else {
353
+ this.viewer.clearOverlays();
354
+ this.emitTo('pb-refresh');
355
+ this.ownerPage.style.display = this.pbPageDisplay;
347
356
  }
348
- this._facsimileObserver();
349
-
350
- this.signalReady();
357
+ });
351
358
  }
352
-
353
- _facsimileObserver() {
354
- if (!this.viewer) {
355
- return;
356
- }
357
- if (this._facsimiles.length === 0) {
358
- return this.viewer.close()
359
- }
360
- const uris = this._facsimiles.map(facsLink => {
361
- const url = this.baseUri + (facsLink.getImage ? facsLink.getImage() : facsLink)
362
- if (this.type === 'iiif') {
363
- return `${url}/info.json`;
364
- }
365
- return {
366
- tileSource: {
367
- type: 'image',
368
- url,
369
- buildPyramid: false
370
- }
371
- }
372
- });
373
-
374
- this.viewer.open(uris)
375
- this.viewer.goToPage(0)
359
+ this._scheduleFacsimileObserver();
360
+ console.log('facsimile ready');
361
+ this.signalReady();
362
+ }
363
+
364
+ /**
365
+ * A single transcription can have tons of pb-facs-link elements. Always debounce loading these
366
+ */
367
+ _scheduleFacsimileObserver() {
368
+ if (this._facsimileObserverScheduled) {
369
+ return;
376
370
  }
377
-
378
- _clearAll() {
379
- if (!this.viewer) {
380
- return;
381
- }
382
- this.resetZoom();
383
- this.viewer.clearOverlays();
384
- this.facsimiles = [];
371
+ this._facsimileObserverScheduled = true;
372
+ setTimeout(() => {
373
+ this._facsimileObserverScheduled = false;
374
+ this._facsimileObserver();
375
+ }, 0);
376
+ }
377
+
378
+ _facsimileObserver() {
379
+ if (!this.viewer) {
380
+ return;
385
381
  }
382
+ if (this._facsimiles.length === 0) {
383
+ this.viewer.close();
384
+ return;
385
+ }
386
+
387
+ // Limit the number of facsimiles to prevent runaway requests
388
+ // Only load the first 10 facsimiles to avoid server overload
389
+ const maxFacsimiles = 10;
390
+ const limitedFacsimiles = this._facsimiles.slice(0, maxFacsimiles);
391
+
392
+ const uris = limitedFacsimiles.map(facsLink => {
393
+ const url = this.baseUri + (facsLink.getImage ? facsLink.getImage() : facsLink);
394
+ if (this.type === 'iiif') {
395
+ return `${url}/info.json`;
396
+ }
397
+ return {
398
+ tileSource: {
399
+ type: 'image',
400
+ url,
401
+ buildPyramid: false,
402
+ },
403
+ };
404
+ });
405
+
406
+ // Deduplicate URIs to prevent duplicate network requests
407
+ // If multiple pb-facs-link elements point to the same image, only request it once
408
+ const deduplicatedUris = [];
409
+ const uriSet = new Set();
410
+ for (const uri of uris) {
411
+ const hashKey = JSON.stringify(uri);
412
+ if (!uriSet.has(hashKey)) {
413
+ uriSet.add(hashKey);
414
+ deduplicatedUris.push(uri);
415
+ }
416
+ }
417
+ this.viewer.open(deduplicatedUris);
418
+ this.viewer.goToPage(0);
419
+ }
386
420
 
387
- _showAnnotationListener(event) {
388
- if (!this.viewer) {
389
- return;
390
- }
391
- const overlayId = 'runtime-overlay'
421
+ _clearAll() {
422
+ if (!this.viewer) {
423
+ return;
424
+ }
425
+ this.resetZoom();
426
+ this.viewer.clearOverlays();
427
+ this.facsimiles = [];
428
+ }
429
+
430
+ _showAnnotationListener(event) {
431
+ if (!this.viewer) {
432
+ return;
433
+ }
434
+ const overlayId = 'runtime-overlay';
392
435
 
393
- // remove old overlay
436
+ // remove old overlay
437
+ if (this.overlay) {
438
+ try {
394
439
  this.viewer.removeOverlay(this.overlay);
440
+ } catch (e) {
441
+ // ignore if overlay was already removed or viewer not ready
442
+ }
443
+ }
395
444
 
396
- // check event data for completeness
397
- if (!event.detail.file || event.detail.file === 0) {
398
- return console.error('file missing', event.detail)
399
- }
400
-
401
- if (
402
- event.detail.coordinates &&
403
- (!event.detail.coordinates[0] ||
404
- event.detail.coordinates.length !== 4)
405
- ) {
406
- return console.error('coords incomplete or missing', event.detail)
407
- }
408
-
409
- // find page to show
410
- const page = event.detail.element ? this._pageByElement(event.detail.element) : this._pageIndexByUrl(event.detail.file);
445
+ // check event data for completeness
446
+ if (!event.detail.file || event.detail.file === 0) {
447
+ return console.error('file missing', event.detail);
448
+ }
411
449
 
412
- if (page < 0) {
413
- return console.error('page not found', event.detail)
414
- }
450
+ if (
451
+ event.detail.coordinates &&
452
+ (!event.detail.coordinates[0] || event.detail.coordinates.length !== 4)
453
+ ) {
454
+ return console.error('coords incomplete or missing', event.detail);
455
+ }
415
456
 
416
- if (this.viewer.currentPage() !== page) {
417
- this.viewer.goToPage(page);
418
- }
457
+ // find page to show
458
+ const page = event.detail.element
459
+ ? this._pageByElement(event.detail.element)
460
+ : this._pageIndexByUrl(event.detail.file);
419
461
 
420
- if (event.detail.coordinates) {
421
- // deconstruct given coordinates into variables
422
- const [x1, y1, w, h] = event.detail.coordinates;
423
- const tiledImage = this.viewer.world.getItemAt(0);
424
- const currentRect = tiledImage.viewportToImageRectangle(
425
- tiledImage.getBounds(true));
426
-
427
- // scroll into view?
428
- if (!currentRect.containsPoint(new OpenSeadragon.Point(x1, y1))) {
429
- this.viewer.viewport.fitBoundsWithConstraints(
430
- tiledImage.imageToViewportRectangle(x1, y1, currentRect.width, currentRect.height));
431
- }
432
-
433
- // create new overlay
434
- const overlay = document.createElement('div');
435
- this.overlay = overlay
436
- overlay.id = overlayId;
437
-
438
- // place marker
439
- const marker = tiledImage.imageToViewportRectangle(x1, y1, w, h);
440
-
441
- this.viewer.addOverlay({
442
- element: overlay,
443
- location: marker
444
- });
445
- }
462
+ if (page < 0) {
463
+ return console.error('page not found', event.detail);
446
464
  }
447
465
 
448
- _pageByElement(element) {
449
- return this._facsimiles.indexOf(element);
466
+ if (this.viewer.currentPage() !== page) {
467
+ this.viewer.goToPage(page);
450
468
  }
451
469
 
452
- _pageIndexByUrl(file) {
453
- return this._facsimiles.findIndex(element => element.getImage() === file);
470
+ if (event.detail.coordinates) {
471
+ // deconstruct given coordinates into variables
472
+ const [x1, y1, w, h] = event.detail.coordinates;
473
+ const tiledImage = this.viewer.world.getItemAt(0);
474
+ if (!tiledImage) {
475
+ console.error('No tiled image available yet for annotation.');
476
+ return;
477
+ }
478
+ const currentRect = tiledImage.viewportToImageRectangle(tiledImage.getBounds(true));
479
+
480
+ // scroll into view?
481
+ if (!currentRect.containsPoint(new OpenSeadragon.Point(x1, y1))) {
482
+ this.viewer.viewport.fitBoundsWithConstraints(
483
+ tiledImage.imageToViewportRectangle(x1, y1, currentRect.width, currentRect.height),
484
+ );
485
+ }
486
+
487
+ // create new overlay
488
+ const overlay = document.createElement('div');
489
+ this.overlay = overlay;
490
+ overlay.id = overlayId;
491
+
492
+ // place marker
493
+ const marker = tiledImage.imageToViewportRectangle(x1, y1, w, h);
494
+
495
+ this.viewer.addOverlay({
496
+ element: overlay,
497
+ location: marker,
498
+ });
454
499
  }
500
+ }
455
501
 
456
- // reset zoom
457
- resetZoom() {
458
- if (!this.viewer) {
459
- return;
460
- }
461
- this.viewer.viewport.goHome();
462
- }
502
+ _pageByElement(element) {
503
+ return this._facsimiles.indexOf(element);
504
+ }
463
505
 
506
+ _pageIndexByUrl(file) {
507
+ return this._facsimiles.findIndex(element => element.getImage() === file);
508
+ }
464
509
 
510
+ // reset zoom
511
+ resetZoom() {
512
+ if (!this.viewer) {
513
+ return;
514
+ }
515
+ this.viewer.viewport.goHome();
516
+ }
517
+
518
+ _overrideFullscreenButton() {
519
+ // The full page control in OSD makes all body elements disappear, causing all kinds of bugs in
520
+ // our webcomponents. Replace it with something that just calls `requestFullScreen` instead.
521
+ // This approach (but more elaborate) is also PR-ed to OSD
522
+ // (https://github.com/openseadragon/openseadragon/pull/2786). This code can be dropped the
523
+ // moment we upgrade to OSD.
524
+
525
+ const toggleFullscreenButton = this.viewer.buttonGroup.buttons.find(
526
+ button => button.tooltip === 'Toggle full page',
527
+ );
528
+ if (!toggleFullscreenButton) {
529
+ return;
530
+ }
531
+ const releaseHandler = async () => {
532
+ if (!document.fullscreenElement) {
533
+ await this.viewer.element.requestFullscreen();
534
+ return;
535
+ }
536
+ await document.exitFullscreen();
537
+ };
538
+
539
+ toggleFullscreenButton.onRelease = releaseHandler;
540
+ const oldRaiseEvent = toggleFullscreenButton.raiseEvent;
541
+ toggleFullscreenButton.raiseEvent = (eventName, args) => {
542
+ if (eventName === 'release') {
543
+ releaseHandler();
544
+ } else {
545
+ oldRaiseEvent.call(toggleFullscreenButton, eventName, args);
546
+ }
547
+ };
548
+ }
465
549
  }
466
550
  if (!customElements.get('pb-facsimile')) {
467
- customElements.define('pb-facsimile', PbFacsimile);
551
+ customElements.define('pb-facsimile', PbFacsimile);
468
552
  }