umap-project 2.3.0__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 (211) 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/locale/pl/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/pl/LC_MESSAGES/django.po +83 -78
  7. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/pt/LC_MESSAGES/django.po +129 -123
  9. umap/management/commands/run_websocket_server.py +23 -0
  10. umap/models.py +6 -1
  11. umap/settings/base.py +11 -3
  12. umap/static/umap/base.css +68 -186
  13. umap/static/umap/content.css +3 -2
  14. umap/static/umap/css/dialog.css +18 -0
  15. umap/static/umap/css/icon.css +8 -0
  16. umap/static/umap/css/importers.css +51 -0
  17. umap/static/umap/css/panel.css +18 -57
  18. umap/static/umap/css/tooltip.css +59 -0
  19. umap/static/umap/css/window.css +35 -0
  20. umap/static/umap/img/16-white.svg +1 -3
  21. umap/static/umap/img/alert-icon-error.svg +8 -0
  22. umap/static/umap/img/alert-icon-info.svg +4 -0
  23. umap/static/umap/img/alert-icon-success.svg +3 -0
  24. umap/static/umap/img/icon-external-link.svg +3 -0
  25. umap/static/umap/img/importers/communesfr.svg +5 -0
  26. umap/static/umap/img/importers/datasets.svg +13 -0
  27. umap/static/umap/img/importers/geodatamine.svg +10 -0
  28. umap/static/umap/img/importers/overpass.svg +7 -0
  29. umap/static/umap/img/importers/random.svg +18 -0
  30. umap/static/umap/img/importers/random1.svg +4 -0
  31. umap/static/umap/img/importers/random2.svg +4 -0
  32. umap/static/umap/img/source/16-white.svg +2 -4
  33. umap/static/umap/js/components/alerts/alert.css +160 -0
  34. umap/static/umap/js/components/alerts/alert.js +169 -0
  35. umap/static/umap/js/components/base.js +54 -0
  36. umap/static/umap/js/modules/autocomplete.js +347 -0
  37. umap/static/umap/js/modules/browser.js +14 -21
  38. umap/static/umap/js/modules/caption.js +119 -0
  39. umap/static/umap/js/modules/global.js +37 -11
  40. umap/static/umap/js/modules/help.js +255 -0
  41. umap/static/umap/js/modules/importer.js +308 -0
  42. umap/static/umap/js/modules/importers/communesfr.js +44 -0
  43. umap/static/umap/js/modules/importers/datasets.js +42 -0
  44. umap/static/umap/js/modules/importers/geodatamine.js +95 -0
  45. umap/static/umap/js/modules/importers/overpass.js +84 -0
  46. umap/static/umap/js/modules/request.js +12 -14
  47. umap/static/umap/js/modules/rules.js +241 -0
  48. umap/static/umap/js/modules/schema.js +63 -14
  49. umap/static/umap/js/modules/sync/engine.js +93 -0
  50. umap/static/umap/js/modules/sync/updaters.js +109 -0
  51. umap/static/umap/js/modules/sync/websocket.js +25 -0
  52. umap/static/umap/js/modules/ui/dialog.js +52 -0
  53. umap/static/umap/js/modules/{panel.js → ui/panel.js} +37 -20
  54. umap/static/umap/js/modules/ui/tooltip.js +116 -0
  55. umap/static/umap/js/modules/utils.js +25 -18
  56. umap/static/umap/js/umap.controls.js +37 -112
  57. umap/static/umap/js/umap.core.js +1 -327
  58. umap/static/umap/js/umap.features.js +77 -29
  59. umap/static/umap/js/umap.forms.js +17 -19
  60. umap/static/umap/js/umap.js +265 -228
  61. umap/static/umap/js/umap.layer.js +154 -76
  62. umap/static/umap/js/umap.permissions.js +5 -9
  63. umap/static/umap/js/umap.popup.js +2 -1
  64. umap/static/umap/js/umap.tableeditor.js +8 -8
  65. umap/static/umap/locale/am_ET.js +51 -16
  66. umap/static/umap/locale/am_ET.json +51 -16
  67. umap/static/umap/locale/ar.js +51 -16
  68. umap/static/umap/locale/ar.json +51 -16
  69. umap/static/umap/locale/ast.js +51 -16
  70. umap/static/umap/locale/ast.json +51 -16
  71. umap/static/umap/locale/bg.js +51 -16
  72. umap/static/umap/locale/bg.json +51 -16
  73. umap/static/umap/locale/br.js +55 -20
  74. umap/static/umap/locale/br.json +55 -20
  75. umap/static/umap/locale/ca.js +51 -16
  76. umap/static/umap/locale/ca.json +51 -16
  77. umap/static/umap/locale/cs_CZ.js +93 -58
  78. umap/static/umap/locale/cs_CZ.json +93 -58
  79. umap/static/umap/locale/da.js +51 -16
  80. umap/static/umap/locale/da.json +51 -16
  81. umap/static/umap/locale/de.js +56 -21
  82. umap/static/umap/locale/de.json +56 -21
  83. umap/static/umap/locale/el.js +51 -16
  84. umap/static/umap/locale/el.json +51 -16
  85. umap/static/umap/locale/en.js +52 -16
  86. umap/static/umap/locale/en.json +52 -16
  87. umap/static/umap/locale/en_US.json +51 -16
  88. umap/static/umap/locale/es.js +51 -16
  89. umap/static/umap/locale/es.json +51 -16
  90. umap/static/umap/locale/et.js +51 -16
  91. umap/static/umap/locale/et.json +51 -16
  92. umap/static/umap/locale/eu.js +51 -16
  93. umap/static/umap/locale/eu.json +51 -16
  94. umap/static/umap/locale/fa_IR.js +51 -16
  95. umap/static/umap/locale/fa_IR.json +51 -16
  96. umap/static/umap/locale/fi.js +51 -16
  97. umap/static/umap/locale/fi.json +51 -16
  98. umap/static/umap/locale/fr.js +61 -25
  99. umap/static/umap/locale/fr.json +61 -25
  100. umap/static/umap/locale/gl.js +51 -16
  101. umap/static/umap/locale/gl.json +51 -16
  102. umap/static/umap/locale/he.js +51 -16
  103. umap/static/umap/locale/he.json +51 -16
  104. umap/static/umap/locale/hr.js +51 -16
  105. umap/static/umap/locale/hr.json +51 -16
  106. umap/static/umap/locale/hu.js +51 -16
  107. umap/static/umap/locale/hu.json +51 -16
  108. umap/static/umap/locale/id.js +51 -16
  109. umap/static/umap/locale/id.json +51 -16
  110. umap/static/umap/locale/is.js +51 -16
  111. umap/static/umap/locale/is.json +51 -16
  112. umap/static/umap/locale/it.js +51 -16
  113. umap/static/umap/locale/it.json +51 -16
  114. umap/static/umap/locale/ja.js +51 -16
  115. umap/static/umap/locale/ja.json +51 -16
  116. umap/static/umap/locale/ko.js +51 -16
  117. umap/static/umap/locale/ko.json +51 -16
  118. umap/static/umap/locale/lt.js +51 -16
  119. umap/static/umap/locale/lt.json +51 -16
  120. umap/static/umap/locale/ms.js +51 -16
  121. umap/static/umap/locale/ms.json +51 -16
  122. umap/static/umap/locale/nl.js +51 -16
  123. umap/static/umap/locale/nl.json +51 -16
  124. umap/static/umap/locale/no.js +51 -16
  125. umap/static/umap/locale/no.json +51 -16
  126. umap/static/umap/locale/pl.js +93 -58
  127. umap/static/umap/locale/pl.json +93 -58
  128. umap/static/umap/locale/pl_PL.json +51 -16
  129. umap/static/umap/locale/pt.js +215 -180
  130. umap/static/umap/locale/pt.json +215 -180
  131. umap/static/umap/locale/pt_BR.js +51 -16
  132. umap/static/umap/locale/pt_BR.json +51 -16
  133. umap/static/umap/locale/pt_PT.js +51 -16
  134. umap/static/umap/locale/pt_PT.json +51 -16
  135. umap/static/umap/locale/ro.js +51 -16
  136. umap/static/umap/locale/ro.json +51 -16
  137. umap/static/umap/locale/ru.js +51 -16
  138. umap/static/umap/locale/ru.json +51 -16
  139. umap/static/umap/locale/si.js +51 -16
  140. umap/static/umap/locale/si.json +51 -16
  141. umap/static/umap/locale/sk_SK.js +51 -16
  142. umap/static/umap/locale/sk_SK.json +51 -16
  143. umap/static/umap/locale/sl.js +51 -16
  144. umap/static/umap/locale/sl.json +51 -16
  145. umap/static/umap/locale/sr.js +51 -16
  146. umap/static/umap/locale/sr.json +51 -16
  147. umap/static/umap/locale/sv.js +51 -16
  148. umap/static/umap/locale/sv.json +51 -16
  149. umap/static/umap/locale/th_TH.js +51 -16
  150. umap/static/umap/locale/th_TH.json +51 -16
  151. umap/static/umap/locale/tr.js +51 -16
  152. umap/static/umap/locale/tr.json +51 -16
  153. umap/static/umap/locale/uk_UA.js +51 -16
  154. umap/static/umap/locale/uk_UA.json +51 -16
  155. umap/static/umap/locale/vi.js +51 -16
  156. umap/static/umap/locale/vi.json +51 -16
  157. umap/static/umap/locale/vi_VN.json +51 -16
  158. umap/static/umap/locale/zh.js +51 -16
  159. umap/static/umap/locale/zh.json +51 -16
  160. umap/static/umap/locale/zh_CN.json +51 -16
  161. umap/static/umap/locale/zh_TW.Big5.json +51 -16
  162. umap/static/umap/locale/zh_TW.js +51 -16
  163. umap/static/umap/locale/zh_TW.json +51 -16
  164. umap/static/umap/map.css +40 -53
  165. umap/static/umap/unittests/sync.js +105 -0
  166. umap/static/umap/unittests/utils.js +78 -36
  167. umap/static/umap/vars.css +19 -1
  168. umap/static/umap/vendors/dompurify/purify.es.js +50 -15
  169. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  170. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +2 -2
  171. umap/templates/umap/components/alerts/alert.html +89 -0
  172. umap/templates/umap/content.html +4 -3
  173. umap/templates/umap/css.html +4 -0
  174. umap/templates/umap/home.html +3 -0
  175. umap/templates/umap/js.html +0 -3
  176. umap/templates/umap/map_init.html +2 -8
  177. umap/templates/umap/messages.html +9 -11
  178. umap/templates/umap/search.html +3 -0
  179. umap/tests/base.py +3 -0
  180. umap/tests/integration/conftest.py +30 -0
  181. umap/tests/integration/test_anonymous_owned_map.py +8 -13
  182. umap/tests/integration/test_browser.py +81 -6
  183. umap/tests/integration/test_caption.py +27 -0
  184. umap/tests/integration/test_conditional_rules.py +201 -0
  185. umap/tests/integration/test_dashboard.py +1 -1
  186. umap/tests/integration/test_datalayer.py +2 -3
  187. umap/tests/integration/test_edit_datalayer.py +32 -3
  188. umap/tests/integration/test_edit_map.py +1 -1
  189. umap/tests/integration/test_facets_browser.py +7 -4
  190. umap/tests/integration/test_import.py +185 -49
  191. umap/tests/integration/test_map.py +31 -17
  192. umap/tests/integration/{test_collaborative_editing.py → test_optimistic_merge.py} +7 -7
  193. umap/tests/integration/test_owned_map.py +1 -1
  194. umap/tests/integration/test_picto.py +2 -2
  195. umap/tests/integration/test_statics.py +1 -1
  196. umap/tests/integration/test_view_marker.py +19 -2
  197. umap/tests/integration/test_websocket_sync.py +283 -0
  198. umap/tests/settings.py +5 -0
  199. umap/tests/test_datalayer_views.py +0 -1
  200. umap/tests/test_views.py +53 -0
  201. umap/urls.py +5 -0
  202. umap/views.py +40 -11
  203. umap/websocket_server.py +92 -0
  204. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/METADATA +13 -11
  205. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/RECORD +208 -172
  206. umap/static/umap/js/umap.autocomplete.js +0 -341
  207. umap/static/umap/js/umap.importer.js +0 -187
  208. umap/static/umap/js/umap.ui.js +0 -190
  209. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/WHEEL +0 -0
  210. {umap_project-2.3.0.dist-info → umap_project-2.4.0.dist-info}/entry_points.txt +0 -0
  211. {umap_project-2.3.0.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,12 +1,14 @@
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) {
6
6
  this.parent = map._controlContainer
7
7
  this.map = map
8
8
  this.container = DomUtil.create('div', '', this.parent)
9
- this.mode = 'condensed'
9
+ // This will be set once according to the panel configurated at load
10
+ // or by using panels as popups
11
+ this.mode = null
10
12
  this.classname = 'left'
11
13
  DomEvent.disableClickPropagation(this.container)
12
14
  DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu.
@@ -14,40 +16,55 @@ export class Panel {
14
16
  DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation)
15
17
  }
16
18
 
19
+ setDefaultMode(mode) {
20
+ if (!this.mode) this.mode = mode
21
+ }
22
+
23
+ isOpen() {
24
+ return this.container.classList.contains('on')
25
+ }
26
+
17
27
  open({ content, className, actions = [] } = {}) {
18
- this.container.className = `with-transition panel ${this.classname} ${this.mode}`
28
+ this.container.className = `with-transition panel window ${this.classname} ${
29
+ this.mode || ''
30
+ }`
19
31
  this.container.innerHTML = ''
20
- const actionsContainer = DomUtil.create('ul', 'toolbox', this.container)
32
+ const actionsContainer = DomUtil.create('ul', 'buttons', this.container)
21
33
  const body = DomUtil.create('div', 'body', this.container)
22
34
  body.appendChild(content)
23
- const closeLink = DomUtil.create('li', 'umap-close-link', actionsContainer)
24
- DomUtil.add('i', 'icon icon-16 icon-close', closeLink)
25
- closeLink.title = translate('Close')
26
- const resizeLink = DomUtil.create('li', 'umap-resize-link', actionsContainer)
27
- DomUtil.add('i', 'icon icon-16 icon-resize', resizeLink)
28
- resizeLink.title = translate('Toggle size')
29
- for (let action of actions) {
30
- 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)
31
48
  }
32
49
  if (className) DomUtil.addClass(body, className)
33
50
  const promise = new Promise((resolve, reject) => {
34
51
  DomUtil.addClass(this.container, 'on')
35
52
  resolve()
36
53
  })
37
- DomEvent.on(closeLink, 'click', this.close, this)
38
- DomEvent.on(resizeLink, 'click', this.resize, this)
54
+ DomEvent.on(closeButton, 'click', this.close, this)
55
+ DomEvent.on(resizeButton, 'click', this.resize, this)
39
56
  return promise
40
57
  }
41
58
 
42
59
  resize() {
43
- if (this.mode === 'expanded') {
44
- this.mode = 'condensed'
45
- this.container.classList.remove('expanded')
46
- this.container.classList.add('condensed')
47
- } else {
60
+ if (this.mode === 'condensed') {
48
61
  this.mode = 'expanded'
49
62
  this.container.classList.remove('condensed')
50
63
  this.container.classList.add('expanded')
64
+ } else {
65
+ this.mode = 'condensed'
66
+ this.container.classList.remove('expanded')
67
+ this.container.classList.add('condensed')
51
68
  }
52
69
  }
53
70
 
@@ -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