@panoramax/web-viewer 3.2.3-develop-3ea5b063 → 3.2.3-develop-83778bdd

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 (32) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/build/index.js +2 -2
  3. package/build/index.js.map +1 -1
  4. package/config/jest/mocks.js +21 -2
  5. package/docs/reference/components/core/Basic.md +28 -0
  6. package/docs/reference/components/core/CoverageMap.md +28 -0
  7. package/docs/reference/components/core/Editor.md +28 -0
  8. package/docs/reference/components/core/PhotoViewer.md +29 -0
  9. package/docs/reference/components/core/Viewer.md +28 -0
  10. package/docs/tutorials/migrate_v4.md +14 -1
  11. package/docs/tutorials/synced_coverage.md +1 -0
  12. package/package.json +1 -1
  13. package/scripts/doc.js +3 -1
  14. package/src/components/core/Basic.js +55 -0
  15. package/src/components/core/CoverageMap.js +6 -0
  16. package/src/components/core/Editor.js +4 -0
  17. package/src/components/core/PhotoViewer.js +21 -0
  18. package/src/components/core/Viewer.js +4 -0
  19. package/src/utils/PhotoAdapter.js +1 -0
  20. package/src/utils/URLHandler.js +1 -5
  21. package/tests/components/core/Basic.test.js +130 -0
  22. package/tests/components/core/BasicMock.js +20 -0
  23. package/tests/components/core/CoverageMap.test.js +20 -0
  24. package/tests/components/core/Editor.test.js +20 -0
  25. package/tests/components/core/PhotoViewer.test.js +20 -0
  26. package/tests/components/core/Viewer.test.js +20 -0
  27. package/tests/components/ui/CopyButton.test.js +1 -1
  28. package/tests/components/ui/Loader.test.js +1 -0
  29. package/tests/components/ui/Popup.test.js +2 -0
  30. package/tests/components/ui/QualityScore.test.js +1 -0
  31. package/tests/components/ui/SearchBar.test.js +3 -0
  32. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +3 -4
@@ -15,8 +15,8 @@ jest.mock("lit", () => {
15
15
  this.requestUpdate = jest.fn();
16
16
  this.updateComplete = Promise.resolve();
17
17
  this.style = {};
18
- this.addEventListener = jest.fn();
19
- this.dispatchEvent = jest.fn();
18
+ this._handlers = {};
19
+ this._handlesOnce = {};
20
20
  this.renderRoot = {
21
21
  querySelectorAll: jest.fn(),
22
22
  };
@@ -32,6 +32,24 @@ jest.mock("lit", () => {
32
32
  return "";
33
33
  }
34
34
  getAttribute() {}
35
+ addEventListener(type, handler, options) {
36
+ if(options?.once) {
37
+ if(!this._handlersOnce) { this._handlersOnce = {}; }
38
+ if(!this._handlersOnce?.[type]) { this._handlersOnce[type] = []; }
39
+ this._handlersOnce[type].push(handler);
40
+ }
41
+ else {
42
+ if(!this._handlers[type]) { this._handlers[type] = []; }
43
+ this._handlers[type].push(handler);
44
+ }
45
+ }
46
+ dispatchEvent(event, opts) {
47
+ this._handlers[event.type]?.forEach(f => f(opts));
48
+ this._handlersOnce?.[event.type]?.forEach(f => f(opts));
49
+ if(this._handlersOnce?.[event.type]) {
50
+ this._handlersOnce[event.type] = [];
51
+ }
52
+ }
35
53
  }
36
54
 
37
55
  return {
@@ -57,6 +75,7 @@ jest.mock("lit/directives/map.js", () => ({
57
75
  jest.mock("maplibre-gl", () => ({
58
76
  addProtocol: jest.fn(),
59
77
  AttributionControl: jest.fn(),
78
+ NavigationControl: jest.fn(),
60
79
  GeolocateControl: class {
61
80
  onAdd() {;}
62
81
  },
@@ -21,6 +21,8 @@
21
21
  * [.select([seqId], [picId], [force])](#Panoramax.components.core.Basic+select)
22
22
  * [.isWidthSmall()](#Panoramax.components.core.Basic+isWidthSmall) ⇒ <code>boolean</code>
23
23
  * [.isHeightSmall()](#Panoramax.components.core.Basic+isHeightSmall) ⇒ <code>boolean</code>
24
+ * [.getSubComponentsNames()](#Panoramax.components.core.Basic+getSubComponentsNames) ⇒ <code>Array.&lt;string&gt;</code>
25
+ * [.addEventListener(type, listener, [options])](#Panoramax.components.core.Basic+addEventListener)
24
26
  * ["menu-opened"](#Panoramax.components.core.Basic+event_menu-opened)
25
27
  * ["select"](#Panoramax.components.core.Basic+event_select)
26
28
  * ["ready"](#Panoramax.components.core.Basic+event_ready)
@@ -106,6 +108,32 @@ Is the view running in a small-height container (small embed or smartphone)
106
108
 
107
109
  **Kind**: instance method of [<code>Basic</code>](#Panoramax.components.core.Basic)
108
110
  **Returns**: <code>boolean</code> - True if container height is small
111
+ <a name="Panoramax.components.core.Basic+getSubComponentsNames"></a>
112
+
113
+ ### basic.getSubComponentsNames() ⇒ <code>Array.&lt;string&gt;</code>
114
+ List names of sub-components (like loader, api, map, psv) available in this component.
115
+
116
+ **Kind**: instance method of [<code>Basic</code>](#Panoramax.components.core.Basic)
117
+ **Returns**: <code>Array.&lt;string&gt;</code> - Sub-components names.
118
+ <a name="Panoramax.components.core.Basic+addEventListener"></a>
119
+
120
+ ### basic.addEventListener(type, listener, [options])
121
+ Listen to events from this components or one of its sub-components.
122
+
123
+ For example, you can listen to `map` events using prefix `map:`.
124
+
125
+ ```js
126
+ me.addEventListener("map:move", doSomething);
127
+ ```
128
+
129
+ **Kind**: instance method of [<code>Basic</code>](#Panoramax.components.core.Basic)
130
+
131
+ | Param | Type | Description |
132
+ | --- | --- | --- |
133
+ | type | <code>string</code> | The event type to listen for |
134
+ | listener | <code>function</code> | The event handler |
135
+ | [options] | <code>object</code> | [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) |
136
+
109
137
  <a name="Panoramax.components.core.Basic+event_menu-opened"></a>
110
138
 
111
139
  ### "menu-opened"
@@ -23,6 +23,8 @@
23
23
  * [.select([seqId], [picId], [force])](#Panoramax.components.core.Basic+select)
24
24
  * [.isWidthSmall()](Basic.md/#Panoramax.components.core.Basic+isWidthSmall) ⇒ <code>boolean</code>
25
25
  * [.isHeightSmall()](Basic.md/#Panoramax.components.core.Basic+isHeightSmall) ⇒ <code>boolean</code>
26
+ * [.getSubComponentsNames()](Basic.md/#Panoramax.components.core.Basic+getSubComponentsNames) ⇒ <code>Array.&lt;string&gt;</code>
27
+ * [.addEventListener(type, listener, [options])](#Panoramax.components.core.Basic+addEventListener)
26
28
  * ["menu-opened"](Basic.md/#Panoramax.components.core.Basic+event_menu-opened)
27
29
  * ["select"](Basic.md/#Panoramax.components.core.Basic+event_select)
28
30
  * ["ready"](Basic.md/#Panoramax.components.core.Basic+event_ready)
@@ -117,6 +119,32 @@ Is the view running in a small-height container (small embed or smartphone)
117
119
 
118
120
  **Kind**: instance method of [<code>CoverageMap</code>](#Panoramax.components.core.CoverageMap)
119
121
  **Returns**: <code>boolean</code> - True if container height is small
122
+ <a name="Panoramax.components.core.Basic+getSubComponentsNames"></a>
123
+
124
+ ### coverageMap.getSubComponentsNames() ⇒ <code>Array.&lt;string&gt;</code>
125
+ List names of sub-components (like loader, api, map, psv) available in this component.
126
+
127
+ **Kind**: instance method of [<code>CoverageMap</code>](#Panoramax.components.core.CoverageMap)
128
+ **Returns**: <code>Array.&lt;string&gt;</code> - Sub-components names.
129
+ <a name="Panoramax.components.core.Basic+addEventListener"></a>
130
+
131
+ ### coverageMap.addEventListener(type, listener, [options])
132
+ Listen to events from this components or one of its sub-components.
133
+
134
+ For example, you can listen to `map` events using prefix `map:`.
135
+
136
+ ```js
137
+ me.addEventListener("map:move", doSomething);
138
+ ```
139
+
140
+ **Kind**: instance method of [<code>CoverageMap</code>](#Panoramax.components.core.CoverageMap)
141
+
142
+ | Param | Type | Description |
143
+ | --- | --- | --- |
144
+ | type | <code>string</code> | The event type to listen for |
145
+ | listener | <code>function</code> | The event handler |
146
+ | [options] | <code>object</code> | [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) |
147
+
120
148
  <a name="Panoramax.components.core.Basic+event_menu-opened"></a>
121
149
 
122
150
  ### "menu-opened"
@@ -25,6 +25,8 @@
25
25
  * [.select([seqId], [picId], [force])](#Panoramax.components.core.Basic+select)
26
26
  * [.isWidthSmall()](Basic.md/#Panoramax.components.core.Basic+isWidthSmall) ⇒ <code>boolean</code>
27
27
  * [.isHeightSmall()](Basic.md/#Panoramax.components.core.Basic+isHeightSmall) ⇒ <code>boolean</code>
28
+ * [.getSubComponentsNames()](Basic.md/#Panoramax.components.core.Basic+getSubComponentsNames) ⇒ <code>Array.&lt;string&gt;</code>
29
+ * [.addEventListener(type, listener, [options])](#Panoramax.components.core.Basic+addEventListener)
28
30
  * ["menu-opened"](Basic.md/#Panoramax.components.core.Basic+event_menu-opened)
29
31
  * ["select"](Basic.md/#Panoramax.components.core.Basic+event_select)
30
32
  * ["ready"](Basic.md/#Panoramax.components.core.Basic+event_ready)
@@ -132,6 +134,32 @@ Is the view running in a small-height container (small embed or smartphone)
132
134
 
133
135
  **Kind**: instance method of [<code>Editor</code>](#Panoramax.components.core.Editor)
134
136
  **Returns**: <code>boolean</code> - True if container height is small
137
+ <a name="Panoramax.components.core.Basic+getSubComponentsNames"></a>
138
+
139
+ ### editor.getSubComponentsNames() ⇒ <code>Array.&lt;string&gt;</code>
140
+ List names of sub-components (like loader, api, map, psv) available in this component.
141
+
142
+ **Kind**: instance method of [<code>Editor</code>](#Panoramax.components.core.Editor)
143
+ **Returns**: <code>Array.&lt;string&gt;</code> - Sub-components names.
144
+ <a name="Panoramax.components.core.Basic+addEventListener"></a>
145
+
146
+ ### editor.addEventListener(type, listener, [options])
147
+ Listen to events from this components or one of its sub-components.
148
+
149
+ For example, you can listen to `map` events using prefix `map:`.
150
+
151
+ ```js
152
+ me.addEventListener("map:move", doSomething);
153
+ ```
154
+
155
+ **Kind**: instance method of [<code>Editor</code>](#Panoramax.components.core.Editor)
156
+
157
+ | Param | Type | Description |
158
+ | --- | --- | --- |
159
+ | type | <code>string</code> | The event type to listen for |
160
+ | listener | <code>function</code> | The event handler |
161
+ | [options] | <code>object</code> | [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) |
162
+
135
163
  <a name="Panoramax.components.core.Basic+event_menu-opened"></a>
136
164
 
137
165
  ### "menu-opened"
@@ -35,11 +35,13 @@
35
35
  * [.moveRight()](#Panoramax.components.core.PhotoViewer+moveRight)
36
36
  * [.moveUp()](#Panoramax.components.core.PhotoViewer+moveUp)
37
37
  * [.moveDown()](#Panoramax.components.core.PhotoViewer+moveDown)
38
+ * [.addEventListener(type, listener, [options])](#Panoramax.components.core.PhotoViewer+addEventListener)
38
39
  * [.onceAPIReady()](Basic.md/#Panoramax.components.core.Basic+onceAPIReady) ⇒ <code>Promise</code>
39
40
  * [.getClassName()](Basic.md/#Panoramax.components.core.Basic+getClassName) ⇒ <code>string</code>
40
41
  * [.select([seqId], [picId], [force])](#Panoramax.components.core.Basic+select)
41
42
  * [.isWidthSmall()](Basic.md/#Panoramax.components.core.Basic+isWidthSmall) ⇒ <code>boolean</code>
42
43
  * [.isHeightSmall()](Basic.md/#Panoramax.components.core.Basic+isHeightSmall) ⇒ <code>boolean</code>
44
+ * [.getSubComponentsNames()](Basic.md/#Panoramax.components.core.Basic+getSubComponentsNames) ⇒ <code>Array.&lt;string&gt;</code>
43
45
  * ["menu-opened"](Basic.md/#Panoramax.components.core.Basic+event_menu-opened)
44
46
  * ["select"](Basic.md/#Panoramax.components.core.Basic+event_select)
45
47
  * ["ready"](Basic.md/#Panoramax.components.core.Basic+event_ready)
@@ -163,6 +165,26 @@ Moves the view of main component slightly to the top.
163
165
  Moves the view of main component slightly to the bottom.
164
166
 
165
167
  **Kind**: instance method of [<code>PhotoViewer</code>](#Panoramax.components.core.PhotoViewer)
168
+ <a name="Panoramax.components.core.PhotoViewer+addEventListener"></a>
169
+
170
+ ### photoViewer.addEventListener(type, listener, [options])
171
+ Listen to events from this components or one of its sub-components.
172
+
173
+ For example, you can listen to `psv` events using prefix `psv:`.
174
+
175
+ ```js
176
+ me.addEventListener("psv:picture-loading", doSomething);
177
+ ```
178
+
179
+ **Kind**: instance method of [<code>PhotoViewer</code>](#Panoramax.components.core.PhotoViewer)
180
+ **Overrides**: [<code>addEventListener</code>](Basic.md/#Panoramax.components.core.Basic+addEventListener)
181
+
182
+ | Param | Type | Description |
183
+ | --- | --- | --- |
184
+ | type | <code>string</code> | The event type to listen for |
185
+ | listener | <code>function</code> | The event handler |
186
+ | [options] | <code>object</code> | [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) |
187
+
166
188
  <a name="Panoramax.components.core.Basic+onceAPIReady"></a>
167
189
 
168
190
  ### photoViewer.onceAPIReady() ⇒ <code>Promise</code>
@@ -209,6 +231,13 @@ Is the view running in a small-height container (small embed or smartphone)
209
231
 
210
232
  **Kind**: instance method of [<code>PhotoViewer</code>](#Panoramax.components.core.PhotoViewer)
211
233
  **Returns**: <code>boolean</code> - True if container height is small
234
+ <a name="Panoramax.components.core.Basic+getSubComponentsNames"></a>
235
+
236
+ ### photoViewer.getSubComponentsNames() ⇒ <code>Array.&lt;string&gt;</code>
237
+ List names of sub-components (like loader, api, map, psv) available in this component.
238
+
239
+ **Kind**: instance method of [<code>PhotoViewer</code>](#Panoramax.components.core.PhotoViewer)
240
+ **Returns**: <code>Array.&lt;string&gt;</code> - Sub-components names.
212
241
  <a name="Panoramax.components.core.Basic+event_menu-opened"></a>
213
242
 
214
243
  ### "menu-opened"
@@ -39,11 +39,13 @@
39
39
  * [.moveRight()](PhotoViewer.md/#Panoramax.components.core.PhotoViewer+moveRight)
40
40
  * [.moveUp()](PhotoViewer.md/#Panoramax.components.core.PhotoViewer+moveUp)
41
41
  * [.moveDown()](PhotoViewer.md/#Panoramax.components.core.PhotoViewer+moveDown)
42
+ * [.addEventListener(type, listener, [options])](#Panoramax.components.core.PhotoViewer+addEventListener)
42
43
  * [.onceAPIReady()](Basic.md/#Panoramax.components.core.Basic+onceAPIReady) ⇒ <code>Promise</code>
43
44
  * [.getClassName()](Basic.md/#Panoramax.components.core.Basic+getClassName) ⇒ <code>string</code>
44
45
  * [.select([seqId], [picId], [force])](#Panoramax.components.core.Basic+select)
45
46
  * [.isWidthSmall()](Basic.md/#Panoramax.components.core.Basic+isWidthSmall) ⇒ <code>boolean</code>
46
47
  * [.isHeightSmall()](Basic.md/#Panoramax.components.core.Basic+isHeightSmall) ⇒ <code>boolean</code>
48
+ * [.getSubComponentsNames()](Basic.md/#Panoramax.components.core.Basic+getSubComponentsNames) ⇒ <code>Array.&lt;string&gt;</code>
47
49
  * ["focus-changed"](#Panoramax.components.core.Viewer+event_focus-changed)
48
50
  * ["menu-opened"](Basic.md/#Panoramax.components.core.Basic+event_menu-opened)
49
51
  * ["select"](Basic.md/#Panoramax.components.core.Basic+event_select)
@@ -194,6 +196,25 @@ Moves the view of main component slightly to the top.
194
196
  Moves the view of main component slightly to the bottom.
195
197
 
196
198
  **Kind**: instance method of [<code>Viewer</code>](#Panoramax.components.core.Viewer)
199
+ <a name="Panoramax.components.core.PhotoViewer+addEventListener"></a>
200
+
201
+ ### viewer.addEventListener(type, listener, [options])
202
+ Listen to events from this components or one of its sub-components.
203
+
204
+ For example, you can listen to `psv` events using prefix `psv:`.
205
+
206
+ ```js
207
+ me.addEventListener("psv:picture-loading", doSomething);
208
+ ```
209
+
210
+ **Kind**: instance method of [<code>Viewer</code>](#Panoramax.components.core.Viewer)
211
+
212
+ | Param | Type | Description |
213
+ | --- | --- | --- |
214
+ | type | <code>string</code> | The event type to listen for |
215
+ | listener | <code>function</code> | The event handler |
216
+ | [options] | <code>object</code> | [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) |
217
+
197
218
  <a name="Panoramax.components.core.Basic+onceAPIReady"></a>
198
219
 
199
220
  ### viewer.onceAPIReady() ⇒ <code>Promise</code>
@@ -240,6 +261,13 @@ Is the view running in a small-height container (small embed or smartphone)
240
261
 
241
262
  **Kind**: instance method of [<code>Viewer</code>](#Panoramax.components.core.Viewer)
242
263
  **Returns**: <code>boolean</code> - True if container height is small
264
+ <a name="Panoramax.components.core.Basic+getSubComponentsNames"></a>
265
+
266
+ ### viewer.getSubComponentsNames() ⇒ <code>Array.&lt;string&gt;</code>
267
+ List names of sub-components (like loader, api, map, psv) available in this component.
268
+
269
+ **Kind**: instance method of [<code>Viewer</code>](#Panoramax.components.core.Viewer)
270
+ **Returns**: <code>Array.&lt;string&gt;</code> - Sub-components names.
243
271
  <a name="Panoramax.components.core.Viewer+event_focus-changed"></a>
244
272
 
245
273
  ### "focus-changed"
@@ -104,7 +104,7 @@ Many events names were changed, and some of them moved to sub-components.
104
104
  | Viewer | `sequence-playing` | components.ui.Photo | `sequence-playing` |
105
105
  | Viewer | `sequence-stopped` | components.ui.Photo | `sequence-stopped` |
106
106
 
107
- To listen to these events, you have to use `map` and `psv` properties of your component.
107
+ To listen to these events, you can use `map` and `psv` properties of your component.
108
108
 
109
109
  ```js
110
110
  myviewer.addEventListener("focus-changed", e => console.log(e));
@@ -114,6 +114,19 @@ myviewer.psv.addEventListener("sequence-playing", e => console.log(e));
114
114
  myviewer.map.on("picture-click", e => console.log(e));
115
115
  ```
116
116
 
117
+ To make use of these sub-components events easier, note that you can also call them from parent component using the property name as a prefix. For example:
118
+
119
+ ```js
120
+ // Transfers listener to map sub-component
121
+ myviewer.addEventListener("map:moveend", e => console.log(e));
122
+
123
+ // Works for any sub-component, like URL Handler or PSV
124
+ myviewer.addEventListener("urlHandler:url-changed", e => console.log(e));
125
+ myviewer.addEventListener("psv:picture-loaded", e => console.log(e));
126
+ ```
127
+
128
+ To know more about available events per component, please refer [to their individual API page](../reference.md).
129
+
117
130
  ## ⚙️ Functions
118
131
 
119
132
  Many functions were changed as well, in order to reduce source files size and make things more logical. You can find [all API reference in this doc](../reference.md), here's a summary:
@@ -22,6 +22,7 @@ coverage.addEventListener("ready", () => {
22
22
  coverage.map.fitBounds([0, 0, 180, 90]);
23
23
 
24
24
  // Listen to sequence hovered on map
25
+ // You can alternatively use: coverage.addEventListener("map:sequence-hover", ...)
25
26
  coverage.map.on("sequence-hover", e => {
26
27
  console.log("Hovered sequence", e.seqId);
27
28
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panoramax/web-viewer",
3
- "version": "3.2.3-develop-3ea5b063",
3
+ "version": "3.2.3-develop-83778bdd",
4
4
  "description": "Panoramax web viewer for geolocated pictures",
5
5
  "main": "build/index.js",
6
6
  "author": "Panoramax team",
package/scripts/doc.js CHANGED
@@ -47,7 +47,9 @@ async function writeDocs(templateData) {
47
47
  const template = `{{#class name="${cl.name}"}}{{>docs}}{{/class}}`;
48
48
  let output = await jsdoc2md.render({ data: templateData, template: template });
49
49
  output = cleanupMdLinks(cl.id, output);
50
- await fs.writeFile(path.resolve(docPath, `${cl.name}.md`), output);
50
+ if(!output.startsWith("ERROR")) {
51
+ await fs.writeFile(path.resolve(docPath, `${cl.name}.md`), output);
52
+ }
51
53
  }
52
54
  }
53
55
 
@@ -264,4 +264,59 @@ export default class Basic extends LitElement {
264
264
  render() {
265
265
  return html`<p>Should not be used directly, use Viewer/CoverageMap/Editor instead</p>`;
266
266
  }
267
+
268
+ /**
269
+ * List names of sub-components (like loader, api, map, psv) available in this component.
270
+ * @returns {string[]} Sub-components names.
271
+ * @memberof Panoramax.components.core.Basic#
272
+ */
273
+ getSubComponentsNames() {
274
+ return ["loader", "api"];
275
+ }
276
+
277
+ /**
278
+ * Listen to events from this components or one of its sub-components.
279
+ *
280
+ * For example, you can listen to `map` events using prefix `map:`.
281
+ *
282
+ * ```js
283
+ * me.addEventListener("map:move", doSomething);
284
+ * ```
285
+ * @param {string} type The event type to listen for
286
+ * @param {function} listener The event handler
287
+ * @param {object} [options] [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options)
288
+ * @memberof Panoramax.components.core.Basic#
289
+ */
290
+ addEventListener(type, listener, options) {
291
+ // Check if listener is for sub-component
292
+ let prefix = type.split(":").shift();
293
+ if(prefix && this.getSubComponentsNames().includes(prefix)) {
294
+ const subType = type.substring(prefix.length+1);
295
+
296
+ // Special handling for map: not eventtarget
297
+ if(prefix === "map") {
298
+ if(this.map?.on) {
299
+ if(options?.once) { this.map.once(subType, listener); }
300
+ else { this.map.on(subType, listener); }
301
+ }
302
+ else {
303
+ setTimeout(() => {
304
+ this.addEventListener(type, listener, options);
305
+ }, 50);
306
+ }
307
+ }
308
+ // Add directly if available
309
+ else if(this[prefix]?.addEventListener) {
310
+ this[prefix].addEventListener(subType, listener, options);
311
+ }
312
+ // Wait for addEventListener to be available
313
+ else {
314
+ setTimeout(() => this.addEventListener(type, listener, options), 50);
315
+ }
316
+ }
317
+ // Otherwise, reuse classic function
318
+ else {
319
+ super.addEventListener(type, listener, options);
320
+ }
321
+ }
267
322
  }
@@ -77,6 +77,12 @@ export default class CoverageMap extends Basic {
77
77
  return [this.loader, this._mapContainer];
78
78
  }
79
79
 
80
+ getSubComponentsNames() {
81
+ const scn = super.getSubComponentsNames();
82
+ scn.push("map");
83
+ return scn;
84
+ }
85
+
80
86
  /**
81
87
  * Creates map object
82
88
  * @private
@@ -99,6 +99,10 @@ export default class Editor extends Basic {
99
99
  return [this.loader, this._psvContainer, this._mapContainer];
100
100
  }
101
101
 
102
+ getSubComponentsNames() {
103
+ return super.getSubComponentsNames().concat(["map", "psv"]);
104
+ }
105
+
102
106
  /** @private */
103
107
  _initPSV() {
104
108
  try {
@@ -186,6 +186,10 @@ export default class PhotoViewer extends Basic {
186
186
  return [this.loader, this.grid, this.popup, this.slot];
187
187
  }
188
188
 
189
+ getSubComponentsNames() {
190
+ return super.getSubComponentsNames().concat(["psv", "grid", "popup", "urlHandler"]);
191
+ }
192
+
189
193
  /**
190
194
  * Waiting for Photo Sphere Viewer to be available.
191
195
  * @returns {Promise} When PSV is ready to use
@@ -436,6 +440,23 @@ export default class PhotoViewer extends Basic {
436
440
  if(this._lastPsvAnim) { this._lastPsvAnim.cancel(); }
437
441
  this._lastPsvAnim = this.psv.animate(options);
438
442
  }
443
+
444
+ /**
445
+ * Listen to events from this components or one of its sub-components.
446
+ *
447
+ * For example, you can listen to `psv` events using prefix `psv:`.
448
+ *
449
+ * ```js
450
+ * me.addEventListener("psv:picture-loading", doSomething);
451
+ * ```
452
+ * @param {string} type The event type to listen for
453
+ * @param {function} listener The event handler
454
+ * @param {object} [options] [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options)
455
+ * @memberof Panoramax.components.core.PhotoViewer#
456
+ */
457
+ addEventListener(type, listener, options) {
458
+ super.addEventListener(type, listener, options);
459
+ }
439
460
  }
440
461
 
441
462
  customElements.define("pnx-photo-viewer", PhotoViewer);
@@ -197,6 +197,10 @@ export default class Viewer extends PhotoViewer {
197
197
  return "Viewer";
198
198
  }
199
199
 
200
+ getSubComponentsNames() {
201
+ return super.getSubComponentsNames().concat(["mini", "map"]);
202
+ }
203
+
200
204
  /**
201
205
  * Waits for Viewer to be completely ready (map & PSV loaded, first picture also if one is wanted)
202
206
  * @returns {Promise} When viewer is ready
@@ -2,6 +2,7 @@ import { EquirectangularTilesAdapter } from "@photo-sphere-viewer/equirectangula
2
2
 
3
3
  /**
4
4
  * Override of PSV EquirectangularTilesAdapter for fine-tweaking.
5
+ * @class Panoramax.utils.PhotoAdapter
5
6
  * @private
6
7
  */
7
8
  export default class PhotoAdapter extends EquirectangularTilesAdapter {
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  alterPSVState, MAP_FILTERS_JS2URL, alterMapState, alterViewerState, alterPhotoViewerState
3
3
  } from "./InitParameters";
4
- import Viewer from "../components/core/Viewer";
5
4
 
6
5
  // List of supported parameters
7
6
  const MANAGED_PARAMETERS = [
@@ -96,9 +95,6 @@ export default class URLHandler extends EventTarget {
96
95
  }
97
96
  }
98
97
  }
99
- else if(this._parent instanceof Viewer) {
100
- hashParts.map = "none";
101
- }
102
98
  return hashParts;
103
99
  }
104
100
 
@@ -260,7 +256,7 @@ export default class URLHandler extends EventTarget {
260
256
  _onURLChange() {
261
257
  let vals = this.currentURLParams();
262
258
 
263
- if(this._parent instanceof Viewer) { alterViewerState(this._parent, vals); }
259
+ if(this._parent.getClassName() === "Viewer") { alterViewerState(this._parent, vals); }
264
260
  else { alterPhotoViewerState(this._parent, vals); }
265
261
 
266
262
  alterPSVState(this._parent.psv, vals);
@@ -0,0 +1,130 @@
1
+ import Basic from "../../../src/components/core/Basic";
2
+ import API from "../../../src/utils/API";
3
+
4
+ jest.mock("../../../src/utils/API");
5
+ jest.mock("../../../src/utils/i18n");
6
+ jest.mock("../../../src/utils/widgets");
7
+ jest.mock("../../../src/utils/utils");
8
+ jest.mock("../../../package.json", () => ({ version: "1.0.0", repository: { url: "https://example.com" } }));
9
+
10
+ let basic;
11
+ global.console = { info: jest.fn(), error: jest.fn(), warn: jest.fn(), log: global.console.log };
12
+
13
+ beforeEach(() => {
14
+ basic = new Basic(true);
15
+ });
16
+
17
+ afterEach(() => {
18
+ jest.clearAllMocks();
19
+ });
20
+
21
+ describe("constructor", () => {
22
+ it("should initialize with default values", () => {
23
+ expect(basic.users).toEqual(["geovisio"]);
24
+ expect(basic.mapstyle).toBeDefined();
25
+ expect(basic.lang).toBeNull();
26
+ expect(basic.endpoint).toBeNull();
27
+ expect(basic.picture).toBeNull();
28
+ expect(basic.sequence).toBeNull();
29
+ });
30
+
31
+ it("should log version info on initialization", () => {
32
+ new Basic(true);
33
+ expect(console.info).toHaveBeenCalledWith(expect.stringContaining("Panoramax Basic - Version 1.0.0"));
34
+ });
35
+
36
+ it("should set up API when endpoint is provided", async () => {
37
+ basic.endpoint = "https://api.example.com";
38
+ basic.connectedCallback();
39
+ expect(API).toHaveBeenCalledWith("https://api.example.com", expect.any(Object));
40
+ });
41
+ });
42
+
43
+ describe("attributeChangedCallback", () => {
44
+ it("should dispatch select event when picture or sequence changes", () => {
45
+ const eventSpy = jest.spyOn(basic, "dispatchEvent");
46
+ basic.attributeChangedCallback("picture", null, "new-pic-id");
47
+ expect(eventSpy).toHaveBeenCalledWith(expect.any(CustomEvent));
48
+ eventSpy.mockRestore();
49
+ });
50
+ });
51
+
52
+ describe("getClassName", () => {
53
+ it("works", () => {
54
+ expect(basic.getClassName()).toBe("Basic");
55
+ });
56
+ });
57
+
58
+ describe("select", () => {
59
+ it("works", () => {
60
+ basic.select("new-seq-id", "new-pic-id");
61
+ expect(basic.picture).toBe("new-pic-id");
62
+ expect(basic.sequence).toBe("new-seq-id");
63
+ });
64
+ });
65
+
66
+ describe("offsetWidth", () => {
67
+ it("works", () => {
68
+ basic.offsetWidth = 500;
69
+ expect(basic.isWidthSmall()).toBe(true);
70
+ basic.offsetWidth = 600;
71
+ expect(basic.isWidthSmall()).toBe(false);
72
+ });
73
+ });
74
+
75
+ describe("offsetHeight", () => {
76
+ it("works", () => {
77
+ basic.offsetHeight = 300;
78
+ expect(basic.isHeightSmall()).toBe(true);
79
+ basic.offsetHeight = 500;
80
+ expect(basic.isHeightSmall()).toBe(false);
81
+ });
82
+ });
83
+
84
+ describe("getSubComponentsNames", () => {
85
+ it("works", () => {
86
+ expect(basic.getSubComponentsNames()).toEqual(["loader", "api"]);
87
+ });
88
+ });
89
+
90
+ describe("addEventListener", () => {
91
+ it("should add event listener for standard events", () => {
92
+ const listener = jest.fn();
93
+ basic.addEventListener("ready", listener);
94
+ basic.dispatchEvent(new CustomEvent("ready"));
95
+ expect(listener).toHaveBeenCalled();
96
+ });
97
+
98
+ it("should add event listener with options", () => {
99
+ const listener = jest.fn();
100
+ basic.addEventListener("once-event", listener, { once: true });
101
+ basic.dispatchEvent(new CustomEvent("once-event"));
102
+ basic.dispatchEvent(new CustomEvent("once-event"));
103
+ expect(listener).toHaveBeenCalledTimes(1);
104
+ });
105
+
106
+ it("should wait for map to be available", () => {
107
+ const listener = jest.fn();
108
+ global.setTimeout = jest.fn();
109
+ basic.getSubComponentsNames = () => ["loader", "api", "map"];
110
+ basic.map = null;
111
+ basic.addEventListener("map:move", listener);
112
+ expect(global.setTimeout).toHaveBeenCalled();
113
+ });
114
+
115
+ it("should add event listener with once option for map", () => {
116
+ const listener = jest.fn();
117
+ basic.getSubComponentsNames = () => ["loader", "api", "map"];
118
+ basic.map = { on: jest.fn(), once: jest.fn() };
119
+ basic.addEventListener("map:move", listener, { once: true });
120
+ expect(basic.map.on).toHaveBeenCalledTimes(0);
121
+ expect(basic.map.once).toHaveBeenCalledWith("move", listener);
122
+ });
123
+
124
+ it("adds listener to basic if no sub-component matches prefix", () => {
125
+ const listener = jest.fn();
126
+ basic.addEventListener("unknown:event", listener);
127
+ basic.dispatchEvent(new CustomEvent("unknown:event"));
128
+ expect(listener).toHaveBeenCalled();
129
+ });
130
+ });
@@ -0,0 +1,20 @@
1
+ jest.mock("../../../src/components/core/Basic", () => (
2
+ class Basic extends EventTarget {
3
+ constructor() {
4
+ super();
5
+ this.loader = { setAttribute: jest.fn() };
6
+ this.api = {
7
+ getMapStyle: () => ({}),
8
+ _getMapRequestTransform: () => ({}),
9
+ };
10
+ this._t = { maplibre: {}, psv: {} };
11
+ }
12
+ isWidthSmall() { return false; }
13
+ getSubComponentsNames() {
14
+ return ["loader", "api"];
15
+ }
16
+ onceAPIReady() {
17
+ return Promise.resolve();
18
+ }
19
+ }
20
+ ));