@kalisio/kdk 2.1.9 → 2.2.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 (243) hide show
  1. package/.travis.test.sh +42 -10
  2. package/README.md +2 -2
  3. package/core/api/application.js +6 -1
  4. package/core/api/authentication.js +17 -1
  5. package/core/api/db.js +7 -2
  6. package/core/api/hooks/hooks.authentication.js +4 -2
  7. package/core/api/hooks/hooks.authorisations.js +12 -2
  8. package/core/api/hooks/hooks.model.js +5 -5
  9. package/core/api/hooks/hooks.organisations.js +0 -4
  10. package/core/api/services/account/account.hooks.js +10 -6
  11. package/core/api/services/account/account.service.js +1 -1
  12. package/{map/api/services/geocoder/geocoder.hooks.js → core/api/services/import-export/import-export.hooks.js} +7 -5
  13. package/core/api/services/import-export/import-export.service.js +11 -0
  14. package/core/api/services/index.js +13 -1
  15. package/core/api/services/users/users.hooks.js +2 -3
  16. package/core/client/api.js +16 -14
  17. package/core/client/capabilities.js +6 -2
  18. package/core/client/components/KContent.vue +11 -1
  19. package/core/client/components/KDialog.vue +17 -15
  20. package/core/client/components/KSponsor.vue +1 -1
  21. package/core/client/components/KTextArea.vue +5 -1
  22. package/core/client/components/app/KAbout.vue +1 -2
  23. package/core/client/components/app/KWelcome.vue +3 -5
  24. package/core/client/components/chart/KTimeSeriesChart.vue +24 -37
  25. package/core/client/components/collection/KColumn.vue +20 -17
  26. package/core/client/components/editor/KModalEditor.vue +0 -2
  27. package/core/client/components/form/KChipsField.vue +12 -2
  28. package/core/client/components/form/KColorField.vue +12 -2
  29. package/core/client/components/form/KColorScaleField.vue +12 -2
  30. package/core/client/components/form/KDateTimeRangeField.vue +12 -2
  31. package/core/client/components/form/KDatetimeField.vue +12 -2
  32. package/core/client/components/form/KEmailField.vue +12 -2
  33. package/core/client/components/form/KFileField.vue +12 -2
  34. package/core/client/components/form/KForm.vue +43 -9
  35. package/core/client/components/form/KIconField.vue +12 -2
  36. package/core/client/components/form/KItemField.vue +25 -4
  37. package/core/client/components/form/KNumberField.vue +12 -2
  38. package/core/client/components/form/KOptionsField.vue +12 -2
  39. package/core/client/components/form/KPasswordField.vue +12 -2
  40. package/core/client/components/form/KPhoneField.vue +13 -3
  41. package/core/client/components/form/KPropertyItemField.vue +12 -2
  42. package/core/client/components/form/KResolutionField.vue +126 -0
  43. package/core/client/components/form/KRoleField.vue +12 -2
  44. package/core/client/components/form/KSelectField.vue +14 -4
  45. package/core/client/components/form/KTextField.vue +12 -2
  46. package/core/client/components/form/KTextareaField.vue +13 -3
  47. package/core/client/components/form/KToggleField.vue +12 -2
  48. package/core/client/components/form/KTokenField.vue +12 -2
  49. package/core/client/components/form/KUnitField.vue +12 -2
  50. package/core/client/components/form/KUrlField.vue +12 -2
  51. package/core/client/components/input/KIconChooser.vue +10 -12
  52. package/core/client/components/input/KPalette.vue +2 -1
  53. package/core/client/components/layout/KPage.vue +5 -4
  54. package/core/client/components/layout/KWindow.vue +10 -10
  55. package/core/client/components/media/KColorScale.vue +26 -20
  56. package/core/client/components/media/KImageViewer.vue +57 -33
  57. package/core/client/components/media/KShape.vue +14 -103
  58. package/core/client/components/screen/KRegisterScreen.vue +0 -1
  59. package/core/client/components/screen/KScreenFooter.vue +0 -18
  60. package/core/client/components/team/KAddMember.vue +16 -22
  61. package/core/client/components/team/KGroupsActivity.vue +14 -0
  62. package/core/client/components/team/KMembersActivity.vue +12 -0
  63. package/core/client/components/team/KTagsActivity.vue +14 -0
  64. package/core/client/components/time/KDateTime.vue +23 -7
  65. package/core/client/components/time/KTimeControl.vue +142 -0
  66. package/core/client/components/tool/KExportTool.vue +57 -0
  67. package/core/client/composables/collection.js +0 -1
  68. package/core/client/composables/pwa.js +0 -1
  69. package/core/client/composables/schema.js +1 -1
  70. package/core/client/composables/session.js +30 -6
  71. package/core/client/exporter.js +141 -0
  72. package/core/client/i18n/core_en.json +91 -23
  73. package/core/client/i18n/core_fr.json +92 -23
  74. package/core/client/index.js +3 -0
  75. package/core/client/layout.js +34 -14
  76. package/core/client/local-storage.js +8 -6
  77. package/core/client/mixins/index.js +0 -1
  78. package/core/client/mixins/mixin.base-field.js +24 -2
  79. package/core/client/mixins/mixin.object-proxy.js +0 -1
  80. package/core/client/search.js +2 -1
  81. package/core/client/services/index.js +2 -1
  82. package/core/client/services/local-settings.service.js +4 -4
  83. package/core/client/theme.js +3 -3
  84. package/core/client/time.js +4 -0
  85. package/core/client/units.js +149 -4
  86. package/core/client/utils/index.js +13 -6
  87. package/core/client/utils/utils.account.js +1 -1
  88. package/core/client/utils/utils.colors.js +43 -0
  89. package/core/client/utils/utils.platform.js +0 -1
  90. package/core/client/utils/utils.pwa.js +14 -14
  91. package/core/client/utils/utils.session.js +1 -1
  92. package/core/client/utils/utils.shapes.js +270 -0
  93. package/core/client/utils/utils.time.js +37 -0
  94. package/core/common/permissions.js +3 -0
  95. package/core/common/schemas/settings.update.json +50 -29
  96. package/extras/css/core.variables.scss +3 -1
  97. package/extras/tours/map/navigation-bar.js +17 -15
  98. package/extras/tours/map/timeline.js +33 -33
  99. package/map/api/config/categories.cjs +4 -1
  100. package/map/api/hooks/hooks.catalog.js +39 -0
  101. package/map/api/hooks/hooks.features.js +23 -3
  102. package/map/api/hooks/hooks.query.js +31 -10
  103. package/map/api/models/projects.model.mongodb.js +8 -0
  104. package/map/api/services/catalog/catalog.hooks.js +5 -3
  105. package/map/api/services/features/features.hooks.js +18 -6
  106. package/map/api/services/index.js +22 -6
  107. package/map/api/services/projects/projects.hooks.js +118 -0
  108. package/map/client/capture.js +16 -0
  109. package/map/client/cesium/utils/index.js +3 -0
  110. package/map/client/cesium/utils/utils.events.js +30 -0
  111. package/map/client/cesium/utils/utils.popup.js +17 -0
  112. package/map/client/cesium/{utils.js → utils/utils.style.js} +53 -49
  113. package/map/client/components/KCapture.vue +50 -0
  114. package/map/client/components/KCaptureTextArea.vue +53 -0
  115. package/map/client/components/KCompass.vue +2 -2
  116. package/map/client/components/KFeaturesChart.vue +1 -1
  117. package/map/client/components/KFeaturesFilter.vue +2 -2
  118. package/map/client/components/KLayerStyleForm.vue +256 -430
  119. package/map/client/components/KLevelSlider.vue +1 -1
  120. package/map/client/components/KNorth.vue +31 -0
  121. package/map/client/components/KProjectMenu.vue +88 -0
  122. package/map/client/components/KTimezoneMap.vue +36 -23
  123. package/map/client/components/catalog/KAddLayer.vue +3 -4
  124. package/map/client/components/catalog/KConnectLayer.vue +16 -4
  125. package/map/client/components/catalog/KCreateLayer.vue +1 -2
  126. package/map/client/components/catalog/KCreateProject.vue +100 -0
  127. package/map/client/components/catalog/KCreateView.vue +25 -2
  128. package/map/client/components/catalog/KLayersPanel.vue +24 -27
  129. package/map/client/components/catalog/KLayersSelector.vue +1 -1
  130. package/map/client/components/catalog/KProjectEditor.vue +91 -0
  131. package/map/client/components/catalog/KProjectManager.vue +60 -0
  132. package/map/client/components/catalog/KProjectSelector.vue +38 -0
  133. package/map/client/components/catalog/KProjectsPanel.vue +153 -0
  134. package/map/client/components/catalog/KSelectLayers.vue +96 -0
  135. package/map/client/components/catalog/KSelectViews.vue +96 -0
  136. package/map/client/components/catalog/KViewsPanel.vue +66 -30
  137. package/map/client/components/form/KDirectionField.vue +24 -5
  138. package/map/client/components/form/KLayerCategoryField.vue +12 -2
  139. package/map/client/components/form/KLocationField.vue +20 -5
  140. package/map/client/components/form/KOwsLayerField.vue +12 -2
  141. package/map/client/components/form/KOwsServiceField.vue +12 -2
  142. package/map/client/components/form/KSelectLayersField.vue +159 -0
  143. package/map/client/components/form/KSelectViewsField.vue +121 -0
  144. package/map/client/components/form/KTimezoneField.vue +24 -17
  145. package/map/client/components/legend/KColorScaleLegend.vue +1 -1
  146. package/map/client/components/legend/KLayerLegend.vue +61 -0
  147. package/map/client/components/legend/KLegend.vue +45 -44
  148. package/map/client/components/legend/KLegendRenderer.vue +5 -3
  149. package/map/client/components/legend/KSymbolsLegend.vue +12 -10
  150. package/map/client/components/legend/KVariablesLegend.vue +78 -0
  151. package/map/client/components/location/KGeocodersFilter.vue +2 -4
  152. package/map/client/components/location/KLocationMap.vue +48 -17
  153. package/map/client/components/location/KLocationSearch.vue +13 -3
  154. package/map/client/components/tools/KSearchTool.vue +17 -12
  155. package/map/client/components/widget/KElevationProfile.vue +16 -19
  156. package/map/client/components/widget/KMapillaryViewer.vue +21 -22
  157. package/map/client/components/widget/KTimeSeries.vue +35 -23
  158. package/map/client/composables/activity.js +15 -2
  159. package/map/client/composables/catalog.js +66 -0
  160. package/map/client/composables/highlight.js +56 -20
  161. package/map/client/composables/index.js +2 -0
  162. package/map/client/composables/location.js +25 -18
  163. package/map/client/composables/project.js +122 -0
  164. package/map/client/geolocation.js +1 -1
  165. package/map/client/globe.js +2 -0
  166. package/map/client/i18n/map_en.json +123 -76
  167. package/map/client/i18n/map_fr.json +124 -72
  168. package/map/client/index.js +3 -0
  169. package/map/client/init.js +17 -0
  170. package/map/client/leaflet/GSMaPLayer.js +16 -17
  171. package/map/client/leaflet/ShapeMarker.js +40 -0
  172. package/map/client/leaflet/TiledFeatureLayer.js +1 -1
  173. package/map/client/leaflet/TiledMeshLayer.js +11 -15
  174. package/map/client/leaflet/TiledWindLayer.js +6 -10
  175. package/map/client/leaflet/utils/index.js +4 -0
  176. package/map/client/leaflet/utils/utils.events.js +41 -0
  177. package/map/client/leaflet/utils/utils.popup.js +21 -0
  178. package/map/client/leaflet/utils/utils.style.js +191 -0
  179. package/map/client/leaflet/utils/utils.tiles.js +87 -0
  180. package/map/client/map.js +2 -0
  181. package/map/client/mixins/globe/mixin.base-globe.js +29 -21
  182. package/map/client/mixins/globe/mixin.geojson-layers.js +132 -69
  183. package/map/client/mixins/globe/mixin.popup.js +2 -1
  184. package/map/client/mixins/globe/mixin.style.js +6 -4
  185. package/map/client/mixins/globe/mixin.tooltip.js +8 -3
  186. package/map/client/mixins/map/mixin.base-map.js +13 -11
  187. package/map/client/mixins/map/mixin.edit-layers.js +15 -15
  188. package/map/client/mixins/map/mixin.forecast-layers.js +3 -1
  189. package/map/client/mixins/map/mixin.geojson-layers.js +56 -20
  190. package/map/client/mixins/map/mixin.georaster-layers.js +4 -11
  191. package/map/client/mixins/map/mixin.heatmap-layers.js +1 -1
  192. package/map/client/mixins/map/mixin.popup.js +2 -1
  193. package/map/client/mixins/map/mixin.style.js +4 -67
  194. package/map/client/mixins/map/mixin.tiled-mesh-layers.js +2 -1
  195. package/map/client/mixins/map/mixin.tiled-wind-layers.js +4 -2
  196. package/map/client/mixins/map/mixin.tooltip.js +2 -1
  197. package/map/client/mixins/mixin.activity.js +66 -191
  198. package/map/client/mixins/mixin.catalog-panel.js +6 -6
  199. package/map/client/mixins/mixin.context.js +12 -9
  200. package/map/client/mixins/mixin.feature-service.js +29 -300
  201. package/map/client/mixins/mixin.weacast.js +11 -17
  202. package/map/client/pixi-utils.js +1 -1
  203. package/map/client/planets.js +58 -0
  204. package/map/client/utils/index.js +6 -0
  205. package/map/client/utils/utils.capture.js +176 -0
  206. package/map/client/utils/utils.catalog.js +149 -0
  207. package/map/client/utils/utils.features.js +364 -0
  208. package/map/client/utils/utils.js +0 -151
  209. package/map/client/utils/utils.layers.js +174 -0
  210. package/map/client/utils/utils.location.js +91 -23
  211. package/map/client/utils/utils.project.js +8 -0
  212. package/map/client/utils/utils.schema.js +0 -1
  213. package/map/client/utils/utils.style.js +297 -0
  214. package/map/client/utils.all.js +2 -2
  215. package/map/client/utils.globe.js +1 -1
  216. package/map/client/utils.map.js +1 -1
  217. package/map/common/permissions.js +2 -0
  218. package/map/common/schemas/capture.create.json +132 -0
  219. package/map/common/schemas/projects.create.json +52 -0
  220. package/map/common/schemas/projects.update.json +52 -0
  221. package/package.json +6 -5
  222. package/test/api/core/account.test.js +20 -0
  223. package/test/api/core/config/default.cjs +16 -3
  224. package/test/api/core/import-export.test.js +86 -0
  225. package/test/api/core/test-log-2024-01-04.log +14 -0
  226. package/test/api/map/catalog.test.js +164 -0
  227. package/test/api/map/index.test.js +25 -61
  228. package/test/api/map/test-log-2024-01-04.log +2 -0
  229. package/test/api/map/test-log-2024-01-11.log +1 -0
  230. package/test/api/map/test-log-2024-01-25.log +19 -0
  231. package/test/client/core/layout.js +25 -5
  232. package/test/client/core/utils.js +7 -0
  233. package/test/client/map/catalog.js +78 -1
  234. package/test/client/map/time.js +2 -1
  235. package/core/client/components/screen/KEndpointScreen.vue +0 -80
  236. package/core/client/mixins/mixin.account.js +0 -61
  237. package/extras/icons/kdk.png +0 -0
  238. package/map/api/services/geocoder/geocoder.service.js +0 -79
  239. package/map/client/components/KCaptureToolbar.vue +0 -155
  240. package/map/client/components/KColorLegend.vue +0 -349
  241. package/map/client/components/KTimeline.vue +0 -293
  242. package/map/client/components/KUrlLegend.vue +0 -122
  243. package/map/client/leaflet/utils.js +0 -246
@@ -1,4 +1,5 @@
1
1
  import _ from 'lodash'
2
+ import logger from 'loglevel'
2
3
  import * as math from 'mathjs'
3
4
  import config from 'config'
4
5
  import { i18n } from './i18n.js'
@@ -93,6 +94,106 @@ const angle = {
93
94
  label: 'units.RADIAN_LABEL'
94
95
  }
95
96
  }
97
+ const fraction = {
98
+ ppm: {
99
+ symbol: 'units.PPM_SYMBOL',
100
+ label: 'units.PPM_LABEL'
101
+ }
102
+ }
103
+ const density = {
104
+ 'ug/m^3': {
105
+ symbol: 'units.MICROGRAM_PER_M3_SYMBOL',
106
+ label: 'units.MICROGRAM_PER_M3_LABEL'
107
+ }
108
+ }
109
+ const volumeVelocity = {
110
+ 'm^3/s': {
111
+ symbol: 'units.CUBIC_METER_PER_SECOND_SYMBOL',
112
+ label: 'units.CUBIC_METER_PER_SECOND_LABEL'
113
+ }
114
+ }
115
+ const radioactivity = {
116
+ bq: {
117
+ symbol: 'units.BEQUEREL_SYMBOL',
118
+ label: 'units.BEQUEREL_LABEL',
119
+ baseName: 'radioactivity'
120
+ }
121
+ }
122
+ const radioactivityDensity = {
123
+ 'bq/m^2': {
124
+ symbol: 'units.BEQUEREL_PER_M2_SYMBOL',
125
+ label: 'units.BEQUEREL_PER_M2_LABEL'
126
+ },
127
+ 'bq/m^3': {
128
+ symbol: 'units.BEQUEREL_PER_M3_SYMBOL',
129
+ label: 'units.BEQUEREL_PER_M3_LABEL'
130
+ }
131
+ }
132
+ const equivalentDose = {
133
+ sv: {
134
+ symbol: 'units.SIEVERT_SYMBOL',
135
+ label: 'units.SIEVERT_LABEL',
136
+ baseName: 'equivalentDose',
137
+ aliases: ['sievert']
138
+ },
139
+ msv: {
140
+ symbol: 'units.MILLISIEVERT_SYMBOL',
141
+ label: 'units.MILLISIEVERT_LABEL',
142
+ definition: '0.001 sv'
143
+ },
144
+ usv: {
145
+ symbol: 'units.MICROSIEVERT_SYMBOL',
146
+ label: 'units.MICROSIEVERT_LABEL',
147
+ definition: '0.000001 sv'
148
+ },
149
+ nsv: {
150
+ symbol: 'units.NANOSIEVERT_SYMBOL',
151
+ label: 'units.NANOSIEVERT_LABEL',
152
+ definition: '0.000000001 sv'
153
+ }
154
+ }
155
+ const equivalentDoseRate = {
156
+ svs: {
157
+ symbol: 'units.SIEVERT_PER_SECOND_SYMBOL',
158
+ label: 'units.SIEVERT_PER_SECOND_LABEL',
159
+ baseName: 'equivalentDoseRate'
160
+ },
161
+ msvs: {
162
+ symbol: 'units.MILLISIEVERT_PER_SECOND_SYMBOL',
163
+ label: 'units.MILLISIEVERT_PER_SECOND_LABEL',
164
+ definition: '0.001 svs'
165
+ },
166
+ usvs: {
167
+ symbol: 'units.MICROSIEVERT_PER_SECOND_SYMBOL',
168
+ label: 'units.MICROSIEVERT_PER_SECOND_LABEL',
169
+ definition: '0.000001 svs'
170
+ },
171
+ nsvs: {
172
+ symbol: 'units.NANOSIEVERT_PER_SECOND_SYMBOL',
173
+ label: 'units.NANOSIEVERT_PER_SECOND_LABEL',
174
+ definition: '0.000000001 svs'
175
+ },
176
+ svh: {
177
+ symbol: 'units.SIEVERT_PER_HOUR_SYMBOL',
178
+ label: 'units.SIEVERT_PER_HOUR_LABEL',
179
+ definition: '0.000277778 svs'
180
+ },
181
+ msvh: {
182
+ symbol: 'units.MILLISIEVERT_PER_HOUR_SYMBOL',
183
+ label: 'units.MILLISIEVERT_PER_HOUR_LABEL',
184
+ definition: '0.000000277778 svs'
185
+ },
186
+ usvh: {
187
+ symbol: 'units.MICROSIEVERT_PER_HOUR_SYMBOL',
188
+ label: 'units.MICROSIEVERT_PER_HOUR_LABEL',
189
+ definition: '0.000000000277778 svs'
190
+ },
191
+ nsvh: {
192
+ symbol: 'units.NANOSIEVERT_PER_HOUR_SYMBOL',
193
+ label: 'units.NANOSIEVERT_PER_HOUR_LABEL',
194
+ definition: '0.000000000000277778 svs'
195
+ }
196
+ }
96
197
 
97
198
  const quantities = {
98
199
  length,
@@ -100,7 +201,14 @@ const quantities = {
100
201
  area,
101
202
  velocity,
102
203
  temperature,
103
- angle
204
+ angle,
205
+ fraction,
206
+ density,
207
+ volumeVelocity,
208
+ radioactivity,
209
+ radioactivityDensity,
210
+ equivalentDose,
211
+ equivalentDoseRate
104
212
  }
105
213
 
106
214
  // Export singleton
@@ -116,6 +224,9 @@ export const Units = {
116
224
  velocity: 'm/s',
117
225
  temperature: 'degC',
118
226
  angle: 'deg',
227
+ radioactivity: 'bq',
228
+ equivalentDose: 'usv',
229
+ equivalentDoseRate: 'usvh',
119
230
  notation: 'auto',
120
231
  precision: 3
121
232
  }
@@ -171,33 +282,67 @@ export const Units = {
171
282
  return units
172
283
  }
173
284
  },
285
+ // Get unit definition by name
174
286
  getUnit (unit) {
175
287
  return _.find(this.getUnits(), { name: unit })
176
288
  },
289
+ // Get unit symbol by unit name
177
290
  getUnitSymbol (unit) {
178
291
  const definition = this.getUnit(unit)
179
292
  return (definition ? i18n.t(definition.symbol) : unit)
180
293
  },
294
+ // Get default unit definition (if any) for a given quantity/unit name
181
295
  getDefaultUnit (quantityOrUnit) {
182
296
  // Check for quantity first
183
297
  let defaultUnit = Store.get(`units.default.${quantityOrUnit}`)
184
298
  // If not check by matching quantity based on given unit
185
299
  if (!defaultUnit) {
186
- const baseUnit = Units.getUnit(quantityOrUnit)
300
+ const baseUnit = this.getUnit(quantityOrUnit)
187
301
  // Get default unit for this quantity instead
188
- if (baseUnit) defaultUnit = Units.getDefaultUnit(baseUnit.quantity)
302
+ if (baseUnit) defaultUnit = this.getDefaultUnit(baseUnit.quantity)
189
303
  }
190
304
  return defaultUnit
191
305
  },
306
+ // Get symbol of default unit (if any) for a given quantity/unit name
307
+ getDefaultUnitSymbol (quantityOrUnit) {
308
+ return this.getUnitSymbol(this.getDefaultUnit(quantityOrUnit))
309
+ },
310
+ // Get target unit for a source unit, will be default unit (if any) or source unit
311
+ getTargetUnit (sourceUnit) {
312
+ return this.getDefaultUnit(sourceUnit) || sourceUnit
313
+ },
314
+ // Get target unit symbol for a source unit, will be default unit symbol (if any) or source unit symbol
315
+ getTargetUnitSymbol (sourceUnit) {
316
+ return this.getUnitSymbol(this.getTargetUnit(sourceUnit))
317
+ },
318
+ // Convert between units by names
319
+ // If target unit is not specified will use default unit (if any) for source unit
192
320
  convert (value, sourceUnit, targetUnit) {
193
- if (sourceUnit === targetUnit) return value
321
+ if (_.isNil(value)) {
322
+ logger.warn(`[KDK] cannont convert an nil value`)
323
+ return
324
+ }
325
+ if (value === Number.MIN_VALUE || value === Number.MAX_VALUE) return value
326
+ // If target unit is not given use default one
327
+ if (!targetUnit) targetUnit = this.getDefaultUnit(sourceUnit)
328
+ // Check if the target unit does exist
329
+ if (!targetUnit) return value
330
+ // Check if the source unit does exist
331
+ if (!math.Unit.isValuelessUnit(sourceUnit)) return value
194
332
  let n = math.unit(value, sourceUnit)
195
333
  n = n.toNumber(targetUnit)
196
334
  // Remap from [-180,+180[ to [0,360[ for angles
197
335
  n = (targetUnit === 'deg' ? (n < 0.0 ? n + 360.0 : n) : n)
198
336
  return n
199
337
  },
338
+ // Format display of source value in target unit, converting from source unit
339
+ // If target unit is not specified will use default unit (if any) for source unit
340
+ // options are mathjs format options
200
341
  format (value, sourceUnit, targetUnit, options) {
342
+ if (_.isNil(value)) {
343
+ logger.warn(`[KDK] cannot format a nil value`)
344
+ return
345
+ }
201
346
  // If target unit is not given use default one
202
347
  if (!targetUnit) targetUnit = this.getDefaultUnit(sourceUnit)
203
348
  const n = (targetUnit ? this.convert(value, sourceUnit, targetUnit) : value)
@@ -12,7 +12,9 @@ export * from './utils.locale.js'
12
12
  export * from './utils.platform.js'
13
13
  export * from './utils.push.js'
14
14
  export * from './utils.pwa.js'
15
+ export * from './utils.shapes.js'
15
16
  export * from './utils.session.js'
17
+ export * from './utils.time.js'
16
18
 
17
19
  Notify.setDefaults({
18
20
  position: 'bottom-left',
@@ -107,6 +109,17 @@ export function dataUriToBlob (dataUri) {
107
109
  return new Blob([ab], { type: mimeType })
108
110
  }
109
111
 
112
+ // Taken from https://github.com/juanelas/base64
113
+ export const base64Encode = function (bytes) {
114
+ bytes = new Uint8Array(bytes)
115
+ const CHUNK_SIZE = 0x8000
116
+ const array = []
117
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
118
+ array.push(String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK_SIZE)))
119
+ }
120
+ return btoa(array.join(''))
121
+ }
122
+
110
123
  export function downloadAsBlob (data, filename, mimeType) {
111
124
  const blob = new Blob([data], { type: mimeType })
112
125
  exportFile(filename, blob)
@@ -174,12 +187,6 @@ export function isObjectID (id) {
174
187
  return (id.length === 24 && checkForHexRegExp.test(id))
175
188
  }
176
189
 
177
- // Add UTC offset to timezone name
178
- export function getTimezoneLabel (timezone) {
179
- const offset = moment().tz(timezone).format('Z')
180
- return `${timezone} (${offset})`
181
- }
182
-
183
190
  // Helper function to load a schema
184
191
  // @schema alias shoud be added in the quasar.config build section
185
192
  export async function loadSchema (schemaName) {
@@ -2,7 +2,7 @@ import { api } from '../api.js'
2
2
 
3
3
  export async function verifyEmail (email) {
4
4
  const response = await api.getService('account').verifyEmail({ email })
5
- return response.status === 200 ? true : false
5
+ return response.status === 200
6
6
  }
7
7
 
8
8
  export function resendVerifySignup (email) {
@@ -1,6 +1,10 @@
1
1
  import _ from 'lodash'
2
+ import logger from 'loglevel'
3
+ import { getCssVar } from 'quasar'
4
+ import chroma from 'chroma-js'
2
5
 
3
6
  export const Colors = {
7
+ white: '#fff',
4
8
  dark: '#333',
5
9
  red: '#f44336',
6
10
  pink: '#e91e63',
@@ -23,6 +27,11 @@ export const Colors = {
23
27
  'blue-grey': '#607d8b'
24
28
  }
25
29
 
30
+ export function getHtmlColor (color, defaultColor) {
31
+ if (!color) return defaultColor
32
+ return getCssVar(color) || color
33
+ }
34
+
26
35
  export function getPaletteFromColor (color) {
27
36
  // Check if already a color of the palette
28
37
  if (Colors[color]) return color
@@ -34,3 +43,37 @@ export function getColorFromPalette (color) {
34
43
  if (color.startsWith('#')) return color
35
44
  else return Colors[color] || '#ffffff'
36
45
  }
46
+
47
+ export function buildColorScale (options) {
48
+ if (!options) {
49
+ logger.warn(`[KDK] buildColorScale: 'options' argument must be defined`)
50
+ return
51
+ }
52
+ let colors = options.colors
53
+ if (!colors) {
54
+ // For backward compatibility
55
+ if (options.scale) {
56
+ logger.warn(`[KDK] buildColorScale: please update 'scale' property to 'colors'`)
57
+ colors = options.scale
58
+ } else {
59
+ logger.warn(`[KDK] buildColorScale: no colors defined, using default default colors 'Spectral'`)
60
+ colors = 'Spectral'
61
+ }
62
+ }
63
+ let scale = chroma.scale(colors)
64
+ if (options.classes) {
65
+ if (Array.isArray(options.classes)) {
66
+ // The provided classes implicitly define the domain, then we omit the domain
67
+ scale = scale.classes(options.classes)
68
+ }
69
+ else {
70
+ // In this case, we need to take into account an optional domain
71
+ if (options.domain) scale = scale.domain(options.domain).classes(options.classes)
72
+ else scale = scale.classes(options.classes)
73
+ }
74
+ } else {
75
+ // No classes defined, we need to take into account an optional domain
76
+ if (options.domain) scale = scale.domain(options.domain)
77
+ }
78
+ return scale
79
+ }
@@ -8,4 +8,3 @@ export function getPlatform () {
8
8
  agent: Platform.userAgent
9
9
  })
10
10
  }
11
-
@@ -1,4 +1,4 @@
1
- import { useQuasar } from 'quasar'
1
+ import { Dialog } from 'quasar'
2
2
  import { i18n } from '../i18n.js'
3
3
 
4
4
  export let InstallPwaPrompt = null
@@ -6,22 +6,22 @@ export let InstallPwaPrompt = null
6
6
  window.addEventListener('beforeinstallprompt', (e) => {
7
7
  e.preventDefault()
8
8
  // Stash the event so it can be triggered later
9
- InstallPwaPrompt = e
9
+ InstallPwaPrompt = e
10
10
  })
11
11
 
12
12
  export function installDefaultPrompt () {
13
- useQuasar().dialog({
14
- title: i18n.t('composables.pwa.INSTALL_TITLE'),
15
- message: i18n.t('composables.pwa.INSTALL_MESSAGE'),
13
+ Dialog.create({
14
+ title: i18n.t('utils.pwa.INSTALL_TITLE'),
15
+ message: i18n.t('utils.pwa.INSTALL_MESSAGE'),
16
16
  cancel: {
17
17
  id: 'ignore-button',
18
- label: i18n.t('composables.pwa.IGNORE'),
18
+ label: i18n.t('utils.pwa.IGNORE'),
19
19
  color: 'primary',
20
20
  outline: true
21
21
  },
22
22
  ok: {
23
23
  id: 'install-button',
24
- label: i18n.t('composables.pwa.INSTALL'),
24
+ label: i18n.t('utils.pwa.INSTALL'),
25
25
  color: 'primary'
26
26
  },
27
27
  persistent: true,
@@ -37,9 +37,9 @@ export function installDefaultPrompt () {
37
37
  }
38
38
 
39
39
  export function installSafariPrompt () {
40
- useQuasar().dialog({
41
- title: i18n.t('composables.pwa.INSTALL_TITLE'),
42
- message: i18n.t('composables.pwa.IOS_INSTALL_MESSAGE'),
40
+ Dialog.create({
41
+ title: i18n.t('utils.pwa.INSTALL_TITLE'),
42
+ message: i18n.t('utils.pwa.IOS_INSTALL_MESSAGE'),
43
43
  ok: {
44
44
  color: 'primary'
45
45
  },
@@ -50,9 +50,9 @@ export function installSafariPrompt () {
50
50
  }
51
51
 
52
52
  export function installFFDesktopPrompt () {
53
- useQuasar().dialog({
54
- title: i18n.t('composables.pwa.INSTALL_TITLE'),
55
- message: i18n.t('composables.pwa.FIREFOX_DESKTOP_INSTALL_MESSAGE'),
53
+ Dialog.create({
54
+ title: i18n.t('utils.pwa.INSTALL_TITLE'),
55
+ message: i18n.t('utils.pwa.FIREFOX_DESKTOP_INSTALL_MESSAGE'),
56
56
  ok: {
57
57
  color: 'primary'
58
58
  },
@@ -60,4 +60,4 @@ export function installFFDesktopPrompt () {
60
60
  position: 'bottom',
61
61
  html: true
62
62
  })
63
- }
63
+ }
@@ -1,3 +1,4 @@
1
+ import _ from 'lodash'
1
2
  import logger from 'debug'
2
3
  import { Store } from '../store.js'
3
4
  import { api } from '../api.js'
@@ -34,7 +35,6 @@ export async function logout () {
34
35
  // Rethrow for caller to handle
35
36
  throw error
36
37
  }
37
-
38
38
  }
39
39
 
40
40
  export async function restoreSession () {
@@ -0,0 +1,270 @@
1
+ import _ from 'lodash'
2
+ import logger from 'loglevel'
3
+ import { uid } from 'quasar'
4
+ import { getHtmlColor } from './utils.colors.js'
5
+
6
+ const defaultSize = { width: 24, height: 24 }
7
+ const defaultColor = 'black'
8
+ const defaultIconSize = 12
9
+ const defaultTextSize = 12
10
+
11
+ function defaultRadiusToSize (r) {
12
+ return { width: r * 2, height: r * 2 }
13
+ }
14
+
15
+ // Predefined shapes
16
+ // see https://www.svgrepo.com/ to look for a shape
17
+ export const Shapes = {
18
+ circle: {
19
+ viewBox: [0, 0, 100, 100],
20
+ content: '<circle cx="50" cy="50" r="50" />'
21
+ },
22
+ rect: {
23
+ viewBox: [0, 0, 100, 100],
24
+ content: '<rect cx="0" cy="0" width="100" height="100" />',
25
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 0.9), height: Math.round(r * 2 * 0.9) } }
26
+ },
27
+ 'rounded-rect': {
28
+ viewBox: [0, 0, 100, 100],
29
+ content: '<rect cx="0" cy="0" width="100" height="100" rx="20" ry="20" />',
30
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 0.9), height: Math.round(r * 2 * 0.9) } }
31
+ },
32
+ diamond: {
33
+ viewBox: [0, 0, 100, 100],
34
+ content: '<polygon points="50 0, 100 50, 50 100, 0 50" />',
35
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 1.2), height: Math.round(r * 2 * 1.2) } }
36
+ },
37
+ triangle: {
38
+ viewBox: [0, 0, 100, 100],
39
+ content: '<polygon points="50 0, 100 100, 0 100" />',
40
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 1.125), height: Math.round(r * 2 * 1.025) } }
41
+ },
42
+ 'triangle-down': {
43
+ viewBox: [0, 0, 100, 100],
44
+ content: '<polygon points="0 0, 100 0, 50 100" />',
45
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 1.125), height: Math.round(r * 2 * 1.025) } }
46
+ },
47
+ 'triangle-left': {
48
+ viewBox: [0, 0, 100, 100],
49
+ content: '<polygon points="0 50, 100 0, 100 100" />',
50
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 1.025), height: Math.round(r * 2 * 1.125) } }
51
+ },
52
+ 'triangle-right': {
53
+ viewBox: [0, 0, 100, 100],
54
+ content: '<polygon points="0 0, 100 50, 0 100" />',
55
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 1.025), height: Math.round(r * 2 * 1.125) } }
56
+ },
57
+ star: {
58
+ viewBox: [0, 0, 48, 48],
59
+ content: '<path d="m24,1 6,17h18l-14,11 5,17-15-10-15,10 5-17-14-11h18z" />',
60
+ radiusToSize: (r) => { return { width: Math.round(r * 2 * 1.4), height: Math.round(r * 2 * 1.4) } }
61
+ },
62
+ 'marker-pin': {
63
+ viewBox: [0, 0, 384, 512],
64
+ content: '<path d="M384 192c0 87.4-117 243-168.3 307.2c-12.3 15.3-35.1 15.3-47.4 0C117 435 0 279.4 0 192C0 86 86 0 192 0s192 86 192 192z" />',
65
+ icon: {
66
+ translation: ['-50%', '-70%']
67
+ },
68
+ text: {
69
+ translation: ['-50%', '-70%']
70
+ },
71
+ anchor: 'bottom-center'
72
+ },
73
+ 'square-pin': {
74
+ viewBox: [0, 0, 56, 56],
75
+ content: '<path d="M 27.9532 52.3633 C 29.0079 52.3633 29.9923 51.9180 30.9298 50.3008 L 35.2657 43.0586 L 43.0938 43.0586 C 50.0783 43.0586 53.8280 39.1914 53.8280 32.3242 L 53.8280 14.3711 C 53.8280 7.5039 50.0783 3.6367 43.0938 3.6367 L 12.9064 3.6367 C 5.9454 3.6367 2.1720 7.4805 2.1720 14.3711 L 2.1720 32.3242 C 2.1720 39.2148 5.9454 43.0586 12.9064 43.0586 L 20.6407 43.0586 L 24.9766 50.3008 C 25.9142 51.9180 26.8985 52.3633 27.9532 52.3633 Z"/>',
76
+ icon: {
77
+ translation: ['-50%', '-75%']
78
+ },
79
+ text: {
80
+ translation: ['-50%', '-60%']
81
+ },
82
+ anchor: 'bottom-center'
83
+ }
84
+ }
85
+
86
+ /*
87
+ Helper functions
88
+ */
89
+ function addTagAttribute (tag, attibute, value) {
90
+ return tag.slice(0, -1) + ` ${attibute}="${value}">`
91
+ }
92
+ function addSvgAttribute (svg, attibute, value) {
93
+ return svg.slice(0, -2) + ` ${attibute}="${value}" />`
94
+ }
95
+
96
+ function getSize (size) {
97
+ if (!Array.isArray(size)) return { width: size, height: size }
98
+ return { width: size[0], height: size[1] }
99
+ }
100
+
101
+ /*
102
+ Utility to create a shape with the following options:
103
+ - shape: String | Object - name of the predefined shape or object specifyinfg the viewBox and the content
104
+ - size : Array - [width, height] of the maker
105
+ - radius: Number - the radius to compute a "visual" size. If the size is defined, the radius is omitted.
106
+ - color: String - the fill color
107
+ - opacity: Number - the fill opacity
108
+ - stroke: Object specifying the stroke properties
109
+ - color: String - the stroke color
110
+ - width: Number - the stroke width - 0
111
+ - opacity: Number - the stroke opacity - 1
112
+ - cap: String - the stroke linecap - 'butt'
113
+ - join: String - the stroke linejoin - 'miter'
114
+ - dashArray: String - the stroke dasharray - 'none'
115
+ - dashOffset: Number - the stroke dashoffset - 0
116
+ - icon: Object specifying an icon overlay
117
+ - classes: String - the icon class
118
+ - url: String - the icon url
119
+ - color: String - the icon color
120
+ - opacity: Number - the icon opacity
121
+ - size: Number - the icon size in pixel - 14
122
+ - translation: Array - the translation to apply from the center of the shape ['-50%', '-50%']
123
+ - rotation: Number - the rotation to apply in degree
124
+ - text: Object specifying a text overlay
125
+ - label: String - the label to display
126
+ - classes: extra classes to apply to the text
127
+ - color: String - the text color
128
+ - size: Number - the font size in pixel - 14
129
+ - translation: Array - the translation to apply from the center of the shape ['-50%', '-50%']
130
+ - rotation: Number - the rotation to apply in degree
131
+ - html: Object specifyinng an html overlay
132
+
133
+ */
134
+ export function createShape (options) {
135
+ // Check arguments
136
+ if (!options) {
137
+ logger.warn(`[KDK] 'options' argument is required`)
138
+ return
139
+ }
140
+ // Define the anchor
141
+ let anchor = 'middle-center'
142
+ // Define the shape
143
+ let shape
144
+ if (options.shape && options.shape !== 'none') {
145
+ if (typeof options.shape === 'object') shape = options.shape
146
+ else {
147
+ shape = Shapes[options.shape]
148
+ if (!shape) {
149
+ logger.warn(`[KDK] unknown shape '${options.shape}'. Using default shape 'circle'`)
150
+ shape = Shapes['circle']
151
+ }
152
+ }
153
+ anchor = shape.anchor || anchor
154
+ }
155
+ // Define the size
156
+ let size = defaultSize
157
+ if (options.size) {
158
+ size = getSize(options.size)
159
+ } else {
160
+ if (options.radius) {
161
+ const radiusToSize = _.get(shape, 'radiusToSize', defaultRadiusToSize)
162
+ size = radiusToSize(options.radius)
163
+ }
164
+ }
165
+ // Set div container vars
166
+ const beginDivTag = `<div style="position: relative; width: ${size.width}px; height: ${size.height}px;">`
167
+ const endDivTag = '</div>'
168
+ // Render shape
169
+ let beginSvgTag = ''
170
+ let svgShapeContent = ''
171
+ let svgClipPath = ''
172
+ let endSvgTag = ''
173
+ if (shape) {
174
+ beginSvgTag = `<svg xmlns="http://www.w3.org/2000/svg" width="${size.width}" height="${size.height}" preserveAspectRatio="none">`
175
+ beginSvgTag = addTagAttribute(beginSvgTag, 'viewBox', _.join(shape.viewBox, ' '))
176
+ svgShapeContent = shape.content
177
+ svgClipPath = ''
178
+ endSvgTag = '</svg>'
179
+ // Apply fill style
180
+ const color = getHtmlColor(options.color, defaultColor)
181
+ svgShapeContent = addSvgAttribute(svgShapeContent, 'fill', color)
182
+ if (options.opacity) svgShapeContent = addSvgAttribute(svgShapeContent, 'fill-opacity', options.opacity)
183
+ // Aply stroke style
184
+ if (options.stroke) {
185
+ // Ensure the stroke color is defined and not transparent
186
+ const strokeColor = getHtmlColor(options.stroke.color, defaultColor)
187
+ if (strokeColor !== 'transparent') {
188
+ svgShapeContent = addSvgAttribute(svgShapeContent, 'stroke', strokeColor)
189
+ // draw inner stroke by double the width and clip the shape by itself
190
+ // see https://stackoverflow.com/questions/7241393/can-you-control-how-an-svgs-stroke-width-is-drawn
191
+ const strokeWidth = options.stroke.width || 1
192
+ svgShapeContent = addSvgAttribute(svgShapeContent, 'stroke-width', strokeWidth * 2)
193
+ // prevent scaling stroke
194
+ svgShapeContent = addSvgAttribute(svgShapeContent, 'vector-effect', 'non-scaling-stroke')
195
+ // additional properties
196
+ if (options.stroke.cap) svgShapeContent = addSvgAttribute(svgShapeContent, 'stroke-linecap', options.stroke.cap)
197
+ if (options.stroke.join) svgShapeContent = addSvgAttribute(svgShapeContent, 'stroke-linejoin', options.stroke.join)
198
+ if (options.stroke.dashArray) svgShapeContent = addSvgAttribute(svgShapeContent, 'stroke-dasharray', options.stroke.dashArray)
199
+ if (options.stroke.dashOffset) svgShapeContent = addSvgAttribute(svgShapeContent, 'stroke-dashoffset', options.stroke.dashOffset)
200
+ const clipId = uid()
201
+ // clip the shape to avoid stroke overflow
202
+ svgShapeContent = addSvgAttribute(svgShapeContent, 'clip-path', `url(#${clipId})`)
203
+ svgClipPath = `<clipPath id="${clipId}">${_.clone(shape.content)}</clipPath>`
204
+ }
205
+ }
206
+ }
207
+ // Render icon
208
+ let iconTag = ''
209
+ if (options.icon) {
210
+ if (!_.isNil(options.icon.classes) || !_.isNil(options.icon.url)) {
211
+ if (!_.isEmpty(options.icon.classes) || !_.isEmpty(options.icon.url)) {
212
+ let specificStyle = ''
213
+ if (options.icon.url) {
214
+ let iconSize = options.icon.size ? getSize(options.icon.size) : size
215
+ iconTag = `<img src="${options.icon.url}" `
216
+ // handle size
217
+ iconTag += `width=${iconSize.width} height=${iconSize.height} `
218
+ } else {
219
+ iconTag += `<i class="${options.icon.classes}" `
220
+ // handle color
221
+ const color = getHtmlColor(options.icon.color, defaultColor)
222
+ specificStyle += `color: ${color};`
223
+ // handle size
224
+ let iconSize = options.icon.size || defaultIconSize
225
+ specificStyle += `font-size: ${iconSize}px;`
226
+ }
227
+ const opacity = options.icon.opacity || 1
228
+ const translation = options.icon.translation || _.get(shape, 'icon.translation', ['-50%', '-50%'])
229
+ const rotation = options.icon.rotation || _.get(shape, 'icon.rotation', 0)
230
+ iconTag += `style="position: absolute; top: 50%; left: 50%; transform: translate(${translation[0]},${translation[1]}) rotate(${rotation}deg); opacity: ${opacity}; ${specificStyle}"`
231
+ iconTag += '/>'
232
+ }
233
+ } else {
234
+ logger.warn(`[KDK] icon must contain either the 'classes' property or the 'url' property`)
235
+ }
236
+ }
237
+ // Render text
238
+ let textTag = ''
239
+ if (options.text) {
240
+ if (!_.isNil(options.text.label)) {
241
+ if (!_.isEmpty(options.text.label)) {
242
+ textTag = '<span '
243
+ if (options.text.classes) textTag += `classes="${options.text.classes}" `
244
+ const color = getHtmlColor(options.text.color, defaultColor)
245
+ const textSize = options.text.size || defaultTextSize
246
+ const translation = options.text.translation || _.get(shape, 'text.translation', ['-50%', '-50%'])
247
+ const rotation = options.text.rotation || _.get(shape, 'icon.rotation', 0)
248
+ textTag += `style="position: absolute; top: 50%; left: 50%; transform: translate(${translation[0]},${translation[1]}) rotate(${rotation}deg); color: ${color}; font-size: ${textSize}px;"`
249
+ textTag += '>'
250
+ textTag += options.text.label
251
+ textTag += '</span>'
252
+ }
253
+ } else {
254
+ logger.warn(`[KDK] text must contain the 'label' property`)
255
+ }
256
+ }
257
+ // Render html
258
+ let htmlTag = ''
259
+ if (options.html) {
260
+ htmlTag = '<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">'
261
+ htmlTag += options.html
262
+ htmlTag += '</div>'
263
+ }
264
+ return {
265
+ html: beginDivTag + beginSvgTag + svgClipPath + svgShapeContent + endSvgTag + iconTag + textTag + htmlTag + endDivTag,
266
+ size,
267
+ anchor
268
+ }
269
+ }
270
+