diff --git a/docs/dev/frontend.md b/docs/dev/frontend.md index 1e3556fa..96ff271f 100644 --- a/docs/dev/frontend.md +++ b/docs/dev/frontend.md @@ -63,15 +63,3 @@ When the data layers are initialized, they can have two states: To mark what needs to be synced with the server, uMap currently mark objects as "dirty". Something marked as "dirty" has changed on the client, but is not yet saved on the server. Each map, datalayer and permission objects can be marked as "dirty". When a change is made on an object, it will mark its parent as "dirty" as well, so it can be updated accordingly. - -### Saving data to the server with `umap.save()` - -Here is what's being done when you call `map.save()`: - -1. `map.saveSelf()`, posting `name`, `center` and `settings` to the server, and then -2. calls `permission.save()`, which will post the permissions to the server, and then call back -3. `map.continueSaving()`, which will take the first dirtyLayer and call -4. `datalayer.save()` on it. It does the following: - 1. Post the data (`name`, `displayOnLoad`, `rank`, `settings`, and `geojson`) - 2. Calls `permission.save()`, posting `edit_status` to the server, and then calling `map.continue_saving()` and remove the datalayer from `dirtyDatalayers`. -5. When the `dirtyDatalayers` list is empty, we are done. diff --git a/umap/static/umap/js/modules/browser.js b/umap/static/umap/js/modules/browser.js index b8f59dac..3faef4cc 100644 --- a/umap/static/umap/js/modules/browser.js +++ b/umap/static/umap/js/modules/browser.js @@ -6,9 +6,10 @@ import { EXPORT_FORMATS } from './formatter.js' import ContextMenu from './ui/contextmenu.js' export default class Browser { - constructor(umap) { - this.umap = umap - this.umap._leafletMap.on('moveend', this.onMoveEnd, this) + constructor(umap, leafletMap) { + this._umap = umap + this._leafletMap = leafletMap + this._leafletMap.on('moveend', this.onMoveEnd, this) this.options = { filter: '', inBbox: false, @@ -82,7 +83,7 @@ export default class Browser { updateDatalayer(datalayer) { // Compute once, but use it for each feature later. - this.bounds = this.umap._leafletMap.getBounds() + this.bounds = this._leafletMap.getBounds() const parent = DomUtil.get(this.datalayerId(datalayer)) // Panel is not open if (!parent) return @@ -115,10 +116,10 @@ export default class Browser { } onFormChange() { - this.umap.eachBrowsableDataLayer((datalayer) => { + this._umap.eachBrowsableDataLayer((datalayer) => { datalayer.resetLayer(true) this.updateDatalayer(datalayer) - if (this.umap.fullPanel?.isOpen()) datalayer.tableEdit() + if (this._umap.fullPanel?.isOpen()) datalayer.tableEdit() }) this.toggleBadge() } @@ -132,13 +133,13 @@ export default class Browser { } hasFilters() { - return !!this.options.filter || this.umap.facets.isActive() + return !!this.options.filter || this._umap.facets.isActive() } onMoveEnd() { if (!this.isOpen()) return const isListDynamic = this.options.inBbox - this.umap.eachBrowsableDataLayer((datalayer) => { + this._umap.eachBrowsableDataLayer((datalayer) => { if (!isListDynamic && !datalayer.hasDynamicData()) return this.updateDatalayer(datalayer) }) @@ -147,7 +148,7 @@ export default class Browser { update() { if (!this.isOpen()) return this.dataContainer.innerHTML = '' - this.umap.eachBrowsableDataLayer((datalayer) => { + this._umap.eachBrowsableDataLayer((datalayer) => { this.addDataLayer(datalayer, this.dataContainer) }) } @@ -186,9 +187,9 @@ export default class Browser { DomEvent.on(builder.form, 'reset', () => { window.setTimeout(builder.syncAll.bind(builder)) }) - if (this.umap.properties.facetKey) { - fields = this.umap.facets.build() - filtersBuilder = new L.FormBuilder(this.umap.facets, fields, { + if (this._umap.properties.facetKey) { + fields = this._umap.facets.build() + filtersBuilder = new L.FormBuilder(this._umap.facets, fields, { callback: () => this.onFormChange(), }) DomEvent.on(filtersBuilder.form, 'reset', () => { @@ -206,7 +207,7 @@ export default class Browser { textContent: translate('Reset all'), }) - this.umap.panel.open({ + this._umap.panel.open({ content: container, className: 'umap-browser', }) @@ -230,7 +231,7 @@ export default class Browser { `) container.appendChild(toolbox) toggle.addEventListener('click', () => this.toggleLayers()) - fitBounds.addEventListener('click', () => this.umap.fitDataBounds()) + fitBounds.addEventListener('click', () => this._umap.fitDataBounds()) download.addEventListener('click', () => this.downloadVisible(download)) } @@ -240,7 +241,7 @@ export default class Browser { for (const format of Object.keys(EXPORT_FORMATS)) { items.push({ label: format, - action: () => this.umap.share.download(format), + action: () => this._umap.share.download(format), }) } menu.openBelow(element, items) @@ -250,10 +251,10 @@ export default class Browser { // If at least one layer is shown, hide it // otherwise show all let allHidden = true - this.umap.eachBrowsableDataLayer((datalayer) => { + this._umap.eachBrowsableDataLayer((datalayer) => { if (datalayer.isVisible()) allHidden = false }) - this.umap.eachBrowsableDataLayer((datalayer) => { + this._umap.eachBrowsableDataLayer((datalayer) => { if (allHidden) { datalayer.show() } else { @@ -262,7 +263,7 @@ export default class Browser { }) } - static backButton(map) { + static backButton(umap) { const button = DomUtil.createButtonIcon( DomUtil.create('li', '', undefined), 'icon-back', @@ -271,7 +272,7 @@ export default class Browser { // Fixme: remove me when this is merged and released // https://github.com/Leaflet/Leaflet/pull/9052 DomEvent.disableClickPropagation(button) - DomEvent.on(button, 'click', map.umap.openBrowser, map.umap) + DomEvent.on(button, 'click', () => umap.openBrowser()) return button } } diff --git a/umap/static/umap/js/modules/caption.js b/umap/static/umap/js/modules/caption.js index 1fa62e48..e1a8babd 100644 --- a/umap/static/umap/js/modules/caption.js +++ b/umap/static/umap/js/modules/caption.js @@ -3,8 +3,9 @@ import { translate } from './i18n.js' import * as Utils from './utils.js' export default class Caption { - constructor(umap) { - this.umap = umap + constructor(umap, leafletMap) { + this._umap = umap + this._leafletMap = leafletMap } isOpen() { @@ -21,30 +22,30 @@ export default class Caption { const hgroup = DomUtil.element({ tagName: 'hgroup', parent: container }) DomUtil.createTitle( hgroup, - this.umap.getDisplayName(), + this._umap.getDisplayName(), 'icon-caption icon-block', 'map-name' ) const title = Utils.loadTemplate('

') hgroup.appendChild(title) - this.umap.addAuthorLink(title) - if (this.umap.properties.description) { + this._umap.addAuthorLink(title) + if (this._umap.properties.description) { const description = DomUtil.element({ tagName: 'div', className: 'umap-map-description text', - safeHTML: Utils.toHTML(this.umap.properties.description), + safeHTML: Utils.toHTML(this._umap.properties.description), parent: container, }) } const datalayerContainer = DomUtil.create('div', 'datalayer-container', container) - this.umap.eachDataLayerReverse((datalayer) => + this._umap.eachDataLayerReverse((datalayer) => this.addDataLayer(datalayer, datalayerContainer) ) const creditsContainer = DomUtil.create('div', 'credits-container', container) this.addCredits(creditsContainer) - this.umap.panel.open({ content: container }).then(() => { + this._umap.panel.open({ content: container }).then(() => { // Create the legend when the panel is actually on the DOM - this.umap.eachDataLayerReverse((datalayer) => datalayer.renderLegend()) + this._umap.eachDataLayerReverse((datalayer) => datalayer.renderLegend()) }) } @@ -67,16 +68,16 @@ export default class Caption { addCredits(container) { const credits = DomUtil.createFieldset(container, translate('Credits')) let title = DomUtil.add('h5', '', credits, translate('User content credits')) - if (this.umap.properties.shortCredit || this.umap.properties.longCredit) { + if (this._umap.properties.shortCredit || this._umap.properties.longCredit) { DomUtil.element({ tagName: 'p', parent: credits, safeHTML: Utils.toHTML( - this.umap.properties.longCredit || this.umap.properties.shortCredit + this._umap.properties.longCredit || this._umap.properties.shortCredit ), }) } - if (this.umap.properties.licence) { + if (this._umap.properties.licence) { const licence = DomUtil.add( 'p', '', @@ -86,8 +87,8 @@ export default class Caption { DomUtil.createLink( '', licence, - this.umap.properties.licence.name, - this.umap.properties.licence.url + this._umap.properties.licence.name, + this._umap.properties.licence.url ) } else { DomUtil.add('p', '', credits, translate('No licence has been set')) @@ -98,19 +99,19 @@ export default class Caption { DomUtil.element({ tagName: 'strong', parent: tilelayerCredit, - textContent: `${this.umap._leafletMap.selectedTilelayer.options.name} `, + textContent: `${this._leafletMap.selectedTilelayer.options.name} `, }) DomUtil.element({ tagName: 'span', parent: tilelayerCredit, - safeHTML: this.umap._leafletMap.selectedTilelayer.getAttribution(), + safeHTML: this._leafletMap.selectedTilelayer.getAttribution(), }) const urls = { leaflet: 'http://leafletjs.com', django: 'https://www.djangoproject.com', umap: 'https://umap-project.org/', changelog: 'https://docs.umap-project.org/en/master/changelog/', - version: this.umap.properties.umap_version, + version: this._umap.properties.umap_version, } const creditHTML = translate( ` diff --git a/umap/static/umap/js/modules/data/features.js b/umap/static/umap/js/modules/data/features.js index cd118d30..894b3a3a 100644 --- a/umap/static/umap/js/modules/data/features.js +++ b/umap/static/umap/js/modules/data/features.js @@ -18,8 +18,9 @@ import { import loadPopup from '../rendering/popup.js' class Feature { - constructor(datalayer, geojson = {}, id = null) { - this.sync = datalayer.umap.sync_engine.proxy(this) + constructor(umap, datalayer, geojson = {}, id = null) { + this._umap = umap + this.sync = umap.sync_engine.proxy(this) this._marked_for_deletion = false this._isDirty = false this._ui = null @@ -69,10 +70,6 @@ class Feature { return this._ui } - get umap() { - return this.datalayer?.umap - } - get center() { return this.ui.getCenter() } @@ -168,7 +165,7 @@ class Feature { } getSlug() { - return this.properties[this.umap.getOption('slugKey') || 'name'] || '' + return this.properties[this._umap.getProperty('slugKey') || 'name'] || '' } getPermalink() { @@ -196,10 +193,10 @@ class Feature { return } // TODO deal with an event instead? - if (this.umap.slideshow) { - this.umap.slideshow.current = this + if (this._umap.slideshow) { + this._umap.slideshow.current = this } - this.umap.currentFeature = this + this._umap.currentFeature = this this.attachPopup() this.ui.openPopup(latlng || this.center) } @@ -209,7 +206,7 @@ class Feature { return field.startsWith('properties.') }) if (impactData) { - if (this.umap.currentFeature === this) { + if (this._umap.currentFeature === this) { this.view() } } @@ -217,7 +214,7 @@ class Feature { } edit(event) { - if (!this.umap.editEnabled || this.isReadOnly()) return + if (!this._umap.editEnabled || this.isReadOnly()) return const container = DomUtil.create('div', 'umap-feature-container') DomUtil.createTitle( container, @@ -256,12 +253,12 @@ class Feature { translate('Advanced actions') ) this.getAdvancedEditActions(advancedActions) - const onLoad = this.umap.editPanel.open({ content: container }) + const onLoad = this._umap.editPanel.open({ content: container }) onLoad.then(() => { builder.helpers['properties.name'].input.focus() }) - this.umap.editedFeature = this - if (!this.ui.isOnScreen(this.umap._leafletMap.getBounds())) this.zoomTo(event) + this._umap.editedFeature = this + if (!this.ui.isOnScreen(this._umap._leafletMap.getBounds())) this.zoomTo(event) } getAdvancedEditActions(container) { @@ -270,7 +267,7 @@ class Feature { ${translate('Delete')} `) button.addEventListener('click', () => { - this.confirmDelete().then(() => this.umap.editPanel.close()) + this.confirmDelete().then(() => this._umap.editPanel.close()) }) container.appendChild(button) } @@ -320,20 +317,25 @@ class Feature { endEdit() {} getDisplayName(fallback) { - if (fallback === undefined) fallback = this.datalayer.getName() const key = this.getOption('labelKey') || 'name' // Variables mode. - if (U.Utils.hasVar(key)) - return U.Utils.greedyTemplate(key, this.extendedProperties()) + if (Utils.hasVar(key)) { + return Utils.greedyTemplate(key, this.extendedProperties()) + } // Simple mode. - return this.properties[key] || this.properties.title || fallback + return ( + this.properties[key] || + this.properties.title || + fallback || + this.datalayer.getName() + ) } hasPopupFooter() { if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic) { return false } - return this.umap.getOption('displayPopupFooter') + return this._umap.getProperty('displayPopupFooter') } getPopupClass() { @@ -347,7 +349,7 @@ class Feature { } async confirmDelete() { - const confirmed = await this.umap.dialog.confirm( + const confirmed = await this._umap.dialog.confirm( translate('Are you sure you want to delete the feature?') ) if (confirmed) { @@ -359,7 +361,7 @@ class Feature { del(sync) { this.isDirty = true - this.umap._leafletMap.closePopup() + this._umap._leafletMap.closePopup() if (this.datalayer) { this.datalayer.removeFeature(this, sync) } @@ -417,34 +419,37 @@ class Feature { let value = fallback if (typeof this.staticOptions[option] !== 'undefined') { value = this.staticOptions[option] - } else if (U.Utils.usableOption(this.properties._umap_options, option)) { + } else if (Utils.usableOption(this.properties._umap_options, option)) { value = this.properties._umap_options[option] } else if (this.datalayer) { value = this.datalayer.getOption(option, this) } else { - value = this.umap.getOption(option) + value = this._umap.getProperty(option) } return value } - getDynamicOption(option, fallback) { - let value = this.getOption(option, fallback) + getDynamicOption(key, fallback) { + let value = this.getOption(key, fallback) // There is a variable inside. - if (U.Utils.hasVar(value)) { - value = U.Utils.greedyTemplate(value, this.properties, true) - if (U.Utils.hasVar(value)) value = this.umap.getDefaultOption(option) + if (Utils.hasVar(value)) { + value = Utils.greedyTemplate(value, this.properties, true) + if (Utils.hasVar(value)) value = SCHEMA[key]?.default } return value } zoomTo({ easing, latlng, callback } = {}) { - if (easing === undefined) easing = this.umap.getOption('easing') - if (callback) this.umap._leafletMap.once('moveend', callback.bind(this)) + if (easing === undefined) easing = this._umap.getProperty('easing') + if (callback) this._umap._leafletMap.once('moveend', callback.bind(this)) if (easing) { - this.umap._leafletMap.flyTo(this.center, this.getBestZoom()) + this._umap._leafletMap.flyTo(this.center, this.getBestZoom()) } else { latlng = latlng || this.center - this.umap._leafletMap.setView(latlng, this.getBestZoom() || this.umap._leafletMap.getZoom()) + this._umap._leafletMap.setView( + latlng, + this.getBestZoom() || this._umap._leafletMap.getZoom() + ) } } @@ -494,13 +499,9 @@ class Feature { return [U.ToggleEditAction, U.DeleteFeatureAction] } - getMap() { - return this.map - } - isFiltered() { const filterKeys = this.datalayer.getFilterKeys() - const filter = this.umap.browser.options.filter + const filter = this._umap.browser.options.filter if (filter && !this.matchFilter(filter, filterKeys)) return true if (!this.matchFacets()) return true return false @@ -525,10 +526,10 @@ class Feature { } matchFacets() { - const selected = this.umap.facets.selected + const selected = this._umap.facets.selected for (const [name, { type, min, max, choices }] of Object.entries(selected)) { let value = this.properties[name] - const parser = this.umap.facets.getParser(type) + const parser = this._umap.facets.getParser(type) value = parser(value) switch (type) { case 'date': @@ -562,7 +563,7 @@ class Feature { extendedProperties() { // Include context properties - const properties = this.umap.getGeoContext() + const properties = this._umap.getGeoContext() const locale = L.getLocale() if (locale) properties.locale = locale if (U.lang) properties.lang = U.lang @@ -612,10 +613,10 @@ class Feature { label: translate('Copy as GeoJSON'), action: () => { L.Util.copyToClipboard(JSON.stringify(this.toGeoJSON())) - this.umap.tooltip.open({ content: L._('✅ Copied!') }) + this._umap.tooltip.open({ content: L._('✅ Copied!') }) }, }) - if (this.umap.editEnabled && !this.isReadOnly()) { + if (this._umap.editEnabled && !this.isReadOnly()) { items = items.concat(this.getContextMenuEditItems(event)) } return items @@ -623,7 +624,7 @@ class Feature { getContextMenuEditItems() { let items = ['-'] - if (this.umap.editedFeature !== this) { + if (this._umap.editedFeature !== this) { items.push({ label: `${translate('Edit this feature')} (⇧+Click)`, action: () => this.edit(), @@ -631,7 +632,7 @@ class Feature { } items = items.concat( { - label: this.umap.help.displayLabel('EDIT_FEATURE_LAYER'), + label: this._umap.help.displayLabel('EDIT_FEATURE_LAYER'), action: () => this.datalayer.edit(), }, { @@ -648,8 +649,8 @@ class Feature { } export class Point extends Feature { - constructor(datalayer, geojson, id) { - super(datalayer, geojson, id) + constructor(umap, datalayer, geojson, id) { + super(umap, datalayer, geojson, id) this.staticOptions = { mainColor: 'color', className: 'marker', @@ -750,17 +751,17 @@ class Path extends Feature { } edit(event) { - if (this.umap.editEnabled) { + if (this._umap.editEnabled) { super.edit(event) if (!this.ui.editEnabled()) this.ui.makeGeometryEditable() } } _toggleEditing(event) { - if (this.umap.editEnabled) { + if (this._umap.editEnabled) { if (this.ui.editEnabled()) { this.endEdit() - this.umap.editPanel.close() + this._umap.editPanel.close() } else { this.edit(event) } @@ -787,7 +788,8 @@ class Path extends Feature { getBestZoom() { return ( - this.getOption('zoomTo') || this.umap._leafletMap.getBoundsZoom(this.bounds, true) + this.getOption('zoomTo') || + this._umap._leafletMap.getBoundsZoom(this.bounds, true) ) } @@ -827,13 +829,13 @@ class Path extends Feature { zoomTo({ easing, callback }) { // Use bounds instead of centroid for paths. - easing = easing || this.umap.getOption('easing') + easing = easing || this._umap.getProperty('easing') if (easing) { - this.umap._leafletMap.flyToBounds(this.bounds, this.getBestZoom()) + this._umap._leafletMap.flyToBounds(this.bounds, this.getBestZoom()) } else { - this.umap._leafletMap.fitBounds( + this._umap._leafletMap.fitBounds( this.bounds, - this.getBestZoom() || this.umap._leafletMap.getZoom() + this.getBestZoom() || this._umap._leafletMap.getZoom() ) } if (callback) callback.call(this) @@ -845,7 +847,7 @@ class Path extends Feature { label: translate('Display measure'), action: () => Alert.info(this.ui.getMeasure()), }) - if (this.umap.editEnabled && !this.isReadOnly() && this.isMulti()) { + if (this._umap.editEnabled && !this.isReadOnly() && this.isMulti()) { items.push(...this.getContextMenuMultiItems(event)) } return items @@ -876,11 +878,14 @@ class Path extends Feature { getContextMenuEditItems(event) { const items = super.getContextMenuEditItems(event) - if (this.map?.editedFeature !== this && this.isSameClass(this.umap.editedFeature)) { + if ( + this._umap?.editedFeature !== this && + this.isSameClass(this._umap.editedFeature) + ) { items.push({ label: translate('Transfer shape to edited feature'), action: () => { - this.transferShape(event.latlng, this.umap.editedFeature) + this.transferShape(event.latlng, this._umap.editedFeature) }, }) } @@ -897,8 +902,8 @@ class Path extends Feature { } export class LineString extends Path { - constructor(datalayer, geojson, id) { - super(datalayer, geojson, id) + constructor(umap, datalayer, geojson, id) { + super(umap, datalayer, geojson, id) this.staticOptions = { stroke: true, fill: false, @@ -982,8 +987,8 @@ export class LineString extends Path { } const a = toMerge[0] const b = toMerge[1] - const p1 = this.umap._leafletMap.latLngToContainerPoint(a[a.length - 1]) - const p2 = this.umap._leafletMap.latLngToContainerPoint(b[0]) + const p1 = this._umap._leafletMap.latLngToContainerPoint(a[a.length - 1]) + const p2 = this._umap._leafletMap.latLngToContainerPoint(b[0]) const tolerance = 5 // px on screen if (Math.abs(p1.x - p2.x) <= tolerance && Math.abs(p1.y - p2.y) <= tolerance) { a.pop() @@ -1027,7 +1032,7 @@ export class LineString extends Path { }) } else if (index === 0 || index === event.vertex.getLastIndex()) { items.push({ - label: this.umap.help.displayLabel('CONTINUE_LINE'), + label: this._umap.help.displayLabel('CONTINUE_LINE'), action: () => event.vertex.continue(), }) } @@ -1046,8 +1051,8 @@ export class LineString extends Path { } export class Polygon extends Path { - constructor(datalayer, geojson, id) { - super(datalayer, geojson, id) + constructor(umap, datalayer, geojson, id) { + super(umap, datalayer, geojson, id) this.staticOptions = { mainColor: 'fillColor', className: 'polygon', diff --git a/umap/static/umap/js/modules/data/layer.js b/umap/static/umap/js/modules/data/layer.js index 33098d2c..6ac45029 100644 --- a/umap/static/umap/js/modules/data/layer.js +++ b/umap/static/umap/js/modules/data/layer.js @@ -21,6 +21,7 @@ import { DataLayerPermissions } from '../permissions.js' import { Point, LineString, Polygon } from './features.js' import TableEditor from '../tableeditor.js' import { ServerStored } from '../saving.js' +import * as Schema from '../schema.js' export const LAYER_TYPES = [ DefaultLayer, @@ -37,9 +38,9 @@ const LAYER_MAP = LAYER_TYPES.reduce((acc, klass) => { }, {}) export class DataLayer extends ServerStored { - constructor(umap, data) { + constructor(umap, leafletMap, data) { super() - this.umap = umap + this._umap = umap this.sync = umap.sync_engine.proxy(this) this._index = Array() this._features = {} @@ -48,8 +49,9 @@ export class DataLayer extends ServerStored { this._loaded = false // Are layer metadata loaded this._dataloaded = false // Are layer data loaded - this.parentPane = this.umap._leafletMap.getPane('overlayPane') - this.pane = this.umap._leafletMap.createPane( + this._leafletMap = leafletMap + this.parentPane = this._leafletMap.getPane('overlayPane') + this.pane = this._leafletMap.createPane( `datalayer${stamp(this)}`, this.parentPane ) @@ -81,7 +83,7 @@ export class DataLayer extends ServerStored { } this.backupOptions() this.connectToMap() - this.permissions = new DataLayerPermissions(this) + this.permissions = new DataLayerPermissions(this._umap, this) if (!this.umap_id) { if (this.showAtLoad()) this.show() this.isDirty = true @@ -131,7 +133,7 @@ export class DataLayer extends ServerStored { for (const impact of impacts) { switch (impact) { case 'ui': - this.umap.onDataLayersChanged() + this._umap.onDataLayersChanged() break case 'data': if (fields.includes('options.type')) { @@ -156,8 +158,8 @@ export class DataLayer extends ServerStored { } autoLoaded() { - if (!this.umap.datalayersFromQueryString) return this.options.displayOnLoad - const datalayerIds = this.umap.datalayersFromQueryString + if (!this._umap.datalayersFromQueryString) return this.options.displayOnLoad + const datalayerIds = this._umap.datalayersFromQueryString let loadMe = datalayerIds.includes(this.umap_id.toString()) if (this.options.old_id) { loadMe = loadMe || datalayerIds.includes(this.options.old_id.toString()) @@ -195,7 +197,7 @@ export class DataLayer extends ServerStored { const visible = this.isVisible() if (this.layer) this.layer.clearLayers() // delete this.layer? - if (visible) this.umap._leafletMap.removeLayer(this.layer) + if (visible) this._leafletMap.removeLayer(this.layer) const Class = LAYER_MAP[this.options.type] || DefaultLayer this.layer = new Class(this) // Rendering layer changed, so let's force reset the feature rendering too. @@ -216,7 +218,7 @@ export class DataLayer extends ServerStored { if (!this.umap_id) return if (this._loading) return this._loading = true - const [geojson, response, error] = await this.umap.server.get(this._dataUrl()) + const [geojson, response, error] = await this._umap.server.get(this._dataUrl()) if (!error) { this._reference_version = response.headers.get('X-Datalayer-Version') // FIXME: for now this property is set dynamically from backend @@ -237,7 +239,7 @@ export class DataLayer extends ServerStored { dataChanged() { if (!this.hasDataLoaded()) return - this.umap.onDataLayersChanged() + this._umap.onDataLayersChanged() this.layer.dataChanged() } @@ -278,14 +280,14 @@ export class DataLayer extends ServerStored { reindex() { const features = Object.values(this._features) - Utils.sortFeatures(features, this.umap.getOption('sortKey'), U.lang) + Utils.sortFeatures(features, this._umap.getProperty('sortKey'), U.lang) this._index = features.map((feature) => stamp(feature)) } showAtZoom() { const from = Number.parseInt(this.options.fromZoom, 10) const to = Number.parseInt(this.options.toZoom, 10) - const zoom = this.umap._leafletMap.getZoom() + const zoom = this._leafletMap.getZoom() return !((!Number.isNaN(from) && zoom < from) || (!Number.isNaN(to) && zoom > to)) } @@ -297,14 +299,14 @@ export class DataLayer extends ServerStored { if (!this.isRemoteLayer()) return if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return if (!this.isVisible()) return - let url = this.umap.localizeUrl(this.options.remoteData.url) + let url = this._umap.renderUrl(this.options.remoteData.url) if (this.options.remoteData.proxy) { - url = this.umap.proxyUrl(url, this.options.remoteData.ttl) + url = this._umap.proxyUrl(url, this.options.remoteData.ttl) } - const response = await this.umap.request.get(url) + const response = await this._umap.request.get(url) if (response?.ok) { this.clear() - this.umap.formatter + this._umap.formatter .parse(await response.text(), this.options.remoteData.format) .then((geojson) => this.fromGeoJSON(geojson)) } @@ -344,23 +346,23 @@ export class DataLayer extends ServerStored { connectToMap() { const id = stamp(this) - if (!this.umap.datalayers[id]) { - this.umap.datalayers[id] = this + if (!this._umap.datalayers[id]) { + this._umap.datalayers[id] = this } - if (!this.umap.datalayersIndex.includes(this)) { - this.umap.datalayersIndex.push(this) + if (!this._umap.datalayersIndex.includes(this)) { + this._umap.datalayersIndex.push(this) } - this.umap.onDataLayersChanged() + this._umap.onDataLayersChanged() } _dataUrl() { - let url = this.umap.urls.get('datalayer_view', { + let url = this._umap.urls.get('datalayer_view', { pk: this.umap_id, - map_id: this.umap.properties.umap_id, + map_id: this._umap.properties.umap_id, }) // No browser cache for owners/editors. - if (this.umap.hasEditMode()) url = `${url}?${Date.now()}` + if (this._umap.hasEditMode()) url = `${url}?${Date.now()}` return url } @@ -387,7 +389,7 @@ export class DataLayer extends ServerStored { this._index.push(id) this._features[id] = feature this.indexProperties(feature) - this.umap.featuresIndex[feature.getSlug()] = feature + this._umap.featuresIndex[feature.getSlug()] = feature this.showFeature(feature) this.dataChanged() } @@ -396,7 +398,7 @@ export class DataLayer extends ServerStored { const id = stamp(feature) if (sync !== false) feature.sync.delete() this.hideFeature(feature) - delete this.umap.featuresIndex[feature.getSlug()] + delete this._umap.featuresIndex[feature.getSlug()] feature.disconnectFromDataLayer(this) this._index.splice(this._index.indexOf(id), 1) delete this._features[id] @@ -448,7 +450,7 @@ export class DataLayer extends ServerStored { ? geojson : geojson.features || geojson.geometries if (!collection) return - Utils.sortFeatures(collection, this.umap.getOption('sortKey'), U.lang) + Utils.sortFeatures(collection, this._umap.getProperty('sortKey'), U.lang) for (const feature of collection) { this.makeFeature(feature, sync) } @@ -462,15 +464,15 @@ export class DataLayer extends ServerStored { switch (geometry.type) { case 'Point': // FIXME: deal with MultiPoint - feature = new Point(this, geojson, id) + feature = new Point(this._umap, this, geojson, id) break case 'MultiLineString': case 'LineString': - feature = new LineString(this, geojson, id) + feature = new LineString(this._umap, this, geojson, id) break case 'MultiPolygon': case 'Polygon': - feature = new Polygon(this, geojson, id) + feature = new Polygon(this._umap, this, geojson, id) break default: console.log(geojson) @@ -488,7 +490,7 @@ export class DataLayer extends ServerStored { } async importRaw(raw, format) { - this.umap.formatter + this._umap.formatter .parse(raw, format) .then((geojson) => this.addData(geojson)) .then(() => this.zoomTo()) @@ -509,35 +511,35 @@ export class DataLayer extends ServerStored { } async importFromUrl(uri, type) { - uri = this.umap.localizeUrl(uri) - const response = await this.umap.request.get(uri) + uri = this._umap.renderUrl(uri) + const response = await this._umap.request.get(uri) if (response?.ok) { this.importRaw(await response.text(), type) } } getColor() { - return this.options.color || this.umap.getOption('color') + return this.options.color || this._umap.getProperty('color') } getDeleteUrl() { - return this.umap.urls.get('datalayer_delete', { + return this._umap.urls.get('datalayer_delete', { pk: this.umap_id, - map_id: this.umap.properties.umap_id, + map_id: this._umap.properties.umap_id, }) } getVersionsUrl() { - return this.umap.urls.get('datalayer_versions', { + return this._umap.urls.get('datalayer_versions', { pk: this.umap_id, - map_id: this.umap.properties.umap_id, + map_id: this._umap.properties.umap_id, }) } getVersionUrl(name) { - return this.umap.urls.get('datalayer_version', { + return this._umap.urls.get('datalayer_version', { pk: this.umap_id, - map_id: this.umap.properties.umap_id, + map_id: this._umap.properties.umap_id, name: name, }) } @@ -558,17 +560,17 @@ export class DataLayer extends ServerStored { options.name = translate('Clone of {name}', { name: this.options.name }) delete options.id const geojson = Utils.CopyJSON(this._geojson) - const datalayer = this.umap.createDataLayer(options) + const datalayer = this._umap.createDataLayer(options) datalayer.fromGeoJSON(geojson) return datalayer } erase() { this.hide() - this.umap.datalayersIndex.splice(this.getRank(), 1) + this._umap.datalayersIndex.splice(this.getRank(), 1) this.parentPane.removeChild(this.pane) - this.umap.onDataLayersChanged() - this.layer.onDelete(this.umap._leafletMap) + this._umap.onDataLayersChanged() + this.layer.onDelete(this._leafletMap) this.propagateDelete() this._leaflet_events_bk = this._leaflet_events this.clear() @@ -599,7 +601,7 @@ export class DataLayer extends ServerStored { } edit() { - if (!this.umap.editEnabled || !this.isLoaded()) { + if (!this._umap.editEnabled || !this.isLoaded()) { return } const container = DomUtil.create('div', 'umap-layer-properties-container') @@ -633,7 +635,7 @@ export class DataLayer extends ServerStored { DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers') let builder = new U.FormBuilder(this, metadataFields, { callback(e) { - this.umap.onDataLayersChanged() + this._umap.onDataLayersChanged() if (e.helper.field === 'options.type') { this.edit() } @@ -744,7 +746,7 @@ export class DataLayer extends ServerStored { }, ], ] - if (this.umap.properties.urls.ajax_proxy) { + if (this._umap.properties.urls.ajax_proxy) { remoteDataFields.push([ 'options.remoteData.proxy', { @@ -770,7 +772,7 @@ export class DataLayer extends ServerStored { this ) - if (this.umap.properties.urls.datalayer_versions) + if (this._umap.properties.urls.datalayer_versions) this.buildVersionsFieldset(container) const advancedActions = DomUtil.createFieldset( @@ -784,7 +786,7 @@ export class DataLayer extends ServerStored { `) deleteButton.addEventListener('click', () => { this._delete() - this.umap.editPanel.close() + this._umap.editPanel.close() }) advancedButtons.appendChild(deleteButton) @@ -823,9 +825,9 @@ export class DataLayer extends ServerStored { // Fixme: remove me when this is merged and released // https://github.com/Leaflet/Leaflet/pull/9052 DomEvent.disableClickPropagation(backButton) - DomEvent.on(backButton, 'click', this.umap.editDatalayers, this.umap) + DomEvent.on(backButton, 'click', this._umap.editDatalayers, this._umap) - this.umap.editPanel.open({ + this._umap.editPanel.open({ content: container, actions: [backButton], }) @@ -846,7 +848,7 @@ export class DataLayer extends ServerStored { if (this.layer?.defaults?.[option]) { return this.layer.defaults[option] } - return this.umap.getOption(option, feature) + return this._umap.getProperty(option, feature) } async buildVersionsFieldset(container) { @@ -867,7 +869,7 @@ export class DataLayer extends ServerStored { const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), { async callback() { - const [{ versions }, response, error] = await this.umap.server.get( + const [{ versions }, response, error] = await this._umap.server.get( this.getVersionsUrl() ) if (!error) versions.forEach(appendVersion) @@ -877,11 +879,11 @@ export class DataLayer extends ServerStored { } async restore(version) { - if (!this.umap.editEnabled) return - this.umap.dialog + if (!this._umap.editEnabled) return + this._umap.dialog .confirm(translate('Are you sure you want to restore this version?')) .then(async () => { - const [geojson, response, error] = await this.umap.server.get( + const [geojson, response, error] = await this._umap.server.get( this.getVersionUrl(version) ) if (!error) { @@ -902,13 +904,13 @@ export class DataLayer extends ServerStored { } async show() { - this.umap._leafletMap.addLayer(this.layer) + this._leafletMap.addLayer(this.layer) if (!this.isLoaded()) await this.fetchData() this.propagateShow() } hide() { - this.umap._leafletMap.removeLayer(this.layer) + this._leafletMap.removeLayer(this.layer) this.propagateHide() } @@ -925,7 +927,7 @@ export class DataLayer extends ServerStored { const bounds = this.layer.getBounds() if (bounds.isValid()) { const options = { maxZoom: this.getOption('zoomTo') } - this.umap._leafletMap.fitBounds(bounds, options) + this._leafletMap.fitBounds(bounds, options) } } @@ -956,7 +958,7 @@ export class DataLayer extends ServerStored { } isVisible() { - return Boolean(this.layer && this.umap._leafletMap.hasLayer(this.layer)) + return Boolean(this.layer && this._leafletMap.hasLayer(this.layer)) } getFeatureByIndex(index) { @@ -993,7 +995,7 @@ export class DataLayer extends ServerStored { getPreviousBrowsable() { let id = this.getRank() let next - const index = this.umap.datalayersIndex + const index = this._umap.datalayersIndex while (((id = index[++id] ? id : 0), (next = index[id]))) { if (next === this || next.canBrowse()) break } @@ -1003,7 +1005,7 @@ export class DataLayer extends ServerStored { getNextBrowsable() { let id = this.getRank() let prev - const index = this.umap.datalayersIndex + const index = this._umap.datalayersIndex while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) { if (prev === this || prev.canBrowse()) break } @@ -1019,7 +1021,7 @@ export class DataLayer extends ServerStored { } getRank() { - return this.umap.datalayersIndex.indexOf(this) + return this._umap.datalayersIndex.indexOf(this) } isReadOnly() { @@ -1046,8 +1048,8 @@ export class DataLayer extends ServerStored { // Filename support is shaky, don't do it for now. const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' }) formData.append('geojson', blob) - const saveUrl = this.umap.urls.get('datalayer_save', { - map_id: this.umap.properties.umap_id, + const saveUrl = this._umap.urls.get('datalayer_save', { + map_id: this._umap.properties.umap_id, pk: this.umap_id, }) const headers = this._reference_version @@ -1059,7 +1061,7 @@ export class DataLayer extends ServerStored { } async _trySave(url, headers, formData) { - const [data, response, error] = await this.umap.server.post(url, headers, formData) + const [data, response, error] = await this._umap.server.post(url, headers, formData) if (error) { if (response && response.status === 412) { AlertConflict.error( @@ -1075,7 +1077,7 @@ export class DataLayer extends ServerStored { // Call the main save, in case something else needs to be saved // as the conflict stopped the saving flow - await this.umap.saveAll() + await this._umap.saveAll() } } ) @@ -1104,23 +1106,19 @@ export class DataLayer extends ServerStored { async saveDelete() { if (this.umap_id) { - await this.umap.server.post(this.getDeleteUrl()) + await this._umap.server.post(this.getDeleteUrl()) } - delete this.umap.datalayers[stamp(this)] + delete this._umap.datalayers[stamp(this)] return true } - getMap() { - return this.map - } - getName() { return this.options.name || translate('Untitled layer') } tableEdit() { if (!this.isVisible()) return - const editor = new TableEditor(this) + const editor = new TableEditor(this._umap, this, this._leafletMap) editor.open() } @@ -1128,9 +1126,9 @@ export class DataLayer extends ServerStored { // This keys will be used to filter feature from the browser text input. // By default, it will we use the "name" property, which is also the one used as label in the features list. // When map owner has configured another label or sort key, we try to be smart and search in the same keys. - if (this.umap.properties.filterKey) return this.umap.properties.filterKey + if (this._umap.properties.filterKey) return this._umap.properties.filterKey if (this.getOption('labelKey')) return this.getOption('labelKey') - if (this.umap.properties.sortKey) return this.umap.properties.sortKey + if (this._umap.properties.sortKey) return this._umap.properties.sortKey return 'displayName' } @@ -1181,7 +1179,7 @@ export class DataLayer extends ServerStored { 'click', function () { if (!this.isVisible()) return - this.umap.dialog + this._umap.dialog .confirm(translate('Are you sure you want to delete this layer?')) .then(() => { this._delete() diff --git a/umap/static/umap/js/modules/facets.js b/umap/static/umap/js/modules/facets.js index 0debaaae..da896af1 100644 --- a/umap/static/umap/js/modules/facets.js +++ b/umap/static/umap/js/modules/facets.js @@ -4,7 +4,7 @@ import * as Utils from './utils.js' export default class Facets { constructor(umap) { - this.umap = umap + this._umap = umap this.selected = {} } @@ -24,7 +24,7 @@ export default class Facets { this.selected[name] = selected } - this.umap.eachBrowsableDataLayer((datalayer) => { + this._umap.eachBrowsableDataLayer((datalayer) => { datalayer.eachFeature((feature) => { for (const name of names) { let value = feature.properties[name] @@ -108,8 +108,8 @@ export default class Facets { const defaultType = 'checkbox' const allowedTypes = [defaultType, 'radio', 'number', 'date', 'datetime'] const defined = new Map() - if (!this.umap.properties.facetKey) return defined - return (this.umap.properties.facetKey || '').split(',').reduce((acc, curr) => { + if (!this._umap.properties.facetKey) return defined + return (this._umap.properties.facetKey || '').split(',').reduce((acc, curr) => { let [name, label, type] = curr.split('|') type = allowedTypes.includes(type) ? type : defaultType acc.set(name, { label: label || name, type: type }) @@ -146,15 +146,15 @@ export default class Facets { const defined = this.getDefined() if (!defined.has(property)) { defined.set(property, { label, type }) - this.umap.properties.facetKey = this.dumps(defined) - this.umap.isDirty = true + this._umap.properties.facetKey = this.dumps(defined) + this._umap.isDirty = true } } remove(property) { const defined = this.getDefined() defined.delete(property) - this.umap.properties.facetKey = this.dumps(defined) - this.umap.isDirty = true + this._umap.properties.facetKey = this.dumps(defined) + this._umap.isDirty = true } } diff --git a/umap/static/umap/js/modules/importer.js b/umap/static/umap/js/modules/importer.js index 515c9a17..6d959524 100644 --- a/umap/static/umap/js/modules/importer.js +++ b/umap/static/umap/js/modules/importer.js @@ -49,7 +49,7 @@ const TEMPLATE = ` export default class Importer { constructor(umap) { - this.umap = umap + this._umap = umap this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap'] this.IMPORTERS = [] this.loadImporters() @@ -57,9 +57,9 @@ export default class Importer { } loadImporters() { - for (const [name, config] of Object.entries(this.umap.properties.importers || {})) { + for (const [name, config] of Object.entries(this._umap.properties.importers || {})) { const register = (mod) => { - this.IMPORTERS.push(new mod.Importer(this.umap, config)) + this.IMPORTERS.push(new mod.Importer(this._umap, config)) } // We need to have explicit static paths for Django's collectstatic with hashes. switch (name) { @@ -139,8 +139,8 @@ export default class Importer { get layer() { return ( - this.umap.datalayers[this.layerId] || - this.umap.createDataLayer({ name: this.layerName }) + this._umap.datalayers[this.layerId] || + this._umap.createDataLayer({ name: this.layerName }) ) } @@ -167,7 +167,7 @@ export default class Importer { textContent: type, }) } - this.umap.help.parse(this.container) + this._umap.help.parse(this.container) DomEvent.on(this.qs('[name=submit]'), 'click', this.submit, this) DomEvent.on(this.qs('[type=file]'), 'change', this.onFileChange, this) for (const element of this.container.querySelectorAll('[onchange]')) { @@ -206,7 +206,7 @@ export default class Importer { this.layerName = null const layerSelect = this.qs('[name="layer-id"]') layerSelect.innerHTML = '' - this.umap.eachDataLayerReverse((datalayer) => { + this._umap.eachDataLayerReverse((datalayer) => { if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) { DomUtil.element({ tagName: 'option', @@ -227,7 +227,7 @@ export default class Importer { open() { if (!this.container) this.build() - const onLoad = this.umap.editPanel.open({ content: this.container }) + const onLoad = this._umap.editPanel.open({ content: this.container }) onLoad.then(() => this.onLoad()) } @@ -254,12 +254,12 @@ export default class Importer { try { if (this.files.length) { for (const file of this.files) { - this.umap.processFileToImport(file, null, 'umap') + this._umap.processFileToImport(file, null, 'umap') } } else if (this.raw) { - this.umap.importRaw(this.raw) + this._umap.importRaw(this.raw) } else if (this.url) { - this.umap.importFromUrl(this.url, this.format) + this._umap.importFromUrl(this.url, this.format) } } catch (e) { Alert.error(translate('Invalid umap data')) @@ -281,7 +281,7 @@ export default class Importer { url: this.url, format: this.format, } - if (this.umap.properties.urls.ajax_proxy) { + if (this._umap.properties.urls.ajax_proxy) { layer.options.remoteData.proxy = true layer.options.remoteData.ttl = SCHEMA.ttl.default } @@ -299,7 +299,7 @@ export default class Importer { if (this.clear) layer.empty() if (this.files.length) { for (const file of this.files) { - this.umap.processFileToImport(file, layer, this.format) + this._umap.processFileToImport(file, layer, this.format) } } else if (this.raw) { layer.importRaw(this.raw, this.format) diff --git a/umap/static/umap/js/modules/permissions.js b/umap/static/umap/js/modules/permissions.js index d636836d..020ba0f7 100644 --- a/umap/static/umap/js/modules/permissions.js +++ b/umap/static/umap/js/modules/permissions.js @@ -9,13 +9,13 @@ import * as Utils from './utils.js' export class MapPermissions extends ServerStored { constructor(umap) { super() - this.setOptions(umap.properties.permissions) - this.umap = umap + this.setProperties(umap.properties.permissions) + this._umap = umap this._isDirty = false } - setOptions(options) { - this.options = Object.assign( + setProperties(properties) { + this.properties = Object.assign( { owner: null, team: null, @@ -23,42 +23,42 @@ export class MapPermissions extends ServerStored { share_status: null, edit_status: null, }, - options + properties ) } isOwner() { - return Boolean(this.umap.properties.user?.is_owner) + return Boolean(this._umap.properties.user?.is_owner) } isAnonymousMap() { - return !this.umap.properties.permissions.owner + return !this._umap.properties.permissions.owner } _editAnonymous(container) { const fields = [] if (this.isOwner()) { fields.push([ - 'options.edit_status', + 'properties.edit_status', { handler: 'IntSelect', label: translate('Who can edit'), - selectOptions: this.umap.properties.edit_statuses, + selectOptions: this._umap.properties.edit_statuses, }, ]) const builder = new U.FormBuilder(this, fields) const form = builder.build() container.appendChild(form) - if (this.options.anonymous_edit_url) { + if (this.properties.anonymous_edit_url) { DomUtil.createCopiableInput( container, translate('Secret edit link:'), - this.options.anonymous_edit_url + this.properties.anonymous_edit_url ) } - if (this.umap.properties.user?.id) { + if (this._umap.properties.user?.id) { // We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map. // Note: real check is made on the back office anyway. const advancedActions = DomUtil.createFieldset( @@ -86,38 +86,38 @@ export class MapPermissions extends ServerStored { container.appendChild(fieldset) if (this.isOwner()) { topFields.push([ - 'options.edit_status', + 'properties.edit_status', { handler: 'IntSelect', label: translate('Who can edit'), - selectOptions: this.umap.properties.edit_statuses, + selectOptions: this._umap.properties.edit_statuses, }, ]) topFields.push([ - 'options.share_status', + 'properties.share_status', { handler: 'IntSelect', label: translate('Who can view'), - selectOptions: this.umap.properties.share_statuses, + selectOptions: this._umap.properties.share_statuses, }, ]) collaboratorsFields.push([ - 'options.owner', + 'properties.owner', { handler: 'ManageOwner', label: translate("Map's owner") }, ]) - if (this.umap.properties.user?.teams?.length) { + if (this._umap.properties.user?.teams?.length) { collaboratorsFields.push([ - 'options.team', + 'properties.team', { handler: 'ManageTeam', label: translate('Attach map to a team'), - teams: this.umap.properties.user.teams, + teams: this._umap.properties.user.teams, }, ]) } } collaboratorsFields.push([ - 'options.editors', + 'properties.editors', { handler: 'ManageEditors', label: translate("Map's editors") }, ]) @@ -136,20 +136,20 @@ export class MapPermissions extends ServerStored { } _editDatalayers(container) { - if (this.umap.hasLayers()) { + if (this._umap.hasLayers()) { const fieldset = Utils.loadTemplate( `
${translate('Datalayers')}
` ) container.appendChild(fieldset) - this.umap.eachDataLayer((datalayer) => { + this._umap.eachDataLayer((datalayer) => { datalayer.permissions.edit(fieldset) }) } } edit() { - if (this.umap.properties.editMode !== 'advanced') return - if (!this.umap.properties.umap_id) { + if (this._umap.properties.editMode !== 'advanced') return + if (!this._umap.properties.umap_id) { Alert.info(translate('Please save the map first')) return } @@ -158,35 +158,35 @@ export class MapPermissions extends ServerStored { if (this.isAnonymousMap()) this._editAnonymous(container) else this._editWithOwner(container) this._editDatalayers(container) - this.umap.editPanel.open({ content: container, className: 'dark' }) + this._umap.editPanel.open({ content: container, className: 'dark' }) } async attach() { - const [data, response, error] = await this.umap.server.post(this.getAttachUrl()) + const [data, response, error] = await this._umap.server.post(this.getAttachUrl()) if (!error) { - this.options.owner = this.umap.properties.user + this.properties.owner = this._umap.properties.user Alert.success(translate('Map has been attached to your account')) - this.umap.editPanel.close() + this._umap.editPanel.close() } } async save() { if (!this.isDirty) return const formData = new FormData() - if (!this.isAnonymousMap() && this.options.editors) { - const editors = this.options.editors.map((u) => u.id) - for (let i = 0; i < this.options.editors.length; i++) - formData.append('editors', this.options.editors[i].id) + if (!this.isAnonymousMap() && this.properties.editors) { + const editors = this.properties.editors.map((u) => u.id) + for (let i = 0; i < this.properties.editors.length; i++) + formData.append('editors', this.properties.editors[i].id) } if (this.isOwner() || this.isAnonymousMap()) { - formData.append('edit_status', this.options.edit_status) + formData.append('edit_status', this.properties.edit_status) } if (this.isOwner()) { - formData.append('owner', this.options.owner?.id) - formData.append('team', this.options.team?.id || '') - formData.append('share_status', this.options.share_status) + formData.append('owner', this.properties.owner?.id) + formData.append('team', this.properties.team?.id || '') + formData.append('share_status', this.properties.share_status) } - const [data, response, error] = await this.umap.server.post( + const [data, response, error] = await this._umap.server.post( this.getUrl(), {}, formData @@ -198,38 +198,39 @@ export class MapPermissions extends ServerStored { } getUrl() { - return this.umap.urls.get('map_update_permissions', { - map_id: this.umap.properties.umap_id, + return this._umap.urls.get('map_update_permissions', { + map_id: this._umap.properties.umap_id, }) } getAttachUrl() { - return this.umap.urls.get('map_attach_owner', { - map_id: this.umap.properties.umap_id, + return this._umap.urls.get('map_attach_owner', { + map_id: this._umap.properties.umap_id, }) } commit() { - this.umap.properties.permissions = Object.assign( + this._umap.properties.permissions = Object.assign( {}, - this.umap.properties.permissions, - this.options + this._umap.properties.permissions, + this.properties ) } getShareStatusDisplay() { - if (this.umap.properties.share_statuses) { - return Object.fromEntries(this.umap.properties.share_statuses)[ - this.options.share_status + if (this._umap.properties.share_statuses) { + return Object.fromEntries(this._umap.properties.share_statuses)[ + this.properties.share_status ] } } } export class DataLayerPermissions extends ServerStored { - constructor(datalayer) { + constructor(umap, datalayer) { super() - this.options = Object.assign( + this._umap = umap + this.properties = Object.assign( { edit_status: null, }, @@ -239,20 +240,16 @@ export class DataLayerPermissions extends ServerStored { this.datalayer = datalayer } - get umap() { - return this.datalayer.umap - } - edit(container) { const fields = [ [ - 'options.edit_status', + 'properties.edit_status', { handler: 'IntSelect', label: translate('Who can edit "{layer}"', { layer: this.datalayer.getName(), }), - selectOptions: this.umap.properties.datalayer_edit_statuses, + selectOptions: this._umap.properties.datalayer_edit_statuses, }, ], ] @@ -264,8 +261,8 @@ export class DataLayerPermissions extends ServerStored { } getUrl() { - return this.umap.urls.get('datalayer_permissions', { - map_id: this.umap.properties.umap_id, + return this._umap.urls.get('datalayer_permissions', { + map_id: this._umap.properties.umap_id, pk: this.datalayer.umap_id, }) } @@ -273,8 +270,8 @@ export class DataLayerPermissions extends ServerStored { async save() { if (!this.isDirty) return const formData = new FormData() - formData.append('edit_status', this.options.edit_status) - const [data, response, error] = await this.umap.server.post( + formData.append('edit_status', this.properties.edit_status) + const [data, response, error] = await this._umap.server.post( this.getUrl(), {}, formData @@ -289,7 +286,7 @@ export class DataLayerPermissions extends ServerStored { this.datalayer.options.permissions = Object.assign( {}, this.datalayer.options.permissions, - this.options + this.properties ) } } diff --git a/umap/static/umap/js/modules/rendering/layers/base.js b/umap/static/umap/js/modules/rendering/layers/base.js index 2a7c5085..0a112a35 100644 --- a/umap/static/umap/js/modules/rendering/layers/base.js +++ b/umap/static/umap/js/modules/rendering/layers/base.js @@ -5,20 +5,20 @@ import * as Utils from '../../utils.js' export const LayerMixin = { browsable: true, - onInit: function (map) { - if (this.datalayer.autoLoaded()) map.on('zoomend', this.onZoomEnd, this) + onInit: function (leafletMap) { + if (this.datalayer.autoLoaded()) leafletMap.on('zoomend', this.onZoomEnd, this) }, - onDelete: function (map) { - map.off('zoomend', this.onZoomEnd, this) + onDelete: function (leafletMap) { + leafletMap.off('zoomend', this.onZoomEnd, this) }, - onAdd: function (map) { - map.on('moveend', this.onMoveEnd, this) + onAdd: function (leafletMap) { + leafletMap.on('moveend', this.onMoveEnd, this) }, - onRemove: function (map) { - map.off('moveend', this.onMoveEnd, this) + onRemove: function (leafletMap) { + leafletMap.off('moveend', this.onMoveEnd, this) }, getType: function () { @@ -73,17 +73,17 @@ export const Default = FeatureGroup.extend({ initialize: function (datalayer) { this.datalayer = datalayer FeatureGroup.prototype.initialize.call(this) - LayerMixin.onInit.call(this, this.datalayer.umap._leafletMap) + LayerMixin.onInit.call(this, this.datalayer._leafletMap) }, - onAdd: function (map) { - LayerMixin.onAdd.call(this, map) - return FeatureGroup.prototype.onAdd.call(this, map) + onAdd: function (leafletMap) { + LayerMixin.onAdd.call(this, leafletMap) + return FeatureGroup.prototype.onAdd.call(this, leafletMap) }, - onRemove: function (map) { - LayerMixin.onRemove.call(this, map) - return FeatureGroup.prototype.onRemove.call(this, map) + onRemove: function (leafletMap) { + LayerMixin.onRemove.call(this, leafletMap) + return FeatureGroup.prototype.onRemove.call(this, leafletMap) }, }) diff --git a/umap/static/umap/js/modules/rendering/layers/classified.js b/umap/static/umap/js/modules/rendering/layers/classified.js index b5bb509e..52f9127d 100644 --- a/umap/static/umap/js/modules/rendering/layers/classified.js +++ b/umap/static/umap/js/modules/rendering/layers/classified.js @@ -20,7 +20,7 @@ const ClassifiedMixin = { } this.ensureOptions(this.datalayer.options[key]) FeatureGroup.prototype.initialize.call(this, [], this.datalayer.options[key]) - LayerMixin.onInit.call(this, this.datalayer.umap._leafletMap) + LayerMixin.onInit.call(this, this.datalayer._leafletMap) }, ensureOptions: () => {}, diff --git a/umap/static/umap/js/modules/rendering/layers/cluster.js b/umap/static/umap/js/modules/rendering/layers/cluster.js index 0ce8249d..227ea82c 100644 --- a/umap/static/umap/js/modules/rendering/layers/cluster.js +++ b/umap/static/umap/js/modules/rendering/layers/cluster.js @@ -40,7 +40,7 @@ export const Cluster = L.MarkerClusterGroup.extend({ options.maxClusterRadius = this.datalayer.options.cluster.radius } L.MarkerClusterGroup.prototype.initialize.call(this, options) - LayerMixin.onInit.call(this, this.datalayer.umap._leafletMap) + LayerMixin.onInit.call(this, this.datalayer._leafletMap) this._markerCluster = MarkerCluster this._layers = [] }, diff --git a/umap/static/umap/js/modules/rendering/layers/heat.js b/umap/static/umap/js/modules/rendering/layers/heat.js index 24e6b411..422a7919 100644 --- a/umap/static/umap/js/modules/rendering/layers/heat.js +++ b/umap/static/umap/js/modules/rendering/layers/heat.js @@ -21,7 +21,7 @@ export const Heat = L.HeatLayer.extend({ initialize: function (datalayer) { this.datalayer = datalayer L.HeatLayer.prototype.initialize.call(this, [], this.datalayer.options.heat) - LayerMixin.onInit.call(this, this.datalayer.umap._leafletMap) + LayerMixin.onInit.call(this, this.datalayer._leafletMap) if (!Utils.isObject(this.datalayer.options.heat)) { this.datalayer.options.heat = {} } diff --git a/umap/static/umap/js/modules/rendering/map.js b/umap/static/umap/js/modules/rendering/map.js index b50b0275..9c4c5bfa 100644 --- a/umap/static/umap/js/modules/rendering/map.js +++ b/umap/static/umap/js/modules/rendering/map.js @@ -39,7 +39,7 @@ const ControlsMixin = { initControls: function () { this._controls = {} - if (this.umap.hasEditMode() && !this.options.noControl) { + if (this._umap.hasEditMode() && !this.options.noControl) { new U.EditControl(this).addTo(this) new U.DrawToolbar({ map: this }).addTo(this) @@ -60,8 +60,8 @@ const ControlsMixin = { zoomInTitle: translate('Zoom in'), zoomOutTitle: translate('Zoom out'), }) - this._controls.datalayers = new U.DataLayersControl(this.umap) - this._controls.caption = new U.CaptionControl(this.umap) + this._controls.datalayers = new U.DataLayersControl(this._umap) + this._controls.caption = new U.CaptionControl(this._umap) this._controls.locate = new U.Locate(this, { strings: { title: translate('Center map on your location'), @@ -82,9 +82,9 @@ const ControlsMixin = { }, }) this._controls.search = new U.SearchControl() - this._controls.embed = new Control.Embed(this.umap) + this._controls.embed = new Control.Embed(this._umap) this._controls.tilelayersChooser = new U.TileLayerChooser(this) - if (this.options.user?.id) this._controls.star = new U.StarControl(this.umap) + if (this.options.user?.id) this._controls.star = new U.StarControl(this._umap) this._controls.editinosm = new Control.EditInOSM({ position: 'topleft', widgetOptions: { @@ -99,7 +99,7 @@ const ControlsMixin = { this._controls.permanentCredit = new U.PermanentCreditsControl(this) if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable() else this.scrollWheelZoom.disable() - this.umap.drop = new U.DropControl(this) + this._umap.drop = new U.DropControl(this) this._controls.tilelayers = new U.TileLayerControl(this) }, @@ -119,13 +119,13 @@ const ControlsMixin = { if (this.selectedTilelayer) { this._controls.miniMap = new Control.MiniMap(this.selectedTilelayer, { aimingRectOptions: { - color: this.umap.getOption('color'), - fillColor: this.umap.getOption('fillColor'), - stroke: this.umap.getOption('stroke'), - fill: this.umap.getOption('fill'), - weight: this.umap.getOption('weight'), - opacity: this.umap.getOption('opacity'), - fillOpacity: this.umap.getOption('fillOpacity'), + color: this._umap.getProperty('color'), + fillColor: this._umap.getProperty('fillColor'), + stroke: this._umap.getProperty('stroke'), + fill: this._umap.getProperty('fill'), + weight: this._umap.getProperty('weight'), + opacity: this._umap.getProperty('opacity'), + fillOpacity: this._umap.getProperty('fillOpacity'), }, }).addTo(this) this._controls.miniMap._miniMap.invalidateSize() @@ -133,7 +133,7 @@ const ControlsMixin = { }) } for (const name of this.HIDDABLE_CONTROLS) { - const status = this.umap.getOption(`${name}Control`) + const status = this._umap.getProperty(`${name}Control`) if (status === false) continue const control = this._controls[name] if (!control) continue @@ -144,10 +144,10 @@ const ControlsMixin = { DomUtil.removeClass(control._container, 'display-on-more') } } - if (this.umap.getOption('permanentCredit')) + if (this._umap.getProperty('permanentCredit')) this._controls.permanentCredit.addTo(this) - if (this.umap.getOption('moreControl')) this._controls.more.addTo(this) - if (this.umap.getOption('scaleControl')) this._controls.scale.addTo(this) + if (this._umap.getProperty('moreControl')) this._controls.more.addTo(this) + if (this._umap.getProperty('scaleControl')) this._controls.scale.addTo(this) this._controls.tilelayers.setLayers() }, @@ -163,7 +163,7 @@ const ControlsMixin = { DomUtil.createLink('', logo, 'uMap', '/', null, translate('Go to the homepage')) const nameButton = DomUtil.createButton('map-name', leftContainer, '') DomEvent.on(nameButton, 'mouseover', () => { - this.umap.tooltip.open({ + this._umap.tooltip.open({ content: translate('Edit the title of the map'), anchor: nameButton, position: 'bottom', @@ -175,11 +175,11 @@ const ControlsMixin = { 'share-status', leftContainer, '', - this.umap.permissions.edit, - this.umap.permissions + this._umap.permissions.edit, + this._umap.permissions ) DomEvent.on(shareStatusButton, 'mouseover', () => { - this.umap.tooltip.open({ + this._umap.tooltip.open({ content: translate('Update who can see and edit the map'), anchor: shareStatusButton, position: 'bottom', @@ -188,12 +188,12 @@ const ControlsMixin = { }) }) if (this.options.editMode === 'advanced') { - DomEvent.on(nameButton, 'click', this.umap.editCaption, this.umap) + DomEvent.on(nameButton, 'click', this._umap.editCaption, this._umap) DomEvent.on( shareStatusButton, 'click', - this.umap.permissions.edit, - this.umap.permissions + this._umap.permissions.edit, + this._umap.permissions ) } if (this.options.user?.id) { @@ -208,21 +208,21 @@ const ControlsMixin = { const actions = [ { label: translate('New map'), - action: this.umap.urls.get('map_new'), + action: this._umap.urls.get('map_new'), }, { label: translate('My maps'), - action: this.umap.urls.get('user_dashboard'), + action: this._umap.urls.get('user_dashboard'), }, { label: translate('My teams'), - action: this.umap.urls.get('user_teams'), + action: this._umap.urls.get('user_teams'), }, ] - if (this.umap.urls.has('user_profile')) { + if (this._umap.urls.has('user_profile')) { actions.push({ label: translate('My profile'), - action: this.umap.urls.get('user_profile'), + action: this._umap.urls.get('user_profile'), }) } button.addEventListener('click', () => { @@ -230,7 +230,7 @@ const ControlsMixin = { }) } - const connectedPeers = this.umap.sync.getNumberOfConnectedPeers() + const connectedPeers = this._umap.sync.getNumberOfConnectedPeers() if (connectedPeers !== 0) { const connectedPeersCount = DomUtil.createButton( 'leaflet-control-connected-peers', @@ -238,7 +238,7 @@ const ControlsMixin = { '' ) DomEvent.on(connectedPeersCount, 'mouseover', () => { - this.umap.tooltip.open({ + this._umap.tooltip.open({ content: translate( '{connectedPeers} peer(s) currently connected to this map', { @@ -253,21 +253,21 @@ const ControlsMixin = { }) const updateConnectedPeersCount = () => { - connectedPeersCount.innerHTML = this.umap.sync.getNumberOfConnectedPeers() + connectedPeersCount.innerHTML = this._umap.sync.getNumberOfConnectedPeers() } updateConnectedPeersCount() } - this.umap.help.getStartedLink(rightContainer) + this._umap.help.getStartedLink(rightContainer) const controlEditCancel = DomUtil.createButton( 'leaflet-control-edit-cancel', rightContainer, DomUtil.add('span', '', null, translate('Cancel edits')), - () => this.umap.askForReset() + () => this._umap.askForReset() ) DomEvent.on(controlEditCancel, 'mouseover', () => { - this.umap.tooltip.open({ - content: this.umap.help.displayLabel('CANCEL'), + this._umap.tooltip.open({ + content: this._umap.help.displayLabel('CANCEL'), anchor: controlEditCancel, position: 'bottom', delay: 500, @@ -278,12 +278,12 @@ const ControlsMixin = { 'leaflet-control-edit-disable', rightContainer, DomUtil.add('span', '', null, translate('View')), - this.umap.disableEdit, - this.umap + this._umap.disableEdit, + this._umap ) DomEvent.on(controlEditDisable, 'mouseover', () => { - this.umap.tooltip.open({ - content: this.umap.help.displayLabel('PREVIEW'), + this._umap.tooltip.open({ + content: this._umap.help.displayLabel('PREVIEW'), anchor: controlEditDisable, position: 'bottom', delay: 500, @@ -294,11 +294,11 @@ const ControlsMixin = { 'leaflet-control-edit-save button', rightContainer, DomUtil.add('span', '', null, translate('Save')), - () => this.umap.saveAll() + () => this._umap.saveAll() ) DomEvent.on(controlEditSave, 'mouseover', () => { - this.umap.tooltip.open({ - content: this.umap.help.displayLabel('SAVE'), + this._umap.tooltip.open({ + content: this._umap.help.displayLabel('SAVE'), anchor: controlEditSave, position: 'bottom', delay: 500, @@ -315,13 +315,13 @@ const ControlsMixin = { container.innerHTML = '' const name = DomUtil.create('h3', 'map-name', container) DomEvent.disableClickPropagation(container) - this.umap.addAuthorLink(container) - if (this.umap.getOption('captionMenus')) { + this._umap.addAuthorLink(container) + if (this._umap.getProperty('captionMenus')) { DomUtil.createButton( 'umap-about-link flat', container, translate('Open caption'), - () => this.umap.openCaption() + () => this._umap.openCaption() ) DomUtil.createButton( 'umap-open-browser-link flat', @@ -338,8 +338,8 @@ const ControlsMixin = { ) } } - this.umap.onceDatalayersLoaded(() => { - this.umap.slideshow.renderToolbox(container) + this._umap.onceDatalayersLoaded(() => { + this._umap.slideshow.renderToolbox(container) }) }, } @@ -434,7 +434,7 @@ const ManageTilelayerMixin = { updateTileLayers: function () { const callback = (tilelayer) => { this.options.tilelayer = tilelayer.toJSON() - this.umap.isDirty = true + this._umap.isDirty = true } if (this._controls.tilelayersChooser) { this._controls.tilelayersChooser.openSwitcher({ callback, edit: true }) @@ -442,30 +442,17 @@ const ManageTilelayerMixin = { }, } -const EditMixin = { - startMarker: function () { - return this.editTools.startMarker() - }, - - startPolyline: function () { - return this.editTools.startPolyline() - }, - - startPolygon: function () { - return this.editTools.startPolygon() - }, - - initEditTools: function () { - this.editTools = new U.Editable(this.umap) - this.renderEditToolbar() - }, -} - export const LeafletMap = BaseMap.extend({ - includes: [ControlsMixin, ManageTilelayerMixin, EditMixin], + includes: [ControlsMixin, ManageTilelayerMixin], + + // The initialize and the setup method might seem similar, but they + // serve two different purposes: + // initialize is for Leaflet internal, when we do "new LeafletMap", + // while setup is the public API for the LeafletMap to actually + // render to the DOM. initialize: function (umap, element) { - this.umap = umap - const options = this.umap.properties + this._umap = umap + const options = this._umap.properties BaseMap.prototype.initialize.call(this, element, options) @@ -489,11 +476,11 @@ export const LeafletMap = BaseMap.extend({ this.initControls() // Needs locate control and hash to exist this.initCenter() - this.update() + this.renderUI() }, - update: function () { - this.setOptions(this.umap.properties) + renderUI: function () { + setOptions(this, this._umap.properties) this.initTileLayers() this.renderCaptionBar() this.renderEditToolbar() @@ -502,10 +489,6 @@ export const LeafletMap = BaseMap.extend({ this.handleLimitBounds() }, - setOptions: function (options) { - setOptions(this, options) - }, - closeInplaceToolbar: function () { const toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id] if (toolbar) toolbar.remove() @@ -534,11 +517,11 @@ export const LeafletMap = BaseMap.extend({ } else if (this.options.defaultView === 'locate' && !this.options.noControl) { this._controls.locate.start() } else if (this.options.defaultView === 'data') { - this.umap.onceDataLoaded(this.umap.fitDataBounds) + this._umap.onceDataLoaded(this._umap.fitDataBounds) } else if (this.options.defaultView === 'latest') { - this.umap.onceDataLoaded(() => { - if (!this.umap.hasData()) return - const datalayer = this.umap.firstVisibleDatalayer() + this._umap.onceDataLoaded(() => { + if (!this._umap.hasData()) return + const datalayer = this._umap.firstVisibleDatalayer() let feature if (datalayer) { const feature = datalayer.getFeatureByIndex(-1) @@ -590,4 +573,9 @@ export const LeafletMap = BaseMap.extend({ } return BaseMap.prototype.setMaxBounds.call(this, bounds) }, + + initEditTools: function () { + this.editTools = new U.Editable(this._umap) + this.renderEditToolbar() + }, }) diff --git a/umap/static/umap/js/modules/rendering/popup.js b/umap/static/umap/js/modules/rendering/popup.js index b485c2c8..e460e118 100644 --- a/umap/static/umap/js/modules/rendering/popup.js +++ b/umap/static/umap/js/modules/rendering/popup.js @@ -61,15 +61,15 @@ const Panel = Popup.extend({ zoomAnimation: false, }, - onAdd: function (map) { - map.umap.panel.setDefaultMode('expanded') - map.umap.panel.open({ + onAdd: function (leafletMap) { + leafletMap._umap.panel.setDefaultMode('expanded') + leafletMap._umap.panel.open({ content: this._content, - actions: [Browser.backButton(map)], + actions: [Browser.backButton(leafletMap._umap)], }) // fire events as in base class Popup.js:onAdd - map.fire('popupopen', { popup: this }) + leafletMap.fire('popupopen', { popup: this }) if (this._source) { this._source.fire('popupopen', { popup: this }, true) if (!(this._source instanceof Path)) { @@ -78,11 +78,11 @@ const Panel = Popup.extend({ } }, - onRemove: function (map) { - map.umap.panel.close() + onRemove: function (leafletMap) { + leafletMap._umap.panel.close() // fire events as in base class Popup.js:onRemove - map.fire('popupclose', { popup: this }) + leafletMap.fire('popupclose', { popup: this }) if (this._source) { this._source.fire('popupclose', { popup: this }, true) if (!(this._source instanceof Path)) { diff --git a/umap/static/umap/js/modules/rendering/ui.js b/umap/static/umap/js/modules/rendering/ui.js index 6e5a4d43..f7779389 100644 --- a/umap/static/umap/js/modules/rendering/ui.js +++ b/umap/static/umap/js/modules/rendering/ui.js @@ -52,7 +52,7 @@ const FeatureMixin = { onClick: function (event) { if (this._map.measureTools?.enabled()) return this._popupHandlersAdded = true // Prevent leaflet from managing event - if (!this._map.umap.editEnabled) { + if (!this._map._umap.editEnabled) { this.feature.view(event) } else if (!this.feature.isReadOnly()) { if (event.originalEvent.shiftKey) { @@ -96,8 +96,8 @@ const FeatureMixin = { DomEvent.stop(event) const items = this.feature .getContextMenuItems(event) - .concat(this._map.umap.getContextMenuItems(event)) - this._map.umap.contextmenu.open(event.originalEvent, items) + .concat(this._map._umap.getSharedContextMenuItems(event)) + this._map._umap.contextmenu.open(event.originalEvent, items) }, onCommit: function () { @@ -134,7 +134,7 @@ const PointMixin = { _enableDragging: function () { // TODO: start dragging after 1 second on mouse down - if (this._map.umap.editEnabled) { + if (this._map._umap.editEnabled) { if (!this.editEnabled()) this.enableEdit() // Enabling dragging on the marker override the Draggable._OnDown // event, which, as it stopPropagation, refrain the call of @@ -146,7 +146,7 @@ const PointMixin = { }, _disableDragging: function () { - if (this._map.umap.editEnabled) { + if (this._map._umap.editEnabled) { if (this.editor?.drawing) return // when creating a new marker, the mouse can trigger the mouseover/mouseout event // do not listen to them this.disableEdit() @@ -253,21 +253,21 @@ export const LeafletMarker = Marker.extend({ const PathMixin = { _onMouseOver: function () { if (this._map.measureTools?.enabled()) { - this._map.umap.tooltip.open({ content: this.getMeasure(), anchor: this }) - } else if (this._map.umap.editEnabled && !this._map.umap.editedFeature) { - this._map.umap.tooltip.open({ content: translate('Click to edit'), anchor: this }) + this._map._umap.tooltip.open({ content: this.getMeasure(), anchor: this }) + } else if (this._map._umap.editEnabled && !this._map._umap.editedFeature) { + this._map._umap.tooltip.open({ content: translate('Click to edit'), anchor: this }) } }, makeGeometryEditable: function () { - if (this._map.umap.editedFeature !== this.feature) { + if (this._map._umap.editedFeature !== this.feature) { this.disableEdit() return } this._map.once('moveend', this.makeGeometryEditable, this) const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0) if (pointsCount > 100 && this._map.getZoom() < this._map.getMaxZoom()) { - this._map.umap.tooltip.open({ content: L._('Please zoom in to edit the geometry') }) + this._map._umap.tooltip.open({ content: L._('Please zoom in to edit the geometry') }) this.disableEdit() } else { this.enableEdit() diff --git a/umap/static/umap/js/modules/rules.js b/umap/static/umap/js/modules/rules.js index 38c81274..3f3244bd 100644 --- a/umap/static/umap/js/modules/rules.js +++ b/umap/static/umap/js/modules/rules.js @@ -22,7 +22,7 @@ class Rule { set isDirty(status) { this._isDirty = status - if (status) this.umap.isDirty = status + if (status) this._umap.isDirty = status } constructor(umap, condition = '', options = {}) { @@ -38,14 +38,14 @@ class Rule { ['!=', this.not_equal], ['=', this.equal], ] - this.umap = umap + this._umap = umap this.active = true this.options = options this.condition = condition } render(fields) { - this.umap.render(fields) + this._umap.render(fields) } equal(other) { @@ -102,10 +102,6 @@ class Rule { return this.operator(this.cast(props[this.key])) } - getMap() { - return this.umap - } - getOption(option) { return this.options[option] } @@ -137,7 +133,7 @@ class Rule { const defaultShapeProperties = DomUtil.add('div', '', container) defaultShapeProperties.appendChild(builder.build()) const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input) - const properties = this.umap.allProperties() + const properties = this._umap.allProperties() autocomplete.suggestions = properties autocomplete.input.addEventListener('input', (event) => { const value = event.target.value @@ -145,12 +141,12 @@ class Rule { autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`] } else if (value.endsWith('=')) { const key = value.split('!')[0].split('=')[0] - autocomplete.suggestions = this.umap + autocomplete.suggestions = this._umap .sortedValues(key) .map((str) => `${value}${str || ''}`) } }) - this.umap.editPanel.open({ content: container }) + this._umap.editPanel.open({ content: container }) } renderToolbox(row) { @@ -177,7 +173,7 @@ class Rule { function () { if (!confirm(translate('Are you sure you want to delete this rule?'))) return this._delete() - this.umap.editPanel.close() + this._umap.editPanel.close() }, this ) @@ -187,27 +183,27 @@ class Rule { DomEvent.on(toggle, 'click', () => { this.active = !this.active row.classList.toggle('off', !this.active) - this.umap.render(['rules']) + this._umap.render(['rules']) }) } _delete() { - this.umap.rules.rules = this.umap.rules.rules.filter((rule) => rule !== this) + this._umap.rules.rules = this._umap.rules.rules.filter((rule) => rule !== this) } } export default class Rules { constructor(umap) { - this.umap = umap + this._umap = umap this.rules = [] this.loadRules() } loadRules() { - if (!this.umap.properties.rules?.length) return - for (const { condition, options } of this.umap.properties.rules) { + if (!this._umap.properties.rules?.length) return + for (const { condition, options } of this._umap.properties.rules) { if (!condition) continue - this.rules.push(new Rule(this.umap, condition, options)) + this.rules.push(new Rule(this._umap, condition, options)) } } @@ -226,7 +222,7 @@ export default class Rules { else newIdx = referenceIdx + 1 this.rules.splice(newIdx, 0, moved) moved.isDirty = true - this.umap.render(['rules']) + this._umap.render(['rules']) } edit(container) { @@ -244,14 +240,14 @@ export default class Rules { } addRule() { - const rule = new Rule(this.umap) + const rule = new Rule(this._umap) rule.isDirty = true this.rules.push(rule) rule.edit(map) } commit() { - this.umap.properties.rules = this.rules.map((rule) => { + this._umap.properties.rules = this.rules.map((rule) => { return { condition: rule.condition, options: rule.options, diff --git a/umap/static/umap/js/modules/saving.js b/umap/static/umap/js/modules/saving.js index 98797633..c478c494 100644 --- a/umap/static/umap/js/modules/saving.js +++ b/umap/static/umap/js/modules/saving.js @@ -10,21 +10,21 @@ export async function save() { } } -export function add(obj) { +function add(obj) { _queue.add(obj) - _onUpdate() + onUpdate() } -export function remove(obj) { +function remove(obj) { _queue.delete(obj) - _onUpdate() + onUpdate() } -export function has(obj) { +function has(obj) { return _queue.has(obj) } -function _onUpdate() { +function onUpdate() { isDirty = Boolean(_queue.size) document.body.classList.toggle('umap-is-dirty', isDirty) } diff --git a/umap/static/umap/js/modules/share.js b/umap/static/umap/js/modules/share.js index 5e2e8215..130ec460 100644 --- a/umap/static/umap/js/modules/share.js +++ b/umap/static/umap/js/modules/share.js @@ -5,7 +5,7 @@ import * as Utils from './utils.js' export default class Share { constructor(umap) { - this.umap = umap + this._umap = umap } build() { @@ -22,11 +22,11 @@ export default class Share { window.location.protocol + Utils.getBaseUrl() ) - if (this.umap.properties.shortUrl) { + if (this._umap.properties.shortUrl) { DomUtil.createCopiableInput( this.container, translate('Short link'), - this.umap.properties.shortUrl + this._umap.properties.shortUrl ) } @@ -60,8 +60,8 @@ export default class Share { this.container, translate('All data and settings of the map') ) - const downloadUrl = this.umap.urls.get('map_download', { - map_id: this.umap.properties.umap_id, + const downloadUrl = this._umap.urls.get('map_download', { + map_id: this._umap.properties.umap_id, }) const link = Utils.loadTemplate(`
@@ -116,10 +116,10 @@ export default class Share { 'queryString.captionMenus', ] // TODO: move HIDDABLE_CONTROLS to SCHEMA ? - for (const name of this.umap._leafletMap.HIDDABLE_CONTROLS) { + for (const name of this._umap._leafletMap.HIDDABLE_CONTROLS) { UIFields.push(`queryString.${name}Control`) } - const iframeExporter = new IframeExporter(this.umap) + const iframeExporter = new IframeExporter(this._umap) const buildIframeCode = () => { iframe.textContent = iframeExporter.build() exportUrl.value = window.location.protocol + iframeExporter.buildUrl() @@ -137,13 +137,13 @@ export default class Share { open() { if (!this.container) this.build() - this.umap.panel.open({ content: this.container }) + this._umap.panel.open({ content: this.container }) } async format(mode) { const type = EXPORT_FORMATS[mode] - const content = await type.formatter(this.umap) - const filename = Utils.slugify(this.umap.properties.name) + type.ext + const content = await type.formatter(this._umap) + const filename = Utils.slugify(this._umap.properties.name) + type.ext return { content, filetype: type.filetype, filename } } @@ -163,7 +163,7 @@ export default class Share { class IframeExporter { constructor(umap) { - this.umap = umap + this._umap = umap this.baseUrl = Utils.getBaseUrl() this.options = { includeFullScreenLink: true, @@ -193,18 +193,18 @@ class IframeExporter { height: '300px', } // Use map default, not generic default - this.queryString.onLoadPanel = this.umap.getOption('onLoadPanel') + this.queryString.onLoadPanel = this._umap.getProperty('onLoadPanel') } buildUrl(options) { const datalayers = [] - if (this.options.viewCurrentFeature && this.umap.currentFeature) { - this.queryString.feature = this.umap.currentFeature.getSlug() + if (this.options.viewCurrentFeature && this._umap.currentFeature) { + this.queryString.feature = this._umap.currentFeature.getSlug() } else { delete this.queryString.feature } if (this.options.keepCurrentDatalayers) { - this.umap.eachDataLayer((datalayer) => { + this._umap.eachDataLayer((datalayer) => { if (datalayer.isVisible() && datalayer.umap_id) { datalayers.push(datalayer.umap_id) } diff --git a/umap/static/umap/js/modules/slideshow.js b/umap/static/umap/js/modules/slideshow.js index 2294249f..0d9a7766 100644 --- a/umap/static/umap/js/modules/slideshow.js +++ b/umap/static/umap/js/modules/slideshow.js @@ -13,20 +13,20 @@ const TOOLBOX_TEMPLATE = ` ` export default class Slideshow extends WithTemplate { - constructor(umap, options) { + constructor(umap, leafletMap, properties) { super() - this.umap = umap + this._umap = umap this._id = null this.CLASSNAME = 'umap-slideshow-active' - this.setOptions(options) + this.setProperties(properties) this._current = null - if (this.options.autoplay) { - this.umap.onceDataLoaded(function () { + if (this.properties.autoplay) { + this._umap.onceDataLoaded(function () { this.play() }, this) } - this.umap._leafletMap.on( + leafletMap.on( 'edit:enabled', function () { this.stop() @@ -54,22 +54,22 @@ export default class Slideshow extends WithTemplate { return this.current.getNext() } - setOptions(options) { - this.options = Object.assign( + setProperties(properties) { + this.properties = Object.assign( { delay: 5000, autoplay: false, }, - options + properties ) } defaultDatalayer() { - return this.umap.findDataLayer((d) => d.canBrowse()) + return this._umap.findDataLayer((d) => d.canBrowse()) } startSpinner() { - const time = Number.parseInt(this.options.delay, 10) + const time = Number.parseInt(this.properties.delay, 10) if (!time) return const css = `rotation ${time / 1000}s infinite linear` const spinner = document.querySelector('.umap-slideshow-toolbox .play .spinner') @@ -83,9 +83,9 @@ export default class Slideshow extends WithTemplate { play() { if (this._id) return - if (this.umap.editEnabled || !this.umap.properties.slideshow.active) return + if (this._umap.editEnabled || !this._umap.properties.slideshow.active) return L.DomUtil.addClass(document.body, this.CLASSNAME) - this._id = window.setInterval(L.bind(this.loop, this), this.options.delay) + this._id = window.setInterval(L.bind(this.loop, this), this.properties.delay) this.startSpinner() this.loop() } @@ -123,7 +123,7 @@ export default class Slideshow extends WithTemplate { step() { if (!this.current) return this.stop() - this.current.zoomTo({ easing: this.options.easing }) + this.current.zoomTo({ easing: this.properties.easing }) this.current.view() } diff --git a/umap/static/umap/js/modules/sync/updaters.js b/umap/static/umap/js/modules/sync/updaters.js index 09169f99..a38975e9 100644 --- a/umap/static/umap/js/modules/sync/updaters.js +++ b/umap/static/umap/js/modules/sync/updaters.js @@ -7,7 +7,7 @@ import { fieldInSchema } from '../utils.js' class BaseUpdater { constructor(umap) { - this.umap = umap + this._umap = umap } updateObjectValue(obj, key, value) { @@ -32,8 +32,8 @@ class BaseUpdater { } getDataLayerFromID(layerId) { - if (layerId) return this.umap.getDataLayerByUmapId(layerId) - return this.umap.defaultEditDataLayer() + if (layerId) return this._umap.getDataLayerByUmapId(layerId) + return this._umap.defaultEditDataLayer() } applyMessage(payload) { @@ -45,18 +45,18 @@ class BaseUpdater { export class MapUpdater extends BaseUpdater { update({ key, value }) { if (fieldInSchema(key)) { - this.updateObjectValue(this.umap, key, value) + this.updateObjectValue(this._umap, key, value) } - this.umap.render([key]) + this._umap.render([key]) } } export class DataLayerUpdater extends BaseUpdater { upsert({ value }) { // Inserts does not happen (we use multiple updates instead). - this.umap.createDataLayer(value, false) - this.umap.render([]) + this._umap.createDataLayer(value, false) + this._umap.render([]) } update({ key, metadata, value }) { diff --git a/umap/static/umap/js/modules/tableeditor.js b/umap/static/umap/js/modules/tableeditor.js index f04734d1..0a2b49ed 100644 --- a/umap/static/umap/js/modules/tableeditor.js +++ b/umap/static/umap/js/modules/tableeditor.js @@ -14,10 +14,11 @@ const TEMPLATE = ` ` export default class TableEditor extends WithTemplate { - constructor(datalayer) { + constructor(umap, datalayer, leafletMap) { super() this.datalayer = datalayer - this.umap = this.datalayer.umap + this._umap = umap + this._leafletMap = leafletMap this.contextmenu = new ContextMenu({ className: 'dark' }) this.table = this.loadTemplate(TEMPLATE) if (!this.datalayer.isRemoteLayer()) { @@ -36,20 +37,20 @@ export default class TableEditor extends WithTemplate { openHeaderMenu(property) { const actions = [] let filterItem - if (this.umap.facets.has(property)) { + if (this._umap.facets.has(property)) { filterItem = { label: translate('Remove filter for this column'), action: () => { - this.umap.facets.remove(property) - this.umap.browser.open('filters') + this._umap.facets.remove(property) + this._umap.browser.open('filters') }, } } else { filterItem = { label: translate('Add filter for this column'), action: () => { - this.umap.facets.add(property) - this.umap.browser.open('filters') + this._umap.facets.add(property) + this._umap.browser.open('filters') }, } } @@ -86,8 +87,8 @@ export default class TableEditor extends WithTemplate { } renderBody() { - const bounds = this.umap._leafletMap.getBounds() - const inBbox = this.umap.browser.options.inBbox + const bounds = this._leafletMap.getBounds() + const inBbox = this._umap.browser.options.inBbox let html = '' this.datalayer.eachFeature((feature) => { if (feature.isFiltered()) return @@ -121,7 +122,7 @@ export default class TableEditor extends WithTemplate { } renameProperty(property) { - this.umap.dialog + this._umap.dialog .prompt(translate('Please enter the new name of this property')) .then(({ prompt }) => { if (!prompt || !this.validateName(prompt)) return @@ -135,7 +136,7 @@ export default class TableEditor extends WithTemplate { } deleteProperty(property) { - this.umap.dialog + this._umap.dialog .confirm( translate('Are you sure you want to delete this property on all the features?') ) @@ -150,7 +151,7 @@ export default class TableEditor extends WithTemplate { } addProperty() { - this.umap.dialog + this._umap.dialog .prompt(translate('Please enter the name of the property')) .then(({ prompt }) => { if (!prompt || !this.validateName(prompt)) return @@ -187,10 +188,10 @@ export default class TableEditor extends WithTemplate { `) - filterButton.addEventListener('click', () => this.umap.browser.open('filters')) + filterButton.addEventListener('click', () => this._umap.browser.open('filters')) actions.push(filterButton) - this.umap.fullPanel.open({ + this._umap.fullPanel.open({ content: this.table, className: 'umap-table-editor', actions: actions, @@ -304,7 +305,7 @@ export default class TableEditor extends WithTemplate { deleteRows() { const selectedRows = this.getSelectedRows() if (!selectedRows.length) return - this.umap.dialog + this._umap.dialog .confirm( translate('Found {count} rows. Are you sure you want to delete all?', { count: selectedRows.length, @@ -320,9 +321,9 @@ export default class TableEditor extends WithTemplate { this.datalayer.show() this.datalayer.dataChanged() this.renderBody() - if (this.umap.browser.isOpen()) { - this.umap.browser.resetFilters() - this.umap.browser.open('filters') + if (this._umap.browser.isOpen()) { + this._umap.browser.resetFilters() + this._umap.browser.open('filters') } }) } diff --git a/umap/static/umap/js/modules/ui/panel.js b/umap/static/umap/js/modules/ui/panel.js index 6d0c14bd..e464a56a 100644 --- a/umap/static/umap/js/modules/ui/panel.js +++ b/umap/static/umap/js/modules/ui/panel.js @@ -2,9 +2,10 @@ import { DomEvent, DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js' import { translate } from '../i18n.js' export class Panel { - constructor(umap) { - this.parent = umap._leafletMap._controlContainer - this.umap = umap + constructor(umap, leafletMap) { + this.parent = leafletMap._controlContainer + this._umap = umap + this._leafletMap = leafletMap this.container = DomUtil.create('div', '', this.parent) // This will be set once according to the panel configurated at load // or by using panels as popups @@ -80,26 +81,26 @@ export class Panel { onClose() { if (DomUtil.hasClass(this.container, 'on')) { DomUtil.removeClass(this.container, 'on') - this.umap._leafletMap.invalidateSize({ pan: false }) + this._leafletMap.invalidateSize({ pan: false }) } } } export class EditPanel extends Panel { - constructor(umap) { - super(umap) + constructor(umap, leafletMap) { + super(umap, leafletMap) this.className = 'right dark' } onClose() { super.onClose() - this.umap.editedFeature = null + this._umap.editedFeature = null } } export class FullPanel extends Panel { - constructor(umap) { - super(umap) + constructor(umap, leafletMap) { + super(umap, leafletMap) this.className = 'full dark' this.mode = 'expanded' } diff --git a/umap/static/umap/js/modules/umap.js b/umap/static/umap/js/modules/umap.js index 8895e068..688bbe73 100644 --- a/umap/static/umap/js/modules/umap.js +++ b/umap/static/umap/js/modules/umap.js @@ -90,26 +90,25 @@ export default class Umap extends ServerStored { // Needed to render controls this.permissions = new MapPermissions(this) this.urls = new URLs(this.properties.urls) - this.slideshow = new Slideshow(this, this.properties.slideshow) + this.slideshow = new Slideshow(this, this._leafletMap, this.properties.slideshow) this._leafletMap.setup() if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema) - - this.panel = new Panel(this) + this.panel = new Panel(this, this._leafletMap) this.dialog = new Dialog({ className: 'dark' }) this.tooltip = new Tooltip(this._leafletMap._controlContainer) this.contextmenu = new ContextMenu() if (this.hasEditMode()) { - this.editPanel = new EditPanel(this) - this.fullPanel = new FullPanel(this) + this.editPanel = new EditPanel(this, this._leafletMap) + this.fullPanel = new FullPanel(this, this._leafletMap) } this.server = new ServerRequest() this.request = new Request() this.facets = new Facets(this) - this.browser = new Browser(this) - this.caption = new Caption(this) + this.browser = new Browser(this, this._leafletMap) + this.caption = new Caption(this, this._leafletMap) this.importer = new Importer(this) this.share = new Share(this) this.rules = new Rules(this) @@ -183,7 +182,7 @@ export default class Umap extends ServerStored { if (!this.properties.noControl) { this.initShortcuts() - this._leafletMap.on('contextmenu', this.onContextMenu) + this._leafletMap.on('contextmenu', (e) => this.onContextMenu(e)) this.onceDataLoaded(this.setViewFromQueryString) this.propagate() } @@ -201,7 +200,7 @@ export default class Umap extends ServerStored { this._editedFeature.endEdit() } this._editedFeature = feature - this._leafletMap.fire('seteditedfeature') + this.fire('seteditedfeature') } setPropertiesFromQueryString() { @@ -227,7 +226,7 @@ export default class Umap extends ServerStored { // FIXME retrocompat asBoolean('displayDataBrowserOnLoad') asBoolean('displayCaptionOnLoad') - for (const [key, schema] of Object.entries(U.SCHEMA)) { + for (const [key, schema] of Object.entries(SCHEMA)) { switch (schema.type) { case Boolean: if (schema.nullable) asNullableBoolean(key) @@ -299,7 +298,7 @@ export default class Umap extends ServerStored { if (dataUrls.length) { for (let dataUrl of dataUrls) { dataUrl = decodeURIComponent(dataUrl) - dataUrl = this.localizeUrl(dataUrl) + dataUrl = this.renderUrl(dataUrl) dataUrl = this.proxyUrl(dataUrl) const datalayer = this.createDataLayer() await datalayer.importFromUrl(dataUrl, dataFormat) @@ -383,7 +382,7 @@ export default class Umap extends ServerStored { return items } - getContextMenuItems(event) { + getSharedContextMenuItems(event) { const items = [] if (this.properties.urls.routing) { items.push('-', { @@ -402,7 +401,7 @@ export default class Umap extends ServerStored { onContextMenu(event) { const items = this.getOwnContextMenuItems(event).concat( - this.getContextMenuItems(event) + this.getSharedContextMenuItems(event) ) this.contextmenu.open(event.originalEvent, items) } @@ -424,17 +423,19 @@ export default class Umap extends ServerStored { return editMode === 'simple' || editMode === 'advanced' } - getDefaultOption(key) { - return SCHEMA[key]?.default - } - - getOption(key, feature) { + getProperty(key, feature) { if (feature) { const value = this.rules.getOption(key, feature) if (value !== undefined) return value } if (Utils.usableOption(this.properties, key)) return this.properties[key] - return this.getDefaultOption(key) + return SCHEMA[key]?.default + } + + getOption(key, feature) { + // TODO: remove when umap.forms.js is refactored and does not call blindly + // obj.getOption anymore + return this.getProperty(key, feature) } getGeoContext() { @@ -457,7 +458,7 @@ export default class Umap extends ServerStored { return context } - localizeUrl(url) { + renderUrl(url) { return Utils.greedyTemplate(url, this.getGeoContext(), true) } @@ -551,18 +552,18 @@ export default class Umap extends ServerStored { this.createDataLayer(options, false) } this.datalayersLoaded = true - this._leafletMap.fire('datalayersloaded') + this.fire('datalayersloaded') for (const datalayer of this.datalayersIndex) { if (datalayer.showAtLoad()) await datalayer.show() } this.dataloaded = true - this._leafletMap.fire('dataloaded') + this.fire('dataloaded') } createDataLayer(options = {}, sync = true) { options.name = options.name || `${translate('Layer')} ${this.datalayersIndex.length + 1}` - const datalayer = new DataLayer(this, options, sync) + const datalayer = new DataLayer(this, this._leafletMap, options, sync) if (sync !== false) { datalayer.sync.upsert(datalayer.options) @@ -580,12 +581,6 @@ export default class Umap extends ServerStored { this.onDataLayersChanged() } - redrawVisibleDataLayers() { - this.eachVisibleDataLayer((datalayer) => { - datalayer.redraw() - }) - } - indexDatalayers() { const panes = this._leafletMap.getPane('overlayPane') @@ -631,7 +626,7 @@ export default class Umap extends ServerStored { // have changed, we'll be more subtil when we'll remove the // save action this.render(['name', 'user', 'permissions']) - this._leafletMap.fire('saved') + this.fire('saved') } propagate() { @@ -663,13 +658,13 @@ export default class Umap extends ServerStored { this._backupProperties = Object.assign({}, this.properties) this._backupProperties.tilelayer = Object.assign({}, this.properties.tilelayer) this._backupProperties.limitBounds = Object.assign({}, this.properties.limitBounds) - this._backupProperties.permissions = Object.assign({}, this.permissions.options) + this._backupProperties.permissions = Object.assign({}, this.permissions.properties) } resetProperties() { this.properties = Object.assign({}, this._backupProperties) this.properties.tilelayer = Object.assign({}, this._backupProperties.tilelayer) - this.permissions.options = Object.assign({}, this._backupProperties.permissions) + this.permissions.properties = Object.assign({}, this._backupProperties.permissions) } hasData() { @@ -994,7 +989,7 @@ export default class Umap extends ServerStored { ], ] const slideshowBuilder = new U.FormBuilder(this, slideshowFields, { - callback: () => this.slideshow.setOptions(this.properties.slideshow), + callback: () => this.slideshow.setProperties(this.properties.slideshow), umap: this, }) slideshow.appendChild(slideshowBuilder.build()) @@ -1120,7 +1115,7 @@ export default class Umap extends ServerStored { this.properties.user = data.user if (!this.properties.umap_id) { this.properties.umap_id = data.id - this.permissions.setOptions(data.permissions) + this.permissions.setProperties(data.permissions) this.permissions.commit() if (data.permissions?.anonymous_edit_url) { this._leafletMap.once('saved', () => { @@ -1142,7 +1137,7 @@ export default class Umap extends ServerStored { if (!this.permissions.isDirty) { // Do not override local changes to permissions, // but update in case some other editors changed them in the meantime. - this.permissions.setOptions(data.permissions) + this.permissions.setProperties(data.permissions) this.permissions.commit() } this._leafletMap.once('saved', () => { @@ -1197,7 +1192,7 @@ export default class Umap extends ServerStored { document.body.classList.add('umap-edit-enabled') this.editEnabled = true this.drop.enable() - this._leafletMap.fire('edit:enabled') + this.fire('edit:enabled') this.initSyncEngine() } @@ -1207,13 +1202,17 @@ export default class Umap extends ServerStored { document.body.classList.remove('umap-edit-enabled') this.editedFeature = null this.editEnabled = false - this._leafletMap.fire('edit:disabled') + this.fire('edit:disabled') this.editPanel.close() this.fullPanel.close() this.sync.stop() this._leafletMap.closeInplaceToolbar() } + fire(name) { + this._leafletMap.fire(name) + } + askForReset(e) { this.dialog .confirm(translate('Are you sure you want to cancel your changes?')) @@ -1256,12 +1255,14 @@ export default class Umap extends ServerStored { for (const impact of impacts) { switch (impact) { case 'ui': - this._leafletMap.update() + this._leafletMap.renderUI() this.browser.redraw() this.propagate() break case 'data': - this.redrawVisibleDataLayers() + this.eachVisibleDataLayer((datalayer) => { + datalayer.redraw() + }) break case 'datalayer-index': this.reindexDataLayers() @@ -1460,7 +1461,7 @@ export default class Umap extends ServerStored { return } if (type === 'umap') { - this.importFromFile(file, 'umap') + this.importUmapFile(file, 'umap') } else { if (!layer) layer = this.createDataLayer({ name: file.name }) layer.importFromFile(file, type) @@ -1479,7 +1480,7 @@ export default class Umap extends ServerStored { let mustReindex = false - for (const option of Object.keys(U.SCHEMA)) { + for (const option of Object.keys(SCHEMA)) { if (typeof importedData.properties[option] !== 'undefined') { this.properties[option] = importedData.properties[option] if (option === 'sortKey') mustReindex = true @@ -1499,7 +1500,7 @@ export default class Umap extends ServerStored { dataLayer.fromUmapGeoJSON(geojson) } - this._leafletMap.update() + this._leafletMap.renderUI() this.eachDataLayer((datalayer) => { if (mustReindex) datalayer.reindex() datalayer.redraw() @@ -1509,7 +1510,7 @@ export default class Umap extends ServerStored { this.isDirty = true } - importFromFile(file) { + importUmapFile(file) { const reader = new FileReader() reader.readAsText(file) reader.onload = (e) => { diff --git a/umap/static/umap/js/modules/utils.js b/umap/static/umap/js/modules/utils.js index 26829f99..b9fe128e 100644 --- a/umap/static/umap/js/modules/utils.js +++ b/umap/static/umap/js/modules/utils.js @@ -25,6 +25,16 @@ export function checkId(string) { return /^[A-Za-z0-9]{5}$/.test(string) } + + +function _getPropertyName(field) { + const filtered_field = ['options.', 'properties.'].reduce( + (acc, prefix) => acc.replace(prefix, ''), + field + ) + return filtered_field.split('.')[0] +} + /** * Compute the impacts for a given list of fields. * @@ -41,7 +51,7 @@ export function getImpactsFromSchema(fields, schema) { // remove the option prefix for fields // And only keep the first part in case of a subfield // (e.g "options.limitBounds.foobar" will just return "limitBounds") - return field.replace('options.', '').replace('properties.', '').split('.')[0] + return _getPropertyName(field) }) .reduce((acc, field) => { // retrieve the "impacts" field from the schema @@ -66,10 +76,7 @@ export function getImpactsFromSchema(fields, schema) { export function fieldInSchema(field, schema) { const current_schema = schema || U.SCHEMA if (typeof field !== 'string') return false - const field_name = field - .replace('options.', '') - .replace('properties.', '') - .split('.')[0] + const field_name = _getPropertyName(field) return current_schema[field_name] !== undefined } diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 58e59534..1f0a773c 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -2,7 +2,7 @@ U.BaseAction = L.ToolbarAction.extend({ initialize: function (map) { this.map = map if (this.options.label) { - this.options.tooltip = this.map.umap.help.displayLabel( + this.options.tooltip = this.map._umap.help.displayLabel( this.options.label, (withKbdTag = false) ) @@ -25,7 +25,7 @@ U.ImportAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.umap.importer.open() + this.map._umap.importer.open() }, }) @@ -37,7 +37,7 @@ U.EditLayersAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.umap.editDatalayers() + this.map._umap.editDatalayers() }, }) @@ -49,7 +49,7 @@ U.EditCaptionAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.umap.editCaption() + this.map._umap.editCaption() }, }) @@ -61,7 +61,7 @@ U.EditPropertiesAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.umap.edit() + this.map._umap.edit() }, }) @@ -84,7 +84,7 @@ U.UpdateExtentAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.umap.setCenterAndZoom() + this.map._umap.setCenterAndZoom() }, }) @@ -95,7 +95,7 @@ U.UpdatePermsAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.umap.permissions.edit() + this.map._umap.permissions.edit() }, }) @@ -107,7 +107,7 @@ U.DrawMarkerAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.startMarker() + this.map.editTools.startMarker() }, }) @@ -119,7 +119,7 @@ U.DrawPolylineAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.startPolyline() + this.map.editTools.startPolyline() }, }) @@ -131,7 +131,7 @@ U.DrawPolygonAction = U.BaseAction.extend({ }, addHooks: function () { - this.map.startPolygon() + this.map.editTools.startPolygon() }, }) @@ -143,7 +143,7 @@ U.AddPolylineShapeAction = U.BaseAction.extend({ addHooks: function () { // FIXME: smells bad - this.map.umap.editedFeature.ui.editor.newShape() + this.map._umap.editedFeature.ui.editor.newShape() }, }) @@ -306,23 +306,23 @@ U.DrawToolbar = L.Toolbar.Control.extend({ appendToContainer: function (container) { this.options.actions = [] - if (this.map.umap.properties.enableMarkerDraw) { + if (this.map._umap.properties.enableMarkerDraw) { this.options.actions.push(U.DrawMarkerAction) } - if (this.map.umap.properties.enablePolylineDraw) { + if (this.map._umap.properties.enablePolylineDraw) { this.options.actions.push(U.DrawPolylineAction) if ( - this.map.umap.editedFeature && - this.map.umap.editedFeature instanceof U.LineString + this.map._umap.editedFeature && + this.map._umap.editedFeature instanceof U.LineString ) { this.options.actions.push(U.AddPolylineShapeAction) } } - if (this.map.umap.properties.enablePolygonDraw) { + if (this.map._umap.properties.enablePolygonDraw) { this.options.actions.push(U.DrawPolygonAction) if ( - this.map.umap.editedFeature && - this.map.umap.editedFeature instanceof U.Polygon + this.map._umap.editedFeature && + this.map._umap.editedFeature instanceof U.Polygon ) { this.options.actions.push(U.AddPolygonShapeAction) } @@ -372,9 +372,9 @@ U.DropControl = L.Class.extend({ this.dropzone.classList.remove('umap-dragover') L.DomEvent.stop(e) for (const file of event.dataTransfer.files) { - this.map.umap.processFileToImport(file) + this.map._umap.processFileToImport(file) } - this.map.umap.onceDataLoaded(this.map.umap.fitDataBounds) + this.map._umap.onceDataLoaded(this.map._umap.fitDataBounds) }, dragleave: function () { @@ -394,15 +394,15 @@ U.EditControl = L.Control.extend({ '', container, L._('Edit'), - map.umap.enableEdit, - map.umap + map._umap.enableEdit, + map._umap ) L.DomEvent.on( enableEditing, 'mouseover', () => { - map.umap.tooltip.open({ - content: map.umap.help.displayLabel('TOGGLE_EDIT'), + map._umap.tooltip.open({ + content: map._umap.help.displayLabel('TOGGLE_EDIT'), anchor: enableEditing, position: 'bottom', delay: 750, @@ -484,7 +484,7 @@ U.PermanentCreditsControl = L.Control.extend({ L.Control.Button = L.Control.extend({ initialize: function (umap, options) { - this.umap = umap + this._umap = umap L.Control.prototype.initialize.call(this, options) }, @@ -517,11 +517,11 @@ U.DataLayersControl = L.Control.Button.extend({ }, afterAdd: function (container) { - U.Utils.toggleBadge(container, this.umap.browser?.hasFilters()) + U.Utils.toggleBadge(container, this._umap.browser?.hasFilters()) }, onClick: function () { - this.umap.openBrowser() + this._umap.openBrowser() }, }) @@ -533,7 +533,7 @@ U.CaptionControl = L.Control.Button.extend({ }, onClick: function () { - this.umap.openCaption() + this._umap.openCaption() }, }) @@ -544,13 +544,12 @@ U.StarControl = L.Control.Button.extend({ }, getClassName: function () { - const status = this.umap.properties.starred ? ' starred' : '' + const status = this._umap.properties.starred ? ' starred' : '' return `leaflet-control-star umap-control${status}` }, onClick: function () { - console.log(this.umap) - this.umap.star() + this._umap.star() }, }) @@ -562,7 +561,7 @@ L.Control.Embed = L.Control.Button.extend({ }, onClick: function () { - this.umap.share.open() + this._umap.share.open() }, }) @@ -655,7 +654,7 @@ U.TileLayerChooser = L.Control.extend({ L.DomUtil.createTitle(container, L._('Change tilelayers'), 'icon-tilelayer') this._tilelayers_container = L.DomUtil.create('ul', '', container) this.buildList(options) - const panel = options.edit ? this.map.umap.editPanel : this.map.umap.panel + const panel = options.edit ? this.map._umap.editPanel : this.map._umap.panel panel.open({ content: container }) }, @@ -708,8 +707,8 @@ U.AttributionControl = L.Control.Attribution.extend({ this._container.innerHTML = '' const container = L.DomUtil.create('div', 'attribution-container', this._container) container.innerHTML = credits - const shortCredit = this._map.umap.getOption('shortCredit') - const captionMenus = this._map.umap.getOption('captionMenus') + const shortCredit = this._map._umap.getProperty('shortCredit') + const captionMenus = this._map._umap.getProperty('captionMenus') if (shortCredit) { L.DomUtil.element({ tagName: 'span', @@ -720,7 +719,7 @@ U.AttributionControl = L.Control.Attribution.extend({ if (captionMenus) { const link = L.DomUtil.add('a', '', container, ` — ${L._('Open caption')}`) L.DomEvent.on(link, 'click', L.DomEvent.stop) - .on(link, 'click', () => this._map.umap.openCaption()) + .on(link, 'click', () => this._map._umap.openCaption()) .on(link, 'dblclick', L.DomEvent.stop) } if (window.top === window.self && captionMenus) { @@ -912,7 +911,7 @@ U.SearchControl = L.Control.extend({ this.map.fire('dataload', { id: id }) }) this.search.resultsContainer = resultsContainer - this.map.umap.panel.open({ content: container }).then(input.focus()) + this.map._umap.panel.open({ content: container }).then(input.focus()) }, }) @@ -953,7 +952,7 @@ L.Control.Loading.include({ U.Editable = L.Editable.extend({ initialize: function (umap, options) { - this.umap = umap + this._umap = umap L.Editable.prototype.initialize.call(this, umap._leafletMap, options) this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip) // Layer for items added by users @@ -962,7 +961,7 @@ U.Editable = L.Editable.extend({ }) this.on('editable:drawing:commit', function (event) { event.layer.feature.isDirty = true - if (this.umap.editedFeature !== event.layer) event.layer.feature.edit(event) + if (this._umap.editedFeature !== event.layer) event.layer.feature.edit(event) }) this.on('editable:editing', (event) => { const feature = event.layer.feature @@ -984,24 +983,24 @@ U.Editable = L.Editable.extend({ }, createPolyline: function (latlngs) { - const datalayer = this.umap.defaultEditDataLayer() - const point = new U.LineString(datalayer, { + const datalayer = this._umap.defaultEditDataLayer() + const point = new U.LineString(this._umap, datalayer, { geometry: { type: 'LineString', coordinates: [] }, }) return point.ui }, createPolygon: function (latlngs) { - const datalayer = this.umap.defaultEditDataLayer() - const point = new U.Polygon(datalayer, { + const datalayer = this._umap.defaultEditDataLayer() + const point = new U.Polygon(this._umap, datalayer, { geometry: { type: 'Polygon', coordinates: [] }, }) return point.ui }, createMarker: function (latlng) { - const datalayer = this.umap.defaultEditDataLayer() - const point = new U.Point(datalayer, { + const datalayer = this._umap.defaultEditDataLayer() + const point = new U.Point(this._umap, datalayer, { geometry: { type: 'Point', coordinates: [latlng.lng, latlng.lat] }, }) return point.ui @@ -1009,15 +1008,15 @@ U.Editable = L.Editable.extend({ _getDefaultProperties: function () { const result = {} - if (this.umap.properties.featuresHaveOwner?.user) { - result.geojson = { properties: { owner: this.umap.properties.user.id } } + if (this._umap.properties.featuresHaveOwner?.user) { + result.geojson = { properties: { owner: this._umap.properties.user.id } } } return result }, connectCreatedToMap: function (layer) { // Overrided from Leaflet.Editable - const datalayer = this.umap.defaultEditDataLayer() + const datalayer = this._umap.defaultEditDataLayer() datalayer.addFeature(layer.feature) layer.isDirty = true return layer @@ -1025,7 +1024,7 @@ U.Editable = L.Editable.extend({ drawingTooltip: function (e) { if (e.layer instanceof L.Marker && e.type === 'editable:drawing:start') { - this.umap.tooltip.open({ content: L._('Click to add a marker') }) + this._umap.tooltip.open({ content: L._('Click to add a marker') }) } if (!(e.layer instanceof L.Polyline)) { // only continue with Polylines and Polygons @@ -1072,12 +1071,12 @@ U.Editable = L.Editable.extend({ } } if (content) { - this.umap.tooltip.open({ content: content }) + this._umap.tooltip.open({ content: content }) } }, closeTooltip: function () { - this.umap.closeTooltip() + this._umap.closeTooltip() }, onVertexRawClick: (e) => { @@ -1088,7 +1087,7 @@ U.Editable = L.Editable.extend({ onEscape: function () { this.once('editable:drawing:end', (event) => { - this.umap.tooltip.close() + this._umap.tooltip.close() // Leaflet.Editable will delete the drawn shape if invalid // (eg. line has only one drawn point) // So let's check if the layer has no more shape diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index 357113d5..c7ea1bed 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -221,21 +221,16 @@ L.FormBuilder.Element.include({ this.label = L.DomUtil.create('label', '', this.getLabelParent()) this.label.textContent = this.label.title = this.options.label if (this.options.helpEntries) { - this.builder.umap.help.button(this.label, this.options.helpEntries) + this.builder._umap.help.button(this.label, this.options.helpEntries) } else if (this.options.helpTooltip) { const info = L.DomUtil.create('i', 'info', this.label) - L.DomEvent.on( - info, - 'mouseover', - function () { - this.builder.umap.tooltip.open({ - anchor: info, - content: this.options.helpTooltip, - position: 'top', - }) - }, - this - ) + L.DomEvent.on(info, 'mouseover', () => { + this.builder._umap.tooltip.open({ + anchor: info, + content: this.options.helpTooltip, + position: 'top', + }) + }) } } }, @@ -359,7 +354,7 @@ L.FormBuilder.SlideshowDelay = L.FormBuilder.IntSelect.extend({ L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({ getOptions: function () { const options = [] - this.builder.umap.eachDataLayerReverse((datalayer) => { + this.builder._umap.eachDataLayerReverse((datalayer) => { if ( datalayer.isLoaded() && !datalayer.isDataReadOnly() && @@ -376,11 +371,11 @@ L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({ }, toJS: function () { - return this.builder.umap.datalayers[this.value()] + return this.builder._umap.datalayers[this.value()] }, set: function () { - this.builder.umap.lastUsedDataLayer = this.toJS() + this.builder._umap.lastUsedDataLayer = this.toJS() this.obj.changeDataLayer(this.toJS()) }, }) @@ -469,8 +464,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ onDefine: async function () { this.buttons.innerHTML = '' this.footer.innerHTML = '' - const [{ pictogram_list }, response, error] = await this.builder.umap.server.get( - this.builder.umap.properties.urls.pictogram_list_json + const [{ pictogram_list }, response, error] = await this.builder._umap.server.get( + this.builder._umap.properties.urls.pictogram_list_json ) if (!error) this.pictogram_list = pictogram_list this.buildTabs() @@ -1168,7 +1163,7 @@ U.FormBuilder = L.FormBuilder.extend({ }, initialize: function (obj, fields, options = {}) { - this.umap = obj.umap || options.umap + this._umap = obj._umap || options.umap this.computeDefaultOptions() L.FormBuilder.prototype.initialize.call(this, obj, fields, options) this.on('finish', this.finish)