mirror of
https://github.com/umap-project/umap.git
synced 2025-05-04 21:51:50 +02:00
Compare commits
No commits in common. "51e41b7bcebe0b6022d4becdc972d2db2309b999" and "b0eb263d93216d3b4591a33dbb9cb11d639e18dc" have entirely different histories.
51e41b7bce
...
b0eb263d93
12 changed files with 317 additions and 61 deletions
|
@ -1,13 +1,37 @@
|
||||||
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
import {
|
||||||
import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
|
uMapAlert as Alert,
|
||||||
|
uMapAlertCreation as AlertCreation,
|
||||||
|
} from '../components/alerts/alert.js'
|
||||||
|
import {
|
||||||
|
AjaxAutocomplete,
|
||||||
|
AjaxAutocompleteMultiple,
|
||||||
|
AutocompleteDatalist,
|
||||||
|
} from './autocomplete.js'
|
||||||
|
import Browser from './browser.js'
|
||||||
|
import Caption from './caption.js'
|
||||||
|
import ContextMenu from './ui/contextmenu.js'
|
||||||
|
import Facets from './facets.js'
|
||||||
|
import { Formatter } from './formatter.js'
|
||||||
import Help from './help.js'
|
import Help from './help.js'
|
||||||
import { ServerRequest } from './request.js'
|
import Importer from './importer.js'
|
||||||
|
import Orderable from './orderable.js'
|
||||||
|
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
|
||||||
|
import Rules from './rules.js'
|
||||||
import { SCHEMA } from './schema.js'
|
import { SCHEMA } from './schema.js'
|
||||||
|
import Share from './share.js'
|
||||||
|
import Slideshow from './slideshow.js'
|
||||||
|
import { SyncEngine } from './sync/engine.js'
|
||||||
|
import Dialog from './ui/dialog.js'
|
||||||
|
import { EditPanel, FullPanel, Panel } from './ui/panel.js'
|
||||||
|
import Tooltip from './ui/tooltip.js'
|
||||||
|
import URLs from './urls.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import * as Icon from './rendering/icon.js'
|
import * as Icon from './rendering/icon.js'
|
||||||
import { LAYER_TYPES } from './data/layer.js'
|
import { DataLayer, LAYER_TYPES } from './data/layer.js'
|
||||||
|
import { DataLayerPermissions, MapPermissions } from './permissions.js'
|
||||||
import { Point, LineString, Polygon } from './data/features.js'
|
import { Point, LineString, Polygon } from './data/features.js'
|
||||||
import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.js'
|
import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.js'
|
||||||
|
import * as SAVEMANAGER from './saving.js'
|
||||||
|
|
||||||
// Import modules and export them to the global scope.
|
// Import modules and export them to the global scope.
|
||||||
// For the not yet module-compatible JS out there.
|
// For the not yet module-compatible JS out there.
|
||||||
|
@ -15,18 +39,45 @@ import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.j
|
||||||
// By alphabetic order
|
// By alphabetic order
|
||||||
window.U = {
|
window.U = {
|
||||||
Alert,
|
Alert,
|
||||||
|
AlertCreation,
|
||||||
AjaxAutocomplete,
|
AjaxAutocomplete,
|
||||||
AjaxAutocompleteMultiple,
|
AjaxAutocompleteMultiple,
|
||||||
|
AutocompleteDatalist,
|
||||||
|
Browser,
|
||||||
|
Caption,
|
||||||
|
ContextMenu,
|
||||||
|
DataLayer,
|
||||||
|
DataLayerPermissions,
|
||||||
|
Dialog,
|
||||||
|
EditPanel,
|
||||||
|
Facets,
|
||||||
|
Formatter,
|
||||||
|
FullPanel,
|
||||||
Help,
|
Help,
|
||||||
|
HTTPError,
|
||||||
Icon,
|
Icon,
|
||||||
|
Importer,
|
||||||
LAYER_TYPES,
|
LAYER_TYPES,
|
||||||
LeafletMarker,
|
LeafletMarker,
|
||||||
LeafletPolygon,
|
LeafletPolygon,
|
||||||
LeafletPolyline,
|
LeafletPolyline,
|
||||||
LineString,
|
LineString,
|
||||||
|
MapPermissions,
|
||||||
|
NOKError,
|
||||||
|
Orderable,
|
||||||
|
Panel,
|
||||||
Point,
|
Point,
|
||||||
Polygon,
|
Polygon,
|
||||||
|
Request,
|
||||||
|
RequestError,
|
||||||
|
Rules,
|
||||||
|
SAVEMANAGER,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
ServerRequest,
|
ServerRequest,
|
||||||
|
Share,
|
||||||
|
Slideshow,
|
||||||
|
SyncEngine,
|
||||||
|
Tooltip,
|
||||||
|
URLs,
|
||||||
Utils,
|
Utils,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import Dialog from './ui/dialog.js'
|
|
||||||
|
|
||||||
const SHORTCUTS = {
|
const SHORTCUTS = {
|
||||||
DRAW_MARKER: {
|
DRAW_MARKER: {
|
||||||
|
@ -167,7 +166,7 @@ const ENTRIES = {
|
||||||
export default class Help {
|
export default class Help {
|
||||||
constructor(umap) {
|
constructor(umap) {
|
||||||
this.umap = umap
|
this.umap = umap
|
||||||
this.dialog = new Dialog({ className: 'dark', accept: false, cancel: false })
|
this.dialog = new U.Dialog({ className: 'dark', accept: false, cancel: false })
|
||||||
this.isMacOS = /mac/i.test(
|
this.isMacOS = /mac/i.test(
|
||||||
// eslint-disable-next-line compat/compat -- Fallback available.
|
// eslint-disable-next-line compat/compat -- Fallback available.
|
||||||
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
|
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
|
||||||
|
|
|
@ -251,6 +251,7 @@ export default class Importer {
|
||||||
}
|
}
|
||||||
|
|
||||||
full() {
|
full() {
|
||||||
|
this.umap._leafletMap.once('postsync', this.umap._leafletMap._setDefaultCenter)
|
||||||
try {
|
try {
|
||||||
if (this.files.length) {
|
if (this.files.length) {
|
||||||
for (const file of this.files) {
|
for (const file of this.files) {
|
||||||
|
|
|
@ -193,6 +193,7 @@ export class MapPermissions extends ServerStored {
|
||||||
)
|
)
|
||||||
if (!error) {
|
if (!error) {
|
||||||
this.commit()
|
this.commit()
|
||||||
|
this.umap._leafletMap.fire('postsync')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { translate } from '../i18n.js'
|
||||||
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
||||||
import * as Utils from '../utils.js'
|
import * as Utils from '../utils.js'
|
||||||
import * as Icon from './icon.js'
|
import * as Icon from './icon.js'
|
||||||
import ContextMenu from '../ui/contextmenu.js'
|
|
||||||
|
|
||||||
// Those options are not saved on the server, so they can live here
|
// Those options are not saved on the server, so they can live here
|
||||||
// instead of in umap.properties
|
// instead of in umap.properties
|
||||||
|
@ -204,7 +203,7 @@ const ControlsMixin = {
|
||||||
</button>
|
</button>
|
||||||
`)
|
`)
|
||||||
rightContainer.appendChild(button)
|
rightContainer.appendChild(button)
|
||||||
const menu = new ContextMenu({ className: 'dark', fixed: true })
|
const menu = new U.ContextMenu({ className: 'dark', fixed: true })
|
||||||
const actions = [
|
const actions = [
|
||||||
{
|
{
|
||||||
label: translate('New map'),
|
label: translate('New map'),
|
||||||
|
@ -253,7 +252,7 @@ const ControlsMixin = {
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateConnectedPeersCount = () => {
|
const updateConnectedPeersCount = () => {
|
||||||
connectedPeersCount.innerHTML = this.umap.sync.getNumberOfConnectedPeers()
|
connectedPeersCount.innerHTML = this.sync.getNumberOfConnectedPeers()
|
||||||
}
|
}
|
||||||
updateConnectedPeersCount()
|
updateConnectedPeersCount()
|
||||||
}
|
}
|
||||||
|
@ -307,12 +306,8 @@ const ControlsMixin = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
renderCaptionBar: function () {
|
initCaptionBar: function () {
|
||||||
if (this.options.noControl) return
|
const container = DomUtil.create('div', 'umap-caption-bar', this._controlContainer)
|
||||||
const container =
|
|
||||||
this._controlContainer.querySelector('.umap-caption-bar') ||
|
|
||||||
DomUtil.create('div', 'umap-caption-bar', this._controlContainer)
|
|
||||||
container.innerHTML = ''
|
|
||||||
const name = DomUtil.create('h3', 'map-name', container)
|
const name = DomUtil.create('h3', 'map-name', container)
|
||||||
DomEvent.disableClickPropagation(container)
|
DomEvent.disableClickPropagation(container)
|
||||||
this.umap.addAuthorLink(container)
|
this.umap.addAuthorLink(container)
|
||||||
|
@ -321,7 +316,8 @@ const ControlsMixin = {
|
||||||
'umap-about-link flat',
|
'umap-about-link flat',
|
||||||
container,
|
container,
|
||||||
translate('Open caption'),
|
translate('Open caption'),
|
||||||
() => this.umap.openCaption()
|
this.umap.openCaption,
|
||||||
|
this
|
||||||
)
|
)
|
||||||
DomUtil.createButton(
|
DomUtil.createButton(
|
||||||
'umap-open-browser-link flat',
|
'umap-open-browser-link flat',
|
||||||
|
@ -338,8 +334,8 @@ const ControlsMixin = {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.umap.onceDatalayersLoaded(() => {
|
this.umap.onceDatalayersLoaded(function () {
|
||||||
this.umap.slideshow.renderToolbox(container)
|
this.slideshow.renderToolbox(container)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -485,18 +481,11 @@ export const LeafletMap = BaseMap.extend({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: function () {
|
attachToDom: function () {
|
||||||
this.initControls()
|
this.initControls()
|
||||||
// Needs locate control and hash to exist
|
// Needs locate control and hash to exist
|
||||||
this.initCenter()
|
this.initCenter()
|
||||||
this.update()
|
|
||||||
},
|
|
||||||
|
|
||||||
update: function () {
|
|
||||||
this.setOptions(this.umap.properties)
|
|
||||||
this.initTileLayers()
|
this.initTileLayers()
|
||||||
this.renderCaptionBar()
|
|
||||||
this.renderEditToolbar()
|
|
||||||
// Needs tilelayer to exist for minimap
|
// Needs tilelayer to exist for minimap
|
||||||
this.renderControls()
|
this.renderControls()
|
||||||
this.handleLimitBounds()
|
this.handleLimitBounds()
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import { AutocompleteDatalist } from './autocomplete.js'
|
import { AutocompleteDatalist } from './autocomplete.js'
|
||||||
import Orderable from './orderable.js'
|
|
||||||
|
|
||||||
const EMPTY_VALUES = ['', undefined, null]
|
const EMPTY_VALUES = ['', undefined, null]
|
||||||
|
|
||||||
|
@ -237,7 +236,7 @@ export default class Rules {
|
||||||
rule.renderToolbox(DomUtil.create('li', 'orderable', ul))
|
rule.renderToolbox(DomUtil.create('li', 'orderable', ul))
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderable = new Orderable(ul, this.onReorder.bind(this))
|
const orderable = new U.Orderable(ul, this.onReorder.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
DomUtil.createButton('umap-add', body, translate('Add rule'), this.addRule, this)
|
DomUtil.createButton('umap-add', body, translate('Add rule'), this.addRule, this)
|
||||||
|
|
|
@ -83,7 +83,7 @@ export default class Slideshow extends WithTemplate {
|
||||||
|
|
||||||
play() {
|
play() {
|
||||||
if (this._id) return
|
if (this._id) return
|
||||||
if (this.umap.editEnabled || !this.umap.properties.slideshow.active) return
|
if (this.umap.editEnabled || !this.umap.options.slideshow.active) return
|
||||||
L.DomUtil.addClass(document.body, this.CLASSNAME)
|
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.options.delay)
|
||||||
this.startSpinner()
|
this.startSpinner()
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { fieldInSchema } from '../utils.js'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BaseUpdater {
|
class BaseUpdater {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this.umap = umap
|
this.map = map
|
||||||
}
|
}
|
||||||
|
|
||||||
updateObjectValue(obj, key, value) {
|
updateObjectValue(obj, key, value) {
|
||||||
|
@ -32,8 +32,8 @@ class BaseUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDataLayerFromID(layerId) {
|
getDataLayerFromID(layerId) {
|
||||||
if (layerId) return this.umap.getDataLayerByUmapId(layerId)
|
if (layerId) return this.map.getDataLayerByUmapId(layerId)
|
||||||
return this.umap.defaultEditDataLayer()
|
return this.map.defaultEditDataLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
applyMessage(payload) {
|
applyMessage(payload) {
|
||||||
|
@ -45,18 +45,18 @@ class BaseUpdater {
|
||||||
export class MapUpdater extends BaseUpdater {
|
export class MapUpdater extends BaseUpdater {
|
||||||
update({ key, value }) {
|
update({ key, value }) {
|
||||||
if (fieldInSchema(key)) {
|
if (fieldInSchema(key)) {
|
||||||
this.updateObjectValue(this.umap, key, value)
|
this.updateObjectValue(this.map, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.umap.render([key])
|
this.map.render([key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataLayerUpdater extends BaseUpdater {
|
export class DataLayerUpdater extends BaseUpdater {
|
||||||
upsert({ value }) {
|
upsert({ value }) {
|
||||||
// Inserts does not happen (we use multiple updates instead).
|
// Inserts does not happen (we use multiple updates instead).
|
||||||
this.umap.createDataLayer(value, false)
|
this.map.createDataLayer(value, false)
|
||||||
this.umap.render([])
|
this.map.render([])
|
||||||
}
|
}
|
||||||
|
|
||||||
update({ key, metadata, value }) {
|
update({ key, metadata, value }) {
|
||||||
|
|
|
@ -32,7 +32,6 @@ import {
|
||||||
uMapAlertCreation as AlertCreation,
|
uMapAlertCreation as AlertCreation,
|
||||||
uMapAlert as Alert,
|
uMapAlert as Alert,
|
||||||
} from '../components/alerts/alert.js'
|
} from '../components/alerts/alert.js'
|
||||||
import Orderable from './orderable.js'
|
|
||||||
|
|
||||||
export default class Umap extends ServerStored {
|
export default class Umap extends ServerStored {
|
||||||
constructor(element, geojson) {
|
constructor(element, geojson) {
|
||||||
|
@ -86,16 +85,11 @@ export default class Umap extends ServerStored {
|
||||||
if (center) {
|
if (center) {
|
||||||
this._leafletMap.options.center = this._leafletMap.latLng(center)
|
this._leafletMap.options.center = this._leafletMap.latLng(center)
|
||||||
}
|
}
|
||||||
|
this._leafletMap.attachToDom()
|
||||||
// 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._leafletMap.setup()
|
|
||||||
|
|
||||||
if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema)
|
if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema)
|
||||||
|
|
||||||
|
this.urls = new URLs(this.properties.urls)
|
||||||
|
|
||||||
this.panel = new Panel(this)
|
this.panel = new Panel(this)
|
||||||
this.dialog = new Dialog({ className: 'dark' })
|
this.dialog = new Dialog({ className: 'dark' })
|
||||||
|
@ -177,12 +171,15 @@ export default class Umap extends ServerStored {
|
||||||
await this.loadDataFromQueryString()
|
await this.loadDataFromQueryString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.slideshow = new Slideshow(this, this.properties.slideshow)
|
||||||
|
this.permissions = new MapPermissions(this)
|
||||||
if (this.hasEditMode()) {
|
if (this.hasEditMode()) {
|
||||||
this._leafletMap.initEditTools()
|
this._leafletMap.initEditTools()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.properties.noControl) {
|
if (!this.properties.noControl) {
|
||||||
this.initShortcuts()
|
this.initShortcuts()
|
||||||
|
this._leafletMap.initCaptionBar()
|
||||||
this._leafletMap.on('contextmenu', this.onContextMenu)
|
this._leafletMap.on('contextmenu', this.onContextMenu)
|
||||||
this.onceDataLoaded(this.setViewFromQueryString)
|
this.onceDataLoaded(this.setViewFromQueryString)
|
||||||
this.propagate()
|
this.propagate()
|
||||||
|
@ -1248,7 +1245,7 @@ export default class Umap extends ServerStored {
|
||||||
|
|
||||||
render(fields) {
|
render(fields) {
|
||||||
if (fields.includes('numberOfConnectedPeers')) {
|
if (fields.includes('numberOfConnectedPeers')) {
|
||||||
this._leafletMap.renderEditToolbar()
|
this.renderEditToolbar()
|
||||||
this.propagate()
|
this.propagate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,7 +1253,10 @@ export default class Umap extends ServerStored {
|
||||||
for (const impact of impacts) {
|
for (const impact of impacts) {
|
||||||
switch (impact) {
|
switch (impact) {
|
||||||
case 'ui':
|
case 'ui':
|
||||||
this._leafletMap.update()
|
this._leafletMap.setOptions(this.properties)
|
||||||
|
this._leafletMap.initCaptionBar()
|
||||||
|
this._leafletMap.renderEditToolbar()
|
||||||
|
this._leafletMap.renderControls()
|
||||||
this.browser.redraw()
|
this.browser.redraw()
|
||||||
this.propagate()
|
this.propagate()
|
||||||
break
|
break
|
||||||
|
@ -1383,7 +1383,7 @@ export default class Umap extends ServerStored {
|
||||||
})
|
})
|
||||||
this.indexDatalayers()
|
this.indexDatalayers()
|
||||||
}
|
}
|
||||||
const orderable = new Orderable(ul, onReorder)
|
const orderable = new U.Orderable(ul, onReorder)
|
||||||
|
|
||||||
const bar = DomUtil.create('div', 'button-bar', container)
|
const bar = DomUtil.create('div', 'button-bar', container)
|
||||||
DomUtil.createButton(
|
DomUtil.createButton(
|
||||||
|
@ -1499,13 +1499,17 @@ export default class Umap extends ServerStored {
|
||||||
dataLayer.fromUmapGeoJSON(geojson)
|
dataLayer.fromUmapGeoJSON(geojson)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._leafletMap.update()
|
// TODO: refactor with leafletMap init / render
|
||||||
|
this._leafletMap.setOptions(this.properties)
|
||||||
|
this._leafletMap.initTileLayers()
|
||||||
|
this._leafletMap.renderControls()
|
||||||
|
this._leafletMap.handleLimitBounds()
|
||||||
this.eachDataLayer((datalayer) => {
|
this.eachDataLayer((datalayer) => {
|
||||||
if (mustReindex) datalayer.reindex()
|
if (mustReindex) datalayer.reindex()
|
||||||
datalayer.redraw()
|
datalayer.redraw()
|
||||||
})
|
})
|
||||||
this.propagate()
|
this.propagate()
|
||||||
this._leafletMap._setDefaultCenter()
|
this._leafletMap.fire('postsync')
|
||||||
this.isDirty = true
|
this.isDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,10 +66,7 @@ export function getImpactsFromSchema(fields, schema) {
|
||||||
export function fieldInSchema(field, schema) {
|
export function fieldInSchema(field, schema) {
|
||||||
const current_schema = schema || U.SCHEMA
|
const current_schema = schema || U.SCHEMA
|
||||||
if (typeof field !== 'string') return false
|
if (typeof field !== 'string') return false
|
||||||
const field_name = field
|
const field_name = field.replace('options.', '').split('.')[0]
|
||||||
.replace('options.', '')
|
|
||||||
.replace('properties.', '')
|
|
||||||
.split('.')[0]
|
|
||||||
return current_schema[field_name] !== undefined
|
return current_schema[field_name] !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
215
umap/static/umap/js/umap.js
Normal file
215
umap/static/umap/js/umap.js
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
L.Map.mergeOptions({
|
||||||
|
overlay: {},
|
||||||
|
datalayers: [],
|
||||||
|
maxZoomLimit: 24,
|
||||||
|
attributionControl: false,
|
||||||
|
editMode: 'advanced',
|
||||||
|
noControl: false, // Do not render any control.
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
// When a TileLayer is in TMS mode, it needs -y instead of y.
|
||||||
|
// This is usually handled by the TileLayer instance itself, but
|
||||||
|
// we cannot rely on this because of the y is overriden by Leaflet
|
||||||
|
// See https://github.com/Leaflet/Leaflet/pull/9201
|
||||||
|
// And let's remove this -y when this PR is merged and released.
|
||||||
|
demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, '-y': 181, r: '' },
|
||||||
|
licences: [],
|
||||||
|
licence: '',
|
||||||
|
slideshow: {},
|
||||||
|
clickable: true,
|
||||||
|
permissions: {},
|
||||||
|
featuresHaveOwner: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
U.Map = L.Map.extend({
|
||||||
|
includes: [ControlsMixin],
|
||||||
|
|
||||||
|
initialize: async function (el, geojson) {
|
||||||
|
this.sync_engine = new U.SyncEngine(this)
|
||||||
|
this.sync = this.sync_engine.proxy(this)
|
||||||
|
// Locale name (pt_PT, en_US…)
|
||||||
|
// To be used for Django localization
|
||||||
|
if (geojson.properties.locale) L.setLocale(geojson.properties.locale)
|
||||||
|
|
||||||
|
// Language code (pt-pt, en-us…)
|
||||||
|
// To be used in javascript APIs
|
||||||
|
if (geojson.properties.lang) L.lang = geojson.properties.lang
|
||||||
|
|
||||||
|
this.setOptionsFromQueryString(geojson.properties)
|
||||||
|
// Prevent default creation of controls
|
||||||
|
const zoomControl = geojson.properties.zoomControl
|
||||||
|
const fullscreenControl = geojson.properties.fullscreenControl
|
||||||
|
geojson.properties.zoomControl = false
|
||||||
|
geojson.properties.fullscreenControl = false
|
||||||
|
|
||||||
|
L.Map.prototype.initialize.call(this, el, geojson.properties)
|
||||||
|
|
||||||
|
if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema)
|
||||||
|
|
||||||
|
// After calling parent initialize, as we are doing initCenter our-selves
|
||||||
|
if (geojson.geometry) this.options.center = this.latLng(geojson.geometry)
|
||||||
|
this.urls = new U.URLs(this.options.urls)
|
||||||
|
|
||||||
|
this.panel = new U.Panel(this)
|
||||||
|
this.dialog = new U.Dialog({ className: 'dark' })
|
||||||
|
this.tooltip = new U.Tooltip(this._controlContainer)
|
||||||
|
this.contextmenu = new U.ContextMenu()
|
||||||
|
if (this.hasEditMode()) {
|
||||||
|
this.editPanel = new U.EditPanel(this)
|
||||||
|
this.fullPanel = new U.FullPanel(this)
|
||||||
|
}
|
||||||
|
if (!this.options.noControl) {
|
||||||
|
L.DomEvent.on(document.body, 'dataloading', (e) => this.fire('dataloading', e))
|
||||||
|
L.DomEvent.on(document.body, 'dataload', (e) => this.fire('dataload', e))
|
||||||
|
}
|
||||||
|
this.server = new U.ServerRequest()
|
||||||
|
this.request = new U.Request()
|
||||||
|
|
||||||
|
this.initLoader()
|
||||||
|
this.name = this.options.name
|
||||||
|
this.description = this.options.description
|
||||||
|
this.demoTileInfos = this.options.demoTileInfos
|
||||||
|
this.options.zoomControl = zoomControl !== undefined ? zoomControl : true
|
||||||
|
this.options.fullscreenControl =
|
||||||
|
fullscreenControl !== undefined ? fullscreenControl : true
|
||||||
|
|
||||||
|
this.datalayersFromQueryString = L.Util.queryString('datalayers')
|
||||||
|
if (this.datalayersFromQueryString) {
|
||||||
|
this.datalayersFromQueryString = this.datalayersFromQueryString
|
||||||
|
.toString()
|
||||||
|
.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
let editedFeature = null
|
||||||
|
try {
|
||||||
|
Object.defineProperty(this, 'editedFeature', {
|
||||||
|
get: () => editedFeature,
|
||||||
|
set: (feature) => {
|
||||||
|
if (editedFeature && editedFeature !== feature) {
|
||||||
|
editedFeature.endEdit()
|
||||||
|
}
|
||||||
|
editedFeature = feature
|
||||||
|
this.fire('seteditedfeature')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
// Certainly IE8, which has a limited version of defineProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrocompat
|
||||||
|
if (this.options.slideshow?.delay && this.options.slideshow.active === undefined) {
|
||||||
|
this.options.slideshow.active = true
|
||||||
|
}
|
||||||
|
if (this.options.advancedFilterKey) {
|
||||||
|
this.options.facetKey = this.options.advancedFilterKey
|
||||||
|
delete this.options.advancedFilterKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global storage for retrieving datalayers and features.
|
||||||
|
this.datalayers = {} // All datalayers, including deleted.
|
||||||
|
this.datalayers_index = [] // Datalayers actually on the map and ordered.
|
||||||
|
this.features_index = {}
|
||||||
|
|
||||||
|
// Needed for actions labels
|
||||||
|
this.help = new U.Help(this)
|
||||||
|
|
||||||
|
this.formatter = new U.Formatter(this)
|
||||||
|
|
||||||
|
this.initControls()
|
||||||
|
// Needs locate control and hash to exist
|
||||||
|
this.initCenter()
|
||||||
|
this.initTileLayers()
|
||||||
|
// Needs tilelayer to exist for minimap
|
||||||
|
this.renderControls()
|
||||||
|
this.handleLimitBounds()
|
||||||
|
this.initDataLayers()
|
||||||
|
|
||||||
|
if (this.options.displayCaptionOnLoad) {
|
||||||
|
// Retrocompat
|
||||||
|
if (!this.options.onLoadPanel) {
|
||||||
|
this.options.onLoadPanel = 'caption'
|
||||||
|
}
|
||||||
|
delete this.options.displayCaptionOnLoad
|
||||||
|
}
|
||||||
|
if (this.options.displayDataBrowserOnLoad) {
|
||||||
|
// Retrocompat
|
||||||
|
if (!this.options.onLoadPanel) {
|
||||||
|
this.options.onLoadPanel = 'databrowser'
|
||||||
|
}
|
||||||
|
delete this.options.displayDataBrowserOnLoad
|
||||||
|
}
|
||||||
|
if (this.options.datalayersControl === 'expanded') {
|
||||||
|
if (!this.options.onLoadPanel) {
|
||||||
|
this.options.onLoadPanel = 'datalayers'
|
||||||
|
}
|
||||||
|
delete this.options.datalayersControl
|
||||||
|
}
|
||||||
|
if (this.options.onLoadPanel === 'facet') {
|
||||||
|
this.options.onLoadPanel = 'datafilters'
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove me when moved to modules
|
||||||
|
// and inheriting from ServerStored
|
||||||
|
try {
|
||||||
|
Object.defineProperty(this, 'isDirty', {
|
||||||
|
get: () => U.SAVEMANAGER.has(this),
|
||||||
|
set: (status) => {
|
||||||
|
if (status) {
|
||||||
|
U.SAVEMANAGER.add(this)
|
||||||
|
} else {
|
||||||
|
U.SAVEMANAGER.remove(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
// Certainly IE8, which has a limited version of defineProperty
|
||||||
|
}
|
||||||
|
this.on(
|
||||||
|
'baselayerchange',
|
||||||
|
function (e) {
|
||||||
|
if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
|
// Creation mode
|
||||||
|
if (!this.options.umap_id) {
|
||||||
|
if (!this.options.preview) {
|
||||||
|
this.isDirty = true
|
||||||
|
this.enableEdit()
|
||||||
|
}
|
||||||
|
this._default_extent = true
|
||||||
|
this.options.name = L._('Untitled map')
|
||||||
|
await this.loadDataFromQueryString()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slideshow = new U.Slideshow(this, this.options.slideshow)
|
||||||
|
this.permissions = new U.MapPermissions(this)
|
||||||
|
if (this.hasEditMode()) {
|
||||||
|
this.editTools = new U.Editable(this)
|
||||||
|
this.renderEditToolbar()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.noControl) {
|
||||||
|
this.initShortcuts()
|
||||||
|
this.initCaptionBar()
|
||||||
|
this.on('contextmenu', this.onContextMenu)
|
||||||
|
this.onceDataLoaded(this.setViewFromQueryString)
|
||||||
|
this.on('click', this.closeInplaceToolbar)
|
||||||
|
this.propagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onbeforeunload = () => (this.editEnabled && U.SAVEMANAGER.isDirty) || null
|
||||||
|
this.backup()
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getFeatureById: function (id) {
|
||||||
|
let feature
|
||||||
|
for (const datalayer of this.datalayers_index) {
|
||||||
|
feature = datalayer.getFeatureById(id)
|
||||||
|
if (feature) return feature
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
|
@ -161,7 +161,7 @@ def test_websocket_connection_can_sync_polygons(
|
||||||
|
|
||||||
@pytest.mark.xdist_group(name="websockets")
|
@pytest.mark.xdist_group(name="websockets")
|
||||||
def test_websocket_connection_can_sync_map_properties(
|
def test_websocket_connection_can_sync_map_properties(
|
||||||
new_page, live_server, websocket_server, tilelayer
|
context, live_server, websocket_server, tilelayer
|
||||||
):
|
):
|
||||||
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
||||||
map.settings["properties"]["syncEnabled"] = True
|
map.settings["properties"]["syncEnabled"] = True
|
||||||
|
@ -169,9 +169,9 @@ def test_websocket_connection_can_sync_map_properties(
|
||||||
DataLayerFactory(map=map, data={})
|
DataLayerFactory(map=map, data={})
|
||||||
|
|
||||||
# Create two tabs
|
# Create two tabs
|
||||||
peerA = new_page()
|
peerA = context.new_page()
|
||||||
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
peerB = new_page()
|
peerB = context.new_page()
|
||||||
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
|
|
||||||
# Name change is synced
|
# Name change is synced
|
||||||
|
@ -193,7 +193,7 @@ def test_websocket_connection_can_sync_map_properties(
|
||||||
|
|
||||||
@pytest.mark.xdist_group(name="websockets")
|
@pytest.mark.xdist_group(name="websockets")
|
||||||
def test_websocket_connection_can_sync_datalayer_properties(
|
def test_websocket_connection_can_sync_datalayer_properties(
|
||||||
new_page, live_server, websocket_server, tilelayer
|
context, live_server, websocket_server, tilelayer
|
||||||
):
|
):
|
||||||
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
||||||
map.settings["properties"]["syncEnabled"] = True
|
map.settings["properties"]["syncEnabled"] = True
|
||||||
|
@ -201,9 +201,9 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
||||||
DataLayerFactory(map=map, data={})
|
DataLayerFactory(map=map, data={})
|
||||||
|
|
||||||
# Create two tabs
|
# Create two tabs
|
||||||
peerA = new_page()
|
peerA = context.new_page()
|
||||||
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
peerB = new_page()
|
peerB = context.new_page()
|
||||||
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
|
|
||||||
# Layer addition, name and type are synced
|
# Layer addition, name and type are synced
|
||||||
|
@ -215,7 +215,7 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
||||||
peerA.locator("body").press("Escape")
|
peerA.locator("body").press("Escape")
|
||||||
|
|
||||||
peerB.get_by_role("link", name="Manage layers").click()
|
peerB.get_by_role("link", name="Manage layers").click()
|
||||||
peerB.locator(".panel.right").get_by_role("button", name="Edit").first.click()
|
peerB.get_by_role("button", name="Edit").first.click()
|
||||||
expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
|
expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
|
||||||
expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")
|
expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue