@panoramax/web-viewer 5.0.0-develop-d7206514 → 5.0.0-develop-d26305dd

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 (152) hide show
  1. package/build/bundle.cjs +3399 -0
  2. package/build/bundle.cjs.map +1 -0
  3. package/build/bundle_photoviewer.cjs +2510 -0
  4. package/build/bundle_photoviewer.cjs.map +1 -0
  5. package/build/cjs/index.js +1 -1
  6. package/build/cjs/index_photoviewer.js +1 -1
  7. package/build/components/core/Basic.css +56 -0
  8. package/build/components/core/Basic.js +378 -0
  9. package/build/components/core/CoverageMap.css +10 -0
  10. package/build/components/core/CoverageMap.js +169 -0
  11. package/build/components/core/Editor.css +33 -0
  12. package/build/components/core/Editor.js +398 -0
  13. package/build/components/core/PhotoViewer.css +70 -0
  14. package/build/components/core/PhotoViewer.js +650 -0
  15. package/build/components/core/Viewer.css +130 -0
  16. package/build/components/core/Viewer.js +711 -0
  17. package/build/components/core/index.js +10 -0
  18. package/build/components/index.js +11 -0
  19. package/build/components/index_photoviewer.js +6 -0
  20. package/build/components/layout/BottomDrawer.js +258 -0
  21. package/build/components/layout/CorneredGrid.js +143 -0
  22. package/build/components/layout/Mini.js +121 -0
  23. package/build/components/layout/Tabs.js +140 -0
  24. package/build/components/layout/index.js +9 -0
  25. package/build/components/menus/LocationPrecisionDoc.js +42 -0
  26. package/build/components/menus/MapBackground.js +110 -0
  27. package/build/components/menus/MapFilters.js +567 -0
  28. package/build/components/menus/MapLayers.js +238 -0
  29. package/build/components/menus/MapLegend.js +68 -0
  30. package/build/components/menus/MiniPictureLegend.js +73 -0
  31. package/build/components/menus/PictureLegend.js +379 -0
  32. package/build/components/menus/PictureMetadata.js +380 -0
  33. package/build/components/menus/PlayerOptions.js +93 -0
  34. package/build/components/menus/QualityScoreDoc.js +42 -0
  35. package/build/components/menus/ReportForm.js +132 -0
  36. package/build/components/menus/SemanticsDoc.js +38 -0
  37. package/build/components/menus/SemanticsDownload.js +33 -0
  38. package/build/components/menus/SemanticsFilters.js +153 -0
  39. package/build/components/menus/SemanticsList.js +413 -0
  40. package/build/components/menus/SemanticsMetadata.js +368 -0
  41. package/build/components/menus/Share.js +105 -0
  42. package/build/components/menus/index.js +22 -0
  43. package/build/components/menus/index_photoviewer.js +11 -0
  44. package/build/components/styles.js +557 -0
  45. package/build/components/ui/AnnotationsSwitch.js +159 -0
  46. package/build/components/ui/Button.js +77 -0
  47. package/build/components/ui/ButtonGroup.css +59 -0
  48. package/build/components/ui/ButtonGroup.js +69 -0
  49. package/build/components/ui/CopyButton.js +110 -0
  50. package/build/components/ui/Grade.js +54 -0
  51. package/build/components/ui/GradeFilter.js +122 -0
  52. package/build/components/ui/IconSwitch.js +193 -0
  53. package/build/components/ui/LinkButton.js +67 -0
  54. package/build/components/ui/ListGroup.js +66 -0
  55. package/build/components/ui/ListItem.js +90 -0
  56. package/build/components/ui/Loader.js +203 -0
  57. package/build/components/ui/Map.css +63 -0
  58. package/build/components/ui/Map.js +853 -0
  59. package/build/components/ui/MapMore.js +175 -0
  60. package/build/components/ui/Photo.css +50 -0
  61. package/build/components/ui/Photo.js +1502 -0
  62. package/build/components/ui/Popup.js +145 -0
  63. package/build/components/ui/ProgressBar.js +104 -0
  64. package/build/components/ui/QualityScore.js +147 -0
  65. package/build/components/ui/SearchBar.js +374 -0
  66. package/build/components/ui/SemanticsEditor.js +191 -0
  67. package/build/components/ui/SemanticsTable.js +88 -0
  68. package/build/components/ui/Switch.js +139 -0
  69. package/build/components/ui/TogglableGroup.js +157 -0
  70. package/build/components/ui/index.js +29 -0
  71. package/build/components/ui/index_photoviewer.js +21 -0
  72. package/build/components/ui/widgets/CopyCoordinates.js +75 -0
  73. package/build/components/ui/widgets/GeoSearch.css +21 -0
  74. package/build/components/ui/widgets/GeoSearch.js +150 -0
  75. package/build/components/ui/widgets/Legend.js +190 -0
  76. package/build/components/ui/widgets/LevelSelect.css +51 -0
  77. package/build/components/ui/widgets/LevelSelect.js +143 -0
  78. package/build/components/ui/widgets/MapFiltersButton.js +114 -0
  79. package/build/components/ui/widgets/MapLayersButton.js +79 -0
  80. package/build/components/ui/widgets/OSMEditors.js +155 -0
  81. package/build/components/ui/widgets/PictureLegendActions.js +99 -0
  82. package/build/components/ui/widgets/Player.css +7 -0
  83. package/build/components/ui/widgets/Player.js +154 -0
  84. package/build/components/ui/widgets/SemanticsFiltersButton.js +65 -0
  85. package/build/components/ui/widgets/Zoom.js +84 -0
  86. package/build/components/ui/widgets/index.js +16 -0
  87. package/build/components/ui/widgets/index_photoviewer.js +7 -0
  88. package/build/esm/components/core/Basic.js +1 -1
  89. package/build/img/arrow_360.svg +14 -0
  90. package/build/img/arrow_flat.svg +11 -0
  91. package/build/img/arrow_triangle.svg +9 -0
  92. package/build/img/arrow_turn.svg +8 -0
  93. package/build/img/bg_aerial.jpg +0 -0
  94. package/build/img/bg_streets.jpg +0 -0
  95. package/build/img/loader_base.jpg +0 -0
  96. package/build/img/logo_dead.svg +91 -0
  97. package/build/img/marker.svg +17 -0
  98. package/build/img/marker_blue.svg +20 -0
  99. package/build/img/osm.svg +49 -0
  100. package/build/img/panoramax.svg +13 -0
  101. package/build/img/switch_big.svg +54 -0
  102. package/build/img/switch_mini.svg +48 -0
  103. package/build/img/wd.svg +1 -0
  104. package/build/index_photoviewer.js +4 -0
  105. package/build/package.json +148 -0
  106. package/build/servers.js +14 -0
  107. package/build/translations/ar.json +1 -0
  108. package/build/translations/be.json +257 -0
  109. package/build/translations/br.json +81 -0
  110. package/build/translations/cy.json +117 -0
  111. package/build/translations/da.json +300 -0
  112. package/build/translations/de.json +309 -0
  113. package/build/translations/en.json +294 -0
  114. package/build/translations/eo.json +235 -0
  115. package/build/translations/es.json +292 -0
  116. package/build/translations/fi.json +1 -0
  117. package/build/translations/fr.json +294 -0
  118. package/build/translations/hr.json +294 -0
  119. package/build/translations/hu.json +294 -0
  120. package/build/translations/it.json +306 -0
  121. package/build/translations/ja.json +182 -0
  122. package/build/translations/ko.json +1 -0
  123. package/build/translations/nl.json +305 -0
  124. package/build/translations/nn.json +1 -0
  125. package/build/translations/pl.json +169 -0
  126. package/build/translations/pt.json +296 -0
  127. package/build/translations/pt_BR.json +304 -0
  128. package/build/translations/sv.json +182 -0
  129. package/build/translations/ti.json +9 -0
  130. package/build/translations/tr.json +297 -0
  131. package/build/translations/uk.json +268 -0
  132. package/build/translations/zh_Hant.json +309 -0
  133. package/build/utils/API.js +928 -0
  134. package/build/utils/InitParameters.js +521 -0
  135. package/build/utils/MapStyleComposer.js +889 -0
  136. package/build/utils/PanoraMapProtocol.js +49 -0
  137. package/build/utils/PhotoAdapter.js +49 -0
  138. package/build/utils/PresetsManager.js +148 -0
  139. package/build/utils/SemanticsMapProtocol.js +144 -0
  140. package/build/utils/URLHandler.js +426 -0
  141. package/build/utils/geocoder.js +203 -0
  142. package/build/utils/i18n.js +128 -0
  143. package/build/utils/index.js +17 -0
  144. package/build/utils/index_photoviewer.js +14 -0
  145. package/build/utils/indoor.js +200 -0
  146. package/build/utils/map.js +788 -0
  147. package/build/utils/picture.js +507 -0
  148. package/build/utils/semantics.js +321 -0
  149. package/build/utils/services.js +148 -0
  150. package/build/utils/utils.js +433 -0
  151. package/build/utils/widgets.js +110 -0
  152. package/package.json +1 -1
@@ -0,0 +1,567 @@
1
+ import { LitElement, css, html, nothing } from "lit";
2
+ import { fa, onceParentAvailable } from "../../utils/widgets.js";
3
+ import { QUALITYSCORE_GPS_VALUES, getUserAccount } from "../../utils/utils.js";
4
+ import { isPanoramaxEndpointSingleUser } from "../../utils/map.js";
5
+ import { faSvg, titles } from "../styles.js";
6
+ import {
7
+ faArrowRight, faCalendar, faGlobeEurope, faImage, faInfoCircle,
8
+ faMapMarkerAlt, faMedal, faStar, faUser,
9
+ } from "@fortawesome/free-solid-svg-icons";
10
+
11
+
12
+ /**
13
+ * Map Filters menu allows user to select map data they want displayed.
14
+ * @class Panoramax.components.menus.MapFilters
15
+ * @element pnx-map-filters-menu
16
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
17
+ * @example
18
+ * ```html
19
+ * <pnx-map-filters-menu user-search="" _parent=${viewer} />
20
+ * ```
21
+ */
22
+ export default class MapFilters extends LitElement {
23
+ /** @private */
24
+ static styles = [ faSvg, titles, css`
25
+ .pnx-input-group {
26
+ display: flex;
27
+ flex-direction: row;
28
+ align-items: center;
29
+ justify-content: center;
30
+ gap: 5px;
31
+ }
32
+
33
+ /* Filter block */
34
+ .pnx-filter-block {
35
+ position: relative;
36
+ padding: 10px 15px;
37
+ border-bottom: 2px solid var(--widget-border-div);
38
+ }
39
+ .pnx-filter-block:first-child {
40
+ padding-top: 15px;
41
+ }
42
+ .pnx-filter-block:last-child {
43
+ border-bottom: none;
44
+ padding-bottom: 15px;
45
+ }
46
+ .pnx-filter-block h4 {
47
+ margin-bottom: 5px;
48
+ }
49
+ .pnx-filter-zoomin {
50
+ z-index: 131;
51
+ background-color: rgba(255,255,255,0.9);
52
+ position: absolute;
53
+ top: 0;
54
+ right: 0;
55
+ left: 0;
56
+ bottom: 0;
57
+ border-radius: 25px;
58
+ display: flex;
59
+ flex-direction: column;
60
+ justify-content: center;
61
+ align-items: baseline;
62
+ padding: 15px;
63
+ }
64
+ .pnx-filter-zoomin h5 {
65
+ font-weight: 900;
66
+ margin: 0 0 5px 0;
67
+ font-size: 1em;
68
+ }
69
+ .pnx-filter-zoomin div {
70
+ font-weight: unset;
71
+ font-size: 0.9em;
72
+ }
73
+ .pnx-filter-zoomin.hidden {
74
+ display: none;
75
+ }
76
+
77
+ /* Input styles */
78
+ .pnx-filter-active,
79
+ pnx-search-bar.pnx-filter-active::part(container),
80
+ pnx-search-bar.pnx-filter-active::part(input) {
81
+ background-color: var(--widget-bg-active) !important;
82
+ border-color: var(--widget-bg-active) !important;
83
+ color: var(--widget-font-active) !important;
84
+ }
85
+ input[type=date] {
86
+ min-width: 0;
87
+ flex-grow: 2;
88
+ padding: 2px 0;
89
+ text-align: center;
90
+ background-color: var(--widget-bg);
91
+ color: var(--widget-font);
92
+ border: 1px solid var(--widget-border-div);
93
+ border-radius: 20px;
94
+ font-family: var(--font-family);
95
+ }
96
+
97
+ /* Input shortcuts */
98
+ .pnx-input-shortcuts {
99
+ margin-top: 0px;
100
+ margin-bottom: 5px;
101
+ }
102
+ .pnx-input-shortcuts button {
103
+ border: none;
104
+ font-size: 0.75em;
105
+ padding: 2px 6px;
106
+ vertical-align: middle;
107
+ background-color: var(--grey-pale);
108
+ color: var(--black);
109
+ border-radius: 10px;
110
+ cursor: pointer;
111
+ font-family: var(--font-family);
112
+ }
113
+ .pnx-input-shortcuts button:hover {
114
+ background-color: #d9dcd9;
115
+ }
116
+
117
+ /* Checkbox looking like buttons */
118
+ .pnx-input-group.pnx-checkbox-btns {
119
+ gap: 0;
120
+ align-items: stretch;
121
+ }
122
+ .pnx-checkbox-btns label {
123
+ display: inline-block;
124
+ padding: 2px 7px;
125
+ background: none;
126
+ border: 1px solid var(--widget-border-btn);
127
+ color: var(--widget-font-direct);
128
+ cursor: pointer;
129
+ font-size: 16px;
130
+ text-decoration: none;
131
+ border-left-width: 0px;
132
+ }
133
+ .pnx-checkbox-btns label:hover {
134
+ background-color: var(--widget-bg-hover);
135
+ }
136
+ .pnx-checkbox-btns label:first-of-type {
137
+ border-top-left-radius: 8px;
138
+ border-bottom-left-radius: 8px;
139
+ border-left-width: 1px;
140
+ }
141
+ .pnx-checkbox-btns label:last-of-type {
142
+ border-top-right-radius: 8px;
143
+ border-bottom-right-radius: 8px;
144
+ }
145
+ .pnx-checkbox-btns input[type="radio"] { display: none; }
146
+ .pnx-checkbox-btns input[type="radio"]:checked + label {
147
+ background-color: var(--widget-bg-active);
148
+ color: var(--widget-font-active);
149
+ }
150
+ .pnx-checkbox-btns input[type="radio"]:checked + label:first-of-type {
151
+ border-right-color: white;
152
+ }
153
+
154
+ /* Force user search width */
155
+ #pnx-filter-search-user::part(container) { width: 100%; }
156
+ ` ];
157
+
158
+ /**
159
+ * Component properties.
160
+ * @memberof Panoramax.components.menus.MapFilters#
161
+ * @type {Object}
162
+ * @property {boolean} [user-search=false] Should user search filter show up ?
163
+ * @property {boolean} [quality-score=false] Should quality score filter show up ?
164
+ * @property {boolean} [no-date=false] Should date filters be hidden ?
165
+ * @property {boolean} [no-picture-type=false] Should picture type filter be hidden ?
166
+ * @property {boolean} [no-gps=false] Should GPS precision filter be hidden ?
167
+ * @property {boolean} [no-source=false] Should pictures source (world, instance) filter be hidden ?
168
+ */
169
+ static properties = {
170
+ "quality-score": {type: Boolean}, // Should be available in pnx-widget-mapfilters as well
171
+ "user-search": {type: Boolean}, // Should be available in pnx-widget-mapfilters as well
172
+ "no-date": {type: Boolean}, // Should be available in pnx-widget-mapfilters as well
173
+ "no-picture-type": {type: Boolean}, // Should be available in pnx-widget-mapfilters as well
174
+ "no-gps": {type: Boolean}, // Should be available in pnx-widget-mapfilters as well
175
+ "no-source": {type: Boolean}, // Should be available in pnx-widget-mapfilters as well
176
+ showZoomIn: {state: true},
177
+ minDate: {state: true},
178
+ maxDate: {state: true},
179
+ type: {state: true},
180
+ score: {state: true},
181
+ user: {state: true},
182
+ gps: {state: true},
183
+ source: {state: true},
184
+ };
185
+
186
+ constructor() {
187
+ super();
188
+ this._formDelay = null;
189
+ this.showZoomIn = true;
190
+ }
191
+
192
+ /** @private */
193
+ connectedCallback() {
194
+ super.connectedCallback();
195
+
196
+ // Input changes
197
+ for(let i of this.shadowRoot.querySelectorAll("input")) {
198
+ i.addEventListener("change", this._onFormChange.bind(this));
199
+ i.addEventListener("keypress", this._onFormChange.bind(this));
200
+ i.addEventListener("paste", this._onFormChange.bind(this));
201
+ i.addEventListener("input", this._onFormChange.bind(this));
202
+ }
203
+
204
+ // Map zoom
205
+ onceParentAvailable(this).then(() => this._parent.onceMapReady?.().then(() => {
206
+ this._parent.map.on("zoomend", this._onMapZoom.bind(this));
207
+ this._parent.mapStyleComposer.addEventListener("filters-changed", e => this._onParentFilterChange(e.detail));
208
+ this._parent.mapStyleComposer.addEventListener("panoramax-changed", () => this._onParentFilterChange(this._parent.mapStyleComposer.panoramaxFilters));
209
+ this._onMapZoom();
210
+ this._onParentFilterChange(this._parent.mapStyleComposer.panoramaxFilters);
211
+ }));
212
+ }
213
+
214
+ /**
215
+ * Map zoom event handler: show/hide "zoom in" labels
216
+ * @private
217
+ */
218
+ _onMapZoom() {
219
+ this.showZoomIn = this._parent.map.getZoom() < 7;
220
+ }
221
+
222
+ /**
223
+ * Filter changes on parent: update input fields
224
+ * @private
225
+ */
226
+ _onParentFilterChange(e) {
227
+ this.minDate = e?.minDate || null;
228
+ this.maxDate = e?.maxDate || null;
229
+
230
+ // Sanity check for date
231
+ if(this.minDate && this.maxDate && this.minDate > this.maxDate) {
232
+ const prevMin = this.minDate;
233
+ this.minDate = this.maxDate;
234
+ this.maxDate = prevMin;
235
+ this._onFormChange();
236
+ }
237
+
238
+ this.score = Array.isArray(e?.qualityscore) ? e.qualityscore.join(",") : (e?.qualityscore || "");
239
+ this.gps = e?.gps && Array.isArray(e.gps) ? e.gps.join(",") : (e?.gps?.length > 0 ? e.gps : "");
240
+
241
+ this.type = "";
242
+ if(e?.pic_type && e.pic_type !== "") {
243
+ this.type = e.pic_type === "flat" ? "flat" : "equirectangular";
244
+ }
245
+
246
+ if(!isPanoramaxEndpointSingleUser(this._parent.mapStyleComposer.panoramax)) {
247
+ this.source = this._parent.mapStyleComposer.panoramax;
248
+ this.user = null;
249
+ }
250
+ else {
251
+ this.source = this._parent.mapStyleComposer.panoramax.startsWith("metacatalog_") ? "metacatalog" : "default";
252
+
253
+ // Find username if first load
254
+ // eslint-disable-next-line eqeqeq
255
+ if(this.user == null) {
256
+ let userId = this._parent.mapStyleComposer.panoramax.replace("metacatalog_", "");
257
+ this._parent.getAPI().getUserName(userId).then(username => {
258
+ this.user = username;
259
+ this.shadowRoot.querySelector("#pnx-filter-search-user").setAttribute("value", username);
260
+ });
261
+ }
262
+ }
263
+ }
264
+
265
+ /** @private */
266
+ _onSubmit(e) {
267
+ e.preventDefault();
268
+ this._onFormChange();
269
+ return false;
270
+ }
271
+
272
+ /** @private */
273
+ _onFormChange() {
274
+ if(this._formDelay) { clearTimeout(this._formDelay); }
275
+ this._formDelay = setTimeout(() => {
276
+ this.minDate = this.shadowRoot.querySelector("#pnx-filter-date-from")?.value;
277
+ this.maxDate = this.shadowRoot.querySelector("#pnx-filter-date-end")?.value;
278
+ const fTypeFlat = this.shadowRoot.querySelector("#pnx-filter-type-flat");
279
+ const fType360 = this.shadowRoot.querySelector("#pnx-filter-type-360");
280
+ this.type = fType360.checked ? "equirectangular" : (fTypeFlat.checked ? "flat" : "");
281
+ this.score = this.shadowRoot.querySelector("#pnx-filter-qualityscore")?.grade;
282
+ this.gps = this.shadowRoot.querySelector("#pnx-filter-gps")?.grade;
283
+ const msc = this._parent.mapStyleComposer;
284
+ const newFilters = {
285
+ minDate: this.minDate,
286
+ maxDate: this.maxDate,
287
+ pic_type: this.type,
288
+ qualityscore: this.score,
289
+ gps: this.gps,
290
+ featuresRestrictions: msc.panoramaxFilters.featuresRestrictions,
291
+ };
292
+ msc.setPanoramaxFilters(newFilters);
293
+ }, 250);
294
+ }
295
+
296
+ /** @private */
297
+ _onSourceChange(e) {
298
+ this.source = e.target.checked ? "default" : "metacatalog";
299
+ this.user = null;
300
+ this._onUserSearchResult();
301
+ }
302
+
303
+ /** @private */
304
+ _userSearch(value) {
305
+ return this._parent.getAPI?.().searchUsers(value)
306
+ .then(data => ((data || [])
307
+ .map(f => ({
308
+ title: f.label,
309
+ data: f
310
+ }))
311
+ ));
312
+ }
313
+
314
+ /** @private */
315
+ _onUserSearchResult(e) {
316
+ if(e?.detail?.data) {
317
+ e.target.classList.add("pnx-filter-active");
318
+ const userid = this.source === "metacatalog" ? "metacatalog_"+e.detail.data.id : e.detail.data.id;
319
+ return this._parent?.mapStyleComposer._createPanoramaxEndpointForUser(userid);
320
+ } else {
321
+ const field = (e?.target || this.shadowRoot.querySelector("#pnx-filter-search-user"));
322
+ if(field) {
323
+ field.classList.remove("pnx-filter-active");
324
+ field.setAttribute("value", "");
325
+ }
326
+ return this._parent?.mapStyleComposer.switchPanoramaxEndpoint(this.source);
327
+ }
328
+ }
329
+
330
+ /** @private */
331
+ _onReset() {
332
+ this.shadowRoot.querySelector("#pnx-filter-qualityscore")?.setAttribute("grade", "");
333
+ this.shadowRoot.querySelector("#pnx-filter-search-user")?.reset();
334
+ this.shadowRoot.querySelector("#pnx-filter-gps")?.setAttribute("grade", "");
335
+ this.minDate = null;
336
+ this.maxDate = null;
337
+ this.type = "";
338
+ this.score = null;
339
+ this.user = null;
340
+ this._onFormChange();
341
+ }
342
+
343
+ /** @private */
344
+ _onDateShortcut(date) {
345
+ const dateFromField = this.shadowRoot.getElementById("pnx-filter-date-from");
346
+ const dateToField = this.shadowRoot.getElementById("pnx-filter-date-end");
347
+
348
+ if(dateFromField) {
349
+ if(dateFromField.value !== date) { dateFromField.value = date; }
350
+ else { dateFromField.value = ""; }
351
+ }
352
+
353
+ if(dateToField) { dateToField.value = ""; }
354
+ }
355
+
356
+ /** @private */
357
+ _onMeUserSearch() {
358
+ const userAccount = getUserAccount();
359
+ if(!userAccount) { return; }
360
+
361
+ const userField = this.shadowRoot.getElementById("pnx-filter-search-user");
362
+ if(!this._parent?.mapStyleComposer?.panoramax?.endsWith(userAccount.id)) {
363
+ userField?._onResultClick({title: userAccount.name, data: {id: userAccount.id }});
364
+ }
365
+ else {
366
+ userField?._onResultClick();
367
+ }
368
+ }
369
+
370
+ /** @private */
371
+ render() {
372
+ const userAccount = getUserAccount();
373
+
374
+ const gpsPrecLabels = {
375
+ "1": this._parent?._t.pnx.filter_gps_precision_morethan
376
+ .replace("{n}", QUALITYSCORE_GPS_VALUES[7])
377
+ .replace(/\.01/g, ""),
378
+ "2": this._parent?._t.pnx.filter_gps_precision_between
379
+ .replace("{n1}", QUALITYSCORE_GPS_VALUES[5])
380
+ .replace("{n2}", QUALITYSCORE_GPS_VALUES[7])
381
+ .replace(/\.01/g, ""),
382
+ "3": this._parent?._t.pnx.filter_gps_precision_between
383
+ .replace("{n1}", QUALITYSCORE_GPS_VALUES[3])
384
+ .replace("{n2}", QUALITYSCORE_GPS_VALUES[5])
385
+ .replace(/\.01/g, ""),
386
+ "4": this._parent?._t.pnx.filter_gps_precision_between
387
+ .replace("{n1}", QUALITYSCORE_GPS_VALUES[1])
388
+ .replace("{n2}", QUALITYSCORE_GPS_VALUES[3])
389
+ .replace(/\.01/g, ""),
390
+ "5": this._parent?._t.pnx.filter_gps_precision_lessthan
391
+ .replace("{n}", QUALITYSCORE_GPS_VALUES[1])
392
+ .replace(/\.01/g, ""),
393
+ };
394
+
395
+ let serverName = this._parent.api._metadata?.name; // Keep _parent.api as it's local instance name
396
+ if(!serverName || serverName === "Unnamed") { serverName = this._parent?._t.pnx.filter_source_local; }
397
+
398
+ const lockedFilter = () => html`
399
+ <div class="pnx-filter-zoomin ${this.showZoomIn ? "" : "hidden"}">
400
+ <h5>${this._parent?._t.pnx.filter_locked}</h5>
401
+ <div>${this._parent?._t.pnx.filter_zoom_in}</div>
402
+ </div>`;
403
+
404
+ return html`<form
405
+ @reset=${this._onReset}
406
+ @change=${this._onFormChange}
407
+ @submit=${this._onSubmit}
408
+ >
409
+ ${this["no-source"] ? "" : html`<div class="pnx-filter-block">
410
+ <pnx-icon-switch
411
+ id="pnx-pictures-source"
412
+ name="pnx-pictures-source"
413
+ @change=${this._onSourceChange}
414
+ .checked=${this.source === "default"}
415
+ >
416
+ ${fa(faGlobeEurope, { attributes: { slot: "icon-left" }})}
417
+ <span slot="label-left">${this._parent?._t.pnx.filter_source_world}</span>
418
+ ${fa(faStar, { attributes: { slot: "icon-right" }})}
419
+ <span slot="label-right">${serverName}</span>
420
+ </pnx-icon-switch>
421
+ </div>`}
422
+
423
+ ${this["no-picture-type"] ? "" : html`<div class="pnx-filter-block">
424
+ <h4>${fa(faImage)} ${this._parent?._t.pnx.filter_picture}</h4>
425
+ <div class="pnx-input-group pnx-checkbox-btns" style="justify-content: center;">
426
+ <input
427
+ type="radio"
428
+ id="pnx-filter-type-all"
429
+ name="pnx-filter-type"
430
+ value=""
431
+ .checked=${!this.type || this.type === ""}
432
+ />
433
+ <label for="pnx-filter-type-all">${this._parent?._t.pnx.picture_all}</label>
434
+ <input
435
+ type="radio"
436
+ id="pnx-filter-type-flat"
437
+ name="pnx-filter-type"
438
+ value="flat"
439
+ .checked=${this.type === "flat"}
440
+ />
441
+ <label for="pnx-filter-type-flat">${this._parent?._t.pnx.picture_flat}</label>
442
+ <input
443
+ type="radio"
444
+ id="pnx-filter-type-360"
445
+ name="pnx-filter-type"
446
+ value="equirectangular"
447
+ .checked=${this.type === "equirectangular"}
448
+ />
449
+ <label for="pnx-filter-type-360">${this._parent?._t.pnx.picture_360}</label>
450
+ </div>
451
+ </div>`}
452
+
453
+ ${this["user-search"] ? html`
454
+ <div class="pnx-filter-block">
455
+ <h4>${fa(faUser)} ${this._parent?._t.pnx.filter_user}</h4>
456
+ ${this.source === "default" && userAccount ? html`
457
+ <div class="pnx-input-shortcuts">
458
+ <button @click=${this._onMeUserSearch}>
459
+ ${this._parent?._t.pnx.filter_user_mypics}
460
+ </button>
461
+ </div>
462
+ ` : nothing}
463
+ <pnx-search-bar
464
+ id="pnx-filter-search-user"
465
+ placeholder=${this._parent?._t.pnx.search_user}
466
+ class=${this.user ? "pnx-filter-active" : ""}
467
+ .value=${this.user}
468
+ @result=${this._onUserSearchResult}
469
+ .searcher=${this._userSearch.bind(this)}
470
+ ._parent=${this._parent}
471
+ no-menu-closure
472
+ >
473
+ </pnx-search-bar>
474
+ </div>
475
+ ` : nothing}
476
+
477
+ ${this["no-date"] ? "" : html`<div class="pnx-filter-block">
478
+ ${lockedFilter()}
479
+ <h4>${fa(faCalendar)} ${this._parent?._t.pnx.filter_date}</h4>
480
+ <div class="pnx-input-shortcuts">
481
+ <button
482
+ @click=${() => this._onDateShortcut(new Date(new Date().setMonth(new Date().getMonth() - 1)).toISOString().split("T")[0])}
483
+ >${this._parent?._t.pnx.filter_date_1month}</button>
484
+ <button
485
+ @click=${() => this._onDateShortcut(new Date(new Date().setMonth(new Date().getMonth() - 6)).toISOString().split("T")[0])}
486
+ >${this._parent?._t.pnx.filter_date_6months}</button>
487
+ <button
488
+ @click=${() => this._onDateShortcut(new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString().split("T")[0])}
489
+ >${this._parent?._t.pnx.filter_date_1year}</button>
490
+ </div>
491
+ <div class="pnx-input-group">
492
+ <input
493
+ type="date"
494
+ id="pnx-filter-date-from"
495
+ .value=${this.minDate}
496
+ class=${this.minDate && this.minDate !== "" ? "pnx-filter-active" : ""}
497
+ />
498
+ ${fa(faArrowRight)}
499
+ <input
500
+ type="date"
501
+ id="pnx-filter-date-end"
502
+ .value=${this.maxDate}
503
+ class=${this.maxDate && this.maxDate !== "" ? "pnx-filter-active" : ""}
504
+ />
505
+ </div>
506
+ </div>`}
507
+
508
+ ${this["quality-score"] ? html`
509
+ <div class="pnx-filter-block">
510
+ ${lockedFilter()}
511
+ <h4>
512
+ ${fa(faMedal)} ${this._parent?._t.pnx.filter_qualityscore}
513
+ <pnx-button
514
+ title="${this._parent?._t.pnx.metadata_quality_help}"
515
+ kind="superinline"
516
+ @click=${() => this._parent?._showQualityScoreDoc()}
517
+ >
518
+ ${fa(faInfoCircle)}
519
+ </pnx-button>
520
+ </h4>
521
+ <div class="pnx-input-group">
522
+ <pnx-quality-score
523
+ id="pnx-filter-qualityscore"
524
+ ._t=${this._parent?._t}
525
+ input="pnx-filter-qualityscore"
526
+ grade=${this.score}
527
+ @change=${this._onFormChange}
528
+ >
529
+ </pnx-quality-score>
530
+ </div>
531
+ </div>
532
+ ` : nothing}
533
+
534
+ ${this["no-gps"] ? "" : html`
535
+ <div class="pnx-filter-block">
536
+ ${lockedFilter()}
537
+ <h4>
538
+ ${fa(faMapMarkerAlt)} ${this._parent?._t.pnx.filter_gps_precision}
539
+ ${this._parent._showLocationPrecisionDoc ? html`
540
+ <pnx-button
541
+ title="${this._parent?._t.pnx.filter_gps_precision_help}"
542
+ kind="superinline"
543
+ @click=${() => this._parent._showLocationPrecisionDoc()}
544
+ >
545
+ ${fa(faInfoCircle)}
546
+ </pnx-button>
547
+ ` : nothing}
548
+ </h4>
549
+ <div class="pnx-input-group">
550
+ <pnx-grade-filter
551
+ id="pnx-filter-gps"
552
+ ._t=${this._parent?._t}
553
+ input="pnx-filter-gps"
554
+ grade=${this.gps}
555
+ @change=${this._onFormChange}
556
+ .labels=${gpsPrecLabels}
557
+ >
558
+ </pnx-grade-filter>
559
+ </div>
560
+ </div>
561
+ `}
562
+
563
+ </form>`;
564
+ }
565
+ }
566
+
567
+ customElements.define("pnx-map-filters-menu", MapFilters);