@teipublisher/pb-components 2.26.1-next.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/.github/workflows/main.yml +3 -3
  2. package/.github/workflows/node.js.yml +3 -3
  3. package/.github/workflows/release.js.yml +4 -4
  4. package/.releaserc.json +2 -2
  5. package/CHANGELOG.md +262 -11
  6. package/Dockerfile +78 -70
  7. package/css/components.css +5 -5
  8. package/css/leaflet/images/layers.png +0 -0
  9. package/dist/demo/components.css +46 -1
  10. package/dist/demo/pb-browse-docs2.html +1 -1
  11. package/dist/demo/pb-dialog.html +3 -5
  12. package/dist/demo/pb-drawer2.html +1 -1
  13. package/dist/demo/pb-facsimile.html +2 -2
  14. package/dist/demo/pb-grid.html +19 -6
  15. package/dist/demo/pb-leaflet-map.html +1 -1
  16. package/dist/demo/pb-login.html +0 -2
  17. package/dist/demo/pb-message.html +1 -2
  18. package/dist/demo/pb-progress.html +2 -2
  19. package/dist/demo/pb-repeat.html +1 -3
  20. package/dist/demo/pb-search.html +7 -4
  21. package/dist/demo/pb-search3.html +1 -1
  22. package/dist/demo/pb-search4.html +2 -2
  23. package/dist/demo/pb-view3.html +1 -1
  24. package/dist/{iron-form-3b8dcaa7.js → iron-form-dfb3e3b1.js} +95 -95
  25. package/dist/paper-checkbox-645e1077.js +200 -0
  26. package/dist/{paper-icon-button-b1d31571.js → paper-icon-button-984162bd.js} +1 -1
  27. package/dist/{paper-checkbox-515a5284.js → paper-inky-focus-behavior-fa16796b.js} +58 -247
  28. package/dist/{paper-listbox-a3b7175c.js → paper-listbox-5f5d1cec.js} +152 -162
  29. package/dist/pb-code-editor.js +25 -20
  30. package/dist/pb-component-docs.js +68 -64
  31. package/dist/pb-components-bundle.js +1983 -2293
  32. package/dist/pb-edit-app.js +167 -107
  33. package/dist/pb-elements.json +176 -120
  34. package/dist/{pb-i18n-0611135a.js → pb-i18n-4cc00bfe.js} +1 -1
  35. package/dist/pb-leaflet-map.js +23 -23
  36. package/dist/pb-mei.js +56 -41
  37. package/dist/{pb-mixin-b1caa22e.js → pb-mixin-886ece32.js} +1 -1
  38. package/dist/pb-odd-editor.js +1023 -782
  39. package/dist/pb-tify.js +2 -2
  40. package/dist/vaadin-element-mixin-beb74ffd.js +545 -0
  41. package/gh-pages.js +5 -3
  42. package/i18n/common/en.json +6 -0
  43. package/i18n/common/pl.json +2 -2
  44. package/lib/openseadragon.min.js +6 -6
  45. package/package.json +3 -3
  46. package/pb-elements.json +176 -120
  47. package/src/assets/components.css +5 -5
  48. package/src/authority/airtable.js +20 -21
  49. package/src/authority/anton.js +129 -129
  50. package/src/authority/custom.js +23 -21
  51. package/src/authority/geonames.js +38 -32
  52. package/src/authority/gnd.js +50 -42
  53. package/src/authority/kbga.js +137 -134
  54. package/src/authority/metagrid.js +44 -46
  55. package/src/authority/reconciliation.js +66 -67
  56. package/src/authority/registry.js +4 -4
  57. package/src/docs/pb-component-docs.js +2 -2
  58. package/src/docs/pb-component-view.js +5 -5
  59. package/src/docs/pb-components-list.js +2 -2
  60. package/src/docs/pb-demo-snippet.js +2 -2
  61. package/src/dts-client.js +299 -297
  62. package/src/dts-select-endpoint.js +90 -82
  63. package/src/parse-date-service.js +184 -135
  64. package/src/pb-ajax.js +158 -171
  65. package/src/pb-authority-lookup.js +191 -156
  66. package/src/pb-autocomplete.js +292 -280
  67. package/src/pb-blacklab-highlight.js +264 -259
  68. package/src/pb-blacklab-results.js +236 -221
  69. package/src/pb-browse-docs.js +540 -475
  70. package/src/pb-browse.js +68 -65
  71. package/src/pb-clipboard.js +79 -76
  72. package/src/pb-code-editor.js +110 -102
  73. package/src/pb-code-highlight.js +209 -204
  74. package/src/pb-codepen.js +79 -72
  75. package/src/pb-collapse.js +211 -151
  76. package/src/pb-combo-box.js +190 -190
  77. package/src/pb-components-bundle.js +1 -1
  78. package/src/pb-components.js +1 -0
  79. package/src/pb-custom-form.js +173 -153
  80. package/src/pb-dialog.js +98 -62
  81. package/src/pb-document.js +89 -90
  82. package/src/pb-download.js +212 -196
  83. package/src/pb-drawer.js +145 -148
  84. package/src/pb-edit-app.js +301 -229
  85. package/src/pb-edit-xml.js +100 -97
  86. package/src/pb-events.js +114 -107
  87. package/src/pb-facs-link.js +104 -102
  88. package/src/pb-facsimile.js +474 -410
  89. package/src/pb-formula.js +151 -153
  90. package/src/pb-geolocation.js +129 -131
  91. package/src/pb-grid-action.js +53 -56
  92. package/src/pb-grid.js +231 -228
  93. package/src/pb-highlight.js +140 -140
  94. package/src/pb-hotkeys.js +40 -42
  95. package/src/pb-i18n.js +101 -104
  96. package/src/pb-image-strip.js +84 -78
  97. package/src/pb-lang.js +142 -57
  98. package/src/pb-leaflet-map.js +488 -485
  99. package/src/pb-link.js +126 -124
  100. package/src/pb-load.js +431 -429
  101. package/src/pb-login.js +299 -244
  102. package/src/pb-manage-odds.js +352 -336
  103. package/src/pb-map-icon.js +89 -89
  104. package/src/pb-map-layer.js +85 -85
  105. package/src/pb-markdown.js +90 -99
  106. package/src/pb-media-query.js +74 -72
  107. package/src/pb-mei.js +306 -295
  108. package/src/pb-message.js +139 -97
  109. package/src/pb-mixin.js +269 -264
  110. package/src/pb-navigation.js +80 -95
  111. package/src/pb-observable.js +38 -38
  112. package/src/pb-odd-editor.js +1054 -958
  113. package/src/pb-odd-elementspec-editor.js +349 -298
  114. package/src/pb-odd-model-editor.js +1075 -909
  115. package/src/pb-odd-parameter-editor.js +200 -178
  116. package/src/pb-odd-rendition-editor.js +136 -124
  117. package/src/pb-page.js +431 -422
  118. package/src/pb-paginate.js +228 -179
  119. package/src/pb-panel.js +198 -182
  120. package/src/pb-popover-themes.js +15 -8
  121. package/src/pb-popover.js +296 -287
  122. package/src/pb-print-preview.js +127 -127
  123. package/src/pb-progress.js +51 -51
  124. package/src/pb-repeat.js +105 -104
  125. package/src/pb-restricted.js +84 -77
  126. package/src/pb-search.js +256 -228
  127. package/src/pb-select-feature.js +127 -120
  128. package/src/pb-select-odd.js +132 -124
  129. package/src/pb-select-template.js +89 -78
  130. package/src/pb-select.js +251 -227
  131. package/src/pb-split-list.js +179 -174
  132. package/src/pb-svg.js +80 -79
  133. package/src/pb-table-column.js +54 -54
  134. package/src/pb-table-grid.js +221 -203
  135. package/src/pb-tabs.js +61 -63
  136. package/src/pb-tify.js +154 -154
  137. package/src/pb-timeline.js +382 -249
  138. package/src/pb-toggle-feature.js +195 -187
  139. package/src/pb-upload.js +184 -174
  140. package/src/pb-version.js +30 -30
  141. package/src/pb-view-annotate.js +135 -98
  142. package/src/pb-view.js +1282 -1270
  143. package/src/pb-zoom.js +127 -45
  144. package/src/polymer-hack.js +1 -1
  145. package/src/search-result-service.js +256 -223
  146. package/src/seed-element.js +13 -20
  147. package/src/settings.js +4 -4
  148. package/src/theming.js +98 -91
  149. package/src/urls.js +289 -289
  150. package/src/utils.js +53 -51
  151. package/css/pb-styles.css +0 -51
  152. package/dist/vaadin-element-mixin-fe4a4883.js +0 -527
  153. package/src/assets/pb-styles.css +0 -51
  154. package/src/pb-light-dom.js +0 -41
@@ -1,5 +1,5 @@
1
1
  import { LitElement, html, css } from 'lit-element';
2
- import "@lrnwebcomponents/es-global-bridge";
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,509 @@ 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
+ }
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 =>
194
+ item.getOrder ? item.getOrder() : Number.POSITIVE_INFINITY,
195
+ );
196
+ const insertAt = itemOrder.reduce((result, next, index) => {
197
+ if (order < next) return result;
198
+ if (order === next) return index;
199
+ return index + 1;
200
+ }, 0);
201
+
202
+ this._facsimiles.splice(insertAt, 0, element);
203
+ this.loaded = this._facsimiles.length > 0;
204
+
205
+ this._scheduleFacsimileObserver();
206
+ });
207
+ this.subscribeTo('pb-show-annotation', this._showAnnotationListener.bind(this));
208
+ }
209
+
210
+ firstUpdated() {
211
+ try {
212
+ const bridge = window.ESGlobalBridge.requestAvailability();
213
+ const path = resolveURL('../lib/openseadragon.min.js');
214
+ // check if OpenSeadragon is already loaded
215
+ if (bridge.imports['openseadragon']) {
216
+ this._initOpenSeadragon();
217
+ return;
218
+ }
219
+ // Wait for OpenSeadragon to load
220
+ window.addEventListener(
221
+ 'es-bridge-openseadragon-loaded',
222
+ this._initOpenSeadragon.bind(this),
223
+ { once: true },
224
+ );
225
+ // load OpenSeadragon
226
+ bridge.load('openseadragon', path);
227
+ } catch (error) {
228
+ console.error(error.message);
158
229
  }
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));
230
+ }
231
+
232
+ render() {
233
+ return html`
234
+ <slot name="before"></slot>
235
+ <!-- Openseadragon -->
236
+
237
+ <div id="viewer" part="image"></div>
238
+ <slot name="after"></slot>
239
+ ${this.showDownloadButton ? html`<a id="downloadBtn" title="Download">&#8676;</a>` : ''}
240
+ `;
241
+ }
242
+
243
+ static get styles() {
244
+ return css`
245
+ :host {
246
+ display: flex;
247
+ flex-direction: column;
248
+ position: relative;
249
+ background: transparent;
250
+ }
251
+
252
+ #runtime-overlay {
253
+ border: var(--pb-facsimile-border, 4px solid rgba(0, 0, 128, 0.5));
254
+ }
255
+
256
+ #viewer {
257
+ flex: 1;
258
+ position: relative;
259
+ max-height: var(--pb-facsimile-height, auto);
260
+ width: 100%;
261
+ }
262
+ #downloadBtn {
263
+ position: absolute;
264
+ z-index: 100;
265
+ bottom: 0.25rem;
266
+ width: 1.35rem;
267
+ height: 1.35rem;
268
+ transform: rotate(-90deg);
269
+ cursor: pointer;
270
+ border: thin solid #d7dde8;
271
+ display: flex;
272
+ align-items: center;
273
+ justify-content: center;
274
+ border-radius: 0.75rem;
275
+ background-image: linear-gradient(to left, #fafafa 0%, #d7dde8 51%, #bbbbbb 100%);
276
+ font-size: 1.2rem;
277
+ box-shadow: -2px 1px 5px 0px rgba(0, 0, 0, 0.75);
278
+ }
279
+ #downloadBtn:hover {
280
+ background-image: radial-gradient(white, #efefef);
281
+ }
282
+ `;
283
+ }
284
+
285
+ // Init openseadragon
286
+ _initOpenSeadragon() {
287
+ const prefixUrl = resolveURL(this.prefixUrl + (this.prefixUrl.endsWith('/') ? '' : '/'));
288
+ const options = {
289
+ element: this.shadowRoot.getElementById('viewer'),
290
+ prefixUrl,
291
+ preserveViewport: true,
292
+ showZoomControl: true,
293
+ sequenceMode: this.showSequenceMode,
294
+ showHomeControl: this.showHomeControl,
295
+ showFullPageControl: this.showFullPageControl,
296
+ showNavigator: this.showNavigator,
297
+ showNavigationControl: this.showNavigationControl,
298
+ showRotationControl: this.showRotationControl,
299
+ autoHideControls: false,
300
+ visibilityRatio: 1,
301
+ minZoomLevel: 1,
302
+ defaultZoomLevel: this.defaultZoomLevel,
303
+ constrainDuringPan: true,
304
+ crossOriginPolicy: this.crossOriginPolicy,
305
+ };
306
+
307
+ if (this.referenceStrip) {
308
+ options.showReferenceStrip = true;
309
+ options.referenceStripSizeRatio = this.referenceStripSizeRatio;
206
310
  }
311
+ this.viewer = OpenSeadragon(options);
207
312
 
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
- `;
313
+ if (this.showFullPageControl) {
314
+ this._overrideFullscreenButton();
235
315
  }
236
316
 
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
- `;
317
+ this.viewer.addHandler('open', () => {
318
+ this.resetZoom();
319
+ this.emitTo('pb-facsimile-status', { status: 'loaded', facsimiles: this._facsimiles });
320
+ });
321
+ this.viewer.addHandler('open-failed', ev => {
322
+ console.error('<pb-facsimile> open failed: %s', ev.message);
323
+ this.loaded = false;
324
+ this.emitTo('pb-facsimile-status', { status: 'fail' });
325
+ });
326
+
327
+ const download = this.shadowRoot.querySelector('#downloadBtn');
328
+ if (this.showDownloadButton) {
329
+ download.addEventListener('click', ev => {
330
+ ev.preventDefault();
331
+ const currentImage = this.viewer.drawer.canvas.toDataURL('image/png');
332
+ const downloadLink = document.createElement('a');
333
+ downloadLink.href = currentImage;
334
+ downloadLink.download = 'download';
335
+ downloadLink.click();
336
+ });
277
337
  }
278
338
 
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
- /*
339
+ /*
330
340
  handling of full-screen view requires to hide/unhide the content of body to allow full screen viewer
331
341
  to full-page functionality. Standard OSD completely deletes all body children disconnecting all event-handlers
332
342
  that have been there. This solution just uses style.display to hide/show. Former display value of pb-page
333
343
  will be preserved.
334
344
  */
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
- });
345
+ this.ownerPage = document.querySelector('pb-page');
346
+ if (this.ownerPage) {
347
+ this.pbPageDisplay = window.getComputedStyle(this.ownerPage).getPropertyValue('display');
348
+ this.viewer.addHandler('full-screen', ev => {
349
+ if (ev.fullScreen) {
350
+ this.ownerPage.style.display = 'none';
351
+ } else {
352
+ this.viewer.clearOverlays();
353
+ this.emitTo('pb-refresh');
354
+ this.ownerPage.style.display = this.pbPageDisplay;
347
355
  }
348
- this._facsimileObserver();
349
-
350
- this.signalReady();
356
+ });
351
357
  }
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)
358
+ this._scheduleFacsimileObserver();
359
+ this.signalReady();
360
+ }
361
+
362
+ /**
363
+ * A single transcription can have tons of pb-facs-link elements. Always debounce loading these
364
+ */
365
+ _scheduleFacsimileObserver() {
366
+ if (this._facsimileObserverScheduled) {
367
+ return;
376
368
  }
377
-
378
- _clearAll() {
379
- if (!this.viewer) {
380
- return;
381
- }
382
- this.resetZoom();
383
- this.viewer.clearOverlays();
384
- this.facsimiles = [];
369
+ this._facsimileObserverScheduled = true;
370
+ setTimeout(() => {
371
+ this._facsimileObserverScheduled = false;
372
+ this._facsimileObserver();
373
+ }, 0);
374
+ }
375
+
376
+ _facsimileObserver() {
377
+ if (!this.viewer) {
378
+ return;
385
379
  }
380
+ if (this._facsimiles.length === 0) {
381
+ this.viewer.close();
382
+ return;
383
+ }
384
+ const uris = this._facsimiles.map(facsLink => {
385
+ const url = this.baseUri + (facsLink.getImage ? facsLink.getImage() : facsLink);
386
+ if (this.type === 'iiif') {
387
+ return `${url}/info.json`;
388
+ }
389
+ return {
390
+ tileSource: {
391
+ type: 'image',
392
+ url,
393
+ buildPyramid: false,
394
+ },
395
+ };
396
+ });
397
+
398
+ const deduplicatedUris = [];
399
+ const uriSet = new Set();
400
+ for (const uri of uris) {
401
+ const hashKey = JSON.stringify(uri);
402
+ if (!uriSet.has(hashKey)) {
403
+ uriSet.add(hashKey);
404
+ deduplicatedUris.push(uri);
405
+ }
406
+ }
407
+ this.viewer.open(deduplicatedUris);
408
+ this.viewer.goToPage(0);
409
+ }
386
410
 
387
- _showAnnotationListener(event) {
388
- if (!this.viewer) {
389
- return;
390
- }
391
- const overlayId = 'runtime-overlay'
392
-
393
- // remove old overlay
394
- this.viewer.removeOverlay(this.overlay);
395
-
396
- // check event data for completeness
397
- if (!event.detail.file || event.detail.file === 0) {
398
- return console.error('file missing', event.detail)
399
- }
411
+ _clearAll() {
412
+ if (!this.viewer) {
413
+ return;
414
+ }
415
+ this.resetZoom();
416
+ this.viewer.clearOverlays();
417
+ this.facsimiles = [];
418
+ }
419
+
420
+ _showAnnotationListener(event) {
421
+ if (!this.viewer) {
422
+ return;
423
+ }
424
+ const overlayId = 'runtime-overlay';
400
425
 
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
- }
426
+ // remove old overlay
427
+ this.viewer.removeOverlay(this.overlay);
408
428
 
409
- // find page to show
410
- const page = event.detail.element ? this._pageByElement(event.detail.element) : this._pageIndexByUrl(event.detail.file);
429
+ // check event data for completeness
430
+ if (!event.detail.file || event.detail.file === 0) {
431
+ return console.error('file missing', event.detail);
432
+ }
411
433
 
412
- if (page < 0) {
413
- return console.error('page not found', event.detail)
414
- }
434
+ if (
435
+ event.detail.coordinates &&
436
+ (!event.detail.coordinates[0] || event.detail.coordinates.length !== 4)
437
+ ) {
438
+ return console.error('coords incomplete or missing', event.detail);
439
+ }
415
440
 
416
- if (this.viewer.currentPage() !== page) {
417
- this.viewer.goToPage(page);
418
- }
441
+ // find page to show
442
+ const page = event.detail.element
443
+ ? this._pageByElement(event.detail.element)
444
+ : this._pageIndexByUrl(event.detail.file);
419
445
 
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
- }
446
+ if (page < 0) {
447
+ return console.error('page not found', event.detail);
446
448
  }
447
449
 
448
- _pageByElement(element) {
449
- return this._facsimiles.indexOf(element);
450
+ if (this.viewer.currentPage() !== page) {
451
+ this.viewer.goToPage(page);
450
452
  }
451
453
 
452
- _pageIndexByUrl(file) {
453
- return this._facsimiles.findIndex(element => element.getImage() === file);
454
+ if (event.detail.coordinates) {
455
+ // deconstruct given coordinates into variables
456
+ const [x1, y1, w, h] = event.detail.coordinates;
457
+ const tiledImage = this.viewer.world.getItemAt(0);
458
+ const currentRect = tiledImage.viewportToImageRectangle(tiledImage.getBounds(true));
459
+
460
+ // scroll into view?
461
+ if (!currentRect.containsPoint(new OpenSeadragon.Point(x1, y1))) {
462
+ this.viewer.viewport.fitBoundsWithConstraints(
463
+ tiledImage.imageToViewportRectangle(x1, y1, currentRect.width, currentRect.height),
464
+ );
465
+ }
466
+
467
+ // create new overlay
468
+ const overlay = document.createElement('div');
469
+ this.overlay = overlay;
470
+ overlay.id = overlayId;
471
+
472
+ // place marker
473
+ const marker = tiledImage.imageToViewportRectangle(x1, y1, w, h);
474
+
475
+ this.viewer.addOverlay({
476
+ element: overlay,
477
+ location: marker,
478
+ });
454
479
  }
480
+ }
455
481
 
456
- // reset zoom
457
- resetZoom() {
458
- if (!this.viewer) {
459
- return;
460
- }
461
- this.viewer.viewport.goHome();
462
- }
482
+ _pageByElement(element) {
483
+ return this._facsimiles.indexOf(element);
484
+ }
463
485
 
486
+ _pageIndexByUrl(file) {
487
+ return this._facsimiles.findIndex(element => element.getImage() === file);
488
+ }
464
489
 
490
+ // reset zoom
491
+ resetZoom() {
492
+ if (!this.viewer) {
493
+ return;
494
+ }
495
+ this.viewer.viewport.goHome();
496
+ }
497
+
498
+ _overrideFullscreenButton() {
499
+ // The full page control in OSD makes all body elements disappear, causing all kinds of bugs in
500
+ // our webcomponents. Replace it with something that just calls `requestFullScreen` instead.
501
+ // This approach (but more elaborate) is also PR-ed to OSD
502
+ // (https://github.com/openseadragon/openseadragon/pull/2786). This code can be dropped the
503
+ // moment we upgrade to OSD.
504
+
505
+ const toggleFullscreenButton = this.viewer.buttonGroup.buttons.find(
506
+ button => button.tooltip === 'Toggle full page',
507
+ );
508
+ if (!toggleFullscreenButton) {
509
+ return;
510
+ }
511
+ const releaseHandler = async () => {
512
+ if (!document.fullscreenElement) {
513
+ await this.viewer.element.requestFullscreen();
514
+ return;
515
+ }
516
+ await document.exitFullscreen();
517
+ };
518
+
519
+ toggleFullscreenButton.onRelease = releaseHandler;
520
+ const oldRaiseEvent = toggleFullscreenButton.raiseEvent;
521
+ toggleFullscreenButton.raiseEvent = (eventName, args) => {
522
+ if (eventName === 'release') {
523
+ releaseHandler();
524
+ } else {
525
+ oldRaiseEvent.call(toggleFullscreenButton, eventName, args);
526
+ }
527
+ };
528
+ }
465
529
  }
466
530
  if (!customElements.get('pb-facsimile')) {
467
- customElements.define('pb-facsimile', PbFacsimile);
531
+ customElements.define('pb-facsimile', PbFacsimile);
468
532
  }