[chore] move umap utils to a module
Allow the tests to be run from inside a cli, without requiring a browser.
This commit is contained in:
parent
a28db94f72
commit
c4e527bf8e
20 changed files with 419 additions and 383 deletions
|
@ -40,8 +40,9 @@
|
||||||
"@tmcw/togeojson": "^5.8.0",
|
"@tmcw/togeojson": "^5.8.0",
|
||||||
"colorbrewer": "^1.5.6",
|
"colorbrewer": "^1.5.6",
|
||||||
"csv2geojson": "5.1.1",
|
"csv2geojson": "5.1.1",
|
||||||
"dompurify": "^3.0.3",
|
"dompurify": "^3.0.11",
|
||||||
"georsstogeojson": "^0.1.0",
|
"georsstogeojson": "^0.1.0",
|
||||||
|
"jsdom": "^24.0.0",
|
||||||
"leaflet": "1.9.4",
|
"leaflet": "1.9.4",
|
||||||
"leaflet-contextmenu": "^1.4.0",
|
"leaflet-contextmenu": "^1.4.0",
|
||||||
"leaflet-editable": "^1.2.0",
|
"leaflet-editable": "^1.2.0",
|
||||||
|
|
|
@ -25,7 +25,7 @@ mkdir -p umap/static/umap/vendors/georsstogeojson/ && cp -r node_modules/georsst
|
||||||
mkdir -p umap/static/umap/vendors/togpx/ && cp -r node_modules/togpx/togpx.js umap/static/umap/vendors/togpx/
|
mkdir -p umap/static/umap/vendors/togpx/ && cp -r node_modules/togpx/togpx.js umap/static/umap/vendors/togpx/
|
||||||
mkdir -p umap/static/umap/vendors/tokml && cp -r node_modules/tokml/tokml.js umap/static/umap/vendors/tokml
|
mkdir -p umap/static/umap/vendors/tokml && cp -r node_modules/tokml/tokml.js umap/static/umap/vendors/tokml
|
||||||
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.* umap/static/umap/vendors/locatecontrol/
|
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.* umap/static/umap/vendors/locatecontrol/
|
||||||
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.min.* umap/static/umap/vendors/dompurify/
|
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/*.mjs umap/static/umap/vendors/dompurify/
|
||||||
mkdir -p umap/static/umap/vendors/colorbrewer/ && cp node_modules/colorbrewer/index.js umap/static/umap/vendors/colorbrewer/colorbrewer.js
|
mkdir -p umap/static/umap/vendors/colorbrewer/ && cp node_modules/colorbrewer/index.js umap/static/umap/vendors/colorbrewer/colorbrewer.js
|
||||||
mkdir -p umap/static/umap/vendors/simple-statistics/ && cp node_modules/simple-statistics/dist/simple-statistics.min.* umap/static/umap/vendors/simple-statistics/
|
mkdir -p umap/static/umap/vendors/simple-statistics/ && cp node_modules/simple-statistics/dist/simple-statistics.min.* umap/static/umap/vendors/simple-statistics/
|
||||||
mkdir -p umap/static/umap/vendors/iconlayers/ && cp node_modules/leaflet-iconlayers/dist/* umap/static/umap/vendors/iconlayers/
|
mkdir -p umap/static/umap/vendors/iconlayers/ && cp node_modules/leaflet-iconlayers/dist/* umap/static/umap/vendors/iconlayers/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import URLs from './urls.js'
|
import URLs from './urls.js'
|
||||||
import Browser from './browser.js'
|
import Browser from './browser.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import {SCHEMA} from './schema.js'
|
import { SCHEMA } from './schema.js'
|
||||||
import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
|
import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
|
||||||
|
|
||||||
// Import modules and export them to the global scope.
|
// Import modules and export them to the global scope.
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
// Vendorized from leaflet.utils
|
import { template } from "./utils.js"
|
||||||
// https://github.com/Leaflet/Leaflet/blob/108c6717b70f57c63645498f9bd66b6677758786/src/core/Util.js#L132-L151
|
|
||||||
var templateRe = /\{ *([\w_ -]+) *\}/g
|
|
||||||
|
|
||||||
function template(str, data) {
|
|
||||||
return str.replace(templateRe, function (str, key) {
|
|
||||||
var value = data[key]
|
|
||||||
|
|
||||||
if (value === undefined) {
|
|
||||||
throw new Error('No value provided for variable ' + str)
|
|
||||||
} else if (typeof value === 'function') {
|
|
||||||
value = value(data)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class URLs {
|
export default class URLs {
|
||||||
constructor(serverUrls) {
|
constructor(serverUrls) {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { default as DOMPurifyInitializer } from '../../vendors/dompurify/purify.es.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a pseudo-unique identifier (5 chars long, mixed-case alphanumeric)
|
* Generate a pseudo-unique identifier (5 chars long, mixed-case alphanumeric)
|
||||||
*
|
*
|
||||||
|
@ -22,3 +24,284 @@ export function checkId(string) {
|
||||||
if (typeof string !== 'string') return false
|
if (typeof string !== 'string') return false
|
||||||
return /^[A-Za-z0-9]{5}$/.test(string)
|
return /^[A-Za-z0-9]{5}$/.test(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import DOM purify, and initialize it.
|
||||||
|
*
|
||||||
|
* If the context is a node server, uses jsdom to provide
|
||||||
|
* DOM APIs
|
||||||
|
*/
|
||||||
|
export default function getPurify() {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return DOMPurifyInitializer(new global.JSDOM('').window)
|
||||||
|
} else {
|
||||||
|
return DOMPurifyInitializer(window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function escapeHTML(s) {
|
||||||
|
s = s ? s.toString() : ''
|
||||||
|
s = getPurify().sanitize(s, {
|
||||||
|
USE_PROFILES: { html: true },
|
||||||
|
ADD_TAGS: ['iframe'],
|
||||||
|
ALLOWED_TAGS: [
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'hr',
|
||||||
|
'strong',
|
||||||
|
'em',
|
||||||
|
'ul',
|
||||||
|
'li',
|
||||||
|
'a',
|
||||||
|
'div',
|
||||||
|
'iframe',
|
||||||
|
'img',
|
||||||
|
'br',
|
||||||
|
],
|
||||||
|
ADD_ATTR: ['target', 'allow', 'allowfullscreen', 'frameborder', 'scrolling'],
|
||||||
|
ALLOWED_ATTR: ['href', 'src', 'width', 'height'],
|
||||||
|
// Added: `geo:` URL scheme as defined in RFC5870:
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc5870.html
|
||||||
|
// The base RegExp comes from:
|
||||||
|
// https://github.com/cure53/DOMPurify/blob/main/src/regexp.js#L10
|
||||||
|
ALLOWED_URI_REGEXP:
|
||||||
|
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|geo):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
|
||||||
|
})
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toHTML(r, options) {
|
||||||
|
if (!r) return ''
|
||||||
|
const target = (options && options.target) || 'blank'
|
||||||
|
let ii
|
||||||
|
|
||||||
|
// detect newline format
|
||||||
|
const newline = r.indexOf('\r\n') != -1 ? '\r\n' : r.indexOf('\n') != -1 ? '\n' : ''
|
||||||
|
|
||||||
|
// headings and hr
|
||||||
|
r = r.replace(/^### (.*)/gm, '<h5>$1</h5>')
|
||||||
|
r = r.replace(/^## (.*)/gm, '<h4>$1</h4>')
|
||||||
|
r = r.replace(/^# (.*)/gm, '<h3>$1</h3>')
|
||||||
|
r = r.replace(/^---/gm, '<hr>')
|
||||||
|
|
||||||
|
// bold, italics
|
||||||
|
r = r.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||||
|
r = r.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
||||||
|
|
||||||
|
// unordered lists
|
||||||
|
r = r.replace(/^\*\* (.*)/gm, '<ul><ul><li>$1</li></ul></ul>')
|
||||||
|
r = r.replace(/^\* (.*)/gm, '<ul><li>$1</li></ul>')
|
||||||
|
for (ii = 0; ii < 3; ii++)
|
||||||
|
r = r.replace(new RegExp(`</ul>${newline}<ul>`, 'g'), newline)
|
||||||
|
|
||||||
|
// links
|
||||||
|
r = r.replace(/(\[\[http)/g, '[[h_t_t_p') // Escape for avoiding clash between [[http://xxx]] and http://xxx
|
||||||
|
r = r.replace(/({{http)/g, '{{h_t_t_p')
|
||||||
|
r = r.replace(/(=http)/g, '=h_t_t_p') // http://xxx as query string, see https://github.com/umap-project/umap/issues/607
|
||||||
|
r = r.replace(/(https?:[^ \<)\n]*)/g, `<a target="_${target}" href="$1">$1</a>`)
|
||||||
|
r = r.replace(
|
||||||
|
/\[\[(h_t_t_ps?:[^\]|]*?)\]\]/g,
|
||||||
|
`<a target="_${target}" href="$1">$1</a>`
|
||||||
|
)
|
||||||
|
r = r.replace(
|
||||||
|
/\[\[(h_t_t_ps?:[^|]*?)\|(.*?)\]\]/g,
|
||||||
|
`<a target="_${target}" href="$1">$2</a>`
|
||||||
|
)
|
||||||
|
r = r.replace(/\[\[([^\]|]*?)\]\]/g, `<a target="_${target}" href="$1">$1</a>`)
|
||||||
|
r = r.replace(/\[\[([^|]*?)\|(.*?)\]\]/g, `<a target="_${target}" href="$1">$2</a>`)
|
||||||
|
|
||||||
|
// iframe
|
||||||
|
r = r.replace(
|
||||||
|
/{{{(h_t_t_ps?[^ |{]*)}}}/g,
|
||||||
|
'<div><iframe frameborder="0" src="$1" width="100%" height="300px"></iframe></div>'
|
||||||
|
)
|
||||||
|
r = r.replace(
|
||||||
|
/{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?}}}/g,
|
||||||
|
'<div><iframe frameborder="0" src="$1" width="100%" height="$2px"></iframe></div>'
|
||||||
|
)
|
||||||
|
r = r.replace(
|
||||||
|
/{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?\*(\d*)(px)?}}}/g,
|
||||||
|
'<div><iframe frameborder="0" src="$1" width="$4px" height="$2px"></iframe></div>'
|
||||||
|
)
|
||||||
|
|
||||||
|
// images
|
||||||
|
r = r.replace(/{{([^\]|]*?)}}/g, '<img src="$1">')
|
||||||
|
r = r.replace(
|
||||||
|
/{{([^|]*?)\|(\d*?)(px)?}}/g,
|
||||||
|
'<img src="$1" style="width:$2px;min-width:$2px;">'
|
||||||
|
)
|
||||||
|
|
||||||
|
//Unescape http
|
||||||
|
r = r.replace(/(h_t_t_p)/g, 'http')
|
||||||
|
|
||||||
|
// Preserver line breaks
|
||||||
|
if (newline) r = r.replace(new RegExp(`${newline}(?=[^]+)`, 'g'), `<br>${newline}`)
|
||||||
|
|
||||||
|
r = escapeHTML(r)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isObject(what) {
|
||||||
|
return typeof what === 'object' && what !== null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CopyJSON(geojson) {
|
||||||
|
return JSON.parse(JSON.stringify(geojson))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detectFileType(f) {
|
||||||
|
const filename = f.name ? escape(f.name.toLowerCase()) : ''
|
||||||
|
function ext(_) {
|
||||||
|
return filename.indexOf(_) !== -1
|
||||||
|
}
|
||||||
|
if (f.type === 'application/vnd.google-earth.kml+xml' || ext('.kml')) {
|
||||||
|
return 'kml'
|
||||||
|
}
|
||||||
|
if (ext('.gpx')) return 'gpx'
|
||||||
|
if (ext('.geojson') || ext('.json')) return 'geojson'
|
||||||
|
if (f.type === 'text/csv' || ext('.csv') || ext('.tsv') || ext('.dsv')) {
|
||||||
|
return 'csv'
|
||||||
|
}
|
||||||
|
if (ext('.xml') || ext('.osm')) return 'osm'
|
||||||
|
if (ext('.umap')) return 'umap'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usableOption(options, option) {
|
||||||
|
return options[option] !== undefined && options[option] !== ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function greedyTemplate(str, data, ignore) {
|
||||||
|
function getValue(data, path) {
|
||||||
|
let value = data
|
||||||
|
for (let i = 0; i < path.length; i++) {
|
||||||
|
value = value[path[i]]
|
||||||
|
if (value === undefined) break
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof str !== 'string') return ''
|
||||||
|
|
||||||
|
return str.replace(
|
||||||
|
/\{ *([^\{\}/\-]+)(?:\|("[^"]*"))? *\}/g,
|
||||||
|
(str, key, staticFallback) => {
|
||||||
|
const vars = key.split('|')
|
||||||
|
let value
|
||||||
|
let path
|
||||||
|
if (staticFallback !== undefined) {
|
||||||
|
vars.push(staticFallback)
|
||||||
|
}
|
||||||
|
for (let i = 0; i < vars.length; i++) {
|
||||||
|
path = vars[i]
|
||||||
|
if (path.startsWith('"') && path.endsWith('"'))
|
||||||
|
value = path.substring(1, path.length - 1) // static default value.
|
||||||
|
else value = getValue(data, path.split('.'))
|
||||||
|
if (value !== undefined) break
|
||||||
|
}
|
||||||
|
if (value === undefined) {
|
||||||
|
if (ignore) value = str
|
||||||
|
else value = ''
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function naturalSort(a, b, lang) {
|
||||||
|
return a
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.localeCompare(b.toString().toLowerCase(), lang || 'en', {
|
||||||
|
sensitivity: 'base',
|
||||||
|
numeric: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortFeatures(features, sortKey, lang) {
|
||||||
|
const sortKeys = (sortKey || 'name').split(',')
|
||||||
|
|
||||||
|
const sort = (a, b, i) => {
|
||||||
|
let sortKey = sortKeys[i],
|
||||||
|
reverse = 1
|
||||||
|
if (sortKey[0] === '-') {
|
||||||
|
reverse = -1
|
||||||
|
sortKey = sortKey.substring(1)
|
||||||
|
}
|
||||||
|
let score
|
||||||
|
const valA = a.properties[sortKey] || ''
|
||||||
|
const valB = b.properties[sortKey] || ''
|
||||||
|
if (!valA) score = -1
|
||||||
|
else if (!valB) score = 1
|
||||||
|
else score = naturalSort(valA, valB, lang)
|
||||||
|
if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1)
|
||||||
|
return score * reverse
|
||||||
|
}
|
||||||
|
|
||||||
|
features.sort((a, b) => {
|
||||||
|
if (!a.properties || !b.properties) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return sort(a, b, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
return features
|
||||||
|
}
|
||||||
|
|
||||||
|
export function flattenCoordinates(coords) {
|
||||||
|
while (coords[0] && typeof coords[0][0] !== 'number') coords = coords[0]
|
||||||
|
return coords
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildQueryString(params) {
|
||||||
|
const query_string = []
|
||||||
|
for (const key in params) {
|
||||||
|
query_string.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||||
|
}
|
||||||
|
return query_string.join('&')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBaseUrl() {
|
||||||
|
return `//${window.location.host}${window.location.pathname}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasVar(value) {
|
||||||
|
return typeof value === 'string' && value.indexOf('{') != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPath(value) {
|
||||||
|
return value && value.length && value.startsWith('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRemoteUrl(value) {
|
||||||
|
return value && value.length && value.startsWith('http')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDataImage(value) {
|
||||||
|
return value && value.length && value.startsWith('data:image')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalize(s) {
|
||||||
|
return (s || '')
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vendorized from leaflet.utils
|
||||||
|
// https://github.com/Leaflet/Leaflet/blob/108c6717b70f57c63645498f9bd66b6677758786/src/core/Util.js#L132-L151
|
||||||
|
var templateRe = /\{ *([\w_ -]+) *\}/g
|
||||||
|
|
||||||
|
export function template(str, data) {
|
||||||
|
return str.replace(templateRe, function (str, key) {
|
||||||
|
var value = data[key]
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
throw new Error('No value provided for variable ' + str)
|
||||||
|
} else if (typeof value === 'function') {
|
||||||
|
value = value(data)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ U.AutoComplete = L.Class.extend({
|
||||||
this.displaySelected(choice)
|
this.displaySelected(choice)
|
||||||
this.hide()
|
this.hide()
|
||||||
if (this.options.callback) {
|
if (this.options.callback) {
|
||||||
L.Util.bind(this.options.callback, this)(choice)
|
U.Utils.bind(this.options.callback, this)(choice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -476,7 +476,7 @@ U.PermanentCreditsControl = L.Control.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setCredits: function () {
|
setCredits: function () {
|
||||||
this.paragraphContainer.innerHTML = L.Util.toHTML(this.map.options.permanentCredit)
|
this.paragraphContainer.innerHTML = U.Utils.toHTML(this.map.options.permanentCredit)
|
||||||
},
|
},
|
||||||
|
|
||||||
setBackground: function () {
|
setBackground: function () {
|
||||||
|
@ -820,7 +820,7 @@ const ControlsMixin = {
|
||||||
this.permissions.addOwnerLink('h5', container)
|
this.permissions.addOwnerLink('h5', container)
|
||||||
if (this.options.description) {
|
if (this.options.description) {
|
||||||
const description = L.DomUtil.create('div', 'umap-map-description', container)
|
const description = L.DomUtil.create('div', 'umap-map-description', container)
|
||||||
description.innerHTML = L.Util.toHTML(this.options.description)
|
description.innerHTML = U.Utils.toHTML(this.options.description)
|
||||||
}
|
}
|
||||||
const datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container)
|
const datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container)
|
||||||
this.eachVisibleDataLayer((datalayer) => {
|
this.eachVisibleDataLayer((datalayer) => {
|
||||||
|
@ -832,7 +832,7 @@ const ControlsMixin = {
|
||||||
datalayer.onceLoaded(function () {
|
datalayer.onceLoaded(function () {
|
||||||
datalayer.renderLegend(legend)
|
datalayer.renderLegend(legend)
|
||||||
if (datalayer.options.description) {
|
if (datalayer.options.description) {
|
||||||
description.innerHTML = L.Util.toHTML(datalayer.options.description)
|
description.innerHTML = U.Utils.toHTML(datalayer.options.description)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
datalayer.renderToolbox(headline)
|
datalayer.renderToolbox(headline)
|
||||||
|
@ -846,7 +846,7 @@ const ControlsMixin = {
|
||||||
'p',
|
'p',
|
||||||
'',
|
'',
|
||||||
credits,
|
credits,
|
||||||
L.Util.toHTML(this.options.longCredit || this.options.shortCredit)
|
U.Utils.toHTML(this.options.longCredit || this.options.shortCredit)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (this.options.licence) {
|
if (this.options.licence) {
|
||||||
|
@ -1077,7 +1077,7 @@ U.TileLayerControl = L.Control.IconLayers.extend({
|
||||||
// when the tilelayer is actually added to the map (needs this._tileZoom
|
// when the tilelayer is actually added to the map (needs this._tileZoom
|
||||||
// to be defined)
|
// to be defined)
|
||||||
// Fixme when https://github.com/Leaflet/Leaflet/pull/9201 is released
|
// Fixme when https://github.com/Leaflet/Leaflet/pull/9201 is released
|
||||||
const icon = L.Util.template(
|
const icon = U.Utils.template(
|
||||||
layer.options.url_template,
|
layer.options.url_template,
|
||||||
this.map.demoTileInfos
|
this.map.demoTileInfos
|
||||||
)
|
)
|
||||||
|
@ -1150,7 +1150,7 @@ U.TileLayerChooser = L.Control.extend({
|
||||||
el = L.DomUtil.create('li', selectedClass, this._tilelayers_container),
|
el = L.DomUtil.create('li', selectedClass, this._tilelayers_container),
|
||||||
img = L.DomUtil.create('img', '', el),
|
img = L.DomUtil.create('img', '', el),
|
||||||
name = L.DomUtil.create('div', '', el)
|
name = L.DomUtil.create('div', '', el)
|
||||||
img.src = L.Util.template(tilelayer.options.url_template, this.map.demoTileInfos)
|
img.src = U.Utils.template(tilelayer.options.url_template, this.map.demoTileInfos)
|
||||||
img.loading = 'lazy'
|
img.loading = 'lazy'
|
||||||
name.textContent = tilelayer.options.name
|
name.textContent = tilelayer.options.name
|
||||||
L.DomEvent.on(
|
L.DomEvent.on(
|
||||||
|
@ -1187,7 +1187,7 @@ U.AttributionControl = L.Control.Attribution.extend({
|
||||||
const shortCredit = this._map.getOption('shortCredit'),
|
const shortCredit = this._map.getOption('shortCredit'),
|
||||||
captionMenus = this._map.getOption('captionMenus')
|
captionMenus = this._map.getOption('captionMenus')
|
||||||
if (shortCredit) {
|
if (shortCredit) {
|
||||||
L.DomUtil.add('span', '', container, ` — ${L.Util.toHTML(shortCredit)}`)
|
L.DomUtil.add('span', '', container, ` — ${U.Utils.toHTML(shortCredit)}`)
|
||||||
}
|
}
|
||||||
if (captionMenus) {
|
if (captionMenus) {
|
||||||
const link = L.DomUtil.add('a', '', container, ` — ${L._('About')}`)
|
const link = L.DomUtil.add('a', '', container, ` — ${L._('About')}`)
|
||||||
|
|
|
@ -1,277 +1,3 @@
|
||||||
/*
|
|
||||||
* Utils
|
|
||||||
*/
|
|
||||||
L.Util.queryString = (name, fallback) => {
|
|
||||||
const decode = (s) => decodeURIComponent(s.replace(/\+/g, ' '))
|
|
||||||
const qs = window.location.search.slice(1).split('&'),
|
|
||||||
qa = {}
|
|
||||||
for (const i in qs) {
|
|
||||||
const key = qs[i].split('=')
|
|
||||||
if (!key) continue
|
|
||||||
qa[decode(key[0])] = key[1] ? decode(key[1]) : 1
|
|
||||||
}
|
|
||||||
return qa[name] || fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.booleanFromQueryString = (name) => {
|
|
||||||
const value = L.Util.queryString(name)
|
|
||||||
return value === '1' || value === 'true'
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.setFromQueryString = (options, name) => {
|
|
||||||
const value = L.Util.queryString(name)
|
|
||||||
if (typeof value !== 'undefined') options[name] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.setBooleanFromQueryString = (options, name) => {
|
|
||||||
const value = L.Util.queryString(name)
|
|
||||||
if (typeof value !== 'undefined') options[name] = value == '1' || value == 'true'
|
|
||||||
}
|
|
||||||
L.Util.setNumberFromQueryString = (options, name) => {
|
|
||||||
const value = +L.Util.queryString(name)
|
|
||||||
if (!isNaN(value)) options[name] = value
|
|
||||||
}
|
|
||||||
L.Util.setNullableBooleanFromQueryString = (options, name) => {
|
|
||||||
let value = L.Util.queryString(name)
|
|
||||||
if (typeof value !== 'undefined') {
|
|
||||||
if (value === 'null') value = null
|
|
||||||
else if (value === '0' || value === 'false') value = false
|
|
||||||
else value = true
|
|
||||||
options[name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
L.Util.escapeHTML = (s) => {
|
|
||||||
s = s ? s.toString() : ''
|
|
||||||
s = DOMPurify.sanitize(s, {
|
|
||||||
USE_PROFILES: { html: true },
|
|
||||||
ADD_TAGS: ['iframe'],
|
|
||||||
ALLOWED_TAGS: [
|
|
||||||
'h3',
|
|
||||||
'h4',
|
|
||||||
'h5',
|
|
||||||
'hr',
|
|
||||||
'strong',
|
|
||||||
'em',
|
|
||||||
'ul',
|
|
||||||
'li',
|
|
||||||
'a',
|
|
||||||
'div',
|
|
||||||
'iframe',
|
|
||||||
'img',
|
|
||||||
'br',
|
|
||||||
],
|
|
||||||
ADD_ATTR: ['target', 'allow', 'allowfullscreen', 'frameborder', 'scrolling'],
|
|
||||||
ALLOWED_ATTR: ['href', 'src', 'width', 'height'],
|
|
||||||
// Added: `geo:` URL scheme as defined in RFC5870:
|
|
||||||
// https://www.rfc-editor.org/rfc/rfc5870.html
|
|
||||||
// The base RegExp comes from:
|
|
||||||
// https://github.com/cure53/DOMPurify/blob/main/src/regexp.js#L10
|
|
||||||
ALLOWED_URI_REGEXP:
|
|
||||||
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|geo):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
|
|
||||||
})
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
L.Util.toHTML = (r, options) => {
|
|
||||||
if (!r) return ''
|
|
||||||
const target = (options && options.target) || 'blank'
|
|
||||||
let ii
|
|
||||||
|
|
||||||
// detect newline format
|
|
||||||
const newline = r.indexOf('\r\n') != -1 ? '\r\n' : r.indexOf('\n') != -1 ? '\n' : ''
|
|
||||||
|
|
||||||
// headings and hr
|
|
||||||
r = r.replace(/^### (.*)/gm, '<h5>$1</h5>')
|
|
||||||
r = r.replace(/^## (.*)/gm, '<h4>$1</h4>')
|
|
||||||
r = r.replace(/^# (.*)/gm, '<h3>$1</h3>')
|
|
||||||
r = r.replace(/^---/gm, '<hr>')
|
|
||||||
|
|
||||||
// bold, italics
|
|
||||||
r = r.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
||||||
r = r.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
||||||
|
|
||||||
// unordered lists
|
|
||||||
r = r.replace(/^\*\* (.*)/gm, '<ul><ul><li>$1</li></ul></ul>')
|
|
||||||
r = r.replace(/^\* (.*)/gm, '<ul><li>$1</li></ul>')
|
|
||||||
for (ii = 0; ii < 3; ii++)
|
|
||||||
r = r.replace(new RegExp(`</ul>${newline}<ul>`, 'g'), newline)
|
|
||||||
|
|
||||||
// links
|
|
||||||
r = r.replace(/(\[\[http)/g, '[[h_t_t_p') // Escape for avoiding clash between [[http://xxx]] and http://xxx
|
|
||||||
r = r.replace(/({{http)/g, '{{h_t_t_p')
|
|
||||||
r = r.replace(/(=http)/g, '=h_t_t_p') // http://xxx as query string, see https://github.com/umap-project/umap/issues/607
|
|
||||||
r = r.replace(/(https?:[^ \<)\n]*)/g, `<a target="_${target}" href="$1">$1</a>`)
|
|
||||||
r = r.replace(
|
|
||||||
/\[\[(h_t_t_ps?:[^\]|]*?)\]\]/g,
|
|
||||||
`<a target="_${target}" href="$1">$1</a>`
|
|
||||||
)
|
|
||||||
r = r.replace(
|
|
||||||
/\[\[(h_t_t_ps?:[^|]*?)\|(.*?)\]\]/g,
|
|
||||||
`<a target="_${target}" href="$1">$2</a>`
|
|
||||||
)
|
|
||||||
r = r.replace(/\[\[([^\]|]*?)\]\]/g, `<a target="_${target}" href="$1">$1</a>`)
|
|
||||||
r = r.replace(/\[\[([^|]*?)\|(.*?)\]\]/g, `<a target="_${target}" href="$1">$2</a>`)
|
|
||||||
|
|
||||||
// iframe
|
|
||||||
r = r.replace(
|
|
||||||
/{{{(h_t_t_ps?[^ |{]*)}}}/g,
|
|
||||||
'<div><iframe frameborder="0" src="$1" width="100%" height="300px"></iframe></div>'
|
|
||||||
)
|
|
||||||
r = r.replace(
|
|
||||||
/{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?}}}/g,
|
|
||||||
'<div><iframe frameborder="0" src="$1" width="100%" height="$2px"></iframe></div>'
|
|
||||||
)
|
|
||||||
r = r.replace(
|
|
||||||
/{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?\*(\d*)(px)?}}}/g,
|
|
||||||
'<div><iframe frameborder="0" src="$1" width="$4px" height="$2px"></iframe></div>'
|
|
||||||
)
|
|
||||||
|
|
||||||
// images
|
|
||||||
r = r.replace(/{{([^\]|]*?)}}/g, '<img src="$1">')
|
|
||||||
r = r.replace(
|
|
||||||
/{{([^|]*?)\|(\d*?)(px)?}}/g,
|
|
||||||
'<img src="$1" style="width:$2px;min-width:$2px;">'
|
|
||||||
)
|
|
||||||
|
|
||||||
//Unescape http
|
|
||||||
r = r.replace(/(h_t_t_p)/g, 'http')
|
|
||||||
|
|
||||||
// Preserver line breaks
|
|
||||||
if (newline) r = r.replace(new RegExp(`${newline}(?=[^]+)`, 'g'), `<br>${newline}`)
|
|
||||||
|
|
||||||
r = L.Util.escapeHTML(r)
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
L.Util.isObject = (what) => typeof what === 'object' && what !== null
|
|
||||||
L.Util.CopyJSON = (geojson) => JSON.parse(JSON.stringify(geojson))
|
|
||||||
L.Util.detectFileType = (f) => {
|
|
||||||
const filename = f.name ? escape(f.name.toLowerCase()) : ''
|
|
||||||
function ext(_) {
|
|
||||||
return filename.indexOf(_) !== -1
|
|
||||||
}
|
|
||||||
if (f.type === 'application/vnd.google-earth.kml+xml' || ext('.kml')) {
|
|
||||||
return 'kml'
|
|
||||||
}
|
|
||||||
if (ext('.gpx')) return 'gpx'
|
|
||||||
if (ext('.geojson') || ext('.json')) return 'geojson'
|
|
||||||
if (f.type === 'text/csv' || ext('.csv') || ext('.tsv') || ext('.dsv')) {
|
|
||||||
return 'csv'
|
|
||||||
}
|
|
||||||
if (ext('.xml') || ext('.osm')) return 'osm'
|
|
||||||
if (ext('.umap')) return 'umap'
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.usableOption = (options, option) =>
|
|
||||||
options[option] !== undefined && options[option] !== ''
|
|
||||||
|
|
||||||
L.Util.greedyTemplate = (str, data, ignore) => {
|
|
||||||
function getValue(data, path) {
|
|
||||||
let value = data
|
|
||||||
for (let i = 0; i < path.length; i++) {
|
|
||||||
value = value[path[i]]
|
|
||||||
if (value === undefined) break
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof str !== 'string') return ''
|
|
||||||
|
|
||||||
return str.replace(
|
|
||||||
/\{ *([^\{\}/\-]+)(?:\|("[^"]*"))? *\}/g,
|
|
||||||
(str, key, staticFallback) => {
|
|
||||||
const vars = key.split('|')
|
|
||||||
let value
|
|
||||||
let path
|
|
||||||
if (staticFallback !== undefined) {
|
|
||||||
vars.push(staticFallback)
|
|
||||||
}
|
|
||||||
for (let i = 0; i < vars.length; i++) {
|
|
||||||
path = vars[i]
|
|
||||||
if (path.startsWith('"') && path.endsWith('"'))
|
|
||||||
value = path.substring(1, path.length - 1) // static default value.
|
|
||||||
else value = getValue(data, path.split('.'))
|
|
||||||
if (value !== undefined) break
|
|
||||||
}
|
|
||||||
if (value === undefined) {
|
|
||||||
if (ignore) value = str
|
|
||||||
else value = ''
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.naturalSort = (a, b) => {
|
|
||||||
return a
|
|
||||||
.toString()
|
|
||||||
.toLowerCase()
|
|
||||||
.localeCompare(b.toString().toLowerCase(), L.lang || 'en', {
|
|
||||||
sensitivity: 'base',
|
|
||||||
numeric: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.sortFeatures = (features, sortKey) => {
|
|
||||||
const sortKeys = (sortKey || 'name').split(',')
|
|
||||||
|
|
||||||
const sort = (a, b, i) => {
|
|
||||||
let sortKey = sortKeys[i],
|
|
||||||
reverse = 1
|
|
||||||
if (sortKey[0] === '-') {
|
|
||||||
reverse = -1
|
|
||||||
sortKey = sortKey.substring(1)
|
|
||||||
}
|
|
||||||
let score
|
|
||||||
const valA = a.properties[sortKey] || ''
|
|
||||||
const valB = b.properties[sortKey] || ''
|
|
||||||
if (!valA) score = -1
|
|
||||||
else if (!valB) score = 1
|
|
||||||
else score = L.Util.naturalSort(valA, valB)
|
|
||||||
if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1)
|
|
||||||
return score * reverse
|
|
||||||
}
|
|
||||||
|
|
||||||
features.sort((a, b) => {
|
|
||||||
if (!a.properties || !b.properties) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return sort(a, b, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
return features
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.flattenCoordinates = (coords) => {
|
|
||||||
while (coords[0] && typeof coords[0][0] !== 'number') coords = coords[0]
|
|
||||||
return coords
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.buildQueryString = (params) => {
|
|
||||||
const query_string = []
|
|
||||||
for (const key in params) {
|
|
||||||
query_string.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
|
||||||
}
|
|
||||||
return query_string.join('&')
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.getBaseUrl = () => `//${window.location.host}${window.location.pathname}`
|
|
||||||
|
|
||||||
L.Util.hasVar = (value) => {
|
|
||||||
return typeof value === 'string' && value.indexOf('{') != -1
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.isPath = function (value) {
|
|
||||||
return value && value.length && value.startsWith('/')
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.isRemoteUrl = function (value) {
|
|
||||||
return value && value.length && value.startsWith('http')
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.isDataImage = function (value) {
|
|
||||||
return value && value.length && value.startsWith('data:image')
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Util.copyToClipboard = function (textToCopy) {
|
L.Util.copyToClipboard = function (textToCopy) {
|
||||||
// https://stackoverflow.com/a/65996386
|
// https://stackoverflow.com/a/65996386
|
||||||
// Navigator clipboard api needs a secure context (https)
|
// Navigator clipboard api needs a secure context (https)
|
||||||
|
@ -299,11 +25,46 @@ L.Util.copyToClipboard = function (textToCopy) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Util.normalize = function (s) {
|
L.Util.queryString = function (name, fallback) {
|
||||||
return (s || '')
|
const decode = (s) => decodeURIComponent(s.replace(/\+/g, ' '))
|
||||||
.toLowerCase()
|
const qs = window.location.search.slice(1).split('&'),
|
||||||
.normalize('NFD')
|
qa = {}
|
||||||
.replace(/[\u0300-\u036f]/g, '')
|
for (const i in qs) {
|
||||||
|
const key = qs[i].split('=')
|
||||||
|
if (!key) continue
|
||||||
|
qa[decode(key[0])] = key[1] ? decode(key[1]) : 1
|
||||||
|
}
|
||||||
|
return qa[name] || fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.booleanFromQueryString = function (name) {
|
||||||
|
const value = L.Util.queryString(name)
|
||||||
|
return value === '1' || value === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setFromQueryString = function (options, name) {
|
||||||
|
const value = L.Util.queryString(name)
|
||||||
|
if (typeof value !== 'undefined') options[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setBooleanFromQueryString = function (options, name) {
|
||||||
|
const value = L.Util.queryString(name)
|
||||||
|
if (typeof value !== 'undefined') options[name] = value == '1' || value == 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setNumberFromQueryString = function (options, name) {
|
||||||
|
const value = +L.Util.queryString(name)
|
||||||
|
if (!isNaN(value)) options[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setNullableBooleanFromQueryString = function (options, name) {
|
||||||
|
let value = L.Util.queryString(name)
|
||||||
|
if (typeof value !== 'undefined') {
|
||||||
|
if (value === 'null') value = null
|
||||||
|
else if (value === '0' || value === 'false') value = false
|
||||||
|
else value = true
|
||||||
|
options[name] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
L.DomUtil.add = (tagName, className, container, content) => {
|
L.DomUtil.add = (tagName, className, container, content) => {
|
||||||
|
@ -367,7 +128,7 @@ L.DomUtil.createCopiableInput = (parent, label, value) => {
|
||||||
'',
|
'',
|
||||||
wrapper,
|
wrapper,
|
||||||
'',
|
'',
|
||||||
() => L.Util.copyToClipboard(input.value),
|
() => U.Utils.copyToClipboard(input.value),
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
button.title = L._('copy')
|
button.title = L._('copy')
|
||||||
|
|
|
@ -46,7 +46,7 @@ U.DataLayerPermissions = L.Class.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getUrl: function () {
|
getUrl: function () {
|
||||||
return L.Util.template(this.datalayer.map.options.urls.datalayer_permissions, {
|
return U.Utils.template(this.datalayer.map.options.urls.datalayer_permissions, {
|
||||||
map_id: this.datalayer.map.options.umap_id,
|
map_id: this.datalayer.map.options.umap_id,
|
||||||
pk: this.datalayer.umap_id,
|
pk: this.datalayer.umap_id,
|
||||||
})
|
})
|
||||||
|
|
|
@ -59,7 +59,7 @@ U.FeatureMixin = {
|
||||||
getPermalink: function () {
|
getPermalink: function () {
|
||||||
const slug = this.getSlug()
|
const slug = this.getSlug()
|
||||||
if (slug)
|
if (slug)
|
||||||
return `${L.Util.getBaseUrl()}?${L.Util.buildQueryString({ feature: slug })}${
|
return `${U.Utils.getBaseUrl()}?${U.Utils.buildQueryString({ feature: slug })}${
|
||||||
window.location.hash
|
window.location.hash
|
||||||
}`
|
}`
|
||||||
},
|
},
|
||||||
|
@ -204,7 +204,8 @@ U.FeatureMixin = {
|
||||||
if (fallback === undefined) fallback = this.datalayer.options.name
|
if (fallback === undefined) fallback = this.datalayer.options.name
|
||||||
const key = this.getOption('labelKey') || 'name'
|
const key = this.getOption('labelKey') || 'name'
|
||||||
// Variables mode.
|
// Variables mode.
|
||||||
if (L.Util.hasVar(key)) return L.Util.greedyTemplate(key, this.extendedProperties())
|
if (U.Utils.hasVar(key))
|
||||||
|
return U.Utils.greedyTemplate(key, this.extendedProperties())
|
||||||
// Simple mode.
|
// Simple mode.
|
||||||
return this.properties[key] || this.properties.title || fallback
|
return this.properties[key] || this.properties.title || fallback
|
||||||
},
|
},
|
||||||
|
@ -291,7 +292,7 @@ U.FeatureMixin = {
|
||||||
let value = fallback
|
let value = fallback
|
||||||
if (typeof this.staticOptions[option] !== 'undefined') {
|
if (typeof this.staticOptions[option] !== 'undefined') {
|
||||||
value = this.staticOptions[option]
|
value = this.staticOptions[option]
|
||||||
} else if (L.Util.usableOption(this.properties._umap_options, option)) {
|
} else if (U.Utils.usableOption(this.properties._umap_options, option)) {
|
||||||
value = this.properties._umap_options[option]
|
value = this.properties._umap_options[option]
|
||||||
} else if (this.datalayer) {
|
} else if (this.datalayer) {
|
||||||
value = this.datalayer.getOption(option, this)
|
value = this.datalayer.getOption(option, this)
|
||||||
|
@ -304,9 +305,9 @@ U.FeatureMixin = {
|
||||||
getDynamicOption: function (option, fallback) {
|
getDynamicOption: function (option, fallback) {
|
||||||
let value = this.getOption(option, fallback)
|
let value = this.getOption(option, fallback)
|
||||||
// There is a variable inside.
|
// There is a variable inside.
|
||||||
if (L.Util.hasVar(value)) {
|
if (U.Utils.hasVar(value)) {
|
||||||
value = L.Util.greedyTemplate(value, this.properties, true)
|
value = U.Utils.greedyTemplate(value, this.properties, true)
|
||||||
if (L.Util.hasVar(value)) value = this.map.getDefaultOption(option)
|
if (U.Utils.hasVar(value)) value = this.map.getDefaultOption(option)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
|
@ -485,7 +486,7 @@ U.FeatureMixin = {
|
||||||
options.permanent = showLabel === true
|
options.permanent = showLabel === true
|
||||||
this.unbindTooltip()
|
this.unbindTooltip()
|
||||||
if ((showLabel === true || showLabel === null) && displayName)
|
if ((showLabel === true || showLabel === null) && displayName)
|
||||||
this.bindTooltip(L.Util.escapeHTML(displayName), options)
|
this.bindTooltip(U.Utils.escapeHTML(displayName), options)
|
||||||
},
|
},
|
||||||
|
|
||||||
matchFilter: function (filter, keys) {
|
matchFilter: function (filter, keys) {
|
||||||
|
@ -1059,7 +1060,7 @@ U.Polyline = L.Polyline.extend({
|
||||||
const geojson = this.toGeoJSON()
|
const geojson = this.toGeoJSON()
|
||||||
geojson.geometry.type = 'Polygon'
|
geojson.geometry.type = 'Polygon'
|
||||||
geojson.geometry.coordinates = [
|
geojson.geometry.coordinates = [
|
||||||
L.Util.flattenCoordinates(geojson.geometry.coordinates),
|
U.Utils.flattenCoordinates(geojson.geometry.coordinates),
|
||||||
]
|
]
|
||||||
const polygon = this.datalayer.geojsonToFeatures(geojson)
|
const polygon = this.datalayer.geojsonToFeatures(geojson)
|
||||||
polygon.edit()
|
polygon.edit()
|
||||||
|
@ -1199,7 +1200,7 @@ U.Polygon = L.Polygon.extend({
|
||||||
toPolyline: function () {
|
toPolyline: function () {
|
||||||
const geojson = this.toGeoJSON()
|
const geojson = this.toGeoJSON()
|
||||||
geojson.geometry.type = 'LineString'
|
geojson.geometry.type = 'LineString'
|
||||||
geojson.geometry.coordinates = L.Util.flattenCoordinates(
|
geojson.geometry.coordinates = U.Utils.flattenCoordinates(
|
||||||
geojson.geometry.coordinates
|
geojson.geometry.coordinates
|
||||||
)
|
)
|
||||||
const polyline = this.datalayer.geojsonToFeatures(geojson)
|
const polyline = this.datalayer.geojsonToFeatures(geojson)
|
||||||
|
|
|
@ -492,8 +492,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
this.buildTabs()
|
this.buildTabs()
|
||||||
const value = this.value()
|
const value = this.value()
|
||||||
if (U.Icon.RECENT.length) this.showRecentTab()
|
if (U.Icon.RECENT.length) this.showRecentTab()
|
||||||
else if (!value || L.Util.isPath(value)) this.showSymbolsTab()
|
else if (!value || U.Utils.isPath(value)) this.showSymbolsTab()
|
||||||
else if (L.Util.isRemoteUrl(value) || L.Util.isDataImage(value)) this.showURLTab()
|
else if (U.Utils.isRemoteUrl(value) || U.Utils.isDataImage(value)) this.showURLTab()
|
||||||
else this.showCharsTab()
|
else this.showCharsTab()
|
||||||
const closeButton = L.DomUtil.createButton(
|
const closeButton = L.DomUtil.createButton(
|
||||||
'button action-button',
|
'button action-button',
|
||||||
|
@ -567,7 +567,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
updatePreview: function () {
|
updatePreview: function () {
|
||||||
this.buttons.innerHTML = ''
|
this.buttons.innerHTML = ''
|
||||||
if (this.isDefault()) return
|
if (this.isDefault()) return
|
||||||
if (!L.Util.hasVar(this.value())) {
|
if (!U.Utils.hasVar(this.value())) {
|
||||||
// Do not try to render URL with variables
|
// Do not try to render URL with variables
|
||||||
const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
|
const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
|
||||||
L.DomEvent.on(box, 'click', this.onDefine, this)
|
L.DomEvent.on(box, 'click', this.onDefine, this)
|
||||||
|
@ -585,11 +585,11 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
addIconPreview: function (pictogram, parent) {
|
addIconPreview: function (pictogram, parent) {
|
||||||
const baseClass = 'umap-pictogram-choice',
|
const baseClass = 'umap-pictogram-choice',
|
||||||
value = pictogram.src,
|
value = pictogram.src,
|
||||||
search = L.Util.normalize(this.searchInput.value),
|
search = U.Utils.normalize(this.searchInput.value),
|
||||||
title = pictogram.attribution
|
title = pictogram.attribution
|
||||||
? `${pictogram.name} — © ${pictogram.attribution}`
|
? `${pictogram.name} — © ${pictogram.attribution}`
|
||||||
: pictogram.name || pictogram.src
|
: pictogram.name || pictogram.src
|
||||||
if (search && L.Util.normalize(title).indexOf(search) === -1) return
|
if (search && U.Utils.normalize(title).indexOf(search) === -1) return
|
||||||
const className = value === this.value() ? `${baseClass} selected` : baseClass,
|
const className = value === this.value() ? `${baseClass} selected` : baseClass,
|
||||||
container = L.DomUtil.create('div', className, parent)
|
container = L.DomUtil.create('div', className, parent)
|
||||||
U.Icon.makeIconElement(value, container)
|
U.Icon.makeIconElement(value, container)
|
||||||
|
@ -637,7 +637,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
categories[category].push(props)
|
categories[category].push(props)
|
||||||
}
|
}
|
||||||
const sorted = Object.entries(categories).toSorted(([a], [b]) =>
|
const sorted = Object.entries(categories).toSorted(([a], [b]) =>
|
||||||
L.Util.naturalSort(a, b)
|
U.Utils.naturalSort(a, b, L.lang)
|
||||||
)
|
)
|
||||||
for (let [name, items] of sorted) {
|
for (let [name, items] of sorted) {
|
||||||
this.addCategory(items, name)
|
this.addCategory(items, name)
|
||||||
|
@ -688,7 +688,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
showURLTab: function () {
|
showURLTab: function () {
|
||||||
this.openTab('url')
|
this.openTab('url')
|
||||||
const value =
|
const value =
|
||||||
L.Util.isRemoteUrl(this.value()) || L.Util.isDataImage(this.value())
|
U.Utils.isRemoteUrl(this.value()) || U.Utils.isDataImage(this.value())
|
||||||
? this.value()
|
? this.value()
|
||||||
: null
|
: null
|
||||||
const input = this.buildInput(this.body, value)
|
const input = this.buildInput(this.body, value)
|
||||||
|
|
|
@ -18,7 +18,7 @@ U.Icon = L.DivIcon.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
_setRecent: function (url) {
|
_setRecent: function (url) {
|
||||||
if (L.Util.hasVar(url)) return
|
if (U.Utils.hasVar(url)) return
|
||||||
if (url === U.SCHEMA.iconUrl.default) return
|
if (url === U.SCHEMA.iconUrl.default) return
|
||||||
if (U.Icon.RECENT.indexOf(url) === -1) {
|
if (U.Icon.RECENT.indexOf(url) === -1) {
|
||||||
U.Icon.RECENT.push(url)
|
U.Icon.RECENT.push(url)
|
||||||
|
@ -50,7 +50,10 @@ U.Icon = L.DivIcon.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
formatUrl: function (url, feature) {
|
formatUrl: function (url, feature) {
|
||||||
return L.Util.greedyTemplate(url || '', feature ? feature.extendedProperties() : {})
|
return U.Utils.greedyTemplate(
|
||||||
|
url || '',
|
||||||
|
feature ? feature.extendedProperties() : {}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
onAdd: function () {},
|
onAdd: function () {},
|
||||||
|
@ -206,7 +209,7 @@ U.Icon.Cluster = L.DivIcon.extend({
|
||||||
})
|
})
|
||||||
|
|
||||||
U.Icon.isImg = function (src) {
|
U.Icon.isImg = function (src) {
|
||||||
return L.Util.isPath(src) || L.Util.isRemoteUrl(src) || L.Util.isDataImage(src)
|
return U.Utils.isPath(src) || U.Utils.isRemoteUrl(src) || U.Utils.isDataImage(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
U.Icon.makeIconElement = function (src, parent) {
|
U.Icon.makeIconElement = function (src, parent) {
|
||||||
|
@ -236,7 +239,11 @@ U.Icon.setIconContrast = function (icon, parent, src, bgcolor) {
|
||||||
if (L.DomUtil.contrastedColor(parent, bgcolor)) {
|
if (L.DomUtil.contrastedColor(parent, bgcolor)) {
|
||||||
// Decide whether to switch svg to white or not, but do it
|
// Decide whether to switch svg to white or not, but do it
|
||||||
// only for internal SVG, as invert could do weird things
|
// only for internal SVG, as invert could do weird things
|
||||||
if (L.Util.isPath(src) && src.endsWith('.svg') && src !== U.SCHEMA.iconUrl.default) {
|
if (
|
||||||
|
U.Utils.isPath(src) &&
|
||||||
|
src.endsWith('.svg') &&
|
||||||
|
src !== U.SCHEMA.iconUrl.default
|
||||||
|
) {
|
||||||
// Must be called after icon container is added to the DOM
|
// Must be called after icon container is added to the DOM
|
||||||
// An image
|
// An image
|
||||||
icon.style.filter = 'invert(1)'
|
icon.style.filter = 'invert(1)'
|
||||||
|
|
|
@ -102,7 +102,7 @@ U.Importer = L.Class.extend({
|
||||||
let type = '',
|
let type = '',
|
||||||
newType
|
newType
|
||||||
for (let i = 0; i < e.target.files.length; i++) {
|
for (let i = 0; i < e.target.files.length; i++) {
|
||||||
newType = L.Util.detectFileType(e.target.files[i])
|
newType = U.Utils.detectFileType(e.target.files[i])
|
||||||
if (!type && newType) type = newType
|
if (!type && newType) type = newType
|
||||||
if (type && newType !== type) {
|
if (type && newType !== type) {
|
||||||
type = ''
|
type = ''
|
||||||
|
|
|
@ -76,6 +76,7 @@ U.Map = L.Map.extend({
|
||||||
.split(',')
|
.split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let editedFeature = null
|
let editedFeature = null
|
||||||
const self = this
|
const self = this
|
||||||
try {
|
try {
|
||||||
|
@ -344,7 +345,7 @@ U.Map = L.Map.extend({
|
||||||
document.body,
|
document.body,
|
||||||
'umap-caption-bar-enabled',
|
'umap-caption-bar-enabled',
|
||||||
this.options.captionBar ||
|
this.options.captionBar ||
|
||||||
(this.options.slideshow && this.options.slideshow.active)
|
(this.options.slideshow && this.options.slideshow.active)
|
||||||
)
|
)
|
||||||
L.DomUtil.classIf(
|
L.DomUtil.classIf(
|
||||||
document.body,
|
document.body,
|
||||||
|
@ -732,7 +733,7 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getOption: function (option) {
|
getOption: function (option) {
|
||||||
if (L.Util.usableOption(this.options, option)) return this.options[option]
|
if (U.Utils.usableOption(this.options, option)) return this.options[option]
|
||||||
return this.getDefaultOption(option)
|
return this.getDefaultOption(option)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -789,7 +790,7 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
processFileToImport: function (file, layer, type) {
|
processFileToImport: function (file, layer, type) {
|
||||||
type = type || L.Util.detectFileType(file)
|
type = type || U.Utils.detectFileType(file)
|
||||||
if (!type) {
|
if (!type) {
|
||||||
this.ui.alert({
|
this.ui.alert({
|
||||||
content: L._('Unable to detect format of file {filename}', {
|
content: L._('Unable to detect format of file {filename}', {
|
||||||
|
@ -992,7 +993,7 @@ U.Map = L.Map.extend({
|
||||||
{
|
{
|
||||||
label: L._('Copy link'),
|
label: L._('Copy link'),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
L.Util.copyToClipboard(data.permissions.anonymous_edit_url)
|
U.Utils.copyToClipboard(data.permissions.anonymous_edit_url)
|
||||||
this.ui.alert({
|
this.ui.alert({
|
||||||
content: L._('Secret edit link copied to clipboard!'),
|
content: L._('Secret edit link copied to clipboard!'),
|
||||||
level: 'info',
|
level: 'info',
|
||||||
|
@ -1263,7 +1264,7 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
_editTilelayer: function (container) {
|
_editTilelayer: function (container) {
|
||||||
if (!L.Util.isObject(this.options.tilelayer)) {
|
if (!U.Utils.isObject(this.options.tilelayer)) {
|
||||||
this.options.tilelayer = {}
|
this.options.tilelayer = {}
|
||||||
}
|
}
|
||||||
const tilelayerFields = [
|
const tilelayerFields = [
|
||||||
|
@ -1316,7 +1317,7 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
_editOverlay: function (container) {
|
_editOverlay: function (container) {
|
||||||
if (!L.Util.isObject(this.options.overlay)) {
|
if (!U.Utils.isObject(this.options.overlay)) {
|
||||||
this.options.overlay = {}
|
this.options.overlay = {}
|
||||||
}
|
}
|
||||||
const overlayFields = [
|
const overlayFields = [
|
||||||
|
@ -1367,7 +1368,7 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
_editBounds: function (container) {
|
_editBounds: function (container) {
|
||||||
if (!L.Util.isObject(this.options.limitBounds)) {
|
if (!U.Utils.isObject(this.options.limitBounds)) {
|
||||||
this.options.limitBounds = {}
|
this.options.limitBounds = {}
|
||||||
}
|
}
|
||||||
const limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'))
|
const limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'))
|
||||||
|
@ -1401,10 +1402,10 @@ U.Map = L.Map.extend({
|
||||||
L._('Use current bounds'),
|
L._('Use current bounds'),
|
||||||
function () {
|
function () {
|
||||||
const bounds = this.getBounds()
|
const bounds = this.getBounds()
|
||||||
this.options.limitBounds.south = L.Util.formatNum(bounds.getSouth())
|
this.options.limitBounds.south = U.Utils.formatNum(bounds.getSouth())
|
||||||
this.options.limitBounds.west = L.Util.formatNum(bounds.getWest())
|
this.options.limitBounds.west = U.Utils.formatNum(bounds.getWest())
|
||||||
this.options.limitBounds.north = L.Util.formatNum(bounds.getNorth())
|
this.options.limitBounds.north = U.Utils.formatNum(bounds.getNorth())
|
||||||
this.options.limitBounds.east = L.Util.formatNum(bounds.getEast())
|
this.options.limitBounds.east = U.Utils.formatNum(bounds.getEast())
|
||||||
boundsBuilder.fetchAll()
|
boundsBuilder.fetchAll()
|
||||||
this.isDirty = true
|
this.isDirty = true
|
||||||
this.handleLimitBounds()
|
this.handleLimitBounds()
|
||||||
|
@ -1572,10 +1573,10 @@ U.Map = L.Map.extend({
|
||||||
|
|
||||||
initCaptionBar: function () {
|
initCaptionBar: function () {
|
||||||
const container = L.DomUtil.create(
|
const container = L.DomUtil.create(
|
||||||
'div',
|
'div',
|
||||||
'umap-caption-bar',
|
'umap-caption-bar',
|
||||||
this._controlContainer
|
this._controlContainer
|
||||||
),
|
),
|
||||||
name = L.DomUtil.create('h3', '', container)
|
name = L.DomUtil.create('h3', '', container)
|
||||||
L.DomEvent.disableClickPropagation(container)
|
L.DomEvent.disableClickPropagation(container)
|
||||||
this.permissions.addOwnerLink('span', container)
|
this.permissions.addOwnerLink('span', container)
|
||||||
|
@ -1796,12 +1797,12 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
localizeUrl: function (url) {
|
localizeUrl: function (url) {
|
||||||
return L.Util.greedyTemplate(url, this.getGeoContext(), true)
|
return U.Utils.greedyTemplate(url, this.getGeoContext(), true)
|
||||||
},
|
},
|
||||||
|
|
||||||
proxyUrl: function (url, ttl) {
|
proxyUrl: function (url, ttl) {
|
||||||
if (this.options.urls.ajax_proxy) {
|
if (this.options.urls.ajax_proxy) {
|
||||||
url = L.Util.greedyTemplate(this.options.urls.ajax_proxy, {
|
url = U.Utils.greedyTemplate(this.options.urls.ajax_proxy, {
|
||||||
url: encodeURIComponent(url),
|
url: encodeURIComponent(url),
|
||||||
ttl: ttl,
|
ttl: ttl,
|
||||||
})
|
})
|
||||||
|
|
|
@ -97,7 +97,7 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getEditableOptions: function () {
|
getEditableOptions: function () {
|
||||||
if (!L.Util.isObject(this.datalayer.options.cluster)) {
|
if (!U.Utils.isObject(this.datalayer.options.cluster)) {
|
||||||
this.datalayer.options.cluster = {}
|
this.datalayer.options.cluster = {}
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
|
@ -155,7 +155,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
||||||
|
|
||||||
initialize: function (datalayer) {
|
initialize: function (datalayer) {
|
||||||
this.datalayer = datalayer
|
this.datalayer = datalayer
|
||||||
if (!L.Util.isObject(this.datalayer.options.choropleth)) {
|
if (!U.Utils.isObject(this.datalayer.options.choropleth)) {
|
||||||
this.datalayer.options.choropleth = {}
|
this.datalayer.options.choropleth = {}
|
||||||
}
|
}
|
||||||
L.FeatureGroup.prototype.initialize.call(
|
L.FeatureGroup.prototype.initialize.call(
|
||||||
|
@ -381,7 +381,7 @@ U.Layer.Heat = L.HeatLayer.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getEditableOptions: function () {
|
getEditableOptions: function () {
|
||||||
if (!L.Util.isObject(this.datalayer.options.heat)) {
|
if (!U.Utils.isObject(this.datalayer.options.heat)) {
|
||||||
this.datalayer.options.heat = {}
|
this.datalayer.options.heat = {}
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
|
@ -461,8 +461,8 @@ U.Layer.Heat = L.HeatLayer.extend({
|
||||||
this._latlngs[i].alt !== undefined
|
this._latlngs[i].alt !== undefined
|
||||||
? this._latlngs[i].alt
|
? this._latlngs[i].alt
|
||||||
: this._latlngs[i][2] !== undefined
|
: this._latlngs[i][2] !== undefined
|
||||||
? +this._latlngs[i][2]
|
? +this._latlngs[i][2]
|
||||||
: 1
|
: 1
|
||||||
|
|
||||||
grid[y] = grid[y] || []
|
grid[y] = grid[y] || []
|
||||||
cell = grid[y][x]
|
cell = grid[y][x]
|
||||||
|
@ -724,16 +724,16 @@ U.DataLayer = L.Evented.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
backupData: function () {
|
backupData: function () {
|
||||||
this._geojson_bk = L.Util.CopyJSON(this._geojson)
|
this._geojson_bk = U.Utils.CopyJSON(this._geojson)
|
||||||
},
|
},
|
||||||
|
|
||||||
reindex: function () {
|
reindex: function () {
|
||||||
const features = []
|
const features = []
|
||||||
this.eachFeature((feature) => features.push(feature))
|
this.eachFeature((feature) => features.push(feature))
|
||||||
L.Util.sortFeatures(features, this.map.getOption('sortKey'))
|
U.Utils.sortFeatures(features, this.map.getOption('sortKey'), L.lang)
|
||||||
this._index = []
|
this._index = []
|
||||||
for (let i = 0; i < features.length; i++) {
|
for (let i = 0; i < features.length; i++) {
|
||||||
this._index.push(L.Util.stamp(features[i]))
|
this._index.push(U.Utils.stamp(features[i]))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -793,16 +793,16 @@ U.DataLayer = L.Evented.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
backupOptions: function () {
|
backupOptions: function () {
|
||||||
this._backupOptions = L.Util.CopyJSON(this.options)
|
this._backupOptions = U.Utils.CopyJSON(this.options)
|
||||||
},
|
},
|
||||||
|
|
||||||
resetOptions: function () {
|
resetOptions: function () {
|
||||||
this.options = L.Util.CopyJSON(this._backupOptions)
|
this.options = U.Utils.CopyJSON(this._backupOptions)
|
||||||
},
|
},
|
||||||
|
|
||||||
setOptions: function (options) {
|
setOptions: function (options) {
|
||||||
delete options.geojson
|
delete options.geojson
|
||||||
this.options = L.Util.CopyJSON(U.DataLayer.prototype.options) // Start from fresh.
|
this.options = U.Utils.CopyJSON(U.DataLayer.prototype.options) // Start from fresh.
|
||||||
this.updateOptions(options)
|
this.updateOptions(options)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -824,7 +824,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
_dataUrl: function () {
|
_dataUrl: function () {
|
||||||
const template = this.map.options.urls.datalayer_view
|
const template = this.map.options.urls.datalayer_view
|
||||||
|
|
||||||
let url = L.Util.template(template, {
|
let url = U.Utils.template(template, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this.map.options.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
|
@ -971,7 +971,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
let latlngs
|
let latlngs
|
||||||
|
|
||||||
if (features) {
|
if (features) {
|
||||||
L.Util.sortFeatures(features, this.map.getOption('sortKey'))
|
U.Utils.sortFeatures(features, this.map.getOption('sortKey'), L.lang)
|
||||||
for (i = 0, len = features.length; i < len; i++) {
|
for (i = 0, len = features.length; i < len; i++) {
|
||||||
this.geojsonToFeatures(features[i])
|
this.geojsonToFeatures(features[i])
|
||||||
}
|
}
|
||||||
|
@ -1061,7 +1061,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
|
|
||||||
importFromFile: function (f, type) {
|
importFromFile: function (f, type) {
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
type = type || L.Util.detectFileType(f)
|
type = type || U.Utils.detectFileType(f)
|
||||||
reader.readAsText(f)
|
reader.readAsText(f)
|
||||||
reader.onload = (e) => this.importRaw(e.target.result, type)
|
reader.onload = (e) => this.importRaw(e.target.result, type)
|
||||||
},
|
},
|
||||||
|
@ -1079,21 +1079,21 @@ U.DataLayer = L.Evented.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getDeleteUrl: function () {
|
getDeleteUrl: function () {
|
||||||
return L.Util.template(this.map.options.urls.datalayer_delete, {
|
return U.Utils.template(this.map.options.urls.datalayer_delete, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this.map.options.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
getVersionsUrl: function () {
|
getVersionsUrl: function () {
|
||||||
return L.Util.template(this.map.options.urls.datalayer_versions, {
|
return U.Utils.template(this.map.options.urls.datalayer_versions, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this.map.options.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
getVersionUrl: function (name) {
|
getVersionUrl: function (name) {
|
||||||
return L.Util.template(this.map.options.urls.datalayer_version, {
|
return U.Utils.template(this.map.options.urls.datalayer_version, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this.map.options.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -1112,10 +1112,10 @@ U.DataLayer = L.Evented.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
clone: function () {
|
clone: function () {
|
||||||
const options = L.Util.CopyJSON(this.options)
|
const options = U.Utils.CopyJSON(this.options)
|
||||||
options.name = L._('Clone of {name}', { name: this.options.name })
|
options.name = L._('Clone of {name}', { name: this.options.name })
|
||||||
delete options.id
|
delete options.id
|
||||||
const geojson = L.Util.CopyJSON(this._geojson),
|
const geojson = U.Utils.CopyJSON(this._geojson),
|
||||||
datalayer = this.map.createDataLayer(options)
|
datalayer = this.map.createDataLayer(options)
|
||||||
datalayer.fromGeoJSON(geojson)
|
datalayer.fromGeoJSON(geojson)
|
||||||
return datalayer
|
return datalayer
|
||||||
|
@ -1276,7 +1276,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
)
|
)
|
||||||
popupFieldset.appendChild(builder.build())
|
popupFieldset.appendChild(builder.build())
|
||||||
|
|
||||||
if (!L.Util.isObject(this.options.remoteData)) {
|
if (!U.Utils.isObject(this.options.remoteData)) {
|
||||||
this.options.remoteData = {}
|
this.options.remoteData = {}
|
||||||
}
|
}
|
||||||
const remoteDataFields = [
|
const remoteDataFields = [
|
||||||
|
@ -1371,7 +1371,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getOwnOption: function (option) {
|
getOwnOption: function (option) {
|
||||||
if (L.Util.usableOption(this.options, option)) return this.options[option]
|
if (U.Utils.usableOption(this.options, option)) return this.options[option]
|
||||||
},
|
},
|
||||||
|
|
||||||
getOption: function (option, feature) {
|
getOption: function (option, feature) {
|
||||||
|
@ -1668,6 +1668,6 @@ L.TileLayer.include({
|
||||||
},
|
},
|
||||||
|
|
||||||
getAttribution: function () {
|
getAttribution: function () {
|
||||||
return L.Util.toHTML(this.options.attribution)
|
return U.Utils.toHTML(this.options.attribution)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -62,9 +62,8 @@ U.MapPermissions = L.Class.extend({
|
||||||
title = L.DomUtil.create('h3', '', container)
|
title = L.DomUtil.create('h3', '', container)
|
||||||
if (this.isAnonymousMap()) {
|
if (this.isAnonymousMap()) {
|
||||||
if (this.options.anonymous_edit_url) {
|
if (this.options.anonymous_edit_url) {
|
||||||
const helpText = `${L._('Secret edit link:')}<br>${
|
const helpText = `${L._('Secret edit link:')}<br>${this.options.anonymous_edit_url
|
||||||
this.options.anonymous_edit_url
|
}`
|
||||||
}`
|
|
||||||
L.DomUtil.add('p', 'help-text', container, helpText)
|
L.DomUtil.add('p', 'help-text', container, helpText)
|
||||||
fields.push([
|
fields.push([
|
||||||
'options.edit_status',
|
'options.edit_status',
|
||||||
|
@ -171,13 +170,13 @@ U.MapPermissions = L.Class.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getUrl: function () {
|
getUrl: function () {
|
||||||
return L.Util.template(this.map.options.urls.map_update_permissions, {
|
return U.Utils.template(this.map.options.urls.map_update_permissions, {
|
||||||
map_id: this.map.options.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
getAttachUrl: function () {
|
getAttachUrl: function () {
|
||||||
return L.Util.template(this.map.options.urls.map_attach_owner, {
|
return U.Utils.template(this.map.options.urls.map_attach_owner, {
|
||||||
map_id: this.map.options.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -124,12 +124,12 @@ U.PopupTemplate.Default = L.Class.extend({
|
||||||
let center
|
let center
|
||||||
properties = this.feature.extendedProperties()
|
properties = this.feature.extendedProperties()
|
||||||
// Resolve properties inside description
|
// Resolve properties inside description
|
||||||
properties.description = L.Util.greedyTemplate(
|
properties.description = U.Utils.greedyTemplate(
|
||||||
this.feature.properties.description || '',
|
this.feature.properties.description || '',
|
||||||
properties
|
properties
|
||||||
)
|
)
|
||||||
content = L.Util.greedyTemplate(template, properties)
|
content = U.Utils.greedyTemplate(template, properties)
|
||||||
content = L.Util.toHTML(content, { target: target })
|
content = U.Utils.toHTML(content, { target: target })
|
||||||
container.innerHTML = content
|
container.innerHTML = content
|
||||||
return container
|
return container
|
||||||
},
|
},
|
||||||
|
@ -211,7 +211,7 @@ U.PopupTemplate.Table = U.PopupTemplate.BaseWithTitle.extend({
|
||||||
for (const key in this.feature.properties) {
|
for (const key in this.feature.properties) {
|
||||||
if (typeof this.feature.properties[key] === 'object' || key === 'name') continue
|
if (typeof this.feature.properties[key] === 'object' || key === 'name') continue
|
||||||
// TODO, manage links (url, mailto, wikipedia...)
|
// TODO, manage links (url, mailto, wikipedia...)
|
||||||
this.addRow(table, key, L.Util.escapeHTML(this.feature.properties[key]).trim())
|
this.addRow(table, key, U.Utils.escapeHTML(this.feature.properties[key]).trim())
|
||||||
}
|
}
|
||||||
return table
|
return table
|
||||||
},
|
},
|
||||||
|
|
|
@ -51,7 +51,7 @@ U.Share = L.Class.extend({
|
||||||
L.DomUtil.createCopiableInput(
|
L.DomUtil.createCopiableInput(
|
||||||
this.container,
|
this.container,
|
||||||
L._('Link to view the map'),
|
L._('Link to view the map'),
|
||||||
window.location.protocol + L.Util.getBaseUrl()
|
window.location.protocol + U.Utils.getBaseUrl()
|
||||||
)
|
)
|
||||||
|
|
||||||
if (this.map.options.shortUrl) {
|
if (this.map.options.shortUrl) {
|
||||||
|
@ -84,7 +84,7 @@ U.Share = L.Class.extend({
|
||||||
this.container,
|
this.container,
|
||||||
L._('All data and settings of the map')
|
L._('All data and settings of the map')
|
||||||
)
|
)
|
||||||
const downloadUrl = L.Util.template(this.map.options.urls.map_download, {
|
const downloadUrl = U.Utils.template(this.map.options.urls.map_download, {
|
||||||
map_id: this.map.options.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
const link = L.DomUtil.createLink(
|
const link = L.DomUtil.createLink(
|
||||||
|
@ -213,7 +213,7 @@ U.IframeExporter = L.Evented.extend({
|
||||||
|
|
||||||
initialize: function (map) {
|
initialize: function (map) {
|
||||||
this.map = map
|
this.map = map
|
||||||
this.baseUrl = L.Util.getBaseUrl()
|
this.baseUrl = U.Utils.getBaseUrl()
|
||||||
// Use map default, not generic default
|
// Use map default, not generic default
|
||||||
this.queryString.onLoadPanel = this.map.getOption('onLoadPanel')
|
this.queryString.onLoadPanel = this.map.getOption('onLoadPanel')
|
||||||
},
|
},
|
||||||
|
@ -241,7 +241,7 @@ U.IframeExporter = L.Evented.extend({
|
||||||
}
|
}
|
||||||
const currentView = this.options.currentView ? window.location.hash : ''
|
const currentView = this.options.currentView ? window.location.hash : ''
|
||||||
const queryString = L.extend({}, this.queryString, options)
|
const queryString = L.extend({}, this.queryString, options)
|
||||||
return `${this.baseUrl}?${L.Util.buildQueryString(queryString)}${currentView}`
|
return `${this.baseUrl}?${U.Utils.buildQueryString(queryString)}${currentView}`
|
||||||
},
|
},
|
||||||
|
|
||||||
build: function () {
|
build: function () {
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
<script src="../vendors/iconlayers/iconLayers.js" defer></script>
|
<script src="../vendors/iconlayers/iconLayers.js" defer></script>
|
||||||
<script src="../vendors/tokml/tokml.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/locatecontrol/L.Control.Locate.min.js" defer></script>
|
||||||
<script src="../vendors/dompurify/purify.min.js" defer></script>
|
|
||||||
<script src="../vendors/colorbrewer/colorbrewer.js" defer></script>
|
<script src="../vendors/colorbrewer/colorbrewer.js" defer></script>
|
||||||
<script src="../vendors/simple-statistics/simple-statistics.min.js" defer></script>
|
<script src="../vendors/simple-statistics/simple-statistics.min.js" defer></script>
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
<script src="{% static 'umap/vendors/tokml/tokml.js' %}" defer></script>
|
<script src="{% static 'umap/vendors/tokml/tokml.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/vendors/locatecontrol/L.Control.Locate.min.js' %}"
|
<script src="{% static 'umap/vendors/locatecontrol/L.Control.Locate.min.js' %}"
|
||||||
defer></script>
|
defer></script>
|
||||||
<script src="{% static 'umap/vendors/dompurify/purify.min.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/vendors/colorbrewer/colorbrewer.js' %}" defer></script>
|
<script src="{% static 'umap/vendors/colorbrewer/colorbrewer.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/vendors/simple-statistics/simple-statistics.min.js' %}"
|
<script src="{% static 'umap/vendors/simple-statistics/simple-statistics.min.js' %}"
|
||||||
defer></script>
|
defer></script>
|
||||||
|
|
Loading…
Reference in a new issue