umap-project 2.3.1__py3-none-any.whl → 2.4.0__py3-none-any.whl

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.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (204) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/en/LC_MESSAGES/django.po +81 -31
  3. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/fr/LC_MESSAGES/django.po +117 -66
  5. umap/management/commands/run_websocket_server.py +23 -0
  6. umap/models.py +6 -1
  7. umap/settings/base.py +11 -3
  8. umap/static/umap/base.css +64 -184
  9. umap/static/umap/content.css +3 -2
  10. umap/static/umap/css/dialog.css +18 -0
  11. umap/static/umap/css/icon.css +8 -0
  12. umap/static/umap/css/importers.css +51 -0
  13. umap/static/umap/css/panel.css +18 -57
  14. umap/static/umap/css/tooltip.css +59 -0
  15. umap/static/umap/css/window.css +35 -0
  16. umap/static/umap/img/16-white.svg +1 -3
  17. umap/static/umap/img/alert-icon-error.svg +8 -0
  18. umap/static/umap/img/alert-icon-info.svg +4 -0
  19. umap/static/umap/img/alert-icon-success.svg +3 -0
  20. umap/static/umap/img/icon-external-link.svg +3 -0
  21. umap/static/umap/img/importers/communesfr.svg +5 -0
  22. umap/static/umap/img/importers/datasets.svg +13 -0
  23. umap/static/umap/img/importers/geodatamine.svg +10 -0
  24. umap/static/umap/img/importers/overpass.svg +7 -0
  25. umap/static/umap/img/importers/random.svg +18 -0
  26. umap/static/umap/img/importers/random1.svg +4 -0
  27. umap/static/umap/img/importers/random2.svg +4 -0
  28. umap/static/umap/img/source/16-white.svg +2 -4
  29. umap/static/umap/js/components/alerts/alert.css +160 -0
  30. umap/static/umap/js/components/alerts/alert.js +169 -0
  31. umap/static/umap/js/components/base.js +54 -0
  32. umap/static/umap/js/modules/autocomplete.js +347 -0
  33. umap/static/umap/js/modules/browser.js +6 -6
  34. umap/static/umap/js/modules/caption.js +5 -4
  35. umap/static/umap/js/modules/global.js +36 -12
  36. umap/static/umap/js/modules/help.js +255 -0
  37. umap/static/umap/js/modules/importer.js +308 -0
  38. umap/static/umap/js/modules/importers/communesfr.js +44 -0
  39. umap/static/umap/js/modules/importers/datasets.js +42 -0
  40. umap/static/umap/js/modules/importers/geodatamine.js +95 -0
  41. umap/static/umap/js/modules/importers/overpass.js +84 -0
  42. umap/static/umap/js/modules/request.js +12 -14
  43. umap/static/umap/js/modules/rules.js +241 -0
  44. umap/static/umap/js/modules/schema.js +63 -14
  45. umap/static/umap/js/modules/sync/engine.js +93 -0
  46. umap/static/umap/js/modules/sync/updaters.js +109 -0
  47. umap/static/umap/js/modules/sync/websocket.js +25 -0
  48. umap/static/umap/js/modules/ui/dialog.js +52 -0
  49. umap/static/umap/js/modules/{panel.js → ui/panel.js} +25 -14
  50. umap/static/umap/js/modules/ui/tooltip.js +116 -0
  51. umap/static/umap/js/modules/utils.js +25 -18
  52. umap/static/umap/js/umap.controls.js +13 -14
  53. umap/static/umap/js/umap.core.js +1 -324
  54. umap/static/umap/js/umap.features.js +77 -29
  55. umap/static/umap/js/umap.forms.js +9 -13
  56. umap/static/umap/js/umap.js +254 -215
  57. umap/static/umap/js/umap.layer.js +152 -74
  58. umap/static/umap/js/umap.permissions.js +5 -9
  59. umap/static/umap/js/umap.popup.js +1 -1
  60. umap/static/umap/js/umap.tableeditor.js +8 -8
  61. umap/static/umap/locale/am_ET.js +51 -16
  62. umap/static/umap/locale/am_ET.json +51 -16
  63. umap/static/umap/locale/ar.js +51 -16
  64. umap/static/umap/locale/ar.json +51 -16
  65. umap/static/umap/locale/ast.js +51 -16
  66. umap/static/umap/locale/ast.json +51 -16
  67. umap/static/umap/locale/bg.js +51 -16
  68. umap/static/umap/locale/bg.json +51 -16
  69. umap/static/umap/locale/br.js +55 -20
  70. umap/static/umap/locale/br.json +55 -20
  71. umap/static/umap/locale/ca.js +51 -16
  72. umap/static/umap/locale/ca.json +51 -16
  73. umap/static/umap/locale/cs_CZ.js +93 -58
  74. umap/static/umap/locale/cs_CZ.json +93 -58
  75. umap/static/umap/locale/da.js +51 -16
  76. umap/static/umap/locale/da.json +51 -16
  77. umap/static/umap/locale/de.js +56 -21
  78. umap/static/umap/locale/de.json +56 -21
  79. umap/static/umap/locale/el.js +51 -16
  80. umap/static/umap/locale/el.json +51 -16
  81. umap/static/umap/locale/en.js +52 -16
  82. umap/static/umap/locale/en.json +52 -16
  83. umap/static/umap/locale/en_US.json +51 -16
  84. umap/static/umap/locale/es.js +51 -16
  85. umap/static/umap/locale/es.json +51 -16
  86. umap/static/umap/locale/et.js +51 -16
  87. umap/static/umap/locale/et.json +51 -16
  88. umap/static/umap/locale/eu.js +51 -16
  89. umap/static/umap/locale/eu.json +51 -16
  90. umap/static/umap/locale/fa_IR.js +51 -16
  91. umap/static/umap/locale/fa_IR.json +51 -16
  92. umap/static/umap/locale/fi.js +51 -16
  93. umap/static/umap/locale/fi.json +51 -16
  94. umap/static/umap/locale/fr.js +61 -25
  95. umap/static/umap/locale/fr.json +61 -25
  96. umap/static/umap/locale/gl.js +51 -16
  97. umap/static/umap/locale/gl.json +51 -16
  98. umap/static/umap/locale/he.js +51 -16
  99. umap/static/umap/locale/he.json +51 -16
  100. umap/static/umap/locale/hr.js +51 -16
  101. umap/static/umap/locale/hr.json +51 -16
  102. umap/static/umap/locale/hu.js +51 -16
  103. umap/static/umap/locale/hu.json +51 -16
  104. umap/static/umap/locale/id.js +51 -16
  105. umap/static/umap/locale/id.json +51 -16
  106. umap/static/umap/locale/is.js +51 -16
  107. umap/static/umap/locale/is.json +51 -16
  108. umap/static/umap/locale/it.js +51 -16
  109. umap/static/umap/locale/it.json +51 -16
  110. umap/static/umap/locale/ja.js +51 -16
  111. umap/static/umap/locale/ja.json +51 -16
  112. umap/static/umap/locale/ko.js +51 -16
  113. umap/static/umap/locale/ko.json +51 -16
  114. umap/static/umap/locale/lt.js +51 -16
  115. umap/static/umap/locale/lt.json +51 -16
  116. umap/static/umap/locale/ms.js +51 -16
  117. umap/static/umap/locale/ms.json +51 -16
  118. umap/static/umap/locale/nl.js +51 -16
  119. umap/static/umap/locale/nl.json +51 -16
  120. umap/static/umap/locale/no.js +51 -16
  121. umap/static/umap/locale/no.json +51 -16
  122. umap/static/umap/locale/pl.js +93 -58
  123. umap/static/umap/locale/pl.json +93 -58
  124. umap/static/umap/locale/pl_PL.json +51 -16
  125. umap/static/umap/locale/pt.js +215 -180
  126. umap/static/umap/locale/pt.json +215 -180
  127. umap/static/umap/locale/pt_BR.js +51 -16
  128. umap/static/umap/locale/pt_BR.json +51 -16
  129. umap/static/umap/locale/pt_PT.js +51 -16
  130. umap/static/umap/locale/pt_PT.json +51 -16
  131. umap/static/umap/locale/ro.js +51 -16
  132. umap/static/umap/locale/ro.json +51 -16
  133. umap/static/umap/locale/ru.js +51 -16
  134. umap/static/umap/locale/ru.json +51 -16
  135. umap/static/umap/locale/si.js +51 -16
  136. umap/static/umap/locale/si.json +51 -16
  137. umap/static/umap/locale/sk_SK.js +51 -16
  138. umap/static/umap/locale/sk_SK.json +51 -16
  139. umap/static/umap/locale/sl.js +51 -16
  140. umap/static/umap/locale/sl.json +51 -16
  141. umap/static/umap/locale/sr.js +51 -16
  142. umap/static/umap/locale/sr.json +51 -16
  143. umap/static/umap/locale/sv.js +51 -16
  144. umap/static/umap/locale/sv.json +51 -16
  145. umap/static/umap/locale/th_TH.js +51 -16
  146. umap/static/umap/locale/th_TH.json +51 -16
  147. umap/static/umap/locale/tr.js +51 -16
  148. umap/static/umap/locale/tr.json +51 -16
  149. umap/static/umap/locale/uk_UA.js +51 -16
  150. umap/static/umap/locale/uk_UA.json +51 -16
  151. umap/static/umap/locale/vi.js +51 -16
  152. umap/static/umap/locale/vi.json +51 -16
  153. umap/static/umap/locale/vi_VN.json +51 -16
  154. umap/static/umap/locale/zh.js +51 -16
  155. umap/static/umap/locale/zh.json +51 -16
  156. umap/static/umap/locale/zh_CN.json +51 -16
  157. umap/static/umap/locale/zh_TW.Big5.json +51 -16
  158. umap/static/umap/locale/zh_TW.js +51 -16
  159. umap/static/umap/locale/zh_TW.json +51 -16
  160. umap/static/umap/map.css +40 -53
  161. umap/static/umap/unittests/sync.js +105 -0
  162. umap/static/umap/unittests/utils.js +78 -36
  163. umap/static/umap/vars.css +19 -1
  164. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +2 -2
  165. umap/templates/umap/components/alerts/alert.html +89 -0
  166. umap/templates/umap/content.html +4 -3
  167. umap/templates/umap/css.html +4 -0
  168. umap/templates/umap/home.html +3 -0
  169. umap/templates/umap/js.html +0 -3
  170. umap/templates/umap/map_init.html +2 -8
  171. umap/templates/umap/messages.html +9 -11
  172. umap/templates/umap/search.html +3 -0
  173. umap/tests/base.py +2 -0
  174. umap/tests/integration/conftest.py +30 -0
  175. umap/tests/integration/test_anonymous_owned_map.py +8 -13
  176. umap/tests/integration/test_browser.py +77 -4
  177. umap/tests/integration/test_conditional_rules.py +201 -0
  178. umap/tests/integration/test_dashboard.py +1 -1
  179. umap/tests/integration/test_datalayer.py +2 -3
  180. umap/tests/integration/test_edit_datalayer.py +4 -4
  181. umap/tests/integration/test_edit_map.py +1 -1
  182. umap/tests/integration/test_facets_browser.py +3 -3
  183. umap/tests/integration/test_import.py +185 -49
  184. umap/tests/integration/test_map.py +31 -2
  185. umap/tests/integration/{test_collaborative_editing.py → test_optimistic_merge.py} +7 -7
  186. umap/tests/integration/test_owned_map.py +1 -1
  187. umap/tests/integration/test_picto.py +2 -2
  188. umap/tests/integration/test_statics.py +1 -1
  189. umap/tests/integration/test_view_marker.py +2 -2
  190. umap/tests/integration/test_websocket_sync.py +283 -0
  191. umap/tests/settings.py +5 -0
  192. umap/tests/test_datalayer_views.py +0 -1
  193. umap/tests/test_views.py +53 -0
  194. umap/urls.py +5 -0
  195. umap/views.py +40 -11
  196. umap/websocket_server.py +92 -0
  197. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/METADATA +10 -8
  198. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/RECORD +201 -167
  199. umap/static/umap/js/umap.autocomplete.js +0 -341
  200. umap/static/umap/js/umap.importer.js +0 -187
  201. umap/static/umap/js/umap.ui.js +0 -190
  202. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/WHEEL +0 -0
  203. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/entry_points.txt +0 -0
  204. {umap_project-2.3.1.dist-info → umap_project-2.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,93 @@
1
+ import { WebSocketTransport } from './websocket.js'
2
+ import { MapUpdater, DataLayerUpdater, FeatureUpdater } from './updaters.js'
3
+
4
+ export class SyncEngine {
5
+ constructor(map) {
6
+ this.updaters = {
7
+ map: new MapUpdater(map),
8
+ feature: new FeatureUpdater(map),
9
+ datalayer: new DataLayerUpdater(map),
10
+ }
11
+ this.transport = undefined
12
+ }
13
+
14
+ async authenticate(tokenURI, webSocketURI, server) {
15
+ const [response, _, error] = await server.get(tokenURI)
16
+ if (!error) {
17
+ this.start(webSocketURI, response.token)
18
+ }
19
+ }
20
+
21
+ start(webSocketURI, authToken) {
22
+ this.transport = new WebSocketTransport(webSocketURI, authToken, this)
23
+ }
24
+
25
+ stop() {
26
+ if (this.transport) this.transport.close()
27
+ this.transport = undefined
28
+ }
29
+
30
+ _getUpdater(subject, metadata) {
31
+ if (Object.keys(this.updaters).includes(subject)) {
32
+ return this.updaters[subject]
33
+ }
34
+ throw new Error(`Unknown updater ${subject}, ${metadata}`)
35
+ }
36
+
37
+ // This method is called by the transport layer on new messages
38
+ receive({ kind, ...payload }) {
39
+ if (kind == 'operation') {
40
+ let updater = this._getUpdater(payload.subject, payload.metadata)
41
+ updater.applyMessage(payload)
42
+ } else {
43
+ throw new Error(`Unknown dispatch kind: ${kind}`)
44
+ }
45
+ }
46
+
47
+ _send(message) {
48
+ if (this.transport) {
49
+ this.transport.send('operation', message)
50
+ }
51
+ }
52
+
53
+ upsert(subject, metadata, value) {
54
+ this._send({ verb: 'upsert', subject, metadata, value })
55
+ }
56
+
57
+ update(subject, metadata, key, value) {
58
+ this._send({ verb: 'update', subject, metadata, key, value })
59
+ }
60
+
61
+ delete(subject, metadata, key) {
62
+ this._send({ verb: 'delete', subject, metadata, key })
63
+ }
64
+
65
+ /**
66
+ * Create a proxy for this sync engine.
67
+ *
68
+ * The proxy will automatically call `object.getSyncMetadata` and inject the returned
69
+ * `subject` and `metadata`` to the `upsert`, `update` and `delete` calls.
70
+ *
71
+ * The proxy can be used as follows:
72
+ *
73
+ * ```
74
+ * const proxy = sync.proxy(object)
75
+ * proxy.update('key', 'value')
76
+ *```
77
+ */
78
+ proxy(object) {
79
+ const handler = {
80
+ get(target, prop) {
81
+ // Only proxy these methods
82
+ if (['upsert', 'update', 'delete'].includes(prop)) {
83
+ const { subject, metadata } = object.getSyncMetadata()
84
+ // Reflect.get is calling the original method.
85
+ // .bind is adding the parameters automatically
86
+ return Reflect.get(...arguments).bind(target, subject, metadata)
87
+ }
88
+ return Reflect.get(...arguments)
89
+ },
90
+ }
91
+ return new Proxy(this, handler)
92
+ }
93
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * This file contains the updaters: classes that are able to convert messages
3
+ * received from another party (or the server) to changes on the map.
4
+ */
5
+
6
+ class BaseUpdater {
7
+ constructor(map) {
8
+ this.map = map
9
+ }
10
+
11
+ updateObjectValue(obj, key, value) {
12
+ const parts = key.split('.')
13
+ const lastKey = parts.pop()
14
+
15
+ // Reduce the current list of attributes,
16
+ // to find the object to set the property onto
17
+ const objectToSet = parts.reduce((currentObj, part) => {
18
+ if (currentObj !== undefined && part in currentObj) return currentObj[part]
19
+ }, obj)
20
+
21
+ // In case the given path doesn't exist, stop here
22
+ if (objectToSet === undefined) return
23
+
24
+ // Set the value (or delete it)
25
+ if (typeof value === 'undefined') {
26
+ delete objectToSet[lastKey]
27
+ } else {
28
+ objectToSet[lastKey] = value
29
+ }
30
+ }
31
+
32
+ getDataLayerFromID(layerId) {
33
+ if (layerId) return this.map.getDataLayerByUmapId(layerId)
34
+ return this.map.defaultEditDataLayer()
35
+ }
36
+
37
+ applyMessage(payload) {
38
+ let { verb } = payload
39
+ return this[verb](payload)
40
+ }
41
+ }
42
+
43
+ export class MapUpdater extends BaseUpdater {
44
+ update({ key, value }) {
45
+ this.updateObjectValue(this.map, key, value)
46
+ this.map.render([key])
47
+ }
48
+ }
49
+
50
+ export class DataLayerUpdater extends BaseUpdater {
51
+ upsert({ value }) {
52
+ // Inserts does not happen (we use multiple updates instead).
53
+ this.map.createDataLayer(value, false)
54
+ this.map.render([])
55
+ }
56
+
57
+ update({ key, metadata, value }) {
58
+ const datalayer = this.getDataLayerFromID(metadata.id)
59
+ this.updateObjectValue(datalayer, key, value)
60
+ datalayer.render([key])
61
+ }
62
+ }
63
+
64
+ export class FeatureUpdater extends BaseUpdater {
65
+ getFeatureFromMetadata({ id, layerId }) {
66
+ const datalayer = this.getDataLayerFromID(layerId)
67
+ return datalayer.getFeatureById(id)
68
+ }
69
+
70
+ // Create or update an object at a specific position
71
+ upsert({ metadata, value }) {
72
+ let { id, layerId } = metadata
73
+ const datalayer = this.getDataLayerFromID(layerId)
74
+ let feature = this.getFeatureFromMetadata(metadata, value)
75
+
76
+ feature = datalayer.geoJSONToLeaflet({
77
+ geometry: value.geometry,
78
+ geojson: value,
79
+ id,
80
+ feature,
81
+ })
82
+ datalayer.addLayer(feature)
83
+ }
84
+
85
+ // Update a property of an object
86
+ update({ key, metadata, value }) {
87
+ let feature = this.getFeatureFromMetadata(metadata)
88
+ if (feature === undefined) {
89
+ console.error(`Unable to find feature with id = ${metadata.id}.`)
90
+ }
91
+ switch (key) {
92
+ case 'geometry':
93
+ const datalayer = this.getDataLayerFromID(metadata.layerId)
94
+ datalayer.geoJSONToLeaflet({ geometry: value, id: metadata.id, feature })
95
+ default:
96
+ this.updateObjectValue(feature, key, value)
97
+ feature.datalayer.indexProperties(feature)
98
+ }
99
+
100
+ feature.render([key])
101
+ }
102
+
103
+ delete({ metadata }) {
104
+ // XXX Distinguish between properties getting deleted
105
+ // and the wole feature getting deleted
106
+ let feature = this.getFeatureFromMetadata(metadata)
107
+ if (feature) feature.del(false)
108
+ }
109
+ }
@@ -0,0 +1,25 @@
1
+ export class WebSocketTransport {
2
+ constructor(webSocketURI, authToken, messagesReceiver) {
3
+ this.websocket = new WebSocket(webSocketURI)
4
+ this.websocket.onopen = () => {
5
+ this.send('join', { token: authToken })
6
+ }
7
+ this.websocket.addEventListener('message', this.onMessage.bind(this))
8
+ this.receiver = messagesReceiver
9
+ }
10
+
11
+ onMessage(wsMessage) {
12
+ this.receiver.receive(JSON.parse(wsMessage.data))
13
+ }
14
+
15
+ send(kind, payload) {
16
+ const message = { ...payload }
17
+ message.kind = kind
18
+ let encoded = JSON.stringify(message)
19
+ this.websocket.send(encoded)
20
+ }
21
+
22
+ close() {
23
+ this.websocket.close()
24
+ }
25
+ }
@@ -0,0 +1,52 @@
1
+ import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
2
+ import { translate } from '../i18n.js'
3
+
4
+ export default class Dialog {
5
+ constructor(parent) {
6
+ this.parent = parent
7
+ this.className = 'umap-dialog window'
8
+ this.container = DomUtil.create('dialog', this.className, this.parent)
9
+ DomEvent.disableClickPropagation(this.container)
10
+ DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu.
11
+ DomEvent.on(this.container, 'wheel', DomEvent.stopPropagation)
12
+ DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation)
13
+ }
14
+
15
+ get visible() {
16
+ return this.container.open
17
+ }
18
+
19
+ close() {
20
+ this.container.close()
21
+ }
22
+
23
+ currentZIndex() {
24
+ return Math.max(
25
+ ...Array.from(document.querySelectorAll('dialog')).map(
26
+ (el) => window.getComputedStyle(el).getPropertyValue('z-index') || 0
27
+ )
28
+ )
29
+ }
30
+
31
+ open({ className, content, modal } = {}) {
32
+ this.container.innerHTML = ''
33
+ const currentZIndex = this.currentZIndex()
34
+ if (currentZIndex) this.container.style.zIndex = currentZIndex + 1
35
+ if (modal) this.container.showModal()
36
+ else this.container.show()
37
+ if (className) {
38
+ // Reset
39
+ this.container.className = this.className
40
+ this.container.classList.add(...className.split(' '))
41
+ }
42
+ const buttonsContainer = DomUtil.create('ul', 'buttons', this.container)
43
+ const closeButton = DomUtil.createButtonIcon(
44
+ DomUtil.create('li', '', buttonsContainer),
45
+ 'icon-close',
46
+ translate('Close')
47
+ )
48
+ DomEvent.on(closeButton, 'click', this.close, this)
49
+ this.container.appendChild(buttonsContainer)
50
+ this.container.appendChild(content)
51
+ }
52
+ }
@@ -1,5 +1,5 @@
1
- import { DomUtil, DomEvent } from '../../vendors/leaflet/leaflet-src.esm.js'
2
- import { translate } from './i18n.js'
1
+ import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
2
+ import { translate } from '../i18n.js'
3
3
 
4
4
  export class Panel {
5
5
  constructor(map) {
@@ -20,28 +20,39 @@ export class Panel {
20
20
  if (!this.mode) this.mode = mode
21
21
  }
22
22
 
23
+ isOpen() {
24
+ return this.container.classList.contains('on')
25
+ }
26
+
23
27
  open({ content, className, actions = [] } = {}) {
24
- this.container.className = `with-transition panel ${this.classname} ${this.mode || ''}`
28
+ this.container.className = `with-transition panel window ${this.classname} ${
29
+ this.mode || ''
30
+ }`
25
31
  this.container.innerHTML = ''
26
- const actionsContainer = DomUtil.create('ul', 'toolbox', this.container)
32
+ const actionsContainer = DomUtil.create('ul', 'buttons', this.container)
27
33
  const body = DomUtil.create('div', 'body', this.container)
28
34
  body.appendChild(content)
29
- const closeLink = DomUtil.create('li', 'umap-close-link', actionsContainer)
30
- DomUtil.add('i', 'icon icon-16 icon-close', closeLink)
31
- closeLink.title = translate('Close')
32
- const resizeLink = DomUtil.create('li', 'umap-resize-link', actionsContainer)
33
- DomUtil.add('i', 'icon icon-16 icon-resize', resizeLink)
34
- resizeLink.title = translate('Toggle size')
35
- for (let action of actions) {
36
- actionsContainer.appendChild(action)
35
+ const closeButton = DomUtil.createButtonIcon(
36
+ DomUtil.create('li', '', actionsContainer),
37
+ 'icon-close',
38
+ translate('Close')
39
+ )
40
+ const resizeButton = DomUtil.createButtonIcon(
41
+ DomUtil.create('li', '', actionsContainer),
42
+ 'icon-resize',
43
+ translate('Toggle size')
44
+ )
45
+ for (const action of actions) {
46
+ const element = DomUtil.element({ tagName: 'li', parent: actionsContainer })
47
+ element.appendChild(action)
37
48
  }
38
49
  if (className) DomUtil.addClass(body, className)
39
50
  const promise = new Promise((resolve, reject) => {
40
51
  DomUtil.addClass(this.container, 'on')
41
52
  resolve()
42
53
  })
43
- DomEvent.on(closeLink, 'click', this.close, this)
44
- DomEvent.on(resizeLink, 'click', this.resize, this)
54
+ DomEvent.on(closeButton, 'click', this.close, this)
55
+ DomEvent.on(resizeButton, 'click', this.resize, this)
45
56
  return promise
46
57
  }
47
58
 
@@ -0,0 +1,116 @@
1
+ import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
2
+ import { translate } from '../i18n.js'
3
+
4
+ export default class Tooltip {
5
+ constructor(parent) {
6
+ this.parent = parent
7
+ this.container = DomUtil.create('div', 'with-transition', this.parent)
8
+ this.container.id = 'umap-tooltip-container'
9
+ DomEvent.disableClickPropagation(this.container)
10
+ DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu.
11
+ DomEvent.on(this.container, 'wheel', DomEvent.stopPropagation)
12
+ DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation)
13
+ }
14
+
15
+ open(opts) {
16
+ function showIt() {
17
+ if (opts.anchor && opts.position === 'top') {
18
+ this.anchorTop(opts.anchor)
19
+ } else if (opts.anchor && opts.position === 'left') {
20
+ this.anchorLeft(opts.anchor)
21
+ } else if (opts.anchor && opts.position === 'bottom') {
22
+ this.anchorBottom(opts.anchor)
23
+ } else {
24
+ this.anchorAbsolute()
25
+ }
26
+ L.DomUtil.addClass(this.parent, 'umap-tooltip')
27
+ this.container.innerHTML = U.Utils.escapeHTML(opts.content)
28
+ }
29
+ this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0)
30
+ const id = this.TOOLTIP_ID
31
+ const closeIt = () => {
32
+ this.close(id)
33
+ }
34
+ if (opts.anchor) {
35
+ L.DomEvent.once(opts.anchor, 'mouseout', closeIt)
36
+ }
37
+ if (opts.duration !== Infinity) {
38
+ window.setTimeout(closeIt, opts.duration || 3000)
39
+ }
40
+ }
41
+
42
+ anchorAbsolute() {
43
+ this.container.className = ''
44
+ const left =
45
+ this.parent.offsetLeft +
46
+ this.parent.clientWidth / 2 -
47
+ this.container.clientWidth / 2,
48
+ top = this.parent.offsetTop + 75
49
+ this.setPosition({ top: top, left: left })
50
+ }
51
+
52
+ anchorTop(el) {
53
+ this.container.className = 'tooltip-top'
54
+ const coords = this.getPosition(el)
55
+ this.setPosition({
56
+ left: coords.left - 10,
57
+ bottom: this.getDocHeight() - coords.top + 11,
58
+ })
59
+ }
60
+
61
+ anchorBottom(el) {
62
+ this.container.className = 'tooltip-bottom'
63
+ const coords = this.getPosition(el)
64
+ this.setPosition({
65
+ left: coords.left,
66
+ top: coords.bottom + 11,
67
+ })
68
+ }
69
+
70
+ anchorLeft(el) {
71
+ this.container.className = 'tooltip-left'
72
+ const coords = this.getPosition(el)
73
+ this.setPosition({
74
+ top: coords.top,
75
+ right: document.documentElement.offsetWidth - coords.left + 11,
76
+ })
77
+ }
78
+
79
+ close(id) {
80
+ // Clear timetout even if a new tooltip has been added
81
+ // in the meantime. Eg. after a mouseout from the anchor.
82
+ window.clearTimeout(id)
83
+ if (id && id !== this.TOOLTIP_ID) return
84
+ this.container.className = ''
85
+ this.container.innerHTML = ''
86
+ this.setPosition({})
87
+ L.DomUtil.removeClass(this.parent, 'umap-tooltip')
88
+ }
89
+
90
+ getPosition(el) {
91
+ return el.getBoundingClientRect()
92
+ }
93
+
94
+ setPosition(coords) {
95
+ if (coords.left) this.container.style.left = `${coords.left}px`
96
+ else this.container.style.left = 'initial'
97
+ if (coords.right) this.container.style.right = `${coords.right}px`
98
+ else this.container.style.right = 'initial'
99
+ if (coords.top) this.container.style.top = `${coords.top}px`
100
+ else this.container.style.top = 'initial'
101
+ if (coords.bottom) this.container.style.bottom = `${coords.bottom}px`
102
+ else this.container.style.bottom = 'initial'
103
+ }
104
+
105
+ getDocHeight() {
106
+ const D = document
107
+ return Math.max(
108
+ D.body.scrollHeight,
109
+ D.documentElement.scrollHeight,
110
+ D.body.offsetHeight,
111
+ D.documentElement.offsetHeight,
112
+ D.body.clientHeight,
113
+ D.documentElement.clientHeight
114
+ )
115
+ }
116
+ }
@@ -30,7 +30,8 @@ export function checkId(string) {
30
30
  *
31
31
  * Return an array of unique impacts.
32
32
  *
33
- * @param {fields} list[fields]
33
+ * @param {fields} list[fields]
34
+ * @param object schema object. If ommited, global U.SCHEMA will be used.
34
35
  * @returns Array[string]
35
36
  */
36
37
  export function getImpactsFromSchema(fields, schema) {
@@ -84,11 +85,23 @@ export function escapeHTML(s) {
84
85
  'div',
85
86
  'iframe',
86
87
  'img',
88
+ 'audio',
89
+ 'video',
90
+ 'source',
87
91
  'br',
88
92
  'span',
93
+ 'dt',
94
+ 'dd',
89
95
  ],
90
- ADD_ATTR: ['target', 'allow', 'allowfullscreen', 'frameborder', 'scrolling'],
91
- ALLOWED_ATTR: ['href', 'src', 'width', 'height', 'style', 'dir', 'title'],
96
+ ADD_ATTR: [
97
+ 'target',
98
+ 'allow',
99
+ 'allowfullscreen',
100
+ 'frameborder',
101
+ 'scrolling',
102
+ 'controls',
103
+ ],
104
+ ALLOWED_ATTR: ['href', 'src', 'width', 'height', 'style', 'dir', 'title', 'type'],
92
105
  // Added: `geo:` URL scheme as defined in RFC5870:
93
106
  // https://www.rfc-editor.org/rfc/rfc5870.html
94
107
  // The base RegExp comes from:
@@ -102,27 +115,24 @@ export function escapeHTML(s) {
102
115
  export function toHTML(r, options) {
103
116
  if (!r) return ''
104
117
  const target = (options && options.target) || 'blank'
105
- let ii
106
118
 
107
- // detect newline format
108
- const newline = r.indexOf('\r\n') != -1 ? '\r\n' : r.indexOf('\n') != -1 ? '\n' : ''
119
+ // unordered lists
120
+ r = r.replace(/^\*\* (.*)/gm, '<ul><ul><li>$1</li></ul></ul>')
121
+ r = r.replace(/^\* (.*)/gm, '<ul><li>$1</li></ul>')
122
+ for (let ii = 0; ii < 3; ii++) {
123
+ r = r.replace(new RegExp(`</ul>(\r\n|\r|\n)<ul>`, 'g'), '')
124
+ }
109
125
 
110
126
  // headings and hr
111
- r = r.replace(/^### (.*)/gm, '<h5>$1</h5>')
112
- r = r.replace(/^## (.*)/gm, '<h4>$1</h4>')
113
- r = r.replace(/^# (.*)/gm, '<h3>$1</h3>')
127
+ r = r.replace(/^### (.*)(\r\n|\r|\n)?/gm, '<h6>$1</h6>')
128
+ r = r.replace(/^## (.*)(\r\n|\r|\n)?/gm, '<h5>$1</h5>')
129
+ r = r.replace(/^# (.*)(\r\n|\r|\n)?/gm, '<h4>$1</h4>')
114
130
  r = r.replace(/^---/gm, '<hr>')
115
131
 
116
132
  // bold, italics
117
133
  r = r.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
118
134
  r = r.replace(/\*(.*?)\*/g, '<em>$1</em>')
119
135
 
120
- // unordered lists
121
- r = r.replace(/^\*\* (.*)/gm, '<ul><ul><li>$1</li></ul></ul>')
122
- r = r.replace(/^\* (.*)/gm, '<ul><li>$1</li></ul>')
123
- for (ii = 0; ii < 3; ii++)
124
- r = r.replace(new RegExp(`</ul>${newline}<ul>`, 'g'), newline)
125
-
126
136
  // links
127
137
  r = r.replace(/(\[\[http)/g, '[[h_t_t_p') // Escape for avoiding clash between [[http://xxx]] and http://xxx
128
138
  r = r.replace(/({{http)/g, '{{h_t_t_p')
@@ -163,9 +173,6 @@ export function toHTML(r, options) {
163
173
  //Unescape http
164
174
  r = r.replace(/(h_t_t_p)/g, 'http')
165
175
 
166
- // Preserver line breaks
167
- if (newline) r = r.replace(new RegExp(`${newline}(?=[^]+)`, 'g'), `<br>${newline}`)
168
-
169
176
  r = escapeHTML(r)
170
177
 
171
178
  return r
@@ -84,7 +84,7 @@ U.UpdateExtentAction = U.BaseAction.extend({
84
84
  },
85
85
 
86
86
  addHooks: function () {
87
- this.map.updateExtent()
87
+ this.map.setCenterAndZoom()
88
88
  },
89
89
  })
90
90
 
@@ -394,7 +394,7 @@ U.EditControl = L.Control.extend({
394
394
  enableEditing,
395
395
  'mouseover',
396
396
  function () {
397
- map.ui.tooltip({
397
+ map.tooltip.open({
398
398
  content: map.help.displayLabel('TOGGLE_EDIT'),
399
399
  anchor: enableEditing,
400
400
  position: 'bottom',
@@ -510,7 +510,7 @@ U.DataLayersControl = L.Control.Button.extend({
510
510
  options: {
511
511
  position: 'topleft',
512
512
  className: 'umap-control-browse',
513
- title: L._('See layers'),
513
+ title: L._('Open browser'),
514
514
  },
515
515
 
516
516
  afterAdd: function (container) {
@@ -693,7 +693,7 @@ const ControlsMixin = {
693
693
  nameButton,
694
694
  'mouseover',
695
695
  function () {
696
- this.ui.tooltip({
696
+ this.tooltip.open({
697
697
  content: L._('Edit the title of the map'),
698
698
  anchor: nameButton,
699
699
  position: 'bottom',
@@ -714,7 +714,7 @@ const ControlsMixin = {
714
714
  shareStatusButton,
715
715
  'mouseover',
716
716
  function () {
717
- this.ui.tooltip({
717
+ this.tooltip.open({
718
718
  content: L._('Update who can see and edit the map'),
719
719
  anchor: shareStatusButton,
720
720
  position: 'bottom',
@@ -763,7 +763,7 @@ const ControlsMixin = {
763
763
  controlEditCancel,
764
764
  'mouseover',
765
765
  function () {
766
- this.ui.tooltip({
766
+ this.tooltip.open({
767
767
  content: this.help.displayLabel('CANCEL'),
768
768
  anchor: controlEditCancel,
769
769
  position: 'bottom',
@@ -784,7 +784,7 @@ const ControlsMixin = {
784
784
  controlEditDisable,
785
785
  'mouseover',
786
786
  function () {
787
- this.ui.tooltip({
787
+ this.tooltip.open({
788
788
  content: this.help.displayLabel('PREVIEW'),
789
789
  anchor: controlEditDisable,
790
790
  position: 'bottom',
@@ -805,7 +805,7 @@ const ControlsMixin = {
805
805
  controlEditSave,
806
806
  'mouseover',
807
807
  function () {
808
- this.ui.tooltip({
808
+ this.tooltip.open({
809
809
  content: this.help.displayLabel('SAVE'),
810
810
  anchor: controlEditSave,
811
811
  position: 'bottom',
@@ -992,7 +992,7 @@ U.AttributionControl = L.Control.Attribution.extend({
992
992
  })
993
993
  }
994
994
  if (captionMenus) {
995
- const link = L.DomUtil.add('a', '', container, ` — ${L._('About')}`)
995
+ const link = L.DomUtil.add('a', '', container, ` — ${L._('Open caption')}`)
996
996
  L.DomEvent.on(link, 'click', L.DomEvent.stop)
997
997
  .on(link, 'click', this._map.openCaption, this._map)
998
998
  .on(link, 'dblclick', L.DomEvent.stop)
@@ -1048,7 +1048,6 @@ U.Locate = L.Control.Locate.extend({
1048
1048
  if (!this._container || !this._container.parentNode) return
1049
1049
  return L.Control.Locate.prototype.remove.call(this)
1050
1050
  },
1051
-
1052
1051
  })
1053
1052
 
1054
1053
  U.Search = L.PhotonSearch.extend({
@@ -1087,7 +1086,7 @@ U.Search = L.PhotonSearch.extend({
1087
1086
  if (latlng.isValid()) {
1088
1087
  this.reverse.doReverse(latlng)
1089
1088
  } else {
1090
- this.map.ui.alert({ content: 'Invalid latitude or longitude', mode: 'error' })
1089
+ U.Alert.error(L._('Invalid latitude or longitude'))
1091
1090
  }
1092
1091
  return
1093
1092
  }
@@ -1250,7 +1249,7 @@ U.Editable = L.Editable.extend({
1250
1249
  L.Editable.prototype.initialize.call(this, map, options)
1251
1250
  this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip)
1252
1251
  this.on('editable:drawing:end', (e) => {
1253
- this.closeTooltip()
1252
+ this.map.tooltip.close()
1254
1253
  // Leaflet.Editable will delete the drawn shape if invalid
1255
1254
  // (eg. line has only one drawn point)
1256
1255
  // So let's check if the layer has no more shape
@@ -1314,7 +1313,7 @@ U.Editable = L.Editable.extend({
1314
1313
 
1315
1314
  drawingTooltip: function (e) {
1316
1315
  if (e.layer instanceof L.Marker && e.type == 'editable:drawing:start') {
1317
- this.map.ui.tooltip({ content: L._('Click to add a marker') })
1316
+ this.map.tooltip.open({ content: L._('Click to add a marker') })
1318
1317
  }
1319
1318
  if (!(e.layer instanceof L.Polyline)) {
1320
1319
  // only continue with Polylines and Polygons
@@ -1357,7 +1356,7 @@ U.Editable = L.Editable.extend({
1357
1356
  }
1358
1357
  }
1359
1358
  if (content) {
1360
- this.map.ui.tooltip({ content: content })
1359
+ this.map.tooltip.open({ content: content })
1361
1360
  }
1362
1361
  },
1363
1362