@panoramax/web-viewer 3.2.3-develop-f219e404 → 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 +33 -3
  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
@@ -0,0 +1,364 @@
1
+ import {
2
+ alterPSVState, MAP_FILTERS_JS2URL, alterMapState, alterViewerState
3
+ } from "./InitParameters";
4
+
5
+ // List of supported parameters
6
+ const MANAGED_PARAMETERS = [
7
+ "speed", "nav", "focus", "pic", "xyz", "map",
8
+ "background", "users", "pic_score",
9
+ ].concat(Object.values(MAP_FILTERS_JS2URL));
10
+
11
+ // Events to listen on parent and PSV
12
+ const UPDATE_PARENT_EVENTS = ["focus-changed", "pictures-navigation-changed"];
13
+ const UPDATE_PSV_EVENTS = ["position-updated", "zoom-updated", "view-rotated", "picture-loaded", "transition-duration-changed"];
14
+ const UPDATE_MAP_EVENTS = ["moveend", "zoomend", "boxzoomend", "background-changed", "users-changed", "filters-changed"];
15
+
16
+
17
+ /**
18
+ * Updates the URL query part with various parent component information.
19
+ *
20
+ * Note that you may call `listenToChanges()` for this class to be effective once parent is ready-enough.
21
+ *
22
+ * @class Panoramax.utils.URLHandler
23
+ * @typicalname urlHandler
24
+ * @param {Panoramax.components.core.Basic} parent The parent component
25
+ * @fires Panoramax.utils.URLHandler#url-changed
26
+ */
27
+ export default class URLHandler extends EventTarget {
28
+ constructor(parent) {
29
+ super();
30
+ this._parent = parent;
31
+ this._delay = null;
32
+ }
33
+
34
+ /**
35
+ * Start listening to URL & parent changes through events.
36
+ * This leads to parent & URL updates.
37
+ * @memberof Panoramax.utils.URLHandler#
38
+ */
39
+ listenToChanges() {
40
+ window.addEventListener("popstate", this._onURLChange.bind(this), false);
41
+ UPDATE_PARENT_EVENTS.forEach(e => this._parent.addEventListener(e, this._onParentChange.bind(this)));
42
+ UPDATE_PSV_EVENTS.forEach(e => this._parent.psv.addEventListener(e, this._onParentChange.bind(this)));
43
+ if(this._parent.map) {
44
+ UPDATE_MAP_EVENTS.forEach(e => this._parent.map.on(e, this._onParentChange.bind(this)));
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Compute next values to insert in URL
50
+ * @returns {object} Query parameters
51
+ * @memberof Panoramax.utils.URLHandler#
52
+ */
53
+ nextURLParams() {
54
+ let hashParts = {};
55
+
56
+ if(typeof this._parent.psv.getTransitionDuration() == "number") {
57
+ hashParts.speed = this._parent.psv.getTransitionDuration();
58
+ }
59
+
60
+ if(![null, "any"].includes(this._parent.psv.getPicturesNavigation())) {
61
+ hashParts.nav = this._parent.psv.getPicturesNavigation();
62
+ }
63
+
64
+ const picMeta = this._parent.psv.getPictureMetadata();
65
+ if (picMeta) {
66
+ hashParts.pic = picMeta.id;
67
+ hashParts.xyz = this.currentPSVString();
68
+ }
69
+
70
+ if(this._parent.map) {
71
+ hashParts.map = this.currentMapString();
72
+ hashParts.focus = "pic";
73
+ if(this._parent.isMapWide()) { hashParts.focus = "map"; }
74
+ if(this._parent.popup.hasAttribute("visible")) { hashParts.focus = "meta"; }
75
+ if(this._parent.map.hasTwoBackgrounds() && this._parent.map.getBackground()) {
76
+ hashParts.background = this._parent.map.getBackground();
77
+ }
78
+
79
+ const vu = this._parent.map.getVisibleUsers();
80
+ if(vu.length > 1 || !vu.includes("geovisio")) {
81
+ hashParts.users = vu.join(",");
82
+ }
83
+
84
+ if(this._parent.map._mapFilters) {
85
+ for(let k in MAP_FILTERS_JS2URL) {
86
+ if(this._parent.map._mapFilters[k]) {
87
+ hashParts[MAP_FILTERS_JS2URL[k]] = this._parent.map._mapFilters[k];
88
+ }
89
+ }
90
+ if(hashParts.pic_score) {
91
+ const mapping = [null, "E", "D", "C", "B", "A"];
92
+ hashParts.pic_score = hashParts.pic_score.map(v => mapping[v]).join("");
93
+ }
94
+ }
95
+ }
96
+ else {
97
+ hashParts.map = "none";
98
+ }
99
+ return hashParts;
100
+ }
101
+
102
+ /**
103
+ * Compute next URL query string (based on `nextURLParams()`)
104
+ * @memberof Panoramax.utils.URLHandler#
105
+ * @return {string} The query string
106
+ */
107
+ nextURLString() {
108
+ let hash = "";
109
+
110
+ Object.entries(this.nextURLParams())
111
+ .sort((a,b) => a[0].localeCompare(b[0]))
112
+ .forEach(entry => {
113
+ let [ hashName, value ] = entry;
114
+ let found = false;
115
+ const parts = hash.split("&").map(part => {
116
+ const key = part.split("=")[0];
117
+ if (key === hashName) {
118
+ found = true;
119
+ return `${key}=${value}`;
120
+ }
121
+ return part;
122
+ }).filter(a => a);
123
+ if (!found) {
124
+ parts.push(`${hashName}=${value}`);
125
+ }
126
+ hash = `${parts.join("&")}`;
127
+ });
128
+
129
+ return `?${hash}`.replace(/^\?+/, "?");
130
+ }
131
+
132
+ /**
133
+ * Transforms current URL query string into key->value object
134
+ * @return {object} Key-value read from current URL query
135
+ * @memberof Panoramax.utils.URLHandler#
136
+ */
137
+ currentURLParams() {
138
+ // Get the current hash from location, stripped from its number sign
139
+ const hash = window.location.search.replace(/^\?/, "");
140
+
141
+ // Split the parameter-styled hash into parts and find the value we need
142
+ let keyvals = {};
143
+ hash.split("&").map(
144
+ part => part.split("=")
145
+ )
146
+ .filter(part => part[0] !== undefined && part[0].length > 0)
147
+ .forEach(part => {
148
+ keyvals[part[0]] = part[1];
149
+ });
150
+
151
+ // If hash is compressed
152
+ if(keyvals.s) {
153
+ const shortVals = Object.fromEntries(
154
+ keyvals.s
155
+ .split(";")
156
+ .map(kv => [kv[0], kv.substring(1)])
157
+ );
158
+
159
+ keyvals = {};
160
+
161
+ // Used letters: b c d e f k m n p q s t u v
162
+ // Focus
163
+ if(shortVals.f === "m") { keyvals.focus = "map"; }
164
+ else if(shortVals.f === "p") { keyvals.focus = "pic"; }
165
+ else if(shortVals.f === "t") { keyvals.focus = "meta"; }
166
+
167
+ // Speed
168
+ if(shortVals.s !== "") { keyvals.speed = parseFloat(shortVals.s) * 100; }
169
+
170
+ // Nav
171
+ if(shortVals.n === "a") { keyvals.nav = "any"; }
172
+ else if(shortVals.n === "s") { keyvals.nav = "seq"; }
173
+ if(shortVals.n === "n") { keyvals.nav = "none"; }
174
+
175
+ // Pic
176
+ if(shortVals.p !== "") { keyvals.pic = shortVals.p; }
177
+
178
+ // XYZ
179
+ if(shortVals.c !== "") { keyvals.xyz = shortVals.c; }
180
+
181
+ // Map
182
+ if(shortVals.m !== "") { keyvals.map = shortVals.m; }
183
+
184
+ // Date
185
+ if(shortVals.d !== "") { keyvals.date_from = shortVals.d; }
186
+ if(shortVals.e !== "") { keyvals.date_to = shortVals.e; }
187
+
188
+ // Pic type
189
+ if(shortVals.t === "f") { keyvals.pic_type = "flat"; }
190
+ else if(shortVals.t === "e") { keyvals.pic_type = "equirectangular"; }
191
+
192
+ // Camera
193
+ if(shortVals.k !== "") { keyvals.camera = shortVals.k; }
194
+
195
+ // Theme
196
+ if(shortVals.v === "d") { keyvals.theme = "default"; }
197
+ else if(shortVals.v === "a") { keyvals.theme = "age"; }
198
+ else if(shortVals.v === "t") { keyvals.theme = "type"; }
199
+ else if(shortVals.v === "s") { keyvals.theme = "score"; }
200
+
201
+ // Background
202
+ if(shortVals.b === "s") { keyvals.background = "streets"; }
203
+ else if(shortVals.b === "a") { keyvals.background = "aerial"; }
204
+
205
+ // Users
206
+ if(shortVals.u !== "") { keyvals.users = shortVals.u; }
207
+
208
+ // Photoscore
209
+ if(shortVals.q !== "") { keyvals.pic_score = shortVals.q; }
210
+ }
211
+
212
+ return keyvals;
213
+ }
214
+
215
+ /**
216
+ * Get string representation of map position
217
+ * @returns {string} zoom/lat/lon or zoom/lat/lon/bearing/pitch
218
+ * @memberof Panoramax.utils.URLHandler#
219
+ */
220
+ currentMapString() {
221
+ const center = this._parent.map.getCenter(),
222
+ zoom = Math.round(this._parent.map.getZoom() * 100) / 100,
223
+ // derived from equation: 512px * 2^z / 360 / 10^d < 0.5px
224
+ precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / 0.5)) / Math.LN10),
225
+ m = Math.pow(10, precision),
226
+ lng = Math.round(center.lng * m) / m,
227
+ lat = Math.round(center.lat * m) / m,
228
+ bearing = this._parent.map.getBearing(),
229
+ pitch = this._parent.map.getPitch();
230
+ let hash = `${zoom}/${lat}/${lng}`;
231
+
232
+ if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`);
233
+ if (pitch) hash += (`/${Math.round(pitch)}`);
234
+
235
+ return hash;
236
+ }
237
+
238
+ /**
239
+ * Get PSV view position as string
240
+ * @returns {string} x/y/z
241
+ * @memberof Panoramax.utils.URLHandler#
242
+ */
243
+ currentPSVString() {
244
+ const xyz = this._parent.psv.getXYZ();
245
+ const x = xyz.x.toFixed(2),
246
+ y = xyz.y.toFixed(2),
247
+ z = Math.round(xyz.z || 0);
248
+ return `${x}/${y}/${z}`;
249
+ }
250
+
251
+ /**
252
+ * Updates map and PSV according to current hash values
253
+ * @private
254
+ * @memberof Panoramax.utils.URLHandler#
255
+ */
256
+ _onURLChange() {
257
+ let vals = this.currentURLParams();
258
+ alterViewerState(this._parent, vals);
259
+ alterPSVState(this._parent.psv, vals);
260
+ if(this._parent.map) { alterMapState(this._parent.map, vals); }
261
+ }
262
+
263
+ /**
264
+ * Get short link URL (query replaced by Base64)
265
+ * @returns {str} The short link URL
266
+ * @memberof Panoramax.utils.URLHandler#
267
+ */
268
+ nextShortLink(baseUrl) {
269
+ const url = new URL(baseUrl);
270
+ const hashParts = this.nextURLParams();
271
+ const shortVals = {
272
+ f: (hashParts.focus || "").substring(0, 1),
273
+ s: !isNaN(parseInt(hashParts.speed)) ? Math.floor(parseInt(hashParts.speed)/100) : undefined,
274
+ n: (hashParts.nav || "").substring(0, 1),
275
+ p: hashParts.pic,
276
+ c: hashParts.xyz,
277
+ m: hashParts.map,
278
+ d: hashParts.date_from,
279
+ e: hashParts.date_to,
280
+ t: (hashParts.pic_type || "").substring(0, 1),
281
+ k: hashParts.camera,
282
+ v: (hashParts.theme || "").substring(0, 1),
283
+ b: (hashParts.background || "").substring(0, 1),
284
+ u: hashParts.users,
285
+ q: hashParts.pic_score,
286
+ };
287
+ const short = Object.entries(shortVals)
288
+ .filter(([,v]) => v != undefined && v != "")
289
+ .map(([k,v]) => `${k}${v}`)
290
+ .join(";");
291
+ url.search = `s=${short}`;
292
+ return url;
293
+ }
294
+
295
+ /**
296
+ * Returns a string containing only parameters out of URLHandler scope
297
+ * @param {URL} prevUrl The previously set URL
298
+ * @memberof Panoramax.utils.URLHandler#
299
+ */
300
+ getUnmanagedParameters(prevUrl) {
301
+ return new URLSearchParams(
302
+ Array.from(prevUrl.searchParams)
303
+ .filter(([k]) => !MANAGED_PARAMETERS.includes(k))
304
+ ).toString();
305
+ }
306
+
307
+ /**
308
+ * Changes the URL hash using current viewer parameters
309
+ * @private
310
+ * @memberof Panoramax.utils.URLHandler#
311
+ */
312
+ _onParentChange() {
313
+ if(this._delay) {
314
+ clearTimeout(this._delay);
315
+ this._delay = null;
316
+ }
317
+
318
+ this._delay = setTimeout(() => {
319
+ const prevUrl = new URL(window.location.href);
320
+ const nextUrl = new URL(window.location.href);
321
+ const unmanaged = this.getUnmanagedParameters(prevUrl);
322
+ nextUrl.search = this._parent ? this.nextURLString() + (unmanaged.length > 0 ? "&"+unmanaged : ""): "";
323
+
324
+ // Skip hash update if no changes
325
+ if(prevUrl.search == nextUrl.search) { return; }
326
+
327
+ const prevPic = this.currentURLParams().pic || "";
328
+ const nextPic = this._parent?.psv?.getPictureMetadata()?.id || "";
329
+
330
+ const prevFocus = this.currentURLParams().focus || "";
331
+ const nextFocus = nextUrl.search.includes("focus=meta") ? "meta" : (nextUrl.search.includes("focus=map") ? "map" : "pic");
332
+
333
+ try {
334
+ // If different pic, add entry in browser history
335
+ if(prevPic != nextPic) {
336
+ window.history.pushState(window.history.state, null, nextUrl.href);
337
+ }
338
+ // If metadata popup is open, come back to pic/map
339
+ else if(prevFocus != nextFocus && nextFocus == "meta") {
340
+ window.history.pushState(window.history.state, null, nextUrl.href);
341
+ }
342
+ // If same pic, just update viewer params
343
+ else {
344
+ window.history.replaceState(window.history.state, null, nextUrl.href);
345
+ }
346
+
347
+ if(this._parent) {
348
+ /**
349
+ * URL changed event
350
+ * @event Panoramax.utils.URLHandler#url-changed
351
+ * @type {CustomEvent}
352
+ * @property {string} detail.url The new used URL
353
+ */
354
+ const event = new CustomEvent("url-changed", { detail: {url: nextUrl.href}});
355
+ this.dispatchEvent(event);
356
+ }
357
+ } catch (SecurityError) {
358
+ // IE11 does not allow this if the page is within an iframe created
359
+ // with iframe.contentWindow.document.write(...).
360
+ // https://github.com/mapbox/mapbox-gl-js/issues/7410
361
+ }
362
+ }, 500);
363
+ }
364
+ }
@@ -0,0 +1,116 @@
1
+ // DO NOT REMOVE THE "!": bundled builds breaks otherwise !!!
2
+ import maplibregl from "!maplibre-gl";
3
+
4
+ /**
5
+ * Transforms a set of parameters into an URL-ready string
6
+ * It also removes null/undefined values
7
+ *
8
+ * @param {object} params The parameters object
9
+ * @return {string} The URL query part
10
+ * @private
11
+ */
12
+ function geocoderParamsToURLString(params) {
13
+ let p = {};
14
+ Object.entries(params)
15
+ .filter(e => e[1] !== undefined && e[1] !== null)
16
+ .forEach(e => p[e[0]] = e[1]);
17
+
18
+ return new URLSearchParams(p).toString();
19
+ }
20
+
21
+ /**
22
+ * Nominatim (OSM) geocoder, ready to use for our Map
23
+ * @private
24
+ */
25
+ export function forwardGeocodingNominatim(config) {
26
+ // Transform parameters into Nominatim format
27
+ const params = {
28
+ q: config.query,
29
+ countrycodes: config.countries,
30
+ limit: config.limit,
31
+ viewbox: config.bbox,
32
+ };
33
+
34
+ return fetch(`https://nominatim.openstreetmap.org/search?${geocoderParamsToURLString(params)}&format=geojson&polygon_geojson=1&addressdetails=1`)
35
+ .then(res => res.json())
36
+ .then(res => {
37
+ const finalRes = { features: [] };
38
+ const listedNames = [];
39
+ res.features.forEach(f => {
40
+ if(!listedNames.includes(f.properties.display_name)) {
41
+ finalRes.features.push({
42
+ place_type: ["place"],
43
+ place_name: f.properties.display_name,
44
+ bounds: new maplibregl.LngLatBounds(f.bbox),
45
+ });
46
+ listedNames.push(f.properties.display_name);
47
+ }
48
+ });
49
+ return finalRes;
50
+ });
51
+ }
52
+
53
+ export function reverseGeocodingNominatim(lat, lon) {
54
+ return fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&zoom=18&format=jsonv2`)
55
+ .then(res => res.json())
56
+ .then(res => {
57
+ let addr = "";
58
+
59
+ if(res?.address) {
60
+ if(res.address.house_number) { addr = res.address.house_number; }
61
+
62
+ // Street/place/hamlet
63
+ let street;
64
+ if(res.address.road && res.address.road.length > 6) {
65
+ street = res.address.road;
66
+ }
67
+ else if(res.address.hamlet) {
68
+ street = res.address.hamlet;
69
+ }
70
+ else if(res.address.isolated_dwelling) {
71
+ street = res.address.isolated_dwelling;
72
+ }
73
+ if(street && addr.length > 0) { addr += " "+street; }
74
+ else if(street) { addr = street; }
75
+
76
+ // City
77
+ if(res.address.village || res.address.town || res.address.city) {
78
+ if(addr.length > 0) { addr += ", "; }
79
+ addr += res.address.village || res.address.town || res.address.city;
80
+ }
81
+ }
82
+
83
+ return addr;
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Base adresse nationale (FR) geocoder, ready to use for our Map
89
+ * @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://github.com/maplibre/maplibre-gl-geocoder/blob/main/API.md#setgeocoderapi )
90
+ * @returns {object} GeoJSON Feature collection in Carmen GeoJSON format
91
+ * @private
92
+ */
93
+ export function forwardGeocodingBAN(config) {
94
+ // Transform parameters into BAN format
95
+ const params = { q: config.query, limit: config.limit };
96
+ if(typeof config.proximity === "string") {
97
+ const [lat, lon] = config.proximity.split(",").map(v => parseFloat(v.trim()));
98
+ params.lat = lat;
99
+ params.lon = lon;
100
+ }
101
+
102
+ const toPlaceName = p => [p.name, p.district, p.city].filter(v => v).join(", ");
103
+ const placeTypeToZoom = { "housenumber": 20, "street": 18, "locality": 15, "municipality": 12 };
104
+
105
+ return fetch(`https://api-adresse.data.gouv.fr/search/?${geocoderParamsToURLString(params)}`)
106
+ .then(res => res.json())
107
+ .then(res => {
108
+ res.features = res.features.map(f => ({
109
+ place_type: ["place"],
110
+ place_name: toPlaceName(f.properties),
111
+ center: new maplibregl.LngLat(...f.geometry.coordinates),
112
+ zoom: placeTypeToZoom[f.properties.type],
113
+ }));
114
+ return res;
115
+ });
116
+ }
@@ -6,13 +6,15 @@ import T_es from "../translations/es.json";
6
6
  import T_fr from "../translations/fr.json";
7
7
  import T_hu from "../translations/hu.json";
8
8
  import T_it from "../translations/it.json";
9
+ import T_ja from "../translations/ja.json";
9
10
  import T_pl from "../translations/pl.json";
11
+ import T_sv from "../translations/sv.json";
10
12
  import T_zh_Hant from "../translations/zh_Hant.json";
11
13
 
12
14
  const FALLBACK_LOCALE = "en";
13
15
  const TRANSLATIONS = {
14
16
  "da": T_da, "de": T_de, "eo": T_eo, "en": T_en, "es": T_es, "fr": T_fr,
15
- "hu": T_hu, "it": T_it, "pl": T_pl, "zh_Hant": T_zh_Hant,
17
+ "hu": T_hu, "it": T_it, "ja": T_ja, "pl": T_pl, "sv": T_sv, "zh_Hant": T_zh_Hant,
16
18
  };
17
19
 
18
20
  /**
@@ -0,0 +1,11 @@
1
+ import * as geocoder from "./geocoder";
2
+ import * as i18n from "./i18n";
3
+ import * as map from "./map";
4
+ import * as picture from "./picture";
5
+ import * as utils from "./utils";
6
+ import * as widgets from "./widgets";
7
+
8
+ export { geocoder, i18n, map, picture, utils, widgets };
9
+ export {default as API} from "./API";
10
+ export {default as PhotoAdapter} from "./PhotoAdapter";
11
+ export {default as URLHandler} from "./URLHandler";