@panoramax/web-viewer 3.2.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/.gitlab-ci.yml +13 -6
  2. package/CHANGELOG.md +49 -1
  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 +12 -12
  7. package/build/index.css.map +1 -1
  8. package/build/index.html +1 -1
  9. package/build/index.js +2126 -14
  10. package/build/index.js.map +1 -1
  11. package/build/map.html +1 -1
  12. package/build/photo.html +1 -0
  13. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff +0 -0
  14. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff2 +0 -0
  15. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff +0 -0
  16. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff2 +0 -0
  17. package/build/viewer.html +12 -1
  18. package/build/widgets.html +1 -0
  19. package/config/jest/mocks.js +201 -0
  20. package/config/paths.js +2 -0
  21. package/config/webpack.config.js +52 -0
  22. package/docs/03_URL_settings.md +14 -16
  23. package/docs/05_Compatibility.md +59 -76
  24. package/docs/09_Develop.md +46 -11
  25. package/docs/90_Releases.md +2 -2
  26. package/docs/images/class_diagram.drawio +60 -45
  27. package/docs/images/class_diagram.jpg +0 -0
  28. package/docs/images/screenshot.jpg +0 -0
  29. package/docs/index.md +135 -0
  30. package/docs/reference/components/core/Basic.md +196 -0
  31. package/docs/reference/components/core/CoverageMap.md +210 -0
  32. package/docs/reference/components/core/Editor.md +224 -0
  33. package/docs/reference/components/core/PhotoViewer.md +307 -0
  34. package/docs/reference/components/core/Viewer.md +350 -0
  35. package/docs/reference/components/layout/BottomDrawer.md +35 -0
  36. package/docs/reference/components/layout/CorneredGrid.md +29 -0
  37. package/docs/reference/components/layout/Mini.md +45 -0
  38. package/docs/reference/components/layout/Tabs.md +45 -0
  39. package/docs/reference/components/menus/MapBackground.md +32 -0
  40. package/docs/reference/components/menus/MapFilters.md +15 -0
  41. package/docs/reference/components/menus/MapLayers.md +15 -0
  42. package/docs/reference/components/menus/MapLegend.md +15 -0
  43. package/docs/reference/components/menus/PictureLegend.md +16 -0
  44. package/docs/reference/components/menus/PictureMetadata.md +15 -0
  45. package/docs/reference/components/menus/PlayerOptions.md +15 -0
  46. package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
  47. package/docs/reference/components/menus/ReportForm.md +15 -0
  48. package/docs/reference/components/menus/ShareMenu.md +15 -0
  49. package/docs/reference/components/ui/Button.md +40 -0
  50. package/docs/reference/components/ui/ButtonGroup.md +36 -0
  51. package/docs/reference/components/ui/CopyButton.md +38 -0
  52. package/docs/reference/components/ui/Grade.md +32 -0
  53. package/docs/reference/components/ui/LinkButton.md +45 -0
  54. package/docs/reference/components/ui/ListGroup.md +22 -0
  55. package/docs/reference/components/ui/Loader.md +56 -0
  56. package/docs/reference/components/ui/Map.md +239 -0
  57. package/docs/reference/components/ui/MapMore.md +256 -0
  58. package/docs/reference/components/ui/Photo.md +385 -0
  59. package/docs/reference/components/ui/Popup.md +56 -0
  60. package/docs/reference/components/ui/ProgressBar.md +32 -0
  61. package/docs/reference/components/ui/QualityScore.md +45 -0
  62. package/docs/reference/components/ui/SearchBar.md +63 -0
  63. package/docs/reference/components/ui/TogglableGroup.md +39 -0
  64. package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
  65. package/docs/reference/components/ui/widgets/Legend.md +49 -0
  66. package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
  67. package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
  68. package/docs/reference/components/ui/widgets/OSMEditors.md +15 -0
  69. package/docs/reference/components/ui/widgets/PictureLegendActions.md +32 -0
  70. package/docs/reference/components/ui/widgets/Player.md +33 -0
  71. package/docs/reference/components/ui/widgets/Zoom.md +15 -0
  72. package/docs/reference/utils/API.md +334 -0
  73. package/docs/reference/utils/InitParameters.md +68 -0
  74. package/docs/reference/utils/URLHandler.md +107 -0
  75. package/docs/reference.md +79 -0
  76. package/docs/shortcuts.md +11 -0
  77. package/docs/tutorials/aerial_imagery.md +19 -0
  78. package/docs/tutorials/authentication.md +10 -0
  79. package/docs/tutorials/custom_widgets.md +59 -0
  80. package/docs/tutorials/map_style.md +39 -0
  81. package/docs/tutorials/migrate_v4.md +153 -0
  82. package/docs/tutorials/synced_coverage.md +43 -0
  83. package/mkdocs.yml +66 -5
  84. package/package.json +22 -17
  85. package/public/editor.html +21 -29
  86. package/public/index.html +17 -12
  87. package/public/map.html +19 -18
  88. package/public/photo.html +55 -0
  89. package/public/viewer.html +22 -26
  90. package/public/widgets.html +306 -0
  91. package/scripts/doc.js +79 -0
  92. package/src/components/core/Basic.css +48 -0
  93. package/src/components/core/Basic.js +349 -0
  94. package/src/components/core/CoverageMap.css +9 -0
  95. package/src/components/core/CoverageMap.js +139 -0
  96. package/src/components/core/Editor.css +23 -0
  97. package/src/components/core/Editor.js +390 -0
  98. package/src/components/core/PhotoViewer.css +48 -0
  99. package/src/components/core/PhotoViewer.js +499 -0
  100. package/src/components/core/Viewer.css +98 -0
  101. package/src/components/core/Viewer.js +564 -0
  102. package/src/components/core/index.js +12 -0
  103. package/src/components/index.js +13 -0
  104. package/src/components/layout/BottomDrawer.js +257 -0
  105. package/src/components/layout/CorneredGrid.js +112 -0
  106. package/src/components/layout/Mini.js +117 -0
  107. package/src/components/layout/Tabs.js +133 -0
  108. package/src/components/layout/index.js +9 -0
  109. package/src/components/menus/MapBackground.js +106 -0
  110. package/src/components/menus/MapFilters.js +400 -0
  111. package/src/components/menus/MapLayers.js +143 -0
  112. package/src/components/menus/MapLegend.js +34 -0
  113. package/src/components/menus/PictureLegend.js +253 -0
  114. package/src/components/menus/PictureMetadata.js +317 -0
  115. package/src/components/menus/PlayerOptions.js +95 -0
  116. package/src/components/menus/QualityScoreDoc.js +36 -0
  117. package/src/components/menus/ReportForm.js +133 -0
  118. package/src/components/menus/Share.js +100 -0
  119. package/src/components/menus/index.js +15 -0
  120. package/src/components/styles.js +383 -0
  121. package/src/components/ui/Button.js +77 -0
  122. package/src/components/ui/ButtonGroup.css +57 -0
  123. package/src/components/ui/ButtonGroup.js +68 -0
  124. package/src/components/ui/CopyButton.js +106 -0
  125. package/src/components/ui/Grade.js +54 -0
  126. package/src/components/ui/LinkButton.js +67 -0
  127. package/src/components/ui/ListGroup.js +66 -0
  128. package/src/components/ui/Loader.js +203 -0
  129. package/src/components/{Map.css → ui/Map.css} +5 -17
  130. package/src/components/{Map.js → ui/Map.js} +148 -156
  131. package/src/components/ui/MapMore.js +324 -0
  132. package/src/components/{Photo.css → ui/Photo.css} +6 -6
  133. package/src/components/{Photo.js → ui/Photo.js} +313 -101
  134. package/src/components/ui/Popup.js +145 -0
  135. package/src/components/ui/ProgressBar.js +104 -0
  136. package/src/components/ui/QualityScore.js +147 -0
  137. package/src/components/ui/SearchBar.js +367 -0
  138. package/src/components/ui/TogglableGroup.js +157 -0
  139. package/src/components/ui/index.js +22 -0
  140. package/src/components/ui/widgets/GeoSearch.css +21 -0
  141. package/src/components/ui/widgets/GeoSearch.js +139 -0
  142. package/src/components/ui/widgets/Legend.js +113 -0
  143. package/src/components/ui/widgets/MapFiltersButton.js +104 -0
  144. package/src/components/ui/widgets/MapLayersButton.js +79 -0
  145. package/src/components/ui/widgets/OSMEditors.js +155 -0
  146. package/src/components/ui/widgets/PictureLegendActions.js +117 -0
  147. package/src/components/ui/widgets/Player.css +7 -0
  148. package/src/components/ui/widgets/Player.js +151 -0
  149. package/src/components/ui/widgets/Zoom.js +82 -0
  150. package/src/components/ui/widgets/index.js +13 -0
  151. package/src/img/loader_base.jpg +0 -0
  152. package/src/img/panoramax.svg +13 -0
  153. package/src/img/switch_big.svg +20 -10
  154. package/src/index.js +7 -9
  155. package/src/translations/br.json +1 -0
  156. package/src/translations/da.json +38 -15
  157. package/src/translations/de.json +5 -3
  158. package/src/translations/en.json +35 -15
  159. package/src/translations/eo.json +38 -15
  160. package/src/translations/es.json +1 -1
  161. package/src/translations/fr.json +36 -16
  162. package/src/translations/hu.json +1 -1
  163. package/src/translations/it.json +39 -16
  164. package/src/translations/ja.json +182 -1
  165. package/src/translations/nl.json +106 -6
  166. package/src/translations/pl.json +1 -1
  167. package/src/translations/sv.json +182 -0
  168. package/src/translations/zh_Hant.json +35 -14
  169. package/src/utils/API.js +109 -49
  170. package/src/utils/InitParameters.js +388 -0
  171. package/src/utils/PhotoAdapter.js +1 -0
  172. package/src/utils/URLHandler.js +362 -0
  173. package/src/utils/geocoder.js +152 -0
  174. package/src/utils/{I18n.js → i18n.js} +7 -3
  175. package/src/utils/index.js +11 -0
  176. package/src/utils/{Map.js → map.js} +256 -77
  177. package/src/utils/picture.js +442 -0
  178. package/src/utils/utils.js +324 -0
  179. package/src/utils/widgets.js +55 -0
  180. package/tests/components/core/Basic.test.js +121 -0
  181. package/tests/components/core/BasicMock.js +25 -0
  182. package/tests/components/core/CoverageMap.test.js +20 -0
  183. package/tests/components/core/Editor.test.js +20 -0
  184. package/tests/components/core/PhotoViewer.test.js +57 -0
  185. package/tests/components/core/Viewer.test.js +84 -0
  186. package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +73 -0
  187. package/tests/components/core/__snapshots__/Viewer.test.js.snap +145 -0
  188. package/tests/components/ui/CopyButton.test.js +52 -0
  189. package/tests/components/ui/Loader.test.js +55 -0
  190. package/tests/components/{Map.test.js → ui/Map.test.js} +73 -61
  191. package/tests/components/{Photo.test.js → ui/Photo.test.js} +97 -63
  192. package/tests/components/ui/Popup.test.js +26 -0
  193. package/tests/components/ui/QualityScore.test.js +18 -0
  194. package/tests/components/ui/SearchBar.test.js +110 -0
  195. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +33 -0
  196. package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
  197. package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
  198. package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +70 -6
  199. package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
  200. package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
  201. package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
  202. package/tests/utils/API.test.js +83 -83
  203. package/tests/utils/InitParameters.test.js +499 -0
  204. package/tests/utils/URLHandler.test.js +401 -0
  205. package/tests/utils/__snapshots__/API.test.js.snap +10 -0
  206. package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
  207. package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +1 -1
  208. package/tests/utils/__snapshots__/map.test.js.snap +11 -0
  209. package/tests/utils/__snapshots__/picture.test.js.snap +327 -0
  210. package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
  211. package/tests/utils/geocoder.test.js +37 -0
  212. package/tests/utils/{I18n.test.js → i18n.test.js} +8 -8
  213. package/tests/utils/map.test.js +126 -0
  214. package/tests/utils/picture.test.js +745 -0
  215. package/tests/utils/utils.test.js +288 -0
  216. package/tests/utils/widgets.test.js +31 -0
  217. package/docs/01_Start.md +0 -149
  218. package/docs/02_Usage.md +0 -831
  219. package/docs/04_Advanced_examples.md +0 -216
  220. package/src/Editor.css +0 -37
  221. package/src/Editor.js +0 -361
  222. package/src/StandaloneMap.js +0 -114
  223. package/src/Viewer.css +0 -203
  224. package/src/Viewer.js +0 -1246
  225. package/src/components/CoreView.css +0 -70
  226. package/src/components/CoreView.js +0 -175
  227. package/src/components/Loader.css +0 -74
  228. package/src/components/Loader.js +0 -120
  229. package/src/img/loader_hd.jpg +0 -0
  230. package/src/utils/Exif.js +0 -193
  231. package/src/utils/Utils.js +0 -631
  232. package/src/utils/Widgets.js +0 -562
  233. package/src/viewer/URLHash.js +0 -469
  234. package/src/viewer/Widgets.css +0 -880
  235. package/src/viewer/Widgets.js +0 -1470
  236. package/tests/Editor.test.js +0 -126
  237. package/tests/StandaloneMap.test.js +0 -45
  238. package/tests/Viewer.test.js +0 -366
  239. package/tests/__snapshots__/Editor.test.js.snap +0 -298
  240. package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
  241. package/tests/__snapshots__/Viewer.test.js.snap +0 -195
  242. package/tests/components/CoreView.test.js +0 -92
  243. package/tests/components/Loader.test.js +0 -38
  244. package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
  245. package/tests/utils/Exif.test.js +0 -124
  246. package/tests/utils/Map.test.js +0 -113
  247. package/tests/utils/Utils.test.js +0 -300
  248. package/tests/utils/Widgets.test.js +0 -107
  249. package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
  250. package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
  251. package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
  252. package/tests/viewer/URLHash.test.js +0 -559
  253. package/tests/viewer/Widgets.test.js +0 -127
  254. package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
  255. package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
@@ -0,0 +1,362 @@
1
+ import {
2
+ alterPSVState, MAP_FILTERS_JS2URL, alterMapState, alterViewerState, alterPhotoViewerState
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", "s"
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
+ if(this._parent.psv.getPictureId()) {
65
+ hashParts.pic = this._parent.psv.getPictureId();
66
+ }
67
+ const picMeta = this._parent.psv.getPictureMetadata();
68
+ if (picMeta) {
69
+ hashParts.xyz = this.currentPSVString();
70
+ }
71
+
72
+ if(this._parent.map) {
73
+ hashParts.map = this.currentMapString();
74
+ hashParts.focus = "pic";
75
+ if(this._parent.isMapWide()) { hashParts.focus = "map"; }
76
+ if(this._parent.map.hasTwoBackgrounds() && this._parent.map.getBackground()) {
77
+ hashParts.background = this._parent.map.getBackground();
78
+ }
79
+
80
+ const vu = this._parent.map.getVisibleUsers();
81
+ if(vu.length > 1 || !vu.includes("geovisio")) {
82
+ hashParts.users = vu.join(",");
83
+ }
84
+
85
+ if(this._parent.map._mapFilters) {
86
+ for(let k in MAP_FILTERS_JS2URL) {
87
+ if(this._parent.map._mapFilters[k]) {
88
+ hashParts[MAP_FILTERS_JS2URL[k]] = this._parent.map._mapFilters[k];
89
+ }
90
+ }
91
+ if(hashParts.pic_score) {
92
+ const mapping = [null, "E", "D", "C", "B", "A"];
93
+ hashParts.pic_score = hashParts.pic_score.map(v => mapping[v]).join("");
94
+ }
95
+ }
96
+ }
97
+ return hashParts;
98
+ }
99
+
100
+ /**
101
+ * Compute next URL query string (based on `nextURLParams()`)
102
+ * @memberof Panoramax.utils.URLHandler#
103
+ * @return {string} The query string
104
+ */
105
+ nextURLString() {
106
+ let hash = "";
107
+
108
+ Object.entries(this.nextURLParams())
109
+ .sort((a,b) => a[0].localeCompare(b[0]))
110
+ .forEach(entry => {
111
+ let [ hashName, value ] = entry;
112
+ let found = false;
113
+ const parts = hash.split("&").map(part => {
114
+ const key = part.split("=")[0];
115
+ if (key === hashName) {
116
+ found = true;
117
+ return `${key}=${value}`;
118
+ }
119
+ return part;
120
+ }).filter(a => a);
121
+ if (!found) {
122
+ parts.push(`${hashName}=${value}`);
123
+ }
124
+ hash = `${parts.join("&")}`;
125
+ });
126
+
127
+ return `?${hash}`.replace(/^\?+/, "?");
128
+ }
129
+
130
+ /**
131
+ * Transforms current URL query string into key->value object
132
+ * @param {boolean} [readFromHash=false] Switch to reading from hash URL part (for retro-compatibility)
133
+ * @return {object} Key-value read from current URL query
134
+ * @memberof Panoramax.utils.URLHandler#
135
+ */
136
+ currentURLParams(readFromHash = false) {
137
+ // Get the current hash from location, stripped from its number sign
138
+ const hash = (readFromHash ? window.location.hash : window.location.search).replace(/^[?#]/, "");
139
+
140
+ // Split the parameter-styled hash into parts and find the value we need
141
+ let keyvals = {};
142
+ hash.split("&").map(
143
+ part => part.split("=")
144
+ )
145
+ .filter(part => part[0] !== undefined && part[0].length > 0 && MANAGED_PARAMETERS.includes(part[0]))
146
+ .forEach(part => {
147
+ keyvals[part[0]] = part[1];
148
+ });
149
+
150
+ // If hash is compressed
151
+ if(keyvals.s) {
152
+ const shortVals = Object.fromEntries(
153
+ keyvals.s
154
+ .split(";")
155
+ .map(kv => [kv[0], kv.substring(1)])
156
+ );
157
+
158
+ keyvals = {};
159
+
160
+ // Used letters: b c d e f k m n p q s t u v
161
+ // Focus
162
+ if(shortVals.f === "m") { keyvals.focus = "map"; }
163
+ else if(shortVals.f === "p") { keyvals.focus = "pic"; }
164
+ else if(shortVals.f === "t") { keyvals.focus = "meta"; }
165
+
166
+ // Speed
167
+ if(shortVals.s !== "") { keyvals.speed = parseFloat(shortVals.s) * 100; }
168
+
169
+ // Nav
170
+ if(shortVals.n === "a") { keyvals.nav = "any"; }
171
+ else if(shortVals.n === "s") { keyvals.nav = "seq"; }
172
+ if(shortVals.n === "n") { keyvals.nav = "none"; }
173
+
174
+ // Pic
175
+ if(shortVals.p !== "") { keyvals.pic = shortVals.p; }
176
+
177
+ // XYZ
178
+ if(shortVals.c !== "") { keyvals.xyz = shortVals.c; }
179
+
180
+ // Map
181
+ if(shortVals.m !== "") { keyvals.map = shortVals.m; }
182
+
183
+ // Date
184
+ if(shortVals.d !== "") { keyvals.date_from = shortVals.d; }
185
+ if(shortVals.e !== "") { keyvals.date_to = shortVals.e; }
186
+
187
+ // Pic type
188
+ if(shortVals.t === "f") { keyvals.pic_type = "flat"; }
189
+ else if(shortVals.t === "e") { keyvals.pic_type = "equirectangular"; }
190
+
191
+ // Camera
192
+ if(shortVals.k !== "") { keyvals.camera = shortVals.k; }
193
+
194
+ // Theme
195
+ if(shortVals.v === "d") { keyvals.theme = "default"; }
196
+ else if(shortVals.v === "a") { keyvals.theme = "age"; }
197
+ else if(shortVals.v === "t") { keyvals.theme = "type"; }
198
+ else if(shortVals.v === "s") { keyvals.theme = "score"; }
199
+
200
+ // Background
201
+ if(shortVals.b === "s") { keyvals.background = "streets"; }
202
+ else if(shortVals.b === "a") { keyvals.background = "aerial"; }
203
+
204
+ // Users
205
+ if(shortVals.u !== "") { keyvals.users = shortVals.u; }
206
+
207
+ // Photoscore
208
+ if(shortVals.q !== "") { keyvals.pic_score = shortVals.q; }
209
+ }
210
+
211
+ return keyvals;
212
+ }
213
+
214
+ /**
215
+ * Get string representation of map position
216
+ * @returns {string} zoom/lat/lon or zoom/lat/lon/bearing/pitch
217
+ * @memberof Panoramax.utils.URLHandler#
218
+ */
219
+ currentMapString() {
220
+ const center = this._parent.map.getCenter(),
221
+ zoom = Math.round(this._parent.map.getZoom() * 100) / 100,
222
+ // derived from equation: 512px * 2^z / 360 / 10^d < 0.5px
223
+ precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / 0.5)) / Math.LN10),
224
+ m = Math.pow(10, precision),
225
+ lng = Math.round(center.lng * m) / m,
226
+ lat = Math.round(center.lat * m) / m,
227
+ bearing = this._parent.map.getBearing(),
228
+ pitch = this._parent.map.getPitch();
229
+ let hash = `${zoom}/${lat}/${lng}`;
230
+
231
+ if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`);
232
+ if (pitch) hash += (`/${Math.round(pitch)}`);
233
+
234
+ return hash;
235
+ }
236
+
237
+ /**
238
+ * Get PSV view position as string
239
+ * @returns {string} x/y/z
240
+ * @memberof Panoramax.utils.URLHandler#
241
+ */
242
+ currentPSVString() {
243
+ const xyz = this._parent.psv.getXYZ();
244
+ const x = xyz.x.toFixed(2),
245
+ y = xyz.y.toFixed(2),
246
+ z = Math.round(xyz.z || 0);
247
+ return `${x}/${y}/${z}`;
248
+ }
249
+
250
+ /**
251
+ * Updates map and PSV according to current hash values
252
+ * @private
253
+ * @memberof Panoramax.utils.URLHandler#
254
+ */
255
+ _onURLChange() {
256
+ let vals = this.currentURLParams();
257
+
258
+ if(this._parent.getClassName() === "Viewer") { alterViewerState(this._parent, vals); }
259
+ else { alterPhotoViewerState(this._parent, vals); }
260
+
261
+ alterPSVState(this._parent.psv, vals);
262
+ if(this._parent.map) { alterMapState(this._parent.map, vals); }
263
+ }
264
+
265
+ /**
266
+ * Get short link URL (query replaced by Base64)
267
+ * @returns {str} The short link URL
268
+ * @memberof Panoramax.utils.URLHandler#
269
+ */
270
+ nextShortLink(baseUrl) {
271
+ const url = new URL(baseUrl);
272
+ const hashParts = this.nextURLParams();
273
+ const shortVals = {
274
+ f: (hashParts.focus || "").substring(0, 1),
275
+ s: !isNaN(parseInt(hashParts.speed)) ? Math.floor(parseInt(hashParts.speed)/100) : undefined,
276
+ n: (hashParts.nav || "").substring(0, 1),
277
+ p: hashParts.pic,
278
+ c: hashParts.xyz,
279
+ m: hashParts.map,
280
+ d: hashParts.date_from,
281
+ e: hashParts.date_to,
282
+ t: (hashParts.pic_type || "").substring(0, 1),
283
+ k: hashParts.camera,
284
+ v: (hashParts.theme || "").substring(0, 1),
285
+ b: (hashParts.background || "").substring(0, 1),
286
+ u: hashParts.users,
287
+ q: hashParts.pic_score,
288
+ };
289
+ const short = Object.entries(shortVals)
290
+ .filter(([,v]) => v != undefined && v != "")
291
+ .map(([k,v]) => `${k}${v}`)
292
+ .join(";");
293
+ url.search = `s=${short}`;
294
+ return url;
295
+ }
296
+
297
+ /**
298
+ * Returns a string containing only parameters out of URLHandler scope
299
+ * @param {URL} prevUrl The previously set URL
300
+ * @memberof Panoramax.utils.URLHandler#
301
+ */
302
+ getUnmanagedParameters(prevUrl) {
303
+ return new URLSearchParams(
304
+ Array.from(prevUrl.searchParams)
305
+ .filter(([k]) => !MANAGED_PARAMETERS.includes(k))
306
+ ).toString();
307
+ }
308
+
309
+ /**
310
+ * Changes the URL hash using current viewer parameters
311
+ * @private
312
+ * @memberof Panoramax.utils.URLHandler#
313
+ */
314
+ _onParentChange() {
315
+ if(this._delay) {
316
+ clearTimeout(this._delay);
317
+ this._delay = null;
318
+ }
319
+
320
+ this._delay = setTimeout(() => {
321
+ const prevUrl = new URL(window.location.href);
322
+ const nextUrl = new URL(window.location.href);
323
+ const unmanaged = this.getUnmanagedParameters(prevUrl);
324
+ nextUrl.search = this._parent ? this.nextURLString() + (unmanaged.length > 0 ? "&"+unmanaged : ""): "";
325
+
326
+ // Clear out hash if older parameters appear
327
+ if(Object.keys(this.currentURLParams(true)).length > 0) { nextUrl.hash = ""; }
328
+
329
+ // Skip hash update if no changes
330
+ if(prevUrl.search == nextUrl.search) { return; }
331
+
332
+ const prevPic = this.currentURLParams().pic || "";
333
+ const nextPic = this._parent?.psv?.getPictureId?.() || "";
334
+
335
+ try {
336
+ // If different pic, add entry in browser history
337
+ if(prevPic != nextPic) {
338
+ window.history.pushState(window.history.state, null, nextUrl.href);
339
+ }
340
+ // If same pic, just update viewer params
341
+ else {
342
+ window.history.replaceState(window.history.state, null, nextUrl.href);
343
+ }
344
+
345
+ if(this._parent) {
346
+ /**
347
+ * URL changed event
348
+ * @event Panoramax.utils.URLHandler#url-changed
349
+ * @type {CustomEvent}
350
+ * @property {string} detail.url The new used URL
351
+ */
352
+ const event = new CustomEvent("url-changed", { detail: {url: nextUrl.href}});
353
+ this.dispatchEvent(event);
354
+ }
355
+ } catch (SecurityError) {
356
+ // IE11 does not allow this if the page is within an iframe created
357
+ // with iframe.contentWindow.document.write(...).
358
+ // https://github.com/mapbox/mapbox-gl-js/issues/7410
359
+ }
360
+ }, 500);
361
+ }
362
+ }
@@ -0,0 +1,152 @@
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
+ * Transforms Nominatim search result into a nice-to-display address.
23
+ * @param {object} addr The Nominatim API "address" property
24
+ * @returns {string} The clean-up string for display
25
+ * @private
26
+ */
27
+ function nominatimAddressToPlaceName(addr) {
28
+ // API format @ https://nominatim.org/release-docs/develop/api/Output/#addressdetails
29
+ if(!addr || typeof addr != "object") { return ""; }
30
+
31
+ let res = "";
32
+
33
+ // House n°-like
34
+ if(addr.house_number) { res = addr.house_number; }
35
+ else if(addr.house_name) { res = addr.house_name; }
36
+ else {
37
+ const potentialNames = [
38
+ "emergency", "historic", "military", "natural", "landuse", "place", "railway", "man_made",
39
+ "aerialway", "boundary", "amenity", "aeroway", "club", "craft", "leisure", "office",
40
+ "mountain_pass", "shop", "tourism", "bridge", "tunnel", "waterway", "park"
41
+ ];
42
+ for(let pn of potentialNames) {
43
+ if(addr[pn]) {
44
+ res = addr[pn];
45
+ break;
46
+ }
47
+ }
48
+ }
49
+
50
+ // Street-like
51
+ let street;
52
+ if(addr.road && addr.road.length > 6) { street = addr.road; }
53
+ else {
54
+ const potentialNames = [
55
+ // Hamlet-like
56
+ "hamlet", "croft", "isolated_dwelling",
57
+ // Zone Indus-like
58
+ "farm", "farmyard", "industrial", "commercial", "retail", "city_block", "residential",
59
+ // Quarter-like
60
+ "neighbourhood", "allotments", "quarter",
61
+ // Fallback to road if nothing else found
62
+ "road"
63
+ ];
64
+ for(let pn of potentialNames) {
65
+ if(addr[pn]) {
66
+ street = addr[pn];
67
+ break;
68
+ }
69
+ }
70
+ }
71
+
72
+ if(street && res.length > 0) { res += (addr.house_number ? " " : ", ")+street; }
73
+ else if(street) { res = street; }
74
+
75
+ // City
76
+ if(addr.village || addr.town || addr.city || addr.municipality) {
77
+ if(res.length > 0) { res += ", "; }
78
+ res += addr.village || addr.town || addr.city || addr.municipality;
79
+ }
80
+
81
+ return res;
82
+ }
83
+
84
+ /**
85
+ * Nominatim (OSM) geocoder, ready to use for our Map
86
+ * @private
87
+ */
88
+ export function forwardGeocodingNominatim(config) {
89
+ // Transform parameters into Nominatim format
90
+ const params = {
91
+ q: config.query,
92
+ countrycodes: config.countries,
93
+ limit: config.limit,
94
+ viewbox: config.bbox,
95
+ };
96
+
97
+ return fetch(`https://nominatim.openstreetmap.org/search?${geocoderParamsToURLString(params)}&format=geojson&polygon_geojson=1&addressdetails=1`)
98
+ .then(res => res.json())
99
+ .then(res => {
100
+ const finalRes = { features: [] };
101
+ const listedNames = [];
102
+ res.features.forEach(f => {
103
+ const plname = nominatimAddressToPlaceName(f.properties.address) || f.properties.display_name;
104
+ if(!listedNames.includes(plname)) {
105
+ finalRes.features.push({
106
+ place_type: ["place"],
107
+ place_name: plname,
108
+ bounds: new maplibregl.LngLatBounds(f.bbox),
109
+ });
110
+ listedNames.push(plname);
111
+ }
112
+ });
113
+ return finalRes;
114
+ });
115
+ }
116
+
117
+ export function reverseGeocodingNominatim(lat, lon) {
118
+ return fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&zoom=18&format=jsonv2`)
119
+ .then(res => res.json())
120
+ .then(res => nominatimAddressToPlaceName(res?.address));
121
+ }
122
+
123
+ /**
124
+ * Base adresse nationale (FR) geocoder, ready to use for our Map
125
+ * @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 )
126
+ * @returns {object} GeoJSON Feature collection in Carmen GeoJSON format
127
+ * @private
128
+ */
129
+ export function forwardGeocodingBAN(config) {
130
+ // Transform parameters into BAN format
131
+ const params = { q: config.query, limit: config.limit };
132
+ if(typeof config.proximity === "string") {
133
+ const [lat, lon] = config.proximity.split(",").map(v => parseFloat(v.trim()));
134
+ params.lat = lat;
135
+ params.lon = lon;
136
+ }
137
+
138
+ const toPlaceName = p => [p.name, p.district, p.city].filter(v => v).join(", ");
139
+ const placeTypeToZoom = { "housenumber": 20, "street": 18, "locality": 15, "municipality": 12 };
140
+
141
+ return fetch(`https://api-adresse.data.gouv.fr/search/?${geocoderParamsToURLString(params)}`)
142
+ .then(res => res.json())
143
+ .then(res => {
144
+ res.features = res.features.map(f => ({
145
+ place_type: ["place"],
146
+ place_name: toPlaceName(f.properties),
147
+ center: new maplibregl.LngLat(...f.geometry.coordinates),
148
+ zoom: placeTypeToZoom[f.properties.type],
149
+ }));
150
+ return res;
151
+ });
152
+ }
@@ -6,13 +6,17 @@ 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";
10
+ import T_nl from "../translations/nl.json";
9
11
  import T_pl from "../translations/pl.json";
12
+ import T_sv from "../translations/sv.json";
10
13
  import T_zh_Hant from "../translations/zh_Hant.json";
11
14
 
12
15
  const FALLBACK_LOCALE = "en";
13
16
  const TRANSLATIONS = {
14
17
  "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,
18
+ "hu": T_hu, "it": T_it, "ja": T_ja, "nl": T_nl, "pl": T_pl, "sv": T_sv, "zh-Hant": T_zh_Hant,
19
+ "zh": T_zh_Hant,
16
20
  };
17
21
 
18
22
  /**
@@ -30,11 +34,11 @@ export function autoDetectLocale(supportedTranslations, fallback) { // eslint-ig
30
34
  case "zh-TW":
31
35
  case "zh-HK":
32
36
  case "zh-MO":
33
- language = "zh_Hant";
37
+ language = "zh-Hant";
34
38
  break;
35
39
  case "zh-CN":
36
40
  case "zh-SG":
37
- language = "zh_Hans";
41
+ language = "zh-Hans";
38
42
  break;
39
43
  default:
40
44
  if (language.length > 2) {
@@ -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";