@panoramax/web-viewer 3.2.3-develop-d7e5a16d → 3.2.3-develop-6257391e

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 (221) hide show
  1. package/.gitlab-ci.yml +3 -0
  2. package/CHANGELOG.md +19 -0
  3. package/CODE_OF_CONDUCT.md +1 -1
  4. package/README.md +1 -1
  5. package/build/editor.html +10 -1
  6. package/build/index.css +2 -2
  7. package/build/index.css.map +1 -1
  8. package/build/index.html +1 -1
  9. package/build/index.js +1682 -5
  10. package/build/index.js.map +1 -1
  11. package/build/map.html +1 -1
  12. package/build/viewer.html +10 -1
  13. package/build/widgets.html +1 -0
  14. package/config/jest/mocks.js +172 -0
  15. package/config/paths.js +1 -0
  16. package/config/webpack.config.js +26 -0
  17. package/docs/03_URL_settings.md +3 -11
  18. package/docs/05_Compatibility.md +59 -76
  19. package/docs/09_Develop.md +30 -11
  20. package/docs/90_Releases.md +2 -2
  21. package/docs/images/class_diagram.drawio +28 -28
  22. package/docs/images/class_diagram.jpg +0 -0
  23. package/docs/index.md +112 -0
  24. package/docs/reference/components/core/Basic.md +153 -0
  25. package/docs/reference/components/core/CoverageMap.md +160 -0
  26. package/docs/reference/components/core/Editor.md +172 -0
  27. package/docs/reference/components/core/Viewer.md +288 -0
  28. package/docs/reference/components/layout/CorneredGrid.md +29 -0
  29. package/docs/reference/components/layout/Mini.md +45 -0
  30. package/docs/reference/components/menus/MapBackground.md +32 -0
  31. package/docs/reference/components/menus/MapFilters.md +15 -0
  32. package/docs/reference/components/menus/MapLayers.md +15 -0
  33. package/docs/reference/components/menus/MapLegend.md +15 -0
  34. package/docs/reference/components/menus/PictureLegend.md +15 -0
  35. package/docs/reference/components/menus/PictureMetadata.md +15 -0
  36. package/docs/reference/components/menus/PlayerOptions.md +15 -0
  37. package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
  38. package/docs/reference/components/menus/ReportForm.md +15 -0
  39. package/docs/reference/components/menus/ShareMenu.md +15 -0
  40. package/docs/reference/components/ui/Button.md +39 -0
  41. package/docs/reference/components/ui/ButtonGroup.md +36 -0
  42. package/docs/reference/components/ui/CopyButton.md +35 -0
  43. package/docs/reference/components/ui/Grade.md +32 -0
  44. package/docs/reference/components/ui/LinkButton.md +44 -0
  45. package/docs/reference/components/ui/Loader.md +54 -0
  46. package/docs/reference/components/ui/Map.md +214 -0
  47. package/docs/reference/components/ui/MapMore.md +233 -0
  48. package/docs/reference/components/ui/Photo.md +369 -0
  49. package/docs/reference/components/ui/Popup.md +56 -0
  50. package/docs/reference/components/ui/QualityScore.md +45 -0
  51. package/docs/reference/components/ui/SearchBar.md +63 -0
  52. package/docs/reference/components/ui/TogglableGroup.md +39 -0
  53. package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
  54. package/docs/reference/components/ui/widgets/Legend.md +32 -0
  55. package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
  56. package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
  57. package/docs/reference/components/ui/widgets/Player.md +32 -0
  58. package/docs/reference/components/ui/widgets/Share.md +15 -0
  59. package/docs/reference/components/ui/widgets/Zoom.md +15 -0
  60. package/docs/reference/utils/API.md +311 -0
  61. package/docs/reference/utils/InitParameters.md +67 -0
  62. package/docs/reference/utils/URLHandler.md +102 -0
  63. package/docs/reference.md +73 -0
  64. package/docs/shortcuts.md +11 -0
  65. package/docs/tutorials/aerial_imagery.md +19 -0
  66. package/docs/tutorials/authentication.md +10 -0
  67. package/docs/tutorials/custom_widgets.md +64 -0
  68. package/docs/tutorials/map_style.md +27 -0
  69. package/docs/tutorials/migrate_v4.md +122 -0
  70. package/docs/tutorials/synced_coverage.md +42 -0
  71. package/mkdocs.yml +60 -5
  72. package/package.json +10 -7
  73. package/public/editor.html +21 -29
  74. package/public/index.html +3 -3
  75. package/public/map.html +19 -18
  76. package/public/viewer.html +18 -24
  77. package/public/widgets.html +265 -0
  78. package/scripts/doc.js +77 -0
  79. package/src/components/core/Basic.css +44 -0
  80. package/src/components/core/Basic.js +258 -0
  81. package/src/components/core/CoverageMap.css +9 -0
  82. package/src/components/core/CoverageMap.js +105 -0
  83. package/src/components/core/Editor.css +23 -0
  84. package/src/components/core/Editor.js +354 -0
  85. package/src/components/core/Viewer.css +109 -0
  86. package/src/components/core/Viewer.js +707 -0
  87. package/src/components/core/index.js +11 -0
  88. package/src/components/index.js +13 -0
  89. package/src/components/layout/CorneredGrid.js +109 -0
  90. package/src/components/layout/Mini.js +117 -0
  91. package/src/components/layout/index.js +7 -0
  92. package/src/components/menus/MapBackground.js +106 -0
  93. package/src/components/menus/MapFilters.js +386 -0
  94. package/src/components/menus/MapLayers.js +143 -0
  95. package/src/components/menus/MapLegend.js +54 -0
  96. package/src/components/menus/PictureLegend.js +103 -0
  97. package/src/components/menus/PictureMetadata.js +188 -0
  98. package/src/components/menus/PlayerOptions.js +96 -0
  99. package/src/components/menus/QualityScoreDoc.js +36 -0
  100. package/src/components/menus/ReportForm.js +133 -0
  101. package/src/components/menus/Share.js +228 -0
  102. package/src/components/menus/index.js +15 -0
  103. package/src/components/styles.js +365 -0
  104. package/src/components/ui/Button.js +75 -0
  105. package/src/components/ui/ButtonGroup.css +49 -0
  106. package/src/components/ui/ButtonGroup.js +68 -0
  107. package/src/components/ui/CopyButton.js +71 -0
  108. package/src/components/ui/Grade.js +54 -0
  109. package/src/components/ui/LinkButton.js +68 -0
  110. package/src/components/ui/Loader.js +188 -0
  111. package/src/components/{Map.css → ui/Map.css} +5 -17
  112. package/src/components/{Map.js → ui/Map.js} +114 -138
  113. package/src/components/ui/MapMore.js +324 -0
  114. package/src/components/{Photo.css → ui/Photo.css} +6 -6
  115. package/src/components/{Photo.js → ui/Photo.js} +279 -90
  116. package/src/components/ui/Popup.js +145 -0
  117. package/src/components/ui/QualityScore.js +152 -0
  118. package/src/components/ui/SearchBar.js +363 -0
  119. package/src/components/ui/TogglableGroup.js +162 -0
  120. package/src/components/ui/index.js +20 -0
  121. package/src/components/ui/widgets/GeoSearch.css +21 -0
  122. package/src/components/ui/widgets/GeoSearch.js +139 -0
  123. package/src/components/ui/widgets/Legend.js +51 -0
  124. package/src/components/ui/widgets/MapFiltersButton.js +104 -0
  125. package/src/components/ui/widgets/MapLayersButton.js +79 -0
  126. package/src/components/ui/widgets/Player.css +7 -0
  127. package/src/components/ui/widgets/Player.js +148 -0
  128. package/src/components/ui/widgets/Share.js +30 -0
  129. package/src/components/ui/widgets/Zoom.js +82 -0
  130. package/src/components/ui/widgets/index.js +12 -0
  131. package/src/img/panoramax.svg +13 -0
  132. package/src/img/switch_big.svg +20 -10
  133. package/src/index.js +6 -9
  134. package/src/translations/da.json +1 -1
  135. package/src/translations/de.json +1 -1
  136. package/src/translations/en.json +5 -3
  137. package/src/translations/eo.json +1 -1
  138. package/src/translations/es.json +1 -1
  139. package/src/translations/fr.json +5 -3
  140. package/src/translations/hu.json +1 -1
  141. package/src/translations/it.json +1 -1
  142. package/src/translations/ja.json +1 -1
  143. package/src/translations/nl.json +1 -1
  144. package/src/translations/pl.json +1 -1
  145. package/src/translations/sv.json +1 -1
  146. package/src/translations/zh_Hant.json +1 -1
  147. package/src/utils/API.js +74 -42
  148. package/src/utils/InitParameters.js +354 -0
  149. package/src/utils/URLHandler.js +364 -0
  150. package/src/utils/geocoder.js +116 -0
  151. package/src/utils/{I18n.js → i18n.js} +3 -1
  152. package/src/utils/index.js +11 -0
  153. package/src/utils/{Map.js → map.js} +216 -80
  154. package/src/utils/picture.js +433 -0
  155. package/src/utils/utils.js +315 -0
  156. package/src/utils/widgets.js +93 -0
  157. package/tests/components/ui/CopyButton.test.js +52 -0
  158. package/tests/components/ui/Loader.test.js +54 -0
  159. package/tests/components/{Map.test.js → ui/Map.test.js} +19 -61
  160. package/tests/components/{Photo.test.js → ui/Photo.test.js} +89 -57
  161. package/tests/components/ui/Popup.test.js +24 -0
  162. package/tests/components/ui/QualityScore.test.js +17 -0
  163. package/tests/components/ui/SearchBar.test.js +107 -0
  164. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +34 -0
  165. package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
  166. package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
  167. package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +57 -4
  168. package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
  169. package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
  170. package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
  171. package/tests/utils/API.test.js +1 -14
  172. package/tests/utils/InitParameters.test.js +485 -0
  173. package/tests/utils/URLHandler.test.js +350 -0
  174. package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
  175. package/tests/utils/__snapshots__/picture.test.js.snap +315 -0
  176. package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
  177. package/tests/utils/geocoder.test.js +37 -0
  178. package/tests/utils/{I18n.test.js → i18n.test.js} +1 -1
  179. package/tests/utils/map.test.js +67 -0
  180. package/tests/utils/picture.test.js +745 -0
  181. package/tests/utils/utils.test.js +288 -0
  182. package/tests/utils/widgets.test.js +90 -0
  183. package/docs/01_Start.md +0 -149
  184. package/docs/02_Usage.md +0 -831
  185. package/docs/04_Advanced_examples.md +0 -216
  186. package/src/Editor.css +0 -37
  187. package/src/Editor.js +0 -361
  188. package/src/StandaloneMap.js +0 -114
  189. package/src/Viewer.css +0 -203
  190. package/src/Viewer.js +0 -1246
  191. package/src/components/CoreView.css +0 -70
  192. package/src/components/CoreView.js +0 -175
  193. package/src/components/Loader.css +0 -74
  194. package/src/components/Loader.js +0 -120
  195. package/src/utils/Exif.js +0 -193
  196. package/src/utils/Utils.js +0 -631
  197. package/src/utils/Widgets.js +0 -562
  198. package/src/viewer/URLHash.js +0 -469
  199. package/src/viewer/Widgets.css +0 -880
  200. package/src/viewer/Widgets.js +0 -1470
  201. package/tests/Editor.test.js +0 -126
  202. package/tests/StandaloneMap.test.js +0 -45
  203. package/tests/Viewer.test.js +0 -366
  204. package/tests/__snapshots__/Editor.test.js.snap +0 -298
  205. package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
  206. package/tests/__snapshots__/Viewer.test.js.snap +0 -195
  207. package/tests/components/CoreView.test.js +0 -92
  208. package/tests/components/Loader.test.js +0 -38
  209. package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
  210. package/tests/utils/Exif.test.js +0 -124
  211. package/tests/utils/Map.test.js +0 -113
  212. package/tests/utils/Utils.test.js +0 -300
  213. package/tests/utils/Widgets.test.js +0 -107
  214. package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
  215. package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
  216. package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
  217. package/tests/viewer/URLHash.test.js +0 -559
  218. package/tests/viewer/Widgets.test.js +0 -127
  219. package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
  220. package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
  221. /package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +0 -0
@@ -1,10 +1,11 @@
1
1
  import "./Photo.css";
2
- import LoaderImgBase from "../img/loader_base.jpg";
3
- import LogoDead from "../img/logo_dead.svg";
2
+ import LoaderImgBase from "../../img/loader_base.jpg";
3
+ import LogoDead from "../../img/logo_dead.svg";
4
4
  import {
5
5
  getDistance, positionToXYZ, xyzToPosition,
6
- apiFeatureToPSVNode, getRelativeHeading, BASE_PANORAMA_ID,
7
- } from "../utils/Utils";
6
+ getRelativeHeading, BASE_PANORAMA_ID,
7
+ } from "../../utils/utils";
8
+ import { apiFeatureToPSVNode } from "../../utils/picture";
8
9
 
9
10
  // Photo Sphere Viewer imports
10
11
  import "@photo-sphere-viewer/core/index.css";
@@ -13,7 +14,7 @@ import "@photo-sphere-viewer/gallery-plugin/index.css";
13
14
  import "@photo-sphere-viewer/markers-plugin/index.css";
14
15
  import { Viewer as PSViewer } from "@photo-sphere-viewer/core";
15
16
  import { VirtualTourPlugin } from "@photo-sphere-viewer/virtual-tour-plugin";
16
- import PhotoAdapter from "../utils/PhotoAdapter";
17
+ import PhotoAdapter from "../../utils/PhotoAdapter";
17
18
 
18
19
 
19
20
  // Default panorama (logo)
@@ -36,7 +37,7 @@ const BASE_PANORAMA_NODE = {
36
37
  properties: {},
37
38
  };
38
39
 
39
- export const PSV_DEFAULT_ZOOM = 30;
40
+ export const PSV_DEFAULT_ZOOM = 30; // eslint-disable-line import/no-unused-modules
40
41
  export const PSV_ANIM_DURATION = 250;
41
42
  export const PIC_MAX_STAY_DURATION = 3000;
42
43
 
@@ -48,11 +49,27 @@ PSViewer.useNewAnglesOrder = true;
48
49
  *
49
50
  * Note that all functions of [PhotoSphereViewer Viewer class](https://photo-sphere-viewer.js.org/api/classes/core.viewer) are available as well.
50
51
  *
51
- * @param {CoreView} parent The parent view
52
+ * @class Panoramax.components.ui.Photo
53
+ * @extends [photo-sphere-viewer.core.Viewer](https://photo-sphere-viewer.js.org/api/classes/Core.Viewer.html)
54
+ * @param {Panoramax.components.core.basic} parent The parent view
52
55
  * @param {Element} container The DOM element to create into
53
56
  * @param {object} [options] The viewer options. Can be any of [Photo Sphere Viewer options](https://photo-sphere-viewer.js.org/guide/config.html#standard-options)
54
57
  * @param {number} [options.transitionDuration] The number of milliseconds the transition animation should be.
58
+ * @param {number[]} [options.position] Initial geographical coordinates (as [latitude, longitude]) to find picture nearby. Only used if no picture ID is set.
55
59
  * @param {function} [options.shouldGoFast] Function returning a boolean to indicate if we may skip loading HD images.
60
+ * @param {string} [options.picturesNavigation=any] The allowed pictures navigation ("any": no restriction, "seq": only pictures in same sequence, "pic": only selected picture)
61
+ * @fires Panoramax.components.ui.Photo#picture-loading
62
+ * @fires Panoramax.components.ui.Photo#picture-preview-started
63
+ * @fires Panoramax.components.ui.Photo#picture-preview-stopped
64
+ * @fires Panoramax.components.ui.Photo#view-rotated
65
+ * @fires Panoramax.components.ui.Photo#picture-loaded
66
+ * @fires Panoramax.components.ui.Photo#picture-tiles-loaded
67
+ * @fires Panoramax.components.ui.Photo#transition-duration-changed
68
+ * @fires Panoramax.components.ui.Photo#sequence-playing
69
+ * @fires Panoramax.components.ui.Photo#sequence-stopped
70
+ * @fires Panoramax.components.ui.Photo#pictures-navigation-changed
71
+ * @example
72
+ * const psv = new Panoramax.components.ui.Photo(viewer, psvNode, {transitionDuration: 500})
56
73
  */
57
74
  export default class Photo extends PSViewer {
58
75
  constructor(parent, container, options = {}) {
@@ -64,8 +81,8 @@ export default class Photo extends PSViewer {
64
81
  resolution: parent.isWidthSmall() ? 32 : 64,
65
82
  shouldGoFast: options.shouldGoFast,
66
83
  }],
67
- withCredentials: parent._options?.fetchOptions?.credentials == "include",
68
- requestHeaders: parent._options?.fetchOptions?.headers,
84
+ withCredentials: parent?.fetchOptions?.credentials == "include",
85
+ requestHeaders: parent?.fetchOptions?.headers,
69
86
  panorama: BASE_PANORAMA,
70
87
  lang: parent._t.psv,
71
88
  minFov: 5,
@@ -91,7 +108,8 @@ export default class Photo extends PSViewer {
91
108
  });
92
109
 
93
110
  this._parent = parent;
94
- container.classList.add("gvs-psv");
111
+ this._options = options;
112
+ container.classList.add("pnx-psv");
95
113
  this._shouldGoFast = options?.shouldGoFast || (() => false);
96
114
  this._transitionDuration = options?.transitionDuration || PSV_ANIM_DURATION;
97
115
  this._myVTour = this.getPlugin(VirtualTourPlugin);
@@ -99,6 +117,8 @@ export default class Photo extends PSViewer {
99
117
  this._myVTour.config.transitionOptions = this._psvNodeTransition.bind(this);
100
118
  this._clearArrows = this._myVTour.arrowsRenderer.clear.bind(this._myVTour.arrowsRenderer);
101
119
  this._myVTour.arrowsRenderer.clear = () => {};
120
+ this._sequencePlaying = false;
121
+ this._picturesNavigation = this._options.picturesNavigation || "any";
102
122
 
103
123
  // Cache to find sequence ID for a single picture
104
124
  this._picturesSequences = {};
@@ -119,6 +139,11 @@ export default class Photo extends PSViewer {
119
139
  this.loader.thickness = 10;
120
140
  this.loader.canvas.setAttribute("viewBox", "0 0 150 150");
121
141
  this.loader.__updateContent();
142
+
143
+ // Handle initial parameters
144
+ if(this._options.position && !this._parent.picture) {
145
+ this.goToPosition(...this._options.position);
146
+ }
122
147
  }
123
148
 
124
149
  /**
@@ -127,20 +152,21 @@ export default class Photo extends PSViewer {
127
152
  * @private
128
153
  * @param {string} picId The picture UUID
129
154
  * @returns {Promise} Resolves on PSV node metadata
155
+ * @memberof Panoramax.components.ui.Photo#
130
156
  */
131
157
  async _getNodeFromAPI(picId) {
132
158
  if(!picId || picId === BASE_PANORAMA_ID) { return BASE_PANORAMA_NODE; }
133
159
 
134
160
  const picApiResponse = await fetch(
135
- this._parent._api.getPictureMetadataUrl(picId, this._picturesSequences[picId]),
136
- this._parent._api._getFetchOptions()
161
+ this._parent.api.getPictureMetadataUrl(picId, this._picturesSequences[picId]),
162
+ this._parent.api._getFetchOptions()
137
163
  );
138
164
  let metadata = await picApiResponse.json();
139
165
 
140
166
  if(metadata.features) { metadata = metadata.features.pop(); }
141
167
  if(!metadata || Object.keys(metadata).length === 0 || !picApiResponse.ok) {
142
- if(this._parent._loader) {
143
- this._parent._loader.dismiss(true, this._parent._t.gvs.error_pic);
168
+ if(this._parent.loader) {
169
+ this._parent.loader.dismiss(true, this._parent._t.pnx.error_pic);
144
170
  }
145
171
  throw new Error("Picture with ID " + picId + " was not found");
146
172
  }
@@ -150,7 +176,7 @@ export default class Photo extends PSViewer {
150
176
  metadata,
151
177
  this._parent._t,
152
178
  this._parent._isInternetFast,
153
- this._parent._picturesNavFilter?.bind(this._parent)
179
+ this._picturesNavFilter.bind(this)
154
180
  );
155
181
  if(node?.sequence?.prevPic) { this._picturesSequences[node?.sequence?.prevPic] = metadata.collection; }
156
182
  if(node?.sequence?.nextPic) { this._picturesSequences[node?.sequence?.nextPic] = metadata.collection; }
@@ -164,6 +190,7 @@ export default class Photo extends PSViewer {
164
190
  * @param {*} [fromNode] Currently shown node (previous)
165
191
  * @param {*} [fromLink] Link clicked by user to go from current to next node
166
192
  * @private
193
+ * @memberof Panoramax.components.ui.Photo#
167
194
  */
168
195
  _psvNodeTransition(toNode, fromNode, fromLink) {
169
196
  let nodeTransition = {};
@@ -243,18 +270,17 @@ export default class Photo extends PSViewer {
243
270
  /**
244
271
  * Event for picture starting to load
245
272
  *
246
- * @event psv:picture-loading
247
- * @memberof CoreView
248
- * @type {object}
249
- * @property {object} detail Event information
273
+ * @event Panoramax.components.ui.Photo#picture-loading
274
+ * @type {CustomEvent}
250
275
  * @property {string} detail.picId The picture unique identifier
251
276
  * @property {number} detail.lon Longitude (WGS84)
252
277
  * @property {number} detail.lat Latitude (WGS84)
253
278
  * @property {number} detail.x New x position (in degrees, 0-360), corresponds to heading (0° = North, 90° = East, 180° = South, 270° = West)
254
279
  * @property {number} detail.y New y position (in degrees)
255
280
  * @property {number} detail.z New z position (0-100)
281
+ * @property {boolean} detail.first True if first picture loaded
256
282
  */
257
- const event = new CustomEvent("psv:picture-loading", {
283
+ const event = new CustomEvent("picture-loading", {
258
284
  detail: {
259
285
  ...Object.assign({},
260
286
  this.getXYZ(),
@@ -263,10 +289,11 @@ export default class Photo extends PSViewer {
263
289
  ),
264
290
  picId: toNode.id,
265
291
  lon: toNode.gps[0],
266
- lat: toNode.gps[1]
292
+ lat: toNode.gps[1],
293
+ first: this._parent._initParams?.getParentPostInit().picture == toNode.id,
267
294
  }
268
295
  });
269
- this._parent.dispatchEvent(event);
296
+ this.dispatchEvent(event);
270
297
 
271
298
  return nodeTransition;
272
299
  }
@@ -276,6 +303,7 @@ export default class Photo extends PSViewer {
276
303
  * It creates a custom event "picture-preview-started"
277
304
  * @private
278
305
  * @param {object} e The event data
306
+ * @memberof Panoramax.components.ui.Photo#
279
307
  */
280
308
  _onEnterArrow(e) {
281
309
  const fromLink = e.link;
@@ -295,20 +323,18 @@ export default class Photo extends PSViewer {
295
323
  /**
296
324
  * Event for picture preview
297
325
  *
298
- * @event psv:picture-preview-started
299
- * @memberof CoreView
300
- * @type {object}
301
- * @property {object} detail Event information
326
+ * @event Panoramax.components.ui.Photo#picture-preview-started
327
+ * @type {CustomEvent}
302
328
  * @property {string} detail.picId The picture ID
303
329
  * @property {number[]} detail.coordinates [x,y] coordinates
304
330
  * @property {number} detail.direction The theorical picture orientation
305
331
  */
306
- const event = new CustomEvent("psv:picture-preview-started", { detail: {
332
+ const event = new CustomEvent("picture-preview-started", { detail: {
307
333
  picId: fromLink.nodeId,
308
334
  coordinates: fromLink.gps,
309
335
  direction,
310
336
  }});
311
- this._parent.dispatchEvent(event);
337
+ this.dispatchEvent(event);
312
338
  }
313
339
 
314
340
  /**
@@ -316,47 +342,44 @@ export default class Photo extends PSViewer {
316
342
  * It creates a custom event "picture-preview-stopped"
317
343
  * @private
318
344
  * @param {object} e The event data
345
+ * @memberof Panoramax.components.ui.Photo#
319
346
  */
320
347
  _onLeaveArrow(e) {
321
348
  const fromLink = e.link;
322
349
 
323
350
  /**
324
351
  * Event for end of picture preview
325
- *
326
- * @event psv:picture-preview-stopped
327
- * @memberof CoreView
328
- * @type {object}
329
- * @property {object} detail Event information
352
+ * @event Panoramax.components.ui.Photo#picture-preview-stopped
353
+ * @type {CustomEvent}
330
354
  * @property {string} detail.picId The picture ID
331
355
  */
332
- const event = new CustomEvent("psv:picture-preview-stopped", { detail: {
356
+ const event = new CustomEvent("picture-preview-stopped", { detail: {
333
357
  picId: fromLink.nodeId,
334
358
  }});
335
- this._parent.dispatchEvent(event);
359
+ this.dispatchEvent(event);
336
360
  }
337
361
 
338
362
  /**
339
363
  * Event handler for position update in PSV.
340
364
  * Allows to send a custom "view-rotated" event.
341
365
  * @private
366
+ * @memberof Panoramax.components.ui.Photo#
342
367
  */
343
368
  _onPositionUpdated({position}) {
344
369
  const pos = positionToXYZ(position, this.getZoomLevel());
345
370
  pos.x += this.getPictureOriginalHeading();
346
371
  pos.x = pos.x % 360;
372
+
347
373
  /**
348
374
  * Event for viewer rotation
349
- *
350
- * @event psv:view-rotated
351
- * @memberof CoreView
352
- * @type {object}
353
- * @property {object} detail Event information
375
+ * @event Panoramax.components.ui.Photo#view-rotated
376
+ * @type {CustomEvent}
354
377
  * @property {number} detail.x New x position (in degrees, 0-360), corresponds to heading (0° = North, 90° = East, 180° = South, 270° = West)
355
378
  * @property {number} detail.y New y position (in degrees)
356
379
  * @property {number} detail.z New Z position (between 0 and 100)
357
380
  */
358
- const event = new CustomEvent("psv:view-rotated", { detail: pos });
359
- this._parent.dispatchEvent(event);
381
+ const event = new CustomEvent("view-rotated", { detail: pos });
382
+ this.dispatchEvent(event);
360
383
 
361
384
  this._onTilesStartLoading();
362
385
  }
@@ -365,57 +388,59 @@ export default class Photo extends PSViewer {
365
388
  * Event handler for zoom updates in PSV.
366
389
  * Allows to send a custom "view-rotated" event.
367
390
  * @private
391
+ * @memberof Panoramax.components.ui.Photo#
368
392
  */
369
393
  _onZoomUpdated({zoomLevel}) {
370
- const event = new CustomEvent("psv:view-rotated", { detail: { ...this.getXY(), z: zoomLevel} });
371
- this._parent.dispatchEvent(event);
394
+ const event = new CustomEvent("view-rotated", { detail: { ...this.getXY(), z: zoomLevel} });
395
+ this.dispatchEvent(event);
372
396
 
373
397
  this._onTilesStartLoading();
374
398
  }
375
399
 
376
400
  /**
377
401
  * Event handler for node change in PSV.
378
- * Allows to send a custom "psv:picture-loaded" event.
402
+ * Allows to send a custom "picture-loaded" event.
379
403
  * @private
404
+ * @memberof Panoramax.components.ui.Photo#
380
405
  */
381
406
  _onNodeChanged(e) {
382
407
  // Clean up clicked arrows
383
- for(let d of document.getElementsByClassName("gvs-psv-tour-arrows")) {
384
- d.classList.remove("gvs-clicked");
408
+ for(let d of document.getElementsByClassName("pnx-psv-tour-arrows")) {
409
+ d.classList.remove("pnx-clicked");
385
410
  }
386
411
 
387
412
  if(e.node.id) {
413
+ const isFirst = this._parent._initParams?.getParentPostInit().picture == e.node.id;
388
414
  this._parent.select(e.node?.sequence?.id, e.node.id);
389
415
  const picMeta = this.getPictureMetadata();
390
416
  if(!picMeta) {
391
- this._parent.dispatchEvent(new CustomEvent("psv:picture-loaded", {detail: {}}));
417
+ this.dispatchEvent(new CustomEvent("picture-loaded", {detail: {}}));
392
418
  return;
393
419
  }
394
420
  this._prevSequence = picMeta.sequence.id;
395
421
 
396
422
  /**
397
423
  * Event for picture load (low-resolution image is loaded)
398
- *
399
- * @event psv:picture-loaded
400
- * @memberof CoreView
401
- * @type {object}
402
- * @property {object} detail Event information
424
+ * @event Panoramax.components.ui.Photo#picture-loaded
425
+ * @type {CustomEvent}
403
426
  * @property {string} detail.picId The picture unique identifier
404
427
  * @property {number} detail.lon Longitude (WGS84)
405
428
  * @property {number} detail.lat Latitude (WGS84)
406
429
  * @property {number} detail.x New x position (in degrees, 0-360), corresponds to heading (0° = North, 90° = East, 180° = South, 270° = West)
407
430
  * @property {number} detail.y New y position (in degrees)
408
431
  * @property {number} detail.z New z position (0-100)
432
+ * @property {boolean} detail.first True if first picture loaded
409
433
  */
410
- const event = new CustomEvent("psv:picture-loaded", {
434
+ const event = new CustomEvent("picture-loaded", {
411
435
  detail: {
412
436
  ...this.getXYZ(),
413
437
  picId: e.node.id,
414
438
  lon: picMeta.gps[0],
415
- lat: picMeta.gps[1]
416
- }
439
+ lat: picMeta.gps[1],
440
+ first: isFirst
441
+ },
417
442
  });
418
- this._parent.dispatchEvent(event);
443
+ this.dispatchEvent(event);
419
444
 
420
445
  // Change download URL
421
446
  if(picMeta.panorama.hdUrl) {
@@ -432,7 +457,7 @@ export default class Photo extends PSViewer {
432
457
 
433
458
  /**
434
459
  * Event handler for loading a new range of tiles
435
- *
460
+ * @memberof Panoramax.components.ui.Photo#
436
461
  * @private
437
462
  */
438
463
  _onTilesStartLoading() {
@@ -445,15 +470,12 @@ export default class Photo extends PSViewer {
445
470
  if(this._myVTour.state.currentNode) {
446
471
  /**
447
472
  * Event launched when all visible tiles of a picture are loaded
448
- *
449
- * @event psv:picture-tiles-loaded
450
- * @memberof CoreView
451
- * @type {object}
452
- * @property {object} detail Event information
473
+ * @event Panoramax.components.ui.Photo#picture-tiles-loaded
474
+ * @type {CustomEvent}
453
475
  * @property {string} detail.picId The picture unique identifier
454
476
  */
455
- const event = new Event("psv:picture-tiles-loaded", { picId: this._myVTour.state.currentNode.id });
456
- this._parent.dispatchEvent(event);
477
+ const event = new CustomEvent("picture-tiles-loaded", { detail: { picId: this._myVTour.state.currentNode.id }});
478
+ this.dispatchEvent(event);
457
479
  }
458
480
  clearInterval(this._tilesQueueTimer);
459
481
  delete this._tilesQueueTimer;
@@ -463,7 +485,7 @@ export default class Photo extends PSViewer {
463
485
 
464
486
  /**
465
487
  * Access currently shown picture metadata
466
- *
488
+ * @memberof Panoramax.components.ui.Photo#
467
489
  * @returns {object} Picture metadata
468
490
  */
469
491
  getPictureMetadata() {
@@ -474,6 +496,7 @@ export default class Photo extends PSViewer {
474
496
  /**
475
497
  * Handler for select event.
476
498
  * @private
499
+ * @memberof Panoramax.components.ui.Photo#
477
500
  */
478
501
  _onSelect(e) {
479
502
  if(e.detail.seqId) {
@@ -483,13 +506,15 @@ export default class Photo extends PSViewer {
483
506
  if(this._myVTour.getCurrentNode()?.id !== e.detail.picId) {
484
507
  this.loader.show();
485
508
  this._myVTour.setCurrentNode(e.detail.picId).catch(e => {
486
- this.showErrorOverlay(e, this._parent._t.gvs.error_pic, true);
509
+ this.showErrorOverlay(e, this._parent._t.pnx.error_pic, true);
487
510
  });
488
511
  }
489
512
  }
490
513
 
491
514
  /**
492
515
  * Displays next picture in current sequence (if any)
516
+ * @memberof Panoramax.components.ui.Photo#
517
+ * @throws {Error} If no picture is selected, or no next picture available
493
518
  */
494
519
  goToNextPicture() {
495
520
  if(!this.getPictureMetadata()) {
@@ -507,6 +532,8 @@ export default class Photo extends PSViewer {
507
532
 
508
533
  /**
509
534
  * Displays previous picture in current sequence (if any)
535
+ * @memberof Panoramax.components.ui.Photo#
536
+ * @throws {Error} If no picture is selected, or no previous picture available
510
537
  */
511
538
  goToPrevPicture() {
512
539
  if(!this.getPictureMetadata()) {
@@ -524,13 +551,15 @@ export default class Photo extends PSViewer {
524
551
 
525
552
  /**
526
553
  * Displays in viewer a picture near to given coordinates
527
- *
554
+ * @memberof Panoramax.components.ui.Photo#
528
555
  * @param {number} lat Latitude (WGS84)
529
556
  * @param {number} lon Longitude (WGS84)
530
- * @returns {Promise} Resolves on picture ID if picture found, otherwise rejects
557
+ * @returns {Promise}
558
+ * @fulfil {string} Picture ID if picture found
559
+ * @reject {Error} If no picture found
531
560
  */
532
561
  async goToPosition(lat, lon) {
533
- return this._parent._api.getPicturesAroundCoordinates(lat, lon)
562
+ return this._parent.api.getPicturesAroundCoordinates(lat, lon)
534
563
  .then(res => {
535
564
  if(res.features.length > 0) {
536
565
  const f = res.features.pop();
@@ -548,7 +577,7 @@ export default class Photo extends PSViewer {
548
577
 
549
578
  /**
550
579
  * Get 2D position of sphere currently shown to user
551
- *
580
+ * @memberof Panoramax.components.ui.Photo#
552
581
  * @returns {object} Position in format { x: heading in degrees (0° = North, 90° = East, 180° = South, 270° = West), y: top/bottom position in degrees (-90° = bottom, 0° = front, 90° = top) }
553
582
  */
554
583
  getXY() {
@@ -559,7 +588,7 @@ export default class Photo extends PSViewer {
559
588
 
560
589
  /**
561
590
  * Get 3D position of sphere currently shown to user
562
- *
591
+ * @memberof Panoramax.components.ui.Photo#
563
592
  * @returns {object} Position in format { x: heading in degrees (0° = North, 90° = East, 180° = South, 270° = West), y: top/bottom position in degrees (-90° = bottom, 0° = front, 90° = top), z: zoom (0 = wide, 100 = zoomed in) }
564
593
  */
565
594
  getXYZ() {
@@ -570,7 +599,8 @@ export default class Photo extends PSViewer {
570
599
 
571
600
  /**
572
601
  * Get capture orientation of current picture, based on its GPS.
573
- * @returns Picture original heading in degrees (0 to 360°)
602
+ * @returns {number} Picture original heading in degrees (0 to 360°)
603
+ * @memberof Panoramax.components.ui.Photo#
574
604
  */
575
605
  getPictureOriginalHeading() {
576
606
  return this.getPictureMetadata()?.properties?.["view:azimuth"] || 0;
@@ -579,8 +609,8 @@ export default class Photo extends PSViewer {
579
609
  /**
580
610
  * Computes the relative heading of currently selected picture.
581
611
  * This gives the angle of capture compared to sequence path (vehicle movement).
582
- *
583
- * @returns Relative heading in degrees (-180 to 180)
612
+ * @memberof Panoramax.components.ui.Photo#
613
+ * @returns {number} Relative heading in degrees (-180 to 180)
584
614
  */
585
615
  getPictureRelativeHeading() {
586
616
  return getRelativeHeading(this.getPictureMetadata());
@@ -589,6 +619,7 @@ export default class Photo extends PSViewer {
589
619
  /**
590
620
  * Clears the Photo Sphere Viewer metadata cache.
591
621
  * It is useful when current picture or sequence has changed server-side after first load.
622
+ * @memberof Panoramax.components.ui.Photo#
592
623
  */
593
624
  clearPictureMetadataCache() {
594
625
  const oldPicId = this.getPictureMetadata()?.id;
@@ -609,7 +640,7 @@ export default class Photo extends PSViewer {
609
640
 
610
641
  /**
611
642
  * Change the shown position in picture
612
- *
643
+ * @memberof Panoramax.components.ui.Photo#
613
644
  * @param {number} x X position (in degrees)
614
645
  * @param {number} y Y position (in degrees)
615
646
  * @param {number} z Z position (0-100)
@@ -623,6 +654,7 @@ export default class Photo extends PSViewer {
623
654
  /**
624
655
  * Enable or disable higher contrast on picture
625
656
  * @param {boolean} enable True to enable higher contrast
657
+ * @memberof Panoramax.components.ui.Photo#
626
658
  */
627
659
  setHigherContrast(enable) {
628
660
  this.renderer.renderer.toneMapping = enable ? 3 : 0;
@@ -633,6 +665,7 @@ export default class Photo extends PSViewer {
633
665
  /**
634
666
  * Get the duration of stay on a picture during a sequence play.
635
667
  * @returns {number} The duration (in milliseconds)
668
+ * @memberof Panoramax.components.ui.Photo#
636
669
  */
637
670
  getTransitionDuration() {
638
671
  return this._transitionDuration;
@@ -640,7 +673,7 @@ export default class Photo extends PSViewer {
640
673
 
641
674
  /**
642
675
  * Changes the duration of stay on a picture during a sequence play.
643
- *
676
+ * @memberof Panoramax.components.ui.Photo#
644
677
  * @param {number} value The new duration (in milliseconds, between 100 and 3000)
645
678
  */
646
679
  setTransitionDuration(value) {
@@ -652,19 +685,17 @@ export default class Photo extends PSViewer {
652
685
 
653
686
  /**
654
687
  * Event for transition duration change
655
- *
656
- * @event psv:transition-duration-changed
657
- * @memberof CoreView
658
- * @type {object}
659
- * @property {object} detail Event information
688
+ * @event Panoramax.components.ui.Photo#transition-duration-changed
689
+ * @type {CustomEvent}
660
690
  * @property {string} detail.duration New duration (in milliseconds)
661
691
  */
662
- const event = new CustomEvent("psv:transition-duration-changed", { detail: { value } });
663
- this._parent.dispatchEvent(event);
692
+ const event = new CustomEvent("transition-duration-changed", { detail: { value } });
693
+ this.dispatchEvent(event);
664
694
  }
665
695
 
696
+ /** @private */
666
697
  setPanorama(path, options) {
667
- const onFailure = e => this.showErrorOverlay(e, this._parent._t.gvs.error_pic, true);
698
+ const onFailure = e => this.showErrorOverlay(e, this._parent?._t.pnx.error_pic, true);
668
699
  try {
669
700
  return super.setPanorama(path, options).catch(onFailure);
670
701
  }
@@ -678,14 +709,15 @@ export default class Photo extends PSViewer {
678
709
  * @param {object} e The initial error
679
710
  * @param {str} label The main error label to display
680
711
  * @param {boolean} dissmisable Is error dissmisable
712
+ * @memberof Panoramax.components.ui.Photo#
681
713
  */
682
714
  showErrorOverlay(e, label, dissmisable) {
683
- if(this._parent._loader.isVisible() || !this.overlay.isVisible()) {
684
- this._parent._loader.dismiss(
715
+ if(this._parent?.loader.isVisible() || !this.overlay.isVisible()) {
716
+ this._parent?.loader.dismiss(
685
717
  e,
686
718
  label,
687
719
  dissmisable ? () => {
688
- this._parent._loader.dismiss();
720
+ this._parent?.loader.dismiss();
689
721
  this.overlay.hide();
690
722
  } : undefined
691
723
  );
@@ -694,10 +726,167 @@ export default class Photo extends PSViewer {
694
726
  console.error(e);
695
727
  this.overlay.show({
696
728
  image: `<img style="width: 200px" src="${LogoDead}" alt="" />`,
697
- title: this._parent._t.gvs.error,
698
- text: label + "<br />" + this._parent._t.gvs.error_click,
729
+ title: this._parent?._t.pnx.error,
730
+ text: label + "<br />" + this._parent?._t.pnx.error_click,
699
731
  dissmisable,
700
732
  });
701
733
  }
702
734
  }
735
+
736
+ /**
737
+ * Goes continuously to next picture in sequence as long as possible
738
+ * @memberof Panoramax.components.ui.Photo#
739
+ */
740
+ playSequence() {
741
+ this._sequencePlaying = true;
742
+
743
+ /**
744
+ * Event for sequence starting to play
745
+ * @event Panoramax.components.ui.Photo#sequence-playing
746
+ * @type {CustomEvent}
747
+ */
748
+ const event = new Event("sequence-playing", {bubbles: true, composed: true});
749
+ this.dispatchEvent(event);
750
+
751
+ const nextPicturePlay = () => {
752
+ if(this._sequencePlaying) {
753
+ this.addEventListener("picture-loaded", () => {
754
+ this._playTimer = setTimeout(() => {
755
+ nextPicturePlay();
756
+ }, this.getTransitionDuration());
757
+ }, { once: true });
758
+
759
+ try {
760
+ this.goToNextPicture();
761
+ }
762
+ catch(e) {
763
+ this.stopSequence();
764
+ }
765
+ }
766
+ };
767
+
768
+ // Stop playing if user clicks on image
769
+ this.addEventListener("click", () => this.stopSequence());
770
+
771
+ nextPicturePlay();
772
+ }
773
+
774
+ /**
775
+ * Stops playing current sequence
776
+ * @memberof Panoramax.components.ui.Photo#
777
+ */
778
+ stopSequence() {
779
+ this._sequencePlaying = false;
780
+
781
+ // Next picture timer is pending
782
+ if(this._playTimer) {
783
+ clearTimeout(this._playTimer);
784
+ delete this._playTimer;
785
+ }
786
+
787
+ // Force refresh of PSV to eventually load tiles
788
+ this.forceRefresh();
789
+
790
+ /**
791
+ * Event for sequence stopped playing
792
+ * @event Panoramax.components.ui.Photo#sequence-stopped
793
+ * @type {CustomEvent}
794
+ */
795
+ const event = new Event("sequence-stopped", {bubbles: true, composed: true});
796
+ this.dispatchEvent(event);
797
+ }
798
+
799
+ /**
800
+ * Is there any sequence being played right now ?
801
+ * @memberof Panoramax.components.ui.Photo#
802
+ * @returns {boolean} True if sequence is playing
803
+ */
804
+ isSequencePlaying() {
805
+ return this._sequencePlaying;
806
+ }
807
+
808
+ /**
809
+ * Starts/stops the reading of pictures in a sequence
810
+ * @memberof Panoramax.components.ui.Photo#
811
+ */
812
+ toggleSequencePlaying() {
813
+ if(this.isSequencePlaying()) {
814
+ this.stopSequence();
815
+ }
816
+ else {
817
+ this.playSequence();
818
+ }
819
+ }
820
+
821
+ /**
822
+ * Get current pictures navigation mode.
823
+ * @returns {string} The picture navigation mode ("any": no restriction, "seq": only pictures in same sequence, "pic": only selected picture)
824
+ * @memberof Panoramax.components.ui.Photo#
825
+ */
826
+ getPicturesNavigation() {
827
+ return this._picturesNavigation;
828
+ }
829
+
830
+ /**
831
+ * Switch the allowed navigation between pictures.
832
+ * @param {string} pn The picture navigation mode ("any": no restriction, "seq": only pictures in same sequence, "pic": only selected picture)
833
+ * @memberof Panoramax.components.ui.Photo#
834
+ */
835
+ setPicturesNavigation(pn) {
836
+ this._picturesNavigation = pn;
837
+
838
+ /**
839
+ * Event for pictures navigation mode change
840
+ * @event Panoramax.components.ui.Photo#pictures-navigation-changed
841
+ * @type {CustomEvent}
842
+ * @property {string} detail.value New mode (any, pic, seq)
843
+ */
844
+ const event = new CustomEvent("pictures-navigation-changed", { detail: { value: pn } });
845
+ this.dispatchEvent(event);
846
+ }
847
+
848
+ /**
849
+ * Filter function
850
+ * @param {object} link A STAC next/prev/related link definition
851
+ * @returns {boolean} True if link should be kept
852
+ * @private
853
+ */
854
+ _picturesNavFilter(link) {
855
+ switch(this._picturesNavigation) {
856
+ case "seq":
857
+ return ["next", "prev"].includes(link.rel);
858
+ case "pic":
859
+ return false;
860
+ case "any":
861
+ default:
862
+ return true;
863
+ }
864
+ }
865
+
866
+ /**
867
+ * Force reload of texture and tiles.
868
+ * @memberof Panoramax.components.ui.Photo#
869
+ */
870
+ forceRefresh() {
871
+ const cn = this._myVTour.getCurrentNode();
872
+
873
+ // Refresh mode for flat pictures
874
+ if(cn && cn.panorama.baseUrl !== cn?.panorama?.origBaseUrl) {
875
+ const prevZoom = this.getZoomLevel();
876
+ const prevPos = this.getPosition();
877
+ this._myVTour.state.currentNode = null;
878
+ this._myVTour.setCurrentNode(cn.id, {
879
+ zoomTo: prevZoom,
880
+ rotateTo: prevPos,
881
+ fadeIn: false,
882
+ speed: 0,
883
+ rotation: false,
884
+ });
885
+ }
886
+
887
+ // Refresh mode for 360 pictures
888
+ if(cn && cn.panorama.rows > 1) {
889
+ this.adapter.__refresh();
890
+ }
891
+ }
703
892
  }