@panoramax/web-viewer 3.2.3-develop-fbf5d630 → 3.2.3-develop-881b0162

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.
@@ -89,6 +89,7 @@ jest.mock("maplibre-gl", () => ({
89
89
  constructor(opts) {
90
90
  this._mapOpts = opts;
91
91
  this._handlers = {};
92
+ this._handlersOnce = {};
92
93
  }
93
94
  getContainer() {
94
95
  return this._mapOpts.container;
@@ -98,6 +99,7 @@ jest.mock("maplibre-gl", () => ({
98
99
  addLayer() {;}
99
100
  getLayer() {;}
100
101
  setLayoutProperty() {;}
102
+ setPaintProperty() {;}
101
103
  getStyle() {
102
104
  return {
103
105
  layers: [],
@@ -110,8 +112,16 @@ jest.mock("maplibre-gl", () => ({
110
112
  if(!this._handlers[type]) { this._handlers[type] = []; }
111
113
  this._handlers[type].push(handler);
112
114
  }
115
+ once(type, handler) {
116
+ if(!this._handlersOnce[type]) { this._handlersOnce[type] = []; }
117
+ this._handlersOnce[type].push(handler);
118
+ }
113
119
  fire(type, opts) {
114
120
  this._handlers[type]?.forEach(f => f(opts));
121
+ this._handlersOnce?.[type]?.forEach(f => f(opts));
122
+ if(this._handlersOnce?.[type]) {
123
+ this._handlersOnce[type] = [];
124
+ }
115
125
  }
116
126
  },
117
127
  LngLat: function() {
@@ -164,13 +164,28 @@ Event for sequence/picture selection
164
164
  <a name="Panoramax.components.core.Basic+event_ready"></a>
165
165
 
166
166
  ### "ready"
167
- Event for component being ready to use (API loaded)
167
+ Event when component is ready to use.
168
+ This happens when loader screen disappear, with picture and map loaded.
169
+
170
+ To follow more precisely loading steps, you can also watch for sub-components `ready` events.
171
+ ```js
172
+ // Watch API-readiness
173
+ viewer.addEventListener("api:ready", ...); // From parent
174
+ viewer.api.addEventListener("ready", ...); // Or on sub-component
175
+ ```
168
176
 
169
177
  **Kind**: event emitted by [<code>Basic</code>](#Panoramax.components.core.Basic)
170
178
  <a name="Panoramax.components.core.Basic+event_broken"></a>
171
179
 
172
180
  ### "broken"
173
- Event for viewer failing to initially load
181
+ Event for viewer failing to initially load.
182
+
183
+ To follow more precisely loading failures, you can also watch for sub-components `broken` events.
184
+ ```js
185
+ // Watch API breaks
186
+ viewer.addEventListener("api:broken", ...); // From parent
187
+ viewer.api.addEventListener("broken", ...); // Or on sub-component
188
+ ```
174
189
 
175
190
  **Kind**: event emitted by [<code>Basic</code>](#Panoramax.components.core.Basic)
176
191
  **Properties**
@@ -178,13 +178,28 @@ Event for sequence/picture selection
178
178
  <a name="Panoramax.components.core.Basic+event_ready"></a>
179
179
 
180
180
  ### "ready"
181
- Event for component being ready to use (API loaded)
181
+ Event when component is ready to use.
182
+ This happens when loader screen disappear, with picture and map loaded.
183
+
184
+ To follow more precisely loading steps, you can also watch for sub-components `ready` events.
185
+ ```js
186
+ // Watch API-readiness
187
+ viewer.addEventListener("api:ready", ...); // From parent
188
+ viewer.api.addEventListener("ready", ...); // Or on sub-component
189
+ ```
182
190
 
183
191
  **Kind**: event emitted by [<code>CoverageMap</code>](#Panoramax.components.core.CoverageMap)
184
192
  <a name="Panoramax.components.core.Basic+event_broken"></a>
185
193
 
186
194
  ### "broken"
187
- Event for viewer failing to initially load
195
+ Event for viewer failing to initially load.
196
+
197
+ To follow more precisely loading failures, you can also watch for sub-components `broken` events.
198
+ ```js
199
+ // Watch API breaks
200
+ viewer.addEventListener("api:broken", ...); // From parent
201
+ viewer.api.addEventListener("broken", ...); // Or on sub-component
202
+ ```
188
203
 
189
204
  **Kind**: event emitted by [<code>CoverageMap</code>](#Panoramax.components.core.CoverageMap)
190
205
  **Properties**
@@ -193,13 +193,28 @@ Event for sequence/picture selection
193
193
  <a name="Panoramax.components.core.Basic+event_ready"></a>
194
194
 
195
195
  ### "ready"
196
- Event for component being ready to use (API loaded)
196
+ Event when component is ready to use.
197
+ This happens when loader screen disappear, with picture and map loaded.
198
+
199
+ To follow more precisely loading steps, you can also watch for sub-components `ready` events.
200
+ ```js
201
+ // Watch API-readiness
202
+ viewer.addEventListener("api:ready", ...); // From parent
203
+ viewer.api.addEventListener("ready", ...); // Or on sub-component
204
+ ```
197
205
 
198
206
  **Kind**: event emitted by [<code>Editor</code>](#Panoramax.components.core.Editor)
199
207
  <a name="Panoramax.components.core.Basic+event_broken"></a>
200
208
 
201
209
  ### "broken"
202
- Event for viewer failing to initially load
210
+ Event for viewer failing to initially load.
211
+
212
+ To follow more precisely loading failures, you can also watch for sub-components `broken` events.
213
+ ```js
214
+ // Watch API breaks
215
+ viewer.addEventListener("api:broken", ...); // From parent
216
+ viewer.api.addEventListener("broken", ...); // Or on sub-component
217
+ ```
203
218
 
204
219
  **Kind**: event emitted by [<code>Editor</code>](#Panoramax.components.core.Editor)
205
220
  **Properties**
@@ -273,13 +273,28 @@ Event for sequence/picture selection
273
273
  <a name="Panoramax.components.core.Basic+event_ready"></a>
274
274
 
275
275
  ### "ready"
276
- Event for component being ready to use (API loaded)
276
+ Event when component is ready to use.
277
+ This happens when loader screen disappear, with picture and map loaded.
278
+
279
+ To follow more precisely loading steps, you can also watch for sub-components `ready` events.
280
+ ```js
281
+ // Watch API-readiness
282
+ viewer.addEventListener("api:ready", ...); // From parent
283
+ viewer.api.addEventListener("ready", ...); // Or on sub-component
284
+ ```
277
285
 
278
286
  **Kind**: event emitted by [<code>PhotoViewer</code>](#Panoramax.components.core.PhotoViewer)
279
287
  <a name="Panoramax.components.core.Basic+event_broken"></a>
280
288
 
281
289
  ### "broken"
282
- Event for viewer failing to initially load
290
+ Event for viewer failing to initially load.
291
+
292
+ To follow more precisely loading failures, you can also watch for sub-components `broken` events.
293
+ ```js
294
+ // Watch API breaks
295
+ viewer.addEventListener("api:broken", ...); // From parent
296
+ viewer.api.addEventListener("broken", ...); // Or on sub-component
297
+ ```
283
298
 
284
299
  **Kind**: event emitted by [<code>PhotoViewer</code>](#Panoramax.components.core.PhotoViewer)
285
300
  **Properties**
@@ -316,13 +316,28 @@ Event for sequence/picture selection
316
316
  <a name="Panoramax.components.core.Basic+event_ready"></a>
317
317
 
318
318
  ### "ready"
319
- Event for component being ready to use (API loaded)
319
+ Event when component is ready to use.
320
+ This happens when loader screen disappear, with picture and map loaded.
321
+
322
+ To follow more precisely loading steps, you can also watch for sub-components `ready` events.
323
+ ```js
324
+ // Watch API-readiness
325
+ viewer.addEventListener("api:ready", ...); // From parent
326
+ viewer.api.addEventListener("ready", ...); // Or on sub-component
327
+ ```
320
328
 
321
329
  **Kind**: event emitted by [<code>Viewer</code>](#Panoramax.components.core.Viewer)
322
330
  <a name="Panoramax.components.core.Basic+event_broken"></a>
323
331
 
324
332
  ### "broken"
325
- Event for viewer failing to initially load
333
+ Event for viewer failing to initially load.
334
+
335
+ To follow more precisely loading failures, you can also watch for sub-components `broken` events.
336
+ ```js
337
+ // Watch API breaks
338
+ viewer.addEventListener("api:broken", ...); // From parent
339
+ viewer.api.addEventListener("broken", ...); // Or on sub-component
340
+ ```
326
341
 
327
342
  **Kind**: event emitted by [<code>Viewer</code>](#Panoramax.components.core.Viewer)
328
343
  **Properties**
@@ -3,7 +3,7 @@
3
3
  ## Panoramax.components.ui.Map ⇐ <code>[maplibregl.Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/)</code>
4
4
  **Kind**: static class of <code>Panoramax.components.ui</code>
5
5
  **Extends**: <code>[maplibregl.Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/)</code>
6
- **Emits**: [<code>background-changed</code>](#Panoramax.components.ui.Map+event_background-changed), [<code>users-changed</code>](#Panoramax.components.ui.Map+event_users-changed), [<code>sequence-hover</code>](#Panoramax.components.ui.Map+event_sequence-hover), [<code>sequence-click</code>](#Panoramax.components.ui.Map+event_sequence-click), [<code>picture-click</code>](#Panoramax.components.ui.Map+event_picture-click)
6
+ **Emits**: [<code>background-changed</code>](#Panoramax.components.ui.Map+event_background-changed), [<code>users-changed</code>](#Panoramax.components.ui.Map+event_users-changed), [<code>sequence-hover</code>](#Panoramax.components.ui.Map+event_sequence-hover), [<code>sequence-click</code>](#Panoramax.components.ui.Map+event_sequence-click), [<code>picture-click</code>](#Panoramax.components.ui.Map+event_picture-click), [<code>ready</code>](#Panoramax.components.ui.Map+event_ready)
7
7
 
8
8
  * [.Map](#Panoramax.components.ui.Map) ⇐ <code>[maplibregl.Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/)</code>
9
9
  * [new Map(parent, container, [options])](#new_Panoramax.components.ui.Map_new)
@@ -18,6 +18,8 @@
18
18
  * [.filterUserLayersContent(dataType, filter)](#Panoramax.components.ui.Map+filterUserLayersContent)
19
19
  * [.displayPictureMarker(lon, lat, heading, [skipCenter])](#Panoramax.components.ui.Map+displayPictureMarker)
20
20
  * [.reloadLayersStyles()](#Panoramax.components.ui.Map+reloadLayersStyles)
21
+ * [.addEventListener(type, listener)](#Panoramax.components.ui.Map+addEventListener)
22
+ * ["ready"](#Panoramax.components.ui.Map+event_ready)
21
23
  * ["background-changed"](#Panoramax.components.ui.Map+event_background-changed)
22
24
  * ["users-changed"](#Panoramax.components.ui.Map+event_users-changed)
23
25
  * ["sequence-hover"](#Panoramax.components.ui.Map+event_sequence-hover)
@@ -36,6 +38,7 @@ A more complete version of Map (with filters & themes) is available through [Map
36
38
  ⚠️ This class doesn't inherit from [EventTarget](https://developer.mozilla.org/fr/docs/Web/API/EventTarget), so it doesn't have `addEventListener` and `dispatchEvent` functions.
37
39
  It uses instead [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and `fire` functions from MapLibre Map class.
38
40
  `fire` function doesn't take directly [`Event`](https://developer.mozilla.org/fr/docs/Web/API/Event) objects, but a string and object data.
41
+ A shorthand `addEventListener` function is added for simpler usage.
39
42
 
40
43
 
41
44
  | Param | Type | Default | Description |
@@ -150,6 +153,27 @@ Forces reload of pictures/sequences layer styles.
150
153
  This is useful after a map theme change.
151
154
 
152
155
  **Kind**: instance method of [<code>Map</code>](#Panoramax.components.ui.Map)
156
+ <a name="Panoramax.components.ui.Map+addEventListener"></a>
157
+
158
+ ### map.addEventListener(type, listener)
159
+ Listen to map events.
160
+ This is a binder to [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and [`once`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#once) MapLibre GL functions.
161
+
162
+ **Kind**: instance method of [<code>Map</code>](#Panoramax.components.ui.Map)
163
+
164
+ | Param | Type | Default | Description |
165
+ | --- | --- | --- | --- |
166
+ | type | <code>string</code> | | The event type to listen for |
167
+ | listener | <code>function</code> | | The event handler |
168
+ | [options.once] | <code>boolean</code> | <code>false</code> | Set to true to only listen to first event. |
169
+
170
+ <a name="Panoramax.components.ui.Map+event_ready"></a>
171
+
172
+ ### "ready"
173
+ Event when map is ready to display.
174
+ This includes Maplibre initial load, enough map data display and styling.
175
+
176
+ **Kind**: event emitted by [<code>Map</code>](#Panoramax.components.ui.Map)
153
177
  <a name="Panoramax.components.ui.Map+event_background-changed"></a>
154
178
 
155
179
  ### "background-changed"
@@ -18,7 +18,9 @@
18
18
  * [.filterUserLayersContent(dataType, filter)](Map.md/#Panoramax.components.ui.Map+filterUserLayersContent)
19
19
  * [.displayPictureMarker(lon, lat, heading, [skipCenter])](#Panoramax.components.ui.Map+displayPictureMarker)
20
20
  * [.reloadLayersStyles()](Map.md/#Panoramax.components.ui.Map+reloadLayersStyles)
21
+ * [.addEventListener(type, listener)](Map.md/#Panoramax.components.ui.Map+addEventListener)
21
22
  * ["filters-changed"](#Panoramax.components.ui.MapMore+event_filters-changed)
23
+ * ["ready"](Map.md/#Panoramax.components.ui.Map+event_ready)
22
24
  * ["background-changed"](Map.md/#Panoramax.components.ui.Map+event_background-changed)
23
25
  * ["users-changed"](Map.md/#Panoramax.components.ui.Map+event_users-changed)
24
26
  * ["sequence-hover"](Map.md/#Panoramax.components.ui.Map+event_sequence-hover)
@@ -151,6 +153,20 @@ Forces reload of pictures/sequences layer styles.
151
153
  This is useful after a map theme change.
152
154
 
153
155
  **Kind**: instance method of [<code>MapMore</code>](#Panoramax.components.ui.MapMore)
156
+ <a name="Panoramax.components.ui.Map+addEventListener"></a>
157
+
158
+ ### mapMore.addEventListener(type, listener)
159
+ Listen to map events.
160
+ This is a binder to [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and [`once`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#once) MapLibre GL functions.
161
+
162
+ **Kind**: instance method of [<code>MapMore</code>](#Panoramax.components.ui.MapMore)
163
+
164
+ | Param | Type | Default | Description |
165
+ | --- | --- | --- | --- |
166
+ | type | <code>string</code> | | The event type to listen for |
167
+ | listener | <code>function</code> | | The event handler |
168
+ | [options.once] | <code>boolean</code> | <code>false</code> | Set to true to only listen to first event. |
169
+
154
170
  <a name="Panoramax.components.ui.MapMore+event_filters-changed"></a>
155
171
 
156
172
  ### "filters-changed"
@@ -168,6 +184,13 @@ Event for filters changes
168
184
  | [theme] | <code>string</code> | Map theme |
169
185
  | [qualityscore] | <code>Array.&lt;number&gt;</code> | QualityScore values, as a list of 1 to 5 grades |
170
186
 
187
+ <a name="Panoramax.components.ui.Map+event_ready"></a>
188
+
189
+ ### "ready"
190
+ Event when map is ready to display.
191
+ This includes Maplibre initial load, enough map data display and styling.
192
+
193
+ **Kind**: event emitted by [<code>MapMore</code>](#Panoramax.components.ui.MapMore)
171
194
  <a name="Panoramax.components.ui.Map+event_background-changed"></a>
172
195
 
173
196
  ### "background-changed"
@@ -3,7 +3,7 @@
3
3
  ## Panoramax.components.ui.Photo ⇐ <code>[photo-sphere-viewer.core.Viewer](https://photo-sphere-viewer.js.org/api/classes/Core.Viewer.html)</code>
4
4
  **Kind**: static class of <code>Panoramax.components.ui</code>
5
5
  **Extends**: <code>[photo-sphere-viewer.core.Viewer](https://photo-sphere-viewer.js.org/api/classes/Core.Viewer.html)</code>
6
- **Emits**: [<code>picture-loading</code>](#Panoramax.components.ui.Photo+event_picture-loading), [<code>picture-preview-started</code>](#Panoramax.components.ui.Photo+event_picture-preview-started), [<code>picture-preview-stopped</code>](#Panoramax.components.ui.Photo+event_picture-preview-stopped), [<code>view-rotated</code>](#Panoramax.components.ui.Photo+event_view-rotated), [<code>picture-loaded</code>](#Panoramax.components.ui.Photo+event_picture-loaded), [<code>picture-tiles-loaded</code>](#Panoramax.components.ui.Photo+event_picture-tiles-loaded), [<code>transition-duration-changed</code>](#Panoramax.components.ui.Photo+event_transition-duration-changed), [<code>sequence-playing</code>](#Panoramax.components.ui.Photo+event_sequence-playing), [<code>sequence-stopped</code>](#Panoramax.components.ui.Photo+event_sequence-stopped), [<code>pictures-navigation-changed</code>](#Panoramax.components.ui.Photo+event_pictures-navigation-changed)
6
+ **Emits**: [<code>picture-loading</code>](#Panoramax.components.ui.Photo+event_picture-loading), [<code>picture-preview-started</code>](#Panoramax.components.ui.Photo+event_picture-preview-started), [<code>picture-preview-stopped</code>](#Panoramax.components.ui.Photo+event_picture-preview-stopped), [<code>view-rotated</code>](#Panoramax.components.ui.Photo+event_view-rotated), [<code>picture-loaded</code>](#Panoramax.components.ui.Photo+event_picture-loaded), [<code>picture-tiles-loaded</code>](#Panoramax.components.ui.Photo+event_picture-tiles-loaded), [<code>transition-duration-changed</code>](#Panoramax.components.ui.Photo+event_transition-duration-changed), [<code>sequence-playing</code>](#Panoramax.components.ui.Photo+event_sequence-playing), [<code>sequence-stopped</code>](#Panoramax.components.ui.Photo+event_sequence-stopped), [<code>pictures-navigation-changed</code>](#Panoramax.components.ui.Photo+event_pictures-navigation-changed), [<code>ready</code>](#Panoramax.components.ui.Photo+event_ready)
7
7
 
8
8
  * [.Photo](#Panoramax.components.ui.Photo) ⇐ <code>[photo-sphere-viewer.core.Viewer](https://photo-sphere-viewer.js.org/api/classes/Core.Viewer.html)</code>
9
9
  * [new Photo(parent, container, [options])](#new_Panoramax.components.ui.Photo_new)
@@ -29,6 +29,7 @@
29
29
  * [.getPicturesNavigation()](#Panoramax.components.ui.Photo+getPicturesNavigation) ⇒ <code>string</code>
30
30
  * [.setPicturesNavigation(pn)](#Panoramax.components.ui.Photo+setPicturesNavigation)
31
31
  * [.forceRefresh()](#Panoramax.components.ui.Photo+forceRefresh)
32
+ * ["ready"](#Panoramax.components.ui.Photo+event_ready)
32
33
  * ["picture-loading"](#Panoramax.components.ui.Photo+event_picture-loading)
33
34
  * ["picture-preview-started"](#Panoramax.components.ui.Photo+event_picture-preview-started)
34
35
  * ["picture-preview-stopped"](#Panoramax.components.ui.Photo+event_picture-preview-stopped)
@@ -251,6 +252,13 @@ Switch the allowed navigation between pictures.
251
252
  Force reload of texture and tiles.
252
253
 
253
254
  **Kind**: instance method of [<code>Photo</code>](#Panoramax.components.ui.Photo)
255
+ <a name="Panoramax.components.ui.Photo+event_ready"></a>
256
+
257
+ ### "ready"
258
+ Triggered once when the panorama image has been loaded and the viewer is ready to perform the first render.
259
+
260
+ **Kind**: event emitted by [<code>Photo</code>](#Panoramax.components.ui.Photo)
261
+ **See**: [Photo Sphere Viewer documentation](https://photo-sphere-viewer.js.org/guide/events.html#ready)
254
262
  <a name="Panoramax.components.ui.Photo+event_picture-loading"></a>
255
263
 
256
264
  ### "picture-loading"
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Panoramax.utils.API
4
4
  **Kind**: static class of <code>Panoramax.utils</code>
5
+ **Emits**: [<code>ready</code>](#Panoramax.utils.API+event_ready), [<code>broken</code>](#Panoramax.utils.API+event_broken)
5
6
 
6
7
  * [.API](#Panoramax.utils.API)
7
8
  * [new API(endpoint, [options])](#new_Panoramax.utils.API_new)
@@ -24,6 +25,8 @@
24
25
  * [.searchUsers(query)](#Panoramax.utils.API+searchUsers) ⇒ <code>Promise</code>
25
26
  * [.getUserName(userId)](#Panoramax.utils.API+getUserName) ⇒ <code>Promise</code>
26
27
  * [.sendReport(data)](#Panoramax.utils.API+sendReport) ⇒ <code>Promise</code>
28
+ * ["broken"](#Panoramax.utils.API+event_broken)
29
+ * ["ready"](#Panoramax.utils.API+event_ready)
27
30
  * _static_
28
31
  * [.isValidHttpUrl(str)](#Panoramax.utils.API.isValidHttpUrl) ⇒ <code>boolean</code>
29
32
  * [.isIdValid(id)](#Panoramax.utils.API.isIdValid) ⇒ <code>boolean</code>
@@ -281,6 +284,26 @@ Send a report to API
281
284
  | --- | --- | --- |
282
285
  | data | <code>object</code> | The input form data |
283
286
 
287
+ <a name="Panoramax.utils.API+event_broken"></a>
288
+
289
+ ### "broken"
290
+ Event when API is broken.
291
+ This happens on any API loading or map styling issue.
292
+
293
+ **Kind**: event emitted by [<code>API</code>](#Panoramax.utils.API)
294
+ **Properties**
295
+
296
+ | Name | Type | Description |
297
+ | --- | --- | --- |
298
+ | detail.error | <code>Error</code> | The original error |
299
+
300
+ <a name="Panoramax.utils.API+event_ready"></a>
301
+
302
+ ### "ready"
303
+ Event when API is ready to use.
304
+ This happens after initial API read and map styles load.
305
+
306
+ **Kind**: event emitted by [<code>API</code>](#Panoramax.utils.API)
284
307
  <a name="Panoramax.utils.API.isValidHttpUrl"></a>
285
308
 
286
309
  ### API.isValidHttpUrl(str) ⇒ <code>boolean</code>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panoramax/web-viewer",
3
- "version": "3.2.3-develop-fbf5d630",
3
+ "version": "3.2.3-develop-881b0162",
4
4
  "description": "Panoramax web viewer for geolocated pictures",
5
5
  "main": "build/index.js",
6
6
  "author": "Panoramax team",
@@ -100,7 +100,7 @@
100
100
  "@photo-sphere-viewer/virtual-tour-plugin": "5.12.1",
101
101
  "json5": "^2.2.3",
102
102
  "lit": "^3.2.1",
103
- "maplibre-gl": "^5.2.0",
103
+ "maplibre-gl": "^5.3.0",
104
104
  "pmtiles": "^4.3.0",
105
105
  "query-selector-shadow-dom": "^1.0.1"
106
106
  },
@@ -322,20 +322,8 @@ export default class Basic extends LitElement {
322
322
  if(prefix && this.getSubComponentsNames().includes(prefix)) {
323
323
  const subType = type.substring(prefix.length+1);
324
324
 
325
- // Special handling for map: not eventtarget
326
- if(prefix === "map") {
327
- if(this.map?.on) {
328
- if(options?.once) { this.map.once(subType, listener); }
329
- else { this.map.on(subType, listener); }
330
- }
331
- else {
332
- setTimeout(() => {
333
- this.addEventListener(type, listener, options);
334
- }, 50);
335
- }
336
- }
337
325
  // Add directly if available
338
- else if(this[prefix]?.addEventListener) {
326
+ if(this[prefix]?.addEventListener) {
339
327
  this[prefix].addEventListener(subType, listener, options);
340
328
  }
341
329
  // Wait for addEventListener to be available
@@ -253,11 +253,11 @@ export default class Viewer extends PhotoViewer {
253
253
  return new Promise(resolve => {
254
254
  waiter = setInterval(() => {
255
255
  if(typeof this.map === "object") {
256
- if(this.map.loaded?.()) {
256
+ if(this.map?.loaded?.()) {
257
257
  clearInterval(waiter);
258
258
  resolve();
259
259
  }
260
- else if(this.map.once) {
260
+ else if(this.map?.once) {
261
261
  this.map.once("render", () => {
262
262
  clearInterval(waiter);
263
263
  resolve();
@@ -110,7 +110,15 @@ export default class Loader extends LitElement {
110
110
  setTimeout(() => this.parentNode.removeChild(this), 2000);
111
111
 
112
112
  /**
113
- * Event for component being ready to use (API loaded)
113
+ * Event when component is ready to use.
114
+ * This happens when loader screen disappear, with picture and map loaded.
115
+ *
116
+ * To follow more precisely loading steps, you can also watch for sub-components `ready` events.
117
+ * ```js
118
+ * // Watch API-readiness
119
+ * viewer.addEventListener("api:ready", ...); // From parent
120
+ * viewer.api.addEventListener("ready", ...); // Or on sub-component
121
+ * ```
114
122
  * @event Panoramax.components.core.Basic#ready
115
123
  * @type {CustomEvent}
116
124
  */
@@ -138,7 +146,14 @@ export default class Loader extends LitElement {
138
146
  const errLabel = errMeaningful || "Panoramax JS had a blocking exception";
139
147
 
140
148
  /**
141
- * Event for viewer failing to initially load
149
+ * Event for viewer failing to initially load.
150
+ *
151
+ * To follow more precisely loading failures, you can also watch for sub-components `broken` events.
152
+ * ```js
153
+ * // Watch API breaks
154
+ * viewer.addEventListener("api:broken", ...); // From parent
155
+ * viewer.api.addEventListener("broken", ...); // Or on sub-component
156
+ * ```
142
157
  * @event Panoramax.components.core.Basic#broken
143
158
  * @type {CustomEvent}
144
159
  * @property {string} detail.error The user-friendly error message to display
@@ -28,6 +28,7 @@ maplibregl.addProtocol("pmtiles", new pmtiles.Protocol().tile);
28
28
  * ⚠️ This class doesn't inherit from [EventTarget](https://developer.mozilla.org/fr/docs/Web/API/EventTarget), so it doesn't have `addEventListener` and `dispatchEvent` functions.
29
29
  * It uses instead [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and `fire` functions from MapLibre Map class.
30
30
  * `fire` function doesn't take directly [`Event`](https://developer.mozilla.org/fr/docs/Web/API/Event) objects, but a string and object data.
31
+ * A shorthand `addEventListener` function is added for simpler usage.
31
32
  * @class Panoramax.components.ui.Map
32
33
  * @extends [maplibregl.Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/)
33
34
  * @param {Panoramax.components.core.Basic} parent The parent view
@@ -41,6 +42,7 @@ maplibregl.addProtocol("pmtiles", new pmtiles.Protocol().tile);
41
42
  * @fires Panoramax.components.ui.Map#sequence-hover
42
43
  * @fires Panoramax.components.ui.Map#sequence-click
43
44
  * @fires Panoramax.components.ui.Map#picture-click
45
+ * @fires Panoramax.components.ui.Map#ready
44
46
  * @example
45
47
  * const map = new Panoramax.components.ui.Map(viewer, mapNode, {center: {lat: 48.7, lng: -1.7}});
46
48
  */
@@ -103,7 +105,7 @@ export default class Map extends maplibregl.Map {
103
105
 
104
106
  // Timeout for initial loading
105
107
  setTimeout(() => {
106
- if(!this.loaded() && this._parent.loader.isVisible()) {
108
+ if(!this.loaded() && this._parent?.loader.isVisible()) {
107
109
  this._parent.loader.dismiss({}, this._parent._t.map.slow_loading, () => {});
108
110
  }
109
111
  }, 15000);
@@ -118,6 +120,14 @@ export default class Map extends maplibregl.Map {
118
120
  this.resize();
119
121
  await this.setVisibleUsers(this._parent.users);
120
122
  this.reloadLayersStyles();
123
+
124
+ /**
125
+ * Event when map is ready to display.
126
+ * This includes Maplibre initial load, enough map data display and styling.
127
+ * @event Panoramax.components.ui.Map#ready
128
+ * @type {maplibregl.util.evented.Event}
129
+ */
130
+ this.fire("ready");
121
131
  }
122
132
 
123
133
  /**
@@ -917,4 +927,17 @@ export default class Map extends maplibregl.Map {
917
927
  });
918
928
  }
919
929
  }
930
+
931
+ /**
932
+ * Listen to map events.
933
+ * This is a binder to [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and [`once`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#once) MapLibre GL functions.
934
+ * @param {string} type The event type to listen for
935
+ * @param {function} listener The event handler
936
+ * @param {boolean} [options.once=false] Set to true to only listen to first event.
937
+ * @memberof Panoramax.components.ui.Map#
938
+ */
939
+ addEventListener(type, listener, options = {}) {
940
+ if(options?.once) { this.once(type, listener); }
941
+ else { this.on(type, listener); }
942
+ }
920
943
  }
@@ -43,6 +43,14 @@ export const PIC_MAX_STAY_DURATION = 3000;
43
43
 
44
44
  PSViewer.useNewAnglesOrder = true;
45
45
 
46
+ /**
47
+ * Triggered once when the panorama image has been loaded and the viewer is ready to perform the first render.
48
+ * @see [Photo Sphere Viewer documentation](https://photo-sphere-viewer.js.org/guide/events.html#ready)
49
+ * @event Panoramax.components.ui.Photo#ready
50
+ * @memberof Panoramax.components.ui.Photo
51
+ * @type {Event}
52
+ */
53
+
46
54
  /**
47
55
  * Photo is the component showing a single picture.
48
56
  * It uses Photo Sphere Viewer as a basis, and pre-configure dialog with STAC API.
@@ -68,6 +76,7 @@ PSViewer.useNewAnglesOrder = true;
68
76
  * @fires Panoramax.components.ui.Photo#sequence-playing
69
77
  * @fires Panoramax.components.ui.Photo#sequence-stopped
70
78
  * @fires Panoramax.components.ui.Photo#pictures-navigation-changed
79
+ * @fires Panoramax.components.ui.Photo#ready
71
80
  * @example
72
81
  * const psv = new Panoramax.components.ui.Photo(viewer, psvNode, {transitionDuration: 500})
73
82
  */
@@ -162,7 +162,7 @@
162
162
  "filter_date_1month": "1 個月",
163
163
  "filter_date_1year": "1 年",
164
164
  "qualityscore_doc_2": "分數以 A/B/C/D/E 等級的形式顯示 (A 最佳、E 最差) ,並透過這個量尺視覺化顯示:",
165
- "qualityscore_doc_link": "關於品質分數的計算方式,請參閱我們的文件。",
165
+ "qualityscore_doc_link": "深入了解品質分數",
166
166
  "qualityscore_title": "關於品質分數",
167
167
  "qualityscore_doc_1": "Panoramax 為每張相片提供品質分數,這能幫助我們輕鬆地在地圖上篩選和顯示高品質相片。",
168
168
  "qualityscore_doc_3": "它根據 GPS 精確度和相片解析度計算得出,專業設備的等級為 A、360° 運動相機的等級為 B、智慧型手機的等級為 C/D/E。",
@@ -172,7 +172,9 @@
172
172
  "loading": "正在載入…",
173
173
  "not_public": "未公開顯示",
174
174
  "thumbnail": "遊標懸停相片的縮圖",
175
- "slow_loading": "地圖載入速度緩慢而且可能出現損壞"
175
+ "slow_loading": "地圖載入速度緩慢而且可能出現損壞",
176
+ "more_panoramax": "深入了解 Panoramax",
177
+ "map_data": "地圖資料:"
176
178
  },
177
179
  "psv": {
178
180
  "ctrlZoom": "使用 CTRL + 滑鼠滾輪縮放圖片",
package/src/utils/API.js CHANGED
@@ -6,6 +6,8 @@ import { isNullId } from "./utils";
6
6
  *
7
7
  * @class Panoramax.utils.API
8
8
  * @typicalname api
9
+ * @fires Panoramax.utils.API#ready
10
+ * @fires Panoramax.utils.API#broken
9
11
  * @param {string} endpoint The endpoint. It corresponds to the <a href="https://github.com/radiantearth/stac-api-spec/blob/main/overview.md#example-landing-page">STAC landing page</a>, with all links describing the API capabilites.
10
12
  * @param {object} [options] Options
11
13
  * @param {string|object} [options.style] General map style
@@ -14,10 +16,14 @@ import { isNullId } from "./utils";
14
16
  * @param {object} [options.fetch] Set custom options for fetch calls made against API ([same syntax as fetch options parameter](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters))
15
17
  * @param {string[]} [options.users] List of initial user IDs to load map styles for
16
18
  */
17
- export default class API {
19
+ export default class API extends EventTarget {
18
20
  constructor(endpoint, options = {}) {
21
+ super();
22
+
19
23
  if(endpoint === null || endpoint === undefined || typeof endpoint !== "string") {
20
- throw new Error("endpoint parameter is empty or not a valid string");
24
+ const e = new Error("endpoint parameter is empty or not a valid string");
25
+ this.dispatchEvent(new CustomEvent("broken", {detail: {error: e}}));
26
+ throw e;
21
27
  }
22
28
 
23
29
  // Parse local endpoints
@@ -27,7 +33,9 @@ export default class API {
27
33
 
28
34
  // Check endpoint
29
35
  if(!API.isValidHttpUrl(endpoint)) {
30
- throw new Error(`endpoint parameter is not a valid URL: ${endpoint}`);
36
+ const e = new Error(`endpoint parameter is not a valid URL: ${endpoint}`);
37
+ this.dispatchEvent(new CustomEvent("broken", {detail: {error: e}}));
38
+ throw e;
31
39
  }
32
40
 
33
41
  this._endpoint = endpoint;
@@ -43,11 +51,30 @@ export default class API {
43
51
  .catch(e => {
44
52
  this._isReady = -1;
45
53
  console.error(e);
54
+
55
+ /**
56
+ * Event when API is broken.
57
+ * This happens on any API loading or map styling issue.
58
+ * @event Panoramax.utils.API#broken
59
+ * @type {CustomEvent}
60
+ * @property {Error} detail.error The original error
61
+ */
62
+ this.dispatchEvent(new CustomEvent("broken", {detail: {error: e}}));
63
+
46
64
  return Promise.reject("Viewer failed to communicate with API");
47
65
  })
48
66
  .then(() => this._loadMapStyles(options.style, options.users))
49
67
  .then(() => {
50
68
  this._isReady = 1;
69
+
70
+ /**
71
+ * Event when API is ready to use.
72
+ * This happens after initial API read and map styles load.
73
+ * @event Panoramax.utils.API#ready
74
+ * @type {Event}
75
+ */
76
+ this.dispatchEvent(new Event("ready"));
77
+
51
78
  return "API is ready";
52
79
  });
53
80
  }
@@ -103,7 +103,7 @@ describe("addEventListener", () => {
103
103
  expect(listener).toHaveBeenCalledTimes(1);
104
104
  });
105
105
 
106
- it("should wait for map to be available", () => {
106
+ it("should wait for sub-component to be available", () => {
107
107
  const listener = jest.fn();
108
108
  global.setTimeout = jest.fn();
109
109
  basic.getSubComponentsNames = () => ["loader", "api", "map"];
@@ -112,15 +112,6 @@ describe("addEventListener", () => {
112
112
  expect(global.setTimeout).toHaveBeenCalled();
113
113
  });
114
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
115
  it("adds listener to basic if no sub-component matches prefix", () => {
125
116
  const listener = jest.fn();
126
117
  basic.addEventListener("unknown:event", listener);