@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,562 +0,0 @@
1
- // Every single icon imported separately to reduce bundle size
2
- import { icon } from "@fortawesome/fontawesome-svg-core";
3
- import { faXmark } from "@fortawesome/free-solid-svg-icons/faXmark";
4
- import { faCircleExclamation } from "@fortawesome/free-solid-svg-icons/faCircleExclamation";
5
- import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
6
- import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons/faMagnifyingGlass";
7
- import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
8
- import { faCheck } from "@fortawesome/free-solid-svg-icons/faCheck";
9
- import { faCopy } from "@fortawesome/free-solid-svg-icons/faCopy";
10
- import { faStar } from "@fortawesome/free-solid-svg-icons/faStar";
11
- import { faStar as farStar } from "@fortawesome/free-regular-svg-icons/faStar";
12
- import { QUALITYSCORE_VALUES } from "./Utils";
13
-
14
-
15
- /**
16
- * Creates a new button, already styled
17
- * @param {string} id The component ID
18
- * @param {string|Element} content The text content
19
- * @param {string} [title] A title label on overlay
20
- * @param {string[]} [classes] List of CSS classes to add
21
- * @returns {Element} The created button
22
- * @private
23
- */
24
- export function createButton(id, content = null, title = null, classes = []) {
25
- const btn = document.createElement("button");
26
- if(content) {
27
- if(content instanceof HTMLElement || content instanceof Node) {
28
- btn.appendChild(content);
29
- }
30
- else {
31
- btn.innerHTML = content;
32
- }
33
- }
34
- btn.id = id;
35
- if(Array.isArray(classes)) {
36
- classes = classes.filter(c => c != null && c.length > 0);
37
- }
38
- btn.classList.add("gvs-btn", "gvs-widget-bg", ...classes);
39
- if(title) { btn.title = title; }
40
- return btn;
41
- }
42
-
43
- /**
44
- * Creates a new "expandable" button, already styled
45
- * @param {string} id The component ID
46
- * @param {object} icon The FontAwesome icon definition
47
- * @param {string} label The label text
48
- * @param {Widgets} container The widgets container
49
- * @param {string[]} [classes] List of CSS classes to add
50
- * @returns {Element} The created button
51
- * @private
52
- */
53
- export function createExpandableButton(id, icon, label, container, classes = []) {
54
- const btn = document.createElement("button");
55
- btn.id = id;
56
- btn.appendChild(fa(icon));
57
- if(!container._viewer.isWidthSmall()) {
58
- btn.appendChild(document.createTextNode(label));
59
- if(classes.includes("gvs-filter-unset-btn")) {
60
- const resetIcon = fa(faXmark, { classes: ["gvs-filter-unset-btn"]});
61
- resetIcon.style.display = "none";
62
- btn.appendChild(resetIcon);
63
- classes = classes.filter(v => v !== "gvs-filter-unset-btn");
64
- }
65
- btn.appendChild(fa(faChevronDown));
66
- }
67
- else {
68
- btn.title = label;
69
- }
70
- btn.classList.add("gvs-btn", "gvs-widget-bg", "gvs-btn-expandable", ...classes);
71
- btn.setActive = val => {
72
- let span = btn.querySelector(".gvs-filters-active");
73
- const resetIcon = btn.querySelector(".gvs-filter-unset-btn");
74
- const downIcon = btn.querySelector(".fa-chevron-down");
75
- if(val && !span) {
76
- span = document.createElement("span");
77
- span.classList.add("gvs-filters-active");
78
- const svg = btn.querySelector("svg");
79
- if(svg.nextSibling) { btn.insertBefore(span, svg.nextSibling); }
80
- else { btn.appendChild(span); }
81
- if(resetIcon) {
82
- resetIcon.style.display = null;
83
- downIcon.style.display = "none";
84
- }
85
- }
86
- else if(!val && span) {
87
- span.remove();
88
- if(resetIcon) {
89
- resetIcon.style.display = "none";
90
- downIcon.style.display = null;
91
- }
92
- }
93
- };
94
- return btn;
95
- }
96
-
97
- /**
98
- * Creates a new search bar
99
- * @param {string} id The bar ID
100
- * @param {string} placeholder The default label to display when search field is empty
101
- * @param {function} onInput Event handler for search text input (should return a Promise)
102
- * @param {function} onResultClick Event handler for result entry click
103
- * @param {Widgets} container The widgets container
104
- * @param {boolean} [nonClosingPanel] Should the search result closes other panels
105
- * @param {boolean} [reduced] Should the search bar be reduced by default ?
106
- * @param {Element} [preContent] DOM element to insert before search input
107
- * @returns {Element} The search bar
108
- * @private
109
- */
110
- export function createSearchBar(
111
- id, placeholder, onInput, onResultClick,
112
- container, nonClosingPanel = false, reduced = false,
113
- preContent = null,
114
- ) {
115
- // Container
116
- const bar = document.createElement("div");
117
- bar.classList.add("gvs-widget-bg", "gvs-search-bar");
118
- bar.id = id;
119
- if(reduced) { bar.classList.add("gvs-search-bar-reducable"); }
120
-
121
- // Pre-content
122
- if(preContent) {
123
- bar.appendChild(preContent);
124
- }
125
-
126
- // Input field
127
- const input = document.createElement("input");
128
- input.type = "text";
129
- input.placeholder = placeholder;
130
- input.id = `${id}-input`;
131
- input.setAttribute("autocomplete", "off");
132
- bar.appendChild(input);
133
- const extendInput = () => {
134
- bar.classList.remove("gvs-search-bar-reduced");
135
- };
136
- const reduceInput = () => {
137
- bar.classList.add("gvs-search-bar-reduced");
138
- };
139
- if(reduced) { reduceInput(); }
140
-
141
- // Status icon
142
- const icon = document.createElement("span");
143
- icon.classList.add("gvs-search-bar-icon");
144
- const iconSearch = fa(faMagnifyingGlass);
145
- const iconLoading = fa(faCircleNotch, { classes: ["fa-spin"] });
146
- const iconEmpty = fa(faXmark);
147
- const iconWarn = fa(faCircleExclamation);
148
- icon.appendChild(iconSearch);
149
- bar.appendChild(icon);
150
-
151
- // List of results
152
- const list = createPanel(container, bar, [], ["gvs-search-bar-results"], nonClosingPanel);
153
- bar.appendChild(list);
154
-
155
- // Change status icon
156
- const switchIcon = newStatusIcon => {
157
- icon.innerHTML = "";
158
- icon.appendChild(newStatusIcon);
159
- };
160
-
161
- // Reset search bar
162
- const resetSearch = () => {
163
- if(bar._throttler) { clearTimeout(bar._throttler); }
164
- input.value = "";
165
- list.innerHTML = "";
166
- list._toggle(false);
167
- delete bar._lastSearch;
168
- switchIcon(iconSearch);
169
- onResultClick(null);
170
- if(reduced) { reduceInput(); }
171
- };
172
- bar.resetSearch = resetSearch;
173
-
174
- // Handle result item click
175
- input.goItem = (entry) => {
176
- if(reduced) {
177
- onResultClick(entry);
178
- resetSearch();
179
- }
180
- else {
181
- if(bar._throttler) { clearTimeout(bar._throttler); }
182
- input.value = entry.title;
183
- list.innerHTML = "";
184
- list._toggle(false);
185
- switchIcon(iconEmpty);
186
- onResultClick(entry);
187
- }
188
- };
189
-
190
- // Force item selection
191
- input.setItem = (text) => {
192
- if(bar._throttler) { clearTimeout(bar._throttler); }
193
- input.value = text;
194
- list.innerHTML = "";
195
- list._toggle(false);
196
- switchIcon(iconEmpty);
197
- if(reduced) { extendInput(); }
198
- };
199
-
200
- // Handle search
201
- const goSearch = () => {
202
- if(bar._throttler) { clearTimeout(bar._throttler); }
203
-
204
- if(input.value.length === 0) {
205
- list.innerHTML = "";
206
- list._toggle(false);
207
- return;
208
- }
209
-
210
- bar._throttler = setTimeout(() => {
211
- list.innerHTML = "";
212
- list._toggle(false);
213
- switchIcon(iconLoading);
214
-
215
- onInput(input.value).then(data => {
216
- switchIcon(iconEmpty);
217
- list._toggle(true);
218
-
219
- if(!data || data.length == 0) {
220
- list.innerHTML = `<div class="gvs-search-empty">${container._t.gvs.search_empty}</li>`;
221
- return;
222
- }
223
- else if(data === true) {
224
- list._toggle(false);
225
- return;
226
- }
227
-
228
- data.forEach(entry => {
229
- const listEntry = document.createElement("div");
230
- listEntry.classList.add("gvs-search-bar-result");
231
- listEntry.innerHTML = `${entry.title}<br /><small>${entry?.subtitle || ""}</small>`;
232
- list.appendChild(listEntry);
233
- listEntry.addEventListener("click", () => input.goItem(entry));
234
- });
235
- }).catch(e => {
236
- console.error(e);
237
- switchIcon(iconWarn);
238
- });
239
- }, 250);
240
- };
241
-
242
- input.addEventListener("change", goSearch);
243
- input.addEventListener("keypress", goSearch);
244
- input.addEventListener("paste", goSearch);
245
- input.addEventListener("input", goSearch);
246
- icon.addEventListener("click", () => {
247
- if(icon.firstChild == iconEmpty || icon.firstChild == iconWarn) {
248
- resetSearch();
249
- }
250
- if(reduced && icon.firstChild == iconSearch) {
251
- if(!bar.classList.contains("gvs-search-bar-reduced")) { reduceInput(); }
252
- else { extendInput(); }
253
- }
254
- });
255
-
256
- return bar;
257
- }
258
-
259
- /**
260
- * Creates a panel associated to a button
261
- * @param {Widgets} container The widgets container
262
- * @param {Element} btn The component to associate to
263
- * @param {Element[]} [elements] DOM elements to append into
264
- * @param {str[]} [classes] CSS classes to add
265
- * @param {boolean} [nonClosingPanel] Should this panel closes other when opened
266
- * @returns {Element} The created panel
267
- * @private
268
- */
269
- export function createPanel(container, btn, elements = [], classes = [], nonClosingPanel = false) {
270
- const panel = document.createElement("div");
271
- panel.id = btn.id + "-panel";
272
- if(Array.isArray(classes)) {
273
- classes = classes.filter(c => c != null && c.length > 0);
274
- }
275
- panel.classList.add("gvs-panel", "gvs-widget-bg", "gvs-hidden", ...classes);
276
- for(let e of elements) {
277
- panel.appendChild(e);
278
- }
279
-
280
- const togglePanel = (e, visible) => {
281
- if(e) { e.stopPropagation(); }
282
- if(visible === true) { panel.classList.remove("gvs-hidden"); }
283
- else if(visible === false) { panel.classList.add("gvs-hidden"); }
284
- else {
285
- panel.classList.toggle("gvs-hidden");
286
- visible = !panel.classList.contains("gvs-hidden");
287
- }
288
-
289
- // Hide all other panels
290
- if(visible && !nonClosingPanel) {
291
- closeOtherPanels(panel, container._viewer.container);
292
- }
293
- };
294
- panel._toggle = v => togglePanel(null, v);
295
-
296
- if(btn.tagName == "BUTTON") {
297
- btn.addEventListener("click", togglePanel);
298
- btn.addEventListener("hover", togglePanel);
299
- }
300
-
301
- return panel;
302
- }
303
-
304
- /**
305
- * Makes all previously opened panels closed if clicked outside of one.
306
- * @param {Element} target The DOM element which has been clicked
307
- * @param {Element} container The viewer container
308
- * @private
309
- */
310
- export function closeOtherPanels(target, container) {
311
- const isPanel = p => (
312
- p.classList.contains("gvs-panel")
313
- || p.classList.contains("gvs-search-bar-result")
314
- || p.classList.contains("gvs-search-empty")
315
- || p.classList.contains("gvs-search-bar-reducable")
316
- );
317
-
318
- // Find nearest panel
319
- if(!isPanel(target) && target?.parentNode) {
320
- target = target.parentNode;
321
- while(target instanceof Element) {
322
- if(isPanel(target)) { break; }
323
- else { target = target.parentNode; }
324
- }
325
- }
326
-
327
- // Click outside of open panel = closing
328
- for(const p of container.getElementsByClassName("gvs-panel")) {
329
- if(p != target && !p.contains(target) && !p.classList.contains("gvs-hidden")) {
330
- p.classList.add("gvs-hidden");
331
- }
332
- }
333
- for(const p of container.getElementsByClassName("gvs-search-bar-reducable")) {
334
- if(p != target && !p.contains(target) && !p.classList.contains("gvs-search-bar-reduced")) {
335
- p.resetSearch();
336
- }
337
- }
338
- }
339
-
340
- /**
341
- * Creates a new group of elements, already styled
342
- * @param {str} id
343
- * @param {str} position (format: component-corner, with component = main/mini, and corner = top-left, top-right, top, bottom-left, bottom, bottom-right)
344
- * @param {Element[]} [elements] The children elements to add
345
- * @param {str[]} [classes] The CSS classes to add
346
- * @returns {Element} The created group
347
- * @private
348
- */
349
- export function createGroup(id, position, container, elements = [], classes = []) {
350
- const group = document.createElement("div");
351
- group.id = id;
352
- if(Array.isArray(classes)) {
353
- classes = classes.filter(c => c != null && c.length > 0);
354
- }
355
- group.classList.add("gvs-group", ...classes);
356
- for(let e of elements) {
357
- group.appendChild(e);
358
- }
359
- container._corners[position].appendChild(group);
360
- return group;
361
- }
362
-
363
- /**
364
- * Make all buttons with data-copy=* or data-input=* attributes able to copy to clipboard.
365
- *
366
- * @param {Element} container The parent container
367
- * @param {object} t The translation container
368
- * @private
369
- */
370
- export function enableCopyButton(container, t) {
371
- for(let btn of container.getElementsByTagName("button")) {
372
- const field = btn.getAttribute("data-input");
373
- const copy = btn.getAttribute("data-copy");
374
- if(field || copy) {
375
- btn.addEventListener("click", () => {
376
- let text;
377
- if(field) {
378
- const inputField = document.getElementById(field);
379
- text = inputField.innerText || inputField.value;
380
- }
381
- else if(copy) {
382
- text = btn.getAttribute("data-copy");
383
- }
384
- navigator.clipboard.writeText(text);
385
- const btnOrigContent = btn.innerHTML;
386
- btn.innerHTML = `${t.gvs.copied} ${fat(faCheck)}`;
387
- btn.classList.add("gvs-btn-active");
388
- setTimeout(() => {
389
- btn.innerHTML = btnOrigContent;
390
- btn.classList.remove("gvs-btn-active");
391
- }, 2000);
392
- });
393
- }
394
- }
395
- }
396
-
397
- /**
398
- * Make a button usable
399
- * @param {Element} btn
400
- * @private
401
- */
402
- export function enableButton(btn) {
403
- btn.removeAttribute("disabled");
404
- }
405
-
406
- /**
407
- * Make a button unusable
408
- * @param {Element} btn
409
- * @private
410
- */
411
- export function disableButton(btn) {
412
- btn.setAttribute("disabled", "");
413
- }
414
-
415
- /**
416
- * Transform Font Awesome icon definition into HTML element
417
- * @param {IconDefinition} i The icon to use
418
- * @param {object} [o] [FontAwesome icon parameters](https://origin.fontawesome.com/docs/apis/javascript/methods#icon-icondefinition-params)
419
- * @returns {Element} HTML element
420
- * @private
421
- */
422
- export function fa(i, o) {
423
- return icon(i, o).node[0];
424
- }
425
-
426
- /**
427
- * Transform Font Awesome icon definition into HTML text
428
- * @param {IconDefinition} i The icon to use
429
- * @param {object} [o] [FontAwesome icon parameters](https://origin.fontawesome.com/docs/apis/javascript/methods#icon-icondefinition-params)
430
- * @returns {string} HTML element as text
431
- * @private
432
- */
433
- export function fat(i, o) {
434
- return icon(i, o).html[0];
435
- }
436
-
437
- /**
438
- * Table cell with a copy link
439
- * @private
440
- */
441
- export function createLinkCell(id, url, title, buttonTitle) {
442
- const link = document.createElement("a");
443
- link.href = url;
444
- link.target = "_blank";
445
- link.title = title;
446
- link.textContent = id;
447
-
448
- const buttonContainer = createButtonSpan(`${fat(faCopy)} ${buttonTitle}`, id);
449
- return [link, buttonContainer];
450
- }
451
-
452
- /**
453
- * Create a light table
454
- * @private
455
- */
456
- export function createTable(className, rows) {
457
- const table = document.createElement("table");
458
- table.className = className;
459
-
460
- rows.forEach(({ section, value, values, classes }) => {
461
- const tr = document.createElement("tr");
462
- const th = document.createElement("th");
463
- th.scope = "row";
464
- th.textContent = section;
465
- tr.appendChild(th);
466
-
467
- const td = document.createElement("td");
468
- if(classes) { td.classList.add(...classes); }
469
- if(values) { values.forEach(v => td.appendChild(v)); }
470
- else if(value instanceof HTMLElement) { td.appendChild(value); }
471
- else { td.innerHTML = value; }
472
- tr.appendChild(td);
473
-
474
- table.appendChild(tr);
475
- });
476
-
477
- return table;
478
- }
479
-
480
- /**
481
- * Create block header
482
- * @private
483
- */
484
- export function createHeader(tag, innerHTML) {
485
- const header = document.createElement(tag);
486
- header.innerHTML = innerHTML;
487
- return header;
488
- }
489
-
490
- /**
491
- * Create copy to clipboard button
492
- * @private
493
- */
494
- export function createButtonSpan(innerHTML, dataCopy = null) {
495
- const button = document.createElement("button");
496
- button.innerHTML = innerHTML;
497
- if (dataCopy) button.setAttribute("data-copy", dataCopy);
498
-
499
- const span = document.createElement("span");
500
- span.className = "gvs-input-btn";
501
- span.appendChild(button);
502
-
503
- return span;
504
- }
505
-
506
- /**
507
- * Create an input label
508
- * @private
509
- */
510
- export function createLabel(forAttr, text, faIcon = null) {
511
- const label = document.createElement("label");
512
- label.htmlFor = forAttr;
513
- if(faIcon) { label.appendChild(fa(faIcon)); }
514
- label.appendChild(document.createTextNode(text));
515
- return label;
516
- }
517
-
518
- /**
519
- * Show a grade in a nice, user-friendly way
520
- * @param {number} grade The obtained grade
521
- * @returns {string} Nice to display grade display
522
- * @private
523
- */
524
- export function showGrade(grade, t) {
525
- let label = "<span class=\"gvs-grade\">";
526
-
527
- for(let i=1; i <= grade; i++) {
528
- label += fat(faStar);
529
- }
530
- for(let i=grade+1; i <= 5; i++) {
531
- label += fat(farStar);
532
- }
533
-
534
- label += "</span> (";
535
- if(grade === null) { label += t.gvs.metadata_quality_missing+")"; }
536
- else { label += grade + "/5)"; }
537
- return label;
538
- }
539
-
540
- /**
541
- * Displays a nice QualityScore
542
- * @param {number} grade The 1 to 5 grade
543
- * @returns {Element} The HTML code for showing the grade
544
- * @private
545
- */
546
- export function showQualityScore(grade) {
547
- const span = document.createElement("span");
548
-
549
- for(let i=1; i <= QUALITYSCORE_VALUES.length; i++) {
550
- const pv = QUALITYSCORE_VALUES[i-1];
551
- const sub = document.createElement("span");
552
- sub.appendChild(document.createTextNode(pv.label));
553
- sub.classList.add("gvs-qualityscore");
554
- sub.style.backgroundColor = pv.color;
555
- if(i === (6-grade)) {
556
- sub.classList.add("gvs-qualityscore-selected");
557
- }
558
- span.appendChild(sub);
559
- }
560
-
561
- return span;
562
- }