[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",
|
||||
"colorbrewer": "^1.5.6",
|
||||
"csv2geojson": "5.1.1",
|
||||
"dompurify": "^3.0.3",
|
||||
"dompurify": "^3.0.11",
|
||||
"georsstogeojson": "^0.1.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-contextmenu": "^1.4.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/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/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/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/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import URLs from './urls.js'
|
||||
import Browser from './browser.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 modules and export them to the global scope.
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
// Vendorized from leaflet.utils
|
||||
// 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
|
||||
})
|
||||
}
|
||||
import { template } from "./utils.js"
|
||||
|
||||
export default class URLs {
|
||||
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)
|
||||
*
|
||||
|
@ -22,3 +24,284 @@ export function checkId(string) {
|
|||
if (typeof string !== 'string') return false
|
||||
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.hide()
|
||||
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 () {
|
||||
this.paragraphContainer.innerHTML = L.Util.toHTML(this.map.options.permanentCredit)
|
||||
this.paragraphContainer.innerHTML = U.Utils.toHTML(this.map.options.permanentCredit)
|
||||
},
|
||||
|
||||
setBackground: function () {
|
||||
|
@ -820,7 +820,7 @@ const ControlsMixin = {
|
|||
this.permissions.addOwnerLink('h5', container)
|
||||
if (this.options.description) {
|
||||
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)
|
||||
this.eachVisibleDataLayer((datalayer) => {
|
||||
|
@ -832,7 +832,7 @@ const ControlsMixin = {
|
|||
datalayer.onceLoaded(function () {
|
||||
datalayer.renderLegend(legend)
|
||||
if (datalayer.options.description) {
|
||||
description.innerHTML = L.Util.toHTML(datalayer.options.description)
|
||||
description.innerHTML = U.Utils.toHTML(datalayer.options.description)
|
||||
}
|
||||
})
|
||||
datalayer.renderToolbox(headline)
|
||||
|
@ -846,7 +846,7 @@ const ControlsMixin = {
|
|||
'p',
|
||||
'',
|
||||
credits,
|
||||
L.Util.toHTML(this.options.longCredit || this.options.shortCredit)
|
||||
U.Utils.toHTML(this.options.longCredit || this.options.shortCredit)
|
||||
)
|
||||
}
|
||||
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
|
||||
// to be defined)
|
||||
// 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,
|
||||
this.map.demoTileInfos
|
||||
)
|
||||
|
@ -1150,7 +1150,7 @@ U.TileLayerChooser = L.Control.extend({
|
|||
el = L.DomUtil.create('li', selectedClass, this._tilelayers_container),
|
||||
img = L.DomUtil.create('img', '', 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'
|
||||
name.textContent = tilelayer.options.name
|
||||
L.DomEvent.on(
|
||||
|
@ -1187,7 +1187,7 @@ U.AttributionControl = L.Control.Attribution.extend({
|
|||
const shortCredit = this._map.getOption('shortCredit'),
|
||||
captionMenus = this._map.getOption('captionMenus')
|
||||
if (shortCredit) {
|
||||
L.DomUtil.add('span', '', container, ` — ${L.Util.toHTML(shortCredit)}`)
|
||||
L.DomUtil.add('span', '', container, ` — ${U.Utils.toHTML(shortCredit)}`)
|
||||
}
|
||||
if (captionMenus) {
|
||||
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) {
|
||||
// https://stackoverflow.com/a/65996386
|
||||
// Navigator clipboard api needs a secure context (https)
|
||||
|
@ -299,11 +25,46 @@ L.Util.copyToClipboard = function (textToCopy) {
|
|||
}
|
||||
}
|
||||
|
||||
L.Util.normalize = function (s) {
|
||||
return (s || '')
|
||||
.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
L.Util.queryString = function (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 = 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) => {
|
||||
|
@ -367,7 +128,7 @@ L.DomUtil.createCopiableInput = (parent, label, value) => {
|
|||
'',
|
||||
wrapper,
|
||||
'',
|
||||
() => L.Util.copyToClipboard(input.value),
|
||||
() => U.Utils.copyToClipboard(input.value),
|
||||
this
|
||||
)
|
||||
button.title = L._('copy')
|
||||
|
|
|
@ -46,7 +46,7 @@ U.DataLayerPermissions = L.Class.extend({
|
|||
},
|
||||
|
||||
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,
|
||||
pk: this.datalayer.umap_id,
|
||||
})
|
||||
|
|
|
@ -59,7 +59,7 @@ U.FeatureMixin = {
|
|||
getPermalink: function () {
|
||||
const slug = this.getSlug()
|
||||
if (slug)
|
||||
return `${L.Util.getBaseUrl()}?${L.Util.buildQueryString({ feature: slug })}${
|
||||
return `${U.Utils.getBaseUrl()}?${U.Utils.buildQueryString({ feature: slug })}${
|
||||
window.location.hash
|
||||
}`
|
||||
},
|
||||
|
@ -204,7 +204,8 @@ U.FeatureMixin = {
|
|||
if (fallback === undefined) fallback = this.datalayer.options.name
|
||||
const key = this.getOption('labelKey') || 'name'
|
||||
// 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.
|
||||
return this.properties[key] || this.properties.title || fallback
|
||||
},
|
||||
|
@ -291,7 +292,7 @@ U.FeatureMixin = {
|
|||
let value = fallback
|
||||
if (typeof this.staticOptions[option] !== 'undefined') {
|
||||
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]
|
||||
} else if (this.datalayer) {
|
||||
value = this.datalayer.getOption(option, this)
|
||||
|
@ -304,9 +305,9 @@ U.FeatureMixin = {
|
|||
getDynamicOption: function (option, fallback) {
|
||||
let value = this.getOption(option, fallback)
|
||||
// There is a variable inside.
|
||||
if (L.Util.hasVar(value)) {
|
||||
value = L.Util.greedyTemplate(value, this.properties, true)
|
||||
if (L.Util.hasVar(value)) value = this.map.getDefaultOption(option)
|
||||
if (U.Utils.hasVar(value)) {
|
||||
value = U.Utils.greedyTemplate(value, this.properties, true)
|
||||
if (U.Utils.hasVar(value)) value = this.map.getDefaultOption(option)
|
||||
}
|
||||
return value
|
||||
},
|
||||
|
@ -485,7 +486,7 @@ U.FeatureMixin = {
|
|||
options.permanent = showLabel === true
|
||||
this.unbindTooltip()
|
||||
if ((showLabel === true || showLabel === null) && displayName)
|
||||
this.bindTooltip(L.Util.escapeHTML(displayName), options)
|
||||
this.bindTooltip(U.Utils.escapeHTML(displayName), options)
|
||||
},
|
||||
|
||||
matchFilter: function (filter, keys) {
|
||||
|
@ -1059,7 +1060,7 @@ U.Polyline = L.Polyline.extend({
|
|||
const geojson = this.toGeoJSON()
|
||||
geojson.geometry.type = 'Polygon'
|
||||
geojson.geometry.coordinates = [
|
||||
L.Util.flattenCoordinates(geojson.geometry.coordinates),
|
||||
U.Utils.flattenCoordinates(geojson.geometry.coordinates),
|
||||
]
|
||||
const polygon = this.datalayer.geojsonToFeatures(geojson)
|
||||
polygon.edit()
|
||||
|
@ -1199,7 +1200,7 @@ U.Polygon = L.Polygon.extend({
|
|||
toPolyline: function () {
|
||||
const geojson = this.toGeoJSON()
|
||||
geojson.geometry.type = 'LineString'
|
||||
geojson.geometry.coordinates = L.Util.flattenCoordinates(
|
||||
geojson.geometry.coordinates = U.Utils.flattenCoordinates(
|
||||
geojson.geometry.coordinates
|
||||
)
|
||||
const polyline = this.datalayer.geojsonToFeatures(geojson)
|
||||
|
|
|
@ -492,8 +492,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
|||
this.buildTabs()
|
||||
const value = this.value()
|
||||
if (U.Icon.RECENT.length) this.showRecentTab()
|
||||
else if (!value || L.Util.isPath(value)) this.showSymbolsTab()
|
||||
else if (L.Util.isRemoteUrl(value) || L.Util.isDataImage(value)) this.showURLTab()
|
||||
else if (!value || U.Utils.isPath(value)) this.showSymbolsTab()
|
||||
else if (U.Utils.isRemoteUrl(value) || U.Utils.isDataImage(value)) this.showURLTab()
|
||||
else this.showCharsTab()
|
||||
const closeButton = L.DomUtil.createButton(
|
||||
'button action-button',
|
||||
|
@ -567,7 +567,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
|||
updatePreview: function () {
|
||||
this.buttons.innerHTML = ''
|
||||
if (this.isDefault()) return
|
||||
if (!L.Util.hasVar(this.value())) {
|
||||
if (!U.Utils.hasVar(this.value())) {
|
||||
// Do not try to render URL with variables
|
||||
const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
|
||||
L.DomEvent.on(box, 'click', this.onDefine, this)
|
||||
|
@ -585,11 +585,11 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
|||
addIconPreview: function (pictogram, parent) {
|
||||
const baseClass = 'umap-pictogram-choice',
|
||||
value = pictogram.src,
|
||||
search = L.Util.normalize(this.searchInput.value),
|
||||
search = U.Utils.normalize(this.searchInput.value),
|
||||
title = pictogram.attribution
|
||||
? `${pictogram.name} — © ${pictogram.attribution}`
|
||||
: 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,
|
||||
container = L.DomUtil.create('div', className, parent)
|
||||
U.Icon.makeIconElement(value, container)
|
||||
|
@ -637,7 +637,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
|||
categories[category].push(props)
|
||||
}
|
||||
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) {
|
||||
this.addCategory(items, name)
|
||||
|
@ -688,7 +688,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
|||
showURLTab: function () {
|
||||
this.openTab('url')
|
||||
const value =
|
||||
L.Util.isRemoteUrl(this.value()) || L.Util.isDataImage(this.value())
|
||||
U.Utils.isRemoteUrl(this.value()) || U.Utils.isDataImage(this.value())
|
||||
? this.value()
|
||||
: null
|
||||
const input = this.buildInput(this.body, value)
|
||||
|
|
|
@ -18,7 +18,7 @@ U.Icon = L.DivIcon.extend({
|
|||
},
|
||||
|
||||
_setRecent: function (url) {
|
||||
if (L.Util.hasVar(url)) return
|
||||
if (U.Utils.hasVar(url)) return
|
||||
if (url === U.SCHEMA.iconUrl.default) return
|
||||
if (U.Icon.RECENT.indexOf(url) === -1) {
|
||||
U.Icon.RECENT.push(url)
|
||||
|
@ -50,7 +50,10 @@ U.Icon = L.DivIcon.extend({
|
|||
},
|
||||
|
||||
formatUrl: function (url, feature) {
|
||||
return L.Util.greedyTemplate(url || '', feature ? feature.extendedProperties() : {})
|
||||
return U.Utils.greedyTemplate(
|
||||
url || '',
|
||||
feature ? feature.extendedProperties() : {}
|
||||
)
|
||||
},
|
||||
|
||||
onAdd: function () {},
|
||||
|
@ -206,7 +209,7 @@ U.Icon.Cluster = L.DivIcon.extend({
|
|||
})
|
||||
|
||||
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) {
|
||||
|
@ -236,7 +239,11 @@ U.Icon.setIconContrast = function (icon, parent, src, bgcolor) {
|
|||
if (L.DomUtil.contrastedColor(parent, bgcolor)) {
|
||||
// Decide whether to switch svg to white or not, but do it
|
||||
// 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
|
||||
// An image
|
||||
icon.style.filter = 'invert(1)'
|
||||
|
|
|
@ -102,7 +102,7 @@ U.Importer = L.Class.extend({
|
|||
let type = '',
|
||||
newType
|
||||
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) {
|
||||
type = ''
|
||||
|
|
|
@ -76,6 +76,7 @@ U.Map = L.Map.extend({
|
|||
.split(',')
|
||||
}
|
||||
|
||||
|
||||
let editedFeature = null
|
||||
const self = this
|
||||
try {
|
||||
|
@ -344,7 +345,7 @@ U.Map = L.Map.extend({
|
|||
document.body,
|
||||
'umap-caption-bar-enabled',
|
||||
this.options.captionBar ||
|
||||
(this.options.slideshow && this.options.slideshow.active)
|
||||
(this.options.slideshow && this.options.slideshow.active)
|
||||
)
|
||||
L.DomUtil.classIf(
|
||||
document.body,
|
||||
|
@ -732,7 +733,7 @@ U.Map = L.Map.extend({
|
|||
},
|
||||
|
||||
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)
|
||||
},
|
||||
|
||||
|
@ -789,7 +790,7 @@ U.Map = L.Map.extend({
|
|||
},
|
||||
|
||||
processFileToImport: function (file, layer, type) {
|
||||
type = type || L.Util.detectFileType(file)
|
||||
type = type || U.Utils.detectFileType(file)
|
||||
if (!type) {
|
||||
this.ui.alert({
|
||||
content: L._('Unable to detect format of file {filename}', {
|
||||
|
@ -992,7 +993,7 @@ U.Map = L.Map.extend({
|
|||
{
|
||||
label: L._('Copy link'),
|
||||
callback: () => {
|
||||
L.Util.copyToClipboard(data.permissions.anonymous_edit_url)
|
||||
U.Utils.copyToClipboard(data.permissions.anonymous_edit_url)
|
||||
this.ui.alert({
|
||||
content: L._('Secret edit link copied to clipboard!'),
|
||||
level: 'info',
|
||||
|
@ -1263,7 +1264,7 @@ U.Map = L.Map.extend({
|
|||
},
|
||||
|
||||
_editTilelayer: function (container) {
|
||||
if (!L.Util.isObject(this.options.tilelayer)) {
|
||||
if (!U.Utils.isObject(this.options.tilelayer)) {
|
||||
this.options.tilelayer = {}
|
||||
}
|
||||
const tilelayerFields = [
|
||||
|
@ -1316,7 +1317,7 @@ U.Map = L.Map.extend({
|
|||
},
|
||||
|
||||
_editOverlay: function (container) {
|
||||
if (!L.Util.isObject(this.options.overlay)) {
|
||||
if (!U.Utils.isObject(this.options.overlay)) {
|
||||
this.options.overlay = {}
|
||||
}
|
||||
const overlayFields = [
|
||||
|
@ -1367,7 +1368,7 @@ U.Map = L.Map.extend({
|
|||
},
|
||||
|
||||
_editBounds: function (container) {
|
||||
if (!L.Util.isObject(this.options.limitBounds)) {
|
||||
if (!U.Utils.isObject(this.options.limitBounds)) {
|
||||
this.options.limitBounds = {}
|
||||
}
|
||||
const limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'))
|
||||
|
@ -1401,10 +1402,10 @@ U.Map = L.Map.extend({
|
|||
L._('Use current bounds'),
|
||||
function () {
|
||||
const bounds = this.getBounds()
|
||||
this.options.limitBounds.south = L.Util.formatNum(bounds.getSouth())
|
||||
this.options.limitBounds.west = L.Util.formatNum(bounds.getWest())
|
||||
this.options.limitBounds.north = L.Util.formatNum(bounds.getNorth())
|
||||
this.options.limitBounds.east = L.Util.formatNum(bounds.getEast())
|
||||
this.options.limitBounds.south = U.Utils.formatNum(bounds.getSouth())
|
||||
this.options.limitBounds.west = U.Utils.formatNum(bounds.getWest())
|
||||
this.options.limitBounds.north = U.Utils.formatNum(bounds.getNorth())
|
||||
this.options.limitBounds.east = U.Utils.formatNum(bounds.getEast())
|
||||
boundsBuilder.fetchAll()
|
||||
this.isDirty = true
|
||||
this.handleLimitBounds()
|
||||
|
@ -1572,10 +1573,10 @@ U.Map = L.Map.extend({
|
|||
|
||||
initCaptionBar: function () {
|
||||
const container = L.DomUtil.create(
|
||||
'div',
|
||||
'umap-caption-bar',
|
||||
this._controlContainer
|
||||
),
|
||||
'div',
|
||||
'umap-caption-bar',
|
||||
this._controlContainer
|
||||
),
|
||||
name = L.DomUtil.create('h3', '', container)
|
||||
L.DomEvent.disableClickPropagation(container)
|
||||
this.permissions.addOwnerLink('span', container)
|
||||
|
@ -1796,12 +1797,12 @@ U.Map = L.Map.extend({
|
|||
},
|
||||
|
||||
localizeUrl: function (url) {
|
||||
return L.Util.greedyTemplate(url, this.getGeoContext(), true)
|
||||
return U.Utils.greedyTemplate(url, this.getGeoContext(), true)
|
||||
},
|
||||
|
||||
proxyUrl: function (url, ttl) {
|
||||
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),
|
||||
ttl: ttl,
|
||||
})
|
||||
|
|
|
@ -97,7 +97,7 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
|||
},
|
||||
|
||||
getEditableOptions: function () {
|
||||
if (!L.Util.isObject(this.datalayer.options.cluster)) {
|
||||
if (!U.Utils.isObject(this.datalayer.options.cluster)) {
|
||||
this.datalayer.options.cluster = {}
|
||||
}
|
||||
return [
|
||||
|
@ -155,7 +155,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|||
|
||||
initialize: function (datalayer) {
|
||||
this.datalayer = datalayer
|
||||
if (!L.Util.isObject(this.datalayer.options.choropleth)) {
|
||||
if (!U.Utils.isObject(this.datalayer.options.choropleth)) {
|
||||
this.datalayer.options.choropleth = {}
|
||||
}
|
||||
L.FeatureGroup.prototype.initialize.call(
|
||||
|
@ -381,7 +381,7 @@ U.Layer.Heat = L.HeatLayer.extend({
|
|||
},
|
||||
|
||||
getEditableOptions: function () {
|
||||
if (!L.Util.isObject(this.datalayer.options.heat)) {
|
||||
if (!U.Utils.isObject(this.datalayer.options.heat)) {
|
||||
this.datalayer.options.heat = {}
|
||||
}
|
||||
return [
|
||||
|
@ -461,8 +461,8 @@ U.Layer.Heat = L.HeatLayer.extend({
|
|||
this._latlngs[i].alt !== undefined
|
||||
? this._latlngs[i].alt
|
||||
: this._latlngs[i][2] !== undefined
|
||||
? +this._latlngs[i][2]
|
||||
: 1
|
||||
? +this._latlngs[i][2]
|
||||
: 1
|
||||
|
||||
grid[y] = grid[y] || []
|
||||
cell = grid[y][x]
|
||||
|
@ -724,16 +724,16 @@ U.DataLayer = L.Evented.extend({
|
|||
},
|
||||
|
||||
backupData: function () {
|
||||
this._geojson_bk = L.Util.CopyJSON(this._geojson)
|
||||
this._geojson_bk = U.Utils.CopyJSON(this._geojson)
|
||||
},
|
||||
|
||||
reindex: function () {
|
||||
const features = []
|
||||
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 = []
|
||||
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 () {
|
||||
this._backupOptions = L.Util.CopyJSON(this.options)
|
||||
this._backupOptions = U.Utils.CopyJSON(this.options)
|
||||
},
|
||||
|
||||
resetOptions: function () {
|
||||
this.options = L.Util.CopyJSON(this._backupOptions)
|
||||
this.options = U.Utils.CopyJSON(this._backupOptions)
|
||||
},
|
||||
|
||||
setOptions: function (options) {
|
||||
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)
|
||||
},
|
||||
|
||||
|
@ -824,7 +824,7 @@ U.DataLayer = L.Evented.extend({
|
|||
_dataUrl: function () {
|
||||
const template = this.map.options.urls.datalayer_view
|
||||
|
||||
let url = L.Util.template(template, {
|
||||
let url = U.Utils.template(template, {
|
||||
pk: this.umap_id,
|
||||
map_id: this.map.options.umap_id,
|
||||
})
|
||||
|
@ -971,7 +971,7 @@ U.DataLayer = L.Evented.extend({
|
|||
let latlngs
|
||||
|
||||
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++) {
|
||||
this.geojsonToFeatures(features[i])
|
||||
}
|
||||
|
@ -1061,7 +1061,7 @@ U.DataLayer = L.Evented.extend({
|
|||
|
||||
importFromFile: function (f, type) {
|
||||
const reader = new FileReader()
|
||||
type = type || L.Util.detectFileType(f)
|
||||
type = type || U.Utils.detectFileType(f)
|
||||
reader.readAsText(f)
|
||||
reader.onload = (e) => this.importRaw(e.target.result, type)
|
||||
},
|
||||
|
@ -1079,21 +1079,21 @@ U.DataLayer = L.Evented.extend({
|
|||
},
|
||||
|
||||
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,
|
||||
map_id: this.map.options.umap_id,
|
||||
})
|
||||
},
|
||||
|
||||
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,
|
||||
map_id: this.map.options.umap_id,
|
||||
})
|
||||
},
|
||||
|
||||
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,
|
||||
map_id: this.map.options.umap_id,
|
||||
name: name,
|
||||
|
@ -1112,10 +1112,10 @@ U.DataLayer = L.Evented.extend({
|
|||
},
|
||||
|
||||
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 })
|
||||
delete options.id
|
||||
const geojson = L.Util.CopyJSON(this._geojson),
|
||||
const geojson = U.Utils.CopyJSON(this._geojson),
|
||||
datalayer = this.map.createDataLayer(options)
|
||||
datalayer.fromGeoJSON(geojson)
|
||||
return datalayer
|
||||
|
@ -1276,7 +1276,7 @@ U.DataLayer = L.Evented.extend({
|
|||
)
|
||||
popupFieldset.appendChild(builder.build())
|
||||
|
||||
if (!L.Util.isObject(this.options.remoteData)) {
|
||||
if (!U.Utils.isObject(this.options.remoteData)) {
|
||||
this.options.remoteData = {}
|
||||
}
|
||||
const remoteDataFields = [
|
||||
|
@ -1371,7 +1371,7 @@ U.DataLayer = L.Evented.extend({
|
|||
},
|
||||
|
||||
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) {
|
||||
|
@ -1668,6 +1668,6 @@ L.TileLayer.include({
|
|||
},
|
||||
|
||||
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)
|
||||
if (this.isAnonymousMap()) {
|
||||
if (this.options.anonymous_edit_url) {
|
||||
const helpText = `${L._('Secret edit link:')}<br>${
|
||||
this.options.anonymous_edit_url
|
||||
}`
|
||||
const helpText = `${L._('Secret edit link:')}<br>${this.options.anonymous_edit_url
|
||||
}`
|
||||
L.DomUtil.add('p', 'help-text', container, helpText)
|
||||
fields.push([
|
||||
'options.edit_status',
|
||||
|
@ -171,13 +170,13 @@ U.MapPermissions = L.Class.extend({
|
|||
},
|
||||
|
||||
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,
|
||||
})
|
||||
},
|
||||
|
||||
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,
|
||||
})
|
||||
},
|
||||
|
|
|
@ -124,12 +124,12 @@ U.PopupTemplate.Default = L.Class.extend({
|
|||
let center
|
||||
properties = this.feature.extendedProperties()
|
||||
// Resolve properties inside description
|
||||
properties.description = L.Util.greedyTemplate(
|
||||
properties.description = U.Utils.greedyTemplate(
|
||||
this.feature.properties.description || '',
|
||||
properties
|
||||
)
|
||||
content = L.Util.greedyTemplate(template, properties)
|
||||
content = L.Util.toHTML(content, { target: target })
|
||||
content = U.Utils.greedyTemplate(template, properties)
|
||||
content = U.Utils.toHTML(content, { target: target })
|
||||
container.innerHTML = content
|
||||
return container
|
||||
},
|
||||
|
@ -211,7 +211,7 @@ U.PopupTemplate.Table = U.PopupTemplate.BaseWithTitle.extend({
|
|||
for (const key in this.feature.properties) {
|
||||
if (typeof this.feature.properties[key] === 'object' || key === 'name') continue
|
||||
// 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
|
||||
},
|
||||
|
|
|
@ -51,7 +51,7 @@ U.Share = L.Class.extend({
|
|||
L.DomUtil.createCopiableInput(
|
||||
this.container,
|
||||
L._('Link to view the map'),
|
||||
window.location.protocol + L.Util.getBaseUrl()
|
||||
window.location.protocol + U.Utils.getBaseUrl()
|
||||
)
|
||||
|
||||
if (this.map.options.shortUrl) {
|
||||
|
@ -84,7 +84,7 @@ U.Share = L.Class.extend({
|
|||
this.container,
|
||||
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,
|
||||
})
|
||||
const link = L.DomUtil.createLink(
|
||||
|
@ -213,7 +213,7 @@ U.IframeExporter = L.Evented.extend({
|
|||
|
||||
initialize: function (map) {
|
||||
this.map = map
|
||||
this.baseUrl = L.Util.getBaseUrl()
|
||||
this.baseUrl = U.Utils.getBaseUrl()
|
||||
// Use map default, not generic default
|
||||
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 queryString = L.extend({}, this.queryString, options)
|
||||
return `${this.baseUrl}?${L.Util.buildQueryString(queryString)}${currentView}`
|
||||
return `${this.baseUrl}?${U.Utils.buildQueryString(queryString)}${currentView}`
|
||||
},
|
||||
|
||||
build: function () {
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
<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/dompurify/purify.min.js" defer></script>
|
||||
<script src="../vendors/colorbrewer/colorbrewer.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/locatecontrol/L.Control.Locate.min.js' %}"
|
||||
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/simple-statistics/simple-statistics.min.js' %}"
|
||||
defer></script>
|
||||
|
|
Loading…
Reference in a new issue