diff --git a/umap/static/umap/js/modules/index.js b/umap/static/umap/js/modules/index.js new file mode 100644 index 00000000..c9f37269 --- /dev/null +++ b/umap/static/umap/js/modules/index.js @@ -0,0 +1,6 @@ +import URLs from './urls.js' + +// expose the modules to the window.umap global scope +window.umap = { URLs } + +export { URLs } diff --git a/umap/static/umap/js/modules/urls.js b/umap/static/umap/js/modules/urls.js new file mode 100644 index 00000000..66db6b1a --- /dev/null +++ b/umap/static/umap/js/modules/urls.js @@ -0,0 +1,29 @@ +import { Util } from './vendors.js' + +export default class URLs { + constructor(serverUrls) { + this.urls = serverUrls + } + + get(urlName, params) { + if (typeof this[urlName] === 'function') return this[urlName](params) + + if (this.urls.hasOwnProperty(urlName)) { + return Util.template(this.urls[urlName], params) + } else { + throw `Unable to find a URL for route ${urlName}` + } + } + + // Update if map_id is passed, create otherwise. + map_save({ map_id, ...options }) { + if (map_id) return this.get('map_update', { map_id, ...options }) + return this.get('map_create') + } + + // Update the layer if pk is passed, create otherwise. + datalayer_save({ map_id, pk }, ...options) { + if (pk) return this.get('datalayer_update', { map_id, pk }, ...options) + return this.get('datalayer_create', { map_id, pk }, ...options) + } +} diff --git a/umap/static/umap/js/modules/vendors.js b/umap/static/umap/js/modules/vendors.js new file mode 100644 index 00000000..1bf5fa64 --- /dev/null +++ b/umap/static/umap/js/modules/vendors.js @@ -0,0 +1,7 @@ +import { Util } from '../../vendors/leaflet/leaflet-src.esm.js' + +// expose the modules to the window.vendors global scope +window.vendors = { + Util, +} +export { Util } diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index 036ed0bd..88d328e7 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -95,6 +95,7 @@ L.U.Map.include({ // After calling parent initialize, as we are doing initCenter our-selves if (geojson.geometry) this.options.center = this.latLng(geojson.geometry) + this.urls = new window.umap.URLs(this.options.urls) this.ui = new L.U.UI(this._container) this.xhr = new L.U.Xhr(this.ui) @@ -279,10 +280,12 @@ L.U.Map.include({ url.searchParams.delete('edit') history.pushState({}, '', url) } - if (L.Util.queryString('download')) - window.location = L.Util.template(this.options.urls.map_download, { + if (L.Util.queryString('download')) { + const download_url = this.urls.get('map_download', { map_id: this.options.umap_id, }) + window.location = download_url + } }) window.onbeforeunload = () => this.isDirty || null @@ -1085,7 +1088,7 @@ L.U.Map.include({ formData.append('name', this.options.name) formData.append('center', JSON.stringify(this.geometry())) formData.append('settings', JSON.stringify(geojson)) - this.post(this.getSaveUrl(), { + this.post(this.urls.get('map_save', { map_id: this.options.umap_id }), { data: formData, context: this, callback: function (data) { @@ -1161,42 +1164,25 @@ L.U.Map.include({ }, sendEditLink: function () { - const url = L.Util.template(this.options.urls.map_send_edit_link, { - map_id: this.options.umap_id, - }), - input = this.ui._alert.querySelector('input'), - email = input.value + const input = this.ui._alert.querySelector('input') + const email = input.value const formData = new FormData() formData.append('email', email) + + const url = this.urls.get('map_send_edit_link', { map_id: this.options.umap_id }) this.post(url, { data: formData, }) }, - getEditUrl: function () { - return L.Util.template(this.options.urls.map_update, { - map_id: this.options.umap_id, - }) - }, - - getCreateUrl: function () { - return L.Util.template(this.options.urls.map_create) - }, - - getSaveUrl: function () { - return (this.options.umap_id && this.getEditUrl()) || this.getCreateUrl() - }, - star: function () { if (!this.options.umap_id) return this.ui.alert({ content: L._('Please save the map first'), level: 'error', }) - let url = L.Util.template(this.options.urls.map_star, { - map_id: this.options.umap_id, - }) + const url = this.urls.get('map_star', { map_id: this.options.umap_id }) this.post(url, { context: this, callback: function (data) { @@ -1804,9 +1790,7 @@ L.U.Map.include({ del: function () { if (confirm(L._('Are you sure you want to delete this map?'))) { - const url = L.Util.template(this.options.urls.map_delete, { - map_id: this.options.umap_id, - }) + const url = this.urls.get('map_delete', { map_id: this.options.umap_id }) this.post(url) } }, @@ -1815,9 +1799,7 @@ L.U.Map.include({ if ( confirm(L._('Are you sure you want to clone this map and all its datalayers?')) ) { - const url = L.Util.template(this.options.urls.map_clone, { - map_id: this.options.umap_id, - }) + const url = this.urls.get('map_clone', { map_id: this.options.umap_id }) this.post(url) } }, @@ -1952,17 +1934,13 @@ L.U.Map.include({ }, openExternalRouting: function (e) { - const url = this.options.urls.routing - if (url) { - const params = { - lat: e.latlng.lat, - lng: e.latlng.lng, - locale: L.locale, - zoom: this.getZoom(), - } - window.open(L.Util.template(url, params)) - } - return + const url = this.urls.get('routing', { + lat: e.latlng.lat, + lng: e.latlng.lng, + locale: L.locale, + zoom: this.getZoom(), + }) + if (url) window.open(url) }, getMap: function () { diff --git a/umap/static/umap/js/umap.layer.js b/umap/static/umap/js/umap.layer.js index b30241e9..49edb3c4 100644 --- a/umap/static/umap/js/umap.layer.js +++ b/umap/static/umap/js/umap.layer.js @@ -1069,23 +1069,6 @@ L.U.DataLayer = L.Evented.extend({ }) }, - getEditUrl: function () { - return L.Util.template(this.map.options.urls.datalayer_update, { - map_id: this.map.options.umap_id, - pk: this.umap_id, - }) - }, - - getCreateUrl: function () { - return L.Util.template(this.map.options.urls.datalayer_create, { - map_id: this.map.options.umap_id, - }) - }, - - getSaveUrl: function () { - return (this.umap_id && this.getEditUrl()) || this.getCreateUrl() - }, - getColor: function () { return this.options.color || this.map.getOption('color') }, @@ -1577,7 +1560,11 @@ L.U.DataLayer = L.Evented.extend({ // Filename support is shaky, don't do it for now. const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' }) formData.append('geojson', blob) - this.map.post(this.getSaveUrl(), { + const saveUrl = this.map.urls.get('datalayer_save', { + map_id: this.map.options.umap_id, + pk: this.umap_id, + }) + this.map.post(saveUrl, { data: formData, callback: function (data, response) { // Response contains geojson only if save has conflicted and conflicts have diff --git a/umap/static/umap/test/URLs.js b/umap/static/umap/test/URLs.js new file mode 100644 index 00000000..ccd8a91b --- /dev/null +++ b/umap/static/umap/test/URLs.js @@ -0,0 +1,54 @@ +import URLs from '../js/modules/urls.js' + +describe('URLs', () => { + // Mock server URLs that will be used for testing + const mockServerUrls = { + map_create: '/maps/create', + map_update: '/maps/{map_id}/update', + datalayer_create: '/maps/{map_id}/datalayers/create', + datalayer_update: '/maps/{map_id}/datalayers/{pk}/update', + } + + let urls = new URLs(mockServerUrls) + + describe('get()', () => { + it('should throw an error if the urlName does not exist', () => { + expect(() => urls.get('non_existent')).to.throw() + }) + + it('should return the correct templated URL for known urlNames', () => { + expect(urls.get('map_create')).to.be.equal('/maps/create') + expect(urls.get('map_update', { map_id: '123' })).to.be.equal('/maps/123/update') + }) + + it('should return the correct templated URL when provided with parameters', () => { + expect(urls.get('datalayer_update', { map_id: '123', pk: '456' })).to.be.equal( + '/maps/123/datalayers/456/update' + ) + }) + }) + + describe('map_save()', () => { + it('should return the create URL if no map_id is provided', () => { + expect(urls.map_save({})).to.be.equal('/maps/create') + }) + + it('should return the update URL if a map_id is provided', () => { + expect(urls.map_save({ map_id: '123' })).to.be.equal('/maps/123/update') + }) + }) + + describe('datalayer_save()', () => { + it('should return the create URL if no pk is provided', () => { + expect(urls.datalayer_save({ map_id: '123' })).to.be.equal( + '/maps/123/datalayers/create' + ) + }) + + it('should return the update URL if a pk is provided', () => { + expect(urls.datalayer_save({ map_id: '123', pk: '456' })).to.be.equal( + '/maps/123/datalayers/456/update' + ) + }) + }) +}) diff --git a/umap/static/umap/test/Util.js b/umap/static/umap/test/Util.js index 3449fc40..08852926 100644 --- a/umap/static/umap/test/Util.js +++ b/umap/static/umap/test/Util.js @@ -86,7 +86,7 @@ describe('L.Util', function () { it('should handle target option', function () { assert.equal( - L.Util.toHTML('A simple http://osm.org link', {target: 'self'}), + L.Util.toHTML('A simple http://osm.org link', { target: 'self' }), 'A simple http://osm.org link' ) }) @@ -257,8 +257,8 @@ describe('L.Util', function () { it('should accept non ascii chars', function () { assert.equal( L.Util.greedyTemplate('A phrase with a {Accessibilité} and {переменная}.', { - 'Accessibilité': 'value', - 'переменная': 'another', + Accessibilité: 'value', + переменная: 'another', }), 'A phrase with a value and another.' ) @@ -475,70 +475,72 @@ describe('L.Util', function () { }) }) - describe("#normalize()", function () { - - if('should remove accents', function () { - // French é - assert.equal(L.Util.normalize('aéroport'), 'aeroport') - // American é - assert.equal(L.Util.normalize('aéroport'), 'aeroport') - }) + describe('#normalize()', function () { + if ( + ('should remove accents', + function () { + // French é + assert.equal(L.Util.normalize('aéroport'), 'aeroport') + // American é + assert.equal(L.Util.normalize('aéroport'), 'aeroport') + }) + ); }) - describe("#sortFeatures()", function () { + describe('#sortFeatures()', function () { let feat1, feat2, feat3 before(function () { - feat1 = {properties: {}} - feat2 = {properties: {}} - feat3 = {properties: {}} + feat1 = { properties: {} } + feat2 = { properties: {} } + feat3 = { properties: {} } }) it('should sort feature from custom key', function () { - feat1.properties.mykey = "13. foo" - feat2.properties.mykey = "7. foo" - feat3.properties.mykey = "111. foo" - let features = L.Util.sortFeatures([feat1, feat2, feat3], "mykey") + feat1.properties.mykey = '13. foo' + feat2.properties.mykey = '7. foo' + feat3.properties.mykey = '111. foo' + let features = L.Util.sortFeatures([feat1, feat2, feat3], 'mykey') assert.equal(features[0], feat2) assert.equal(features[1], feat1) assert.equal(features[2], feat3) }) it('should sort feature from multiple keys', function () { - feat1.properties.mykey = "13. foo" - feat2.properties.mykey = "111. foo" - feat3.properties.mykey = "111. foo" - feat1.properties.otherkey = "C" - feat2.properties.otherkey = "B" - feat3.properties.otherkey = "A" - let features = L.Util.sortFeatures([feat1, feat2, feat3], "mykey,otherkey") + feat1.properties.mykey = '13. foo' + feat2.properties.mykey = '111. foo' + feat3.properties.mykey = '111. foo' + feat1.properties.otherkey = 'C' + feat2.properties.otherkey = 'B' + feat3.properties.otherkey = 'A' + let features = L.Util.sortFeatures([feat1, feat2, feat3], 'mykey,otherkey') assert.equal(features[0], feat1) assert.equal(features[1], feat3) assert.equal(features[2], feat2) }) it('should sort feature from custom key reverse', function () { - feat1.properties.mykey = "13. foo" - feat2.properties.mykey = "7. foo" - feat3.properties.mykey = "111. foo" - let features = L.Util.sortFeatures([feat1, feat2, feat3], "-mykey") + feat1.properties.mykey = '13. foo' + feat2.properties.mykey = '7. foo' + feat3.properties.mykey = '111. foo' + let features = L.Util.sortFeatures([feat1, feat2, feat3], '-mykey') assert.equal(features[0], feat3) assert.equal(features[1], feat1) assert.equal(features[2], feat2) }) it('should sort feature from multiple keys with reverse', function () { - feat1.properties.mykey = "13. foo" - feat2.properties.mykey = "111. foo" - feat3.properties.mykey = "111. foo" - feat1.properties.otherkey = "C" - feat2.properties.otherkey = "B" - feat3.properties.otherkey = "A" - let features = L.Util.sortFeatures([feat1, feat2, feat3], "mykey,-otherkey") + feat1.properties.mykey = '13. foo' + feat2.properties.mykey = '111. foo' + feat3.properties.mykey = '111. foo' + feat1.properties.otherkey = 'C' + feat2.properties.otherkey = 'B' + feat3.properties.otherkey = 'A' + let features = L.Util.sortFeatures([feat1, feat2, feat3], 'mykey,-otherkey') assert.equal(features[0], feat1) assert.equal(features[1], feat2) assert.equal(features[2], feat3) }) it('should sort feature with space first', function () { - feat1.properties.mykey = "1 foo" - feat2.properties.mykey = "2 foo" - feat3.properties.mykey = "1a foo" - let features = L.Util.sortFeatures([feat1, feat2, feat3], "mykey") + feat1.properties.mykey = '1 foo' + feat2.properties.mykey = '2 foo' + feat3.properties.mykey = '1a foo' + let features = L.Util.sortFeatures([feat1, feat2, feat3], 'mykey') assert.equal(features[0], feat1) assert.equal(features[1], feat3) assert.equal(features[2], feat2) diff --git a/umap/static/umap/test/index.html b/umap/static/umap/test/index.html index 328c5262..f0b0e487 100644 --- a/umap/static/umap/test/index.html +++ b/umap/static/umap/test/index.html @@ -1,3 +1,4 @@ +