diff --git a/umap/static/umap/js/components/fragment.js b/umap/static/umap/js/components/fragment.js index 63613ca1..d76aaf46 100644 --- a/umap/static/umap/js/components/fragment.js +++ b/umap/static/umap/js/components/fragment.js @@ -1,6 +1,8 @@ +import Umap from '../modules/umap.js' + class UmapFragment extends HTMLElement { connectedCallback() { - new U.Map(this.firstElementChild.id, JSON.parse(this.dataset.settings)) + new Umap(this.firstElementChild.id, JSON.parse(this.dataset.settings)) } } diff --git a/umap/static/umap/js/modules/browser.js b/umap/static/umap/js/modules/browser.js index 6af51fa6..b8f59dac 100644 --- a/umap/static/umap/js/modules/browser.js +++ b/umap/static/umap/js/modules/browser.js @@ -6,9 +6,9 @@ import { EXPORT_FORMATS } from './formatter.js' import ContextMenu from './ui/contextmenu.js' export default class Browser { - constructor(map) { - this.map = map - this.map.on('moveend', this.onMoveEnd, this) + constructor(umap) { + this.umap = umap + this.umap._leafletMap.on('moveend', this.onMoveEnd, this) this.options = { filter: '', inBbox: false, @@ -82,7 +82,7 @@ export default class Browser { updateDatalayer(datalayer) { // Compute once, but use it for each feature later. - this.bounds = this.map.getBounds() + this.bounds = this.umap._leafletMap.getBounds() const parent = DomUtil.get(this.datalayerId(datalayer)) // Panel is not open if (!parent) return @@ -115,10 +115,10 @@ export default class Browser { } onFormChange() { - this.map.eachBrowsableDataLayer((datalayer) => { + this.umap.eachBrowsableDataLayer((datalayer) => { datalayer.resetLayer(true) this.updateDatalayer(datalayer) - if (this.map.fullPanel?.isOpen()) datalayer.tableEdit() + if (this.umap.fullPanel?.isOpen()) datalayer.tableEdit() }) this.toggleBadge() } @@ -132,13 +132,13 @@ export default class Browser { } hasFilters() { - return !!this.options.filter || this.map.facets.isActive() + return !!this.options.filter || this.umap.facets.isActive() } onMoveEnd() { if (!this.isOpen()) return const isListDynamic = this.options.inBbox - this.map.eachBrowsableDataLayer((datalayer) => { + this.umap.eachBrowsableDataLayer((datalayer) => { if (!isListDynamic && !datalayer.hasDynamicData()) return this.updateDatalayer(datalayer) }) @@ -147,7 +147,7 @@ export default class Browser { update() { if (!this.isOpen()) return this.dataContainer.innerHTML = '' - this.map.eachBrowsableDataLayer((datalayer) => { + this.umap.eachBrowsableDataLayer((datalayer) => { this.addDataLayer(datalayer, this.dataContainer) }) } @@ -186,9 +186,9 @@ export default class Browser { DomEvent.on(builder.form, 'reset', () => { window.setTimeout(builder.syncAll.bind(builder)) }) - if (this.map.options.facetKey) { - fields = this.map.facets.build() - filtersBuilder = new L.FormBuilder(this.map.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 +206,7 @@ export default class Browser { textContent: translate('Reset all'), }) - this.map.panel.open({ + this.umap.panel.open({ content: container, className: 'umap-browser', }) @@ -230,7 +230,7 @@ export default class Browser { `) container.appendChild(toolbox) toggle.addEventListener('click', () => this.toggleLayers()) - fitBounds.addEventListener('click', () => this.map.fitDataBounds()) + fitBounds.addEventListener('click', () => this.umap.fitDataBounds()) download.addEventListener('click', () => this.downloadVisible(download)) } @@ -240,7 +240,7 @@ export default class Browser { for (const format of Object.keys(EXPORT_FORMATS)) { items.push({ label: format, - action: () => this.map.share.download(format), + action: () => this.umap.share.download(format), }) } menu.openBelow(element, items) @@ -250,10 +250,10 @@ export default class Browser { // If at least one layer is shown, hide it // otherwise show all let allHidden = true - this.map.eachBrowsableDataLayer((datalayer) => { + this.umap.eachBrowsableDataLayer((datalayer) => { if (datalayer.isVisible()) allHidden = false }) - this.map.eachBrowsableDataLayer((datalayer) => { + this.umap.eachBrowsableDataLayer((datalayer) => { if (allHidden) { datalayer.show() } else { @@ -271,7 +271,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.openBrowser, map) + DomEvent.on(button, 'click', map.umap.openBrowser, map.umap) return button } } diff --git a/umap/static/umap/js/modules/caption.js b/umap/static/umap/js/modules/caption.js index d68d478f..1fa62e48 100644 --- a/umap/static/umap/js/modules/caption.js +++ b/umap/static/umap/js/modules/caption.js @@ -3,8 +3,8 @@ import { translate } from './i18n.js' import * as Utils from './utils.js' export default class Caption { - constructor(map) { - this.map = map + constructor(umap) { + this.umap = umap } isOpen() { @@ -21,38 +21,36 @@ export default class Caption { const hgroup = DomUtil.element({ tagName: 'hgroup', parent: container }) DomUtil.createTitle( hgroup, - this.map.getDisplayName(), + this.umap.getDisplayName(), 'icon-caption icon-block', 'map-name' ) - this.map.addAuthorLink('h4', hgroup) - if (this.map.options.description) { + const title = Utils.loadTemplate('
') + hgroup.appendChild(title) + this.umap.addAuthorLink(title) + if (this.umap.properties.description) { const description = DomUtil.element({ tagName: 'div', className: 'umap-map-description text', - safeHTML: Utils.toHTML(this.map.options.description), + safeHTML: Utils.toHTML(this.umap.properties.description), parent: container, }) } const datalayerContainer = DomUtil.create('div', 'datalayer-container', container) - this.map.eachDataLayerReverse((datalayer) => + this.umap.eachDataLayerReverse((datalayer) => this.addDataLayer(datalayer, datalayerContainer) ) const creditsContainer = DomUtil.create('div', 'credits-container', container) this.addCredits(creditsContainer) - this.map.panel.open({ content: container }).then(() => { + this.umap.panel.open({ content: container }).then(() => { // Create the legend when the panel is actually on the DOM - this.map.eachDataLayerReverse((datalayer) => datalayer.renderLegend()) + this.umap.eachDataLayerReverse((datalayer) => datalayer.renderLegend()) }) } addDataLayer(datalayer, container) { if (!datalayer.options.inCaption) return - const p = DomUtil.create( - 'p', - `caption-item ${datalayer.cssId}`, - container - ) + const p = DomUtil.create('p', `caption-item ${datalayer.cssId}`, container) const legend = DomUtil.create('span', 'datalayer-legend', p) const headline = DomUtil.create('strong', '', p) if (datalayer.options.description) { @@ -69,16 +67,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.map.options.shortCredit || this.map.options.longCredit) { + if (this.umap.properties.shortCredit || this.umap.properties.longCredit) { DomUtil.element({ tagName: 'p', parent: credits, safeHTML: Utils.toHTML( - this.map.options.longCredit || this.map.options.shortCredit + this.umap.properties.longCredit || this.umap.properties.shortCredit ), }) } - if (this.map.options.licence) { + if (this.umap.properties.licence) { const licence = DomUtil.add( 'p', '', @@ -88,8 +86,8 @@ export default class Caption { DomUtil.createLink( '', licence, - this.map.options.licence.name, - this.map.options.licence.url + this.umap.properties.licence.name, + this.umap.properties.licence.url ) } else { DomUtil.add('p', '', credits, translate('No licence has been set')) @@ -100,19 +98,19 @@ export default class Caption { DomUtil.element({ tagName: 'strong', parent: tilelayerCredit, - textContent: `${this.map.selected_tilelayer.options.name} `, + textContent: `${this.umap._leafletMap.selectedTilelayer.options.name} `, }) DomUtil.element({ tagName: 'span', parent: tilelayerCredit, - safeHTML: this.map.selected_tilelayer.getAttribution(), + safeHTML: this.umap._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.map.options.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 260ed28f..cd118d30 100644 --- a/umap/static/umap/js/modules/data/features.js +++ b/umap/static/umap/js/modules/data/features.js @@ -19,7 +19,7 @@ import loadPopup from '../rendering/popup.js' class Feature { constructor(datalayer, geojson = {}, id = null) { - this.sync = datalayer.map.sync_engine.proxy(this) + this.sync = datalayer.umap.sync_engine.proxy(this) this._marked_for_deletion = false this._isDirty = false this._ui = null @@ -69,8 +69,8 @@ class Feature { return this._ui } - get map() { - return this.datalayer?.map + get umap() { + return this.datalayer?.umap } get center() { @@ -168,7 +168,7 @@ class Feature { } getSlug() { - return this.properties[this.map.getOption('slugKey') || 'name'] || '' + return this.properties[this.umap.getOption('slugKey') || 'name'] || '' } getPermalink() { @@ -196,10 +196,10 @@ class Feature { return } // TODO deal with an event instead? - if (this.map.slideshow) { - this.map.slideshow.current = this + if (this.umap.slideshow) { + this.umap.slideshow.current = this } - this.map.currentFeature = this + this.umap.currentFeature = this this.attachPopup() this.ui.openPopup(latlng || this.center) } @@ -209,7 +209,7 @@ class Feature { return field.startsWith('properties.') }) if (impactData) { - if (this.map.currentFeature === this) { + if (this.umap.currentFeature === this) { this.view() } } @@ -217,7 +217,7 @@ class Feature { } edit(event) { - if (!this.map.editEnabled || this.isReadOnly()) return + if (!this.umap.editEnabled || this.isReadOnly()) return const container = DomUtil.create('div', 'umap-feature-container') DomUtil.createTitle( container, @@ -256,12 +256,12 @@ class Feature { translate('Advanced actions') ) this.getAdvancedEditActions(advancedActions) - const onLoad = this.map.editPanel.open({ content: container }) + const onLoad = this.umap.editPanel.open({ content: container }) onLoad.then(() => { builder.helpers['properties.name'].input.focus() }) - this.map.editedFeature = this - if (!this.ui.isOnScreen(this.map.getBounds())) this.zoomTo(event) + this.umap.editedFeature = this + if (!this.ui.isOnScreen(this.umap._leafletMap.getBounds())) this.zoomTo(event) } getAdvancedEditActions(container) { @@ -270,7 +270,7 @@ class Feature { ${translate('Delete')} `) button.addEventListener('click', () => { - this.confirmDelete().then(() => this.map.editPanel.close()) + this.confirmDelete().then(() => this.umap.editPanel.close()) }) container.appendChild(button) } @@ -333,7 +333,7 @@ class Feature { if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic) { return false } - return this.map.getOption('displayPopupFooter') + return this.umap.getOption('displayPopupFooter') } getPopupClass() { @@ -347,7 +347,7 @@ class Feature { } async confirmDelete() { - const confirmed = await this.map.dialog.confirm( + const confirmed = await this.umap.dialog.confirm( translate('Are you sure you want to delete the feature?') ) if (confirmed) { @@ -359,7 +359,7 @@ class Feature { del(sync) { this.isDirty = true - this.map.closePopup() + this.umap._leafletMap.closePopup() if (this.datalayer) { this.datalayer.removeFeature(this, sync) } @@ -422,7 +422,7 @@ class Feature { } else if (this.datalayer) { value = this.datalayer.getOption(option, this) } else { - value = this.map.getOption(option) + value = this.umap.getOption(option) } return value } @@ -432,19 +432,19 @@ class Feature { // 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.map.getDefaultOption(option) + if (U.Utils.hasVar(value)) value = this.umap.getDefaultOption(option) } return value } zoomTo({ easing, latlng, callback } = {}) { - if (easing === undefined) easing = this.map.getOption('easing') - if (callback) this.map.once('moveend', callback.bind(this)) + if (easing === undefined) easing = this.umap.getOption('easing') + if (callback) this.umap._leafletMap.once('moveend', callback.bind(this)) if (easing) { - this.map.flyTo(this.center, this.getBestZoom()) + this.umap._leafletMap.flyTo(this.center, this.getBestZoom()) } else { latlng = latlng || this.center - this.map.setView(latlng, this.getBestZoom() || this.map.getZoom()) + this.umap._leafletMap.setView(latlng, this.getBestZoom() || this.umap._leafletMap.getZoom()) } } @@ -500,7 +500,7 @@ class Feature { isFiltered() { const filterKeys = this.datalayer.getFilterKeys() - const filter = this.map.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 +525,10 @@ class Feature { } matchFacets() { - const selected = this.map.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.map.facets.getParser(type) + const parser = this.umap.facets.getParser(type) value = parser(value) switch (type) { case 'date': @@ -562,10 +562,10 @@ class Feature { extendedProperties() { // Include context properties - const properties = this.map.getGeoContext() + const properties = this.umap.getGeoContext() const locale = L.getLocale() if (locale) properties.locale = locale - if (L.lang) properties.lang = L.lang + if (U.lang) properties.lang = U.lang properties.rank = this.getRank() + 1 properties.layer = this.datalayer.getName() if (this.ui._map && this.hasGeom()) { @@ -612,10 +612,10 @@ class Feature { label: translate('Copy as GeoJSON'), action: () => { L.Util.copyToClipboard(JSON.stringify(this.toGeoJSON())) - this.map.tooltip.open({ content: L._('✅ Copied!') }) + this.umap.tooltip.open({ content: L._('✅ Copied!') }) }, }) - if (this.map.editEnabled && !this.isReadOnly()) { + if (this.umap.editEnabled && !this.isReadOnly()) { items = items.concat(this.getContextMenuEditItems(event)) } return items @@ -623,7 +623,7 @@ class Feature { getContextMenuEditItems() { let items = ['-'] - if (this.map.editedFeature !== this) { + if (this.umap.editedFeature !== this) { items.push({ label: `${translate('Edit this feature')} (⇧+Click)`, action: () => this.edit(), @@ -631,7 +631,7 @@ class Feature { } items = items.concat( { - label: this.map.help.displayLabel('EDIT_FEATURE_LAYER'), + label: this.umap.help.displayLabel('EDIT_FEATURE_LAYER'), action: () => this.datalayer.edit(), }, { @@ -750,17 +750,17 @@ class Path extends Feature { } edit(event) { - if (this.map.editEnabled) { + if (this.umap.editEnabled) { super.edit(event) if (!this.ui.editEnabled()) this.ui.makeGeometryEditable() } } _toggleEditing(event) { - if (this.map.editEnabled) { + if (this.umap.editEnabled) { if (this.ui.editEnabled()) { this.endEdit() - this.map.editPanel.close() + this.umap.editPanel.close() } else { this.edit(event) } @@ -786,7 +786,9 @@ class Path extends Feature { } getBestZoom() { - return this.getOption('zoomTo') || this.map.getBoundsZoom(this.bounds, true) + return ( + this.getOption('zoomTo') || this.umap._leafletMap.getBoundsZoom(this.bounds, true) + ) } endEdit() { @@ -825,11 +827,14 @@ class Path extends Feature { zoomTo({ easing, callback }) { // Use bounds instead of centroid for paths. - easing = easing || this.map.getOption('easing') + easing = easing || this.umap.getOption('easing') if (easing) { - this.map.flyToBounds(this.bounds, this.getBestZoom()) + this.umap._leafletMap.flyToBounds(this.bounds, this.getBestZoom()) } else { - this.map.fitBounds(this.bounds, this.getBestZoom() || this.map.getZoom()) + this.umap._leafletMap.fitBounds( + this.bounds, + this.getBestZoom() || this.umap._leafletMap.getZoom() + ) } if (callback) callback.call(this) } @@ -840,7 +845,7 @@ class Path extends Feature { label: translate('Display measure'), action: () => Alert.info(this.ui.getMeasure()), }) - if (this.map.editEnabled && !this.isReadOnly() && this.isMulti()) { + if (this.umap.editEnabled && !this.isReadOnly() && this.isMulti()) { items.push(...this.getContextMenuMultiItems(event)) } return items @@ -871,11 +876,11 @@ class Path extends Feature { getContextMenuEditItems(event) { const items = super.getContextMenuEditItems(event) - if (this.map?.editedFeature !== this && this.isSameClass(this.map.editedFeature)) { + if (this.map?.editedFeature !== this && this.isSameClass(this.umap.editedFeature)) { items.push({ label: translate('Transfer shape to edited feature'), action: () => { - this.transferShape(event.latlng, this.map.editedFeature) + this.transferShape(event.latlng, this.umap.editedFeature) }, }) } @@ -977,8 +982,8 @@ export class LineString extends Path { } const a = toMerge[0] const b = toMerge[1] - const p1 = this.map.latLngToContainerPoint(a[a.length - 1]) - const p2 = this.map.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() @@ -1022,7 +1027,7 @@ export class LineString extends Path { }) } else if (index === 0 || index === event.vertex.getLastIndex()) { items.push({ - label: this.map.help.displayLabel('CONTINUE_LINE'), + label: this.umap.help.displayLabel('CONTINUE_LINE'), action: () => event.vertex.continue(), }) } diff --git a/umap/static/umap/js/modules/data/layer.js b/umap/static/umap/js/modules/data/layer.js index 1602cf52..33098d2c 100644 --- a/umap/static/umap/js/modules/data/layer.js +++ b/umap/static/umap/js/modules/data/layer.js @@ -37,10 +37,10 @@ const LAYER_MAP = LAYER_TYPES.reduce((acc, klass) => { }, {}) export class DataLayer extends ServerStored { - constructor(map, data) { + constructor(umap, data) { super() - this.map = map - this.sync = map.sync_engine.proxy(this) + this.umap = umap + this.sync = umap.sync_engine.proxy(this) this._index = Array() this._features = {} this._geojson = null @@ -48,8 +48,11 @@ export class DataLayer extends ServerStored { this._loaded = false // Are layer metadata loaded this._dataloaded = false // Are layer data loaded - this.parentPane = this.map.getPane('overlayPane') - this.pane = this.map.createPane(`datalayer${stamp(this)}`, this.parentPane) + this.parentPane = this.umap._leafletMap.getPane('overlayPane') + this.pane = this.umap._leafletMap.createPane( + `datalayer${stamp(this)}`, + this.parentPane + ) this.pane.dataset.id = stamp(this) // FIXME: should be on layer this.renderer = L.svg({ pane: this.pane }) @@ -128,7 +131,7 @@ export class DataLayer extends ServerStored { for (const impact of impacts) { switch (impact) { case 'ui': - this.map.onDataLayersChanged() + this.umap.onDataLayersChanged() break case 'data': if (fields.includes('options.type')) { @@ -153,8 +156,8 @@ export class DataLayer extends ServerStored { } autoLoaded() { - if (!this.map.datalayersFromQueryString) return this.options.displayOnLoad - const datalayerIds = this.map.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()) @@ -192,7 +195,7 @@ export class DataLayer extends ServerStored { const visible = this.isVisible() if (this.layer) this.layer.clearLayers() // delete this.layer? - if (visible) this.map.removeLayer(this.layer) + if (visible) this.umap._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. @@ -213,7 +216,7 @@ export class DataLayer extends ServerStored { if (!this.umap_id) return if (this._loading) return this._loading = true - const [geojson, response, error] = await this.map.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 @@ -234,7 +237,7 @@ export class DataLayer extends ServerStored { dataChanged() { if (!this.hasDataLoaded()) return - this.map.onDataLayersChanged() + this.umap.onDataLayersChanged() this.layer.dataChanged() } @@ -275,14 +278,14 @@ export class DataLayer extends ServerStored { reindex() { const features = Object.values(this._features) - Utils.sortFeatures(features, this.map.getOption('sortKey'), L.lang) + Utils.sortFeatures(features, this.umap.getOption('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.map.getZoom() + const zoom = this.umap._leafletMap.getZoom() return !((!Number.isNaN(from) && zoom < from) || (!Number.isNaN(to) && zoom > to)) } @@ -294,14 +297,14 @@ export class DataLayer extends ServerStored { if (!this.isRemoteLayer()) return if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return if (!this.isVisible()) return - let url = this.map.localizeUrl(this.options.remoteData.url) + let url = this.umap.localizeUrl(this.options.remoteData.url) if (this.options.remoteData.proxy) { - url = this.map.proxyUrl(url, this.options.remoteData.ttl) + url = this.umap.proxyUrl(url, this.options.remoteData.ttl) } - const response = await this.map.request.get(url) + const response = await this.umap.request.get(url) if (response?.ok) { this.clear() - this.map.formatter + this.umap.formatter .parse(await response.text(), this.options.remoteData.format) .then((geojson) => this.fromGeoJSON(geojson)) } @@ -341,25 +344,23 @@ export class DataLayer extends ServerStored { connectToMap() { const id = stamp(this) - if (!this.map.datalayers[id]) { - this.map.datalayers[id] = this + if (!this.umap.datalayers[id]) { + this.umap.datalayers[id] = this } - if (!this.map.datalayers_index.includes(this)) { - this.map.datalayers_index.push(this) + if (!this.umap.datalayersIndex.includes(this)) { + this.umap.datalayersIndex.push(this) } - this.map.onDataLayersChanged() + this.umap.onDataLayersChanged() } _dataUrl() { - const template = this.map.options.urls.datalayer_view - - let url = Utils.template(template, { + let url = this.umap.urls.get('datalayer_view', { pk: this.umap_id, - map_id: this.map.options.umap_id, + map_id: this.umap.properties.umap_id, }) // No browser cache for owners/editors. - if (this.map.hasEditMode()) url = `${url}?${Date.now()}` + if (this.umap.hasEditMode()) url = `${url}?${Date.now()}` return url } @@ -386,7 +387,7 @@ export class DataLayer extends ServerStored { this._index.push(id) this._features[id] = feature this.indexProperties(feature) - this.map.features_index[feature.getSlug()] = feature + this.umap.featuresIndex[feature.getSlug()] = feature this.showFeature(feature) this.dataChanged() } @@ -395,7 +396,7 @@ export class DataLayer extends ServerStored { const id = stamp(feature) if (sync !== false) feature.sync.delete() this.hideFeature(feature) - delete this.map.features_index[feature.getSlug()] + delete this.umap.featuresIndex[feature.getSlug()] feature.disconnectFromDataLayer(this) this._index.splice(this._index.indexOf(id), 1) delete this._features[id] @@ -446,7 +447,8 @@ export class DataLayer extends ServerStored { const collection = Array.isArray(geojson) ? geojson : geojson.features || geojson.geometries - Utils.sortFeatures(collection, this.map.getOption('sortKey'), L.lang) + if (!collection) return + Utils.sortFeatures(collection, this.umap.getOption('sortKey'), U.lang) for (const feature of collection) { this.makeFeature(feature, sync) } @@ -486,7 +488,7 @@ export class DataLayer extends ServerStored { } async importRaw(raw, format) { - this.map.formatter + this.umap.formatter .parse(raw, format) .then((geojson) => this.addData(geojson)) .then(() => this.zoomTo()) @@ -507,35 +509,35 @@ export class DataLayer extends ServerStored { } async importFromUrl(uri, type) { - uri = this.map.localizeUrl(uri) - const response = await this.map.request.get(uri) + uri = this.umap.localizeUrl(uri) + const response = await this.umap.request.get(uri) if (response?.ok) { this.importRaw(await response.text(), type) } } getColor() { - return this.options.color || this.map.getOption('color') + return this.options.color || this.umap.getOption('color') } getDeleteUrl() { - return Utils.template(this.map.options.urls.datalayer_delete, { + return this.umap.urls.get('datalayer_delete', { pk: this.umap_id, - map_id: this.map.options.umap_id, + map_id: this.umap.properties.umap_id, }) } getVersionsUrl() { - return Utils.template(this.map.options.urls.datalayer_versions, { + return this.umap.urls.get('datalayer_versions', { pk: this.umap_id, - map_id: this.map.options.umap_id, + map_id: this.umap.properties.umap_id, }) } getVersionUrl(name) { - return Utils.template(this.map.options.urls.datalayer_version, { + return this.umap.urls.get('datalayer_version', { pk: this.umap_id, - map_id: this.map.options.umap_id, + map_id: this.umap.properties.umap_id, name: name, }) } @@ -556,17 +558,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.map.createDataLayer(options) + const datalayer = this.umap.createDataLayer(options) datalayer.fromGeoJSON(geojson) return datalayer } erase() { this.hide() - this.map.datalayers_index.splice(this.getRank(), 1) + this.umap.datalayersIndex.splice(this.getRank(), 1) this.parentPane.removeChild(this.pane) - this.map.onDataLayersChanged() - this.layer.onDelete(this.map) + this.umap.onDataLayersChanged() + this.layer.onDelete(this.umap._leafletMap) this.propagateDelete() this._leaflet_events_bk = this._leaflet_events this.clear() @@ -597,7 +599,7 @@ export class DataLayer extends ServerStored { } edit() { - if (!this.map.editEnabled || !this.isLoaded()) { + if (!this.umap.editEnabled || !this.isLoaded()) { return } const container = DomUtil.create('div', 'umap-layer-properties-container') @@ -631,7 +633,7 @@ export class DataLayer extends ServerStored { DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers') let builder = new U.FormBuilder(this, metadataFields, { callback(e) { - this.map.onDataLayersChanged() + this.umap.onDataLayersChanged() if (e.helper.field === 'options.type') { this.edit() } @@ -742,7 +744,7 @@ export class DataLayer extends ServerStored { }, ], ] - if (this.map.options.urls.ajax_proxy) { + if (this.umap.properties.urls.ajax_proxy) { remoteDataFields.push([ 'options.remoteData.proxy', { @@ -768,7 +770,8 @@ export class DataLayer extends ServerStored { this ) - if (this.map.options.urls.datalayer_versions) this.buildVersionsFieldset(container) + if (this.umap.properties.urls.datalayer_versions) + this.buildVersionsFieldset(container) const advancedActions = DomUtil.createFieldset( container, @@ -781,7 +784,7 @@ export class DataLayer extends ServerStored { `) deleteButton.addEventListener('click', () => { this._delete() - this.map.editPanel.close() + this.umap.editPanel.close() }) advancedButtons.appendChild(deleteButton) @@ -820,9 +823,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.map.editDatalayers, this.map) + DomEvent.on(backButton, 'click', this.umap.editDatalayers, this.umap) - this.map.editPanel.open({ + this.umap.editPanel.open({ content: container, actions: [backButton], }) @@ -843,13 +846,13 @@ export class DataLayer extends ServerStored { if (this.layer?.defaults?.[option]) { return this.layer.defaults[option] } - return this.map.getOption(option, feature) + return this.umap.getOption(option, feature) } async buildVersionsFieldset(container) { const appendVersion = (data) => { const date = new Date(Number.parseInt(data.at, 10)) - const content = `${date.toLocaleString(L.lang)} (${Number.parseInt(data.size) / 1000}Kb)` + const content = `${date.toLocaleString(U.lang)} (${Number.parseInt(data.size) / 1000}Kb)` const el = DomUtil.create('div', 'umap-datalayer-version', versionsContainer) const button = DomUtil.createButton( '', @@ -864,7 +867,7 @@ export class DataLayer extends ServerStored { const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), { async callback() { - const [{ versions }, response, error] = await this.map.server.get( + const [{ versions }, response, error] = await this.umap.server.get( this.getVersionsUrl() ) if (!error) versions.forEach(appendVersion) @@ -874,11 +877,11 @@ export class DataLayer extends ServerStored { } async restore(version) { - if (!this.map.editEnabled) return - this.map.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.map.server.get( + const [geojson, response, error] = await this.umap.server.get( this.getVersionUrl(version) ) if (!error) { @@ -899,13 +902,13 @@ export class DataLayer extends ServerStored { } async show() { - this.map.addLayer(this.layer) + this.umap._leafletMap.addLayer(this.layer) if (!this.isLoaded()) await this.fetchData() this.propagateShow() } hide() { - this.map.removeLayer(this.layer) + this.umap._leafletMap.removeLayer(this.layer) this.propagateHide() } @@ -922,7 +925,7 @@ export class DataLayer extends ServerStored { const bounds = this.layer.getBounds() if (bounds.isValid()) { const options = { maxZoom: this.getOption('zoomTo') } - this.map.fitBounds(bounds, options) + this.umap._leafletMap.fitBounds(bounds, options) } } @@ -953,7 +956,7 @@ export class DataLayer extends ServerStored { } isVisible() { - return Boolean(this.layer && this.map.hasLayer(this.layer)) + return Boolean(this.layer && this.umap._leafletMap.hasLayer(this.layer)) } getFeatureByIndex(index) { @@ -990,7 +993,7 @@ export class DataLayer extends ServerStored { getPreviousBrowsable() { let id = this.getRank() let next - const index = this.map.datalayers_index + const index = this.umap.datalayersIndex while (((id = index[++id] ? id : 0), (next = index[id]))) { if (next === this || next.canBrowse()) break } @@ -1000,7 +1003,7 @@ export class DataLayer extends ServerStored { getNextBrowsable() { let id = this.getRank() let prev - const index = this.map.datalayers_index + const index = this.umap.datalayersIndex while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) { if (prev === this || prev.canBrowse()) break } @@ -1016,7 +1019,7 @@ export class DataLayer extends ServerStored { } getRank() { - return this.map.datalayers_index.indexOf(this) + return this.umap.datalayersIndex.indexOf(this) } isReadOnly() { @@ -1043,8 +1046,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.map.urls.get('datalayer_save', { - map_id: this.map.options.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 @@ -1056,7 +1059,7 @@ export class DataLayer extends ServerStored { } async _trySave(url, headers, formData) { - const [data, response, error] = await this.map.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( @@ -1072,7 +1075,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.map.saveAll() + await this.umap.saveAll() } } ) @@ -1101,9 +1104,9 @@ export class DataLayer extends ServerStored { async saveDelete() { if (this.umap_id) { - await this.map.server.post(this.getDeleteUrl()) + await this.umap.server.post(this.getDeleteUrl()) } - delete this.map.datalayers[stamp(this)] + delete this.umap.datalayers[stamp(this)] return true } @@ -1125,9 +1128,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.map.options.filterKey) return this.map.options.filterKey + if (this.umap.properties.filterKey) return this.umap.properties.filterKey if (this.getOption('labelKey')) return this.getOption('labelKey') - if (this.map.options.sortKey) return this.map.options.sortKey + if (this.umap.properties.sortKey) return this.umap.properties.sortKey return 'displayName' } @@ -1178,7 +1181,7 @@ export class DataLayer extends ServerStored { 'click', function () { if (!this.isVisible()) return - this.map.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 d595bab7..0debaaae 100644 --- a/umap/static/umap/js/modules/facets.js +++ b/umap/static/umap/js/modules/facets.js @@ -3,8 +3,8 @@ import { translate } from './i18n.js' import * as Utils from './utils.js' export default class Facets { - constructor(map) { - this.map = map + constructor(umap) { + this.umap = umap this.selected = {} } @@ -24,7 +24,7 @@ export default class Facets { this.selected[name] = selected } - this.map.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.map.options.facetKey) return defined - return (this.map.options.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.map.options.facetKey = this.dumps(defined) - this.map.isDirty = true + this.umap.properties.facetKey = this.dumps(defined) + this.umap.isDirty = true } } remove(property) { const defined = this.getDefined() defined.delete(property) - this.map.options.facetKey = this.dumps(defined) - this.map.isDirty = true + this.umap.properties.facetKey = this.dumps(defined) + this.umap.isDirty = true } } diff --git a/umap/static/umap/js/modules/formatter.js b/umap/static/umap/js/modules/formatter.js index 4f1059ed..c641da2f 100644 --- a/umap/static/umap/js/modules/formatter.js +++ b/umap/static/umap/js/modules/formatter.js @@ -3,24 +3,24 @@ import { translate } from './i18n.js' export const EXPORT_FORMATS = { geojson: { - formatter: async (map) => JSON.stringify(map.toGeoJSON(), null, 2), + formatter: async (umap) => JSON.stringify(umap.toGeoJSON(), null, 2), ext: '.geojson', filetype: 'application/json', }, gpx: { - formatter: async (map) => await map.formatter.toGPX(map.toGeoJSON()), + formatter: async (umap) => await umap.formatter.toGPX(umap.toGeoJSON()), ext: '.gpx', filetype: 'application/gpx+xml', }, kml: { - formatter: async (map) => await map.formatter.toKML(map.toGeoJSON()), + formatter: async (umap) => await umap.formatter.toKML(umap.toGeoJSON()), ext: '.kml', filetype: 'application/vnd.google-earth.kml+xml', }, csv: { - formatter: async (map) => { + formatter: async (umap) => { const table = [] - map.eachFeature((feature) => { + umap.eachFeature((feature) => { const row = feature.toGeoJSON().properties const center = feature.center delete row._umap_options diff --git a/umap/static/umap/js/modules/importer.js b/umap/static/umap/js/modules/importer.js index 036a372d..df41b4f4 100644 --- a/umap/static/umap/js/modules/importer.js +++ b/umap/static/umap/js/modules/importer.js @@ -48,8 +48,8 @@ const TEMPLATE = ` ` export default class Importer { - constructor(map) { - this.map = map + constructor(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.map.options.importers || {})) { + for (const [name, config] of Object.entries(this.umap.properties.importers || {})) { const register = (mod) => { - this.IMPORTERS.push(new mod.Importer(this.map, 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.map.datalayers[this.layerId] || - this.map.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.map.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.map.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.map.editPanel.open({ content: this.container }) + const onLoad = this.umap.editPanel.open({ content: this.container }) onLoad.then(() => this.onLoad()) } @@ -251,16 +251,16 @@ export default class Importer { } full() { - this.map.once('postsync', this.map._setDefaultCenter) + this.umap._leafletMap.once('postsync', this.umap._leafletMap._setDefaultCenter) try { if (this.files.length) { for (const file of this.files) { - this.map.processFileToImport(file, null, 'umap') + this.umap.processFileToImport(file, null, 'umap') } } else if (this.raw) { - this.map.importRaw(this.raw) + this.umap.importRaw(this.raw) } else if (this.url) { - this.map.importFromUrl(this.url, this.format) + this.umap.importFromUrl(this.url, this.format) } } catch (e) { Alert.error(translate('Invalid umap data')) @@ -282,7 +282,7 @@ export default class Importer { url: this.url, format: this.format, } - if (this.map.options.urls.ajax_proxy) { + if (this.umap.properties.urls.ajax_proxy) { layer.options.remoteData.proxy = true layer.options.remoteData.ttl = SCHEMA.ttl.default } @@ -300,7 +300,7 @@ export default class Importer { if (this.clear) layer.empty() if (this.files.length) { for (const file of this.files) { - this.map.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 ce0a2655..ebf586ee 100644 --- a/umap/static/umap/js/modules/permissions.js +++ b/umap/static/umap/js/modules/permissions.js @@ -7,10 +7,10 @@ import * as Utils from './utils.js' // Dedicated object so we can deal with a separate dirty status, and thus // call the endpoint only when needed, saving one call at each save. export class MapPermissions extends ServerStored { - constructor(map) { + constructor(umap) { super() - this.setOptions(map.options.permissions) - this.map = map + this.setOptions(umap.properties.permissions) + this.umap = umap this._isDirty = false } @@ -28,11 +28,11 @@ export class MapPermissions extends ServerStored { } isOwner() { - return Boolean(this.map.options.user?.is_owner) + return Boolean(this.umap.properties.user?.is_owner) } isAnonymousMap() { - return !this.map.options.permissions.owner + return !this.umap.properties.permissions.owner } _editAnonymous(container) { @@ -43,7 +43,7 @@ export class MapPermissions extends ServerStored { { handler: 'IntSelect', label: translate('Who can edit'), - selectOptions: this.map.options.edit_statuses, + selectOptions: this.umap.properties.edit_statuses, }, ]) const builder = new U.FormBuilder(this, fields) @@ -58,7 +58,7 @@ export class MapPermissions extends ServerStored { ) } - if (this.map.options.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( @@ -90,7 +90,7 @@ export class MapPermissions extends ServerStored { { handler: 'IntSelect', label: translate('Who can edit'), - selectOptions: this.map.options.edit_statuses, + selectOptions: this.umap.properties.edit_statuses, }, ]) topFields.push([ @@ -98,20 +98,20 @@ export class MapPermissions extends ServerStored { { handler: 'IntSelect', label: translate('Who can view'), - selectOptions: this.map.options.share_statuses, + selectOptions: this.umap.properties.share_statuses, }, ]) collaboratorsFields.push([ 'options.owner', { handler: 'ManageOwner', label: translate("Map's owner") }, ]) - if (this.map.options.user?.teams?.length) { + if (this.umap.properties.user?.teams?.length) { collaboratorsFields.push([ 'options.team', { handler: 'ManageTeam', label: translate('Attach map to a team'), - teams: this.map.options.user.teams, + teams: this.umap.properties.user.teams, }, ]) } @@ -136,20 +136,20 @@ export class MapPermissions extends ServerStored { } _editDatalayers(container) { - if (this.map.hasLayers()) { + if (this.umap.hasLayers()) { const fieldset = Utils.loadTemplate( `` ) container.appendChild(fieldset) - this.map.eachDataLayer((datalayer) => { + this.umap.eachDataLayer((datalayer) => { datalayer.permissions.edit(fieldset) }) } } edit() { - if (this.map.options.editMode !== 'advanced') return - if (!this.map.options.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,15 +158,15 @@ export class MapPermissions extends ServerStored { if (this.isAnonymousMap()) this._editAnonymous(container) else this._editWithOwner(container) this._editDatalayers(container) - this.map.editPanel.open({ content: container, className: 'dark' }) + this.umap.editPanel.open({ content: container, className: 'dark' }) } async attach() { - const [data, response, error] = await this.map.server.post(this.getAttachUrl()) + const [data, response, error] = await this.umap.server.post(this.getAttachUrl()) if (!error) { - this.options.owner = this.map.options.user + this.options.owner = this.umap.properties.user Alert.success(translate('Map has been attached to your account')) - this.map.editPanel.close() + this.umap.editPanel.close() } } @@ -186,40 +186,41 @@ export class MapPermissions extends ServerStored { formData.append('team', this.options.team?.id || '') formData.append('share_status', this.options.share_status) } - const [data, response, error] = await this.map.server.post( + const [data, response, error] = await this.umap.server.post( this.getUrl(), {}, formData ) if (!error) { this.commit() - this.map.fire('postsync') + this.umap._leafletMap.fire('postsync') return true } } getUrl() { - return Utils.template(this.map.options.urls.map_update_permissions, { - map_id: this.map.options.umap_id, + return this.umap.urls.get('map_update_permissions', { + map_id: this.umap.properties.umap_id, }) } getAttachUrl() { - return Utils.template(this.map.options.urls.map_attach_owner, { - map_id: this.map.options.umap_id, + return this.umap.urls.get('map_attach_owner', { + map_id: this.umap.properties.umap_id, }) } commit() { - this.map.options.permissions = Object.assign( - this.map.options.permissions, + this.umap.properties.permissions = Object.assign( + {}, + this.umap.properties.permissions, this.options ) } getShareStatusDisplay() { - if (this.map.options.share_statuses) { - return Object.fromEntries(this.map.options.share_statuses)[ + if (this.umap.properties.share_statuses) { + return Object.fromEntries(this.umap.properties.share_statuses)[ this.options.share_status ] } @@ -239,8 +240,8 @@ export class DataLayerPermissions extends ServerStored { this.datalayer = datalayer } - get map() { - return this.datalayer.map + get umap() { + return this.datalayer.umap } edit(container) { @@ -252,7 +253,7 @@ export class DataLayerPermissions extends ServerStored { label: translate('Who can edit "{layer}"', { layer: this.datalayer.getName(), }), - selectOptions: this.map.options.datalayer_edit_statuses, + selectOptions: this.umap.properties.datalayer_edit_statuses, }, ], ] @@ -264,8 +265,8 @@ export class DataLayerPermissions extends ServerStored { } getUrl() { - return this.map.urls.get('datalayer_permissions', { - map_id: this.map.options.umap_id, + return this.umap.urls.get('datalayer_permissions', { + map_id: this.umap.properties.umap_id, pk: this.datalayer.umap_id, }) } @@ -274,7 +275,7 @@ export class DataLayerPermissions extends ServerStored { if (!this.isDirty) return const formData = new FormData() formData.append('edit_status', this.options.edit_status) - const [data, response, error] = await this.map.server.post( + const [data, response, error] = await this.umap.server.post( this.getUrl(), {}, formData diff --git a/umap/static/umap/js/modules/rendering/layers/base.js b/umap/static/umap/js/modules/rendering/layers/base.js index 25951a27..2a7c5085 100644 --- a/umap/static/umap/js/modules/rendering/layers/base.js +++ b/umap/static/umap/js/modules/rendering/layers/base.js @@ -73,7 +73,7 @@ export const Default = FeatureGroup.extend({ initialize: function (datalayer) { this.datalayer = datalayer FeatureGroup.prototype.initialize.call(this) - LayerMixin.onInit.call(this, this.datalayer.map) + LayerMixin.onInit.call(this, this.datalayer.umap._leafletMap) }, onAdd: function (map) { diff --git a/umap/static/umap/js/modules/rendering/layers/classified.js b/umap/static/umap/js/modules/rendering/layers/classified.js index 7b5eada4..b5bb509e 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.map) + LayerMixin.onInit.call(this, this.datalayer.umap._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 af206795..0ce8249d 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.map) + LayerMixin.onInit.call(this, this.datalayer.umap._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 a8d7dca0..24e6b411 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.map) + LayerMixin.onInit.call(this, this.datalayer.umap._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 new file mode 100644 index 00000000..ce91b416 --- /dev/null +++ b/umap/static/umap/js/modules/rendering/map.js @@ -0,0 +1,583 @@ +// Goes here all code related to Leaflet, DOM and user interactions. +import { + Map as BaseMap, + DomUtil, + DomEvent, + latLngBounds, + latLng, + Control, + setOptions, +} from '../../../vendors/leaflet/leaflet-src.esm.js' +import { translate } from '../i18n.js' +import { uMapAlert as Alert } from '../../components/alerts/alert.js' +import * as Utils from '../utils.js' +import * as Icon from './icon.js' + +// Those options are not saved on the server, so they can live here +// instead of in umap.properties +BaseMap.mergeOptions({ + demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, '-y': 181, r: '' }, + attributionControl: false, +}) + +const ControlsMixin = { + HIDDABLE_CONTROLS: [ + 'zoom', + 'search', + 'fullscreen', + 'embed', + 'datalayers', + 'caption', + 'locate', + 'measure', + 'editinosm', + 'star', + 'tilelayers', + ], + + initControls: function () { + this.helpMenuActions = {} + this._controls = {} + + if (this.umap.hasEditMode() && !this.options.noControl) { + new U.EditControl(this).addTo(this) + + new U.DrawToolbar({ map: this }).addTo(this) + const editActions = [ + U.EditCaptionAction, + U.EditPropertiesAction, + U.EditLayersAction, + U.ChangeTileLayerAction, + U.UpdateExtentAction, + U.UpdatePermsAction, + U.ImportAction, + ] + if (this.options.editMode === 'advanced') { + new U.SettingsToolbar({ actions: editActions }).addTo(this) + } + } + this._controls.zoom = new Control.Zoom({ + 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.locate = new U.Locate(this, { + strings: { + title: translate('Center map on your location'), + }, + showPopup: false, + // We style this control in our own CSS for consistency with other controls, + // but the control breaks if we don't specify a class here, so a fake class + // will do. + icon: 'umap-fake-class', + iconLoading: 'umap-fake-class', + flyTo: this.options.easing, + onLocationError: (err) => U.Alert.error(err.message), + }) + this._controls.fullscreen = new Control.Fullscreen({ + title: { + false: translate('View Fullscreen'), + true: translate('Exit Fullscreen'), + }, + }) + this._controls.search = new U.SearchControl() + 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) + this._controls.editinosm = new Control.EditInOSM({ + position: 'topleft', + widgetOptions: { + helpText: translate( + 'Open this map extent in a map editor to provide more accurate data to OpenStreetMap' + ), + }, + }) + this._controls.measure = new L.MeasureControl().initHandler(this) + this._controls.more = new U.MoreControls() + this._controls.scale = L.control.scale() + 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._controls.tilelayers = new U.TileLayerControl(this) + }, + + renderControls: function () { + const hasSlideshow = Boolean(this.options.slideshow?.active) + const barEnabled = this.options.captionBar || hasSlideshow + document.body.classList.toggle('umap-caption-bar-enabled', barEnabled) + document.body.classList.toggle('umap-slideshow-enabled', hasSlideshow) + for (const control of Object.values(this._controls)) { + this.removeControl(control) + } + if (this.options.noControl) return + + this._controls.attribution = new U.AttributionControl().addTo(this) + if (this.options.miniMap) { + this.whenReady(function () { + 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'), + }, + }).addTo(this) + this._controls.miniMap._miniMap.invalidateSize() + } + }) + } + for (const name of this.HIDDABLE_CONTROLS) { + const status = this.umap.getOption(`${name}Control`) + if (status === false) continue + const control = this._controls[name] + if (!control) continue + control.addTo(this) + if (status === undefined || status === null) { + DomUtil.addClass(control._container, 'display-on-more') + } else { + DomUtil.removeClass(control._container, 'display-on-more') + } + } + if (this.umap.getOption('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) + this._controls.tilelayers.setLayers() + }, + + renderEditToolbar: function () { + const className = 'umap-main-edit-toolbox' + const container = + document.querySelector(`.${className}`) || + DomUtil.create('div', `${className} with-transition dark`, this._controlContainer) + container.innerHTML = '' + const leftContainer = DomUtil.create('div', 'umap-left-edit-toolbox', container) + const rightContainer = DomUtil.create('div', 'umap-right-edit-toolbox', container) + const logo = DomUtil.create('div', 'logo', leftContainer) + 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({ + content: translate('Edit the title of the map'), + anchor: nameButton, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }) + const shareStatusButton = DomUtil.createButton( + 'share-status', + leftContainer, + '', + this.umap.permissions.edit, + this.umap.permissions + ) + DomEvent.on(shareStatusButton, 'mouseover', () => { + this.umap.tooltip.open({ + content: translate('Update who can see and edit the map'), + anchor: shareStatusButton, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }) + if (this.options.editMode === 'advanced') { + DomEvent.on(nameButton, 'click', this.umap.editCaption, this.umap) + DomEvent.on( + shareStatusButton, + 'click', + this.umap.permissions.edit, + this.umap.permissions + ) + } + if (this.options.user?.id) { + const button = U.Utils.loadTemplate(` + + `) + rightContainer.appendChild(button) + const menu = new U.ContextMenu({ className: 'dark', fixed: true }) + const actions = [ + { + label: translate('New map'), + action: this.umap.urls.get('map_new'), + }, + { + label: translate('My maps'), + action: this.umap.urls.get('user_dashboard'), + }, + { + label: translate('My teams'), + action: this.umap.urls.get('user_teams'), + }, + ] + if (this.umap.urls.has('user_profile')) { + actions.push({ + label: translate('My profile'), + action: this.umap.urls.get('user_profile'), + }) + } + button.addEventListener('click', () => { + menu.openBelow(button, actions) + }) + } + + const connectedPeers = this.umap.sync.getNumberOfConnectedPeers() + if (connectedPeers !== 0) { + const connectedPeersCount = DomUtil.createButton( + 'leaflet-control-connected-peers', + rightContainer, + '' + ) + DomEvent.on(connectedPeersCount, 'mouseover', () => { + this.umap.tooltip.open({ + content: translate( + '{connectedPeers} peer(s) currently connected to this map', + { + connectedPeers: connectedPeers, + } + ), + anchor: connectedPeersCount, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }) + + const updateConnectedPeersCount = () => { + connectedPeersCount.innerHTML = this.sync.getNumberOfConnectedPeers() + } + updateConnectedPeersCount() + } + + this.umap.help.getStartedLink(rightContainer) + const controlEditCancel = DomUtil.createButton( + 'leaflet-control-edit-cancel', + rightContainer, + DomUtil.add('span', '', null, translate('Cancel edits')), + () => this.umap.askForReset() + ) + DomEvent.on(controlEditCancel, 'mouseover', () => { + this.umap.tooltip.open({ + content: this.umap.help.displayLabel('CANCEL'), + anchor: controlEditCancel, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }) + const controlEditDisable = DomUtil.createButton( + 'leaflet-control-edit-disable', + rightContainer, + DomUtil.add('span', '', null, translate('View')), + this.umap.disableEdit, + this.umap + ) + DomEvent.on(controlEditDisable, 'mouseover', () => { + this.umap.tooltip.open({ + content: this.umap.help.displayLabel('PREVIEW'), + anchor: controlEditDisable, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }) + const controlEditSave = DomUtil.createButton( + 'leaflet-control-edit-save button', + rightContainer, + DomUtil.add('span', '', null, translate('Save')), + () => this.umap.saveAll() + ) + DomEvent.on(controlEditSave, 'mouseover', () => { + this.umap.tooltip.open({ + content: this.umap.help.displayLabel('SAVE'), + anchor: controlEditSave, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }) + }, + + initCaptionBar: function () { + const container = DomUtil.create('div', 'umap-caption-bar', this._controlContainer) + const name = DomUtil.create('h3', 'map-name', container) + DomEvent.disableClickPropagation(container) + this.umap.addAuthorLink(container) + if (this.umap.getOption('captionMenus')) { + DomUtil.createButton( + 'umap-about-link flat', + container, + translate('Open caption'), + this.umap.openCaption, + this + ) + DomUtil.createButton( + 'umap-open-browser-link flat', + container, + translate('Browse data'), + () => this.openBrowser('data') + ) + if (this.options.facetKey) { + DomUtil.createButton( + 'umap-open-filter-link flat', + container, + translate('Filter data'), + () => this.openBrowser('filters') + ) + } + } + this.umap.onceDatalayersLoaded(function () { + this.slideshow.renderToolbox(container) + }) + }, +} + +const ManageTilelayerMixin = { + initTileLayers: function () { + this.tilelayers = [] + for (const props of this.options.tilelayers) { + const layer = this.createTileLayer(props) + this.tilelayers.push(layer) + if ( + this.options.tilelayer && + this.options.tilelayer.url_template === props.url_template + ) { + // Keep control over the displayed attribution for non custom tilelayers + this.options.tilelayer.attribution = props.attribution + } + } + if (this.options.tilelayer?.url_template && this.options.tilelayer.attribution) { + this.customTilelayer = this.createTileLayer(this.options.tilelayer) + this.selectTileLayer(this.customTilelayer) + } else { + this.selectTileLayer(this.tilelayers[0]) + } + if (this._controls) this._controls.tilelayers.setLayers() + }, + + createTileLayer: (tilelayer) => new L.TileLayer(tilelayer.url_template, tilelayer), + + selectTileLayer: function (tilelayer) { + if (tilelayer === this.selectedTilelayer) { + return + } + try { + this.addLayer(tilelayer) + this.fire('baselayerchange', { layer: tilelayer }) + if (this.selectedTilelayer) { + this.removeLayer(this.selectedTilelayer) + } + this.selectedTilelayer = tilelayer + if ( + !Number.isNaN(this.selectedTilelayer.options.minZoom) && + this.getZoom() < this.selectedTilelayer.options.minZoom + ) { + this.setZoom(this.selectedTilelayer.options.minZoom) + } + if ( + !Number.isNaN(this.selectedTilelayer.options.maxZoom) && + this.getZoom() > this.selectedTilelayer.options.maxZoom + ) { + this.setZoom(this.selectedTilelayer.options.maxZoom) + } + } catch (e) { + console.error(e) + this.removeLayer(tilelayer) + Alert.error(`${translate('Error in the tilelayer URL')}: ${tilelayer._url}`) + // Users can put tilelayer URLs by hand, and if they add wrong {variable}, + // Leaflet throw an error, and then the map is no more editable + } + this.setOverlay() + }, + + eachTileLayer: function (callback, context) { + const urls = [] + const callOne = (layer) => { + // Prevent adding a duplicate background, + // while adding selected/custom on top of the list + const url = layer.options.url_template + if (urls.indexOf(url) !== -1) return + callback.call(context, layer) + urls.push(url) + } + if (this.selectedTilelayer) callOne(this.selectedTilelayer) + if (this.customTilelayer) callOne(this.customTilelayer) + this.tilelayers.forEach(callOne) + }, + + setOverlay: function () { + if (!this.options.overlay || !this.options.overlay.url_template) return + const overlay = this.createTileLayer(this.options.overlay) + try { + this.addLayer(overlay) + if (this.overlay) this.removeLayer(this.overlay) + this.overlay = overlay + } catch (e) { + this.removeLayer(overlay) + console.error(e) + Alert.error(`${translate('Error in the overlay URL')}: ${overlay._url}`) + } + }, + + updateTileLayers: function () { + const callback = (tilelayer) => { + this.options.tilelayer = tilelayer.toJSON() + this.umap.isDirty = true + } + if (this._controls.tilelayersChooser) { + this._controls.tilelayersChooser.openSwitcher({ callback, edit: true }) + } + }, +} + +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], + initialize: function (umap, element) { + this.umap = umap + const options = this.umap.properties + + BaseMap.prototype.initialize.call(this, element, options) + + // After calling parent initialize, as we are doing initCenter our-selves + + this.loader = new Control.Loading() + this.loader.onAdd(this) + + if (!this.options.noControl) { + DomEvent.on(document.body, 'dataloading', (e) => this.fire('dataloading', e)) + DomEvent.on(document.body, 'dataload', (e) => this.fire('dataload', e)) + this.on('click', this.closeInplaceToolbar) + } + + this.on('baselayerchange', (e) => { + if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e) + }) + }, + + attachToDom: function () { + this.initControls() + // Needs locate control and hash to exist + this.initCenter() + this.initTileLayers() + // Needs tilelayer to exist for minimap + this.renderControls() + this.handleLimitBounds() + }, + + setOptions: function (options) { + setOptions(this, options) + }, + + closeInplaceToolbar: function () { + const toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id] + if (toolbar) toolbar.remove() + }, + + latLng: (a, b, c) => { + // manage geojson case and call original method + if (!(a instanceof L.LatLng) && a.coordinates) { + // Guess it's a geojson + a = [a.coordinates[1], a.coordinates[0]] + } + return latLng(a, b, c) + }, + + _setDefaultCenter: function () { + this.options.center = this.latLng(this.options.center) + this.setView(this.options.center, this.options.zoom) + }, + + initCenter: function () { + this._setDefaultCenter() + if (this.options.hash) this.addHash() + if (this.options.hash && this._hash.parseHash(location.hash)) { + // FIXME An invalid hash will cause the load to fail + this._hash.update() + } 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) + } else if (this.options.defaultView === 'latest') { + this.umap.onceDataLoaded(() => { + if (!this.umap.hasData()) return + const datalayer = this.umap.firstVisibleDatalayer() + let feature + if (datalayer) { + const feature = datalayer.getFeatureByIndex(-1) + if (feature) { + feature.zoomTo({ callback: this.options.noControl ? null : feature.view }) + return + } + } + }) + } + }, + + handleLimitBounds: function () { + const south = Number.parseFloat(this.options.limitBounds.south) + const west = Number.parseFloat(this.options.limitBounds.west) + const north = Number.parseFloat(this.options.limitBounds.north) + const east = Number.parseFloat(this.options.limitBounds.east) + if ( + !Number.isNaN(south) && + !Number.isNaN(west) && + !Number.isNaN(north) && + !Number.isNaN(east) + ) { + const bounds = latLngBounds([ + [south, west], + [north, east], + ]) + this.options.minZoom = this.getBoundsZoom(bounds, false) + try { + this.setMaxBounds(bounds) + } catch (e) { + // Unusable bounds, like -2 -2 -2 -2? + console.error('Error limiting bounds', e) + } + } else { + this.options.minZoom = 0 + this.setMaxBounds() + } + }, + + setMaxBounds: function (bounds) { + // Hack. Remove me when fix is released: + // https://github.com/Leaflet/Leaflet/pull/4494 + bounds = latLngBounds(bounds) + + if (!bounds.isValid()) { + this.options.maxBounds = null + return this.off('moveend', this._panInsideMaxBounds) + } + return BaseMap.prototype.setMaxBounds.call(this, bounds) + }, +}) diff --git a/umap/static/umap/js/modules/rendering/popup.js b/umap/static/umap/js/modules/rendering/popup.js index dc1e460a..b485c2c8 100644 --- a/umap/static/umap/js/modules/rendering/popup.js +++ b/umap/static/umap/js/modules/rendering/popup.js @@ -62,8 +62,8 @@ const Panel = Popup.extend({ }, onAdd: function (map) { - map.panel.setDefaultMode('expanded') - map.panel.open({ + map.umap.panel.setDefaultMode('expanded') + map.umap.panel.open({ content: this._content, actions: [Browser.backButton(map)], }) @@ -79,7 +79,7 @@ const Panel = Popup.extend({ }, onRemove: function (map) { - map.panel.close() + map.umap.panel.close() // fire events as in base class Popup.js:onRemove map.fire('popupclose', { popup: this }) diff --git a/umap/static/umap/js/modules/rendering/ui.js b/umap/static/umap/js/modules/rendering/ui.js index c01d309e..6e5a4d43 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.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.getContextMenuItems(event)) - this._map.contextmenu.open(event.originalEvent, items) + .concat(this._map.umap.getContextMenuItems(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.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.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.tooltip.open({ content: this.getMeasure(), anchor: this }) - } else if (this._map.editEnabled && !this._map.editedFeature) { - this._map.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.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.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 b584d0d8..21a0e06c 100644 --- a/umap/static/umap/js/modules/rules.js +++ b/umap/static/umap/js/modules/rules.js @@ -21,10 +21,10 @@ class Rule { set isDirty(status) { this._isDirty = status - if (status) this.map.isDirty = status + if (status) this.umap.isDirty = status } - constructor(map, condition = '', options = {}) { + constructor(umap, condition = '', options = {}) { // TODO make this public properties when browser coverage is ok // cf https://caniuse.com/?search=public%20class%20field this._condition = null @@ -37,14 +37,14 @@ class Rule { ['!=', this.not_equal], ['=', this.equal], ] - this.map = map + this.umap = umap this.active = true this.options = options this.condition = condition } render(fields) { - this.map.render(fields) + this.umap.render(fields) } equal(other) { @@ -102,7 +102,7 @@ class Rule { } getMap() { - return this.map + return this.umap } getOption(option) { @@ -136,7 +136,7 @@ class Rule { const defaultShapeProperties = DomUtil.add('div', '', container) defaultShapeProperties.appendChild(builder.build()) const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input) - const properties = this.map.allProperties() + const properties = this.umap.allProperties() autocomplete.suggestions = properties autocomplete.input.addEventListener('input', (event) => { const value = event.target.value @@ -144,12 +144,12 @@ class Rule { autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`] } else if (value.endsWith('=')) { const key = value.split('!')[0].split('=')[0] - autocomplete.suggestions = this.map + autocomplete.suggestions = this.umap .sortedValues(key) .map((str) => `${value}${str || ''}`) } }) - this.map.editPanel.open({ content: container }) + this.umap.editPanel.open({ content: container }) } renderToolbox(row) { @@ -176,7 +176,7 @@ class Rule { function () { if (!confirm(translate('Are you sure you want to delete this rule?'))) return this._delete() - this.map.editPanel.close() + this.umap.editPanel.close() }, this ) @@ -186,27 +186,27 @@ class Rule { DomEvent.on(toggle, 'click', () => { this.active = !this.active row.classList.toggle('off', !this.active) - this.map.render(['rules']) + this.umap.render(['rules']) }) } _delete() { - this.map.rules.rules = this.map.rules.rules.filter((rule) => rule !== this) + this.umap.rules.rules = this.umap.rules.rules.filter((rule) => rule !== this) } } export default class Rules { - constructor(map) { - this.map = map + constructor(umap) { + this.umap = umap this.rules = [] this.loadRules() } loadRules() { - if (!this.map.options.rules?.length) return - for (const { condition, options } of this.map.options.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.map, condition, options)) + this.rules.push(new Rule(this.umap, condition, options)) } } @@ -225,7 +225,7 @@ export default class Rules { else newIdx = referenceIdx + 1 this.rules.splice(newIdx, 0, moved) moved.isDirty = true - this.map.render(['rules']) + this.umap.render(['rules']) } edit(container) { @@ -243,14 +243,14 @@ export default class Rules { } addRule() { - const rule = new Rule(this.map) + const rule = new Rule(this.umap) rule.isDirty = true this.rules.push(rule) rule.edit(map) } commit() { - this.map.options.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 2b8b748a..98797633 100644 --- a/umap/static/umap/js/modules/saving.js +++ b/umap/static/umap/js/modules/saving.js @@ -25,7 +25,6 @@ export function has(obj) { } function _onUpdate() { - console.log(_queue) 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 144c252f..5e2e8215 100644 --- a/umap/static/umap/js/modules/share.js +++ b/umap/static/umap/js/modules/share.js @@ -4,8 +4,8 @@ import { translate } from './i18n.js' import * as Utils from './utils.js' export default class Share { - constructor(map) { - this.map = map + constructor(umap) { + this.umap = umap } build() { @@ -22,11 +22,11 @@ export default class Share { window.location.protocol + Utils.getBaseUrl() ) - if (this.map.options.shortUrl) { + if (this.umap.properties.shortUrl) { DomUtil.createCopiableInput( this.container, translate('Short link'), - this.map.options.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 = Utils.template(this.map.options.urls.map_download, { - map_id: this.map.options.umap_id, + const downloadUrl = this.umap.urls.get('map_download', { + map_id: this.umap.properties.umap_id, }) const link = Utils.loadTemplate(`