mirror of
https://github.com/umap-project/umap.git
synced 2025-05-07 06:51:49 +02:00
Compare commits
31 commits
4a1d34540d
...
3b6ff0c57c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b6ff0c57c | ||
![]() |
2a460f03b2 | ||
![]() |
bbde111fdf | ||
![]() |
9c4287ac1e | ||
![]() |
e9f2ff9a6c | ||
![]() |
12e456d24e | ||
![]() |
e004cd461d | ||
![]() |
6bea9339b6 | ||
![]() |
90ea3737f2 | ||
![]() |
a7b750740c | ||
![]() |
101b036a66 | ||
![]() |
983f7f8cb1 | ||
![]() |
9718f11faf | ||
![]() |
88382ab00b | ||
![]() |
0b84084c6b | ||
![]() |
8b2454936b | ||
![]() |
98f2f8df65 | ||
757cb375d1 | |||
![]() |
4ef1411102 | ||
![]() |
01b2053030 | ||
![]() |
64c7fe1ec9 | ||
![]() |
be83eddbd0 | ||
![]() |
4df201107e | ||
![]() |
60f16cbc76 | ||
![]() |
2fa88c36f8 | ||
![]() |
47c5c0a2f0 | ||
![]() |
e548ec60f1 | ||
![]() |
190acbfaf0 | ||
![]() |
1370b1a0e8 | ||
![]() |
aa75b323c8 | ||
![]() |
1c00545095 |
33 changed files with 194 additions and 2052 deletions
|
@ -1,5 +1,5 @@
|
||||||
# Force rtfd to use a recent version of mkdocs
|
# Force rtfd to use a recent version of mkdocs
|
||||||
mkdocs==1.6.1
|
mkdocs==1.6.1
|
||||||
pymdown-extensions==10.14.3
|
pymdown-extensions==10.14.3
|
||||||
mkdocs-material==9.6.7
|
mkdocs-material==9.6.9
|
||||||
mkdocs-static-i18n==1.3.0
|
mkdocs-static-i18n==1.3.0
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Force rtfd to use a recent version of mkdocs
|
# Force rtfd to use a recent version of mkdocs
|
||||||
mkdocs==1.6.1
|
mkdocs==1.6.1
|
||||||
pymdown-extensions==10.14.3
|
pymdown-extensions==10.14.3
|
||||||
mkdocs-material==9.6.7
|
mkdocs-material==9.6.9
|
||||||
mkdocs-static-i18n==1.3.0
|
mkdocs-static-i18n==1.3.0
|
||||||
|
|
|
@ -33,7 +33,7 @@ dependencies = [
|
||||||
"django-environ==0.12.0",
|
"django-environ==0.12.0",
|
||||||
"django-probes==1.7.0",
|
"django-probes==1.7.0",
|
||||||
"Pillow==11.1.0",
|
"Pillow==11.1.0",
|
||||||
"psycopg==3.2.5",
|
"psycopg==3.2.6",
|
||||||
"requests==2.32.3",
|
"requests==2.32.3",
|
||||||
"rcssmin==1.2.1",
|
"rcssmin==1.2.1",
|
||||||
"rjsmin==1.2.4",
|
"rjsmin==1.2.4",
|
||||||
|
@ -44,10 +44,10 @@ dependencies = [
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"hatch==1.14.0",
|
"hatch==1.14.0",
|
||||||
"ruff==0.9.10",
|
"ruff==0.11.2",
|
||||||
"djlint==1.36.4",
|
"djlint==1.36.4",
|
||||||
"mkdocs==1.6.1",
|
"mkdocs==1.6.1",
|
||||||
"mkdocs-material==9.6.7",
|
"mkdocs-material==9.6.9",
|
||||||
"mkdocs-static-i18n==1.3.0",
|
"mkdocs-static-i18n==1.3.0",
|
||||||
"vermin==1.6.0",
|
"vermin==1.6.0",
|
||||||
"pymdown-extensions==10.14.3",
|
"pymdown-extensions==10.14.3",
|
||||||
|
|
|
@ -18,7 +18,6 @@ import { translate } from '../i18n.js'
|
||||||
import { DataLayerPermissions } from '../permissions.js'
|
import { DataLayerPermissions } from '../permissions.js'
|
||||||
import { Point, LineString, Polygon } from './features.js'
|
import { Point, LineString, Polygon } from './features.js'
|
||||||
import TableEditor from '../tableeditor.js'
|
import TableEditor from '../tableeditor.js'
|
||||||
import { ServerStored } from '../saving.js'
|
|
||||||
import * as Schema from '../schema.js'
|
import * as Schema from '../schema.js'
|
||||||
import { MutatingForm } from '../form/builder.js'
|
import { MutatingForm } from '../form/builder.js'
|
||||||
|
|
||||||
|
@ -36,9 +35,8 @@ const LAYER_MAP = LAYER_TYPES.reduce((acc, klass) => {
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
export class DataLayer extends ServerStored {
|
export class DataLayer {
|
||||||
constructor(umap, leafletMap, data = {}) {
|
constructor(umap, leafletMap, data = {}) {
|
||||||
super()
|
|
||||||
this._umap = umap
|
this._umap = umap
|
||||||
this.sync = umap.syncEngine.proxy(this)
|
this.sync = umap.syncEngine.proxy(this)
|
||||||
this._index = Array()
|
this._index = Array()
|
||||||
|
@ -114,7 +112,6 @@ export class DataLayer extends ServerStored {
|
||||||
|
|
||||||
set isDeleted(status) {
|
set isDeleted(status) {
|
||||||
this._isDeleted = status
|
this._isDeleted = status
|
||||||
if (status) this.isDirty = status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDeleted() {
|
get isDeleted() {
|
||||||
|
@ -530,10 +527,6 @@ export class DataLayer extends ServerStored {
|
||||||
return this._umap.formatter
|
return this._umap.formatter
|
||||||
.parse(raw, format)
|
.parse(raw, format)
|
||||||
.then((geojson) => this.addData(geojson))
|
.then((geojson) => this.addData(geojson))
|
||||||
.then((data) => {
|
|
||||||
if (data?.length) this.isDirty = true
|
|
||||||
return data
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.debug(error)
|
console.debug(error)
|
||||||
Alert.error(translate('Import failed: invalid data'))
|
Alert.error(translate('Import failed: invalid data'))
|
||||||
|
@ -610,7 +603,6 @@ export class DataLayer extends ServerStored {
|
||||||
empty() {
|
empty() {
|
||||||
if (this.isRemoteLayer()) return
|
if (this.isRemoteLayer()) return
|
||||||
this.clear()
|
this.clear()
|
||||||
this.isDirty = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
|
@ -634,25 +626,6 @@ export class DataLayer extends ServerStored {
|
||||||
this.clear()
|
this.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
|
||||||
if (!this.createdOnServer) {
|
|
||||||
this.erase()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resetOptions()
|
|
||||||
this.parentPane.appendChild(this.pane)
|
|
||||||
if (this._leaflet_events_bk && !this._leaflet_events) {
|
|
||||||
this._leaflet_events = this._leaflet_events_bk
|
|
||||||
}
|
|
||||||
this.clear()
|
|
||||||
this.hide()
|
|
||||||
if (this.isRemoteLayer()) this.fetchRemoteData()
|
|
||||||
else if (this._geojson_bk) this.fromGeoJSON(this._geojson_bk)
|
|
||||||
this.show()
|
|
||||||
this.isDirty = false
|
|
||||||
}
|
|
||||||
|
|
||||||
redraw() {
|
redraw() {
|
||||||
if (!this.isVisible()) return
|
if (!this.isVisible()) return
|
||||||
this.eachFeature((feature) => feature.redraw())
|
this.eachFeature((feature) => feature.redraw())
|
||||||
|
@ -835,8 +808,9 @@ export class DataLayer extends ServerStored {
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
|
||||||
if (this._umap.properties.urls.datalayer_versions)
|
if (this._umap.properties.urls.datalayer_versions) {
|
||||||
this.buildVersionsFieldset(container)
|
this.buildVersionsFieldset(container)
|
||||||
|
}
|
||||||
|
|
||||||
const advancedActions = DomUtil.createFieldset(
|
const advancedActions = DomUtil.createFieldset(
|
||||||
container,
|
container,
|
||||||
|
@ -911,10 +885,15 @@ export class DataLayer extends ServerStored {
|
||||||
const appendVersion = (data) => {
|
const appendVersion = (data) => {
|
||||||
const date = new Date(Number.parseInt(data.at, 10))
|
const date = new Date(Number.parseInt(data.at, 10))
|
||||||
const content = `${date.toLocaleString(U.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 [el, { button }] = Utils.loadTemplateWithRefs(
|
||||||
const button = DomUtil.createButton('', el, '', () => this.restore(data.ref))
|
`<div class="umap-datalayer-version">
|
||||||
button.title = translate('Restore this version')
|
<button type="button" title="${translate('Restore this version')}" data-ref=button>
|
||||||
DomUtil.add('span', '', el, content)
|
<i class="icon icon-16 icon-restore"></i> ${content}
|
||||||
|
</button>
|
||||||
|
</div>`
|
||||||
|
)
|
||||||
|
versionsContainer.appendChild(el)
|
||||||
|
button.addEventListener('click', () => this.restore(data.ref))
|
||||||
}
|
}
|
||||||
|
|
||||||
const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), {
|
const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), {
|
||||||
|
@ -938,11 +917,14 @@ export class DataLayer extends ServerStored {
|
||||||
)
|
)
|
||||||
if (!error) {
|
if (!error) {
|
||||||
if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat.
|
if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat.
|
||||||
if (geojson._umap_options) this.setOptions(geojson._umap_options)
|
if (geojson._umap_options) {
|
||||||
|
const oldOptions = Utils.CopyJSON(this.options)
|
||||||
|
this.setOptions(geojson._umap_options)
|
||||||
|
this.sync.update('options', this.options, oldOptions)
|
||||||
|
}
|
||||||
this.empty()
|
this.empty()
|
||||||
if (this.isRemoteLayer()) this.fetchRemoteData()
|
if (this.isRemoteLayer()) this.fetchRemoteData()
|
||||||
else this.addData(geojson)
|
else this.addData(geojson)
|
||||||
this.isDirty = true
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1123,6 +1105,10 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _trySave(url, headers, formData) {
|
async _trySave(url, headers, formData) {
|
||||||
|
if (this._forceSave) {
|
||||||
|
headers = {}
|
||||||
|
this._forceSave = false
|
||||||
|
}
|
||||||
const [data, response, error] = await this._umap.server.post(url, headers, formData)
|
const [data, response, error] = await this._umap.server.post(url, headers, formData)
|
||||||
if (error) {
|
if (error) {
|
||||||
if (response && response.status === 412) {
|
if (response && response.status === 412) {
|
||||||
|
@ -1132,15 +1118,8 @@ export class DataLayer extends ServerStored {
|
||||||
'This situation is tricky, you have to choose carefully which version is pertinent.'
|
'This situation is tricky, you have to choose carefully which version is pertinent.'
|
||||||
),
|
),
|
||||||
async () => {
|
async () => {
|
||||||
// Save again this layer
|
this._forceSave = true
|
||||||
const status = await this._trySave(url, {}, formData)
|
await this._umap.saveAll()
|
||||||
if (status) {
|
|
||||||
this.isDirty = false
|
|
||||||
|
|
||||||
// Call the main save, in case something else needs to be saved
|
|
||||||
// as the conflict stopped the saving flow
|
|
||||||
await this._umap.saveAll()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,13 @@ export default class Facets {
|
||||||
for (const [property, { label, type }] of parsed) {
|
for (const [property, { label, type }] of parsed) {
|
||||||
dumped.push([property, label, type].filter(Boolean).join('|'))
|
dumped.push([property, label, type].filter(Boolean).join('|'))
|
||||||
}
|
}
|
||||||
return dumped.join(',')
|
const oldValue = this._umap.properties.facetKey
|
||||||
|
this._umap.properties.facetKey = dumped.join(',')
|
||||||
|
this._umap.sync.update(
|
||||||
|
'properties.facetKey',
|
||||||
|
this._umap.properties.facetKey,
|
||||||
|
oldValue
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
has(property) {
|
has(property) {
|
||||||
|
@ -146,15 +152,13 @@ export default class Facets {
|
||||||
const defined = this.getDefined()
|
const defined = this.getDefined()
|
||||||
if (!defined.has(property)) {
|
if (!defined.has(property)) {
|
||||||
defined.set(property, { label, type })
|
defined.set(property, { label, type })
|
||||||
this._umap.properties.facetKey = this.dumps(defined)
|
this.dumps(defined)
|
||||||
this._umap.isDirty = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(property) {
|
remove(property) {
|
||||||
const defined = this.getDefined()
|
const defined = this.getDefined()
|
||||||
defined.delete(property)
|
defined.delete(property)
|
||||||
this._umap.properties.facetKey = this.dumps(defined)
|
this.dumps(defined)
|
||||||
this._umap.isDirty = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,21 +70,7 @@ export class Form extends Utils.WithEvents {
|
||||||
}
|
}
|
||||||
|
|
||||||
setter(field, value) {
|
setter(field, value) {
|
||||||
const path = field.split('.')
|
Utils.setObjectValue(this.obj, field, value)
|
||||||
let obj = this.obj
|
|
||||||
let what
|
|
||||||
for (let i = 0, l = path.length; i < l; i++) {
|
|
||||||
what = path[i]
|
|
||||||
if (what === path[l - 1]) {
|
|
||||||
if (typeof value === 'undefined') {
|
|
||||||
delete obj[what]
|
|
||||||
} else {
|
|
||||||
obj[what] = value
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj = obj[what]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreField(field) {
|
restoreField(field) {
|
||||||
|
@ -191,7 +177,11 @@ export class MutatingForm extends Form {
|
||||||
|
|
||||||
setter(field, value) {
|
setter(field, value) {
|
||||||
const oldValue = this.getter(field)
|
const oldValue = this.getter(field)
|
||||||
super.setter(field, value)
|
if ('setter' in this.obj) {
|
||||||
|
this.obj.setter(field, value)
|
||||||
|
} else {
|
||||||
|
super.setter(field, value)
|
||||||
|
}
|
||||||
if ('render' in this.obj) {
|
if ('render' in this.obj) {
|
||||||
this.obj.render([field], this)
|
this.obj.render([field], this)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
import { DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
import { DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
||||||
import { ServerStored } from './saving.js'
|
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import { MutatingForm } from './form/builder.js'
|
import { MutatingForm } from './form/builder.js'
|
||||||
|
|
||||||
// Dedicated object so we can deal with a separate dirty status, and thus
|
// 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.
|
// call the endpoint only when needed, saving one call at each save.
|
||||||
export class MapPermissions extends ServerStored {
|
export class MapPermissions {
|
||||||
constructor(umap) {
|
constructor(umap) {
|
||||||
super()
|
|
||||||
this.setProperties(umap.properties.permissions)
|
this.setProperties(umap.properties.permissions)
|
||||||
this._umap = umap
|
this._umap = umap
|
||||||
this._isDirty = false
|
|
||||||
this.sync = umap.syncEngine.proxy(this)
|
this.sync = umap.syncEngine.proxy(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +193,6 @@ export class MapPermissions extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if (!this.isDirty) return
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
if (!this.isAnonymousMap() && this.properties.editors) {
|
if (!this.isAnonymousMap() && this.properties.editors) {
|
||||||
const editors = this.properties.editors.map((u) => u.id)
|
const editors = this.properties.editors.map((u) => u.id)
|
||||||
|
@ -255,9 +251,8 @@ export class MapPermissions extends ServerStored {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataLayerPermissions extends ServerStored {
|
export class DataLayerPermissions {
|
||||||
constructor(umap, datalayer) {
|
constructor(umap, datalayer) {
|
||||||
super()
|
|
||||||
this._umap = umap
|
this._umap = umap
|
||||||
this.properties = Object.assign(
|
this.properties = Object.assign(
|
||||||
{
|
{
|
||||||
|
@ -305,7 +300,6 @@ export class DataLayerPermissions extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if (!this.isDirty) return
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('edit_status', this.properties.edit_status)
|
formData.append('edit_status', this.properties.edit_status)
|
||||||
const [data, response, error] = await this._umap.server.post(
|
const [data, response, error] = await this._umap.server.post(
|
||||||
|
|
|
@ -117,7 +117,7 @@ export const Choropleth = FeatureGroup.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
_getValue: function (feature) {
|
_getValue: function (feature) {
|
||||||
const key = this.datalayer.options.choropleth.property || 'value'
|
const key = this.datalayer.options.choropleth?.property || 'value'
|
||||||
const value = +feature.properties[key]
|
const value = +feature.properties[key]
|
||||||
if (!Number.isNaN(value)) return value
|
if (!Number.isNaN(value)) return value
|
||||||
},
|
},
|
||||||
|
@ -130,12 +130,12 @@ export const Choropleth = FeatureGroup.extend({
|
||||||
this.options.colors = []
|
this.options.colors = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const mode = this.datalayer.options.choropleth.mode
|
const mode = this.datalayer.options.choropleth?.mode
|
||||||
let classes = +this.datalayer.options.choropleth.classes || 5
|
let classes = +this.datalayer.options.choropleth?.classes || 5
|
||||||
let breaks
|
let breaks
|
||||||
classes = Math.min(classes, values.length)
|
classes = Math.min(classes, values.length)
|
||||||
if (mode === 'manual') {
|
if (mode === 'manual') {
|
||||||
const manualBreaks = this.datalayer.options.choropleth.breaks
|
const manualBreaks = this.datalayer.options.choropleth?.breaks
|
||||||
if (manualBreaks) {
|
if (manualBreaks) {
|
||||||
breaks = manualBreaks
|
breaks = manualBreaks
|
||||||
.split(',')
|
.split(',')
|
||||||
|
|
|
@ -17,20 +17,10 @@ class Rule {
|
||||||
this.parse()
|
this.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDirty() {
|
|
||||||
return this._isDirty
|
|
||||||
}
|
|
||||||
|
|
||||||
set isDirty(status) {
|
|
||||||
this._isDirty = status
|
|
||||||
if (status) this._umap.isDirty = status
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(umap, condition = '', options = {}) {
|
constructor(umap, condition = '', options = {}) {
|
||||||
// TODO make this public properties when browser coverage is ok
|
// TODO make this public properties when browser coverage is ok
|
||||||
// cf https://caniuse.com/?search=public%20class%20field
|
// cf https://caniuse.com/?search=public%20class%20field
|
||||||
this._condition = null
|
this._condition = null
|
||||||
this._isDirty = false
|
|
||||||
this.OPERATORS = [
|
this.OPERATORS = [
|
||||||
['>', this.gt],
|
['>', this.gt],
|
||||||
['<', this.lt],
|
['<', this.lt],
|
||||||
|
@ -190,17 +180,25 @@ class Rule {
|
||||||
|
|
||||||
_delete() {
|
_delete() {
|
||||||
this._umap.rules.rules = this._umap.rules.rules.filter((rule) => rule !== this)
|
this._umap.rules.rules = this._umap.rules.rules.filter((rule) => rule !== this)
|
||||||
|
this._umap.rules.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
setter(key, value) {
|
||||||
|
const oldRules = Utils.CopyJSON(this._umap.properties.rules || {})
|
||||||
|
Utils.setObjectValue(this, key, value)
|
||||||
|
this._umap.rules.commit()
|
||||||
|
this._umap.sync.update('properties.rules', this._umap.properties.rules, oldRules)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Rules {
|
export default class Rules {
|
||||||
constructor(umap) {
|
constructor(umap) {
|
||||||
this._umap = umap
|
this._umap = umap
|
||||||
this.rules = []
|
|
||||||
this.load()
|
this.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
|
this.rules = []
|
||||||
if (!this._umap.properties.rules?.length) return
|
if (!this._umap.properties.rules?.length) return
|
||||||
for (const { condition, options } of this._umap.properties.rules) {
|
for (const { condition, options } of this._umap.properties.rules) {
|
||||||
if (!condition) continue
|
if (!condition) continue
|
||||||
|
@ -222,8 +220,8 @@ export default class Rules {
|
||||||
else if (finalIndex > initialIndex) newIdx = referenceIdx
|
else if (finalIndex > initialIndex) newIdx = referenceIdx
|
||||||
else newIdx = referenceIdx + 1
|
else newIdx = referenceIdx + 1
|
||||||
this.rules.splice(newIdx, 0, moved)
|
this.rules.splice(newIdx, 0, moved)
|
||||||
moved.isDirty = true
|
|
||||||
this._umap.render(['rules'])
|
this._umap.render(['rules'])
|
||||||
|
this.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
edit(container) {
|
edit(container) {
|
||||||
|
@ -242,7 +240,6 @@ export default class Rules {
|
||||||
|
|
||||||
addRule() {
|
addRule() {
|
||||||
const rule = new Rule(this._umap)
|
const rule = new Rule(this._umap)
|
||||||
rule.isDirty = true
|
|
||||||
this.rules.push(rule)
|
this.rules.push(rule)
|
||||||
rule.edit(map)
|
rule.edit(map)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
const _queue = new Set()
|
|
||||||
|
|
||||||
export let isDirty = false
|
|
||||||
|
|
||||||
export async function save() {
|
|
||||||
for (const obj of _queue) {
|
|
||||||
const ok = await obj.save()
|
|
||||||
if (!ok) break
|
|
||||||
remove(obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clear() {
|
|
||||||
_queue.clear()
|
|
||||||
onUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
function add(obj) {
|
|
||||||
_queue.add(obj)
|
|
||||||
onUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(obj) {
|
|
||||||
_queue.delete(obj)
|
|
||||||
onUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
function has(obj) {
|
|
||||||
return _queue.has(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
function onUpdate() {
|
|
||||||
isDirty = Boolean(_queue.size)
|
|
||||||
// document.body.classList.toggle('umap-is-dirty', isDirty)
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ServerStored {
|
|
||||||
set isDirty(status) {
|
|
||||||
if (status) {
|
|
||||||
add(this)
|
|
||||||
} else {
|
|
||||||
remove(this)
|
|
||||||
}
|
|
||||||
this.onDirty(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDirty() {
|
|
||||||
return has(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
onDirty(status) {}
|
|
||||||
}
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
} from './updaters.js'
|
} from './updaters.js'
|
||||||
import { WebSocketTransport } from './websocket.js'
|
import { WebSocketTransport } from './websocket.js'
|
||||||
import { UndoManager } from './undo.js'
|
import { UndoManager } from './undo.js'
|
||||||
import * as SaveManager from '../saving.js'
|
|
||||||
|
|
||||||
// Start reconnecting after 2 seconds, then double the delay each time
|
// Start reconnecting after 2 seconds, then double the delay each time
|
||||||
// maxing out at 32 seconds.
|
// maxing out at 32 seconds.
|
||||||
|
@ -73,7 +72,7 @@ export class SyncEngine {
|
||||||
this.websocketConnected = false
|
this.websocketConnected = false
|
||||||
this.closeRequested = false
|
this.closeRequested = false
|
||||||
this.peerId = Utils.generateId()
|
this.peerId = Utils.generateId()
|
||||||
this._undoManager = new UndoManager(this.updaters, this)
|
this._undoManager = new UndoManager(umap, this.updaters, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
get isOpen() {
|
get isOpen() {
|
||||||
|
|
|
@ -2,7 +2,8 @@ import * as Utils from '../utils.js'
|
||||||
import { DataLayerUpdater, FeatureUpdater, MapUpdater } from './updaters.js'
|
import { DataLayerUpdater, FeatureUpdater, MapUpdater } from './updaters.js'
|
||||||
|
|
||||||
export class UndoManager {
|
export class UndoManager {
|
||||||
constructor(updaters, syncEngine) {
|
constructor(umap, updaters, syncEngine) {
|
||||||
|
this._umap = umap
|
||||||
this._syncEngine = syncEngine
|
this._syncEngine = syncEngine
|
||||||
this.updaters = updaters
|
this.updaters = updaters
|
||||||
this._undoStack = []
|
this._undoStack = []
|
||||||
|
@ -10,6 +11,8 @@ export class UndoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleState() {
|
toggleState() {
|
||||||
|
// document is undefined during unittests
|
||||||
|
if (typeof document === 'undefined') return
|
||||||
const undoButton = document.querySelector('.edit-undo')
|
const undoButton = document.querySelector('.edit-undo')
|
||||||
const redoButton = document.querySelector('.edit-redo')
|
const redoButton = document.querySelector('.edit-redo')
|
||||||
if (undoButton) undoButton.disabled = !this._undoStack.length
|
if (undoButton) undoButton.disabled = !this._undoStack.length
|
||||||
|
@ -25,6 +28,7 @@ export class UndoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
isDirty() {
|
isDirty() {
|
||||||
|
if (!this._umap.id) return true
|
||||||
for (const stage of this._undoStack) {
|
for (const stage of this._undoStack) {
|
||||||
if (stage.operation.dirty) return true
|
if (stage.operation.dirty) return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { fieldInSchema } from '../utils.js'
|
import * as Utils from '../utils.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updaters are classes able to convert messages
|
* Updaters are classes able to convert messages
|
||||||
|
@ -10,27 +10,6 @@ class BaseUpdater {
|
||||||
this._umap = umap
|
this._umap = umap
|
||||||
}
|
}
|
||||||
|
|
||||||
updateObjectValue(obj, key, value) {
|
|
||||||
const parts = key.split('.')
|
|
||||||
const lastKey = parts.pop()
|
|
||||||
|
|
||||||
// Reduce the current list of attributes,
|
|
||||||
// to find the object to set the property onto
|
|
||||||
const objectToSet = parts.reduce((currentObj, part) => {
|
|
||||||
if (currentObj !== undefined && part in currentObj) return currentObj[part]
|
|
||||||
}, obj)
|
|
||||||
|
|
||||||
// In case the given path doesn't exist, stop here
|
|
||||||
if (objectToSet === undefined) return
|
|
||||||
|
|
||||||
// Set the value (or delete it)
|
|
||||||
if (typeof value === 'undefined') {
|
|
||||||
delete objectToSet[lastKey]
|
|
||||||
} else {
|
|
||||||
objectToSet[lastKey] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getDataLayerFromID(layerId) {
|
getDataLayerFromID(layerId) {
|
||||||
return this._umap.getDataLayerByUmapId(layerId)
|
return this._umap.getDataLayerByUmapId(layerId)
|
||||||
}
|
}
|
||||||
|
@ -43,8 +22,8 @@ class BaseUpdater {
|
||||||
|
|
||||||
export class MapUpdater extends BaseUpdater {
|
export class MapUpdater extends BaseUpdater {
|
||||||
update({ key, value }) {
|
update({ key, value }) {
|
||||||
if (fieldInSchema(key)) {
|
if (Utils.fieldInSchema(key)) {
|
||||||
this.updateObjectValue(this._umap, key, value)
|
Utils.setObjectValue(this._umap, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._umap.onPropertiesUpdated([key])
|
this._umap.onPropertiesUpdated([key])
|
||||||
|
@ -73,8 +52,10 @@ export class DataLayerUpdater extends BaseUpdater {
|
||||||
|
|
||||||
update({ key, metadata, value }) {
|
update({ key, metadata, value }) {
|
||||||
const datalayer = this.getDataLayerFromID(metadata.id)
|
const datalayer = this.getDataLayerFromID(metadata.id)
|
||||||
if (fieldInSchema(key)) {
|
if (key === 'options') {
|
||||||
this.updateObjectValue(datalayer, key, value)
|
datalayer.setOptions(value)
|
||||||
|
} else if (Utils.fieldInSchema(key)) {
|
||||||
|
Utils.setObjectValue(datalayer, key, value)
|
||||||
} else {
|
} else {
|
||||||
console.debug(
|
console.debug(
|
||||||
'Not applying update for datalayer because key is not in the schema',
|
'Not applying update for datalayer because key is not in the schema',
|
||||||
|
@ -127,7 +108,7 @@ export class FeatureUpdater extends BaseUpdater {
|
||||||
const feature = this.getFeatureFromMetadata(metadata)
|
const feature = this.getFeatureFromMetadata(metadata)
|
||||||
feature.geometry = value
|
feature.geometry = value
|
||||||
} else {
|
} else {
|
||||||
this.updateObjectValue(feature, key, value)
|
Utils.setObjectValue(feature, key, value)
|
||||||
feature.datalayer.indexProperties(feature)
|
feature.datalayer.indexProperties(feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +129,8 @@ export class FeatureUpdater extends BaseUpdater {
|
||||||
|
|
||||||
export class MapPermissionsUpdater extends BaseUpdater {
|
export class MapPermissionsUpdater extends BaseUpdater {
|
||||||
update({ key, value }) {
|
update({ key, value }) {
|
||||||
if (fieldInSchema(key)) {
|
if (Utils.fieldInSchema(key)) {
|
||||||
this.updateObjectValue(this._umap.permissions, key, value)
|
Utils.setObjectValue(this._umap.permissions, key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,8 +141,8 @@ export class MapPermissionsUpdater extends BaseUpdater {
|
||||||
|
|
||||||
export class DataLayerPermissionsUpdater extends BaseUpdater {
|
export class DataLayerPermissionsUpdater extends BaseUpdater {
|
||||||
update({ key, value, metadata }) {
|
update({ key, value, metadata }) {
|
||||||
if (fieldInSchema(key)) {
|
if (Utils.fieldInSchema(key)) {
|
||||||
this.updateObjectValue(this.getDataLayerFromID(metadata.id), key, value)
|
Utils.setObjectValue(this.getDataLayerFromID(metadata.id), key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,7 @@ export class TopBar extends WithTemplate {
|
||||||
this.elements.view.disabled = this._umap.sync._undoManager.isDirty()
|
this.elements.view.disabled = this._umap.sync._undoManager.isDirty()
|
||||||
this.elements.saveLabel.hidden = this._umap.permissions.isDraft()
|
this.elements.saveLabel.hidden = this._umap.permissions.isDraft()
|
||||||
this.elements.saveDraftLabel.hidden = !this._umap.permissions.isDraft()
|
this.elements.saveDraftLabel.hidden = !this._umap.permissions.isDraft()
|
||||||
|
this._umap.sync._undoManager.toggleState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1159,7 +1159,6 @@ export default class Umap {
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
this.rules.commit()
|
|
||||||
const geojson = {
|
const geojson = {
|
||||||
type: 'Feature',
|
type: 'Feature',
|
||||||
geometry: this.geometry(),
|
geometry: this.geometry(),
|
||||||
|
@ -1320,6 +1319,9 @@ export default class Umap {
|
||||||
this.bottomBar.redraw()
|
this.bottomBar.redraw()
|
||||||
break
|
break
|
||||||
case 'data':
|
case 'data':
|
||||||
|
if (fields.includes('properties.rules')) {
|
||||||
|
this.rules.load()
|
||||||
|
}
|
||||||
this.eachVisibleDataLayer((datalayer) => {
|
this.eachVisibleDataLayer((datalayer) => {
|
||||||
datalayer.redraw()
|
datalayer.redraw()
|
||||||
})
|
})
|
||||||
|
|
|
@ -481,6 +481,27 @@ export const debounce = (callback, wait) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setObjectValue(obj, key, value) {
|
||||||
|
const parts = key.split('.')
|
||||||
|
const lastKey = parts.pop()
|
||||||
|
|
||||||
|
// Reduce the current list of attributes,
|
||||||
|
// to find the object to set the property onto
|
||||||
|
const objectToSet = parts.reduce((currentObj, part) => {
|
||||||
|
if (currentObj !== undefined && part in currentObj) return currentObj[part]
|
||||||
|
}, obj)
|
||||||
|
|
||||||
|
// In case the given path doesn't exist, stop here
|
||||||
|
if (objectToSet === undefined) return
|
||||||
|
|
||||||
|
// Set the value (or delete it)
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
delete objectToSet[lastKey]
|
||||||
|
} else {
|
||||||
|
objectToSet[lastKey] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const COLORS = [
|
export const COLORS = [
|
||||||
'Black',
|
'Black',
|
||||||
'Navy',
|
'Navy',
|
||||||
|
|
|
@ -475,22 +475,6 @@ ul.photon-autocomplete {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.umap-datalayer-version {
|
|
||||||
padding: 5px 0;
|
|
||||||
border-bottom: 1px solid #202425;
|
|
||||||
}
|
|
||||||
.umap-datalayer-version button {
|
|
||||||
display: inline-block;
|
|
||||||
width: 24px;
|
|
||||||
min-height: 24px;
|
|
||||||
background-position: -122px -73px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-image: url('./img/16-white.svg');
|
|
||||||
margin-inline-end: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leaflet-toolbar-tip {
|
.leaflet-toolbar-tip {
|
||||||
background-color: var(--color-darkGray);
|
background-color: var(--color-darkGray);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"globals": {
|
|
||||||
"describe": true,
|
|
||||||
"happen": true,
|
|
||||||
"assert": true,
|
|
||||||
"before": true,
|
|
||||||
"after": true,
|
|
||||||
"it": true,
|
|
||||||
"sinon": true,
|
|
||||||
"enableEdit": true,
|
|
||||||
"disableEdit": true,
|
|
||||||
"changeInputValue": true,
|
|
||||||
"resetMap": true,
|
|
||||||
"initMap": true,
|
|
||||||
"clickCancel": true,
|
|
||||||
"map": true,
|
|
||||||
"qs": true,
|
|
||||||
"qsa": true,
|
|
||||||
"qst": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,463 +0,0 @@
|
||||||
describe('U.DataLayer', () => {
|
|
||||||
let path = '/map/99/datalayer/update/62/',
|
|
||||||
map,
|
|
||||||
datalayer
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
fetchMock.mock(/\/datalayer\/62\/\?.*/, JSON.stringify(RESPONSES.datalayer62_GET))
|
|
||||||
fetchMock.sticky('/map/99/update/settings/', { id: 99 })
|
|
||||||
this.options = {
|
|
||||||
umap_id: 99,
|
|
||||||
}
|
|
||||||
MAP = map = initMap({ umap_id: 99 })
|
|
||||||
const datalayer_options = defaultDatalayerData()
|
|
||||||
await map.initDataLayers([datalayer_options])
|
|
||||||
datalayer = map.getDataLayerByUmapId(62)
|
|
||||||
enableEdit()
|
|
||||||
})
|
|
||||||
after(() => {
|
|
||||||
fetchMock.restore()
|
|
||||||
resetMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#init()', () => {
|
|
||||||
it('should be added in datalayers index', () => {
|
|
||||||
assert.notEqual(map.datalayers_index.indexOf(datalayer), -1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#edit()', () => {
|
|
||||||
var editButton, form, input, forceButton
|
|
||||||
|
|
||||||
it('row in control should be active', () => {
|
|
||||||
assert.notOk(
|
|
||||||
qs('.leaflet-control-browse #browse_data_toggle_' + L.stamp(datalayer) + '.off')
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have edit button', () => {
|
|
||||||
editButton = qs('#browse_data_toggle_' + L.stamp(datalayer) + ' .layer-edit')
|
|
||||||
assert.ok(editButton)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have toggle visibility element', () => {
|
|
||||||
assert.ok(qs('.leaflet-control-browse i.layer-toggle'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should exist only one datalayer', () => {
|
|
||||||
assert.equal(qsa('.leaflet-control-browse i.layer-toggle').length, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should build a form on edit button click', () => {
|
|
||||||
happen.click(editButton)
|
|
||||||
form = qs('form.umap-form')
|
|
||||||
input = qs('form.umap-form input[name="name"]')
|
|
||||||
assert.ok(form)
|
|
||||||
assert.ok(input)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update name on input change', () => {
|
|
||||||
var new_name = 'This is a new name'
|
|
||||||
input.value = new_name
|
|
||||||
happen.once(input, { type: 'input' })
|
|
||||||
assert.equal(datalayer.options.name, new_name)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have made datalayer dirty', () => {
|
|
||||||
assert.ok(datalayer.isDirty)
|
|
||||||
assert.notEqual(map.dirty_datalayers.indexOf(datalayer), -1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have made Map dirty', () => {
|
|
||||||
assert.ok(map.isDirty)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should call datalayer.save on save button click', (done) => {
|
|
||||||
const postDatalayer = fetchMock.post(path, () => {
|
|
||||||
return defaultDatalayerData()
|
|
||||||
})
|
|
||||||
clickSave()
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert(fetchMock.called(path))
|
|
||||||
done()
|
|
||||||
}, 500)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show alert if server respond 412', (done) => {
|
|
||||||
cleanAlert()
|
|
||||||
fetchMock.restore()
|
|
||||||
fetchMock.post(path, 412)
|
|
||||||
happen.click(editButton)
|
|
||||||
input = qs('form.umap-form input[name="name"]')
|
|
||||||
input.value = 'a new name'
|
|
||||||
happen.once(input, { type: 'input' })
|
|
||||||
clickSave()
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert(L.DomUtil.hasClass(map._container, 'umap-alert'))
|
|
||||||
assert.notEqual(map.dirty_datalayers.indexOf(datalayer), -1)
|
|
||||||
const forceButton = qs('#umap-alert-container .umap-action')
|
|
||||||
assert.ok(forceButton)
|
|
||||||
done()
|
|
||||||
}, 500)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should save anyway on force save button click', (done) => {
|
|
||||||
const forceButton = qs('#umap-alert-container .umap-action')
|
|
||||||
fetchMock.restore()
|
|
||||||
fetchMock.post(path, defaultDatalayerData)
|
|
||||||
happen.click(forceButton)
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert.notOk(qs('#umap-alert-container .umap-action'))
|
|
||||||
assert(fetchMock.called(path))
|
|
||||||
assert.equal(map.dirty_datalayers.indexOf(datalayer), -1)
|
|
||||||
done()
|
|
||||||
}, 500)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#save() new', () => {
|
|
||||||
let newLayerButton, form, input, newDatalayer, editButton, manageButton
|
|
||||||
|
|
||||||
it('should have a manage datalayers action', () => {
|
|
||||||
enableEdit()
|
|
||||||
manageButton = qs('.manage-datalayers')
|
|
||||||
assert.ok(manageButton)
|
|
||||||
happen.click(manageButton)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have a new layer button', () => {
|
|
||||||
newLayerButton = qs('.panel.right.on .add-datalayer')
|
|
||||||
assert.ok(newLayerButton)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should build a form on new layer button click', () => {
|
|
||||||
happen.click(newLayerButton)
|
|
||||||
form = qs('form.umap-form')
|
|
||||||
input = qs('form.umap-form input[name="name"]')
|
|
||||||
assert.ok(form)
|
|
||||||
assert.ok(input)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have an empty name', () => {
|
|
||||||
assert.notOk(input.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have created a new datalayer', () => {
|
|
||||||
assert.equal(map.datalayers_index.length, 2)
|
|
||||||
newDatalayer = map.datalayers_index[1]
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have made Map dirty', () => {
|
|
||||||
assert.ok(map.isDirty)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update name on input change', () => {
|
|
||||||
var new_name = 'This is a new name'
|
|
||||||
input.value = new_name
|
|
||||||
happen.once(input, { type: 'input' })
|
|
||||||
assert.equal(newDatalayer.options.name, new_name)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should set umap_id on save callback', async () => {
|
|
||||||
assert.notOk(newDatalayer.umap_id)
|
|
||||||
fetchMock.post('/map/99/datalayer/create/', defaultDatalayerData({ id: 63 }))
|
|
||||||
clickSave()
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert.equal(newDatalayer.umap_id, 63)
|
|
||||||
resolve()
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have unset map dirty', () => {
|
|
||||||
assert.notOk(map.isDirty)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have edit button', () => {
|
|
||||||
editButton = qs('#browse_data_toggle_' + L.stamp(newDatalayer) + ' .layer-edit')
|
|
||||||
assert.ok(editButton)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should call update if we edit again', async () => {
|
|
||||||
happen.click(editButton)
|
|
||||||
assert.notOk(map.isDirty)
|
|
||||||
input = qs('form.umap-form input[name="name"]')
|
|
||||||
input.value = "a new name again but we don't care which"
|
|
||||||
happen.once(input, { type: 'input' })
|
|
||||||
assert.ok(map.isDirty)
|
|
||||||
var response = () => {
|
|
||||||
return defaultDatalayerData({ pk: 63 })
|
|
||||||
}
|
|
||||||
var spy = sinon.spy(response)
|
|
||||||
fetchMock.post('/map/99/datalayer/update/63/', spy)
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
clickSave()
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert.ok(spy.calledOnce)
|
|
||||||
resolve()
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#iconClassChange()', () => {
|
|
||||||
it('should change icon class', () => {
|
|
||||||
happen.click(qs('[data-id="' + datalayer._leaflet_id + '"] .layer-edit'))
|
|
||||||
changeSelectValue(
|
|
||||||
qs('form#datalayer-advanced-properties select[name=iconClass]'),
|
|
||||||
'Circle'
|
|
||||||
)
|
|
||||||
assert.notOk(qs('div.umap-div-icon'))
|
|
||||||
assert.ok(qs('div.umap-circle-icon'))
|
|
||||||
happen.click(
|
|
||||||
qs('form#datalayer-advanced-properties .umap-field-iconClass .undefine')
|
|
||||||
)
|
|
||||||
assert.notOk(qs('div.umap-circle-icon'))
|
|
||||||
assert.ok(qs('div.umap-div-icon'))
|
|
||||||
clickCancel()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#show/hide', () => {
|
|
||||||
it('should hide features on hide', () => {
|
|
||||||
assert.ok(qs('div.umap-div-icon'))
|
|
||||||
assert.ok(qs('path[fill="none"]'))
|
|
||||||
datalayer.hide()
|
|
||||||
assert.notOk(qs('div.umap-div-icon'))
|
|
||||||
assert.notOk(qs('path[fill="none"]'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show features on show', () => {
|
|
||||||
assert.notOk(qs('div.umap-div-icon'))
|
|
||||||
assert.notOk(qs('path[fill="none"]'))
|
|
||||||
datalayer.show()
|
|
||||||
assert.ok(qs('div.umap-div-icon'))
|
|
||||||
assert.ok(qs('path[fill="none"]'))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#clone()', () => {
|
|
||||||
it('should clone everything but the id and the name', () => {
|
|
||||||
enableEdit()
|
|
||||||
var clone = datalayer.clone()
|
|
||||||
assert.notOk(clone.umap_id)
|
|
||||||
assert.notEqual(clone.options.name, datalayer.name)
|
|
||||||
assert.ok(clone.options.name)
|
|
||||||
assert.equal(clone.options.color, datalayer.options.color)
|
|
||||||
assert.equal(clone.options.stroke, datalayer.options.stroke)
|
|
||||||
clone._delete()
|
|
||||||
clickSave()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#restore()', () => {
|
|
||||||
var oldConfirm,
|
|
||||||
newConfirm = () => {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
before(() => {
|
|
||||||
oldConfirm = window.confirm
|
|
||||||
window.confirm = newConfirm
|
|
||||||
})
|
|
||||||
after(() => {
|
|
||||||
window.confirm = oldConfirm
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should restore everything', (done) => {
|
|
||||||
enableEdit()
|
|
||||||
var geojson = L.Util.CopyJSON(RESPONSES.datalayer62_GET)
|
|
||||||
geojson.features.push({
|
|
||||||
geometry: {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [-1.274658203125, 50.57634993749885],
|
|
||||||
},
|
|
||||||
type: 'Feature',
|
|
||||||
id: 1807,
|
|
||||||
properties: { _umap_options: {}, name: 'new point from restore' },
|
|
||||||
})
|
|
||||||
geojson._umap_options.color = 'Chocolate'
|
|
||||||
fetchMock.get('/datalayer/62/olderversion.geojson', geojson)
|
|
||||||
sinon.spy(window, 'confirm')
|
|
||||||
datalayer.restore('olderversion.geojson')
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert(window.confirm.calledOnce)
|
|
||||||
window.confirm.restore()
|
|
||||||
assert.equal(datalayer.umap_id, 62)
|
|
||||||
assert.ok(datalayer.isDirty)
|
|
||||||
assert.equal(datalayer._index.length, 4)
|
|
||||||
assert.ok(qs('path[fill="Chocolate"]'))
|
|
||||||
done()
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should revert anything on cancel click', () => {
|
|
||||||
clickCancel()
|
|
||||||
assert.equal(datalayer._index.length, 3)
|
|
||||||
assert.notOk(qs('path[fill="Chocolate"]'))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#smart-options()', () => {
|
|
||||||
let poly, marker
|
|
||||||
before(() => {
|
|
||||||
datalayer.eachLayer(function (layer) {
|
|
||||||
if (!poly && layer instanceof L.Polygon) {
|
|
||||||
poly = layer
|
|
||||||
}
|
|
||||||
if (!marker && layer instanceof L.Marker) {
|
|
||||||
marker = layer
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should parse color variable', () => {
|
|
||||||
let icon = qs('div.umap-div-icon .icon_container')
|
|
||||||
poly.properties.mycolor = 'DarkGoldenRod'
|
|
||||||
marker.properties.mycolor = 'DarkRed'
|
|
||||||
marker.properties._umap_options.color = undefined
|
|
||||||
assert.notOk(qs('path[fill="DarkGoldenRod"]'))
|
|
||||||
assert.equal(icon.style.backgroundColor, 'olivedrab')
|
|
||||||
datalayer.options.color = '{mycolor}'
|
|
||||||
datalayer.options.fillColor = '{mycolor}'
|
|
||||||
datalayer.indexProperties(poly)
|
|
||||||
datalayer.indexProperties(marker)
|
|
||||||
datalayer.redraw()
|
|
||||||
icon = qs('div.umap-div-icon .icon_container')
|
|
||||||
assert.equal(icon.style.backgroundColor, 'darkred')
|
|
||||||
assert.ok(qs('path[fill="DarkGoldenRod"]'))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#facet-search()', () => {
|
|
||||||
before(async () => {
|
|
||||||
fetchMock.get(/\/datalayer\/63\/\?.*/, RESPONSES.datalayer63_GET)
|
|
||||||
map.options.facetKey = 'name'
|
|
||||||
await map.initDataLayers([RESPONSES.datalayer63_GET._umap_options])
|
|
||||||
})
|
|
||||||
it('should not impact non browsable layer', () => {
|
|
||||||
assert.ok(qs('path[fill="SteelBlue"]'))
|
|
||||||
})
|
|
||||||
it('should allow advanced filter', () => {
|
|
||||||
map.openFacet()
|
|
||||||
assert.ok(qs('div.umap-facet-search'))
|
|
||||||
// This one if from the normal datalayer
|
|
||||||
// it's name is "test", so it should be hidden
|
|
||||||
// by the filter
|
|
||||||
assert.ok(qs('path[fill="none"]'))
|
|
||||||
happen.click(qs('input[data-value="name poly"]'))
|
|
||||||
assert.notOk(qs('path[fill="none"]'))
|
|
||||||
// This one comes from a non browsable layer
|
|
||||||
// so it should not be affected by the filter
|
|
||||||
assert.ok(qs('path[fill="SteelBlue"]'))
|
|
||||||
happen.click(qs('input[data-value="name poly"]')) // Undo
|
|
||||||
})
|
|
||||||
it('should allow to control facet label', () => {
|
|
||||||
map.options.facetKey = 'name|Nom'
|
|
||||||
map.openFacet()
|
|
||||||
assert.ok(qs('div.umap-facet-search h5'))
|
|
||||||
assert.equal(qs('div.umap-facet-search h5').textContent, 'Nom')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('#zoomEnd', () => {
|
|
||||||
it('should honour the fromZoom option', () => {
|
|
||||||
map.setZoom(6, { animate: false })
|
|
||||||
assert.ok(qs('path[fill="none"]'))
|
|
||||||
datalayer.options.fromZoom = 6
|
|
||||||
map.setZoom(5, { animate: false })
|
|
||||||
assert.notOk(qs('path[fill="none"]'))
|
|
||||||
map.setZoom(6, { animate: false })
|
|
||||||
assert.ok(qs('path[fill="none"]'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should honour the toZoom option', () => {
|
|
||||||
map.setZoom(6, { animate: false })
|
|
||||||
assert.ok(qs('path[fill="none"]'))
|
|
||||||
datalayer.options.toZoom = 6
|
|
||||||
map.setZoom(7, { animate: false })
|
|
||||||
assert.notOk(qs('path[fill="none"]'))
|
|
||||||
map.setZoom(6, { animate: false })
|
|
||||||
assert.ok(qs('path[fill="none"]'))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#displayOnLoad', () => {
|
|
||||||
before(() => {
|
|
||||||
fetchMock.get(/\/datalayer\/64\/\?.*/, RESPONSES.datalayer64_GET)
|
|
||||||
})
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await map.initDataLayers([RESPONSES.datalayer64_GET._umap_options])
|
|
||||||
datalayer = map.getDataLayerByUmapId(64)
|
|
||||||
map.setZoom(10, { animate: false })
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
datalayer._delete()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not display layer at load', () => {
|
|
||||||
assert.notOk(qs('path[fill="AliceBlue"]'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should display on click', (done) => {
|
|
||||||
happen.click(qs(`[data-id='${L.stamp(datalayer)}'] .layer-toggle`))
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert.ok(qs('path[fill="AliceBlue"]'))
|
|
||||||
done()
|
|
||||||
}, 500)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not display on zoom', (done) => {
|
|
||||||
map.setZoom(9, { animate: false })
|
|
||||||
window.setTimeout(() => {
|
|
||||||
assert.notOk(qs('path[fill="AliceBlue"]'))
|
|
||||||
done()
|
|
||||||
}, 500)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#delete()', () => {
|
|
||||||
let deleteLink,
|
|
||||||
deletePath = '/map/99/datalayer/delete/62/'
|
|
||||||
before(() => {
|
|
||||||
datalayer = map.getDataLayerByUmapId(62)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have a delete link in update form', () => {
|
|
||||||
enableEdit()
|
|
||||||
happen.click(qs('#browse_data_toggle_' + L.stamp(datalayer) + ' .layer-edit'))
|
|
||||||
deleteLink = qs('button.delete_datalayer_button')
|
|
||||||
assert.ok(deleteLink)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should delete features on datalayer delete', () => {
|
|
||||||
happen.click(deleteLink)
|
|
||||||
assert.notOk(qs('div.icon_container'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have set map dirty', () => {
|
|
||||||
assert.ok(map.isDirty)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should delete layer control row on delete', () => {
|
|
||||||
assert.notOk(
|
|
||||||
qs('.leaflet-control-browse #browse_data_toggle_' + L.stamp(datalayer))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be removed from map.datalayers_index', () => {
|
|
||||||
assert.equal(map.datalayers_index.indexOf(datalayer), -1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be removed from map.datalayers', () => {
|
|
||||||
assert.notOk(map.datalayers[L.stamp(datalayer)])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be visible again on edit cancel', () => {
|
|
||||||
clickCancel()
|
|
||||||
assert.ok(qs('div.icon_container'))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,131 +0,0 @@
|
||||||
describe('U.FeatureMixin', function () {
|
|
||||||
let map, datalayer
|
|
||||||
before(async () => {
|
|
||||||
await fetchMock.mock(
|
|
||||||
/\/datalayer\/62\/\?.*/,
|
|
||||||
JSON.stringify(RESPONSES.datalayer62_GET)
|
|
||||||
)
|
|
||||||
this.options = {
|
|
||||||
umap_id: 99,
|
|
||||||
}
|
|
||||||
MAP = map = initMap({ umap_id: 99 })
|
|
||||||
const datalayer_options = defaultDatalayerData()
|
|
||||||
await map.initDataLayers([datalayer_options])
|
|
||||||
datalayer = map.getDataLayerByUmapId(62)
|
|
||||||
})
|
|
||||||
after(function () {
|
|
||||||
fetchMock.restore()
|
|
||||||
resetMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#utils()', function () {
|
|
||||||
var poly, marker
|
|
||||||
function setFeatures(datalayer) {
|
|
||||||
datalayer.eachLayer(function (layer) {
|
|
||||||
if (!poly && layer instanceof L.Polygon) {
|
|
||||||
poly = layer
|
|
||||||
}
|
|
||||||
if (!marker && layer instanceof L.Marker) {
|
|
||||||
marker = layer
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
it('should generate a valid geojson', function () {
|
|
||||||
setFeatures(datalayer)
|
|
||||||
assert.ok(poly)
|
|
||||||
assert.deepEqual(poly.toGeoJSON().geometry, {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[11.25, 53.585984],
|
|
||||||
[10.151367, 52.975108],
|
|
||||||
[12.689209, 52.167194],
|
|
||||||
[14.084473, 53.199452],
|
|
||||||
[12.634277, 53.618579],
|
|
||||||
[11.25, 53.585984],
|
|
||||||
[11.25, 53.585984],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
})
|
|
||||||
// Ensure original latlngs has not been modified
|
|
||||||
assert.equal(poly.getLatLngs()[0].length, 6)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should remove empty _umap_options from exported geojson', function () {
|
|
||||||
setFeatures(datalayer)
|
|
||||||
assert.ok(poly)
|
|
||||||
assert.deepEqual(poly.toGeoJSON().properties, { name: 'name poly' })
|
|
||||||
assert.ok(marker)
|
|
||||||
assert.deepEqual(marker.toGeoJSON().properties, {
|
|
||||||
_umap_options: { color: 'OliveDrab' },
|
|
||||||
name: 'test',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#properties()', function () {
|
|
||||||
it('should rename property', function () {
|
|
||||||
var poly = datalayer._lineToLayer({}, [
|
|
||||||
[0, 0],
|
|
||||||
[0, 1],
|
|
||||||
[0, 2],
|
|
||||||
])
|
|
||||||
poly.properties.prop1 = 'xxx'
|
|
||||||
poly.renameProperty('prop1', 'prop2')
|
|
||||||
assert.equal(poly.properties.prop2, 'xxx')
|
|
||||||
assert.ok(typeof poly.properties.prop1 === 'undefined')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not create property when renaming', function () {
|
|
||||||
var poly = datalayer._lineToLayer({}, [
|
|
||||||
[0, 0],
|
|
||||||
[0, 1],
|
|
||||||
[0, 2],
|
|
||||||
])
|
|
||||||
delete poly.properties.prop2 // Make sure it doesn't exist
|
|
||||||
poly.renameProperty('prop1', 'prop2')
|
|
||||||
assert.ok(typeof poly.properties.prop2 === 'undefined')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should delete property', function () {
|
|
||||||
var poly = datalayer._lineToLayer({}, [
|
|
||||||
[0, 0],
|
|
||||||
[0, 1],
|
|
||||||
[0, 2],
|
|
||||||
])
|
|
||||||
poly.properties.prop = 'xxx'
|
|
||||||
assert.equal(poly.properties.prop, 'xxx')
|
|
||||||
poly.deleteProperty('prop')
|
|
||||||
assert.ok(typeof poly.properties.prop === 'undefined')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#matchFilter()', function () {
|
|
||||||
var poly
|
|
||||||
|
|
||||||
it('should filter on properties', function () {
|
|
||||||
poly = datalayer._lineToLayer({}, [
|
|
||||||
[0, 0],
|
|
||||||
[0, 1],
|
|
||||||
[0, 2],
|
|
||||||
])
|
|
||||||
poly.properties.name = 'mooring'
|
|
||||||
assert.ok(poly.matchFilter('moo', ['name']))
|
|
||||||
assert.notOk(poly.matchFilter('foo', ['name']))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be case unsensitive', function () {
|
|
||||||
assert.ok(poly.matchFilter('Moo', ['name']))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should match also in the middle of a string', function () {
|
|
||||||
assert.ok(poly.matchFilter('oor', ['name']))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should handle multiproperties', function () {
|
|
||||||
poly.properties.city = 'Teulada'
|
|
||||||
assert.ok(poly.matchFilter('eul', ['name', 'city', 'foo']))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
|
@ -1,37 +0,0 @@
|
||||||
describe('U.Map', () => {
|
|
||||||
let map, datalayer
|
|
||||||
before(async () => {
|
|
||||||
await fetchMock.mock(
|
|
||||||
/\/datalayer\/62\/\?.*/,
|
|
||||||
JSON.stringify(RESPONSES.datalayer62_GET)
|
|
||||||
)
|
|
||||||
this.options = {
|
|
||||||
umap_id: 99,
|
|
||||||
}
|
|
||||||
map = initMap({ umap_id: 99 })
|
|
||||||
const datalayer_options = defaultDatalayerData()
|
|
||||||
await map.initDataLayers([datalayer_options])
|
|
||||||
datalayer = map.getDataLayerByUmapId(62)
|
|
||||||
})
|
|
||||||
after(() => {
|
|
||||||
fetchMock.restore()
|
|
||||||
clickCancel()
|
|
||||||
resetMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#localizeUrl()', function () {
|
|
||||||
it('should replace known variables', function () {
|
|
||||||
assert.equal(
|
|
||||||
map.localizeUrl('http://example.org/{zoom}'),
|
|
||||||
'http://example.org/' + map.getZoom()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should keep unknown variables', function () {
|
|
||||||
assert.equal(
|
|
||||||
map.localizeUrl('http://example.org/{unkown}'),
|
|
||||||
'http://example.org/{unkown}'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,126 +0,0 @@
|
||||||
describe('U.Marker', () => {
|
|
||||||
let map, datalayer
|
|
||||||
before(async () => {
|
|
||||||
const datalayer_response = JSON.parse(JSON.stringify(RESPONSES.datalayer62_GET)) // Copy.
|
|
||||||
datalayer_response._umap_options.iconClass = 'Drop'
|
|
||||||
await fetchMock.mock(/\/datalayer\/62\/\?.*/, datalayer_response)
|
|
||||||
this.options = {
|
|
||||||
umap_id: 99,
|
|
||||||
}
|
|
||||||
MAP = map = initMap({ umap_id: 99 })
|
|
||||||
const datalayer_options = defaultDatalayerData()
|
|
||||||
await map.initDataLayers([datalayer_options])
|
|
||||||
datalayer = map.getDataLayerByUmapId(62)
|
|
||||||
})
|
|
||||||
after(() => {
|
|
||||||
fetchMock.restore()
|
|
||||||
resetMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#iconClassChange()', () => {
|
|
||||||
it('should change icon class', () => {
|
|
||||||
enableEdit()
|
|
||||||
happen.click(qs('div.umap-drop-icon'))
|
|
||||||
happen.click(qs('ul.leaflet-inplace-toolbar a.umap-toggle-edit'))
|
|
||||||
changeSelectValue(
|
|
||||||
qs(
|
|
||||||
'form#umap-feature-shape-properties .umap-field-iconClass select[name=iconClass]'
|
|
||||||
),
|
|
||||||
'Circle'
|
|
||||||
)
|
|
||||||
assert.notOk(qs('div.umap-drop-icon'))
|
|
||||||
assert.ok(qs('div.umap-circle-icon'))
|
|
||||||
happen.click(
|
|
||||||
qs('form#umap-feature-shape-properties .umap-field-iconClass .undefine')
|
|
||||||
)
|
|
||||||
assert.notOk(qs('div.umap-circle-icon'))
|
|
||||||
assert.ok(qs('div.umap-drop-icon'))
|
|
||||||
clickCancel()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#iconSymbolChange()', () => {
|
|
||||||
it('should change icon symbol', () => {
|
|
||||||
enableEdit()
|
|
||||||
happen.click(qs('div.umap-drop-icon'))
|
|
||||||
happen.click(qs('ul.leaflet-inplace-toolbar a.umap-toggle-edit'))
|
|
||||||
changeInputValue(
|
|
||||||
qs(
|
|
||||||
'form#umap-feature-shape-properties .umap-field-iconUrl input[name=iconUrl]'
|
|
||||||
),
|
|
||||||
'1'
|
|
||||||
)
|
|
||||||
assert.equal(qs('div.umap-drop-icon span').textContent, '1')
|
|
||||||
changeInputValue(
|
|
||||||
qs(
|
|
||||||
'form#umap-feature-shape-properties .umap-field-iconUrl input[name=iconUrl]'
|
|
||||||
),
|
|
||||||
'{name}'
|
|
||||||
)
|
|
||||||
assert.equal(qs('div.umap-drop-icon span').textContent, 'test')
|
|
||||||
clickCancel()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#iconClassChange()', () => {
|
|
||||||
it('should change icon class', () => {
|
|
||||||
enableEdit()
|
|
||||||
happen.click(qs('div.umap-drop-icon'))
|
|
||||||
happen.click(qs('ul.leaflet-inplace-toolbar a.umap-toggle-edit'))
|
|
||||||
changeSelectValue(
|
|
||||||
qs(
|
|
||||||
'form#umap-feature-shape-properties .umap-field-iconClass select[name=iconClass]'
|
|
||||||
),
|
|
||||||
'Circle'
|
|
||||||
)
|
|
||||||
assert.notOk(qs('div.umap-drop-icon'))
|
|
||||||
assert.ok(qs('div.umap-circle-icon'))
|
|
||||||
happen.click(
|
|
||||||
qs('form#umap-feature-shape-properties .umap-field-iconClass .undefine')
|
|
||||||
)
|
|
||||||
assert.notOk(qs('div.umap-circle-icon'))
|
|
||||||
assert.ok(qs('div.umap-drop-icon'))
|
|
||||||
clickCancel()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#clone', () => {
|
|
||||||
it('should clone marker', () => {
|
|
||||||
var layer = new U.Marker(map, [10, 20], {
|
|
||||||
datalayer: datalayer,
|
|
||||||
}).addTo(datalayer)
|
|
||||||
assert.equal(datalayer._index.length, 4)
|
|
||||||
other = layer.clone()
|
|
||||||
assert.ok(map.hasLayer(other))
|
|
||||||
assert.equal(datalayer._index.length, 5)
|
|
||||||
// Must not be the same reference
|
|
||||||
assert.notEqual(layer._latlng, other._latlng)
|
|
||||||
assert.equal(L.Util.formatNum(layer._latlng.lat), other._latlng.lat)
|
|
||||||
assert.equal(L.Util.formatNum(layer._latlng.lng), other._latlng.lng)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#edit()', function (done) {
|
|
||||||
it('should allow changing coordinates manually', () => {
|
|
||||||
var layer = new U.Marker(map, [10, 20], {
|
|
||||||
datalayer: datalayer,
|
|
||||||
}).addTo(datalayer)
|
|
||||||
enableEdit()
|
|
||||||
layer.edit()
|
|
||||||
changeInputValue(qs('form.umap-form input[name="lat"]'), '54.43')
|
|
||||||
assert.equal(layer._latlng.lat, 54.43)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow invalid latitude nor longitude', () => {
|
|
||||||
var layer = new U.Marker(map, [10, 20], {
|
|
||||||
datalayer: datalayer,
|
|
||||||
}).addTo(datalayer)
|
|
||||||
enableEdit()
|
|
||||||
layer.edit()
|
|
||||||
changeInputValue(qs('form.umap-form input[name="lat"]'), '5443')
|
|
||||||
assert.equal(layer._latlng.lat, 10)
|
|
||||||
changeInputValue(qs('form.umap-form input[name="lng"]'), '5443')
|
|
||||||
assert.equal(layer._latlng.lng, 20)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,111 +0,0 @@
|
||||||
describe('U.Polygon', function () {
|
|
||||||
var p2ll, map, datalayer
|
|
||||||
|
|
||||||
before(function () {
|
|
||||||
map = initMap({ umap_id: 99 })
|
|
||||||
enableEdit()
|
|
||||||
p2ll = function (x, y) {
|
|
||||||
return map.containerPointToLatLng([x, y])
|
|
||||||
}
|
|
||||||
datalayer = map.createDataLayer()
|
|
||||||
datalayer.connectToMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
after(function () {
|
|
||||||
clickCancel()
|
|
||||||
resetMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
datalayer.empty()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#isMulti()', function () {
|
|
||||||
it('should return false for basic Polygon', function () {
|
|
||||||
var layer = new U.Polygon(
|
|
||||||
map,
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
[5, 6],
|
|
||||||
],
|
|
||||||
{ datalayer: datalayer }
|
|
||||||
)
|
|
||||||
assert.notOk(layer.isMulti())
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return false for nested basic Polygon', function () {
|
|
||||||
var latlngs = [[[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]]],
|
|
||||||
layer = new U.Polygon(map, latlngs, { datalayer: datalayer })
|
|
||||||
assert.notOk(layer.isMulti())
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return false for simple Polygon with hole', function () {
|
|
||||||
var layer = new U.Polygon(
|
|
||||||
map,
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
[5, 6],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[7, 8],
|
|
||||||
[9, 10],
|
|
||||||
[11, 12],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
{ datalayer: datalayer }
|
|
||||||
)
|
|
||||||
assert.notOk(layer.isMulti())
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return true for multi Polygon', function () {
|
|
||||||
var latLngs = [
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
[5, 6],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[7, 8],
|
|
||||||
[9, 10],
|
|
||||||
[11, 12],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]
|
|
||||||
var layer = new U.Polygon(map, latLngs, { datalayer: datalayer })
|
|
||||||
assert.ok(layer.isMulti())
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return true for multi Polygon with hole', function () {
|
|
||||||
var latLngs = [
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[10, 20],
|
|
||||||
[30, 40],
|
|
||||||
[50, 60],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[0, 10],
|
|
||||||
[10, 10],
|
|
||||||
[10, 0],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[2, 3],
|
|
||||||
[2, 4],
|
|
||||||
[3, 4],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]
|
|
||||||
var layer = new U.Polygon(map, latLngs, { datalayer: datalayer })
|
|
||||||
assert.ok(layer.isMulti())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
|
@ -1,286 +0,0 @@
|
||||||
describe('U.Polyline', function () {
|
|
||||||
var p2ll, map
|
|
||||||
|
|
||||||
before(function () {
|
|
||||||
this.map = map = initMap({ umap_id: 99 })
|
|
||||||
enableEdit()
|
|
||||||
p2ll = function (x, y) {
|
|
||||||
return map.containerPointToLatLng([x, y])
|
|
||||||
}
|
|
||||||
this.datalayer = this.map.createDataLayer()
|
|
||||||
this.datalayer.connectToMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
after(function () {
|
|
||||||
clickCancel()
|
|
||||||
resetMap()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
this.datalayer.empty()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#isMulti()', function () {
|
|
||||||
it('should return false for basic Polyline', function () {
|
|
||||||
var layer = new U.Polyline(
|
|
||||||
this.map,
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
[5, 6],
|
|
||||||
],
|
|
||||||
{ datalayer: this.datalayer }
|
|
||||||
)
|
|
||||||
assert.notOk(layer.isMulti())
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return false for nested basic Polyline', function () {
|
|
||||||
var layer = new U.Polyline(
|
|
||||||
this.map,
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
[5, 6],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
{ datalayer: this.datalayer }
|
|
||||||
)
|
|
||||||
assert.notOk(layer.isMulti())
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return true for multi Polyline', function () {
|
|
||||||
var latLngs = [
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
[5, 6],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[7, 8],
|
|
||||||
[9, 10],
|
|
||||||
[11, 12],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]
|
|
||||||
var layer = new U.Polyline(this.map, latLngs, { datalayer: this.datalayer })
|
|
||||||
assert.ok(layer.isMulti())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#contextmenu', function () {
|
|
||||||
afterEach(function () {
|
|
||||||
// Make sure contextmenu is hidden.
|
|
||||||
happen.once(document, { type: 'keydown', keyCode: 27 })
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#in edit mode', function () {
|
|
||||||
it('should allow to remove shape when multi', function () {
|
|
||||||
var latlngs = [
|
|
||||||
[p2ll(100, 100), p2ll(100, 200)],
|
|
||||||
[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)],
|
|
||||||
],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.equal(qst('Remove shape from the multi'), 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to remove shape when not multi', function () {
|
|
||||||
var latlngs = [[p2ll(100, 100), p2ll(100, 200)]],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.notOk(qst('Remove shape from the multi'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to isolate shape when not multi', function () {
|
|
||||||
var latlngs = [[p2ll(100, 100), p2ll(100, 200)]],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.notOk(qst('Extract shape to separate feature'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should allow to isolate shape when multi', function () {
|
|
||||||
var latlngs = [
|
|
||||||
[p2ll(100, 150), p2ll(100, 200)],
|
|
||||||
[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)],
|
|
||||||
],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.ok(qst('Extract shape to separate feature'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to transform to polygon when multi', function () {
|
|
||||||
var latlngs = [
|
|
||||||
[p2ll(100, 150), p2ll(100, 200)],
|
|
||||||
[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)],
|
|
||||||
],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.notOk(qst('Transform to polygon'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should allow to transform to polygon when not multi', function () {
|
|
||||||
var latlngs = [p2ll(100, 150), p2ll(100, 200), p2ll(200, 100)],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.equal(qst('Transform to polygon'), 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to transfer shape when not editedFeature', function () {
|
|
||||||
var layer = new U.Polyline(this.map, [p2ll(100, 150), p2ll(100, 200)], {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.notOk(qst('Transfer shape to edited feature'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to transfer shape when editedFeature is not a line', function () {
|
|
||||||
var layer = new U.Polyline(this.map, [p2ll(100, 150), p2ll(100, 200)], {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer),
|
|
||||||
other = new U.Polygon(
|
|
||||||
this.map,
|
|
||||||
[p2ll(200, 300), p2ll(300, 200), p2ll(200, 100)],
|
|
||||||
{ datalayer: this.datalayer }
|
|
||||||
).addTo(this.datalayer)
|
|
||||||
other.edit()
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.notOk(qst('Transfer shape to edited feature'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should allow to transfer shape when another line is edited', function () {
|
|
||||||
var layer = new U.Polyline(
|
|
||||||
this.map,
|
|
||||||
[p2ll(100, 150), p2ll(100, 200), p2ll(200, 100)],
|
|
||||||
{ datalayer: this.datalayer }
|
|
||||||
).addTo(this.datalayer),
|
|
||||||
other = new U.Polyline(this.map, [p2ll(200, 300), p2ll(300, 200)], {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
other.edit()
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.equal(qst('Transfer shape to edited feature'), 1)
|
|
||||||
other.remove()
|
|
||||||
layer.remove()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should allow to merge lines when multi', function () {
|
|
||||||
var latlngs = [
|
|
||||||
[p2ll(100, 100), p2ll(100, 200)],
|
|
||||||
[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)],
|
|
||||||
],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.equal(qst('Merge lines'), 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to merge lines when not multi', function () {
|
|
||||||
var latlngs = [[p2ll(100, 100), p2ll(100, 200)]],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
happen.once(layer._path, { type: 'contextmenu' })
|
|
||||||
assert.notOk(qst('Merge lines'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should allow to split lines when clicking on vertex', function () {
|
|
||||||
var latlngs = [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
layer.enableEdit()
|
|
||||||
happen.at('contextmenu', 350, 400)
|
|
||||||
assert.equal(qst('Split line'), 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to split lines when clicking on first vertex', function () {
|
|
||||||
var latlngs = [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
layer.enableEdit()
|
|
||||||
happen.at('contextmenu', 300, 350)
|
|
||||||
assert.equal(qst('Delete this feature'), 1) // Make sure we have clicked on the vertex.
|
|
||||||
assert.notOk(qst('Split line'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not allow to split lines when clicking on last vertex', function () {
|
|
||||||
var latlngs = [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
layer.enableEdit()
|
|
||||||
happen.at('contextmenu', 400, 300)
|
|
||||||
assert.equal(qst('Delete this feature'), 1) // Make sure we have clicked on the vertex.
|
|
||||||
assert.notOk(qst('Split line'))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#mergeShapes', function () {
|
|
||||||
it('should remove duplicated join point when merging', function () {
|
|
||||||
var latlngs = [
|
|
||||||
[
|
|
||||||
[0, 0],
|
|
||||||
[0, 1],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[0, 1],
|
|
||||||
[0, 2],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
layer.mergeShapes()
|
|
||||||
layer.disableEdit() // Remove vertex from latlngs to compare them.
|
|
||||||
assert.deepEqual(layer.getLatLngs(), [
|
|
||||||
L.latLng([0, 0]),
|
|
||||||
L.latLng([0, 1]),
|
|
||||||
L.latLng([0, 2]),
|
|
||||||
])
|
|
||||||
assert(this.map.isDirty)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should revert candidate if first point is closer', function () {
|
|
||||||
var latlngs = [
|
|
||||||
[
|
|
||||||
[0, 0],
|
|
||||||
[0, 1],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[0, 2],
|
|
||||||
[0, 1],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
layer = new U.Polyline(this.map, latlngs, {
|
|
||||||
datalayer: this.datalayer,
|
|
||||||
}).addTo(this.datalayer)
|
|
||||||
layer.mergeShapes()
|
|
||||||
layer.disableEdit()
|
|
||||||
assert.deepEqual(layer.getLatLngs(), [
|
|
||||||
L.latLng([0, 0]),
|
|
||||||
L.latLng([0, 1]),
|
|
||||||
L.latLng([0, 2]),
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
|
@ -1,28 +0,0 @@
|
||||||
describe('L.Util', function () {
|
|
||||||
describe('#TextColorFromBackgroundColor', function () {
|
|
||||||
it('should output white for black', function () {
|
|
||||||
document.body.style.backgroundColor = 'black'
|
|
||||||
assert.equal(L.DomUtil.TextColorFromBackgroundColor(document.body), '#ffffff')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should output white for brown', function () {
|
|
||||||
document.body.style.backgroundColor = 'brown'
|
|
||||||
assert.equal(L.DomUtil.TextColorFromBackgroundColor(document.body), '#ffffff')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should output black for white', function () {
|
|
||||||
document.body.style.backgroundColor = 'white'
|
|
||||||
assert.equal(L.DomUtil.TextColorFromBackgroundColor(document.body), '#000000')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should output black for tan', function () {
|
|
||||||
document.body.style.backgroundColor = 'tan'
|
|
||||||
assert.equal(L.DomUtil.TextColorFromBackgroundColor(document.body), '#000000')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should output black by default', function () {
|
|
||||||
document.body.style.backgroundColor = 'transparent'
|
|
||||||
assert.equal(L.DomUtil.TextColorFromBackgroundColor(document.body), '#000000')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,455 +0,0 @@
|
||||||
window.assert = chai.assert
|
|
||||||
window.expect = chai.expect
|
|
||||||
|
|
||||||
var qs = function (selector, element) {
|
|
||||||
return (element || document).querySelector(selector)
|
|
||||||
}
|
|
||||||
var qsa = function (selector) {
|
|
||||||
return document.querySelectorAll(selector)
|
|
||||||
}
|
|
||||||
var qst = function (text, parent) {
|
|
||||||
// find element by its text content
|
|
||||||
var r = document.evaluate(
|
|
||||||
"descendant::*[contains(text(),'" + text + "')]",
|
|
||||||
parent || qs('#map'),
|
|
||||||
null,
|
|
||||||
XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
|
|
||||||
null
|
|
||||||
),
|
|
||||||
count = 0
|
|
||||||
while (r.iterateNext()) console.log(++count)
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
happen.at = function (what, x, y, props) {
|
|
||||||
this.once(
|
|
||||||
document.elementFromPoint(x, y),
|
|
||||||
L.Util.extend(
|
|
||||||
{
|
|
||||||
type: what,
|
|
||||||
clientX: x,
|
|
||||||
clientY: y,
|
|
||||||
screenX: x,
|
|
||||||
screenY: y,
|
|
||||||
which: 1,
|
|
||||||
button: 0,
|
|
||||||
},
|
|
||||||
props || {}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
var resetMap = function () {
|
|
||||||
var mapElement = qs('#map')
|
|
||||||
mapElement.innerHTML = 'Done'
|
|
||||||
delete mapElement._leaflet_id
|
|
||||||
document.body.className = ''
|
|
||||||
}
|
|
||||||
var enableEdit = function () {
|
|
||||||
happen.click(qs('div.leaflet-control-edit-enable button'))
|
|
||||||
}
|
|
||||||
var disableEdit = function () {
|
|
||||||
happen.click(qs('.leaflet-control-edit-disable'))
|
|
||||||
}
|
|
||||||
var clickSave = function () {
|
|
||||||
happen.click(qs('.leaflet-control-edit-save'))
|
|
||||||
}
|
|
||||||
var clickCancel = function () {
|
|
||||||
var _confirm = window.confirm
|
|
||||||
window.confirm = function (text) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
happen.click(qs('button.leaflet-control-edit-cancel'))
|
|
||||||
happen.once(document.body, { type: 'keypress', keyCode: 13 })
|
|
||||||
window.confirm = _confirm
|
|
||||||
}
|
|
||||||
var changeInputValue = function (input, value) {
|
|
||||||
input.value = value
|
|
||||||
happen.once(input, { type: 'input' })
|
|
||||||
happen.once(input, { type: 'blur' })
|
|
||||||
}
|
|
||||||
var changeSelectValue = function (path_or_select, value) {
|
|
||||||
if (typeof path_or_select === 'string') path_or_select = qs(path_or_select)
|
|
||||||
var found = false
|
|
||||||
for (var i = 0; i < path_or_select.length; i++) {
|
|
||||||
if (path_or_select.options[i].value === value) {
|
|
||||||
path_or_select.options[i].selected = true
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
happen.once(path_or_select, { type: 'change' })
|
|
||||||
if (!found)
|
|
||||||
throw new Error('Value ' + value + 'not found in select ' + path_or_select)
|
|
||||||
return path_or_select
|
|
||||||
}
|
|
||||||
var cleanAlert = function () {
|
|
||||||
L.DomUtil.removeClass(qs('#map'), 'umap-alert')
|
|
||||||
L.DomUtil.get('umap-alert-container').innerHTML = ''
|
|
||||||
UI_ALERT_ID = null // Prevent setTimeout to be called
|
|
||||||
}
|
|
||||||
var defaultDatalayerData = function (custom) {
|
|
||||||
var _default = {
|
|
||||||
iconClass: 'Default',
|
|
||||||
name: 'Elephants',
|
|
||||||
displayOnLoad: true,
|
|
||||||
id: 62,
|
|
||||||
pictogram_url: null,
|
|
||||||
weight: null,
|
|
||||||
fillColor: '',
|
|
||||||
color: '',
|
|
||||||
stroke: true,
|
|
||||||
smoothFactor: null,
|
|
||||||
dashArray: '',
|
|
||||||
fill: true,
|
|
||||||
}
|
|
||||||
return L.extend({}, _default, custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
function initMap(options) {
|
|
||||||
default_options = {
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
umap_id: 42,
|
|
||||||
datalayers: [],
|
|
||||||
urls: {
|
|
||||||
map: '/map/{slug}_{pk}',
|
|
||||||
datalayer_view: '/datalayer/{pk}/',
|
|
||||||
map_update: '/map/{map_id}/update/settings/',
|
|
||||||
map_old_url: '/map/{username}/{slug}/',
|
|
||||||
map_clone: '/map/{map_id}/update/clone/',
|
|
||||||
map_short_url: '/m/{pk}/',
|
|
||||||
map_anonymous_edit_url: '/map/anonymous-edit/{signature}',
|
|
||||||
map_new: '/map/new/',
|
|
||||||
datalayer_update: '/map/{map_id}/datalayer/update/{pk}/',
|
|
||||||
map_delete: '/map/{map_id}/update/delete/',
|
|
||||||
map_create: '/map/create/',
|
|
||||||
logout: '/logout/',
|
|
||||||
datalayer_create: '/map/{map_id}/datalayer/create/',
|
|
||||||
login_popup_end: '/login/popupd/',
|
|
||||||
login: '/login/',
|
|
||||||
datalayer_delete: '/map/{map_id}/datalayer/delete/{pk}/',
|
|
||||||
datalayer_versions: '/map/{map_id}/datalayer/{pk}/versions/',
|
|
||||||
datalayer_version: '/datalayer/{pk}/{name}',
|
|
||||||
pictogram_list_json: '/pictogram/json/',
|
|
||||||
map_update_permissions: '/map/{map_id}/update/permissions/',
|
|
||||||
map_download: '/map/{map_id}/download/',
|
|
||||||
},
|
|
||||||
default_iconUrl: '../src/img/marker.svg',
|
|
||||||
zoom: 6,
|
|
||||||
share_statuses: [
|
|
||||||
[1, 'Tout le monde (public)'],
|
|
||||||
[2, 'Quiconque a le lien'],
|
|
||||||
[3, 'Éditeurs uniquement'],
|
|
||||||
],
|
|
||||||
tilelayers: [
|
|
||||||
{
|
|
||||||
attribution: '\u00a9 OSM Contributors',
|
|
||||||
name: 'OpenStreetMap',
|
|
||||||
url_template: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
|
|
||||||
minZoom: 0,
|
|
||||||
maxZoom: 18,
|
|
||||||
id: 1,
|
|
||||||
selected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attribution: 'HOT and friends',
|
|
||||||
name: 'HOT OSM-fr server',
|
|
||||||
url_template: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
|
|
||||||
rank: 99,
|
|
||||||
minZoom: 0,
|
|
||||||
maxZoom: 20,
|
|
||||||
id: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tilelayer: {
|
|
||||||
attribution: 'HOT and friends',
|
|
||||||
name: 'HOT OSM-fr server',
|
|
||||||
url_template: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
|
|
||||||
rank: 99,
|
|
||||||
minZoom: 0,
|
|
||||||
maxZoom: 20,
|
|
||||||
id: 2,
|
|
||||||
},
|
|
||||||
licences: {
|
|
||||||
'No licence set': {
|
|
||||||
url: '',
|
|
||||||
name: 'No licence set',
|
|
||||||
},
|
|
||||||
'Licence ouverte/Open Licence': {
|
|
||||||
url: 'http://www.data.gouv.fr/Licence-Ouverte-Open-Licence',
|
|
||||||
name: 'Licence ouverte/Open Licence',
|
|
||||||
},
|
|
||||||
'WTFPL': {
|
|
||||||
url: 'http://www.wtfpl.net/',
|
|
||||||
name: 'WTFPL',
|
|
||||||
},
|
|
||||||
'ODbl': {
|
|
||||||
url: 'http://opendatacommons.org/licenses/odbl/',
|
|
||||||
name: 'ODbl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
name: 'name of the map',
|
|
||||||
description: 'The description of the map',
|
|
||||||
locale: 'en',
|
|
||||||
editMode: 'advanced',
|
|
||||||
moreControl: true,
|
|
||||||
scaleControl: true,
|
|
||||||
miniMap: false,
|
|
||||||
datalayersControl: true,
|
|
||||||
displayCaptionOnLoad: false,
|
|
||||||
displayPopupFooter: false,
|
|
||||||
displayDataBrowserOnLoad: false,
|
|
||||||
permissions: {
|
|
||||||
share_status: 1,
|
|
||||||
owner: {
|
|
||||||
id: 1,
|
|
||||||
name: 'ybon',
|
|
||||||
url: '/en/user/ybon/',
|
|
||||||
},
|
|
||||||
editors: [],
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
id: 1,
|
|
||||||
name: 'foofoo',
|
|
||||||
url: '/en/me',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
options = options || {}
|
|
||||||
options.properties = L.extend({}, default_options.properties, options)
|
|
||||||
options.geometry = {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [5.0592041015625, 52.05924589011585],
|
|
||||||
}
|
|
||||||
return new U.Map('map', options)
|
|
||||||
}
|
|
||||||
|
|
||||||
var RESPONSES = {
|
|
||||||
datalayer62_GET: {
|
|
||||||
crs: null,
|
|
||||||
type: 'FeatureCollection',
|
|
||||||
_umap_options: defaultDatalayerData(),
|
|
||||||
features: [
|
|
||||||
{
|
|
||||||
geometry: {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [-0.274658203125, 52.57634993749885],
|
|
||||||
},
|
|
||||||
type: 'Feature',
|
|
||||||
id: 1807,
|
|
||||||
properties: { _umap_options: { color: 'OliveDrab' }, name: 'test' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
geometry: {
|
|
||||||
type: 'LineString',
|
|
||||||
coordinates: [
|
|
||||||
[-0.5712890625, 54.47642158429295],
|
|
||||||
[0.439453125, 54.610254981579146],
|
|
||||||
[1.724853515625, 53.44880683542759],
|
|
||||||
[4.163818359375, 53.98839506479995],
|
|
||||||
[5.306396484375, 53.533778184257805],
|
|
||||||
[6.591796875, 53.70971358510174],
|
|
||||||
[7.042236328124999, 53.35055131839989],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
type: 'Feature',
|
|
||||||
id: 20,
|
|
||||||
properties: { _umap_options: { fill: false, opacity: 0.6 }, name: 'test' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[11.25, 53.585983654559804],
|
|
||||||
[10.1513671875, 52.9751081817353],
|
|
||||||
[12.689208984375, 52.16719363541221],
|
|
||||||
[14.084472656249998, 53.199451902831555],
|
|
||||||
[12.63427734375, 53.61857936489517],
|
|
||||||
[11.25, 53.585983654559804],
|
|
||||||
[11.25, 53.585983654559804],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
type: 'Feature',
|
|
||||||
id: 76,
|
|
||||||
properties: { name: 'name poly' },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// This one is non browsable
|
|
||||||
datalayer63_GET: {
|
|
||||||
crs: null,
|
|
||||||
type: 'FeatureCollection',
|
|
||||||
_umap_options: defaultDatalayerData({ id: 63, browsable: false }),
|
|
||||||
features: [
|
|
||||||
{
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[5.545478, 45.068383],
|
|
||||||
[5.545907, 45.067277],
|
|
||||||
[5.548439, 45.067565],
|
|
||||||
[5.552516, 45.06752],
|
|
||||||
[5.553288, 45.068217],
|
|
||||||
[5.549405, 45.069247],
|
|
||||||
[5.548224, 45.071005],
|
|
||||||
[5.545907, 45.071096],
|
|
||||||
[5.545478, 45.068383],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
type: 'Feature',
|
|
||||||
id: 76,
|
|
||||||
properties: { name: 'non browsable 1' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
_umap_options: {
|
|
||||||
color: 'SteelBlue',
|
|
||||||
},
|
|
||||||
name: 'non browsable 2',
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[5.550542, 45.071717],
|
|
||||||
[5.548182, 45.071051],
|
|
||||||
[5.549426, 45.069232],
|
|
||||||
[5.553331, 45.068171],
|
|
||||||
[5.554812, 45.070869],
|
|
||||||
[5.553396, 45.072384],
|
|
||||||
[5.550542, 45.071717],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// This one is not shown at load
|
|
||||||
datalayer64_GET: {
|
|
||||||
crs: null,
|
|
||||||
type: 'FeatureCollection',
|
|
||||||
_umap_options: defaultDatalayerData({
|
|
||||||
name: 'hidden',
|
|
||||||
id: 64,
|
|
||||||
displayOnLoad: false,
|
|
||||||
}),
|
|
||||||
features: [
|
|
||||||
{
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[5.545478, 45.068383],
|
|
||||||
[5.545907, 45.067277],
|
|
||||||
[5.548439, 45.067565],
|
|
||||||
[5.552516, 45.06752],
|
|
||||||
[5.553288, 45.068217],
|
|
||||||
[5.549405, 45.069247],
|
|
||||||
[5.548224, 45.071005],
|
|
||||||
[5.545907, 45.071096],
|
|
||||||
[5.545478, 45.068383],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
type: 'Feature',
|
|
||||||
id: 76,
|
|
||||||
properties: { name: 'not shown at load 1' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
_umap_options: {
|
|
||||||
color: 'AliceBlue',
|
|
||||||
},
|
|
||||||
name: 'not shown at load 2',
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[5.550542, 45.071717],
|
|
||||||
[5.548182, 45.071051],
|
|
||||||
[5.549426, 45.069232],
|
|
||||||
[5.553331, 45.068171],
|
|
||||||
[5.554812, 45.070869],
|
|
||||||
[5.553396, 45.072384],
|
|
||||||
[5.550542, 45.071717],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var kml_example =
|
|
||||||
'<?xml version="1.0" encoding="UTF-8"?>' +
|
|
||||||
'<kml xmlns="http://www.opengis.net/kml/2.2">' +
|
|
||||||
'<Placemark>' +
|
|
||||||
'<name>Simple point</name>' +
|
|
||||||
'<description>Here is a simple description.</description>' +
|
|
||||||
'<Point>' +
|
|
||||||
'<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>' +
|
|
||||||
'</Point>' +
|
|
||||||
'</Placemark>' +
|
|
||||||
'<Placemark>' +
|
|
||||||
'<name>Simple path</name>' +
|
|
||||||
'<description>Simple description</description>' +
|
|
||||||
'<LineString>' +
|
|
||||||
'<coordinates>-112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357</coordinates>' +
|
|
||||||
'</LineString>' +
|
|
||||||
'</Placemark>' +
|
|
||||||
'<Placemark>' +
|
|
||||||
'<name>Simple polygon</name>' +
|
|
||||||
'<description>A description.</description>' +
|
|
||||||
'<Polygon>' +
|
|
||||||
'<outerBoundaryIs>' +
|
|
||||||
'<LinearRing>' +
|
|
||||||
'<coordinates>' +
|
|
||||||
' -77.05788457660967,38.87253259892824,100 ' +
|
|
||||||
' -77.05465973756702,38.87291016281703,100 ' +
|
|
||||||
' -77.05315536854791,38.87053267794386,100 ' +
|
|
||||||
' -77.05788457660967,38.87253259892824,100 ' +
|
|
||||||
'</coordinates>' +
|
|
||||||
'</LinearRing>' +
|
|
||||||
'</outerBoundaryIs>' +
|
|
||||||
'</Polygon>' +
|
|
||||||
'</Placemark>' +
|
|
||||||
'</kml>'
|
|
||||||
|
|
||||||
var gpx_example =
|
|
||||||
'<gpx' +
|
|
||||||
' version="1.1"' +
|
|
||||||
' creator="GPSBabel - http://www.gpsbabel.org"' +
|
|
||||||
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
|
|
||||||
' xmlns="http://www.topografix.com/GPX/1/1"' +
|
|
||||||
' xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">' +
|
|
||||||
' <wpt lat="45.44283" lon="-121.72904"><ele>1374</ele><name>Simple Point</name><desc>Simple description</desc></wpt>' +
|
|
||||||
' <trk>' +
|
|
||||||
' <name>Simple path</name>' +
|
|
||||||
' <desc>Simple description</desc>' +
|
|
||||||
' <trkseg>' +
|
|
||||||
' <trkpt lat="45.4431641" lon="-121.7295456"></trkpt>' +
|
|
||||||
' <trkpt lat="45.4428615" lon="-121.7290800"></trkpt>' +
|
|
||||||
' <trkpt lat="45.4425697" lon="-121.7279085"></trkpt>' +
|
|
||||||
' </trkseg>' +
|
|
||||||
' </trk>' +
|
|
||||||
'</gpx>'
|
|
||||||
|
|
||||||
var csv_example =
|
|
||||||
'Foo,Latitude,Longitude,title,description\n' +
|
|
||||||
'bar,41.34,122.86,a point somewhere,the description of this point'
|
|
||||||
|
|
||||||
// Make Sinon log readable
|
|
||||||
sinon.format = function (what) {
|
|
||||||
if (typeof what === 'object') {
|
|
||||||
return JSON.stringify(what, null, 4)
|
|
||||||
} else if (typeof what === 'undefined') {
|
|
||||||
return ''
|
|
||||||
} else {
|
|
||||||
return what.toString()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Umap front Tests</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<script type="module" src="../js/modules/leaflet-configure.js" defer></script>
|
|
||||||
<script type="module" src="../js/modules/global.js" defer></script>
|
|
||||||
|
|
||||||
<script src="../vendors/editable/Path.Drag.js" defer></script>
|
|
||||||
<script src="../vendors/editable/Leaflet.Editable.js" defer></script>
|
|
||||||
<script src="../vendors/hash/leaflet-hash.js" defer></script>
|
|
||||||
<script src="../vendors/editinosm/Leaflet.EditInOSM.js" defer></script>
|
|
||||||
<script src="../vendors/minimap/Control.MiniMap.min.js" defer></script>
|
|
||||||
<script src="../vendors/csv2geojson/csv2geojson.js" defer></script>
|
|
||||||
<script src="../vendors/togeojson/togeojson.umd.js" defer></script>
|
|
||||||
<script src="../vendors/osmtogeojson/osmtogeojson.js" defer></script>
|
|
||||||
<script src="../vendors/loading/Control.Loading.js" defer></script>
|
|
||||||
<script src="../vendors/markercluster/leaflet.markercluster.js" defer></script>
|
|
||||||
<script src="../vendors/contextmenu/leaflet.contextmenu.min.js" defer></script>
|
|
||||||
<script src="../vendors/photon/leaflet.photon.js" defer></script>
|
|
||||||
<script src="../vendors/georsstogeojson/GeoRSSToGeoJSON.js" defer></script>
|
|
||||||
<script src="../vendors/heat/leaflet-heat.js" defer></script>
|
|
||||||
<script src="../vendors/fullscreen/Leaflet.fullscreen.min.js" defer></script>
|
|
||||||
<script src="../vendors/toolbar/leaflet.toolbar.js" defer></script>
|
|
||||||
<script src="../vendors/formbuilder/Leaflet.FormBuilder.js" defer></script>
|
|
||||||
<script src="../vendors/measurable/Leaflet.Measurable.js" defer></script>
|
|
||||||
<script src="../vendors/togpx/togpx.js" defer></script>
|
|
||||||
<script src="../vendors/iconlayers/iconLayers.js" defer></script>
|
|
||||||
<script src="../vendors/tokml/tokml.js" defer></script>
|
|
||||||
<script src="../vendors/locatecontrol/L.Control.Locate.min.js" defer></script>
|
|
||||||
<script src="../vendors/colorbrewer/colorbrewer.js" defer></script>
|
|
||||||
<script src="../vendors/simple-statistics/simple-statistics.min.js" defer></script>
|
|
||||||
|
|
||||||
<script src="../js/umap.core.js" defer></script>
|
|
||||||
<script src="../js/umap.autocomplete.js" defer></script>
|
|
||||||
<script src="../js/umap.popup.js" defer></script>
|
|
||||||
<script src="../js/umap.forms.js" defer></script>
|
|
||||||
<script src="../js/umap.icon.js" defer></script>
|
|
||||||
<script src="../js/umap.features.js" defer></script>
|
|
||||||
<script src="../js/umap.permissions.js" defer></script>
|
|
||||||
<script src="../js/umap.datalayer.permissions.js" defer></script>
|
|
||||||
<script src="../js/umap.layer.js" defer></script>
|
|
||||||
<script src="../js/umap.controls.js" defer></script>
|
|
||||||
<script src="../js/umap.slideshow.js" defer></script>
|
|
||||||
<script src="../js/umap.tableeditor.js" defer></script>
|
|
||||||
<script src="../js/umap.importer.js" defer></script>
|
|
||||||
<script src="../js/umap.share.js" defer></script>
|
|
||||||
<script src="../js/umap.js" defer></script>
|
|
||||||
<script src="../js/umap.ui.js" defer></script>
|
|
||||||
<script src="../js/components/fragment.js" defer></script>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="../vendors/leaflet/leaflet.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/markercluster/MarkerCluster.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/markercluster/MarkerCluster.Default.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/editinosm/Leaflet.EditInOSM.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/minimap/Control.MiniMap.min.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/contextmenu/leaflet.contextmenu.min.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/toolbar/leaflet.toolbar.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/measurable/Leaflet.Measurable.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/fullscreen/leaflet.fullscreen.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/locatecontrol/L.Control.Locate.min.css" />
|
|
||||||
<link rel="stylesheet" href="../vendors/iconlayers/iconLayers.css" />
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="../../umap/vars.css" />
|
|
||||||
<link rel="stylesheet" href="../../umap/font.css" />
|
|
||||||
<link rel="stylesheet" href="../../umap/base.css" />
|
|
||||||
<link rel="stylesheet" href="../../umap/content.css" />
|
|
||||||
<link rel="stylesheet" href="../../umap/nav.css" />
|
|
||||||
<link rel="stylesheet" href="../../umap/map.css" />
|
|
||||||
<link rel="stylesheet" href="../../umap/theme.css" />
|
|
||||||
|
|
||||||
<script src="../../../../node_modules/sinon/pkg/sinon.js"></script>
|
|
||||||
<script src="../../../../node_modules/mocha/mocha.js"></script>
|
|
||||||
<script src="../../../../node_modules/chai/chai.js"></script>
|
|
||||||
<script src="../../../../node_modules/happen/happen.js"></script>
|
|
||||||
<link rel="stylesheet" href="../../../../node_modules/mocha/mocha.css" />
|
|
||||||
<script type="module">
|
|
||||||
import fetchMock from '../../../../node_modules/fetch-mock/esm/client.js';
|
|
||||||
window.fetchMock = fetchMock
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
mocha.setup({
|
|
||||||
ui: 'bdd',
|
|
||||||
bail: window.location.search.indexOf('failfast') !== -1,
|
|
||||||
ignoreLeaks: true,
|
|
||||||
})
|
|
||||||
chai.config.includeStack = true
|
|
||||||
</script>
|
|
||||||
<script src="./_pre.js" defer></script>
|
|
||||||
<script src="./Map.js" defer></script>
|
|
||||||
<script src="./Feature.js" defer></script>
|
|
||||||
<script src="./Marker.js" defer></script>
|
|
||||||
<script src="./Polyline.js" defer></script>
|
|
||||||
<script src="./Polygon.js" defer></script>
|
|
||||||
<script src="./Util.js" defer></script>
|
|
||||||
<script type="module" src="./URLs.js" defer></script>
|
|
||||||
<style type="text/css">
|
|
||||||
#mocha {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 10000;
|
|
||||||
background-color: white;
|
|
||||||
box-shadow: 0px 0px 8px 0px black;
|
|
||||||
overflow-y: auto;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mocha-stats {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="mocha"></div>
|
|
||||||
<div id="map"></div>
|
|
||||||
<script>
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
|
||||||
var runner = (window.mochaPhantomJS || window.mocha).run(function (failures) {
|
|
||||||
if (window.location.search.indexOf('debug') === -1)
|
|
||||||
qs('#mocha').style.display = 'block'
|
|
||||||
console.log(failures)
|
|
||||||
})
|
|
||||||
if (window.location.search.indexOf('debug') !== -1) {
|
|
||||||
runner.on('fail', function (test, err) {
|
|
||||||
console.log(test.title, test.err)
|
|
||||||
console.log(test.err.expected, test.err.actual)
|
|
||||||
console.log(test.err.stack)
|
|
||||||
})
|
|
||||||
sinon.log = function (message) {
|
|
||||||
console.log(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -49,63 +49,6 @@ describe('#dispatch', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Updaters', () => {
|
|
||||||
describe('BaseUpdater', () => {
|
|
||||||
let updater
|
|
||||||
let map
|
|
||||||
let obj
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
map = {}
|
|
||||||
updater = new MapUpdater(map)
|
|
||||||
obj = {}
|
|
||||||
})
|
|
||||||
it('should be able to set object properties', () => {
|
|
||||||
let obj = {}
|
|
||||||
updater.updateObjectValue(obj, 'foo', 'foo')
|
|
||||||
expect(obj).deep.equal({ foo: 'foo' })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be able to set object properties recursively on existing objects', () => {
|
|
||||||
let obj = { foo: {} }
|
|
||||||
updater.updateObjectValue(obj, 'foo.bar', 'foo')
|
|
||||||
expect(obj).deep.equal({ foo: { bar: 'foo' } })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be able to set object properties recursively on deep objects', () => {
|
|
||||||
let obj = { foo: { bar: { baz: {} } } }
|
|
||||||
updater.updateObjectValue(obj, 'foo.bar.baz.test', 'value')
|
|
||||||
expect(obj).deep.equal({ foo: { bar: { baz: { test: 'value' } } } })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be able to replace object properties recursively on deep objects', () => {
|
|
||||||
let obj = { foo: { bar: { baz: { test: 'test' } } } }
|
|
||||||
updater.updateObjectValue(obj, 'foo.bar.baz.test', 'value')
|
|
||||||
expect(obj).deep.equal({ foo: { bar: { baz: { test: 'value' } } } })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not set object properties recursively on non-existing objects', () => {
|
|
||||||
let obj = { foo: {} }
|
|
||||||
updater.updateObjectValue(obj, 'bar.bar', 'value')
|
|
||||||
|
|
||||||
expect(obj).deep.equal({ foo: {} })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should delete keys for undefined values', () => {
|
|
||||||
let obj = { foo: 'foo' }
|
|
||||||
updater.updateObjectValue(obj, 'foo', undefined)
|
|
||||||
|
|
||||||
expect(obj).deep.equal({})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should delete keys for undefined values, recursively', () => {
|
|
||||||
let obj = { foo: { bar: 'bar' } }
|
|
||||||
updater.updateObjectValue(obj, 'foo.bar', undefined)
|
|
||||||
|
|
||||||
expect(obj).deep.equal({ foo: {} })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Operations', () => {
|
describe('Operations', () => {
|
||||||
describe('haveSameContext', () => {
|
describe('haveSameContext', () => {
|
||||||
|
|
|
@ -862,4 +862,51 @@ describe('Utils', () => {
|
||||||
assert.equal(Utils.isObject(''), false)
|
assert.equal(Utils.isObject(''), false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('setObjectValue', () => {
|
||||||
|
it('should be able to set object properties', () => {
|
||||||
|
let obj = {}
|
||||||
|
Utils.setObjectValue(obj, 'foo', 'foo')
|
||||||
|
expect(obj).deep.equal({ foo: 'foo' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to set object properties recursively on existing objects', () => {
|
||||||
|
let obj = { foo: {} }
|
||||||
|
Utils.setObjectValue(obj, 'foo.bar', 'foo')
|
||||||
|
expect(obj).deep.equal({ foo: { bar: 'foo' } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to set object properties recursively on deep objects', () => {
|
||||||
|
let obj = { foo: { bar: { baz: {} } } }
|
||||||
|
Utils.setObjectValue(obj, 'foo.bar.baz.test', 'value')
|
||||||
|
expect(obj).deep.equal({ foo: { bar: { baz: { test: 'value' } } } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to replace object properties recursively on deep objects', () => {
|
||||||
|
let obj = { foo: { bar: { baz: { test: 'test' } } } }
|
||||||
|
Utils.setObjectValue(obj, 'foo.bar.baz.test', 'value')
|
||||||
|
expect(obj).deep.equal({ foo: { bar: { baz: { test: 'value' } } } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not set object properties recursively on non-existing objects', () => {
|
||||||
|
let obj = { foo: {} }
|
||||||
|
Utils.setObjectValue(obj, 'bar.bar', 'value')
|
||||||
|
|
||||||
|
expect(obj).deep.equal({ foo: {} })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should delete keys for undefined values', () => {
|
||||||
|
let obj = { foo: 'foo' }
|
||||||
|
Utils.setObjectValue(obj, 'foo', undefined)
|
||||||
|
|
||||||
|
expect(obj).deep.equal({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should delete keys for undefined values, recursively', () => {
|
||||||
|
let obj = { foo: { bar: 'bar' } }
|
||||||
|
Utils.setObjectValue(obj, 'foo.bar', undefined)
|
||||||
|
|
||||||
|
expect(obj).deep.equal({ foo: {} })
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -261,6 +261,9 @@ def test_can_create_new_rule(live_server, page, openmap):
|
||||||
page.get_by_title("AliceBlue").first.click()
|
page.get_by_title("AliceBlue").first.click()
|
||||||
colors = getColors(markers)
|
colors = getColors(markers)
|
||||||
assert colors.count("rgb(240, 248, 255)") == 3
|
assert colors.count("rgb(240, 248, 255)") == 3
|
||||||
|
page.get_by_role("button", name="Undo").click()
|
||||||
|
colors = getColors(markers)
|
||||||
|
assert colors.count("rgb(240, 248, 255)") == 0
|
||||||
|
|
||||||
|
|
||||||
def test_can_deactive_rule_from_list(live_server, page, openmap):
|
def test_can_deactive_rule_from_list(live_server, page, openmap):
|
||||||
|
|
|
@ -689,3 +689,43 @@ def test_should_sync_datalayer_clear(
|
||||||
peerA.get_by_role("button", name="Undo").click()
|
peerA.get_by_role("button", name="Undo").click()
|
||||||
expect(peerA.locator(".leaflet-marker-icon")).to_have_count(1)
|
expect(peerA.locator(".leaflet-marker-icon")).to_have_count(1)
|
||||||
expect(peerB.locator(".leaflet-marker-icon")).to_have_count(1)
|
expect(peerB.locator(".leaflet-marker-icon")).to_have_count(1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xdist_group(name="websockets")
|
||||||
|
def test_should_save_remote_dirty_datalayers(new_page, asgi_live_server, tilelayer):
|
||||||
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
||||||
|
map.settings["properties"]["syncEnabled"] = True
|
||||||
|
map.save()
|
||||||
|
|
||||||
|
assert not DataLayer.objects.count()
|
||||||
|
|
||||||
|
# Create two tabs
|
||||||
|
peerA = new_page("Page A")
|
||||||
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
||||||
|
peerB = new_page("Page B")
|
||||||
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
||||||
|
|
||||||
|
# Create a new layer from peerA
|
||||||
|
peerA.get_by_role("button", name="Manage layers").click()
|
||||||
|
peerA.get_by_role("button", name="Add a layer").click()
|
||||||
|
|
||||||
|
# Create a new layer from peerB
|
||||||
|
peerB.get_by_role("button", name="Manage layers").click()
|
||||||
|
peerB.get_by_role("button", name="Add a layer").click()
|
||||||
|
|
||||||
|
# Save from peerA to the server
|
||||||
|
counter = 0
|
||||||
|
|
||||||
|
def on_response(response):
|
||||||
|
nonlocal counter
|
||||||
|
if "/datalayer/create/" in response.url:
|
||||||
|
counter += 1
|
||||||
|
# Wait for the two datalayer saves
|
||||||
|
if counter == 2:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
with peerA.expect_response(on_response):
|
||||||
|
peerA.get_by_role("button", name="Save").click()
|
||||||
|
|
||||||
|
assert DataLayer.objects.count() == 2
|
||||||
|
|
|
@ -103,6 +103,7 @@ def test_get_version(map, datalayer):
|
||||||
],
|
],
|
||||||
"type": "Point",
|
"type": "Point",
|
||||||
},
|
},
|
||||||
|
"id": "ExNTQ",
|
||||||
"properties": {
|
"properties": {
|
||||||
"_umap_options": {
|
"_umap_options": {
|
||||||
"color": "DarkCyan",
|
"color": "DarkCyan",
|
||||||
|
|
|
@ -694,6 +694,7 @@ def test_download(client, map, datalayer):
|
||||||
"coordinates": [14.68896484375, 48.55297816440071],
|
"coordinates": [14.68896484375, 48.55297816440071],
|
||||||
"type": "Point",
|
"type": "Point",
|
||||||
},
|
},
|
||||||
|
"id": "ExNTQ",
|
||||||
"properties": {
|
"properties": {
|
||||||
"_umap_options": {"color": "DarkCyan", "iconClass": "Ball"},
|
"_umap_options": {"color": "DarkCyan", "iconClass": "Ball"},
|
||||||
"description": "Da place anonymous again 755",
|
"description": "Da place anonymous again 755",
|
||||||
|
|
Loading…
Reference in a new issue