chore: move share to modules

This commit is contained in:
Yohan Boniface 2024-07-05 10:10:18 +02:00
parent 741fc70a17
commit 49db1f9aaa
4 changed files with 94 additions and 98 deletions

View file

@ -1,7 +1,41 @@
/* Uses globals for: csv2geojson, osmtogeojson, GeoRSSToGeoJSON (not available as ESM) */ /* Uses globals for: csv2geojson, osmtogeojson, GeoRSSToGeoJSON (not available as ESM) */
import { translate } from './i18n.js' import { translate } from './i18n.js'
export default class Formatter { export const EXPORT_FORMATS = {
geojson: {
formatter: async (map) => JSON.stringify(map.toGeoJSON(), null, 2),
ext: '.geojson',
filetype: 'application/json',
},
gpx: {
formatter: async (map) => await map.formatter.toGPX(map.toGeoJSON()),
ext: '.gpx',
filetype: 'application/gpx+xml',
},
kml: {
formatter: async (map) => await map.formatter.toKML(map.toGeoJSON()),
ext: '.kml',
filetype: 'application/vnd.google-earth.kml+xml',
},
csv: {
formatter: async (map) => {
const table = []
map.eachFeature((feature) => {
const row = feature.toGeoJSON().properties
const center = feature.getCenter()
delete row._umap_options
row.Latitude = center.lat
row.Longitude = center.lng
table.push(row)
})
return csv2geojson.dsv.csvFormat(table)
},
ext: '.csv',
filetype: 'text/csv',
},
}
export class Formatter {
async fromGPX(str) { async fromGPX(str) {
const togeojson = await import('../../vendors/togeojson/togeojson.es.js') const togeojson = await import('../../vendors/togeojson/togeojson.es.js')
return togeojson.gpx(this.toDom(str)) return togeojson.gpx(this.toDom(str))

View file

@ -7,13 +7,14 @@ import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
import Browser from './browser.js' import Browser from './browser.js'
import Caption from './caption.js' import Caption from './caption.js'
import Facets from './facets.js' import Facets from './facets.js'
import Formatter from './formatter.js' import { Formatter } from './formatter.js'
import Help from './help.js' import Help from './help.js'
import Importer from './importer.js' import Importer from './importer.js'
import Orderable from './orderable.js' import Orderable from './orderable.js'
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js' import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
import Rules from './rules.js' import Rules from './rules.js'
import { SCHEMA } from './schema.js' import { SCHEMA } from './schema.js'
import Share from './share.js'
import Slideshow from './slideshow.js' import Slideshow from './slideshow.js'
import { SyncEngine } from './sync/engine.js' import { SyncEngine } from './sync/engine.js'
import Dialog from './ui/dialog.js' import Dialog from './ui/dialog.js'
@ -50,6 +51,7 @@ window.U = {
Rules, Rules,
SCHEMA, SCHEMA,
ServerRequest, ServerRequest,
Share,
Slideshow, Slideshow,
SyncEngine, SyncEngine,
Tooltip, Tooltip,

View file

@ -1,43 +1,11 @@
U.Share = L.Class.extend({ import { EXPORT_FORMATS } from './formatter.js'
EXPORT_TYPES: {
geojson: {
formatter: async (map) => JSON.stringify(map.toGeoJSON(), null, 2),
ext: '.geojson',
filetype: 'application/json',
},
gpx: {
formatter: async (map) => await map.formatter.toGPX(map.toGeoJSON()),
ext: '.gpx',
filetype: 'application/gpx+xml',
},
kml: {
formatter: async (map) => await map.formatter.toKML(map.toGeoJSON()),
ext: '.kml',
filetype: 'application/vnd.google-earth.kml+xml',
},
csv: {
formatter: async (map) => {
const table = []
map.eachFeature((feature) => {
const row = feature.toGeoJSON().properties
const center = feature.getCenter()
delete row._umap_options
row.Latitude = center.lat
row.Longitude = center.lng
table.push(row)
})
return csv2geojson.dsv.csvFormat(table)
},
ext: '.csv',
filetype: 'text/csv',
},
},
initialize: function (map) { export default class Share {
constructor(map) {
this.map = map this.map = map
}, }
build: function () { build() {
this.container = L.DomUtil.create('div', '') this.container = L.DomUtil.create('div', '')
this.title = L.DomUtil.createTitle( this.title = L.DomUtil.createTitle(
this.container, this.container,
@ -63,17 +31,11 @@ U.Share = L.Class.extend({
L.DomUtil.add('h4', '', this.container, L._('Download')) L.DomUtil.add('h4', '', this.container, L._('Download'))
L.DomUtil.add('small', 'label', this.container, L._("Only visible layers' data")) L.DomUtil.add('small', 'label', this.container, L._("Only visible layers' data"))
for (const key in this.EXPORT_TYPES) { for (const format of Object.keys(EXPORT_FORMATS)) {
if (this.EXPORT_TYPES.hasOwnProperty(key)) { L.DomUtil.createButton('download-file', this.container, format, () =>
L.DomUtil.createButton( this.download(format)
'download-file',
this.container,
this.EXPORT_TYPES[key].name || key,
() => this.download(key),
this
) )
} }
}
L.DomUtil.create('div', 'vspace', this.container) L.DomUtil.create('div', 'vspace', this.container)
L.DomUtil.add( L.DomUtil.add(
'small', 'small',
@ -135,7 +97,7 @@ U.Share = L.Class.extend({
for (let i = 0; i < this.map.HIDDABLE_CONTROLS.length; i++) { for (let i = 0; i < this.map.HIDDABLE_CONTROLS.length; i++) {
UIFields.push(`queryString.${this.map.HIDDABLE_CONTROLS[i]}Control`) UIFields.push(`queryString.${this.map.HIDDABLE_CONTROLS[i]}Control`)
} }
const iframeExporter = new U.IframeExporter(this.map) const iframeExporter = new IframeExporter(this.map)
const buildIframeCode = () => { const buildIframeCode = () => {
iframe.textContent = iframeExporter.build() iframe.textContent = iframeExporter.build()
exportUrl.value = window.location.protocol + iframeExporter.buildUrl() exportUrl.value = window.location.protocol + iframeExporter.buildUrl()
@ -149,23 +111,23 @@ U.Share = L.Class.extend({
L._('Embed and link options') L._('Embed and link options')
) )
iframeOptions.appendChild(builder.build()) iframeOptions.appendChild(builder.build())
}, }
open: function () { open() {
if (!this.container) this.build() if (!this.container) this.build()
this.map.panel.open({ content: this.container }) this.map.panel.open({ content: this.container })
}, }
format: async function (mode) { async format(mode) {
const type = this.EXPORT_TYPES[mode] const type = EXPORT_FORMATS[mode]
const content = await type.formatter(this.map) const content = await type.formatter(this.map)
let name = this.map.options.name || 'data' let name = this.map.options.name || 'data'
name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase() name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
const filename = name + type.ext const filename = name + type.ext
return { content, filetype: type.filetype, filename } return { content, filetype: type.filetype, filename }
}, }
download: async function (mode) { async download(mode) {
const { content, filetype, filename } = await this.format(mode) const { content, filetype, filename } = await this.format(mode)
const blob = new Blob([content], { type: filetype }) const blob = new Blob([content], { type: filetype })
window.URL = window.URL || window.webkitURL window.URL = window.URL || window.webkitURL
@ -176,18 +138,21 @@ U.Share = L.Class.extend({
document.body.appendChild(el) document.body.appendChild(el)
el.click() el.click()
document.body.removeChild(el) document.body.removeChild(el)
}, }
}) }
U.IframeExporter = L.Evented.extend({ class IframeExporter {
options: { constructor(map) {
this.map = map
this.baseUrl = U.Utils.getBaseUrl()
this.options = {
includeFullScreenLink: true, includeFullScreenLink: true,
currentView: false, currentView: false,
keepCurrentDatalayers: false, keepCurrentDatalayers: false,
viewCurrentFeature: false, viewCurrentFeature: false,
}, }
queryString: { this.queryString = {
scaleControl: false, scaleControl: false,
miniMap: false, miniMap: false,
scrollWheelZoom: false, scrollWheelZoom: false,
@ -201,25 +166,21 @@ U.IframeExporter = L.Evented.extend({
onLoadPanel: 'none', onLoadPanel: 'none',
captionBar: false, captionBar: false,
captionMenus: true, captionMenus: true,
}, }
dimensions: { this.dimensions = {
width: '100%', width: '100%',
height: '300px', height: '300px',
}, }
initialize: function (map) {
this.map = map
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')
}, }
getMap: function () { getMap() {
return this.map return this.map
}, }
buildUrl: function (options) { buildUrl(options) {
const datalayers = [] const datalayers = []
if (this.options.viewCurrentFeature && this.map.currentFeature) { if (this.options.viewCurrentFeature && this.map.currentFeature) {
this.queryString.feature = this.map.currentFeature.getSlug() this.queryString.feature = this.map.currentFeature.getSlug()
@ -239,9 +200,9 @@ 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}?${U.Utils.buildQueryString(queryString)}${currentView}` return `${this.baseUrl}?${U.Utils.buildQueryString(queryString)}${currentView}`
}, }
build: function () { build() {
const iframeUrl = this.buildUrl() const iframeUrl = this.buildUrl()
let code = `<iframe width="${this.dimensions.width}" height="${this.dimensions.height}" frameborder="0" allowfullscreen allow="geolocation" src="${iframeUrl}"></iframe>` let code = `<iframe width="${this.dimensions.width}" height="${this.dimensions.height}" frameborder="0" allowfullscreen allow="geolocation" src="${iframeUrl}"></iframe>`
if (this.options.includeFullScreenLink) { if (this.options.includeFullScreenLink) {
@ -249,5 +210,5 @@ U.IframeExporter = L.Evented.extend({
code += `<p><a href="${fullUrl}">${L._('See full screen')}</a></p>` code += `<p><a href="${fullUrl}">${L._('See full screen')}</a></p>`
} }
return code return code
}, }
}) }

View file

@ -52,6 +52,5 @@
<script src="{% static 'umap/js/umap.layer.js' %}" defer></script> <script src="{% static 'umap/js/umap.layer.js' %}" defer></script>
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script> <script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
<script src="{% static 'umap/js/umap.tableeditor.js' %}" defer></script> <script src="{% static 'umap/js/umap.tableeditor.js' %}" defer></script>
<script src="{% static 'umap/js/umap.share.js' %}" defer></script>
<script src="{% static 'umap/js/umap.js' %}" defer></script> <script src="{% static 'umap/js/umap.js' %}" defer></script>
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script> <script src="{% static 'umap/js/components/fragment.js' %}" defer></script>