diff --git a/.prettierrc.yaml b/.prettierrc.yaml
new file mode 100644
index 00000000..8d2f2cbf
--- /dev/null
+++ b/.prettierrc.yaml
@@ -0,0 +1,6 @@
+trailingComma: "es5"
+tabWidth: 2
+semi: false
+singleQuote: true
+printWidth: 88
+quoteProps: "consistent"
diff --git a/Makefile b/Makefile
index e0fc00c5..13907259 100644
--- a/Makefile
+++ b/Makefile
@@ -52,3 +52,9 @@ tx_push:
tx push -s
tx_pull:
tx pull
+
+jsdir = umap/static/umap/js/
+filepath = "${jsdir}*.js"
+pretty: ## Apply PrettierJS to all JS files (or specified `filepath`)
+ ./node_modules/prettier/bin-prettier.js --write ${filepath}
+
diff --git a/package-lock.json b/package-lock.json
index 31a046ea..12bf20b4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,6 +40,7 @@
"mocha-phantomjs": "^4.0.1",
"optimist": "~0.4.0",
"phantomjs": "^1.9.18",
+ "prettier": "^2.8.8",
"sinon": "^1.10.3",
"uglify-js": "~3.17.4"
}
@@ -1920,6 +1921,21 @@
"node": ">=0.10.0"
}
},
+ "node_modules/prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
@@ -4071,6 +4087,12 @@
"pinkie": "^2.0.0"
}
},
+ "prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "dev": true
+ },
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
diff --git a/package.json b/package.json
index 46fc25cb..8302ef3a 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"mocha-phantomjs": "^4.0.1",
"optimist": "~0.4.0",
"phantomjs": "^1.9.18",
+ "prettier": "^2.8.8",
"sinon": "^1.10.3",
"uglify-js": "~3.17.4"
},
diff --git a/umap/static/umap/js/umap.autocomplete.js b/umap/static/umap/js/umap.autocomplete.js
index efd9bf03..df7966ab 100644
--- a/umap/static/umap/js/umap.autocomplete.js
+++ b/umap/static/umap/js/umap.autocomplete.js
@@ -1,314 +1,341 @@
L.U.AutoComplete = L.Class.extend({
+ options: {
+ placeholder: 'Start typing...',
+ emptyMessage: 'No result',
+ allowFree: true,
+ minChar: 2,
+ maxResults: 5,
+ },
- options: {
- placeholder: 'Start typing...',
- emptyMessage: 'No result',
- allowFree: true,
- minChar: 2,
- maxResults: 5
- },
+ CACHE: '',
+ RESULTS: [],
- CACHE: '',
- RESULTS: [],
-
- initialize: function (el, options) {
- this.el = el;
- var ui = new L.U.UI(document.querySelector('header'));
- this.xhr = new L.U.Xhr(ui);
- L.setOptions(this, options);
- var CURRENT = null;
- try {
- Object.defineProperty(this, 'CURRENT', {
- get: function () {
- return CURRENT;
- },
- set: function (index) {
- if (typeof index === 'object') {
- index = this.resultToIndex(index);
- }
- CURRENT = index;
- }
- });
- } catch (e) {
- // Hello IE8
- }
- return this;
- },
-
- createInput: function () {
- this.input = L.DomUtil.element('input', {
- type: 'text',
- placeholder: this.options.placeholder,
- autocomplete: 'off',
- className: this.options.className
- }, this.el);
- L.DomEvent.on(this.input, 'keydown', this.onKeyDown, this);
- L.DomEvent.on(this.input, 'keyup', this.onKeyUp, this);
- L.DomEvent.on(this.input, 'blur', this.onBlur, this);
- },
-
- createContainer: function () {
- this.container = L.DomUtil.element('ul', {className: 'umap-autocomplete'}, document.body);
- },
-
- resizeContainer: function()
- {
- var l = this.getLeft(this.input);
- var t = this.getTop(this.input) + this.input.offsetHeight;
- this.container.style.left = l + 'px';
- this.container.style.top = t + 'px';
- var width = this.options.width ? this.options.width : this.input.offsetWidth - 2;
- this.container.style.width = width + 'px';
- },
-
-
- onKeyDown: function (e) {
- switch (e.keyCode) {
- case L.U.Keys.TAB:
- if(this.CURRENT !== null) this.setChoice();
- L.DomEvent.stop(e);
- break;
- case L.U.Keys.ENTER:
- L.DomEvent.stop(e);
- this.setChoice();
- break;
- case L.U.Keys.ESC:
- L.DomEvent.stop(e);
- this.hide();
- break;
- case L.U.Keys.DOWN:
- if(this.RESULTS.length > 0) {
- if(this.CURRENT !== null && this.CURRENT < this.RESULTS.length - 1) { // what if one result?
- this.CURRENT++;
- this.highlight();
- }
- else if(this.CURRENT === null) {
- this.CURRENT = 0;
- this.highlight();
- }
- }
- break;
- case L.U.Keys.UP:
- if(this.CURRENT !== null) {
- L.DomEvent.stop(e);
- }
- if(this.RESULTS.length > 0) {
- if(this.CURRENT > 0) {
- this.CURRENT--;
- this.highlight();
- }
- else if(this.CURRENT === 0) {
- this.CURRENT = null;
- this.highlight();
- }
- }
- break;
- }
- },
-
- onKeyUp: function (e) {
- var special = [
- L.U.Keys.TAB,
- L.U.Keys.ENTER,
- L.U.Keys.LEFT,
- L.U.Keys.RIGHT,
- L.U.Keys.DOWN,
- L.U.Keys.UP,
- L.U.Keys.APPLE,
- L.U.Keys.SHIFT,
- L.U.Keys.ALT,
- L.U.Keys.CTRL
- ];
- if (special.indexOf(e.keyCode) === -1)
- {
- this.search();
- }
- },
-
- onBlur: function () {
- var self = this;
- setTimeout(function () {
- self.hide();
- }, 100);
- },
-
- clear: function () {
- this.RESULTS = [];
- this.CURRENT = null;
- this.CACHE = '';
- this.container.innerHTML = '';
- },
-
- hide: function() {
- this.clear();
- this.container.style.display = 'none';
- this.input.value = '';
- },
-
- setChoice: function (choice) {
- choice = choice || this.RESULTS[this.CURRENT];
- if (choice) {
- this.input.value = choice.item.label;
- this.options.on_select(choice);
- this.displaySelected(choice);
- this.hide();
- if (this.options.callback) {
- L.Util.bind(this.options.callback, this)(choice);
- }
- }
- },
-
- search: function() {
- var val = this.input.value;
- if (val.length < this.options.minChar) {
- this.clear();
- return;
- }
- if( val + '' === this.CACHE + '') return;
- else this.CACHE = val;
- this._do_search(val, function (data) {
- this.handleResults(data.data);
- }, this);
- },
-
- createResult: function (item) {
- var el = L.DomUtil.element('li', {}, this.container);
- el.textContent = item.label;
- var result = {
- item: item,
- el: el
- };
- L.DomEvent.on(el, 'mouseover', function () {
- this.CURRENT = result;
- this.highlight();
- }, this);
- L.DomEvent.on(el, 'mousedown', function () {
- this.setChoice();
- }, this);
- return result;
- },
-
- resultToIndex: function (result) {
- var out = null;
- this.forEach(this.RESULTS, function (item, index) {
- if (item.item.value == result.item.value) {
- out = index;
- return;
- }
- });
- return out;
- },
-
- handleResults: function(data) {
- var self = this;
- this.clear();
- this.container.style.display = 'block';
- this.resizeContainer();
- this.forEach(data, function (item) {
- self.RESULTS.push(self.createResult(item));
- });
- this.CURRENT = 0;
- this.highlight();
- //TODO manage no results
- },
-
- highlight: function () {
- var self = this;
- this.forEach(this.RESULTS, function (result, index) {
- if (index === self.CURRENT) L.DomUtil.addClass(result.el, 'on');
- else L.DomUtil.removeClass(result.el, 'on');
- });
- },
-
- getLeft: function (el) {
- var tmp = el.offsetLeft;
- el = el.offsetParent;
- while(el) {
- tmp += el.offsetLeft;
- el = el.offsetParent;
- }
- return tmp;
- },
-
- getTop: function (el) {
- var tmp = el.offsetTop;
- el = el.offsetParent;
- while(el) {
- tmp += el.offsetTop;
- el = el.offsetParent;
- }
- return tmp;
- },
-
- forEach: function (els, callback) {
- Array.prototype.forEach.call(els, callback);
+ initialize: function (el, options) {
+ this.el = el
+ var ui = new L.U.UI(document.querySelector('header'))
+ this.xhr = new L.U.Xhr(ui)
+ L.setOptions(this, options)
+ var CURRENT = null
+ try {
+ Object.defineProperty(this, 'CURRENT', {
+ get: function () {
+ return CURRENT
+ },
+ set: function (index) {
+ if (typeof index === 'object') {
+ index = this.resultToIndex(index)
+ }
+ CURRENT = index
+ },
+ })
+ } catch (e) {
+ // Hello IE8
}
+ return this
+ },
-});
+ createInput: function () {
+ this.input = L.DomUtil.element(
+ 'input',
+ {
+ type: 'text',
+ placeholder: this.options.placeholder,
+ autocomplete: 'off',
+ className: this.options.className,
+ },
+ this.el
+ )
+ L.DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
+ L.DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
+ L.DomEvent.on(this.input, 'blur', this.onBlur, this)
+ },
+ createContainer: function () {
+ this.container = L.DomUtil.element(
+ 'ul',
+ { className: 'umap-autocomplete' },
+ document.body
+ )
+ },
+
+ resizeContainer: function () {
+ var l = this.getLeft(this.input)
+ var t = this.getTop(this.input) + this.input.offsetHeight
+ this.container.style.left = l + 'px'
+ this.container.style.top = t + 'px'
+ var width = this.options.width ? this.options.width : this.input.offsetWidth - 2
+ this.container.style.width = width + 'px'
+ },
+
+ onKeyDown: function (e) {
+ switch (e.keyCode) {
+ case L.U.Keys.TAB:
+ if (this.CURRENT !== null) this.setChoice()
+ L.DomEvent.stop(e)
+ break
+ case L.U.Keys.ENTER:
+ L.DomEvent.stop(e)
+ this.setChoice()
+ break
+ case L.U.Keys.ESC:
+ L.DomEvent.stop(e)
+ this.hide()
+ break
+ case L.U.Keys.DOWN:
+ if (this.RESULTS.length > 0) {
+ if (this.CURRENT !== null && this.CURRENT < this.RESULTS.length - 1) {
+ // what if one result?
+ this.CURRENT++
+ this.highlight()
+ } else if (this.CURRENT === null) {
+ this.CURRENT = 0
+ this.highlight()
+ }
+ }
+ break
+ case L.U.Keys.UP:
+ if (this.CURRENT !== null) {
+ L.DomEvent.stop(e)
+ }
+ if (this.RESULTS.length > 0) {
+ if (this.CURRENT > 0) {
+ this.CURRENT--
+ this.highlight()
+ } else if (this.CURRENT === 0) {
+ this.CURRENT = null
+ this.highlight()
+ }
+ }
+ break
+ }
+ },
+
+ onKeyUp: function (e) {
+ var special = [
+ L.U.Keys.TAB,
+ L.U.Keys.ENTER,
+ L.U.Keys.LEFT,
+ L.U.Keys.RIGHT,
+ L.U.Keys.DOWN,
+ L.U.Keys.UP,
+ L.U.Keys.APPLE,
+ L.U.Keys.SHIFT,
+ L.U.Keys.ALT,
+ L.U.Keys.CTRL,
+ ]
+ if (special.indexOf(e.keyCode) === -1) {
+ this.search()
+ }
+ },
+
+ onBlur: function () {
+ var self = this
+ setTimeout(function () {
+ self.hide()
+ }, 100)
+ },
+
+ clear: function () {
+ this.RESULTS = []
+ this.CURRENT = null
+ this.CACHE = ''
+ this.container.innerHTML = ''
+ },
+
+ hide: function () {
+ this.clear()
+ this.container.style.display = 'none'
+ this.input.value = ''
+ },
+
+ setChoice: function (choice) {
+ choice = choice || this.RESULTS[this.CURRENT]
+ if (choice) {
+ this.input.value = choice.item.label
+ this.options.on_select(choice)
+ this.displaySelected(choice)
+ this.hide()
+ if (this.options.callback) {
+ L.Util.bind(this.options.callback, this)(choice)
+ }
+ }
+ },
+
+ search: function () {
+ var val = this.input.value
+ if (val.length < this.options.minChar) {
+ this.clear()
+ return
+ }
+ if (val + '' === this.CACHE + '') return
+ else this.CACHE = val
+ this._do_search(
+ val,
+ function (data) {
+ this.handleResults(data.data)
+ },
+ this
+ )
+ },
+
+ createResult: function (item) {
+ var el = L.DomUtil.element('li', {}, this.container)
+ el.textContent = item.label
+ var result = {
+ item: item,
+ el: el,
+ }
+ L.DomEvent.on(
+ el,
+ 'mouseover',
+ function () {
+ this.CURRENT = result
+ this.highlight()
+ },
+ this
+ )
+ L.DomEvent.on(
+ el,
+ 'mousedown',
+ function () {
+ this.setChoice()
+ },
+ this
+ )
+ return result
+ },
+
+ resultToIndex: function (result) {
+ var out = null
+ this.forEach(this.RESULTS, function (item, index) {
+ if (item.item.value == result.item.value) {
+ out = index
+ return
+ }
+ })
+ return out
+ },
+
+ handleResults: function (data) {
+ var self = this
+ this.clear()
+ this.container.style.display = 'block'
+ this.resizeContainer()
+ this.forEach(data, function (item) {
+ self.RESULTS.push(self.createResult(item))
+ })
+ this.CURRENT = 0
+ this.highlight()
+ //TODO manage no results
+ },
+
+ highlight: function () {
+ var self = this
+ this.forEach(this.RESULTS, function (result, index) {
+ if (index === self.CURRENT) L.DomUtil.addClass(result.el, 'on')
+ else L.DomUtil.removeClass(result.el, 'on')
+ })
+ },
+
+ getLeft: function (el) {
+ var tmp = el.offsetLeft
+ el = el.offsetParent
+ while (el) {
+ tmp += el.offsetLeft
+ el = el.offsetParent
+ }
+ return tmp
+ },
+
+ getTop: function (el) {
+ var tmp = el.offsetTop
+ el = el.offsetParent
+ while (el) {
+ tmp += el.offsetTop
+ el = el.offsetParent
+ }
+ return tmp
+ },
+
+ forEach: function (els, callback) {
+ Array.prototype.forEach.call(els, callback)
+ },
+})
L.U.AutoComplete.Ajax = L.U.AutoComplete.extend({
+ initialize: function (el, options) {
+ L.U.AutoComplete.prototype.initialize.call(this, el, options)
+ if (!this.el) return this
+ this.createInput()
+ this.createContainer()
+ this.selected_container = this.initSelectedContainer()
+ },
- initialize: function (el, options) {
- L.U.AutoComplete.prototype.initialize.call(this, el, options);
- if (!this.el) return this;
- this.createInput();
- this.createContainer();
- this.selected_container = this.initSelectedContainer();
- },
-
- optionToResult: function (option) {
- return {
- value: option.value,
- label: option.innerHTML
- };
- },
-
- _do_search: function (val, callback, context) {
- val = val.toLowerCase();
- this.xhr.get('/agnocomplete/AutocompleteUser/?q=' + encodeURIComponent(val), {callback: callback, context: context || this});
+ optionToResult: function (option) {
+ return {
+ value: option.value,
+ label: option.innerHTML,
}
+ },
-});
+ _do_search: function (val, callback, context) {
+ val = val.toLowerCase()
+ this.xhr.get('/agnocomplete/AutocompleteUser/?q=' + encodeURIComponent(val), {
+ callback: callback,
+ context: context || this,
+ })
+ },
+})
L.U.AutoComplete.Ajax.SelectMultiple = L.U.AutoComplete.Ajax.extend({
+ initSelectedContainer: function () {
+ return L.DomUtil.after(
+ this.input,
+ L.DomUtil.element('ul', { className: 'umap-multiresult' })
+ )
+ },
- initSelectedContainer: function () {
- return L.DomUtil.after(this.input, L.DomUtil.element('ul', {className: 'umap-multiresult'}));
- },
-
- displaySelected: function (result) {
- var result_el = L.DomUtil.element('li', {}, this.selected_container);
- result_el.textContent = result.item.label;
- var close = L.DomUtil.element('span', {className: 'close'}, result_el);
- close.textContent = '×';
- L.DomEvent.on(close, 'click', function () {
- this.selected_container.removeChild(result_el);
- this.options.on_unselect(result);
- }, this);
- this.hide();
- }
-
-});
-
+ displaySelected: function (result) {
+ var result_el = L.DomUtil.element('li', {}, this.selected_container)
+ result_el.textContent = result.item.label
+ var close = L.DomUtil.element('span', { className: 'close' }, result_el)
+ close.textContent = '×'
+ L.DomEvent.on(
+ close,
+ 'click',
+ function () {
+ this.selected_container.removeChild(result_el)
+ this.options.on_unselect(result)
+ },
+ this
+ )
+ this.hide()
+ },
+})
L.U.AutoComplete.Ajax.Select = L.U.AutoComplete.Ajax.extend({
+ initSelectedContainer: function () {
+ return L.DomUtil.after(
+ this.input,
+ L.DomUtil.element('div', { className: 'umap-singleresult' })
+ )
+ },
- initSelectedContainer: function () {
- return L.DomUtil.after(this.input, L.DomUtil.element('div', {className: 'umap-singleresult'}));
- },
-
- displaySelected: function (result) {
- var result_el = L.DomUtil.element('div', {}, this.selected_container);
- result_el.textContent = result.item.label;
- var close = L.DomUtil.element('span', {className: 'close'}, result_el);
- close.textContent = '×';
- this.input.style.display = 'none';
- L.DomEvent.on(close, 'click', function () {
- this.selected_container.innerHTML = '';
- this.input.style.display = 'block';
- }, this);
- this.hide();
- }
-
-});
+ displaySelected: function (result) {
+ var result_el = L.DomUtil.element('div', {}, this.selected_container)
+ result_el.textContent = result.item.label
+ var close = L.DomUtil.element('span', { className: 'close' }, result_el)
+ close.textContent = '×'
+ this.input.style.display = 'none'
+ L.DomEvent.on(
+ close,
+ 'click',
+ function () {
+ this.selected_container.innerHTML = ''
+ this.input.style.display = 'block'
+ },
+ this
+ )
+ this.hide()
+ },
+})
diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js
index 66d4647e..17e431dc 100644
--- a/umap/static/umap/js/umap.controls.js
+++ b/umap/static/umap/js/umap.controls.js
@@ -1,1310 +1,1360 @@
L.U.BaseAction = L.ToolbarAction.extend({
-
- initialize: function (map) {
- this.map = map;
- this.options.toolbarIcon = {
- className: this.options.className,
- tooltip: this.options.tooltip
- };
- L.ToolbarAction.prototype.initialize.call(this);
- if (this.options.helpMenu && !this.map.helpMenuActions[this.options.className]) this.map.helpMenuActions[this.options.className] = this;
+ initialize: function (map) {
+ this.map = map
+ this.options.toolbarIcon = {
+ className: this.options.className,
+ tooltip: this.options.tooltip,
}
-
-});
+ L.ToolbarAction.prototype.initialize.call(this)
+ if (this.options.helpMenu && !this.map.helpMenuActions[this.options.className])
+ this.map.helpMenuActions[this.options.className] = this
+ },
+})
L.U.ImportAction = L.U.BaseAction.extend({
+ options: {
+ helpMenu: true,
+ className: 'upload-data dark',
+ tooltip: L._('Import data') + ' (Ctrl+I)',
+ },
- options: {
- helpMenu: true,
- className: 'upload-data dark',
- tooltip: L._('Import data') + ' (Ctrl+I)'
- },
-
- addHooks: function () {
- this.map.importPanel();
- }
-
-});
+ addHooks: function () {
+ this.map.importPanel()
+ },
+})
L.U.EditPropertiesAction = L.U.BaseAction.extend({
+ options: {
+ helpMenu: true,
+ className: 'update-map-settings dark',
+ tooltip: L._('Edit map settings'),
+ },
- options: {
- helpMenu: true,
- className: 'update-map-settings dark',
- tooltip: L._('Edit map settings')
- },
-
- addHooks: function () {
- this.map.edit();
- }
-
-});
+ addHooks: function () {
+ this.map.edit()
+ },
+})
L.U.ChangeTileLayerAction = L.U.BaseAction.extend({
+ options: {
+ helpMenu: true,
+ className: 'dark update-map-tilelayers',
+ tooltip: L._('Change tilelayers'),
+ },
- options: {
- helpMenu: true,
- className: 'dark update-map-tilelayers',
- tooltip: L._('Change tilelayers')
- },
-
- addHooks: function () {
- this.map.updateTileLayers();
- }
-
-});
+ addHooks: function () {
+ this.map.updateTileLayers()
+ },
+})
L.U.ManageDatalayersAction = L.U.BaseAction.extend({
+ options: {
+ className: 'dark manage-datalayers',
+ tooltip: L._('Manage layers'),
+ },
- options: {
- className: 'dark manage-datalayers',
- tooltip: L._('Manage layers')
- },
-
- addHooks: function () {
- this.map.manageDatalayers();
- }
-
-});
+ addHooks: function () {
+ this.map.manageDatalayers()
+ },
+})
L.U.UpdateExtentAction = L.U.BaseAction.extend({
+ options: {
+ className: 'update-map-extent dark',
+ tooltip: L._('Save this center and zoom'),
+ },
- options: {
- className: 'update-map-extent dark',
- tooltip: L._('Save this center and zoom')
- },
-
- addHooks: function () {
- this.map.updateExtent();
- }
-
-});
+ addHooks: function () {
+ this.map.updateExtent()
+ },
+})
L.U.UpdatePermsAction = L.U.BaseAction.extend({
+ options: {
+ className: 'update-map-permissions dark',
+ tooltip: L._('Update permissions and editors'),
+ },
- options: {
- className: 'update-map-permissions dark',
- tooltip: L._('Update permissions and editors')
- },
-
- addHooks: function () {
- this.map.permissions.edit();
- }
-
-});
+ addHooks: function () {
+ this.map.permissions.edit()
+ },
+})
L.U.DrawMarkerAction = L.U.BaseAction.extend({
+ options: {
+ helpMenu: true,
+ className: 'umap-draw-marker dark',
+ tooltip: L._('Draw a marker'),
+ },
- options: {
- helpMenu: true,
- className: 'umap-draw-marker dark',
- tooltip: L._('Draw a marker')
- },
-
- addHooks: function () {
- this.map.startMarker();
- }
-
-});
+ addHooks: function () {
+ this.map.startMarker()
+ },
+})
L.U.DrawPolylineAction = L.U.BaseAction.extend({
+ options: {
+ helpMenu: true,
+ className: 'umap-draw-polyline dark',
+ tooltip: L._('Draw a polyline'),
+ },
- options: {
- helpMenu: true,
- className: 'umap-draw-polyline dark',
- tooltip: L._('Draw a polyline')
- },
-
- addHooks: function () {
- this.map.startPolyline();
- }
-
-});
+ addHooks: function () {
+ this.map.startPolyline()
+ },
+})
L.U.DrawPolygonAction = L.U.BaseAction.extend({
+ options: {
+ helpMenu: true,
+ className: 'umap-draw-polygon dark',
+ tooltip: L._('Draw a polygon'),
+ },
- options: {
- helpMenu: true,
- className: 'umap-draw-polygon dark',
- tooltip: L._('Draw a polygon')
- },
-
- addHooks: function () {
- this.map.startPolygon();
- }
-
-});
+ addHooks: function () {
+ this.map.startPolygon()
+ },
+})
L.U.AddPolylineShapeAction = L.U.BaseAction.extend({
+ options: {
+ className: 'umap-draw-polyline-multi dark',
+ tooltip: L._('Add a line to the current multi'),
+ },
- options: {
- className: 'umap-draw-polyline-multi dark',
- tooltip: L._('Add a line to the current multi')
- },
-
- addHooks: function () {
- this.map.editedFeature.editor.newShape();
- }
-
-});
+ addHooks: function () {
+ this.map.editedFeature.editor.newShape()
+ },
+})
L.U.AddPolygonShapeAction = L.U.AddPolylineShapeAction.extend({
-
- options: {
- className: 'umap-draw-polygon-multi dark',
- tooltip: L._('Add a polygon to the current multi')
- }
-
-});
+ options: {
+ className: 'umap-draw-polygon-multi dark',
+ tooltip: L._('Add a polygon to the current multi'),
+ },
+})
L.U.BaseFeatureAction = L.ToolbarAction.extend({
+ initialize: function (map, feature, latlng) {
+ this.map = map
+ this.feature = feature
+ this.latlng = latlng
+ L.ToolbarAction.prototype.initialize.call(this)
+ this.postInit()
+ },
- initialize: function (map, feature, latlng) {
- this.map = map;
- this.feature = feature;
- this.latlng = latlng;
- L.ToolbarAction.prototype.initialize.call(this);
- this.postInit();
- },
+ postInit: function () {},
- postInit: function () {},
+ hideToolbar: function () {
+ this.map.removeLayer(this.toolbar)
+ },
- hideToolbar: function () {
- this.map.removeLayer(this.toolbar);
- },
-
- addHooks: function () {
- this.onClick({latlng: this.latlng});
- this.hideToolbar();
- }
-
-});
+ addHooks: function () {
+ this.onClick({ latlng: this.latlng })
+ this.hideToolbar()
+ },
+})
L.U.CreateHoleAction = L.U.BaseFeatureAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-new-hole',
- tooltip: L._('Start a hole here')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-new-hole',
+ tooltip: L._('Start a hole here'),
},
+ },
- onClick: function (e) {
- this.feature.startHole(e);
- }
-
-});
+ onClick: function (e) {
+ this.feature.startHole(e)
+ },
+})
L.U.ToggleEditAction = L.U.BaseFeatureAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-toggle-edit',
- tooltip: L._('Toggle edit mode (Shift+Click)')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-toggle-edit',
+ tooltip: L._('Toggle edit mode (Shift+Click)'),
},
+ },
- onClick: function (e) {
- if (this.feature._toggleEditing) this.feature._toggleEditing(e); // Path
- else this.feature.edit(e); // Marker
- }
-
-});
+ onClick: function (e) {
+ if (this.feature._toggleEditing) this.feature._toggleEditing(e) // Path
+ else this.feature.edit(e) // Marker
+ },
+})
L.U.DeleteFeatureAction = L.U.BaseFeatureAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-delete-all',
- tooltip: L._('Delete this feature')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-delete-all',
+ tooltip: L._('Delete this feature'),
},
+ },
- postInit: function () {
- if (!this.feature.isMulti()) this.options.toolbarIcon.className = 'umap-delete-one-of-one';
- },
+ postInit: function () {
+ if (!this.feature.isMulti())
+ this.options.toolbarIcon.className = 'umap-delete-one-of-one'
+ },
- onClick: function (e) {
- this.feature.confirmDelete(e);
- }
-
-});
+ onClick: function (e) {
+ this.feature.confirmDelete(e)
+ },
+})
L.U.DeleteShapeAction = L.U.BaseFeatureAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-delete-one-of-multi',
- tooltip: L._('Delete this shape')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-delete-one-of-multi',
+ tooltip: L._('Delete this shape'),
},
+ },
- onClick: function (e) {
- this.feature.enableEdit().deleteShapeAt(e.latlng);
- }
-
-});
+ onClick: function (e) {
+ this.feature.enableEdit().deleteShapeAt(e.latlng)
+ },
+})
L.U.ExtractShapeFromMultiAction = L.U.BaseFeatureAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-extract-shape-from-multi',
- tooltip: L._('Extract shape to separate feature')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-extract-shape-from-multi',
+ tooltip: L._('Extract shape to separate feature'),
},
+ },
- onClick: function (e) {
- this.feature.isolateShape(e.latlng);
- }
-
-});
+ onClick: function (e) {
+ this.feature.isolateShape(e.latlng)
+ },
+})
L.U.BaseVertexAction = L.U.BaseFeatureAction.extend({
-
- initialize: function (map, feature, latlng, vertex) {
- this.vertex = vertex;
- L.U.BaseFeatureAction.prototype.initialize.call(this, map, feature, latlng);
- }
-
-});
+ initialize: function (map, feature, latlng, vertex) {
+ this.vertex = vertex
+ L.U.BaseFeatureAction.prototype.initialize.call(this, map, feature, latlng)
+ },
+})
L.U.DeleteVertexAction = L.U.BaseVertexAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-delete-vertex',
- tooltip: L._('Delete this vertex (Alt+Click)')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-delete-vertex',
+ tooltip: L._('Delete this vertex (Alt+Click)'),
},
+ },
- onClick: function () {
- this.vertex.delete();
- }
-
-});
+ onClick: function () {
+ this.vertex.delete()
+ },
+})
L.U.SplitLineAction = L.U.BaseVertexAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-split-line',
- tooltip: L._('Split line')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-split-line',
+ tooltip: L._('Split line'),
},
+ },
- onClick: function () {
- this.vertex.split();
- }
-
-});
+ onClick: function () {
+ this.vertex.split()
+ },
+})
L.U.ContinueLineAction = L.U.BaseVertexAction.extend({
-
- options: {
- toolbarIcon: {
- className: 'umap-continue-line',
- tooltip: L._('Continue line')
- }
+ options: {
+ toolbarIcon: {
+ className: 'umap-continue-line',
+ tooltip: L._('Continue line'),
},
+ },
- onClick: function () {
- this.vertex.continue();
- }
-
-});
+ onClick: function () {
+ this.vertex.continue()
+ },
+})
// Leaflet.Toolbar doesn't allow twice same toolbar class…
-L.U.SettingsToolbar = L.Toolbar.Control.extend({});
+L.U.SettingsToolbar = L.Toolbar.Control.extend({})
L.U.DrawToolbar = L.Toolbar.Control.extend({
+ initialize: function (options) {
+ L.Toolbar.Control.prototype.initialize.call(this, options)
+ this.map = this.options.map
+ this.map.on('seteditedfeature', this.redraw, this)
+ },
- initialize: function (options) {
- L.Toolbar.Control.prototype.initialize.call(this, options);
- this.map = this.options.map;
- this.map.on('seteditedfeature', this.redraw, this);
- },
-
- appendToContainer: function (container) {
- this.options.actions = [];
- if (this.map.options.enableMarkerDraw) {
- this.options.actions.push(L.U.DrawMarkerAction);
- }
- if (this.map.options.enablePolylineDraw) {
- this.options.actions.push(L.U.DrawPolylineAction);
- if (this.map.editedFeature && this.map.editedFeature instanceof L.U.Polyline) {
- this.options.actions.push(L.U.AddPolylineShapeAction);
- }
- }
- if (this.map.options.enablePolygonDraw) {
- this.options.actions.push(L.U.DrawPolygonAction);
- if (this.map.editedFeature && this.map.editedFeature instanceof L.U.Polygon) {
- this.options.actions.push(L.U.AddPolygonShapeAction);
- }
- }
- L.Toolbar.Control.prototype.appendToContainer.call(this, container);
- },
-
- redraw: function () {
- var container = this._control.getContainer();
- container.innerHTML = '';
- this.appendToContainer(container);
+ appendToContainer: function (container) {
+ this.options.actions = []
+ if (this.map.options.enableMarkerDraw) {
+ this.options.actions.push(L.U.DrawMarkerAction)
}
+ if (this.map.options.enablePolylineDraw) {
+ this.options.actions.push(L.U.DrawPolylineAction)
+ if (this.map.editedFeature && this.map.editedFeature instanceof L.U.Polyline) {
+ this.options.actions.push(L.U.AddPolylineShapeAction)
+ }
+ }
+ if (this.map.options.enablePolygonDraw) {
+ this.options.actions.push(L.U.DrawPolygonAction)
+ if (this.map.editedFeature && this.map.editedFeature instanceof L.U.Polygon) {
+ this.options.actions.push(L.U.AddPolygonShapeAction)
+ }
+ }
+ L.Toolbar.Control.prototype.appendToContainer.call(this, container)
+ },
-});
-
+ redraw: function () {
+ var container = this._control.getContainer()
+ container.innerHTML = ''
+ this.appendToContainer(container)
+ },
+})
L.U.EditControl = L.Control.extend({
+ options: {
+ position: 'topright',
+ },
- options: {
- position: 'topright'
- },
+ onAdd: function (map) {
+ var container = L.DomUtil.create('div', 'leaflet-control-edit-enable umap-control'),
+ edit = L.DomUtil.create('a', '', container)
+ edit.href = '#'
+ edit.title = L._('Enable editing') + ' (Ctrl+E)'
- onAdd: function (map) {
- var container = L.DomUtil.create('div', 'leaflet-control-edit-enable umap-control'),
- edit = L.DomUtil.create('a', '', container);
- edit.href = '#';
- edit.title = L._('Enable editing') + ' (Ctrl+E)';
-
- L.DomEvent
- .addListener(edit, 'click', L.DomEvent.stop)
- .addListener(edit, 'click', map.enableEdit, map);
- return container;
- }
-
-});
+ L.DomEvent.addListener(edit, 'click', L.DomEvent.stop).addListener(
+ edit,
+ 'click',
+ map.enableEdit,
+ map
+ )
+ return container
+ },
+})
/* Share control */
L.Control.Embed = L.Control.extend({
+ options: {
+ position: 'topleft',
+ },
- options: {
- position: 'topleft'
- },
+ onAdd: function (map) {
+ var container = L.DomUtil.create('div', 'leaflet-control-embed umap-control')
- onAdd: function (map) {
- var container = L.DomUtil.create('div', 'leaflet-control-embed umap-control');
+ var link = L.DomUtil.create('a', '', container)
+ link.href = '#'
+ link.title = L._('Embed and share this map')
- var link = L.DomUtil.create('a', '', container);
- link.href = '#';
- link.title = L._('Embed and share this map');
+ L.DomEvent.on(link, 'click', L.DomEvent.stop)
+ .on(link, 'click', map.renderShareBox, map)
+ .on(link, 'dblclick', L.DomEvent.stopPropagation)
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', map.renderShareBox, map)
- .on(link, 'dblclick', L.DomEvent.stopPropagation);
-
- return container;
- }
-});
+ return container
+ },
+})
L.U.MoreControls = L.Control.extend({
+ options: {
+ position: 'topleft',
+ },
- options: {
- position: 'topleft'
- },
+ onAdd: function () {
+ var container = L.DomUtil.create('div', ''),
+ more = L.DomUtil.create('a', 'umap-control-more umap-control-text', container),
+ less = L.DomUtil.create('a', 'umap-control-less umap-control-text', container)
+ more.href = '#'
+ more.title = L._('More controls')
- onAdd: function () {
- var container = L.DomUtil.create('div', ''),
- more = L.DomUtil.create('a', 'umap-control-more umap-control-text', container),
- less = L.DomUtil.create('a', 'umap-control-less umap-control-text', container);
- more.href = '#';
- more.title = L._('More controls');
+ L.DomEvent.on(more, 'click', L.DomEvent.stop).on(more, 'click', this.toggle, this)
- L.DomEvent
- .on(more, 'click', L.DomEvent.stop)
- .on(more, 'click', this.toggle, this);
+ less.href = '#'
+ less.title = L._('Hide controls')
- less.href = '#';
- less.title = L._('Hide controls');
+ L.DomEvent.on(less, 'click', L.DomEvent.stop).on(less, 'click', this.toggle, this)
- L.DomEvent
- .on(less, 'click', L.DomEvent.stop)
- .on(less, 'click', this.toggle, this);
-
- return container;
- },
-
- toggle: function () {
- var pos = this.getPosition(),
- corner = this._map._controlCorners[pos],
- className = 'umap-more-controls';
- if (L.DomUtil.hasClass(corner, className)) L.DomUtil.removeClass(corner, className);
- else L.DomUtil.addClass(corner, className);
- }
-
-});
+ return container
+ },
+ toggle: function () {
+ var pos = this.getPosition(),
+ corner = this._map._controlCorners[pos],
+ className = 'umap-more-controls'
+ if (L.DomUtil.hasClass(corner, className)) L.DomUtil.removeClass(corner, className)
+ else L.DomUtil.addClass(corner, className)
+ },
+})
L.U.PermanentCreditsControl = L.Control.extend({
+ options: {
+ position: 'bottomleft',
+ },
- options: {
- position: 'bottomleft'
- },
+ initialize: function (map, options) {
+ this.map = map
+ L.Control.prototype.initialize.call(this, options)
+ },
- initialize: function (map, options) {
- this.map = map;
- L.Control.prototype.initialize.call(this, options);
- },
+ onAdd: function () {
+ var paragraphContainer = L.DomUtil.create(
+ 'div',
+ 'umap-permanent-credits-container'
+ ),
+ creditsParagraph = L.DomUtil.create('p', '', paragraphContainer)
- onAdd: function () {
- var paragraphContainer = L.DomUtil.create('div', 'umap-permanent-credits-container'),
- creditsParagraph = L.DomUtil.create('p', '', paragraphContainer);
+ this.paragraphContainer = paragraphContainer
+ this.setCredits()
+ this.setBackground()
- this.paragraphContainer = paragraphContainer;
- this.setCredits();
- this.setBackground();
+ return paragraphContainer
+ },
- return paragraphContainer;
- },
+ setCredits: function () {
+ this.paragraphContainer.innerHTML = L.Util.toHTML(this.map.options.permanentCredit)
+ },
- setCredits: function () {
- this.paragraphContainer.innerHTML = L.Util.toHTML(this.map.options.permanentCredit);
- },
-
- setBackground: function () {
- if (this.map.options.permanentCreditBackground) {
- this.paragraphContainer.style.backgroundColor = '#FFFFFFB0';
- } else {
- this.paragraphContainer.style.backgroundColor = '';
- }
+ setBackground: function () {
+ if (this.map.options.permanentCreditBackground) {
+ this.paragraphContainer.style.backgroundColor = '#FFFFFFB0'
+ } else {
+ this.paragraphContainer.style.backgroundColor = ''
}
-
-});
-
+ },
+})
L.U.DataLayersControl = L.Control.extend({
+ options: {
+ position: 'topleft',
+ },
- options: {
- position: 'topleft'
- },
+ labels: {
+ zoomToLayer: L._('Zoom to layer extent'),
+ toggleLayer: L._('Show/hide layer'),
+ editLayer: L._('Edit'),
+ },
- labels: {
- zoomToLayer: L._('Zoom to layer extent'),
- toggleLayer: L._('Show/hide layer'),
- editLayer: L._('Edit')
- },
+ initialize: function (map, options) {
+ this.map = map
+ L.Control.prototype.initialize.call(this, options)
+ },
- initialize: function (map, options) {
- this.map = map;
- L.Control.prototype.initialize.call(this, options);
- },
+ _initLayout: function (map) {
+ var container = (this._container = L.DomUtil.create(
+ 'div',
+ 'leaflet-control-browse umap-control'
+ )),
+ actions = L.DomUtil.create('div', 'umap-browse-actions', container)
+ this._datalayers_container = L.DomUtil.create(
+ 'ul',
+ 'umap-browse-datalayers',
+ actions
+ )
- _initLayout: function (map) {
- var container = this._container = L.DomUtil.create('div', 'leaflet-control-browse umap-control'),
- actions = L.DomUtil.create('div', 'umap-browse-actions', container);
- this._datalayers_container = L.DomUtil.create('ul', 'umap-browse-datalayers', actions);
+ var link = L.DomUtil.create('a', 'umap-browse-link', actions)
+ link.href = '#'
+ link.title = link.textContent = L._('Browse data')
- var link = L.DomUtil.create('a', 'umap-browse-link', actions);
- link.href = '#';
- link.title = link.textContent = L._('Browse data');
+ var toggle = L.DomUtil.create('a', 'umap-browse-toggle', container)
+ toggle.href = '#'
+ toggle.title = L._('See data layers')
- var toggle = L.DomUtil.create('a', 'umap-browse-toggle', container);
- toggle.href = '#';
- toggle.title = L._('See data layers')
+ L.DomEvent.on(toggle, 'click', L.DomEvent.stop)
- L.DomEvent
- .on(toggle, 'click', L.DomEvent.stop);
+ L.DomEvent.on(link, 'click', L.DomEvent.stop).on(
+ link,
+ 'click',
+ map.openBrowser,
+ map
+ )
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', map.openBrowser, map);
+ map.whenReady(function () {
+ this.update()
+ }, this)
- map.whenReady(function () {
- this.update();
- }, this);
-
- if (L.Browser.pointer) {
- L.DomEvent.disableClickPropagation(container);
- L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation);
- L.DomEvent.on(container, 'MozMousePixelScroll', L.DomEvent.stopPropagation);
- }
- if (!L.Browser.touch) {
- L.DomEvent.on(container, {
- mouseenter: this.expand,
- mouseleave: this.collapse
- }, this);
- } else {
- L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
- L.DomEvent.on(toggle, 'click', L.DomEvent.stop)
- .on(toggle, 'click', this.expand, this);
- map.on('click', this.collapse, this);
- }
-
- return container;
- },
-
- onAdd: function (map) {
- if (!this._container) this._initLayout(map);
- if (map.options.datalayersControl === 'expanded') this.expand();
- return this._container;
- },
-
- onRemove: function (map) {
- this.collapse();
- },
-
- update: function () {
- if (this._datalayers_container && this._map) {
- this._datalayers_container.innerHTML = '';
- this._map.eachDataLayerReverse(function (datalayer) {
- this.addDataLayer(this._datalayers_container, datalayer);
- }, this)
- }
- },
-
- expand: function () {
- L.DomUtil.addClass(this._container, 'expanded');
- },
-
- collapse: function () {
- if (this._map.options.datalayersControl === 'expanded') return;
- L.DomUtil.removeClass(this._container, 'expanded');
- },
-
- addDataLayer: function (container, datalayer, draggable) {
- var datalayerLi = L.DomUtil.create('li', '', container);
- if (draggable) L.DomUtil.element('i', {className: 'drag-handle', title: L._('Drag to reorder')}, datalayerLi);
- datalayer.renderToolbox(datalayerLi);
- var title = L.DomUtil.add('span', 'layer-title', datalayerLi, datalayer.options.name);
-
- datalayerLi.id = 'browse_data_toggle_' + L.stamp(datalayer);
- L.DomUtil.classIf(datalayerLi, 'off', !datalayer.isVisible());
-
- title.textContent = datalayer.options.name;
- },
-
- newDataLayer: function () {
- var datalayer = this.map.createDataLayer({});
- datalayer.edit();
- },
-
- openPanel: function () {
- if (!this.map.editEnabled) return;
- var container = L.DomUtil.create('ul', 'umap-browse-datalayers');
- this.map.eachDataLayerReverse(function (datalayer) {
- this.addDataLayer(container, datalayer, true);
- }, this);
- var orderable = new L.U.Orderable(container);
- orderable.on('drop', function (e) {
- var layer = this.map.datalayers[e.src.dataset.id],
- other = this.map.datalayers[e.dst.dataset.id],
- minIndex = Math.min(e.initialIndex, e.finalIndex);
- if (e.finalIndex === 0) layer.bringToTop();
- else if (e.finalIndex > e.initialIndex) layer.insertBefore(other);
- else layer.insertAfter(other);
- this.map.eachDataLayerReverse(function (datalayer) {
- if (datalayer.getRank() >= minIndex) datalayer.isDirty = true;
- });
- this.map.indexDatalayers();
- }, this);
-
- var bar = L.DomUtil.create('div', 'button-bar', container),
- add = L.DomUtil.create('a', 'show-on-edit block add-datalayer button', bar);
- add.href = '#';
- add.textContent = add.title = L._('Add a layer');
-
- L.DomEvent
- .on(add, 'click', L.DomEvent.stop)
- .on(add, 'click', this.newDataLayer, this);
-
- this.map.ui.openPanel({data: {html: container}, className: 'dark'});
+ if (L.Browser.pointer) {
+ L.DomEvent.disableClickPropagation(container)
+ L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation)
+ L.DomEvent.on(container, 'MozMousePixelScroll', L.DomEvent.stopPropagation)
+ }
+ if (!L.Browser.touch) {
+ L.DomEvent.on(
+ container,
+ {
+ mouseenter: this.expand,
+ mouseleave: this.collapse,
+ },
+ this
+ )
+ } else {
+ L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation)
+ L.DomEvent.on(toggle, 'click', L.DomEvent.stop).on(
+ toggle,
+ 'click',
+ this.expand,
+ this
+ )
+ map.on('click', this.collapse, this)
}
-});
+ return container
+ },
+
+ onAdd: function (map) {
+ if (!this._container) this._initLayout(map)
+ if (map.options.datalayersControl === 'expanded') this.expand()
+ return this._container
+ },
+
+ onRemove: function (map) {
+ this.collapse()
+ },
+
+ update: function () {
+ if (this._datalayers_container && this._map) {
+ this._datalayers_container.innerHTML = ''
+ this._map.eachDataLayerReverse(function (datalayer) {
+ this.addDataLayer(this._datalayers_container, datalayer)
+ }, this)
+ }
+ },
+
+ expand: function () {
+ L.DomUtil.addClass(this._container, 'expanded')
+ },
+
+ collapse: function () {
+ if (this._map.options.datalayersControl === 'expanded') return
+ L.DomUtil.removeClass(this._container, 'expanded')
+ },
+
+ addDataLayer: function (container, datalayer, draggable) {
+ var datalayerLi = L.DomUtil.create('li', '', container)
+ if (draggable)
+ L.DomUtil.element(
+ 'i',
+ { className: 'drag-handle', title: L._('Drag to reorder') },
+ datalayerLi
+ )
+ datalayer.renderToolbox(datalayerLi)
+ var title = L.DomUtil.add(
+ 'span',
+ 'layer-title',
+ datalayerLi,
+ datalayer.options.name
+ )
+
+ datalayerLi.id = 'browse_data_toggle_' + L.stamp(datalayer)
+ L.DomUtil.classIf(datalayerLi, 'off', !datalayer.isVisible())
+
+ title.textContent = datalayer.options.name
+ },
+
+ newDataLayer: function () {
+ var datalayer = this.map.createDataLayer({})
+ datalayer.edit()
+ },
+
+ openPanel: function () {
+ if (!this.map.editEnabled) return
+ var container = L.DomUtil.create('ul', 'umap-browse-datalayers')
+ this.map.eachDataLayerReverse(function (datalayer) {
+ this.addDataLayer(container, datalayer, true)
+ }, this)
+ var orderable = new L.U.Orderable(container)
+ orderable.on(
+ 'drop',
+ function (e) {
+ var layer = this.map.datalayers[e.src.dataset.id],
+ other = this.map.datalayers[e.dst.dataset.id],
+ minIndex = Math.min(e.initialIndex, e.finalIndex)
+ if (e.finalIndex === 0) layer.bringToTop()
+ else if (e.finalIndex > e.initialIndex) layer.insertBefore(other)
+ else layer.insertAfter(other)
+ this.map.eachDataLayerReverse(function (datalayer) {
+ if (datalayer.getRank() >= minIndex) datalayer.isDirty = true
+ })
+ this.map.indexDatalayers()
+ },
+ this
+ )
+
+ var bar = L.DomUtil.create('div', 'button-bar', container),
+ add = L.DomUtil.create('a', 'show-on-edit block add-datalayer button', bar)
+ add.href = '#'
+ add.textContent = add.title = L._('Add a layer')
+
+ L.DomEvent.on(add, 'click', L.DomEvent.stop).on(
+ add,
+ 'click',
+ this.newDataLayer,
+ this
+ )
+
+ this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
+ },
+})
L.U.DataLayer.include({
+ renderToolbox: function (container) {
+ var toggle = L.DomUtil.create('i', 'layer-toggle', container),
+ zoomTo = L.DomUtil.create('i', 'layer-zoom_to', container),
+ edit = L.DomUtil.create('i', 'layer-edit show-on-edit', container),
+ table = L.DomUtil.create('i', 'layer-table-edit show-on-edit', container),
+ remove = L.DomUtil.create('i', 'layer-delete show-on-edit', container)
+ zoomTo.title = L._('Zoom to layer extent')
+ toggle.title = L._('Show/hide layer')
+ edit.title = L._('Edit')
+ table.title = L._('Edit properties in a table')
+ remove.title = L._('Delete layer')
+ L.DomEvent.on(toggle, 'click', this.toggle, this)
+ L.DomEvent.on(zoomTo, 'click', this.zoomTo, this)
+ L.DomEvent.on(edit, 'click', this.edit, this)
+ L.DomEvent.on(table, 'click', this.tableEdit, this)
+ L.DomEvent.on(
+ remove,
+ 'click',
+ function () {
+ if (!this.isVisible()) return
+ if (!confirm(L._('Are you sure you want to delete this layer?'))) return
+ this._delete()
+ this.map.ui.closePanel()
+ },
+ this
+ )
+ L.DomUtil.addClass(container, this.getHidableClass())
+ L.DomUtil.classIf(container, 'off', !this.isVisible())
+ container.dataset.id = L.stamp(this)
+ },
- renderToolbox: function (container) {
- var toggle = L.DomUtil.create('i', 'layer-toggle', container),
- zoomTo = L.DomUtil.create('i', 'layer-zoom_to', container),
- edit = L.DomUtil.create('i', 'layer-edit show-on-edit', container),
- table = L.DomUtil.create('i', 'layer-table-edit show-on-edit', container),
- remove = L.DomUtil.create('i', 'layer-delete show-on-edit', container);
- zoomTo.title = L._('Zoom to layer extent');
- toggle.title = L._('Show/hide layer');
- edit.title = L._('Edit');
- table.title = L._('Edit properties in a table');
- remove.title = L._('Delete layer');
- L.DomEvent.on(toggle, 'click', this.toggle, this);
- L.DomEvent.on(zoomTo, 'click', this.zoomTo, this);
- L.DomEvent.on(edit, 'click', this.edit, this);
- L.DomEvent.on(table, 'click', this.tableEdit, this);
- L.DomEvent.on(remove, 'click', function () {
- if (!this.isVisible()) return;
- if (!confirm(L._('Are you sure you want to delete this layer?'))) return;
- this._delete();
- this.map.ui.closePanel();
- }, this);
- L.DomUtil.addClass(container, this.getHidableClass());
- L.DomUtil.classIf(container, 'off', !this.isVisible());
- container.dataset.id = L.stamp(this);
- },
+ getHidableElements: function () {
+ return document.querySelectorAll('.' + this.getHidableClass())
+ },
- getHidableElements: function () {
- return document.querySelectorAll('.' + this.getHidableClass());
- },
+ getHidableClass: function () {
+ return 'show_with_datalayer_' + L.stamp(this)
+ },
- getHidableClass: function () {
- return 'show_with_datalayer_' + L.stamp(this);
- },
-
- propagateRemote: function () {
- var els = this.getHidableElements();
- for (var i = 0; i < els.length; i++) {
- L.DomUtil.classIf(els[i], 'remotelayer', this.isRemoteLayer());
- }
- },
-
- propagateHide: function () {
- var els = this.getHidableElements();
- for (var i = 0; i < els.length; i++) {
- L.DomUtil.addClass(els[i], 'off');
- }
- },
-
- propagateShow: function () {
- this.onceLoaded(function () {
- var els = this.getHidableElements();
- for (var i = 0; i < els.length; i++) {
- L.DomUtil.removeClass(els[i], 'off');
- }
- }, this);
+ propagateRemote: function () {
+ var els = this.getHidableElements()
+ for (var i = 0; i < els.length; i++) {
+ L.DomUtil.classIf(els[i], 'remotelayer', this.isRemoteLayer())
}
+ },
-});
+ propagateHide: function () {
+ var els = this.getHidableElements()
+ for (var i = 0; i < els.length; i++) {
+ L.DomUtil.addClass(els[i], 'off')
+ }
+ },
+
+ propagateShow: function () {
+ this.onceLoaded(function () {
+ var els = this.getHidableElements()
+ for (var i = 0; i < els.length; i++) {
+ L.DomUtil.removeClass(els[i], 'off')
+ }
+ }, this)
+ },
+})
L.U.DataLayer.addInitHook(function () {
- this.on('hide', this.propagateHide);
- this.on('show', this.propagateShow);
- this.propagateShow();
-});
-
+ this.on('hide', this.propagateHide)
+ this.on('show', this.propagateShow)
+ this.propagateShow()
+})
L.U.Map.include({
+ _openBrowser: function () {
+ var browserContainer = L.DomUtil.create('div', 'umap-browse-data'),
+ title = L.DomUtil.add(
+ 'h3',
+ 'umap-browse-title',
+ browserContainer,
+ this.options.name
+ ),
+ filter = L.DomUtil.create('input', '', browserContainer),
+ filterValue = '',
+ featuresContainer = L.DomUtil.create(
+ 'div',
+ 'umap-browse-features',
+ browserContainer
+ ),
+ filterKeys = this.getFilterKeys()
+ filter.type = 'text'
+ filter.placeholder = L._('Filter…')
+ filter.value = this.options.filter || ''
- _openBrowser: function () {
- var browserContainer = L.DomUtil.create('div', 'umap-browse-data'),
- title = L.DomUtil.add('h3', 'umap-browse-title', browserContainer, this.options.name),
- filter = L.DomUtil.create('input', '', browserContainer),
- filterValue = '',
- featuresContainer = L.DomUtil.create('div', 'umap-browse-features', browserContainer),
- filterKeys = this.getFilterKeys();
- filter.type = 'text';
- filter.placeholder = L._('Filter…');
- filter.value = this.options.filter || '';
-
- var addFeature = function (feature) {
- var feature_li = L.DomUtil.create('li', feature.getClassName() + ' feature'),
- zoom_to = L.DomUtil.create('i', 'feature-zoom_to', feature_li),
- edit = L.DomUtil.create('i', 'show-on-edit feature-edit', feature_li),
- color = L.DomUtil.create('i', 'feature-color', feature_li),
- title = L.DomUtil.create('span', 'feature-title', feature_li),
- symbol = feature._getIconUrl ? L.U.Icon.prototype.formatUrl(feature._getIconUrl(), feature): null;
- zoom_to.title = L._('Bring feature to center');
- edit.title = L._('Edit this feature');
- title.textContent = feature.getDisplayName() || '—';
- color.style.backgroundColor = feature.getOption('color');
- if (symbol) {
- color.style.backgroundImage = 'url(' + symbol + ')';
- }
- L.DomEvent.on(zoom_to, 'click', function (e) {
- e.callback = L.bind(this.view, this);
- this.zoomTo(e);
- }, feature);
- L.DomEvent.on(title, 'click', function (e) {
- e.callback = L.bind(this.view, this)
- this.zoomTo(e);
- }, feature);
- L.DomEvent.on(edit, 'click', function () {
- this.edit();
- }, feature);
- return feature_li;
- };
-
- var append = function (datalayer) {
- var container = L.DomUtil.create('div', datalayer.getHidableClass(), featuresContainer),
- headline = L.DomUtil.create('h5', '', container);
- container.id = 'browse_data_datalayer_' + datalayer.umap_id;
- datalayer.renderToolbox(headline);
- L.DomUtil.add('span', '', headline, datalayer.options.name);
- var ul = L.DomUtil.create('ul', '', container);
- L.DomUtil.classIf(container, 'off', !datalayer.isVisible());
-
- var build = function () {
- ul.innerHTML = '';
- datalayer.eachFeature(function (feature) {
- if ((filterValue && !feature.matchFilter(filterValue, filterKeys)) || feature.properties.isVisible === false) return;
- ul.appendChild(addFeature(feature));
- });
- };
- build();
- datalayer.on('datachanged', build);
- datalayer.map.ui.once('panel:closed', function () {
- datalayer.off('datachanged', build);
- });
- datalayer.map.ui.once('panel:ready', function () {
- datalayer.map.ui.once('panel:ready', function () {
- datalayer.off('datachanged', build);
- });
- });
- };
-
- var appendAll = function () {
- this.options.filter = filterValue = filter.value;
- featuresContainer.innerHTML = '';
- this.eachBrowsableDataLayer(function (datalayer) {
- append(datalayer);
- });
- };
- var resetLayers = function () {
- this.eachBrowsableDataLayer(function (datalayer) {
- datalayer.resetLayer(true);
- });
- }
- L.bind(appendAll, this)();
- L.DomEvent.on(filter, 'input', appendAll, this);
- L.DomEvent.on(filter, 'input', resetLayers, this);
- var link = L.DomUtil.create('li', '');
- L.DomUtil.create('i', 'umap-icon-16 umap-caption', link);
- var label = L.DomUtil.create('span', '', link);
- label.textContent = label.title = L._('About');
- L.DomEvent.on(link, 'click', this.displayCaption, this);
- this.ui.openPanel({data: {html: browserContainer}, actions: [link]});
- },
-
- _openFilter: function () {
- var filterContainer = L.DomUtil.create('div', 'umap-filter-data'),
- title = L.DomUtil.add('h3', 'umap-filter-title', filterContainer, this.options.name),
- propertiesContainer = L.DomUtil.create('div', 'umap-filter-properties', filterContainer),
- advancedFilterKeys = this.getAdvancedFilterKeys();
-
- var advancedFiltersFull = {};
- var filtersAlreadyLoaded = true;
- if (!this.getMap().options.advancedFilters) {
- this.getMap().options.advancedFilters = {};
- filtersAlreadyLoaded = false;
- }
- advancedFilterKeys.forEach(property => {
- advancedFiltersFull[property] = [];
- if (!filtersAlreadyLoaded || !this.getMap().options.advancedFilters[property]) {
- this.getMap().options.advancedFilters[property] = [];
- }
- });
- this.eachDataLayer(function (datalayer) {
- datalayer.eachFeature(function (feature) {
- advancedFilterKeys.forEach(property => {
- if (feature.properties[property]) {
- if (!advancedFiltersFull[property].includes(feature.properties[property])) {
- advancedFiltersFull[property].push(feature.properties[property]);
- }
- }
- });
- });
- });
-
- var addPropertyValue = function (property, value) {
- var property_li = L.DomUtil.create('li', ''),
- filter_check = L.DomUtil.create('input', '', property_li),
- filter_label = L.DomUtil.create('label', '', property_li);
- filter_check.type = 'checkbox';
- filter_check.id = `checkbox_${property}_${value}`;
- filter_check.checked = this.getMap().options.advancedFilters[property] && this.getMap().options.advancedFilters[property].includes(value);
- filter_check.setAttribute('data-property', property);
- filter_check.setAttribute('data-value', value);
- filter_label.htmlFor = `checkbox_${property}_${value}`;
- filter_label.innerHTML = value;
- L.DomEvent.on(filter_check, 'change', function (e) {
- var property = e.srcElement.dataset.property;
- var value = e.srcElement.dataset.value;
- if (e.srcElement.checked) {
- this.getMap().options.advancedFilters[property].push(value);
- } else {
- this.getMap().options.advancedFilters[property].splice(this.getMap().options.advancedFilters[property].indexOf(value), 1);
- }
- L.bind(filterFeatures, this)();
- }, this);
- return property_li
- };
-
- var addProperty = function (property) {
- var container = L.DomUtil.create('div', 'property-container', propertiesContainer),
- headline = L.DomUtil.add('h5', '', container, property);
- var ul = L.DomUtil.create('ul', '', container);
- var orderedValues = advancedFiltersFull[property];
- orderedValues.sort();
- orderedValues.forEach(value => {
- ul.appendChild(L.bind(addPropertyValue, this)(property, value));
- });
- };
-
- var filterFeatures = function () {
- var noResults = true;
- this.eachDataLayer(function (datalayer) {
- datalayer.eachFeature(function (feature) {
- feature.properties.isVisible = true;
- for (const [property, values] of Object.entries(this.map.options.advancedFilters)) {
- if (values.length > 0) {
- if (!feature.properties[property] || !values.includes(feature.properties[property])) {
- feature.properties.isVisible = false;
- }
- }
- }
- if (feature.properties.isVisible) {
- noResults = false;
- if (!this.isLoaded()) this.fetchData();
- this.map.addLayer(feature);
- this.fire('show');
- } else {
- this.map.removeLayer(feature);
- this.fire('hide');
- }
- });
- });
- if (noResults) {
- this.help.show('advancedFiltersNoResults');
- } else {
- this.help.hide();
- }
- };
-
- propertiesContainer.innerHTML = '';
- advancedFilterKeys.forEach(property => {
- L.bind(addProperty, this)(property);
- });
-
- var link = L.DomUtil.create('li', '');
- L.DomUtil.create('i', 'umap-icon-16 umap-caption', link);
- var label = L.DomUtil.create('span', '', link);
- label.textContent = label.title = L._('About');
- L.DomEvent.on(link, 'click', this.displayCaption, this);
- this.ui.openPanel({ data: { html: filterContainer }, actions: [link] });
+ var addFeature = function (feature) {
+ var feature_li = L.DomUtil.create('li', feature.getClassName() + ' feature'),
+ zoom_to = L.DomUtil.create('i', 'feature-zoom_to', feature_li),
+ edit = L.DomUtil.create('i', 'show-on-edit feature-edit', feature_li),
+ color = L.DomUtil.create('i', 'feature-color', feature_li),
+ title = L.DomUtil.create('span', 'feature-title', feature_li),
+ symbol = feature._getIconUrl
+ ? L.U.Icon.prototype.formatUrl(feature._getIconUrl(), feature)
+ : null
+ zoom_to.title = L._('Bring feature to center')
+ edit.title = L._('Edit this feature')
+ title.textContent = feature.getDisplayName() || '—'
+ color.style.backgroundColor = feature.getOption('color')
+ if (symbol) {
+ color.style.backgroundImage = 'url(' + symbol + ')'
+ }
+ L.DomEvent.on(
+ zoom_to,
+ 'click',
+ function (e) {
+ e.callback = L.bind(this.view, this)
+ this.zoomTo(e)
+ },
+ feature
+ )
+ L.DomEvent.on(
+ title,
+ 'click',
+ function (e) {
+ e.callback = L.bind(this.view, this)
+ this.zoomTo(e)
+ },
+ feature
+ )
+ L.DomEvent.on(
+ edit,
+ 'click',
+ function () {
+ this.edit()
+ },
+ feature
+ )
+ return feature_li
}
-});
+ var append = function (datalayer) {
+ var container = L.DomUtil.create(
+ 'div',
+ datalayer.getHidableClass(),
+ featuresContainer
+ ),
+ headline = L.DomUtil.create('h5', '', container)
+ container.id = 'browse_data_datalayer_' + datalayer.umap_id
+ datalayer.renderToolbox(headline)
+ L.DomUtil.add('span', '', headline, datalayer.options.name)
+ var ul = L.DomUtil.create('ul', '', container)
+ L.DomUtil.classIf(container, 'off', !datalayer.isVisible())
+ var build = function () {
+ ul.innerHTML = ''
+ datalayer.eachFeature(function (feature) {
+ if (
+ (filterValue && !feature.matchFilter(filterValue, filterKeys)) ||
+ feature.properties.isVisible === false
+ )
+ return
+ ul.appendChild(addFeature(feature))
+ })
+ }
+ build()
+ datalayer.on('datachanged', build)
+ datalayer.map.ui.once('panel:closed', function () {
+ datalayer.off('datachanged', build)
+ })
+ datalayer.map.ui.once('panel:ready', function () {
+ datalayer.map.ui.once('panel:ready', function () {
+ datalayer.off('datachanged', build)
+ })
+ })
+ }
+ var appendAll = function () {
+ this.options.filter = filterValue = filter.value
+ featuresContainer.innerHTML = ''
+ this.eachBrowsableDataLayer(function (datalayer) {
+ append(datalayer)
+ })
+ }
+ var resetLayers = function () {
+ this.eachBrowsableDataLayer(function (datalayer) {
+ datalayer.resetLayer(true)
+ })
+ }
+ L.bind(appendAll, this)()
+ L.DomEvent.on(filter, 'input', appendAll, this)
+ L.DomEvent.on(filter, 'input', resetLayers, this)
+ var link = L.DomUtil.create('li', '')
+ L.DomUtil.create('i', 'umap-icon-16 umap-caption', link)
+ var label = L.DomUtil.create('span', '', link)
+ label.textContent = label.title = L._('About')
+ L.DomEvent.on(link, 'click', this.displayCaption, this)
+ this.ui.openPanel({ data: { html: browserContainer }, actions: [link] })
+ },
+
+ _openFilter: function () {
+ var filterContainer = L.DomUtil.create('div', 'umap-filter-data'),
+ title = L.DomUtil.add(
+ 'h3',
+ 'umap-filter-title',
+ filterContainer,
+ this.options.name
+ ),
+ propertiesContainer = L.DomUtil.create(
+ 'div',
+ 'umap-filter-properties',
+ filterContainer
+ ),
+ advancedFilterKeys = this.getAdvancedFilterKeys()
+
+ var advancedFiltersFull = {}
+ var filtersAlreadyLoaded = true
+ if (!this.getMap().options.advancedFilters) {
+ this.getMap().options.advancedFilters = {}
+ filtersAlreadyLoaded = false
+ }
+ advancedFilterKeys.forEach((property) => {
+ advancedFiltersFull[property] = []
+ if (!filtersAlreadyLoaded || !this.getMap().options.advancedFilters[property]) {
+ this.getMap().options.advancedFilters[property] = []
+ }
+ })
+ this.eachDataLayer(function (datalayer) {
+ datalayer.eachFeature(function (feature) {
+ advancedFilterKeys.forEach((property) => {
+ if (feature.properties[property]) {
+ if (!advancedFiltersFull[property].includes(feature.properties[property])) {
+ advancedFiltersFull[property].push(feature.properties[property])
+ }
+ }
+ })
+ })
+ })
+
+ var addPropertyValue = function (property, value) {
+ var property_li = L.DomUtil.create('li', ''),
+ filter_check = L.DomUtil.create('input', '', property_li),
+ filter_label = L.DomUtil.create('label', '', property_li)
+ filter_check.type = 'checkbox'
+ filter_check.id = `checkbox_${property}_${value}`
+ filter_check.checked =
+ this.getMap().options.advancedFilters[property] &&
+ this.getMap().options.advancedFilters[property].includes(value)
+ filter_check.setAttribute('data-property', property)
+ filter_check.setAttribute('data-value', value)
+ filter_label.htmlFor = `checkbox_${property}_${value}`
+ filter_label.innerHTML = value
+ L.DomEvent.on(
+ filter_check,
+ 'change',
+ function (e) {
+ var property = e.srcElement.dataset.property
+ var value = e.srcElement.dataset.value
+ if (e.srcElement.checked) {
+ this.getMap().options.advancedFilters[property].push(value)
+ } else {
+ this.getMap().options.advancedFilters[property].splice(
+ this.getMap().options.advancedFilters[property].indexOf(value),
+ 1
+ )
+ }
+ L.bind(filterFeatures, this)()
+ },
+ this
+ )
+ return property_li
+ }
+
+ var addProperty = function (property) {
+ var container = L.DomUtil.create(
+ 'div',
+ 'property-container',
+ propertiesContainer
+ ),
+ headline = L.DomUtil.add('h5', '', container, property)
+ var ul = L.DomUtil.create('ul', '', container)
+ var orderedValues = advancedFiltersFull[property]
+ orderedValues.sort()
+ orderedValues.forEach((value) => {
+ ul.appendChild(L.bind(addPropertyValue, this)(property, value))
+ })
+ }
+
+ var filterFeatures = function () {
+ var noResults = true
+ this.eachDataLayer(function (datalayer) {
+ datalayer.eachFeature(function (feature) {
+ feature.properties.isVisible = true
+ for (const [property, values] of Object.entries(
+ this.map.options.advancedFilters
+ )) {
+ if (values.length > 0) {
+ if (
+ !feature.properties[property] ||
+ !values.includes(feature.properties[property])
+ ) {
+ feature.properties.isVisible = false
+ }
+ }
+ }
+ if (feature.properties.isVisible) {
+ noResults = false
+ if (!this.isLoaded()) this.fetchData()
+ this.map.addLayer(feature)
+ this.fire('show')
+ } else {
+ this.map.removeLayer(feature)
+ this.fire('hide')
+ }
+ })
+ })
+ if (noResults) {
+ this.help.show('advancedFiltersNoResults')
+ } else {
+ this.help.hide()
+ }
+ }
+
+ propertiesContainer.innerHTML = ''
+ advancedFilterKeys.forEach((property) => {
+ L.bind(addProperty, this)(property)
+ })
+
+ var link = L.DomUtil.create('li', '')
+ L.DomUtil.create('i', 'umap-icon-16 umap-caption', link)
+ var label = L.DomUtil.create('span', '', link)
+ label.textContent = label.title = L._('About')
+ L.DomEvent.on(link, 'click', this.displayCaption, this)
+ this.ui.openPanel({ data: { html: filterContainer }, actions: [link] })
+ },
+})
L.U.TileLayerControl = L.Control.extend({
+ options: {
+ position: 'topleft',
+ },
- options: {
- position: 'topleft'
- },
+ initialize: function (map, options) {
+ this.map = map
+ L.Control.prototype.initialize.call(this, options)
+ },
- initialize: function (map, options) {
- this.map = map;
- L.Control.prototype.initialize.call(this, options);
- },
+ onAdd: function () {
+ var container = L.DomUtil.create('div', 'leaflet-control-tilelayers umap-control')
- onAdd: function () {
- var container = L.DomUtil.create('div', 'leaflet-control-tilelayers umap-control');
+ var link = L.DomUtil.create('a', '', container)
+ link.href = '#'
+ link.title = L._('Change map background')
- var link = L.DomUtil.create('a', '', container);
- link.href = '#';
- link.title = L._('Change map background');
+ L.DomEvent.on(link, 'click', L.DomEvent.stop)
+ .on(link, 'click', this.openSwitcher, this)
+ .on(link, 'dblclick', L.DomEvent.stopPropagation)
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', this.openSwitcher, this)
- .on(link, 'dblclick', L.DomEvent.stopPropagation);
+ return container
+ },
- return container;
- },
+ openSwitcher: function (options) {
+ this._tilelayers_container = L.DomUtil.create(
+ 'ul',
+ 'umap-tilelayer-switcher-container'
+ )
+ this.buildList(options)
+ },
- openSwitcher: function (options) {
- this._tilelayers_container = L.DomUtil.create('ul', 'umap-tilelayer-switcher-container');
- this.buildList(options);
- },
+ buildList: function (options) {
+ this.map.eachTileLayer(function (tilelayer) {
+ if (
+ window.location.protocol === 'https:' &&
+ tilelayer.options.url_template.indexOf('http:') === 0
+ )
+ return
+ this.addTileLayerElement(tilelayer, options)
+ }, this)
+ this.map.ui.openPanel({
+ data: { html: this._tilelayers_container },
+ className: options.className,
+ })
+ },
- buildList: function (options) {
- this.map.eachTileLayer(function (tilelayer) {
- if (window.location.protocol === 'https:' && tilelayer.options.url_template.indexOf('http:') === 0) return;
- this.addTileLayerElement(tilelayer, options);
- }, this);
- this.map.ui.openPanel({data: {html: this._tilelayers_container}, className: options.className});
- },
-
- addTileLayerElement: function (tilelayer, options) {
- var selectedClass = this.map.hasLayer(tilelayer) ? 'selected' : '',
- 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);
- name.textContent = tilelayer.options.name;
- L.DomEvent.on(el, 'click', function () {
- this.map.selectTileLayer(tilelayer);
- this.map.ui.closePanel();
- if (options && options.callback) options.callback(tilelayer);
- }, this);
- }
-
-
-});
+ addTileLayerElement: function (tilelayer, options) {
+ var selectedClass = this.map.hasLayer(tilelayer) ? 'selected' : '',
+ 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)
+ name.textContent = tilelayer.options.name
+ L.DomEvent.on(
+ el,
+ 'click',
+ function () {
+ this.map.selectTileLayer(tilelayer)
+ this.map.ui.closePanel()
+ if (options && options.callback) options.callback(tilelayer)
+ },
+ this
+ )
+ },
+})
L.U.AttributionControl = L.Control.Attribution.extend({
+ options: {
+ prefix: '',
+ },
- options: {
- prefix: ''
- },
-
- _update: function () {
- L.Control.Attribution.prototype._update.call(this);
- if (this._map.options.shortCredit) {
- L.DomUtil.add('span', '', this._container, ' — ' + L.Util.toHTML(this._map.options.shortCredit));
- }
- if (this._map.options.captionMenus) {
- var link = L.DomUtil.add('a', '', this._container, ' — ' + L._('About'));
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', this._map.displayCaption, this._map)
- .on(link, 'dblclick', L.DomEvent.stop);
- }
- if (window.top === window.self && this._map.options.captionMenus) {
- // We are not in iframe mode
- var home = L.DomUtil.add('a', '', this._container, ' — ' + L._('Home'));
- home.href = '/';
- }
+ _update: function () {
+ L.Control.Attribution.prototype._update.call(this)
+ if (this._map.options.shortCredit) {
+ L.DomUtil.add(
+ 'span',
+ '',
+ this._container,
+ ' — ' + L.Util.toHTML(this._map.options.shortCredit)
+ )
}
-
-});
-
+ if (this._map.options.captionMenus) {
+ var link = L.DomUtil.add('a', '', this._container, ' — ' + L._('About'))
+ L.DomEvent.on(link, 'click', L.DomEvent.stop)
+ .on(link, 'click', this._map.displayCaption, this._map)
+ .on(link, 'dblclick', L.DomEvent.stop)
+ }
+ if (window.top === window.self && this._map.options.captionMenus) {
+ // We are not in iframe mode
+ var home = L.DomUtil.add('a', '', this._container, ' — ' + L._('Home'))
+ home.href = '/'
+ }
+ },
+})
L.U.Search = L.PhotonSearch.extend({
+ initialize: function (map, input, options) {
+ L.PhotonSearch.prototype.initialize.call(this, map, input, options)
+ this.options.url = map.options.urls.search
+ },
- initialize: function (map, input, options) {
- L.PhotonSearch.prototype.initialize.call(this, map, input, options);
- this.options.url = map.options.urls.search;
- },
+ onBlur: function (e) {
+ // Overrided because we don't want to hide the results on blur.
+ this.fire('blur')
+ },
- onBlur: function (e) {
- // Overrided because we don't want to hide the results on blur.
- this.fire('blur');
- },
+ formatResult: function (feature, el) {
+ var self = this
+ var tools = L.DomUtil.create('span', 'search-result-tools', el),
+ zoom = L.DomUtil.create('i', 'feature-zoom_to', tools),
+ edit = L.DomUtil.create('i', 'feature-edit show-on-edit', tools)
+ zoom.title = L._('Zoom to this place')
+ edit.title = L._('Save this location as new feature')
+ // We need to use "mousedown" because Leaflet.Photon listen to mousedown
+ // on el.
+ L.DomEvent.on(zoom, 'mousedown', function (e) {
+ L.DomEvent.stop(e)
+ self.zoomToFeature(feature)
+ })
+ L.DomEvent.on(edit, 'mousedown', function (e) {
+ L.DomEvent.stop(e)
+ var datalayer = self.map.defaultDataLayer()
+ var layer = datalayer.geojsonToFeatures(feature)
+ layer.isDirty = true
+ layer.edit()
+ })
+ this._formatResult(feature, el)
+ },
- formatResult: function (feature, el) {
- var self = this;
- var tools = L.DomUtil.create('span', 'search-result-tools', el),
- zoom = L.DomUtil.create('i', 'feature-zoom_to', tools),
- edit = L.DomUtil.create('i', 'feature-edit show-on-edit', tools);
- zoom.title = L._('Zoom to this place');
- edit.title = L._('Save this location as new feature');
- // We need to use "mousedown" because Leaflet.Photon listen to mousedown
- // on el.
- L.DomEvent.on(zoom, 'mousedown', function (e) {
- L.DomEvent.stop(e);
- self.zoomToFeature(feature);
- });
- L.DomEvent.on(edit, 'mousedown', function (e) {
- L.DomEvent.stop(e);
- var datalayer = self.map.defaultDataLayer();
- var layer = datalayer.geojsonToFeatures(feature);
- layer.isDirty = true;
- layer.edit();
- });
- this._formatResult(feature, el);
- },
+ zoomToFeature: function (feature) {
+ var zoom = Math.max(this.map.getZoom(), 16) // Never unzoom.
+ this.map.setView(
+ [feature.geometry.coordinates[1], feature.geometry.coordinates[0]],
+ zoom
+ )
+ },
- zoomToFeature: function (feature) {
- var zoom = Math.max(this.map.getZoom(), 16); // Never unzoom.
- this.map.setView([feature.geometry.coordinates[1], feature.geometry.coordinates[0]], zoom);
- },
-
- onSelected: function (feature) {
- this.zoomToFeature(feature);
- this.map.ui.closePanel();
- }
-
-});
+ onSelected: function (feature) {
+ this.zoomToFeature(feature)
+ this.map.ui.closePanel()
+ },
+})
L.U.SearchControl = L.Control.extend({
+ options: {
+ position: 'topleft',
+ },
- options: {
- position: 'topleft',
- },
+ onAdd: function (map) {
+ var container = L.DomUtil.create('div', 'leaflet-control-search umap-control'),
+ self = this
- onAdd: function (map) {
- var container = L.DomUtil.create('div', 'leaflet-control-search umap-control'),
- self = this;
+ L.DomEvent.disableClickPropagation(container)
+ var link = L.DomUtil.create('a', '', container)
+ link.href = '#'
+ link.title = L._('Search a place name')
+ L.DomEvent.on(link, 'click', function (e) {
+ L.DomEvent.stop(e)
+ self.openPanel(map)
+ })
+ return container
+ },
- L.DomEvent.disableClickPropagation(container);
- var link = L.DomUtil.create('a', '', container);
- link.href = '#';
- link.title = L._('Search a place name')
- L.DomEvent.on(link, 'click', function (e) {
- L.DomEvent.stop(e);
- self.openPanel(map);
- });
- return container;
- },
-
- openPanel: function (map) {
- var options = {
- limit: 10,
- noResultLabel: L._('No results'),
- }
- if (map.options.photonUrl) options.url = map.options.photonUrl;
- var container = L.DomUtil.create('div', '');
-
- var title = L.DomUtil.create('h3', '', container);
- title.textContent = L._('Search location');
- var input = L.DomUtil.create('input', 'photon-input', container);
- var resultsContainer = L.DomUtil.create('div', 'photon-autocomplete', container);
- this.search = new L.U.Search(map, input, options);
- var id = Math.random();
- this.search.on('ajax:send', function () {
- map.fire('dataloading', {id: id});
- });
- this.search.on('ajax:return', function () {
- map.fire('dataload', {id: id});
- });
- this.search.resultsContainer = resultsContainer;
- map.ui.once('panel:ready', function () {
- input.focus();
- });
- map.ui.openPanel({data: {html: container}});
+ openPanel: function (map) {
+ var options = {
+ limit: 10,
+ noResultLabel: L._('No results'),
}
+ if (map.options.photonUrl) options.url = map.options.photonUrl
+ var container = L.DomUtil.create('div', '')
-});
-
+ var title = L.DomUtil.create('h3', '', container)
+ title.textContent = L._('Search location')
+ var input = L.DomUtil.create('input', 'photon-input', container)
+ var resultsContainer = L.DomUtil.create('div', 'photon-autocomplete', container)
+ this.search = new L.U.Search(map, input, options)
+ var id = Math.random()
+ this.search.on('ajax:send', function () {
+ map.fire('dataloading', { id: id })
+ })
+ this.search.on('ajax:return', function () {
+ map.fire('dataload', { id: id })
+ })
+ this.search.resultsContainer = resultsContainer
+ map.ui.once('panel:ready', function () {
+ input.focus()
+ })
+ map.ui.openPanel({ data: { html: container } })
+ },
+})
L.Control.MiniMap.include({
+ initialize: function (layer, options) {
+ L.Util.setOptions(this, options)
+ this._layer = this._cloneLayer(layer)
+ },
- initialize: function (layer, options) {
- L.Util.setOptions(this, options);
- this._layer = this._cloneLayer(layer);
- },
-
- onMainMapBaseLayerChange: function (e) {
- var layer = this._cloneLayer(e.layer);
- if (this._miniMap.hasLayer(this._layer)) {
- this._miniMap.removeLayer(this._layer);
- }
- this._layer = layer;
- this._miniMap.addLayer(this._layer);
- },
-
- _cloneLayer: function (layer) {
- return new L.TileLayer(layer._url, L.Util.extend({}, layer.options));
+ onMainMapBaseLayerChange: function (e) {
+ var layer = this._cloneLayer(e.layer)
+ if (this._miniMap.hasLayer(this._layer)) {
+ this._miniMap.removeLayer(this._layer)
}
+ this._layer = layer
+ this._miniMap.addLayer(this._layer)
+ },
-});
-
+ _cloneLayer: function (layer) {
+ return new L.TileLayer(layer._url, L.Util.extend({}, layer.options))
+ },
+})
L.Control.Loading.include({
+ onAdd: function (map) {
+ this._container = L.DomUtil.create('div', 'umap-loader', map._controlContainer)
+ map.on('baselayerchange', this._layerAdd, this)
+ this._addMapListeners(map)
+ this._map = map
+ },
- onAdd: function (map) {
- this._container = L.DomUtil.create('div', 'umap-loader', map._controlContainer);
- map.on('baselayerchange', this._layerAdd, this);
- this._addMapListeners(map);
- this._map = map;
- },
-
- _showIndicator: function () {
- L.DomUtil.addClass(this._map._container, 'umap-loading');
- },
-
- _hideIndicator: function() {
- L.DomUtil.removeClass(this._map._container, 'umap-loading');
- }
-
-});
+ _showIndicator: function () {
+ L.DomUtil.addClass(this._map._container, 'umap-loading')
+ },
+ _hideIndicator: function () {
+ L.DomUtil.removeClass(this._map._container, 'umap-loading')
+ },
+})
/*
-* Make it dynamic
-*/
+ * Make it dynamic
+ */
L.U.ContextMenu = L.Map.ContextMenu.extend({
+ _createItems: function (e) {
+ this._map.setContextMenuItems(e)
+ L.Map.ContextMenu.prototype._createItems.call(this)
+ },
- _createItems: function (e) {
- this._map.setContextMenuItems(e);
- L.Map.ContextMenu.prototype._createItems.call(this);
- },
-
- _showAtPoint: function (pt, e) {
- this._items = [];
- this._container.innerHTML = '';
- this._createItems(e);
- L.Map.ContextMenu.prototype._showAtPoint.call(this, pt, e);
- }
-
-});
+ _showAtPoint: function (pt, e) {
+ this._items = []
+ this._container.innerHTML = ''
+ this._createItems(e)
+ L.Map.ContextMenu.prototype._showAtPoint.call(this, pt, e)
+ },
+})
L.U.IframeExporter = L.Evented.extend({
+ options: {
+ includeFullScreenLink: true,
+ currentView: false,
+ keepCurrentDatalayers: false,
+ viewCurrentFeature: false,
+ },
- options: {
- includeFullScreenLink: true,
- currentView: false,
- keepCurrentDatalayers: false,
- viewCurrentFeature: false
- },
+ queryString: {
+ scaleControl: false,
+ miniMap: false,
+ scrollWheelZoom: false,
+ zoomControl: true,
+ allowEdit: false,
+ moreControl: true,
+ searchControl: null,
+ tilelayersControl: null,
+ embedControl: null,
+ datalayersControl: true,
+ onLoadPanel: 'none',
+ captionBar: false,
+ captionMenus: true,
+ },
- queryString: {
- scaleControl: false,
- miniMap: false,
- scrollWheelZoom: false,
- zoomControl: true,
- allowEdit: false,
- moreControl: true,
- searchControl: null,
- tilelayersControl: null,
- embedControl: null,
- datalayersControl: true,
- onLoadPanel: 'none',
- captionBar: false,
- captionMenus: true
- },
+ dimensions: {
+ width: '100%',
+ height: '300px',
+ },
- dimensions: {
- width: '100%',
- height: '300px'
- },
+ initialize: function (map) {
+ this.map = map
+ this.baseUrl = L.Util.getBaseUrl()
+ // Use map default, not generic default
+ this.queryString.onLoadPanel = this.map.options.onLoadPanel
+ },
- initialize: function (map) {
- this.map = map;
- this.baseUrl = L.Util.getBaseUrl();
- // Use map default, not generic default
- this.queryString.onLoadPanel = this.map.options.onLoadPanel;
- },
+ getMap: function () {
+ return this.map
+ },
- getMap: function () {
- return this.map;
- },
-
- build: function () {
- var datalayers = [];
- if (this.options.viewCurrentFeature && this.map.currentFeature) {
- this.queryString.feature = this.map.currentFeature.getSlug();
- }
- if (this.options.keepCurrentDatalayers) {
- this.map.eachDataLayer(function (datalayer) {
- if (datalayer.isVisible() && datalayer.umap_id) {
- datalayers.push(datalayer.umap_id);
- }
- });
- this.queryString.datalayers = datalayers.join(',');
- } else {
- delete this.queryString.datalayers;
- }
- var currentView = this.options.currentView ? window.location.hash : '',
- iframeUrl = this.baseUrl + '?' + L.Util.buildQueryString(this.queryString) + currentView,
- code = '';
- if (this.options.includeFullScreenLink) {
- code += '
' + L._('See full screen') + '
';
- }
- return code;
+ build: function () {
+ var datalayers = []
+ if (this.options.viewCurrentFeature && this.map.currentFeature) {
+ this.queryString.feature = this.map.currentFeature.getSlug()
}
-
-});
+ if (this.options.keepCurrentDatalayers) {
+ this.map.eachDataLayer(function (datalayer) {
+ if (datalayer.isVisible() && datalayer.umap_id) {
+ datalayers.push(datalayer.umap_id)
+ }
+ })
+ this.queryString.datalayers = datalayers.join(',')
+ } else {
+ delete this.queryString.datalayers
+ }
+ var currentView = this.options.currentView ? window.location.hash : '',
+ iframeUrl =
+ this.baseUrl + '?' + L.Util.buildQueryString(this.queryString) + currentView,
+ code =
+ ''
+ if (this.options.includeFullScreenLink) {
+ code += '' + L._('See full screen') + '
'
+ }
+ return code
+ },
+})
L.U.Editable = L.Editable.extend({
+ initialize: function (map, options) {
+ L.Editable.prototype.initialize.call(this, map, options)
+ this.on(
+ 'editable:drawing:start editable:drawing:click editable:drawing:move',
+ this.drawingTooltip
+ )
+ this.on('editable:drawing:end', this.closeTooltip)
+ // Layer for items added by users
+ this.on('editable:drawing:cancel', function (e) {
+ if (e.layer._latlngs && e.layer._latlngs.length < e.layer.editor.MIN_VERTEX)
+ e.layer.del()
+ if (e.layer instanceof L.U.Marker) e.layer.del()
+ })
+ this.on('editable:drawing:commit', function (e) {
+ e.layer.isDirty = true
+ if (this.map.editedFeature !== e.layer) e.layer.edit(e)
+ })
+ this.on('editable:editing', function (e) {
+ var layer = e.layer
+ layer.isDirty = true
+ if (layer._tooltip && layer.isTooltipOpen()) {
+ layer._tooltip.setLatLng(layer.getCenter())
+ layer._tooltip.update()
+ }
+ })
+ this.on('editable:vertex:ctrlclick', function (e) {
+ var index = e.vertex.getIndex()
+ if (index === 0 || (index === e.vertex.getLastIndex() && e.vertex.continue))
+ e.vertex.continue()
+ })
+ this.on('editable:vertex:altclick', function (e) {
+ if (e.vertex.editor.vertexCanBeDeleted(e.vertex)) e.vertex.delete()
+ })
+ this.on('editable:vertex:rawclick', this.onVertexRawClick)
+ },
- initialize: function (map, options) {
- L.Editable.prototype.initialize.call(this, map, options);
- this.on('editable:drawing:start editable:drawing:click editable:drawing:move', this.drawingTooltip);
- this.on('editable:drawing:end', this.closeTooltip);
- // Layer for items added by users
- this.on('editable:drawing:cancel', function (e) {
- if (e.layer._latlngs && e.layer._latlngs.length < e.layer.editor.MIN_VERTEX) e.layer.del();
- if (e.layer instanceof L.U.Marker) e.layer.del();
- });
- this.on('editable:drawing:commit', function (e) {
- e.layer.isDirty = true;
- if (this.map.editedFeature !== e.layer) e.layer.edit(e);
- });
- this.on('editable:editing', function (e) {
- var layer = e.layer;
- layer.isDirty = true;
- if (layer._tooltip && layer.isTooltipOpen()) {
- layer._tooltip.setLatLng(layer.getCenter());
- layer._tooltip.update();
- }
- });
- this.on('editable:vertex:ctrlclick', function (e) {
- var index = e.vertex.getIndex();
- if (index === 0 || index === e.vertex.getLastIndex() && e.vertex.continue) e.vertex.continue();
- });
- this.on('editable:vertex:altclick', function (e) {
- if (e.vertex.editor.vertexCanBeDeleted(e.vertex)) e.vertex.delete();
- });
- this.on('editable:vertex:rawclick', this.onVertexRawClick);
- },
+ createPolyline: function (latlngs) {
+ return new L.U.Polyline(this.map, latlngs)
+ },
- createPolyline: function (latlngs) {
- return new L.U.Polyline(this.map, latlngs);
- },
+ createPolygon: function (latlngs) {
+ var polygon = new L.U.Polygon(this.map, latlngs)
+ return polygon
+ },
- createPolygon: function (latlngs) {
- var polygon = new L.U.Polygon(this.map, latlngs);
- return polygon;
- },
+ createMarker: function (latlng) {
+ return new L.U.Marker(this.map, latlng)
+ },
- createMarker: function (latlng) {
- return new L.U.Marker(this.map, latlng);
- },
+ connectCreatedToMap: function (layer) {
+ // Overrided from Leaflet.Editable
+ var datalayer = this.map.defaultDataLayer()
+ datalayer.addLayer(layer)
+ layer.isDirty = true
+ return layer
+ },
- connectCreatedToMap: function (layer) {
- // Overrided from Leaflet.Editable
- var datalayer = this.map.defaultDataLayer();
- datalayer.addLayer(layer);
- layer.isDirty = true;
- return layer;
- },
-
- drawingTooltip: function (e) {
- if (e.layer instanceof L.Marker && e.type != "editable:drawing:move") {
- this.map.ui.tooltip({content: L._('Click to add a marker')});
- }
- if (!(e.layer instanceof L.Polyline)) {
- // only continue with Polylines and Polygons
- return;
- }
-
- var content;
- var measure;
- if (e.layer.editor._drawnLatLngs) {
- // when drawing (a Polyline or Polygon)
- if (!e.layer.editor._drawnLatLngs.length) {
- // when drawing first point
- if (e.layer instanceof L.Polygon){
- content = L._('Click to start drawing a polygon');
- } else if (e.layer instanceof L.Polyline) {
- content = L._('Click to start drawing a line');
- }
- } else {
- var tmpLatLngs = e.layer.editor._drawnLatLngs.slice();
- tmpLatLngs.push(e.latlng);
- measure = e.layer.getMeasure(tmpLatLngs);
-
- if (e.layer.editor._drawnLatLngs.length < e.layer.editor.MIN_VERTEX) {
- // when drawing second point
- content = L._('Click to continue drawing');
- } else {
- // when drawing third point (or more)
- content = L._('Click last point to finish shape');
- }
- }
- } else {
- // when moving an existing point
- measure = e.layer.getMeasure();
- }
- if (measure){
- if (e.layer instanceof L.Polygon){
- content += L._(' (area: {measure})', { measure: measure });
- } else if (e.layer instanceof L.Polyline) {
- content += L._(' (length: {measure})', { measure: measure });
- }
- }
- if (content) {
- this.map.ui.tooltip({content: content});
- }
- },
-
- closeTooltip: function () {
- this.map.ui.closeTooltip();
- },
-
- onVertexRawClick: function (e) {
- e.layer.onVertexRawClick(e);
- L.DomEvent.stop(e);
- e.cancel();
+ drawingTooltip: function (e) {
+ if (e.layer instanceof L.Marker && e.type != 'editable:drawing:move') {
+ this.map.ui.tooltip({ content: L._('Click to add a marker') })
+ }
+ if (!(e.layer instanceof L.Polyline)) {
+ // only continue with Polylines and Polygons
+ return
}
-});
+ var content
+ var measure
+ if (e.layer.editor._drawnLatLngs) {
+ // when drawing (a Polyline or Polygon)
+ if (!e.layer.editor._drawnLatLngs.length) {
+ // when drawing first point
+ if (e.layer instanceof L.Polygon) {
+ content = L._('Click to start drawing a polygon')
+ } else if (e.layer instanceof L.Polyline) {
+ content = L._('Click to start drawing a line')
+ }
+ } else {
+ var tmpLatLngs = e.layer.editor._drawnLatLngs.slice()
+ tmpLatLngs.push(e.latlng)
+ measure = e.layer.getMeasure(tmpLatLngs)
+
+ if (e.layer.editor._drawnLatLngs.length < e.layer.editor.MIN_VERTEX) {
+ // when drawing second point
+ content = L._('Click to continue drawing')
+ } else {
+ // when drawing third point (or more)
+ content = L._('Click last point to finish shape')
+ }
+ }
+ } else {
+ // when moving an existing point
+ measure = e.layer.getMeasure()
+ }
+ if (measure) {
+ if (e.layer instanceof L.Polygon) {
+ content += L._(' (area: {measure})', { measure: measure })
+ } else if (e.layer instanceof L.Polyline) {
+ content += L._(' (length: {measure})', { measure: measure })
+ }
+ }
+ if (content) {
+ this.map.ui.tooltip({ content: content })
+ }
+ },
+
+ closeTooltip: function () {
+ this.map.ui.closeTooltip()
+ },
+
+ onVertexRawClick: function (e) {
+ e.layer.onVertexRawClick(e)
+ L.DomEvent.stop(e)
+ e.cancel()
+ },
+})
diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js
index 614fce48..43f25d01 100644
--- a/umap/static/umap/js/umap.core.js
+++ b/umap/static/umap/js/umap.core.js
@@ -1,560 +1,632 @@
/* Poor man pub/sub handler, enough for now */
-L.UmapSingleton = L.Evented.extend({});
-L.U = new L.UmapSingleton();
-L.U.Map = L.Map.extend({});
+L.UmapSingleton = L.Evented.extend({})
+L.U = new L.UmapSingleton()
+L.U.Map = L.Map.extend({})
/*
-* Utils
-*/
+ * Utils
+ */
L.Util.queryString = function (name, fallback) {
- var decode = function (s) { return decodeURIComponent(s.replace(/\+/g, ' ')); };
- var qs = window.location.search.slice(1).split('&'), qa = {};
- for (var i in qs) {
- var key = qs[i].split('=');
- if (!key) continue;
- qa[decode(key[0])] = key[1] ? decode(key[1]) : 1;
- }
- return qa[name] || fallback;
-};
+ var decode = function (s) {
+ return decodeURIComponent(s.replace(/\+/g, ' '))
+ }
+ var qs = window.location.search.slice(1).split('&'),
+ qa = {}
+ for (var i in qs) {
+ var 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) {
- var value = L.Util.queryString(name);
- return value === '1' || value === 'true';
-};
+ var value = L.Util.queryString(name)
+ return value === '1' || value === 'true'
+}
L.Util.setFromQueryString = function (options, name) {
- var value = L.Util.queryString(name);
- if (typeof value !== 'undefined') options[name] = value;
-};
+ var value = L.Util.queryString(name)
+ if (typeof value !== 'undefined') options[name] = value
+}
L.Util.setBooleanFromQueryString = function (options, name) {
- var value = L.Util.queryString(name);
- if (typeof value !== 'undefined') options[name] = value == '1' || value == 'true';
-};
+ var value = L.Util.queryString(name)
+ if (typeof value !== 'undefined') options[name] = value == '1' || value == 'true'
+}
L.Util.setNullableBooleanFromQueryString = function (options, name) {
- var 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;
- }
-};
+ var 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 = function (s) {
- s = s? s.toString() : '';
- return s.replace(/$1')
+ r = r.replace(/^## (.*)/gm, '$1
')
+ r = r.replace(/^# (.*)/gm, '$1
')
+ r = r.replace(/^---/gm, '
')
- // headings and hr
- r = r.replace(/^### (.*)/gm, '$1
');
- r = r.replace(/^## (.*)/gm, '$1
');
- r = r.replace(/^# (.*)/gm, '$1
');
- r = r.replace(/^---/gm, '
');
+ // bold, italics
+ r = r.replace(/\*\*(.*?)\*\*/g, '$1')
+ r = r.replace(/\*(.*?)\*/g, '$1')
- // bold, italics
- r = r.replace(/\*\*(.*?)\*\*/g, '$1');
- r = r.replace(/\*(.*?)\*/g, '$1');
+ // unordered lists
+ r = r.replace(/^\*\* (.*)/gm, '')
+ r = r.replace(/^\* (.*)/gm, '')
+ for (ii = 0; ii < 3; ii++)
+ r = r.replace(new RegExp('' + newline + '', 'g'), newline)
- // unordered lists
- r = r.replace(/^\*\* (.*)/gm, '');
- r = r.replace(/^\* (.*)/gm, '');
- for (ii = 0; ii < 3; ii++) r = r.replace(new RegExp('
' + newline + '', '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, '$1')
+ r = r.replace(/\[\[(h_t_t_ps?:[^\]|]*?)\]\]/g, '$1')
+ r = r.replace(
+ /\[\[(h_t_t_ps?:[^|]*?)\|(.*?)\]\]/g,
+ '$2'
+ )
+ r = r.replace(/\[\[([^\]|]*?)\]\]/g, '$1')
+ r = r.replace(/\[\[([^|]*?)\|(.*?)\]\]/g, '$2')
- // 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, '$1');
- r = r.replace(/\[\[(h_t_t_ps?:[^\]|]*?)\]\]/g, '$1');
- r = r.replace(/\[\[(h_t_t_ps?:[^|]*?)\|(.*?)\]\]/g, '$2');
- r = r.replace(/\[\[([^\]|]*?)\]\]/g, '$1');
- r = r.replace(/\[\[([^|]*?)\|(.*?)\]\]/g, '$2');
+ // iframe
+ r = r.replace(
+ /{{{(h_t_t_ps?[^ |{]*)}}}/g,
+ ''
+ )
+ r = r.replace(
+ /{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?}}}/g,
+ ''
+ )
+ r = r.replace(
+ /{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?\*(\d*)(px)?}}}/g,
+ ''
+ )
- // iframe
- r = r.replace(/{{{(h_t_t_ps?[^ |{]*)}}}/g, '');
- r = r.replace(/{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?}}}/g, '');
- r = r.replace(/{{{(h_t_t_ps?[^ |{]*)\|(\d*)(px)?\*(\d*)(px)?}}}/g, '');
+ // images
+ r = r.replace(/{{([^\]|]*?)}}/g, '
')
+ r = r.replace(/{{([^|]*?)\|(\d*?)}}/g, '
')
- // images
- r = r.replace(/{{([^\]|]*?)}}/g, '
');
- r = r.replace(/{{([^|]*?)\|(\d*?)}}/g, '
');
+ //Unescape http
+ r = r.replace(/(h_t_t_p)/g, 'http')
- //Unescape http
- r = r.replace(/(h_t_t_p)/g, 'http');
+ // Preserver line breaks
+ if (newline) r = r.replace(new RegExp(newline + '(?=[^]+)', 'g'), '
' + newline)
- // Preserver line breaks
- if (newline) r = r.replace(new RegExp(newline + '(?=[^]+)', 'g'), '
' + newline);
-
- return r;
-};
+ return r
+}
L.Util.isObject = function (what) {
- return typeof what === 'object' && what !== null;
-};
+ return typeof what === 'object' && what !== null
+}
L.Util.CopyJSON = function (geojson) {
- return JSON.parse(JSON.stringify(geojson));
-};
+ return JSON.parse(JSON.stringify(geojson))
+}
L.Util.detectFileType = function (f) {
- var 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';
-};
+ var 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 = function (options, option) {
- return options[option] !== undefined && options[option] !== '';
-};
+ return options[option] !== undefined && options[option] !== ''
+}
L.Util.greedyTemplate = function (str, data, ignore) {
- function getValue(data, path) {
- var value = data
- for (var i = 0; i < path.length; i++) {
- value = value[path[i]]
- if (value === undefined) break;
- }
- return value;
+ function getValue(data, path) {
+ var value = data
+ for (var i = 0; i < path.length; i++) {
+ value = value[path[i]]
+ if (value === undefined) break
}
+ return value
+ }
- return str.replace(/\{ *([\w_\:\.\|]+)(?:\|("[^"]*"))? *\}/g, function (str, key, staticFallback) {
- var vars = key.split('|'), value, path;
- if (staticFallback !== undefined) {
- vars.push(staticFallback);
- }
- for (var 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;
- });
-};
+ return str.replace(
+ /\{ *([\w_\:\.\|]+)(?:\|("[^"]*"))? *\}/g,
+ function (str, key, staticFallback) {
+ var vars = key.split('|'),
+ value,
+ path
+ if (staticFallback !== undefined) {
+ vars.push(staticFallback)
+ }
+ for (var 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.sortFeatures = function (features, sortKey) {
- var sortKeys = (sortKey || 'name').split(',');
+ var sortKeys = (sortKey || 'name').split(',')
- var sort = function (a, b, i) {
- var sortKey = sortKeys[i], score,
- valA = a.properties[sortKey] || '',
- valB = b.properties[sortKey] || '';
- if (!valA) {
- score = -1;
- } else if (!valB) {
- score = 1;
- } else {
- score = valA.toString().toLowerCase().localeCompare(valB.toString().toLowerCase());
- }
- if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1);
- return score;
- };
+ var sort = function (a, b, i) {
+ var sortKey = sortKeys[i],
+ score,
+ valA = a.properties[sortKey] || '',
+ valB = b.properties[sortKey] || ''
+ if (!valA) {
+ score = -1
+ } else if (!valB) {
+ score = 1
+ } else {
+ score = valA.toString().toLowerCase().localeCompare(valB.toString().toLowerCase())
+ }
+ if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1)
+ return score
+ }
- features.sort(function (a, b) {
- if (!a.properties || !b.properties) {
- return 0;
- }
- return sort(a, b, 0);
- });
+ features.sort(function (a, b) {
+ if (!a.properties || !b.properties) {
+ return 0
+ }
+ return sort(a, b, 0)
+ })
-
- return features;
-};
+ return features
+}
L.Util.flattenCoordinates = function (coords) {
- while (coords[0] && typeof coords[0][0] !== 'number') coords = coords[0];
- return coords;
-};
-
+ while (coords[0] && typeof coords[0][0] !== 'number') coords = coords[0]
+ return coords
+}
L.Util.buildQueryString = function (params) {
- var query_string = [];
- for (var key in params) {
- query_string.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
- }
- return query_string.join('&');
-};
+ var query_string = []
+ for (var key in params) {
+ query_string.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
+ }
+ return query_string.join('&')
+}
L.Util.getBaseUrl = function () {
- return '//' + window.location.host + window.location.pathname;
-};
+ return '//' + window.location.host + window.location.pathname
+}
L.DomUtil.add = function (tagName, className, container, content) {
- var el = L.DomUtil.create(tagName, className, container);
- if (content) {
- if (content.nodeType && content.nodeType === 1) {
- el.appendChild(content);
- }
- else {
- el.innerHTML = content;
- }
+ var el = L.DomUtil.create(tagName, className, container)
+ if (content) {
+ if (content.nodeType && content.nodeType === 1) {
+ el.appendChild(content)
+ } else {
+ el.innerHTML = content
}
- return el;
-};
+ }
+ return el
+}
L.DomUtil.createFieldset = function (container, legend, options) {
- options = options || {};
- var fieldset = L.DomUtil.create('div', 'fieldset toggle', container);
- var legendEl = L.DomUtil.add('h5', 'legend style_options_toggle', fieldset, legend);
- var fieldsEl = L.DomUtil.add('div', 'fields with-transition', fieldset);
- L.DomEvent.on(legendEl, 'click', function () {
- if (L.DomUtil.hasClass(fieldset, 'on')) {
- L.DomUtil.removeClass(fieldset, 'on');
- } else {
- L.DomUtil.addClass(fieldset, 'on');
- if (options.callback) options.callback.call(options.context || this);
- }
- });
- return fieldsEl;
-};
+ options = options || {}
+ var fieldset = L.DomUtil.create('div', 'fieldset toggle', container)
+ var legendEl = L.DomUtil.add('h5', 'legend style_options_toggle', fieldset, legend)
+ var fieldsEl = L.DomUtil.add('div', 'fields with-transition', fieldset)
+ L.DomEvent.on(legendEl, 'click', function () {
+ if (L.DomUtil.hasClass(fieldset, 'on')) {
+ L.DomUtil.removeClass(fieldset, 'on')
+ } else {
+ L.DomUtil.addClass(fieldset, 'on')
+ if (options.callback) options.callback.call(options.context || this)
+ }
+ })
+ return fieldsEl
+}
L.DomUtil.classIf = function (el, className, bool) {
- if (bool) L.DomUtil.addClass(el, className);
- else L.DomUtil.removeClass(el, className);
-};
-
+ if (bool) L.DomUtil.addClass(el, className)
+ else L.DomUtil.removeClass(el, className)
+}
L.DomUtil.element = function (what, attrs, parent) {
- var el = document.createElement(what);
- for (var attr in attrs) {
- el[attr] = attrs[attr];
- }
- if (typeof parent !== 'undefined') {
- parent.appendChild(el);
- }
- return el;
-};
-
+ var el = document.createElement(what)
+ for (var attr in attrs) {
+ el[attr] = attrs[attr]
+ }
+ if (typeof parent !== 'undefined') {
+ parent.appendChild(el)
+ }
+ return el
+}
L.DomUtil.before = function (target, el) {
- target.parentNode.insertBefore(el, target);
- return el;
-};
+ target.parentNode.insertBefore(el, target)
+ return el
+}
-L.DomUtil.after = function (target, el)
-{
- target.parentNode.insertBefore(el, target.nextSibling);
- return el;
-};
+L.DomUtil.after = function (target, el) {
+ target.parentNode.insertBefore(el, target.nextSibling)
+ return el
+}
-L.DomUtil.RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/;
+L.DomUtil.RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/
L.DomUtil.TextColorFromBackgroundColor = function (el) {
- var out = '#000000';
- if (!window.getComputedStyle) {return out;}
- var rgb = window.getComputedStyle(el).getPropertyValue('background-color');
- rgb = L.DomUtil.RGBRegex.exec(rgb);
- if (!rgb || rgb.length !== 4) {return out;}
- rgb = parseInt(rgb[1], 10) + parseInt(rgb[2], 10) + parseInt(rgb[3], 10);
- if (rgb < (255 * 3 / 2)) {
- out = '#ffffff';
- }
- return out;
-};
+ var out = '#000000'
+ if (!window.getComputedStyle) {
+ return out
+ }
+ var rgb = window.getComputedStyle(el).getPropertyValue('background-color')
+ rgb = L.DomUtil.RGBRegex.exec(rgb)
+ if (!rgb || rgb.length !== 4) {
+ return out
+ }
+ rgb = parseInt(rgb[1], 10) + parseInt(rgb[2], 10) + parseInt(rgb[3], 10)
+ if (rgb < (255 * 3) / 2) {
+ out = '#ffffff'
+ }
+ return out
+}
L.DomEvent.once = function (el, types, fn, context) {
- // cf https://github.com/Leaflet/Leaflet/pull/3528#issuecomment-134551575
+ // cf https://github.com/Leaflet/Leaflet/pull/3528#issuecomment-134551575
- if (typeof types === 'object') {
- for (var type in types) {
- L.DomEvent.once(el, type, types[type], fn);
- }
- return L.DomEvent;
+ if (typeof types === 'object') {
+ for (var type in types) {
+ L.DomEvent.once(el, type, types[type], fn)
}
-
- var handler = L.bind(function () {
- L.DomEvent
- .off(el, types, fn, context)
- .off(el, types, handler, context);
- }, L.DomEvent);
-
- // add a listener that's executed once and removed after that
return L.DomEvent
- .on(el, types, fn, context)
- .on(el, types, handler, context);
-};
+ }
+ var handler = L.bind(function () {
+ L.DomEvent.off(el, types, fn, context).off(el, types, handler, context)
+ }, L.DomEvent)
+
+ // add a listener that's executed once and removed after that
+ return L.DomEvent.on(el, types, fn, context).on(el, types, handler, context)
+}
/*
-* Global events
-*/
+ * Global events
+ */
L.U.Keys = {
- LEFT: 37,
- UP: 38,
- RIGHT: 39,
- DOWN: 40,
- TAB: 9,
- ENTER: 13,
- ESC: 27,
- APPLE: 91,
- SHIFT: 16,
- ALT: 17,
- CTRL: 18,
- E: 69,
- F: 70,
- H: 72,
- I: 73,
- L: 76,
- M: 77,
- P: 80,
- S: 83,
- Z: 90
-};
-
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ TAB: 9,
+ ENTER: 13,
+ ESC: 27,
+ APPLE: 91,
+ SHIFT: 16,
+ ALT: 17,
+ CTRL: 18,
+ E: 69,
+ F: 70,
+ H: 72,
+ I: 73,
+ L: 76,
+ M: 77,
+ P: 80,
+ S: 83,
+ Z: 90,
+}
L.U.Help = L.Class.extend({
+ initialize: function (map) {
+ this.map = map
+ this.box = L.DomUtil.create(
+ 'div',
+ 'umap-help-box with-transition dark',
+ document.body
+ )
+ var closeLink = L.DomUtil.create('a', 'umap-close-link', this.box)
+ closeLink.href = '#'
+ L.DomUtil.add('i', 'umap-close-icon', closeLink)
+ var label = L.DomUtil.create('span', '', closeLink)
+ label.title = label.textContent = L._('Close')
+ this.content = L.DomUtil.create('div', 'umap-help-content', this.box)
+ L.DomEvent.on(closeLink, 'click', this.hide, this)
+ },
- initialize: function (map) {
- this.map = map;
- this.box = L.DomUtil.create('div', 'umap-help-box with-transition dark', document.body);
- var closeLink = L.DomUtil.create('a', 'umap-close-link', this.box);
- closeLink.href = '#';
- L.DomUtil.add('i', 'umap-close-icon', closeLink);
- var label = L.DomUtil.create('span', '', closeLink);
- label.title = label.textContent = L._('Close');
- this.content = L.DomUtil.create('div', 'umap-help-content', this.box);
- L.DomEvent.on(closeLink, 'click', this.hide, this);
- },
+ onKeyDown: function (e) {
+ var key = e.keyCode,
+ ESC = 27
+ if (key === ESC) {
+ this.hide()
+ }
+ },
- onKeyDown: function (e) {
- var key = e.keyCode,
- ESC = 27;
- if (key === ESC) {
- this.hide();
- }
- },
+ show: function () {
+ this.content.innerHTML = ''
+ for (var i = 0, name; i < arguments.length; i++) {
+ name = arguments[i]
+ L.DomUtil.add('div', 'umap-help-entry', this.content, this.resolve(name))
+ }
+ L.DomUtil.addClass(document.body, 'umap-help-on')
+ },
- show: function () {
- this.content.innerHTML = '';
- for (var i = 0, name; i < arguments.length; i++) {
- name = arguments[i];
- L.DomUtil.add('div', 'umap-help-entry', this.content, this.resolve(name));
- }
- L.DomUtil.addClass(document.body, 'umap-help-on');
- },
+ hide: function () {
+ L.DomUtil.removeClass(document.body, 'umap-help-on')
+ },
- hide: function () {
- L.DomUtil.removeClass(document.body, 'umap-help-on');
- },
+ visible: function () {
+ return L.DomUtil.hasClass(document.body, 'umap-help-on')
+ },
- visible: function () {
- return L.DomUtil.hasClass(document.body, 'umap-help-on')
- },
+ resolve: function (name) {
+ return typeof this[name] === 'function' ? this[name]() : this[name]
+ },
- resolve: function (name) {
- return typeof this[name] === 'function' ? this[name]() : this[name];
- },
+ button: function (container, entries) {
+ var helpButton = L.DomUtil.create('a', 'umap-help-button', container)
+ helpButton.href = '#'
+ if (entries) {
+ L.DomEvent.on(helpButton, 'click', L.DomEvent.stop).on(
+ helpButton,
+ 'click',
+ function (e) {
+ var args = typeof entries === 'string' ? [entries] : entries
+ this.show.apply(this, args)
+ },
+ this
+ )
+ }
+ return helpButton
+ },
- button: function (container, entries) {
- var helpButton = L.DomUtil.create('a', 'umap-help-button', container);
- helpButton.href = '#';
- if (entries) {
- L.DomEvent
- .on(helpButton, 'click', L.DomEvent.stop)
- .on(helpButton, 'click', function (e) {
- var args = typeof entries === 'string' ? [entries] : entries;
- this.show.apply(this, args);
- }, this);
- }
- return helpButton;
- },
+ edit: function () {
+ var container = L.DomUtil.create('div', ''),
+ self = this,
+ title = L.DomUtil.create('h3', '', container),
+ actionsContainer = L.DomUtil.create('ul', 'umap-edit-actions', container)
+ var addAction = function (action) {
+ var actionContainer = L.DomUtil.add('li', '', actionsContainer)
+ L.DomUtil.add('i', action.options.className, actionContainer),
+ L.DomUtil.add('span', '', actionContainer, action.options.tooltip)
+ L.DomEvent.on(actionContainer, 'click', action.addHooks, action)
+ L.DomEvent.on(actionContainer, 'click', self.hide, self)
+ }
+ title.textContent = L._('Where do we go from here?')
+ for (var id in this.map.helpMenuActions) {
+ addAction(this.map.helpMenuActions[id])
+ }
+ return container
+ },
- edit: function () {
- var container = L.DomUtil.create('div', ''),
- self = this,
- title = L.DomUtil.create('h3', '', container),
- actionsContainer = L.DomUtil.create('ul', 'umap-edit-actions', container);
- var addAction = function (action) {
- var actionContainer = L.DomUtil.add('li', '', actionsContainer);
- L.DomUtil.add('i', action.options.className, actionContainer),
- L.DomUtil.add('span', '', actionContainer, action.options.tooltip);
- L.DomEvent.on(actionContainer, 'click', action.addHooks, action);
- L.DomEvent.on(actionContainer, 'click', self.hide, self);
- };
- title.textContent = L._('Where do we go from here?');
- for (var id in this.map.helpMenuActions) {
- addAction(this.map.helpMenuActions[id]);
- }
- return container;
- },
+ importFormats: function () {
+ var container = L.DomUtil.create('div')
+ L.DomUtil.add('h3', '', container, 'GeojSON')
+ L.DomUtil.add('p', '', container, L._('All properties are imported.'))
+ L.DomUtil.add('h3', '', container, 'GPX')
+ L.DomUtil.add('p', '', container, L._('Properties imported:') + 'name, desc')
+ L.DomUtil.add('h3', '', container, 'KML')
+ L.DomUtil.add('p', '', container, L._('Properties imported:') + 'name, description')
+ L.DomUtil.add('h3', '', container, 'CSV')
+ L.DomUtil.add(
+ 'p',
+ '',
+ container,
+ L._(
+ 'Comma, tab or semi-colon separated values. SRS WGS84 is implied. Only Point geometries are imported. The import will look at the column headers for any mention of «lat» and «lon» at the begining of the header, case insensitive. All other column are imported as properties.'
+ )
+ )
+ L.DomUtil.add('h3', '', container, 'uMap')
+ L.DomUtil.add(
+ 'p',
+ '',
+ container,
+ L._('Imports all umap data, including layers and settings.')
+ )
+ return container
+ },
- importFormats: function () {
- var container = L.DomUtil.create('div');
- L.DomUtil.add('h3', '', container, 'GeojSON');
- L.DomUtil.add('p', '', container, L._('All properties are imported.'));
- L.DomUtil.add('h3', '', container, 'GPX');
- L.DomUtil.add('p', '', container, L._('Properties imported:') + 'name, desc');
- L.DomUtil.add('h3', '', container, 'KML');
- L.DomUtil.add('p', '', container, L._('Properties imported:') + 'name, description');
- L.DomUtil.add('h3', '', container, 'CSV');
- L.DomUtil.add('p', '', container, L._('Comma, tab or semi-colon separated values. SRS WGS84 is implied. Only Point geometries are imported. The import will look at the column headers for any mention of «lat» and «lon» at the begining of the header, case insensitive. All other column are imported as properties.'));
- L.DomUtil.add('h3', '', container, 'uMap');
- L.DomUtil.add('p', '', container, L._('Imports all umap data, including layers and settings.'));
- return container;
- },
+ textFormatting: function () {
+ var container = L.DomUtil.create('div'),
+ title = L.DomUtil.add('h3', '', container, L._('Text formatting')),
+ elements = L.DomUtil.create('ul', '', container)
+ L.DomUtil.add('li', '', elements, L._('*simple star for italic*'))
+ L.DomUtil.add('li', '', elements, L._('**double star for bold**'))
+ L.DomUtil.add('li', '', elements, L._('# one hash for main heading'))
+ L.DomUtil.add('li', '', elements, L._('## two hashes for second heading'))
+ L.DomUtil.add('li', '', elements, L._('### three hashes for third heading'))
+ L.DomUtil.add('li', '', elements, L._('Simple link: [[http://example.com]]'))
+ L.DomUtil.add(
+ 'li',
+ '',
+ elements,
+ L._('Link with text: [[http://example.com|text of the link]]')
+ )
+ L.DomUtil.add('li', '', elements, L._('Image: {{http://image.url.com}}'))
+ L.DomUtil.add(
+ 'li',
+ '',
+ elements,
+ L._('Image with custom width (in px): {{http://image.url.com|width}}')
+ )
+ L.DomUtil.add('li', '', elements, L._('Iframe: {{{http://iframe.url.com}}}'))
+ L.DomUtil.add(
+ 'li',
+ '',
+ elements,
+ L._('Iframe with custom height (in px): {{{http://iframe.url.com|height}}}')
+ )
+ L.DomUtil.add(
+ 'li',
+ '',
+ elements,
+ L._(
+ 'Iframe with custom height and width (in px): {{{http://iframe.url.com|height*width}}}'
+ )
+ )
+ L.DomUtil.add('li', '', elements, L._('--- for an horizontal rule'))
+ return container
+ },
- textFormatting: function () {
- var container = L.DomUtil.create('div'),
- title = L.DomUtil.add('h3', '', container, L._('Text formatting')),
- elements = L.DomUtil.create('ul', '', container);
- L.DomUtil.add('li', '', elements, L._('*simple star for italic*'));
- L.DomUtil.add('li', '', elements, L._('**double star for bold**'));
- L.DomUtil.add('li', '', elements, L._('# one hash for main heading'));
- L.DomUtil.add('li', '', elements, L._('## two hashes for second heading'));
- L.DomUtil.add('li', '', elements, L._('### three hashes for third heading'));
- L.DomUtil.add('li', '', elements, L._('Simple link: [[http://example.com]]'));
- L.DomUtil.add('li', '', elements, L._('Link with text: [[http://example.com|text of the link]]'));
- L.DomUtil.add('li', '', elements, L._('Image: {{http://image.url.com}}'));
- L.DomUtil.add('li', '', elements, L._('Image with custom width (in px): {{http://image.url.com|width}}'));
- L.DomUtil.add('li', '', elements, L._('Iframe: {{{http://iframe.url.com}}}'));
- L.DomUtil.add('li', '', elements, L._('Iframe with custom height (in px): {{{http://iframe.url.com|height}}}'));
- L.DomUtil.add('li', '', elements, L._('Iframe with custom height and width (in px): {{{http://iframe.url.com|height*width}}}'));
- L.DomUtil.add('li', '', elements, L._('--- for an horizontal rule'));
- return container;
- },
-
- dynamicProperties: function () {
- var container = L.DomUtil.create('div');
- L.DomUtil.add('h3', '', container, L._('Dynamic properties'));
- L.DomUtil.add('p', '', container, L._('Use placeholders with feature properties between brackets, eg. {name}, they will be dynamically replaced by the corresponding values.'));
- return container;
- },
-
- formatURL: L._('Supported variables that will be dynamically replaced') + ': {bbox}, {lat}, {lng}, {zoom}, {east}, {north}..., {left}, {top}...',
- formatIconSymbol: L._('Symbol can be either a unicode character or an URL. You can use feature properties as variables: ex.: with "http://myserver.org/images/{name}.png", the {name} variable will be replaced by the "name" value of each marker.'),
- colorValue: L._('Must be a valid CSS value (eg.: DarkBlue or #123456)'),
- smoothFactor: L._('How much to simplify the polyline on each zoom level (more = better performance and smoother look, less = more accurate)'),
- dashArray: L._('A comma separated list of numbers that defines the stroke dash pattern. Ex.: "5, 10, 15".'),
- zoomTo: L._('Zoom level for automatic zooms'),
- labelKey: L._('The name of the property to use as feature label (ex.: "nom")'),
- stroke: L._('Whether to display or not polygons paths.'),
- fill: L._('Whether to fill polygons with color.'),
- fillColor: L._('Optional. Same as color if not set.'),
- shortCredit: L._('Will be displayed in the bottom right corner of the map'),
- longCredit: L._('Will be visible in the caption of the map'),
- permanentCredit: L._('Will be permanently visible in the bottom left corner of the map'),
- sortKey: L._('Property to use for sorting features'),
- slugKey: L._('The name of the property to use as feature unique identifier.'),
- filterKey: L._('Comma separated list of properties to use when filtering features'),
- advancedFilterKey: L._('Comma separated list of properties to use for checkbox filtering'),
- advancedFiltersNoResults: L._('No results for these filters'),
- interactive: L._('If false, the polygon will act as a part of the underlying map.'),
- outlink: L._('Define link to open in a new window on polygon click.'),
- dynamicRemoteData: L._('Fetch data each time map view changes.'),
- proxyRemoteData: L._('To use if remote server doesn\'t allow cross domain (slower)'),
- browsable: L._('Set it to false to hide this layer from the slideshow, the data browser, the popup navigation…')
-});
+ dynamicProperties: function () {
+ var container = L.DomUtil.create('div')
+ L.DomUtil.add('h3', '', container, L._('Dynamic properties'))
+ L.DomUtil.add(
+ 'p',
+ '',
+ container,
+ L._(
+ 'Use placeholders with feature properties between brackets, eg. {name}, they will be dynamically replaced by the corresponding values.'
+ )
+ )
+ return container
+ },
+ formatURL:
+ L._('Supported variables that will be dynamically replaced') +
+ ': {bbox}, {lat}, {lng}, {zoom}, {east}, {north}..., {left}, {top}...',
+ formatIconSymbol: L._(
+ 'Symbol can be either a unicode character or an URL. You can use feature properties as variables: ex.: with "http://myserver.org/images/{name}.png", the {name} variable will be replaced by the "name" value of each marker.'
+ ),
+ colorValue: L._('Must be a valid CSS value (eg.: DarkBlue or #123456)'),
+ smoothFactor: L._(
+ 'How much to simplify the polyline on each zoom level (more = better performance and smoother look, less = more accurate)'
+ ),
+ dashArray: L._(
+ 'A comma separated list of numbers that defines the stroke dash pattern. Ex.: "5, 10, 15".'
+ ),
+ zoomTo: L._('Zoom level for automatic zooms'),
+ labelKey: L._('The name of the property to use as feature label (ex.: "nom")'),
+ stroke: L._('Whether to display or not polygons paths.'),
+ fill: L._('Whether to fill polygons with color.'),
+ fillColor: L._('Optional. Same as color if not set.'),
+ shortCredit: L._('Will be displayed in the bottom right corner of the map'),
+ longCredit: L._('Will be visible in the caption of the map'),
+ permanentCredit: L._(
+ 'Will be permanently visible in the bottom left corner of the map'
+ ),
+ sortKey: L._('Property to use for sorting features'),
+ slugKey: L._('The name of the property to use as feature unique identifier.'),
+ filterKey: L._('Comma separated list of properties to use when filtering features'),
+ advancedFilterKey: L._(
+ 'Comma separated list of properties to use for checkbox filtering'
+ ),
+ advancedFiltersNoResults: L._('No results for these filters'),
+ interactive: L._('If false, the polygon will act as a part of the underlying map.'),
+ outlink: L._('Define link to open in a new window on polygon click.'),
+ dynamicRemoteData: L._('Fetch data each time map view changes.'),
+ proxyRemoteData: L._("To use if remote server doesn't allow cross domain (slower)"),
+ browsable: L._(
+ 'Set it to false to hide this layer from the slideshow, the data browser, the popup navigation…'
+ ),
+})
L.U.Orderable = L.Evented.extend({
+ options: {
+ selector: 'li',
+ color: '#374E75',
+ },
- options: {
- selector: 'li',
- color: '#374E75'
- },
+ initialize: function (parent, options) {
+ L.Util.setOptions(this, options)
+ this.parent = parent
+ this.src = null
+ this.dst = null
+ this.els = this.parent.querySelectorAll(this.options.selector)
+ for (var i = 0; i < this.els.length; i++) this.makeDraggable(this.els[i])
+ },
- initialize: function (parent, options) {
- L.Util.setOptions(this, options);
- this.parent = parent;
- this.src = null;
- this.dst = null;
- this.els = this.parent.querySelectorAll(this.options.selector);
- for (var i = 0; i < this.els.length; i++) this.makeDraggable(this.els[i]);
- },
+ makeDraggable: function (node) {
+ node.draggable = true
+ L.DomEvent.on(node, 'dragstart', this.onDragStart, this)
+ L.DomEvent.on(node, 'dragenter', this.onDragEnter, this)
+ L.DomEvent.on(node, 'dragover', this.onDragOver, this)
+ L.DomEvent.on(node, 'dragleave', this.onDragLeave, this)
+ L.DomEvent.on(node, 'drop', this.onDrop, this)
+ L.DomEvent.on(node, 'dragend', this.onDragEnd, this)
+ },
- makeDraggable: function (node) {
- node.draggable = true;
- L.DomEvent.on(node, 'dragstart', this.onDragStart, this);
- L.DomEvent.on(node, 'dragenter', this.onDragEnter, this);
- L.DomEvent.on(node, 'dragover', this.onDragOver, this);
- L.DomEvent.on(node, 'dragleave', this.onDragLeave, this);
- L.DomEvent.on(node, 'drop', this.onDrop, this);
- L.DomEvent.on(node, 'dragend', this.onDragEnd, this);
- },
+ nodeIndex: function (node) {
+ return Array.prototype.indexOf.call(this.parent.children, node)
+ },
- nodeIndex: function (node) {
- return Array.prototype.indexOf.call(this.parent.children, node);
- },
-
- findTarget: function (node) {
- while (node) {
- if (this.nodeIndex(node) !== -1) return node;
- node = node.parentNode;
- }
- },
-
- onDragStart: function (e) {
- // e.target is the source node.
- this.src = e.target;
- this.initialIndex = this.nodeIndex(this.src);
- this.srcBackgroundColor = this.src.style.backgroundColor;
- this.src.style.backgroundColor = this.options.color;
- e.dataTransfer.effectAllowed = 'move';
- e.dataTransfer.setData('text/html', this.src.innerHTML);
- },
-
- onDragOver: function (e) {
- if (e.preventDefault) e.preventDefault(); // Necessary. Allows us to drop.
- e.dataTransfer.dropEffect = 'move';
- return false;
- },
-
- onDragEnter: function (e) {
- // e.target is the current hover target.
- var dst = this.findTarget(e.target);
- if (!dst || dst === this.src) return;
- this.dst = dst;
- var targetIndex = this.nodeIndex(this.dst),
- srcIndex = this.nodeIndex(this.src);
- if (targetIndex > srcIndex) this.parent.insertBefore(this.dst, this.src);
- else this.parent.insertBefore(this.src, this.dst);
- },
-
- onDragLeave: function (e) {
- // e.target is previous target element.
- },
-
- onDrop: function (e) {
- // e.target is current target element.
- if (e.stopPropagation) e.stopPropagation(); // Stops the browser from redirecting.
- if (!this.dst) return;
- this.fire('drop', {
- src: this.src,
- initialIndex: this.initialIndex,
- finalIndex: this.nodeIndex(this.src),
- dst: this.dst
- });
- return false;
- },
-
- onDragEnd: function (e) {
- // e.target is the source node.
- this.src.style.backgroundColor = this.srcBackgroundColor;
+ findTarget: function (node) {
+ while (node) {
+ if (this.nodeIndex(node) !== -1) return node
+ node = node.parentNode
}
+ },
-});
+ onDragStart: function (e) {
+ // e.target is the source node.
+ this.src = e.target
+ this.initialIndex = this.nodeIndex(this.src)
+ this.srcBackgroundColor = this.src.style.backgroundColor
+ this.src.style.backgroundColor = this.options.color
+ e.dataTransfer.effectAllowed = 'move'
+ e.dataTransfer.setData('text/html', this.src.innerHTML)
+ },
+
+ onDragOver: function (e) {
+ if (e.preventDefault) e.preventDefault() // Necessary. Allows us to drop.
+ e.dataTransfer.dropEffect = 'move'
+ return false
+ },
+
+ onDragEnter: function (e) {
+ // e.target is the current hover target.
+ var dst = this.findTarget(e.target)
+ if (!dst || dst === this.src) return
+ this.dst = dst
+ var targetIndex = this.nodeIndex(this.dst),
+ srcIndex = this.nodeIndex(this.src)
+ if (targetIndex > srcIndex) this.parent.insertBefore(this.dst, this.src)
+ else this.parent.insertBefore(this.src, this.dst)
+ },
+
+ onDragLeave: function (e) {
+ // e.target is previous target element.
+ },
+
+ onDrop: function (e) {
+ // e.target is current target element.
+ if (e.stopPropagation) e.stopPropagation() // Stops the browser from redirecting.
+ if (!this.dst) return
+ this.fire('drop', {
+ src: this.src,
+ initialIndex: this.initialIndex,
+ finalIndex: this.nodeIndex(this.src),
+ dst: this.dst,
+ })
+ return false
+ },
+
+ onDragEnd: function (e) {
+ // e.target is the source node.
+ this.src.style.backgroundColor = this.srcBackgroundColor
+ },
+})
L.LatLng.prototype.isValid = function () {
- return !isNaN(this.lat) && !isNaN(this.lng);
+ return !isNaN(this.lat) && !isNaN(this.lng)
}
diff --git a/umap/static/umap/js/umap.features.js b/umap/static/umap/js/umap.features.js
index 2908a7ac..ba92dd38 100644
--- a/umap/static/umap/js/umap.features.js
+++ b/umap/static/umap/js/umap.features.js
@@ -1,1057 +1,1119 @@
L.U.FeatureMixin = {
+ staticOptions: {},
- staticOptions: {},
-
- initialize: function (map, latlng, options) {
- this.map = map;
- if(typeof options === 'undefined') {
- options = {};
- }
- // DataLayer the marker belongs to
- this.datalayer = options.datalayer || null;
- this.properties = {_umap_options: {}};
- if (options.geojson) {
- this.populate(options.geojson);
- }
- var isDirty = false,
- self = this;
- try {
- Object.defineProperty(this, 'isDirty', {
- get: function () {
- return isDirty;
- },
- set: function (status) {
- if (!isDirty && status) {
- self.fire('isdirty');
- }
- isDirty = status;
- if (self.datalayer) {
- self.datalayer.isDirty = status;
- }
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
- this.preInit();
- this.addInteractions();
- this.parentClass.prototype.initialize.call(this, latlng, options);
- },
-
- preInit: function () {},
-
- isReadOnly: function () {
- return this.datalayer && this.datalayer.isRemoteLayer();
- },
-
- getSlug: function () {
- return this.properties[this.map.options.slugKey || 'name'] || '';
- },
-
- getPermalink: function () {
- const slug = this.getSlug();
- if (slug) return L.Util.getBaseUrl() + "?" + L.Util.buildQueryString({feature: slug}) + window.location.hash;
- },
-
- view: function(e) {
- if (this.map.editEnabled) return;
- var outlink = this.properties._umap_options.outlink,
- target = this.properties._umap_options.outlinkTarget
- if (outlink) {
- switch (target) {
- case 'self':
- window.location = outlink;
- break;
- case 'parent':
- window.top.location = outlink;
- break;
- default:
- var win = window.open(this.properties._umap_options.outlink);
- }
- return;
- }
- // TODO deal with an event instead?
- if (this.map.slideshow) this.map.slideshow.current = this;
- this.map.currentFeature = this;
- this.attachPopup();
- this.openPopup(e && e.latlng || this.getCenter());
- },
-
- openPopup: function () {
- if (this.map.editEnabled) return;
- this.parentClass.prototype.openPopup.apply(this, arguments);
- },
-
- edit: function(e) {
- if(!this.map.editEnabled || this.isReadOnly()) return;
- var container = L.DomUtil.create('div', 'umap-datalayer-container');
-
- var builder = new L.U.FormBuilder(this, ['datalayer'], {
- callback: function () {this.edit(e);} // removeLayer step will close the edit panel, let's reopen it
- });
- container.appendChild(builder.build());
-
- var properties = [], property;
- for (var i = 0; i < this.datalayer._propertiesIndex.length; i++) {
- property = this.datalayer._propertiesIndex[i];
- if (L.Util.indexOf(['name', 'description'], property) !== -1) {continue;}
- properties.push(['properties.' + property, {label: property}]);
- }
- // We always want name and description for now (properties management to come)
- properties.unshift('properties.description');
- properties.unshift('properties.name');
- builder = new L.U.FormBuilder(this, properties,
- {
- id: 'umap-feature-properties',
- callback: this.resetTooltip
- }
- );
- container.appendChild(builder.build());
- this.appendEditFieldsets(container);
- var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'));
- this.getAdvancedEditActions(advancedActions);
- this.map.ui.openPanel({data: {html: container}, className: 'dark'});
- this.map.editedFeature = this;
- if (!this.isOnScreen()) this.zoomTo(e);
- },
-
- getAdvancedEditActions: function (container) {
- var deleteLink = L.DomUtil.create('a', 'button umap-delete', container);
- deleteLink.href = '#';
- deleteLink.textContent = L._('Delete');
- L.DomEvent.on(deleteLink, 'click', function (e) {
- L.DomEvent.stop(e);
- if (this.confirmDelete()) this.map.ui.closePanel();
- }, this);
- },
-
- appendEditFieldsets: function (container) {
- var optionsFields = this.getShapeOptions();
- var builder = new L.U.FormBuilder(this, optionsFields, {
- id: 'umap-feature-shape-properties',
- callback: this._redraw
- });
- var shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'));
- shapeProperties.appendChild(builder.build());
-
- var advancedOptions = this.getAdvancedOptions();
- var builder = new L.U.FormBuilder(this, advancedOptions, {
- id: 'umap-feature-advanced-properties',
- callback: this._redraw
- });
- var advancedProperties = L.DomUtil.createFieldset(container, L._('Advanced properties'));
- advancedProperties.appendChild(builder.build());
-
- var interactionOptions = this.getInteractionOptions();
- builder = new L.U.FormBuilder(this, interactionOptions, {
- callback: this._redraw
- });
- var popupFieldset = L.DomUtil.createFieldset(container, L._('Interaction options'));
- popupFieldset.appendChild(builder.build());
-
- },
-
- getInteractionOptions: function () {
- return [
- 'properties._umap_options.popupShape',
- 'properties._umap_options.popupTemplate',
- 'properties._umap_options.showLabel',
- 'properties._umap_options.labelDirection',
- 'properties._umap_options.labelInteractive'
- ];
- },
-
- endEdit: function () {},
-
- getDisplayName: function (fallback) {
- if (fallback === undefined) fallback = this.datalayer.options.name;
- var key = this.getOption('labelKey') || 'name';
- // Variables mode.
- if (key.indexOf("{") != -1) return L.Util.greedyTemplate(key, this.extendedProperties());
- // Simple mode.
- return this.properties[key] || this.properties.title || fallback;
- },
-
- hasPopupFooter: function () {
- if (L.Browser.ielt9) return false;
- if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic) return false;
- return this.map.options.displayPopupFooter;
- },
-
- getPopupClass: function () {
- var old = this.getOption('popupTemplate'); // Retrocompat.
- return L.U.Popup[this.getOption('popupShape') || old] || L.U.Popup;
- },
-
- attachPopup: function () {
- var Class = this.getPopupClass();
- this.bindPopup(new Class(this));
- },
-
- confirmDelete: function () {
- if (confirm(L._('Are you sure you want to delete the feature?'))) {
- this.del();
- return true;
- }
- return false;
- },
-
- del: function () {
- this.isDirty = true;
- this.map.closePopup();
- if (this.datalayer) {
- this.datalayer.removeLayer(this);
- this.disconnectFromDataLayer(this.datalayer);
- }
- },
-
- connectToDataLayer: function (datalayer) {
- this.datalayer = datalayer;
- this.options.renderer = this.datalayer.renderer;
- },
-
- disconnectFromDataLayer: function (datalayer) {
- if (this.datalayer === datalayer) {
- this.datalayer = null;
- }
- },
-
- populate: function (feature) {
- this.properties = L.extend({}, feature.properties);
- this.properties._umap_options = L.extend({}, this.properties._storage_options, this.properties._umap_options);
- // Retrocompat
- if (this.properties._umap_options.clickable === false) {
- this.properties._umap_options.interactive = false;
- delete this.properties._umap_options.clickable;
- }
- },
-
- changeDataLayer: function(datalayer) {
- if(this.datalayer) {
- this.datalayer.isDirty = true;
- this.datalayer.removeLayer(this);
- }
- datalayer.addLayer(this);
- datalayer.isDirty = true;
- this._redraw();
- },
-
- getOption: function (option, fallback) {
- var value = fallback;
- if (typeof this.staticOptions[option] !== 'undefined') {
- value = this.staticOptions[option];
- }
- else if (L.Util.usableOption(this.properties._umap_options, option)) {
- value = this.properties._umap_options[option];
- }
- else if (this.datalayer) {
- value = this.datalayer.getOption(option);
- }
- else {
- value = this.map.getOption(option);
- }
- return value;
- },
-
- zoomTo: function (e) {
- e = e || {};
- var easing = e.easing !== undefined ? e.easing : this.map.options.easing;
- if (easing) {
- this.map.flyTo(this.getCenter(), this.getBestZoom());
- }
- else {
- var latlng = e.latlng || this.getCenter();
- this.map.setView(latlng, this.getBestZoom() || this.map.getZoom());
- }
- if (e.callback) e.callback.call(this);
- },
-
- getBestZoom: function () {
- return this.getOption('zoomTo');
- },
-
- getNext: function () {
- return this.datalayer.getNextFeature(this);
- },
-
- getPrevious: function () {
- return this.datalayer.getPreviousFeature(this);
- },
-
- cloneProperties: function () {
- var properties = L.extend({}, this.properties);
- properties._umap_options = L.extend({}, properties._umap_options);
- if (Object.keys && Object.keys(properties._umap_options).length === 0) {
- delete properties._umap_options; // It can make a difference on big data sets
- }
- return properties;
- },
-
- deleteProperty: function (property) {
- delete this.properties[property];
- this.makeDirty();
- },
-
- renameProperty: function (from, to) {
- this.properties[to] = this.properties[from];
- this.deleteProperty(from);
- },
-
- toGeoJSON: function () {
- var geojson = this.parentClass.prototype.toGeoJSON.call(this);
- geojson.properties = this.cloneProperties();
- delete geojson.properties._storage_options;
- return geojson;
- },
-
- addInteractions: function () {
- this.on('contextmenu editable:vertex:contextmenu', this._showContextMenu, this);
- this.on('click', this._onClick);
- },
-
- _onClick: function (e) {
- if (this.map.measureTools && this.map.measureTools.enabled()) return;
- this._popupHandlersAdded = true; // Prevent leaflet from managing event
- if(!this.map.editEnabled) {
- this.view(e);
- } else if (!this.isReadOnly()) {
- if(e.originalEvent.shiftKey) {
- if(this._toggleEditing)
- this._toggleEditing(e);
- else
- this.edit(e);
- }
- else {
- new L.Toolbar.Popup(e.latlng, {
- className: 'leaflet-inplace-toolbar',
- anchor: this.getPopupToolbarAnchor(),
- actions: this.getInplaceToolbarActions(e)
- }).addTo(this.map, this, e.latlng);
- }
- }
- L.DomEvent.stop(e);
- },
-
- getPopupToolbarAnchor: function () {
- return [0, 0];
- },
-
- getInplaceToolbarActions: function (e) {
- return [L.U.ToggleEditAction, L.U.DeleteFeatureAction];
- },
-
- _showContextMenu: function (e) {
- L.DomEvent.stop(e);
- var pt = this.map.mouseEventToContainerPoint(e.originalEvent);
- e.relatedTarget = this;
- this.map.contextmenu.showAt(pt, e);
- },
-
- makeDirty: function () {
- this.isDirty = true;
- },
-
- getMap: function () {
- return this.map;
- },
-
- getContextMenuItems: function (e) {
- var permalink = this.getPermalink(),
- items = [];
- if (permalink) items.push({text: L._('Permalink'), callback: function() {window.open(permalink);}});
- if (this.map.editEnabled && !this.isReadOnly()) {
- items = items.concat(this.getContextMenuEditItems(e));
- }
- return items;
- },
-
- getContextMenuEditItems: function () {
- var items = ['-'];
- if (this.map.editedFeature !== this) {
- items.push(
- {
- text: L._('Edit this feature'),
- callback: this.edit,
- context: this,
- iconCls: 'umap-edit'
- }
- );
- }
- items = items.concat(
- {
- text: L._('Edit feature\'s layer'),
- callback: this.datalayer.edit,
- context: this.datalayer,
- iconCls: 'umap-edit'
- },
- {
- text: L._('Delete this feature'),
- callback: this.confirmDelete,
- context: this,
- iconCls: 'umap-delete'
- },
- {
- text: L._('Clone this feature'),
- callback: this.clone,
- context: this
- }
- );
- return items;
- },
-
- onRemove: function (map) {
- this.parentClass.prototype.onRemove.call(this, map);
- if (this.map.editedFeature === this) {
- this.endEdit();
- this.map.ui.closePanel();
- }
- },
-
- resetTooltip: function () {
- var displayName = this.getDisplayName(null),
- showLabel = this.getOption('showLabel'),
- oldLabelHover = this.getOption('labelHover'),
- options = {
- direction: this.getOption('labelDirection'),
- interactive: this.getOption('labelInteractive')
- };
- if (oldLabelHover && showLabel) showLabel = null; // Retrocompat.
- options.permanent = showLabel === true;
- this.unbindTooltip();
- if ((showLabel === true || showLabel === null) && displayName) this.bindTooltip(L.Util.escapeHTML(displayName), options);
- },
-
- matchFilter: function (filter, keys) {
- filter = filter.toLowerCase();
- for (var i = 0; i < keys.length; i++) {
- if ((this.properties[keys[i]] || '').toLowerCase().indexOf(filter) !== -1) return true;
- }
- return false;
- },
-
- onVertexRawClick: function (e) {
- new L.Toolbar.Popup(e.latlng, {
- className: 'leaflet-inplace-toolbar',
- actions: this.getVertexActions(e)
- }).addTo(this.map, this, e.latlng, e.vertex);
- },
-
- getVertexActions: function () {
- return [L.U.DeleteVertexAction];
- },
-
- isMulti: function () {
- return false;
- },
-
- clone: function () {
- var layer = this.datalayer.geojsonToFeatures(this.toGeoJSON());
- layer.isDirty = true;
- layer.edit();
- return layer;
- },
-
- extendedProperties: function () {
- // Include context properties
- properties = this.map.getGeoContext();
- center = this.getCenter();
- properties.lat = center.lat;
- properties.lon = center.lng;
- properties.lng = center.lng;
- properties.rank = this.getRank() + 1;
- if (typeof this.getMeasure !== 'undefined') {
- properties.measure = this.getMeasure();
- }
- return L.extend(properties, this.properties);
- },
-
- getRank: function () {
- return this.datalayer._index.indexOf(L.stamp(this));
+ initialize: function (map, latlng, options) {
+ this.map = map
+ if (typeof options === 'undefined') {
+ options = {}
}
+ // DataLayer the marker belongs to
+ this.datalayer = options.datalayer || null
+ this.properties = { _umap_options: {} }
+ if (options.geojson) {
+ this.populate(options.geojson)
+ }
+ var isDirty = false,
+ self = this
+ try {
+ Object.defineProperty(this, 'isDirty', {
+ get: function () {
+ return isDirty
+ },
+ set: function (status) {
+ if (!isDirty && status) {
+ self.fire('isdirty')
+ }
+ isDirty = status
+ if (self.datalayer) {
+ self.datalayer.isDirty = status
+ }
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
+ }
+ this.preInit()
+ this.addInteractions()
+ this.parentClass.prototype.initialize.call(this, latlng, options)
+ },
-};
+ preInit: function () {},
+
+ isReadOnly: function () {
+ return this.datalayer && this.datalayer.isRemoteLayer()
+ },
+
+ getSlug: function () {
+ return this.properties[this.map.options.slugKey || 'name'] || ''
+ },
+
+ getPermalink: function () {
+ const slug = this.getSlug()
+ if (slug)
+ return (
+ L.Util.getBaseUrl() +
+ '?' +
+ L.Util.buildQueryString({ feature: slug }) +
+ window.location.hash
+ )
+ },
+
+ view: function (e) {
+ if (this.map.editEnabled) return
+ var outlink = this.properties._umap_options.outlink,
+ target = this.properties._umap_options.outlinkTarget
+ if (outlink) {
+ switch (target) {
+ case 'self':
+ window.location = outlink
+ break
+ case 'parent':
+ window.top.location = outlink
+ break
+ default:
+ var win = window.open(this.properties._umap_options.outlink)
+ }
+ return
+ }
+ // TODO deal with an event instead?
+ if (this.map.slideshow) this.map.slideshow.current = this
+ this.map.currentFeature = this
+ this.attachPopup()
+ this.openPopup((e && e.latlng) || this.getCenter())
+ },
+
+ openPopup: function () {
+ if (this.map.editEnabled) return
+ this.parentClass.prototype.openPopup.apply(this, arguments)
+ },
+
+ edit: function (e) {
+ if (!this.map.editEnabled || this.isReadOnly()) return
+ var container = L.DomUtil.create('div', 'umap-datalayer-container')
+
+ var builder = new L.U.FormBuilder(this, ['datalayer'], {
+ callback: function () {
+ this.edit(e)
+ }, // removeLayer step will close the edit panel, let's reopen it
+ })
+ container.appendChild(builder.build())
+
+ var properties = [],
+ property
+ for (var i = 0; i < this.datalayer._propertiesIndex.length; i++) {
+ property = this.datalayer._propertiesIndex[i]
+ if (L.Util.indexOf(['name', 'description'], property) !== -1) {
+ continue
+ }
+ properties.push(['properties.' + property, { label: property }])
+ }
+ // We always want name and description for now (properties management to come)
+ properties.unshift('properties.description')
+ properties.unshift('properties.name')
+ builder = new L.U.FormBuilder(this, properties, {
+ id: 'umap-feature-properties',
+ callback: this.resetTooltip,
+ })
+ container.appendChild(builder.build())
+ this.appendEditFieldsets(container)
+ var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
+ this.getAdvancedEditActions(advancedActions)
+ this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
+ this.map.editedFeature = this
+ if (!this.isOnScreen()) this.zoomTo(e)
+ },
+
+ getAdvancedEditActions: function (container) {
+ var deleteLink = L.DomUtil.create('a', 'button umap-delete', container)
+ deleteLink.href = '#'
+ deleteLink.textContent = L._('Delete')
+ L.DomEvent.on(
+ deleteLink,
+ 'click',
+ function (e) {
+ L.DomEvent.stop(e)
+ if (this.confirmDelete()) this.map.ui.closePanel()
+ },
+ this
+ )
+ },
+
+ appendEditFieldsets: function (container) {
+ var optionsFields = this.getShapeOptions()
+ var builder = new L.U.FormBuilder(this, optionsFields, {
+ id: 'umap-feature-shape-properties',
+ callback: this._redraw,
+ })
+ var shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'))
+ shapeProperties.appendChild(builder.build())
+
+ var advancedOptions = this.getAdvancedOptions()
+ var builder = new L.U.FormBuilder(this, advancedOptions, {
+ id: 'umap-feature-advanced-properties',
+ callback: this._redraw,
+ })
+ var advancedProperties = L.DomUtil.createFieldset(
+ container,
+ L._('Advanced properties')
+ )
+ advancedProperties.appendChild(builder.build())
+
+ var interactionOptions = this.getInteractionOptions()
+ builder = new L.U.FormBuilder(this, interactionOptions, {
+ callback: this._redraw,
+ })
+ var popupFieldset = L.DomUtil.createFieldset(container, L._('Interaction options'))
+ popupFieldset.appendChild(builder.build())
+ },
+
+ getInteractionOptions: function () {
+ return [
+ 'properties._umap_options.popupShape',
+ 'properties._umap_options.popupTemplate',
+ 'properties._umap_options.showLabel',
+ 'properties._umap_options.labelDirection',
+ 'properties._umap_options.labelInteractive',
+ ]
+ },
+
+ endEdit: function () {},
+
+ getDisplayName: function (fallback) {
+ if (fallback === undefined) fallback = this.datalayer.options.name
+ var key = this.getOption('labelKey') || 'name'
+ // Variables mode.
+ if (key.indexOf('{') != -1)
+ return L.Util.greedyTemplate(key, this.extendedProperties())
+ // Simple mode.
+ return this.properties[key] || this.properties.title || fallback
+ },
+
+ hasPopupFooter: function () {
+ if (L.Browser.ielt9) return false
+ if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic)
+ return false
+ return this.map.options.displayPopupFooter
+ },
+
+ getPopupClass: function () {
+ var old = this.getOption('popupTemplate') // Retrocompat.
+ return L.U.Popup[this.getOption('popupShape') || old] || L.U.Popup
+ },
+
+ attachPopup: function () {
+ var Class = this.getPopupClass()
+ this.bindPopup(new Class(this))
+ },
+
+ confirmDelete: function () {
+ if (confirm(L._('Are you sure you want to delete the feature?'))) {
+ this.del()
+ return true
+ }
+ return false
+ },
+
+ del: function () {
+ this.isDirty = true
+ this.map.closePopup()
+ if (this.datalayer) {
+ this.datalayer.removeLayer(this)
+ this.disconnectFromDataLayer(this.datalayer)
+ }
+ },
+
+ connectToDataLayer: function (datalayer) {
+ this.datalayer = datalayer
+ this.options.renderer = this.datalayer.renderer
+ },
+
+ disconnectFromDataLayer: function (datalayer) {
+ if (this.datalayer === datalayer) {
+ this.datalayer = null
+ }
+ },
+
+ populate: function (feature) {
+ this.properties = L.extend({}, feature.properties)
+ this.properties._umap_options = L.extend(
+ {},
+ this.properties._storage_options,
+ this.properties._umap_options
+ )
+ // Retrocompat
+ if (this.properties._umap_options.clickable === false) {
+ this.properties._umap_options.interactive = false
+ delete this.properties._umap_options.clickable
+ }
+ },
+
+ changeDataLayer: function (datalayer) {
+ if (this.datalayer) {
+ this.datalayer.isDirty = true
+ this.datalayer.removeLayer(this)
+ }
+ datalayer.addLayer(this)
+ datalayer.isDirty = true
+ this._redraw()
+ },
+
+ getOption: function (option, fallback) {
+ var value = fallback
+ if (typeof this.staticOptions[option] !== 'undefined') {
+ value = this.staticOptions[option]
+ } else if (L.Util.usableOption(this.properties._umap_options, option)) {
+ value = this.properties._umap_options[option]
+ } else if (this.datalayer) {
+ value = this.datalayer.getOption(option)
+ } else {
+ value = this.map.getOption(option)
+ }
+ return value
+ },
+
+ zoomTo: function (e) {
+ e = e || {}
+ var easing = e.easing !== undefined ? e.easing : this.map.options.easing
+ if (easing) {
+ this.map.flyTo(this.getCenter(), this.getBestZoom())
+ } else {
+ var latlng = e.latlng || this.getCenter()
+ this.map.setView(latlng, this.getBestZoom() || this.map.getZoom())
+ }
+ if (e.callback) e.callback.call(this)
+ },
+
+ getBestZoom: function () {
+ return this.getOption('zoomTo')
+ },
+
+ getNext: function () {
+ return this.datalayer.getNextFeature(this)
+ },
+
+ getPrevious: function () {
+ return this.datalayer.getPreviousFeature(this)
+ },
+
+ cloneProperties: function () {
+ var properties = L.extend({}, this.properties)
+ properties._umap_options = L.extend({}, properties._umap_options)
+ if (Object.keys && Object.keys(properties._umap_options).length === 0) {
+ delete properties._umap_options // It can make a difference on big data sets
+ }
+ return properties
+ },
+
+ deleteProperty: function (property) {
+ delete this.properties[property]
+ this.makeDirty()
+ },
+
+ renameProperty: function (from, to) {
+ this.properties[to] = this.properties[from]
+ this.deleteProperty(from)
+ },
+
+ toGeoJSON: function () {
+ var geojson = this.parentClass.prototype.toGeoJSON.call(this)
+ geojson.properties = this.cloneProperties()
+ delete geojson.properties._storage_options
+ return geojson
+ },
+
+ addInteractions: function () {
+ this.on('contextmenu editable:vertex:contextmenu', this._showContextMenu, this)
+ this.on('click', this._onClick)
+ },
+
+ _onClick: function (e) {
+ if (this.map.measureTools && this.map.measureTools.enabled()) return
+ this._popupHandlersAdded = true // Prevent leaflet from managing event
+ if (!this.map.editEnabled) {
+ this.view(e)
+ } else if (!this.isReadOnly()) {
+ if (e.originalEvent.shiftKey) {
+ if (this._toggleEditing) this._toggleEditing(e)
+ else this.edit(e)
+ } else {
+ new L.Toolbar.Popup(e.latlng, {
+ className: 'leaflet-inplace-toolbar',
+ anchor: this.getPopupToolbarAnchor(),
+ actions: this.getInplaceToolbarActions(e),
+ }).addTo(this.map, this, e.latlng)
+ }
+ }
+ L.DomEvent.stop(e)
+ },
+
+ getPopupToolbarAnchor: function () {
+ return [0, 0]
+ },
+
+ getInplaceToolbarActions: function (e) {
+ return [L.U.ToggleEditAction, L.U.DeleteFeatureAction]
+ },
+
+ _showContextMenu: function (e) {
+ L.DomEvent.stop(e)
+ var pt = this.map.mouseEventToContainerPoint(e.originalEvent)
+ e.relatedTarget = this
+ this.map.contextmenu.showAt(pt, e)
+ },
+
+ makeDirty: function () {
+ this.isDirty = true
+ },
+
+ getMap: function () {
+ return this.map
+ },
+
+ getContextMenuItems: function (e) {
+ var permalink = this.getPermalink(),
+ items = []
+ if (permalink)
+ items.push({
+ text: L._('Permalink'),
+ callback: function () {
+ window.open(permalink)
+ },
+ })
+ if (this.map.editEnabled && !this.isReadOnly()) {
+ items = items.concat(this.getContextMenuEditItems(e))
+ }
+ return items
+ },
+
+ getContextMenuEditItems: function () {
+ var items = ['-']
+ if (this.map.editedFeature !== this) {
+ items.push({
+ text: L._('Edit this feature'),
+ callback: this.edit,
+ context: this,
+ iconCls: 'umap-edit',
+ })
+ }
+ items = items.concat(
+ {
+ text: L._("Edit feature's layer"),
+ callback: this.datalayer.edit,
+ context: this.datalayer,
+ iconCls: 'umap-edit',
+ },
+ {
+ text: L._('Delete this feature'),
+ callback: this.confirmDelete,
+ context: this,
+ iconCls: 'umap-delete',
+ },
+ {
+ text: L._('Clone this feature'),
+ callback: this.clone,
+ context: this,
+ }
+ )
+ return items
+ },
+
+ onRemove: function (map) {
+ this.parentClass.prototype.onRemove.call(this, map)
+ if (this.map.editedFeature === this) {
+ this.endEdit()
+ this.map.ui.closePanel()
+ }
+ },
+
+ resetTooltip: function () {
+ var displayName = this.getDisplayName(null),
+ showLabel = this.getOption('showLabel'),
+ oldLabelHover = this.getOption('labelHover'),
+ options = {
+ direction: this.getOption('labelDirection'),
+ interactive: this.getOption('labelInteractive'),
+ }
+ if (oldLabelHover && showLabel) showLabel = null // Retrocompat.
+ options.permanent = showLabel === true
+ this.unbindTooltip()
+ if ((showLabel === true || showLabel === null) && displayName)
+ this.bindTooltip(L.Util.escapeHTML(displayName), options)
+ },
+
+ matchFilter: function (filter, keys) {
+ filter = filter.toLowerCase()
+ for (var i = 0; i < keys.length; i++) {
+ if ((this.properties[keys[i]] || '').toLowerCase().indexOf(filter) !== -1)
+ return true
+ }
+ return false
+ },
+
+ onVertexRawClick: function (e) {
+ new L.Toolbar.Popup(e.latlng, {
+ className: 'leaflet-inplace-toolbar',
+ actions: this.getVertexActions(e),
+ }).addTo(this.map, this, e.latlng, e.vertex)
+ },
+
+ getVertexActions: function () {
+ return [L.U.DeleteVertexAction]
+ },
+
+ isMulti: function () {
+ return false
+ },
+
+ clone: function () {
+ var layer = this.datalayer.geojsonToFeatures(this.toGeoJSON())
+ layer.isDirty = true
+ layer.edit()
+ return layer
+ },
+
+ extendedProperties: function () {
+ // Include context properties
+ properties = this.map.getGeoContext()
+ center = this.getCenter()
+ properties.lat = center.lat
+ properties.lon = center.lng
+ properties.lng = center.lng
+ properties.rank = this.getRank() + 1
+ if (typeof this.getMeasure !== 'undefined') {
+ properties.measure = this.getMeasure()
+ }
+ return L.extend(properties, this.properties)
+ },
+
+ getRank: function () {
+ return this.datalayer._index.indexOf(L.stamp(this))
+ },
+}
L.U.Marker = L.Marker.extend({
- parentClass: L.Marker,
- includes: [L.U.FeatureMixin],
+ parentClass: L.Marker,
+ includes: [L.U.FeatureMixin],
- preInit: function () {
- this.setIcon(this.getIcon());
- },
+ preInit: function () {
+ this.setIcon(this.getIcon())
+ },
- addInteractions: function () {
- L.U.FeatureMixin.addInteractions.call(this);
- this.on('dragend', function (e) {
- this.isDirty = true;
- this.edit(e);
- }, this);
- if (!this.isReadOnly()) this.on('mouseover', this._enableDragging);
- this.on('mouseout', this._onMouseOut);
- this._popupHandlersAdded = true; // prevent Leaflet from binding event on bindPopup
- },
+ addInteractions: function () {
+ L.U.FeatureMixin.addInteractions.call(this)
+ this.on(
+ 'dragend',
+ function (e) {
+ this.isDirty = true
+ this.edit(e)
+ },
+ this
+ )
+ if (!this.isReadOnly()) this.on('mouseover', this._enableDragging)
+ this.on('mouseout', this._onMouseOut)
+ this._popupHandlersAdded = true // prevent Leaflet from binding event on bindPopup
+ },
- _onMouseOut: function () {
- if(this.dragging && this.dragging._draggable && !this.dragging._draggable._moving) {
- // Do not disable if the mouse went out while dragging
- this._disableDragging();
- }
- },
-
- _enableDragging: function () {
- // TODO: start dragging after 1 second on mouse down
- if(this.map.editEnabled) {
- if (!this.editEnabled()) this.enableEdit();
- // Enabling dragging on the marker override the Draggable._OnDown
- // event, which, as it stopPropagation, refrain the call of
- // _onDown with map-pane element, which is responsible to
- // set the _moved to false, and thus to enable the click.
- // We should find a cleaner way to handle this.
- this.map.dragging._draggable._moved = false;
- }
- },
-
- _disableDragging: function () {
- if(this.map.editEnabled) {
- if (this.editor && this.editor.drawing) return; // when creating a new marker, the mouse can trigger the mouseover/mouseout event
- // do not listen to them
- this.disableEdit();
- }
- },
-
- _redraw: function() {
- if (this.datalayer && this.datalayer.isVisible()) {
- this._initIcon();
- this.update();
- }
- },
-
- _initIcon: function () {
- this.options.icon = this.getIcon();
- L.Marker.prototype._initIcon.call(this);
- this.resetTooltip();
- },
-
- disconnectFromDataLayer: function (datalayer) {
- this.options.icon.datalayer = null;
- L.U.FeatureMixin.disconnectFromDataLayer.call(this, datalayer);
- },
-
- _getIconUrl: function (name) {
- if (typeof name === 'undefined') name = 'icon';
- return this.getOption(name + 'Url');
- },
-
- getIconClass: function () {
- return this.getOption('iconClass');
- },
-
- getIcon: function () {
- var Class = L.U.Icon[this.getIconClass()] || L.U.Icon.Default;
- return new Class(this.map, {feature: this});
- },
-
- getCenter: function () {
- return this._latlng;
- },
-
- getClassName: function () {
- return 'marker';
- },
-
- getShapeOptions: function () {
- return [
- 'properties._umap_options.color',
- 'properties._umap_options.iconClass',
- 'properties._umap_options.iconUrl'
- ];
- },
-
- getAdvancedOptions: function () {
- return [
- 'properties._umap_options.zoomTo'
- ];
- },
-
- appendEditFieldsets: function (container) {
- L.U.FeatureMixin.appendEditFieldsets.call(this, container);
- var coordinatesOptions = [
- ['_latlng.lat', {handler: 'FloatInput', label: L._('Latitude')}],
- ['_latlng.lng', {handler: 'FloatInput', label: L._('Longitude')}]
- ];
- var builder = new L.U.FormBuilder(this, coordinatesOptions, {
- callback: function () {
- if (!this._latlng.isValid()) return this.map.ui.alert({content: L._('Invalid latitude or longitude'), level: 'error'});
- this._redraw();
- this.zoomTo({easing: false});
- },
- callbackContext: this
- });
- var fieldset = L.DomUtil.createFieldset(container, L._('Coordinates'));
- fieldset.appendChild(builder.build());
- },
-
- zoomTo: function (e) {
- if (this.datalayer.isClustered() && !this._icon) {
- // callback is mandatory for zoomToShowLayer
- this.datalayer.layer.zoomToShowLayer(this, e.callback || function (){});
- } else {
- L.U.FeatureMixin.zoomTo.call(this, e);
- }
- },
-
- isOnScreen: function () {
- var bounds = this.map.getBounds();
- return bounds.contains(this._latlng);
- },
-
- getPopupToolbarAnchor: function () {
- return this.options.icon.options.popupAnchor;
+ _onMouseOut: function () {
+ if (
+ this.dragging &&
+ this.dragging._draggable &&
+ !this.dragging._draggable._moving
+ ) {
+ // Do not disable if the mouse went out while dragging
+ this._disableDragging()
}
+ },
-});
+ _enableDragging: function () {
+ // TODO: start dragging after 1 second on mouse down
+ if (this.map.editEnabled) {
+ if (!this.editEnabled()) this.enableEdit()
+ // Enabling dragging on the marker override the Draggable._OnDown
+ // event, which, as it stopPropagation, refrain the call of
+ // _onDown with map-pane element, which is responsible to
+ // set the _moved to false, and thus to enable the click.
+ // We should find a cleaner way to handle this.
+ this.map.dragging._draggable._moved = false
+ }
+ },
+ _disableDragging: function () {
+ if (this.map.editEnabled) {
+ if (this.editor && this.editor.drawing) return // when creating a new marker, the mouse can trigger the mouseover/mouseout event
+ // do not listen to them
+ this.disableEdit()
+ }
+ },
+
+ _redraw: function () {
+ if (this.datalayer && this.datalayer.isVisible()) {
+ this._initIcon()
+ this.update()
+ }
+ },
+
+ _initIcon: function () {
+ this.options.icon = this.getIcon()
+ L.Marker.prototype._initIcon.call(this)
+ this.resetTooltip()
+ },
+
+ disconnectFromDataLayer: function (datalayer) {
+ this.options.icon.datalayer = null
+ L.U.FeatureMixin.disconnectFromDataLayer.call(this, datalayer)
+ },
+
+ _getIconUrl: function (name) {
+ if (typeof name === 'undefined') name = 'icon'
+ return this.getOption(name + 'Url')
+ },
+
+ getIconClass: function () {
+ return this.getOption('iconClass')
+ },
+
+ getIcon: function () {
+ var Class = L.U.Icon[this.getIconClass()] || L.U.Icon.Default
+ return new Class(this.map, { feature: this })
+ },
+
+ getCenter: function () {
+ return this._latlng
+ },
+
+ getClassName: function () {
+ return 'marker'
+ },
+
+ getShapeOptions: function () {
+ return [
+ 'properties._umap_options.color',
+ 'properties._umap_options.iconClass',
+ 'properties._umap_options.iconUrl',
+ ]
+ },
+
+ getAdvancedOptions: function () {
+ return ['properties._umap_options.zoomTo']
+ },
+
+ appendEditFieldsets: function (container) {
+ L.U.FeatureMixin.appendEditFieldsets.call(this, container)
+ var coordinatesOptions = [
+ ['_latlng.lat', { handler: 'FloatInput', label: L._('Latitude') }],
+ ['_latlng.lng', { handler: 'FloatInput', label: L._('Longitude') }],
+ ]
+ var builder = new L.U.FormBuilder(this, coordinatesOptions, {
+ callback: function () {
+ if (!this._latlng.isValid())
+ return this.map.ui.alert({
+ content: L._('Invalid latitude or longitude'),
+ level: 'error',
+ })
+ this._redraw()
+ this.zoomTo({ easing: false })
+ },
+ callbackContext: this,
+ })
+ var fieldset = L.DomUtil.createFieldset(container, L._('Coordinates'))
+ fieldset.appendChild(builder.build())
+ },
+
+ zoomTo: function (e) {
+ if (this.datalayer.isClustered() && !this._icon) {
+ // callback is mandatory for zoomToShowLayer
+ this.datalayer.layer.zoomToShowLayer(this, e.callback || function () {})
+ } else {
+ L.U.FeatureMixin.zoomTo.call(this, e)
+ }
+ },
+
+ isOnScreen: function () {
+ var bounds = this.map.getBounds()
+ return bounds.contains(this._latlng)
+ },
+
+ getPopupToolbarAnchor: function () {
+ return this.options.icon.options.popupAnchor
+ },
+})
L.U.PathMixin = {
+ connectToDataLayer: function (datalayer) {
+ L.U.FeatureMixin.connectToDataLayer.call(this, datalayer)
+ // We keep markers on their own layer on top of the paths.
+ this.options.pane = this.datalayer.pane
+ },
- connectToDataLayer: function (datalayer) {
- L.U.FeatureMixin.connectToDataLayer.call(this, datalayer);
- // We keep markers on their own layer on top of the paths.
- this.options.pane = this.datalayer.pane;
- },
-
- edit: function (e) {
- if(this.map.editEnabled) {
- if (!this.editEnabled()) this.enableEdit();
- L.U.FeatureMixin.edit.call(this, e);
- }
- },
-
- _toggleEditing: function(e) {
- if(this.map.editEnabled) {
- if(this.editEnabled()) {
- this.endEdit();
- this.map.ui.closePanel();
- }
- else {
- this.edit(e);
- }
- }
- // FIXME: disable when disabling global edit
- L.DomEvent.stop(e);
- },
-
- styleOptions: [
- 'smoothFactor',
- 'color',
- 'opacity',
- 'stroke',
- 'weight',
- 'fill',
- 'fillColor',
- 'fillOpacity',
- 'dashArray',
- 'interactive'
- ],
-
- getShapeOptions: function () {
- return [
- 'properties._umap_options.color',
- 'properties._umap_options.opacity',
- 'properties._umap_options.weight'
- ];
- },
-
- getAdvancedOptions: function () {
- return [
- 'properties._umap_options.smoothFactor',
- 'properties._umap_options.dashArray',
- 'properties._umap_options.zoomTo'
- ];
- },
-
- setStyle: function (options) {
- options = options || {};
- var option;
- for (var idx in this.styleOptions) {
- option = this.styleOptions[idx];
- options[option] = this.getOption(option);
- }
- if (options.interactive) this.options.pointerEvents = 'visiblePainted';
- else this.options.pointerEvents = 'stroke';
- this.parentClass.prototype.setStyle.call(this, options);
- },
-
- _redraw: function () {
- this.setStyle();
- this.resetTooltip();
- },
-
- onAdd: function (map) {
- this._container = null;
- this.setStyle();
- // Show tooltip again when Leaflet.label allow static label on path.
- // cf https://github.com/Leaflet/Leaflet/pull/3952
- // this.map.on('showmeasure', this.showMeasureTooltip, this);
- // this.map.on('hidemeasure', this.removeTooltip, this);
- this.parentClass.prototype.onAdd.call(this, map);
- if (this.editing && this.editing.enabled()) this.editing.addHooks();
- this.resetTooltip();
- },
-
- onRemove: function (map) {
- // this.map.off('showmeasure', this.showMeasureTooltip, this);
- // this.map.off('hidemeasure', this.removeTooltip, this);
- if (this.editing && this.editing.enabled()) this.editing.removeHooks();
- L.U.FeatureMixin.onRemove.call(this, map);
- },
-
- getBestZoom: function () {
- return this.getOption("zoomTo") || this.map.getBoundsZoom(this.getBounds(), true);
- },
-
- endEdit: function () {
- this.disableEdit();
- L.U.FeatureMixin.endEdit.call(this);
- },
-
- _onMouseOver: function () {
- if (this.map.measureTools && this.map.measureTools.enabled()) {
- this.map.ui.tooltip({content: this.getMeasure(), anchor: this});
- } else if (this.map.editEnabled && !this.map.editedFeature) {
- this.map.ui.tooltip({content: L._('Click to edit'), anchor: this});
- }
- },
-
- addInteractions: function () {
- L.U.FeatureMixin.addInteractions.call(this);
- this.on('mouseover', this._onMouseOver);
- this.on('edit', this.makeDirty);
- this.on('drag editable:drag', this._onDrag);
- },
-
- _onDrag: function () {
- if (this._tooltip) this._tooltip.setLatLng(this.getCenter());
- },
-
- transferShape: function (at, to) {
- var shape = this.enableEdit().deleteShapeAt(at);
- this.disableEdit();
- if (!shape) return;
- to.enableEdit().appendShape(shape);
- if (!this._latlngs.length || !this._latlngs[0].length) this.del();
- },
-
- isolateShape: function (at) {
- if (!this.isMulti()) return;
- var shape = this.enableEdit().deleteShapeAt(at);
- this.disableEdit();
- if (!shape) return;
- var properties = this.cloneProperties();
- var other = new (this instanceof L.U.Polyline ? L.U.Polyline : L.U.Polygon)(this.map, shape, {geojson: {properties: properties}});
- this.datalayer.addLayer(other);
- other.edit();
- return other;
- },
-
- getContextMenuItems: function (e) {
- var items = L.U.FeatureMixin.getContextMenuItems.call(this, e);
- items.push({
- text: L._('Display measure'),
- callback: function () {
- this.map.ui.alert({content: this.getMeasure(), level: 'info'})
- },
- context: this
- })
- if (this.map.editEnabled && !this.isReadOnly() && this.isMulti()) {
- items = items.concat(this.getContextMenuMultiItems(e));
- }
- return items;
- },
-
- getContextMenuMultiItems: function (e) {
- var items = ['-', {
- text: L._('Remove shape from the multi'),
- callback: function () {
- this.enableEdit().deleteShapeAt(e.latlng);
- },
- context: this
- }];
- var shape = this.shapeAt(e.latlng);
- if (this._latlngs.indexOf(shape) > 0) {
- items.push({
- text: L._('Make main shape'),
- callback: function () {
- this.enableEdit().deleteShape(shape);
- this.editor.prependShape(shape);
- },
- context: this
- });
- }
- return items;
- },
-
- getContextMenuEditItems: function (e) {
- var items = L.U.FeatureMixin.getContextMenuEditItems.call(this, e);
- if (this.map.editedFeature && this.isSameClass(this.map.editedFeature) && this.map.editedFeature !== this) {
- items.push({
- text: L._('Transfer shape to edited feature'),
- callback: function () {
- this.transferShape(e.latlng, this.map.editedFeature);
- },
- context: this
- });
- }
- if (this.isMulti()) {
- items.push({
- text: L._('Extract shape to separate feature'),
- callback: function () {
- this.isolateShape(e.latlng, this.map.editedFeature);
- },
- context: this
- });
- }
- return items;
- },
-
- getInplaceToolbarActions: function (e) {
- var items = L.U.FeatureMixin.getInplaceToolbarActions.call(this, e);
- if (this.isMulti()) {
- items.push(L.U.DeleteShapeAction);
- items.push(L.U.ExtractShapeFromMultiAction);
- }
- return items;
- },
-
- isOnScreen: function () {
- var bounds = this.map.getBounds();
- return bounds.overlaps(this.getBounds());
+ edit: function (e) {
+ if (this.map.editEnabled) {
+ if (!this.editEnabled()) this.enableEdit()
+ L.U.FeatureMixin.edit.call(this, e)
}
+ },
-};
+ _toggleEditing: function (e) {
+ if (this.map.editEnabled) {
+ if (this.editEnabled()) {
+ this.endEdit()
+ this.map.ui.closePanel()
+ } else {
+ this.edit(e)
+ }
+ }
+ // FIXME: disable when disabling global edit
+ L.DomEvent.stop(e)
+ },
+
+ styleOptions: [
+ 'smoothFactor',
+ 'color',
+ 'opacity',
+ 'stroke',
+ 'weight',
+ 'fill',
+ 'fillColor',
+ 'fillOpacity',
+ 'dashArray',
+ 'interactive',
+ ],
+
+ getShapeOptions: function () {
+ return [
+ 'properties._umap_options.color',
+ 'properties._umap_options.opacity',
+ 'properties._umap_options.weight',
+ ]
+ },
+
+ getAdvancedOptions: function () {
+ return [
+ 'properties._umap_options.smoothFactor',
+ 'properties._umap_options.dashArray',
+ 'properties._umap_options.zoomTo',
+ ]
+ },
+
+ setStyle: function (options) {
+ options = options || {}
+ var option
+ for (var idx in this.styleOptions) {
+ option = this.styleOptions[idx]
+ options[option] = this.getOption(option)
+ }
+ if (options.interactive) this.options.pointerEvents = 'visiblePainted'
+ else this.options.pointerEvents = 'stroke'
+ this.parentClass.prototype.setStyle.call(this, options)
+ },
+
+ _redraw: function () {
+ this.setStyle()
+ this.resetTooltip()
+ },
+
+ onAdd: function (map) {
+ this._container = null
+ this.setStyle()
+ // Show tooltip again when Leaflet.label allow static label on path.
+ // cf https://github.com/Leaflet/Leaflet/pull/3952
+ // this.map.on('showmeasure', this.showMeasureTooltip, this);
+ // this.map.on('hidemeasure', this.removeTooltip, this);
+ this.parentClass.prototype.onAdd.call(this, map)
+ if (this.editing && this.editing.enabled()) this.editing.addHooks()
+ this.resetTooltip()
+ },
+
+ onRemove: function (map) {
+ // this.map.off('showmeasure', this.showMeasureTooltip, this);
+ // this.map.off('hidemeasure', this.removeTooltip, this);
+ if (this.editing && this.editing.enabled()) this.editing.removeHooks()
+ L.U.FeatureMixin.onRemove.call(this, map)
+ },
+
+ getBestZoom: function () {
+ return this.getOption('zoomTo') || this.map.getBoundsZoom(this.getBounds(), true)
+ },
+
+ endEdit: function () {
+ this.disableEdit()
+ L.U.FeatureMixin.endEdit.call(this)
+ },
+
+ _onMouseOver: function () {
+ if (this.map.measureTools && this.map.measureTools.enabled()) {
+ this.map.ui.tooltip({ content: this.getMeasure(), anchor: this })
+ } else if (this.map.editEnabled && !this.map.editedFeature) {
+ this.map.ui.tooltip({ content: L._('Click to edit'), anchor: this })
+ }
+ },
+
+ addInteractions: function () {
+ L.U.FeatureMixin.addInteractions.call(this)
+ this.on('mouseover', this._onMouseOver)
+ this.on('edit', this.makeDirty)
+ this.on('drag editable:drag', this._onDrag)
+ },
+
+ _onDrag: function () {
+ if (this._tooltip) this._tooltip.setLatLng(this.getCenter())
+ },
+
+ transferShape: function (at, to) {
+ var shape = this.enableEdit().deleteShapeAt(at)
+ this.disableEdit()
+ if (!shape) return
+ to.enableEdit().appendShape(shape)
+ if (!this._latlngs.length || !this._latlngs[0].length) this.del()
+ },
+
+ isolateShape: function (at) {
+ if (!this.isMulti()) return
+ var shape = this.enableEdit().deleteShapeAt(at)
+ this.disableEdit()
+ if (!shape) return
+ var properties = this.cloneProperties()
+ var other = new (this instanceof L.U.Polyline ? L.U.Polyline : L.U.Polygon)(
+ this.map,
+ shape,
+ { geojson: { properties: properties } }
+ )
+ this.datalayer.addLayer(other)
+ other.edit()
+ return other
+ },
+
+ getContextMenuItems: function (e) {
+ var items = L.U.FeatureMixin.getContextMenuItems.call(this, e)
+ items.push({
+ text: L._('Display measure'),
+ callback: function () {
+ this.map.ui.alert({ content: this.getMeasure(), level: 'info' })
+ },
+ context: this,
+ })
+ if (this.map.editEnabled && !this.isReadOnly() && this.isMulti()) {
+ items = items.concat(this.getContextMenuMultiItems(e))
+ }
+ return items
+ },
+
+ getContextMenuMultiItems: function (e) {
+ var items = [
+ '-',
+ {
+ text: L._('Remove shape from the multi'),
+ callback: function () {
+ this.enableEdit().deleteShapeAt(e.latlng)
+ },
+ context: this,
+ },
+ ]
+ var shape = this.shapeAt(e.latlng)
+ if (this._latlngs.indexOf(shape) > 0) {
+ items.push({
+ text: L._('Make main shape'),
+ callback: function () {
+ this.enableEdit().deleteShape(shape)
+ this.editor.prependShape(shape)
+ },
+ context: this,
+ })
+ }
+ return items
+ },
+
+ getContextMenuEditItems: function (e) {
+ var items = L.U.FeatureMixin.getContextMenuEditItems.call(this, e)
+ if (
+ this.map.editedFeature &&
+ this.isSameClass(this.map.editedFeature) &&
+ this.map.editedFeature !== this
+ ) {
+ items.push({
+ text: L._('Transfer shape to edited feature'),
+ callback: function () {
+ this.transferShape(e.latlng, this.map.editedFeature)
+ },
+ context: this,
+ })
+ }
+ if (this.isMulti()) {
+ items.push({
+ text: L._('Extract shape to separate feature'),
+ callback: function () {
+ this.isolateShape(e.latlng, this.map.editedFeature)
+ },
+ context: this,
+ })
+ }
+ return items
+ },
+
+ getInplaceToolbarActions: function (e) {
+ var items = L.U.FeatureMixin.getInplaceToolbarActions.call(this, e)
+ if (this.isMulti()) {
+ items.push(L.U.DeleteShapeAction)
+ items.push(L.U.ExtractShapeFromMultiAction)
+ }
+ return items
+ },
+
+ isOnScreen: function () {
+ var bounds = this.map.getBounds()
+ return bounds.overlaps(this.getBounds())
+ },
+}
L.U.Polyline = L.Polyline.extend({
- parentClass: L.Polyline,
- includes: [L.U.FeatureMixin, L.U.PathMixin],
+ parentClass: L.Polyline,
+ includes: [L.U.FeatureMixin, L.U.PathMixin],
- staticOptions: {
- stroke: true,
- fill: false
- },
+ staticOptions: {
+ stroke: true,
+ fill: false,
+ },
- isSameClass: function (other) {
- return other instanceof L.U.Polyline;
- },
+ isSameClass: function (other) {
+ return other instanceof L.U.Polyline
+ },
- getClassName: function () {
- return 'polyline';
- },
+ getClassName: function () {
+ return 'polyline'
+ },
- getMeasure: function (shape) {
- var length = L.GeoUtil.lineLength(this.map, shape || this._defaultShape());
- return L.GeoUtil.readableDistance(length, this.map.measureTools.getMeasureUnit());
- },
+ getMeasure: function (shape) {
+ var length = L.GeoUtil.lineLength(this.map, shape || this._defaultShape())
+ return L.GeoUtil.readableDistance(length, this.map.measureTools.getMeasureUnit())
+ },
- getContextMenuEditItems: function (e) {
- var items = L.U.PathMixin.getContextMenuEditItems.call(this, e),
- vertexClicked = e.vertex, index;
- if (!this.isMulti()) {
- items.push({
- text: L._('Transform to polygon'),
- callback: this.toPolygon,
- context: this
- });
- }
- if (vertexClicked) {
- index = e.vertex.getIndex();
- if (index !== 0 && index !== e.vertex.getLastIndex()) {
- items.push({
- text: L._('Split line'),
- callback: e.vertex.split,
- context: e.vertex
- });
- } else if (index === 0 || index === e.vertex.getLastIndex()) {
- items.push({
- text: L._('Continue line (Ctrl+Click)'),
- callback: e.vertex.continue,
- context: e.vertex.continue
- });
- }
- }
- return items;
- },
-
- getContextMenuMultiItems: function (e) {
- var items = L.U.PathMixin.getContextMenuMultiItems.call(this, e);
- items.push({
- text: L._('Merge lines'),
- callback: this.mergeShapes,
- context: this
- });
- return items;
- },
-
- toPolygon: function () {
- var geojson = this.toGeoJSON();
- geojson.geometry.type = 'Polygon';
- geojson.geometry.coordinates = [L.Util.flattenCoordinates(geojson.geometry.coordinates)];
- var polygon = this.datalayer.geojsonToFeatures(geojson);
- polygon.edit();
- this.del();
- },
-
- getAdvancedEditActions: function (container) {
- L.U.FeatureMixin.getAdvancedEditActions.call(this, container);
- var toPolygon = L.DomUtil.create('a', 'button umap-to-polygon', container);
- toPolygon.href = '#';
- toPolygon.textContent = L._('Transform to polygon');
- L.DomEvent.on(toPolygon, 'click', this.toPolygon, this);
- },
-
- _mergeShapes: function (from, to) {
- var toLeft = to[0],
- toRight = to[to.length - 1],
- fromLeft = from[0],
- fromRight = from[from.length - 1],
- l2ldistance = toLeft.distanceTo(fromLeft),
- l2rdistance = toLeft.distanceTo(fromRight),
- r2ldistance = toRight.distanceTo(fromLeft),
- r2rdistance = toRight.distanceTo(fromRight),
- toMerge;
- if (l2rdistance < Math.min(l2ldistance, r2ldistance, r2rdistance)) {
- toMerge = [from, to];
- } else if (r2ldistance < Math.min(l2ldistance, l2rdistance, r2rdistance)) {
- toMerge = [to, from];
- } else if (r2rdistance < Math.min(l2ldistance, l2rdistance, r2ldistance)) {
- from.reverse();
- toMerge = [to, from];
- } else {
- from.reverse();
- toMerge = [from, to];
- }
- var a = toMerge[0],
- b = toMerge[1],
- p1 = this.map.latLngToContainerPoint(a[a.length - 1]),
- p2 = this.map.latLngToContainerPoint(b[0]),
- tolerance = 5; // px on screen
- if (Math.abs(p1.x - p2.x) <= tolerance && Math.abs(p1.y - p2.y) <= tolerance) {
- a.pop();
- }
- return a.concat(b);
- },
-
- mergeShapes: function () {
- if (!this.isMulti()) return;
- var latlngs = this.getLatLngs();
- if (!latlngs.length) return;
- while (latlngs.length > 1) {
- latlngs.splice(0, 2, this._mergeShapes(latlngs[1], latlngs[0]));
- }
- this.setLatLngs(latlngs[0]);
- if (!this.editEnabled()) this.edit();
- this.editor.reset();
- this.isDirty = true;
- },
-
- isMulti: function () {
- return !L.LineUtil.isFlat(this._latlngs) && this._latlngs.length > 1;
- },
-
- getVertexActions: function (e) {
- var actions = L.U.FeatureMixin.getVertexActions.call(this, e),
- index = e.vertex.getIndex();
- if (index === 0 || index === e.vertex.getLastIndex()) actions.push(L.U.ContinueLineAction);
- else actions.push(L.U.SplitLineAction);
- return actions;
+ getContextMenuEditItems: function (e) {
+ var items = L.U.PathMixin.getContextMenuEditItems.call(this, e),
+ vertexClicked = e.vertex,
+ index
+ if (!this.isMulti()) {
+ items.push({
+ text: L._('Transform to polygon'),
+ callback: this.toPolygon,
+ context: this,
+ })
}
+ if (vertexClicked) {
+ index = e.vertex.getIndex()
+ if (index !== 0 && index !== e.vertex.getLastIndex()) {
+ items.push({
+ text: L._('Split line'),
+ callback: e.vertex.split,
+ context: e.vertex,
+ })
+ } else if (index === 0 || index === e.vertex.getLastIndex()) {
+ items.push({
+ text: L._('Continue line (Ctrl+Click)'),
+ callback: e.vertex.continue,
+ context: e.vertex.continue,
+ })
+ }
+ }
+ return items
+ },
-});
+ getContextMenuMultiItems: function (e) {
+ var items = L.U.PathMixin.getContextMenuMultiItems.call(this, e)
+ items.push({
+ text: L._('Merge lines'),
+ callback: this.mergeShapes,
+ context: this,
+ })
+ return items
+ },
+
+ toPolygon: function () {
+ var geojson = this.toGeoJSON()
+ geojson.geometry.type = 'Polygon'
+ geojson.geometry.coordinates = [
+ L.Util.flattenCoordinates(geojson.geometry.coordinates),
+ ]
+ var polygon = this.datalayer.geojsonToFeatures(geojson)
+ polygon.edit()
+ this.del()
+ },
+
+ getAdvancedEditActions: function (container) {
+ L.U.FeatureMixin.getAdvancedEditActions.call(this, container)
+ var toPolygon = L.DomUtil.create('a', 'button umap-to-polygon', container)
+ toPolygon.href = '#'
+ toPolygon.textContent = L._('Transform to polygon')
+ L.DomEvent.on(toPolygon, 'click', this.toPolygon, this)
+ },
+
+ _mergeShapes: function (from, to) {
+ var toLeft = to[0],
+ toRight = to[to.length - 1],
+ fromLeft = from[0],
+ fromRight = from[from.length - 1],
+ l2ldistance = toLeft.distanceTo(fromLeft),
+ l2rdistance = toLeft.distanceTo(fromRight),
+ r2ldistance = toRight.distanceTo(fromLeft),
+ r2rdistance = toRight.distanceTo(fromRight),
+ toMerge
+ if (l2rdistance < Math.min(l2ldistance, r2ldistance, r2rdistance)) {
+ toMerge = [from, to]
+ } else if (r2ldistance < Math.min(l2ldistance, l2rdistance, r2rdistance)) {
+ toMerge = [to, from]
+ } else if (r2rdistance < Math.min(l2ldistance, l2rdistance, r2ldistance)) {
+ from.reverse()
+ toMerge = [to, from]
+ } else {
+ from.reverse()
+ toMerge = [from, to]
+ }
+ var a = toMerge[0],
+ b = toMerge[1],
+ p1 = this.map.latLngToContainerPoint(a[a.length - 1]),
+ p2 = this.map.latLngToContainerPoint(b[0]),
+ tolerance = 5 // px on screen
+ if (Math.abs(p1.x - p2.x) <= tolerance && Math.abs(p1.y - p2.y) <= tolerance) {
+ a.pop()
+ }
+ return a.concat(b)
+ },
+
+ mergeShapes: function () {
+ if (!this.isMulti()) return
+ var latlngs = this.getLatLngs()
+ if (!latlngs.length) return
+ while (latlngs.length > 1) {
+ latlngs.splice(0, 2, this._mergeShapes(latlngs[1], latlngs[0]))
+ }
+ this.setLatLngs(latlngs[0])
+ if (!this.editEnabled()) this.edit()
+ this.editor.reset()
+ this.isDirty = true
+ },
+
+ isMulti: function () {
+ return !L.LineUtil.isFlat(this._latlngs) && this._latlngs.length > 1
+ },
+
+ getVertexActions: function (e) {
+ var actions = L.U.FeatureMixin.getVertexActions.call(this, e),
+ index = e.vertex.getIndex()
+ if (index === 0 || index === e.vertex.getLastIndex())
+ actions.push(L.U.ContinueLineAction)
+ else actions.push(L.U.SplitLineAction)
+ return actions
+ },
+})
L.U.Polygon = L.Polygon.extend({
- parentClass: L.Polygon,
- includes: [L.U.FeatureMixin, L.U.PathMixin],
+ parentClass: L.Polygon,
+ includes: [L.U.FeatureMixin, L.U.PathMixin],
- isSameClass: function (other) {
- return other instanceof L.U.Polygon;
- },
+ isSameClass: function (other) {
+ return other instanceof L.U.Polygon
+ },
- getClassName: function () {
- return 'polygon';
- },
+ getClassName: function () {
+ return 'polygon'
+ },
- getShapeOptions: function () {
- var options = L.U.PathMixin.getShapeOptions();
- options.push('properties._umap_options.stroke',
- 'properties._umap_options.fill',
- 'properties._umap_options.fillColor',
- 'properties._umap_options.fillOpacity'
- );
- return options;
- },
+ getShapeOptions: function () {
+ var options = L.U.PathMixin.getShapeOptions()
+ options.push(
+ 'properties._umap_options.stroke',
+ 'properties._umap_options.fill',
+ 'properties._umap_options.fillColor',
+ 'properties._umap_options.fillOpacity'
+ )
+ return options
+ },
- getInteractionOptions: function () {
- var options = [
- ['properties._umap_options.interactive', {handler: 'Switch', label: L._('Allow interactions'), helpEntries: 'interactive', inheritable: true}],
- ['properties._umap_options.outlink', {label: L._('Link to…'), helpEntries: 'outlink', placeholder: 'http://...', inheritable: true}],
- ['properties._umap_options.outlinkTarget', {handler: 'OutlinkTarget', label: L._('Open link in…'), inheritable: true}]
- ];
- return options.concat(L.U.FeatureMixin.getInteractionOptions());
- },
+ getInteractionOptions: function () {
+ var options = [
+ [
+ 'properties._umap_options.interactive',
+ {
+ handler: 'Switch',
+ label: L._('Allow interactions'),
+ helpEntries: 'interactive',
+ inheritable: true,
+ },
+ ],
+ [
+ 'properties._umap_options.outlink',
+ {
+ label: L._('Link to…'),
+ helpEntries: 'outlink',
+ placeholder: 'http://...',
+ inheritable: true,
+ },
+ ],
+ [
+ 'properties._umap_options.outlinkTarget',
+ { handler: 'OutlinkTarget', label: L._('Open link in…'), inheritable: true },
+ ],
+ ]
+ return options.concat(L.U.FeatureMixin.getInteractionOptions())
+ },
- getMeasure: function (shape) {
- var area = L.GeoUtil.geodesicArea(shape || this._defaultShape());
- return L.GeoUtil.readableArea(area, this.map.measureTools.getMeasureUnit());
- },
+ getMeasure: function (shape) {
+ var area = L.GeoUtil.geodesicArea(shape || this._defaultShape())
+ return L.GeoUtil.readableArea(area, this.map.measureTools.getMeasureUnit())
+ },
- getContextMenuEditItems: function (e) {
- var items = L.U.PathMixin.getContextMenuEditItems.call(this, e),
- shape = this.shapeAt(e.latlng);
- // No multi and no holes.
- if (shape && !this.isMulti() && (L.LineUtil.isFlat(shape) || shape.length === 1)) {
- items.push({
- text: L._('Transform to lines'),
- callback: this.toPolyline,
- context: this
- });
- }
- items.push({
- text: L._('Start a hole here'),
- callback: this.startHole,
- context: this
- });
- return items;
- },
-
- startHole: function (e) {
- this.enableEdit().newHole(e.latlng);
- },
-
- toPolyline: function () {
- var geojson = this.toGeoJSON();
- geojson.geometry.type = 'LineString';
- geojson.geometry.coordinates = L.Util.flattenCoordinates(geojson.geometry.coordinates);
- var polyline = this.datalayer.geojsonToFeatures(geojson);
- polyline.edit();
- this.del();
- },
-
- getAdvancedEditActions: function (container) {
- L.U.FeatureMixin.getAdvancedEditActions.call(this, container);
- var toPolyline = L.DomUtil.create('a', 'button umap-to-polyline', container);
- toPolyline.href = '#';
- toPolyline.textContent = L._('Transform to lines');
- L.DomEvent.on(toPolyline, 'click', this.toPolyline, this);
- },
-
- isMulti: function () {
- // Change me when Leaflet#3279 is merged.
- return !L.LineUtil.isFlat(this._latlngs) && !L.LineUtil.isFlat(this._latlngs[0]) && this._latlngs.length > 1;
- },
-
- getInplaceToolbarActions: function (e) {
- var items = L.U.PathMixin.getInplaceToolbarActions.call(this, e);
- items.push(L.U.CreateHoleAction);
- return items;
+ getContextMenuEditItems: function (e) {
+ var items = L.U.PathMixin.getContextMenuEditItems.call(this, e),
+ shape = this.shapeAt(e.latlng)
+ // No multi and no holes.
+ if (shape && !this.isMulti() && (L.LineUtil.isFlat(shape) || shape.length === 1)) {
+ items.push({
+ text: L._('Transform to lines'),
+ callback: this.toPolyline,
+ context: this,
+ })
}
+ items.push({
+ text: L._('Start a hole here'),
+ callback: this.startHole,
+ context: this,
+ })
+ return items
+ },
-});
+ startHole: function (e) {
+ this.enableEdit().newHole(e.latlng)
+ },
+
+ toPolyline: function () {
+ var geojson = this.toGeoJSON()
+ geojson.geometry.type = 'LineString'
+ geojson.geometry.coordinates = L.Util.flattenCoordinates(
+ geojson.geometry.coordinates
+ )
+ var polyline = this.datalayer.geojsonToFeatures(geojson)
+ polyline.edit()
+ this.del()
+ },
+
+ getAdvancedEditActions: function (container) {
+ L.U.FeatureMixin.getAdvancedEditActions.call(this, container)
+ var toPolyline = L.DomUtil.create('a', 'button umap-to-polyline', container)
+ toPolyline.href = '#'
+ toPolyline.textContent = L._('Transform to lines')
+ L.DomEvent.on(toPolyline, 'click', this.toPolyline, this)
+ },
+
+ isMulti: function () {
+ // Change me when Leaflet#3279 is merged.
+ return (
+ !L.LineUtil.isFlat(this._latlngs) &&
+ !L.LineUtil.isFlat(this._latlngs[0]) &&
+ this._latlngs.length > 1
+ )
+ },
+
+ getInplaceToolbarActions: function (e) {
+ var items = L.U.PathMixin.getInplaceToolbarActions.call(this, e)
+ items.push(L.U.CreateHoleAction)
+ return items
+ },
+})
diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js
index b3a37733..eef555ce 100644
--- a/umap/static/umap/js/umap.forms.js
+++ b/umap/static/umap/js/umap.forms.js
@@ -1,794 +1,1079 @@
L.FormBuilder.Element.include({
-
- getParentNode: function () {
- if (this.options.wrapper) {
- return L.DomUtil.create(this.options.wrapper, this.options.wrapperClass || '', this.form);
- }
- var className = 'formbox';
- if (this.options.inheritable) {
- className += this.get(true) === undefined ? ' inheritable undefined' : ' inheritable ';
- }
- className += ' umap-field-' + this.name;
- this.wrapper = L.DomUtil.create('div', className, this.form);
- this.header = L.DomUtil.create('div', 'header', this.wrapper);
- if (this.options.inheritable) {
- var undefine = L.DomUtil.add('a', 'button undefine', this.header, L._('clear'));
- var define = L.DomUtil.add('a', 'button define', this.header, L._('define'));
- L.DomEvent.on(define, 'click', function (e) {
- L.DomEvent.stop(e);
- this.fetch();
- this.fire('define');
- L.DomUtil.removeClass(this.wrapper, 'undefined');
- }, this);
- L.DomEvent.on(undefine, 'click', function (e) {
- L.DomEvent.stop(e);
- L.DomUtil.addClass(this.wrapper, 'undefined');
- this.clear();
- this.sync();
- }, this);
- }
- this.quickContainer = L.DomUtil.create('span', 'quick-actions show-on-defined', this.header);
- this.extendedContainer = L.DomUtil.create('div', 'show-on-defined', this.wrapper);
- return this.extendedContainer;
- },
-
- getLabelParent: function () {
- return this.header;
- },
-
- clear: function () {
- this.input.value = '';
- },
-
- get: function (own) {
- if (!this.options.inheritable || own) return this.builder.getter(this.field);
- var path = this.field.split('.'),
- key = path[path.length - 1];
- return this.obj.getOption(key);
- },
-
- buildLabel: function () {
- if (this.options.label) {
- this.label = L.DomUtil.create('label', '', this.getLabelParent());
- this.label.textContent = this.label.title = this.options.label;
- if (this.options.helpEntries) this.builder.map.help.button(this.label, this.options.helpEntries);
- else if (this.options.helpTooltip) {
- var info = L.DomUtil.create('i', 'info', this.label);
- L.DomEvent.on(info, 'mouseover', function () {
- this.builder.map.ui.tooltip({anchor: info, content: this.options.helpTooltip, position: 'top'});
- }, this);
- }
- }
+ getParentNode: function () {
+ if (this.options.wrapper) {
+ return L.DomUtil.create(
+ this.options.wrapper,
+ this.options.wrapperClass || '',
+ this.form
+ )
}
+ var className = 'formbox'
+ if (this.options.inheritable) {
+ className +=
+ this.get(true) === undefined ? ' inheritable undefined' : ' inheritable '
+ }
+ className += ' umap-field-' + this.name
+ this.wrapper = L.DomUtil.create('div', className, this.form)
+ this.header = L.DomUtil.create('div', 'header', this.wrapper)
+ if (this.options.inheritable) {
+ var undefine = L.DomUtil.add('a', 'button undefine', this.header, L._('clear'))
+ var define = L.DomUtil.add('a', 'button define', this.header, L._('define'))
+ L.DomEvent.on(
+ define,
+ 'click',
+ function (e) {
+ L.DomEvent.stop(e)
+ this.fetch()
+ this.fire('define')
+ L.DomUtil.removeClass(this.wrapper, 'undefined')
+ },
+ this
+ )
+ L.DomEvent.on(
+ undefine,
+ 'click',
+ function (e) {
+ L.DomEvent.stop(e)
+ L.DomUtil.addClass(this.wrapper, 'undefined')
+ this.clear()
+ this.sync()
+ },
+ this
+ )
+ }
+ this.quickContainer = L.DomUtil.create(
+ 'span',
+ 'quick-actions show-on-defined',
+ this.header
+ )
+ this.extendedContainer = L.DomUtil.create('div', 'show-on-defined', this.wrapper)
+ return this.extendedContainer
+ },
-});
+ getLabelParent: function () {
+ return this.header
+ },
+
+ clear: function () {
+ this.input.value = ''
+ },
+
+ get: function (own) {
+ if (!this.options.inheritable || own) return this.builder.getter(this.field)
+ var path = this.field.split('.'),
+ key = path[path.length - 1]
+ return this.obj.getOption(key)
+ },
+
+ buildLabel: function () {
+ if (this.options.label) {
+ this.label = L.DomUtil.create('label', '', this.getLabelParent())
+ this.label.textContent = this.label.title = this.options.label
+ if (this.options.helpEntries)
+ this.builder.map.help.button(this.label, this.options.helpEntries)
+ else if (this.options.helpTooltip) {
+ var info = L.DomUtil.create('i', 'info', this.label)
+ L.DomEvent.on(
+ info,
+ 'mouseover',
+ function () {
+ this.builder.map.ui.tooltip({
+ anchor: info,
+ content: this.options.helpTooltip,
+ position: 'top',
+ })
+ },
+ this
+ )
+ }
+ }
+ },
+})
L.FormBuilder.Select.include({
+ clear: function () {
+ this.select.value = ''
+ },
- clear: function () {
- this.select.value = '';
- },
-
- getDefault: function () {
- if (this.options.inheritable) return undefined;
- return this.getOptions()[0][0];
- }
-
-});
+ getDefault: function () {
+ if (this.options.inheritable) return undefined
+ return this.getOptions()[0][0]
+ },
+})
L.FormBuilder.CheckBox.include({
+ value: function () {
+ return L.DomUtil.hasClass(this.wrapper, 'undefined')
+ ? undefined
+ : this.input.checked
+ },
- value: function () {
- return L.DomUtil.hasClass(this.wrapper, 'undefined') ? undefined : this.input.checked;
- },
-
- clear: function () {
- this.fetch();
- }
-
-});
+ clear: function () {
+ this.fetch()
+ },
+})
L.FormBuilder.ColorPicker = L.FormBuilder.Input.extend({
+ colors: [
+ 'Black',
+ 'Navy',
+ 'DarkBlue',
+ 'MediumBlue',
+ 'Blue',
+ 'DarkGreen',
+ 'Green',
+ 'Teal',
+ 'DarkCyan',
+ 'DeepSkyBlue',
+ 'DarkTurquoise',
+ 'MediumSpringGreen',
+ 'Lime',
+ 'SpringGreen',
+ 'Aqua',
+ 'Cyan',
+ 'MidnightBlue',
+ 'DodgerBlue',
+ 'LightSeaGreen',
+ 'ForestGreen',
+ 'SeaGreen',
+ 'DarkSlateGray',
+ 'DarkSlateGrey',
+ 'LimeGreen',
+ 'MediumSeaGreen',
+ 'Turquoise',
+ 'RoyalBlue',
+ 'SteelBlue',
+ 'DarkSlateBlue',
+ 'MediumTurquoise',
+ 'Indigo',
+ 'DarkOliveGreen',
+ 'CadetBlue',
+ 'CornflowerBlue',
+ 'MediumAquaMarine',
+ 'DimGray',
+ 'DimGrey',
+ 'SlateBlue',
+ 'OliveDrab',
+ 'SlateGray',
+ 'SlateGrey',
+ 'LightSlateGray',
+ 'LightSlateGrey',
+ 'MediumSlateBlue',
+ 'LawnGreen',
+ 'Chartreuse',
+ 'Aquamarine',
+ 'Maroon',
+ 'Purple',
+ 'Olive',
+ 'Gray',
+ 'Grey',
+ 'SkyBlue',
+ 'LightSkyBlue',
+ 'BlueViolet',
+ 'DarkRed',
+ 'DarkMagenta',
+ 'SaddleBrown',
+ 'DarkSeaGreen',
+ 'LightGreen',
+ 'MediumPurple',
+ 'DarkViolet',
+ 'PaleGreen',
+ 'DarkOrchid',
+ 'YellowGreen',
+ 'Sienna',
+ 'Brown',
+ 'DarkGray',
+ 'DarkGrey',
+ 'LightBlue',
+ 'GreenYellow',
+ 'PaleTurquoise',
+ 'LightSteelBlue',
+ 'PowderBlue',
+ 'FireBrick',
+ 'DarkGoldenRod',
+ 'MediumOrchid',
+ 'RosyBrown',
+ 'DarkKhaki',
+ 'Silver',
+ 'MediumVioletRed',
+ 'IndianRed',
+ 'Peru',
+ 'Chocolate',
+ 'Tan',
+ 'LightGray',
+ 'LightGrey',
+ 'Thistle',
+ 'Orchid',
+ 'GoldenRod',
+ 'PaleVioletRed',
+ 'Crimson',
+ 'Gainsboro',
+ 'Plum',
+ 'BurlyWood',
+ 'LightCyan',
+ 'Lavender',
+ 'DarkSalmon',
+ 'Violet',
+ 'PaleGoldenRod',
+ 'LightCoral',
+ 'Khaki',
+ 'AliceBlue',
+ 'HoneyDew',
+ 'Azure',
+ 'SandyBrown',
+ 'Wheat',
+ 'Beige',
+ 'WhiteSmoke',
+ 'MintCream',
+ 'GhostWhite',
+ 'Salmon',
+ 'AntiqueWhite',
+ 'Linen',
+ 'LightGoldenRodYellow',
+ 'OldLace',
+ 'Red',
+ 'Fuchsia',
+ 'Magenta',
+ 'DeepPink',
+ 'OrangeRed',
+ 'Tomato',
+ 'HotPink',
+ 'Coral',
+ 'DarkOrange',
+ 'LightSalmon',
+ 'Orange',
+ 'LightPink',
+ 'Pink',
+ 'Gold',
+ 'PeachPuff',
+ 'NavajoWhite',
+ 'Moccasin',
+ 'Bisque',
+ 'MistyRose',
+ 'BlanchedAlmond',
+ 'PapayaWhip',
+ 'LavenderBlush',
+ 'SeaShell',
+ 'Cornsilk',
+ 'LemonChiffon',
+ 'FloralWhite',
+ 'Snow',
+ 'Yellow',
+ 'LightYellow',
+ 'Ivory',
+ 'White',
+ ],
- colors: [
- 'Black', 'Navy', 'DarkBlue', 'MediumBlue', 'Blue', 'DarkGreen',
- 'Green', 'Teal', 'DarkCyan', 'DeepSkyBlue', 'DarkTurquoise',
- 'MediumSpringGreen', 'Lime', 'SpringGreen', 'Aqua', 'Cyan',
- 'MidnightBlue', 'DodgerBlue', 'LightSeaGreen', 'ForestGreen',
- 'SeaGreen', 'DarkSlateGray', 'DarkSlateGrey', 'LimeGreen',
- 'MediumSeaGreen', 'Turquoise', 'RoyalBlue', 'SteelBlue',
- 'DarkSlateBlue', 'MediumTurquoise', 'Indigo', 'DarkOliveGreen',
- 'CadetBlue', 'CornflowerBlue', 'MediumAquaMarine', 'DimGray',
- 'DimGrey', 'SlateBlue', 'OliveDrab', 'SlateGray', 'SlateGrey',
- 'LightSlateGray', 'LightSlateGrey', 'MediumSlateBlue', 'LawnGreen',
- 'Chartreuse', 'Aquamarine', 'Maroon', 'Purple', 'Olive', 'Gray',
- 'Grey', 'SkyBlue', 'LightSkyBlue', 'BlueViolet', 'DarkRed',
- 'DarkMagenta', 'SaddleBrown', 'DarkSeaGreen', 'LightGreen',
- 'MediumPurple', 'DarkViolet', 'PaleGreen', 'DarkOrchid',
- 'YellowGreen', 'Sienna', 'Brown', 'DarkGray', 'DarkGrey',
- 'LightBlue', 'GreenYellow', 'PaleTurquoise', 'LightSteelBlue',
- 'PowderBlue', 'FireBrick', 'DarkGoldenRod', 'MediumOrchid',
- 'RosyBrown', 'DarkKhaki', 'Silver', 'MediumVioletRed', 'IndianRed',
- 'Peru', 'Chocolate', 'Tan', 'LightGray', 'LightGrey', 'Thistle',
- 'Orchid', 'GoldenRod', 'PaleVioletRed', 'Crimson', 'Gainsboro',
- 'Plum', 'BurlyWood', 'LightCyan', 'Lavender', 'DarkSalmon',
- 'Violet', 'PaleGoldenRod', 'LightCoral', 'Khaki', 'AliceBlue',
- 'HoneyDew', 'Azure', 'SandyBrown', 'Wheat', 'Beige', 'WhiteSmoke',
- 'MintCream', 'GhostWhite', 'Salmon', 'AntiqueWhite', 'Linen',
- 'LightGoldenRodYellow', 'OldLace', 'Red', 'Fuchsia', 'Magenta',
- 'DeepPink', 'OrangeRed', 'Tomato', 'HotPink', 'Coral', 'DarkOrange',
- 'LightSalmon', 'Orange', 'LightPink', 'Pink', 'Gold', 'PeachPuff',
- 'NavajoWhite', 'Moccasin', 'Bisque', 'MistyRose', 'BlanchedAlmond',
- 'PapayaWhip', 'LavenderBlush', 'SeaShell', 'Cornsilk',
- 'LemonChiffon', 'FloralWhite', 'Snow', 'Yellow', 'LightYellow',
- 'Ivory', 'White'
- ],
+ getParentNode: function () {
+ L.FormBuilder.CheckBox.prototype.getParentNode.call(this)
+ return this.quickContainer
+ },
- getParentNode: function () {
- L.FormBuilder.CheckBox.prototype.getParentNode.call(this);
- return this.quickContainer;
- },
-
- build: function () {
- L.FormBuilder.Input.prototype.build.call(this);
- this.input.placeholder = this.options.placeholder || L._('Inherit');
- this.container = L.DomUtil.create('div', 'umap-color-picker', this.extendedContainer);
- this.container.style.display = 'none';
- for (var idx in this.colors) {
- this.addColor(this.colors[idx]);
- }
- this.spreadColor();
- this.input.autocomplete = 'off';
- L.DomEvent.on(this.input, 'focus', this.onFocus, this);
- L.DomEvent.on(this.input, 'blur', this.onBlur, this);
- L.DomEvent.on(this.input, 'change', this.sync, this);
- this.on('define', this.onFocus);
- },
-
- onFocus: function () {
- this.container.style.display = 'block';
- this.spreadColor();
- },
-
- onBlur: function () {
- var self = this,
- closePicker = function () {
- self.container.style.display = 'none';
- };
- // We must leave time for the click to be listened.
- window.setTimeout(closePicker, 100);
- },
-
- sync: function () {
- this.spreadColor();
- L.FormBuilder.Input.prototype.sync.call(this);
- },
-
- spreadColor: function () {
- if (this.input.value) this.input.style.backgroundColor = this.input.value;
- else this.input.style.backgroundColor = 'inherit';
- },
-
- addColor: function (colorName) {
- var span = L.DomUtil.create('span', '', this.container);
- span.style.backgroundColor = span.title = colorName;
- var updateColorInput = function () {
- this.input.value = colorName;
- this.sync();
- this.container.style.display = 'none';
- };
- L.DomEvent.on(span, 'mousedown', updateColorInput, this);
+ build: function () {
+ L.FormBuilder.Input.prototype.build.call(this)
+ this.input.placeholder = this.options.placeholder || L._('Inherit')
+ this.container = L.DomUtil.create(
+ 'div',
+ 'umap-color-picker',
+ this.extendedContainer
+ )
+ this.container.style.display = 'none'
+ for (var idx in this.colors) {
+ this.addColor(this.colors[idx])
}
+ this.spreadColor()
+ this.input.autocomplete = 'off'
+ L.DomEvent.on(this.input, 'focus', this.onFocus, this)
+ L.DomEvent.on(this.input, 'blur', this.onBlur, this)
+ L.DomEvent.on(this.input, 'change', this.sync, this)
+ this.on('define', this.onFocus)
+ },
-});
+ onFocus: function () {
+ this.container.style.display = 'block'
+ this.spreadColor()
+ },
+
+ onBlur: function () {
+ var self = this,
+ closePicker = function () {
+ self.container.style.display = 'none'
+ }
+ // We must leave time for the click to be listened.
+ window.setTimeout(closePicker, 100)
+ },
+
+ sync: function () {
+ this.spreadColor()
+ L.FormBuilder.Input.prototype.sync.call(this)
+ },
+
+ spreadColor: function () {
+ if (this.input.value) this.input.style.backgroundColor = this.input.value
+ else this.input.style.backgroundColor = 'inherit'
+ },
+
+ addColor: function (colorName) {
+ var span = L.DomUtil.create('span', '', this.container)
+ span.style.backgroundColor = span.title = colorName
+ var updateColorInput = function () {
+ this.input.value = colorName
+ this.sync()
+ this.container.style.display = 'none'
+ }
+ L.DomEvent.on(span, 'mousedown', updateColorInput, this)
+ },
+})
L.FormBuilder.TextColorPicker = L.FormBuilder.ColorPicker.extend({
- colors: [
- 'Black', 'DarkSlateGrey', 'DimGrey', 'SlateGrey', 'LightSlateGrey',
- 'Grey', 'DarkGrey', 'LightGrey', 'White'
- ]
-
-});
+ colors: [
+ 'Black',
+ 'DarkSlateGrey',
+ 'DimGrey',
+ 'SlateGrey',
+ 'LightSlateGrey',
+ 'Grey',
+ 'DarkGrey',
+ 'LightGrey',
+ 'White',
+ ],
+})
L.FormBuilder.IconClassSwitcher = L.FormBuilder.Select.extend({
-
- selectOptions: [
- ['Default', L._('Default')],
- ['Circle', L._('Circle')],
- ['Drop', L._('Drop')],
- ['Ball', L._('Ball')]
- ]
-
-});
+ selectOptions: [
+ ['Default', L._('Default')],
+ ['Circle', L._('Circle')],
+ ['Drop', L._('Drop')],
+ ['Ball', L._('Ball')],
+ ],
+})
L.FormBuilder.ProxyTTLSelect = L.FormBuilder.Select.extend({
-
- selectOptions: [
- [undefined, L._('No cache')],
- ['300', L._('5 min')],
- ['3600', L._('1 hour')],
- ['86400', L._('1 day')]
- ]
-
-});
+ selectOptions: [
+ [undefined, L._('No cache')],
+ ['300', L._('5 min')],
+ ['3600', L._('1 hour')],
+ ['86400', L._('1 day')],
+ ],
+})
L.FormBuilder.PopupShape = L.FormBuilder.Select.extend({
-
- selectOptions: [
- ['Default', L._('Popup')],
- ['Large', L._('Popup (large)')],
- ['Panel', L._('Side panel')],
- ]
-
-});
+ selectOptions: [
+ ['Default', L._('Popup')],
+ ['Large', L._('Popup (large)')],
+ ['Panel', L._('Side panel')],
+ ],
+})
L.FormBuilder.PopupContent = L.FormBuilder.Select.extend({
-
- selectOptions: [
- ['Default', L._('Default')],
- ['Table', L._('Table')],
- ['GeoRSSImage', L._('GeoRSS (title + image)')],
- ['GeoRSSLink', L._('GeoRSS (only link)')],
- ]
-
-});
+ selectOptions: [
+ ['Default', L._('Default')],
+ ['Table', L._('Table')],
+ ['GeoRSSImage', L._('GeoRSS (title + image)')],
+ ['GeoRSSLink', L._('GeoRSS (only link)')],
+ ],
+})
L.FormBuilder.LayerTypeChooser = L.FormBuilder.Select.extend({
-
- selectOptions: [
- ['Default', L._('Default')],
- ['Cluster', L._('Clustered')],
- ['Heat', L._('Heatmap')]
- ]
-
-});
+ selectOptions: [
+ ['Default', L._('Default')],
+ ['Cluster', L._('Clustered')],
+ ['Heat', L._('Heatmap')],
+ ],
+})
L.FormBuilder.SlideshowDelay = L.FormBuilder.IntSelect.extend({
-
- getOptions: function () {
- var options = [];
- for (var i = 1; i < 30; i++) {
- options.push([i * 1000, L._('{delay} seconds', {delay: i})]);
- }
- return options;
+ getOptions: function () {
+ var options = []
+ for (var i = 1; i < 30; i++) {
+ options.push([i * 1000, L._('{delay} seconds', { delay: i })])
}
-
-});
+ return options
+ },
+})
L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({
+ getOptions: function () {
+ var options = []
+ this.builder.map.eachDataLayerReverse(function (datalayer) {
+ if (datalayer.isLoaded() && !datalayer.isRemoteLayer() && datalayer.canBrowse()) {
+ options.push([L.stamp(datalayer), datalayer.getName()])
+ }
+ })
+ return options
+ },
- getOptions: function () {
- var options = [];
- this.builder.map.eachDataLayerReverse(function (datalayer) {
- if(datalayer.isLoaded() && !datalayer.isRemoteLayer() && datalayer.canBrowse()) {
- options.push([L.stamp(datalayer), datalayer.getName()]);
- }
- });
- return options;
- },
+ toHTML: function () {
+ return L.stamp(this.obj.datalayer)
+ },
- toHTML: function () {
- return L.stamp(this.obj.datalayer);
- },
+ toJS: function () {
+ return this.builder.map.datalayers[this.value()]
+ },
- toJS: function () {
- return this.builder.map.datalayers[this.value()];
- },
-
- set: function () {
- this.builder.map.lastUsedDataLayer = this.toJS();
- this.obj.changeDataLayer(this.toJS());
- }
-
-});
+ set: function () {
+ this.builder.map.lastUsedDataLayer = this.toJS()
+ this.obj.changeDataLayer(this.toJS())
+ },
+})
L.FormBuilder.onLoadPanel = L.FormBuilder.Select.extend({
-
- selectOptions: [
- ['none', L._('None')],
- ['caption', L._('Caption')],
- ['databrowser', L._('Data browser')],
- ['datafilters', L._('Data filters')]
- ]
-
-});
+ selectOptions: [
+ ['none', L._('None')],
+ ['caption', L._('Caption')],
+ ['databrowser', L._('Data browser')],
+ ['datafilters', L._('Data filters')],
+ ],
+})
L.FormBuilder.DataFormat = L.FormBuilder.Select.extend({
-
- selectOptions: [
- [undefined, L._('Choose the data format')],
- ['geojson', 'geojson'],
- ['osm', 'osm'],
- ['csv', 'csv'],
- ['gpx', 'gpx'],
- ['kml', 'kml'],
- ['georss', 'georss']
- ]
-
-});
+ selectOptions: [
+ [undefined, L._('Choose the data format')],
+ ['geojson', 'geojson'],
+ ['osm', 'osm'],
+ ['csv', 'csv'],
+ ['gpx', 'gpx'],
+ ['kml', 'kml'],
+ ['georss', 'georss'],
+ ],
+})
L.FormBuilder.LabelDirection = L.FormBuilder.Select.extend({
-
- selectOptions: [
- ['auto', L._('Automatic')],
- ['left', L._('On the left')],
- ['right', L._('On the right')],
- ['top', L._('On the top')],
- ['bottom', L._('On the bottom')]
- ]
-
-});
+ selectOptions: [
+ ['auto', L._('Automatic')],
+ ['left', L._('On the left')],
+ ['right', L._('On the right')],
+ ['top', L._('On the top')],
+ ['bottom', L._('On the bottom')],
+ ],
+})
L.FormBuilder.LicenceChooser = L.FormBuilder.Select.extend({
-
- getOptions: function () {
- var licences = [],
- licencesList = this.builder.obj.options.licences,
- licence;
- for (var i in licencesList) {
- licence = licencesList[i];
- licences.push([i, licence.name]);
- }
- return licences;
- },
-
- toHTML: function () {
- return this.get().name;
- },
-
- toJS: function () {
- return this.builder.obj.options.licences[this.value()];
+ getOptions: function () {
+ var licences = [],
+ licencesList = this.builder.obj.options.licences,
+ licence
+ for (var i in licencesList) {
+ licence = licencesList[i]
+ licences.push([i, licence.name])
}
+ return licences
+ },
-});
+ toHTML: function () {
+ return this.get().name
+ },
+ toJS: function () {
+ return this.builder.obj.options.licences[this.value()]
+ },
+})
L.FormBuilder.NullableBoolean = L.FormBuilder.Select.extend({
- selectOptions: [
- [undefined, L._('inherit')],
- [true, L._('yes')],
- [false, L._('no')]
- ],
+ selectOptions: [
+ [undefined, L._('inherit')],
+ [true, L._('yes')],
+ [false, L._('no')],
+ ],
- toJS: function () {
- var value = this.value();
- switch (value) {
- case 'true':
- case true:
- value = true;
- break;
- case 'false':
- case false:
- value = false;
- break;
- default:
- value = undefined;
- }
- return value;
+ toJS: function () {
+ var value = this.value()
+ switch (value) {
+ case 'true':
+ case true:
+ value = true
+ break
+ case 'false':
+ case false:
+ value = false
+ break
+ default:
+ value = undefined
}
-
-});
-
+ return value
+ },
+})
L.FormBuilder.BlurInput.include({
-
- build: function () {
- this.options.className = 'blur';
- L.FormBuilder.Input.prototype.build.call(this);
- var button = L.DomUtil.create('span', 'button blur-button');
- L.DomUtil.after(this.input, button);
- }
-
-});
+ build: function () {
+ this.options.className = 'blur'
+ L.FormBuilder.Input.prototype.build.call(this)
+ var button = L.DomUtil.create('span', 'button blur-button')
+ L.DomUtil.after(this.input, button)
+ },
+})
L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
+ type: function () {
+ return 'hidden'
+ },
- type: function () {
- return 'hidden';
- },
+ build: function () {
+ this.options.helpText = this.builder.map.help.formatIconSymbol
+ L.FormBuilder.BlurInput.prototype.build.call(this)
+ this.parentContainer = L.DomUtil.create(
+ 'div',
+ 'umap-form-iconfield',
+ this.parentNode
+ )
+ this.buttonsContainer = L.DomUtil.create('div', '', this.parentContainer)
+ this.pictogramsContainer = L.DomUtil.create(
+ 'div',
+ 'umap-pictogram-list',
+ this.parentContainer
+ )
+ this.input.type = 'hidden'
+ this.input.placeholder = L._('Symbol or url')
+ this.udpatePreview()
+ this.on('define', this.fetchIconList)
+ },
- build: function () {
- this.options.helpText = this.builder.map.help.formatIconSymbol;
- L.FormBuilder.BlurInput.prototype.build.call(this);
- this.parentContainer = L.DomUtil.create('div', 'umap-form-iconfield', this.parentNode);
- this.buttonsContainer = L.DomUtil.create('div', '', this.parentContainer);
- this.pictogramsContainer = L.DomUtil.create('div', 'umap-pictogram-list', this.parentContainer);
- this.input.type = 'hidden';
- this.input.placeholder = L._('Symbol or url');
- this.udpatePreview();
- this.on('define', this.fetchIconList);
- },
+ isUrl: function () {
+ return this.value().indexOf('/') !== -1
+ },
- isUrl: function () {
- return (this.value().indexOf('/') !== -1);
- },
-
- udpatePreview: function () {
- if (this.value()&& this.value().indexOf('{') === -1) { // Do not try to render URL with variables
- if (this.isUrl()) {
- var img = L.DomUtil.create('img', '', L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer));
- img.src = this.value();
- L.DomEvent.on(img, 'click', this.fetchIconList, this);
- } else {
- var el = L.DomUtil.create('span', '', L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer));
- el.textContent = this.value();
- L.DomEvent.on(el, 'click', this.fetchIconList, this);
- }
- }
- this.button = L.DomUtil.create('a', '', this.buttonsContainer);
- this.button.textContent = this.value() ? L._('Change symbol') : L._('Add symbol');
- this.button.href = '#';
- L.DomEvent
- .on(this.button, 'click', L.DomEvent.stop)
- .on(this.button, 'click', this.fetchIconList, this);
- },
-
- addIconPreview: function (pictogram) {
- var baseClass = 'umap-icon-choice',
- value = pictogram.src,
- className = value === this.value() ? baseClass + ' selected' : baseClass,
- container = L.DomUtil.create('div', className, this.pictogramsContainer),
- img = L.DomUtil.create('img', '', container);
- img.src = value;
- if (pictogram.name && pictogram.attribution) {
- img.title = pictogram.name + ' — © ' + pictogram.attribution;
- }
- L.DomEvent.on(container, 'click', function (e) {
- this.input.value = value;
- this.sync();
- this.unselectAll(this.pictogramsContainer);
- L.DomUtil.addClass(container, 'selected');
- this.pictogramsContainer.innerHTML = '';
- this.udpatePreview();
- }, this);
- },
-
- clear: function () {
- this.input.value = '';
- this.unselectAll(this.pictogramsContainer);
- this.sync();
- this.pictogramsContainer.innerHTML = '';
- this.udpatePreview();
- },
-
- buildIconList: function (data) {
- this.pictogramsContainer.innerHTML = '';
- this.buttonsContainer.innerHTML = '';
- for (var idx in data.pictogram_list) {
- this.addIconPreview(data.pictogram_list[idx]);
- }
- var cancelButton = L.DomUtil.create('a', '', this.pictogramsContainer);
- cancelButton.textContent = L._('Cancel');
- cancelButton.href = '#';
- cancelButton.style.display = 'block';
- cancelButton.style.clear = 'both';
- L.DomEvent
- .on(cancelButton, 'click', L.DomEvent.stop)
- .on(cancelButton, 'click', function (e) {
- this.pictogramsContainer.innerHTML = '';
- this.udpatePreview();
- }, this);
- var customButton = L.DomUtil.create('a', '', this.pictogramsContainer);
- customButton.textContent = L._('Set symbol');
- customButton.href = '#';
- customButton.style.display = 'block';
- customButton.style.clear = 'both';
- this.builder.map.help.button(customButton, 'formatIconSymbol');
- L.DomEvent
- .on(customButton, 'click', L.DomEvent.stop)
- .on(customButton, 'click', function (e) {
- this.input.type = 'text';
- this.pictogramsContainer.innerHTML = '';
- }, this);
- },
-
- fetchIconList: function (e) {
- this.builder.map.get(this.builder.map.options.urls.pictogram_list_json, {
- callback: this.buildIconList,
- context: this
- });
- },
-
- unselectAll: function (container) {
- var els = container.querySelectorAll('div.selected');
- for (var el in els) {
- if (els.hasOwnProperty(el)) L.DomUtil.removeClass(els[el], 'selected');
- }
+ udpatePreview: function () {
+ if (this.value() && this.value().indexOf('{') === -1) {
+ // Do not try to render URL with variables
+ if (this.isUrl()) {
+ var img = L.DomUtil.create(
+ 'img',
+ '',
+ L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer)
+ )
+ img.src = this.value()
+ L.DomEvent.on(img, 'click', this.fetchIconList, this)
+ } else {
+ var el = L.DomUtil.create(
+ 'span',
+ '',
+ L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer)
+ )
+ el.textContent = this.value()
+ L.DomEvent.on(el, 'click', this.fetchIconList, this)
+ }
}
+ this.button = L.DomUtil.create('a', '', this.buttonsContainer)
+ this.button.textContent = this.value() ? L._('Change symbol') : L._('Add symbol')
+ this.button.href = '#'
+ L.DomEvent.on(this.button, 'click', L.DomEvent.stop).on(
+ this.button,
+ 'click',
+ this.fetchIconList,
+ this
+ )
+ },
-});
+ addIconPreview: function (pictogram) {
+ var baseClass = 'umap-icon-choice',
+ value = pictogram.src,
+ className = value === this.value() ? baseClass + ' selected' : baseClass,
+ container = L.DomUtil.create('div', className, this.pictogramsContainer),
+ img = L.DomUtil.create('img', '', container)
+ img.src = value
+ if (pictogram.name && pictogram.attribution) {
+ img.title = pictogram.name + ' — © ' + pictogram.attribution
+ }
+ L.DomEvent.on(
+ container,
+ 'click',
+ function (e) {
+ this.input.value = value
+ this.sync()
+ this.unselectAll(this.pictogramsContainer)
+ L.DomUtil.addClass(container, 'selected')
+ this.pictogramsContainer.innerHTML = ''
+ this.udpatePreview()
+ },
+ this
+ )
+ },
+
+ clear: function () {
+ this.input.value = ''
+ this.unselectAll(this.pictogramsContainer)
+ this.sync()
+ this.pictogramsContainer.innerHTML = ''
+ this.udpatePreview()
+ },
+
+ buildIconList: function (data) {
+ this.pictogramsContainer.innerHTML = ''
+ this.buttonsContainer.innerHTML = ''
+ for (var idx in data.pictogram_list) {
+ this.addIconPreview(data.pictogram_list[idx])
+ }
+ var cancelButton = L.DomUtil.create('a', '', this.pictogramsContainer)
+ cancelButton.textContent = L._('Cancel')
+ cancelButton.href = '#'
+ cancelButton.style.display = 'block'
+ cancelButton.style.clear = 'both'
+ L.DomEvent.on(cancelButton, 'click', L.DomEvent.stop).on(
+ cancelButton,
+ 'click',
+ function (e) {
+ this.pictogramsContainer.innerHTML = ''
+ this.udpatePreview()
+ },
+ this
+ )
+ var customButton = L.DomUtil.create('a', '', this.pictogramsContainer)
+ customButton.textContent = L._('Set symbol')
+ customButton.href = '#'
+ customButton.style.display = 'block'
+ customButton.style.clear = 'both'
+ this.builder.map.help.button(customButton, 'formatIconSymbol')
+ L.DomEvent.on(customButton, 'click', L.DomEvent.stop).on(
+ customButton,
+ 'click',
+ function (e) {
+ this.input.type = 'text'
+ this.pictogramsContainer.innerHTML = ''
+ },
+ this
+ )
+ },
+
+ fetchIconList: function (e) {
+ this.builder.map.get(this.builder.map.options.urls.pictogram_list_json, {
+ callback: this.buildIconList,
+ context: this,
+ })
+ },
+
+ unselectAll: function (container) {
+ var els = container.querySelectorAll('div.selected')
+ for (var el in els) {
+ if (els.hasOwnProperty(el)) L.DomUtil.removeClass(els[el], 'selected')
+ }
+ },
+})
L.FormBuilder.Url = L.FormBuilder.Input.extend({
-
- type: function () {
- return 'url';
- }
-
-});
+ type: function () {
+ return 'url'
+ },
+})
L.FormBuilder.Switch = L.FormBuilder.CheckBox.extend({
+ getParentNode: function () {
+ L.FormBuilder.CheckBox.prototype.getParentNode.call(this)
+ if (this.options.inheritable) return this.quickContainer
+ return this.extendedContainer
+ },
- getParentNode: function () {
- L.FormBuilder.CheckBox.prototype.getParentNode.call(this);
- if (this.options.inheritable) return this.quickContainer;
- return this.extendedContainer;
- },
-
- build: function () {
- L.FormBuilder.CheckBox.prototype.build.apply(this);
- if (this.options.inheritable) this.label = L.DomUtil.create('label', '', this.input.parentNode);
- else this.input.parentNode.appendChild(this.label);
- L.DomUtil.addClass(this.input.parentNode, 'with-switch');
- var id = (this.builder.options.id || Date.now()) + '.' + this.name;
- this.label.setAttribute('for', id);
- L.DomUtil.addClass(this.input, 'switch');
- this.input.id = id;
- }
-
-});
+ build: function () {
+ L.FormBuilder.CheckBox.prototype.build.apply(this)
+ if (this.options.inheritable)
+ this.label = L.DomUtil.create('label', '', this.input.parentNode)
+ else this.input.parentNode.appendChild(this.label)
+ L.DomUtil.addClass(this.input.parentNode, 'with-switch')
+ var id = (this.builder.options.id || Date.now()) + '.' + this.name
+ this.label.setAttribute('for', id)
+ L.DomUtil.addClass(this.input, 'switch')
+ this.input.id = id
+ },
+})
L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({
+ default: 'null',
+ className: 'umap-multiplechoice',
- default: 'null',
- className: 'umap-multiplechoice',
+ clear: function () {
+ var checked = this.container.querySelector('input[type="radio"]:checked')
+ if (checked) checked.checked = false
+ },
- clear: function () {
- var checked = this.container.querySelector('input[type="radio"]:checked');
- if (checked) checked.checked = false;
- },
+ fetch: function () {
+ var value = (this.backup = this.toHTML())
+ if (!this.container.querySelector('input[type="radio"][value="' + value + '"]'))
+ value = this.default
+ this.container.querySelector(
+ 'input[type="radio"][value="' + value + '"]'
+ ).checked = true
+ },
- fetch: function () {
- var value = this.backup = this.toHTML();
- if (!this.container.querySelector('input[type="radio"][value="' + value + '"]')) value = this.default;
- this.container.querySelector('input[type="radio"][value="' + value + '"]').checked = true;
- },
+ value: function () {
+ var checked = this.container.querySelector('input[type="radio"]:checked')
+ if (checked) return checked.value
+ },
- value: function () {
- var checked = this.container.querySelector('input[type="radio"]:checked');
- if (checked) return checked.value;
- },
+ getChoices: function () {
+ return this.options.choices || this.choices
+ },
- getChoices: function () {
- return this.options.choices || this.choices;
- },
-
- build: function () {
- var choices = this.getChoices();
- this.container = L.DomUtil.create('div', this.className + ' by' + choices.length, this.parentNode);
- for (var i = 0; i < choices.length; i++) {
- this.addChoice(choices[i][0], choices[i][1], i);
- }
- this.fetch();
- },
-
- addChoice: function (value, label, counter) {
- var input = L.DomUtil.create('input', '', this.container);
- label = L.DomUtil.add('label', '', this.container, label);
- input.type = 'radio';
- input.name = this.name;
- input.value = value;
- var id = Date.now() + '.' + this.name + '.' + counter;
- label.setAttribute('for', id);
- input.id = id;
- L.DomEvent.on(input, 'change', this.sync, this);
+ build: function () {
+ var choices = this.getChoices()
+ this.container = L.DomUtil.create(
+ 'div',
+ this.className + ' by' + choices.length,
+ this.parentNode
+ )
+ for (var i = 0; i < choices.length; i++) {
+ this.addChoice(choices[i][0], choices[i][1], i)
}
+ this.fetch()
+ },
-});
+ addChoice: function (value, label, counter) {
+ var input = L.DomUtil.create('input', '', this.container)
+ label = L.DomUtil.add('label', '', this.container, label)
+ input.type = 'radio'
+ input.name = this.name
+ input.value = value
+ var id = Date.now() + '.' + this.name + '.' + counter
+ label.setAttribute('for', id)
+ input.id = id
+ L.DomEvent.on(input, 'change', this.sync, this)
+ },
+})
L.FormBuilder.TernaryChoices = L.FormBuilder.MultiChoice.extend({
+ default: 'null',
- default: 'null',
-
- toJS: function () {
- var value = this.value();
- switch (value) {
- case 'true':
- case true:
- value = true;
- break;
- case 'false':
- case false:
- value = false;
- break;
- default:
- value = null;
- }
- return value;
+ toJS: function () {
+ var value = this.value()
+ switch (value) {
+ case 'true':
+ case true:
+ value = true
+ break
+ case 'false':
+ case false:
+ value = false
+ break
+ default:
+ value = null
}
-
-});
+ return value
+ },
+})
L.FormBuilder.ControlChoice = L.FormBuilder.TernaryChoices.extend({
-
- choices: [
- [true, L._('always')],
- [false, L._('never')],
- ['null', L._('hidden')]
- ]
-
-});
-
+ choices: [
+ [true, L._('always')],
+ [false, L._('never')],
+ ['null', L._('hidden')],
+ ],
+})
L.FormBuilder.LabelChoice = L.FormBuilder.TernaryChoices.extend({
+ default: false,
- default: false,
-
- choices: [
- [true, L._('always')],
- [false, L._('never')],
- ['null', L._('on hover')]
- ]
-
-});
+ choices: [
+ [true, L._('always')],
+ [false, L._('never')],
+ ['null', L._('on hover')],
+ ],
+})
L.FormBuilder.DataLayersControl = L.FormBuilder.ControlChoice.extend({
+ choices: [
+ [true, L._('collapsed')],
+ ['expanded', L._('expanded')],
+ [false, L._('never')],
+ ['null', L._('hidden')],
+ ],
- choices: [
- [true, L._('collapsed')],
- ['expanded', L._('expanded')],
- [false, L._('never')],
- ['null', L._('hidden')]
- ],
-
- toJS: function () {
- var value = this.value();
- if (value !== 'expanded') value = L.FormBuilder.ControlChoice.prototype.toJS.call(this);
- return value;
- }
-
-});
+ toJS: function () {
+ var value = this.value()
+ if (value !== 'expanded')
+ value = L.FormBuilder.ControlChoice.prototype.toJS.call(this)
+ return value
+ },
+})
L.FormBuilder.OutlinkTarget = L.FormBuilder.MultiChoice.extend({
+ default: 'blank',
- default: 'blank',
-
- choices: [
- ['blank', L._('new window')],
- ['self', L._('iframe')],
- ['parent', L._('parent window')]
- ]
-
-});
+ choices: [
+ ['blank', L._('new window')],
+ ['self', L._('iframe')],
+ ['parent', L._('parent window')],
+ ],
+})
L.FormBuilder.Range = L.FormBuilder.Input.extend({
+ type: function () {
+ return 'range'
+ },
- type: function () {
- return 'range';
- },
-
- value: function () {
- return L.DomUtil.hasClass(this.wrapper, 'undefined') ? undefined : this.input.value;
- }
-
-});
-
+ value: function () {
+ return L.DomUtil.hasClass(this.wrapper, 'undefined') ? undefined : this.input.value
+ },
+})
L.FormBuilder.ManageOwner = L.FormBuilder.Element.extend({
-
- build: function () {
- var options = {
- className: 'edit-owner',
- on_select: L.bind(this.onSelect, this)
- };
- this.autocomplete = new L.U.AutoComplete.Ajax.Select(this.parentNode, options);
- var owner = this.toHTML();
- if (owner) this.autocomplete.displaySelected({'item': {'value': owner.id, 'label': owner.name}});
- },
-
- value: function () {
- return this._value;
- },
-
- onSelect: function (choice) {
- this._value = {
- 'id': choice.item.value,
- 'name': choice.item.label,
- 'url': choice.item.url
- };
- this.set();
+ build: function () {
+ var options = {
+ className: 'edit-owner',
+ on_select: L.bind(this.onSelect, this),
}
+ this.autocomplete = new L.U.AutoComplete.Ajax.Select(this.parentNode, options)
+ var owner = this.toHTML()
+ if (owner)
+ this.autocomplete.displaySelected({
+ item: { value: owner.id, label: owner.name },
+ })
+ },
+ value: function () {
+ return this._value
+ },
-});
-
+ onSelect: function (choice) {
+ this._value = {
+ id: choice.item.value,
+ name: choice.item.label,
+ url: choice.item.url,
+ }
+ this.set()
+ },
+})
L.FormBuilder.ManageEditors = L.FormBuilder.Element.extend({
-
- build: function () {
- var options = {
- className: 'edit-editors',
- on_select: L.bind(this.onSelect, this),
- on_unselect: L.bind(this.onUnselect, this)
- };
- this.autocomplete = new L.U.AutoComplete.Ajax.SelectMultiple(this.parentNode, options);
- this._values = this.toHTML();
- if (this._values) for (var i = 0; i < this._values.length; i++) this.autocomplete.displaySelected({'item': {'value': this._values[i].id, 'label': this._values[i].name}});
- },
-
- value: function () {
- return this._values;
- },
-
- onSelect: function (choice) {
- this._values.push({
- 'id': choice.item.value,
- 'name': choice.item.label,
- 'url': choice.item.url
- });
- this.set();
- },
-
- onUnselect: function (choice) {
- var index = this._values.findIndex(function (item) {return item.id === choice.item.value});
- if (index !== -1) {
- this._values.splice(index, 1);
- this.set();
- }
+ build: function () {
+ var options = {
+ className: 'edit-editors',
+ on_select: L.bind(this.onSelect, this),
+ on_unselect: L.bind(this.onUnselect, this),
}
+ this.autocomplete = new L.U.AutoComplete.Ajax.SelectMultiple(
+ this.parentNode,
+ options
+ )
+ this._values = this.toHTML()
+ if (this._values)
+ for (var i = 0; i < this._values.length; i++)
+ this.autocomplete.displaySelected({
+ item: { value: this._values[i].id, label: this._values[i].name },
+ })
+ },
-});
+ value: function () {
+ return this._values
+ },
+
+ onSelect: function (choice) {
+ this._values.push({
+ id: choice.item.value,
+ name: choice.item.label,
+ url: choice.item.url,
+ })
+ this.set()
+ },
+
+ onUnselect: function (choice) {
+ var index = this._values.findIndex(function (item) {
+ return item.id === choice.item.value
+ })
+ if (index !== -1) {
+ this._values.splice(index, 1)
+ this.set()
+ }
+ },
+})
L.U.FormBuilder = L.FormBuilder.extend({
+ options: {
+ className: 'umap-form',
+ },
- options: {
- className: 'umap-form'
+ defaultOptions: {
+ name: { label: L._('name') },
+ description: {
+ label: L._('description'),
+ handler: 'Textarea',
+ helpEntries: 'textFormatting',
},
-
- defaultOptions: {
- name: {label: L._('name')},
- description: {label: L._('description'), handler: 'Textarea', helpEntries: 'textFormatting'},
- color: {handler: 'ColorPicker', label: L._('color'), helpEntries: 'colorValue', inheritable: true},
- opacity: {handler: 'Range', min: 0.1, max: 1, step: 0.1, label: L._('opacity'), inheritable: true},
- stroke: {handler: 'Switch', label: L._('stroke'), helpEntries: 'stroke', inheritable: true},
- weight: {handler: 'Range', min: 1, max: 20, step: 1, label: L._('weight'), inheritable: true},
- fill: {handler: 'Switch', label: L._('fill'), helpEntries: 'fill', inheritable: true},
- fillColor: {handler: 'ColorPicker', label: L._('fill color'), helpEntries: 'fillColor', inheritable: true},
- fillOpacity: {handler: 'Range', min: 0.1, max: 1, step: 0.1, label: L._('fill opacity'), inheritable: true},
- smoothFactor: {handler: 'Range', min: 0, max: 10, step: 0.5, label: L._('Simplify'), helpEntries: 'smoothFactor', inheritable: true},
- dashArray: {label: L._('dash array'), helpEntries: 'dashArray', inheritable: true},
- iconClass: {handler: 'IconClassSwitcher', label: L._('Icon shape'), inheritable: true},
- iconUrl: {handler: 'IconUrl', label: L._('Icon symbol'), inheritable: true, helpText: L.U.Help.formatIconSymbol},
- popupShape: {handler: 'PopupShape', label: L._('Popup shape'), inheritable: true},
- popupTemplate: {handler: 'PopupContent', label: L._('Popup content style'), inheritable: true},
- popupContentTemplate: {label: L._('Popup content template'), handler: 'Textarea', helpEntries: ['dynamicProperties', 'textFormatting'], placeholder: '# {name}', inheritable: true},
- datalayer: {handler: 'DataLayerSwitcher', label: L._('Choose the layer of the feature')},
- moreControl: {handler: 'Switch', label: L._('Do you want to display the «more» control?')},
- scrollWheelZoom: {handler: 'Switch', label: L._('Allow scroll wheel zoom?')},
- miniMap: {handler: 'Switch', label: L._('Do you want to display a minimap?')},
- scaleControl: {handler: 'Switch', label: L._('Do you want to display the scale control?')},
- onLoadPanel: {handler: 'onLoadPanel', label: L._('Do you want to display a panel on load?')},
- displayPopupFooter: {handler: 'Switch', label: L._('Do you want to display popup footer?')},
- captionBar: {handler: 'Switch', label: L._('Do you want to display a caption bar?')},
- captionMenus: {handler: 'Switch', label: L._('Do you want to display caption menus?')},
- zoomTo: {handler: 'IntInput', placeholder: L._('Inherit'), helpEntries: 'zoomTo', label: L._('Default zoom level'), inheritable: true},
- showLabel: {handler: 'LabelChoice', label: L._('Display label'), inheritable: true},
- labelDirection: {handler: 'LabelDirection', label: L._('Label direction'), inheritable: true},
- labelInteractive: {handler: 'Switch', label: L._('Labels are clickable'), inheritable: true},
- labelKey: {helpEntries: 'labelKey', placeholder: L._('Default: name'), label: L._('Label key'), inheritable: true},
- zoomControl: {handler: 'ControlChoice', label: L._('Display the zoom control')},
- searchControl: {handler: 'ControlChoice', label: L._('Display the search control')},
- fullscreenControl: {handler: 'ControlChoice', label: L._('Display the fullscreen control')},
- embedControl: {handler: 'ControlChoice', label: L._('Display the embed control')},
- locateControl: {handler: 'ControlChoice', label: L._('Display the locate control')},
- measureControl: {handler: 'ControlChoice', label: L._('Display the measure control')},
- tilelayersControl: {handler: 'ControlChoice', label: L._('Display the tile layers control')},
- editinosmControl: {handler: 'ControlChoice', label: L._('Display the control to open OpenStreetMap editor')},
- datalayersControl: {handler: 'DataLayersControl', label: L._('Display the data layers control')},
+ color: {
+ handler: 'ColorPicker',
+ label: L._('color'),
+ helpEntries: 'colorValue',
+ inheritable: true,
},
-
- initialize: function (obj, fields, options) {
- this.map = obj.getMap();
- L.FormBuilder.prototype.initialize.call(this, obj, fields, options);
- this.on('finish', this.finish);
+ opacity: {
+ handler: 'Range',
+ min: 0.1,
+ max: 1,
+ step: 0.1,
+ label: L._('opacity'),
+ inheritable: true,
},
-
- setter: function (field, value) {
- L.FormBuilder.prototype.setter.call(this, field, value);
- this.obj.isDirty = true;
+ stroke: {
+ handler: 'Switch',
+ label: L._('stroke'),
+ helpEntries: 'stroke',
+ inheritable: true,
},
+ weight: {
+ handler: 'Range',
+ min: 1,
+ max: 20,
+ step: 1,
+ label: L._('weight'),
+ inheritable: true,
+ },
+ fill: {
+ handler: 'Switch',
+ label: L._('fill'),
+ helpEntries: 'fill',
+ inheritable: true,
+ },
+ fillColor: {
+ handler: 'ColorPicker',
+ label: L._('fill color'),
+ helpEntries: 'fillColor',
+ inheritable: true,
+ },
+ fillOpacity: {
+ handler: 'Range',
+ min: 0.1,
+ max: 1,
+ step: 0.1,
+ label: L._('fill opacity'),
+ inheritable: true,
+ },
+ smoothFactor: {
+ handler: 'Range',
+ min: 0,
+ max: 10,
+ step: 0.5,
+ label: L._('Simplify'),
+ helpEntries: 'smoothFactor',
+ inheritable: true,
+ },
+ dashArray: {
+ label: L._('dash array'),
+ helpEntries: 'dashArray',
+ inheritable: true,
+ },
+ iconClass: {
+ handler: 'IconClassSwitcher',
+ label: L._('Icon shape'),
+ inheritable: true,
+ },
+ iconUrl: {
+ handler: 'IconUrl',
+ label: L._('Icon symbol'),
+ inheritable: true,
+ helpText: L.U.Help.formatIconSymbol,
+ },
+ popupShape: { handler: 'PopupShape', label: L._('Popup shape'), inheritable: true },
+ popupTemplate: {
+ handler: 'PopupContent',
+ label: L._('Popup content style'),
+ inheritable: true,
+ },
+ popupContentTemplate: {
+ label: L._('Popup content template'),
+ handler: 'Textarea',
+ helpEntries: ['dynamicProperties', 'textFormatting'],
+ placeholder: '# {name}',
+ inheritable: true,
+ },
+ datalayer: {
+ handler: 'DataLayerSwitcher',
+ label: L._('Choose the layer of the feature'),
+ },
+ moreControl: {
+ handler: 'Switch',
+ label: L._('Do you want to display the «more» control?'),
+ },
+ scrollWheelZoom: { handler: 'Switch', label: L._('Allow scroll wheel zoom?') },
+ miniMap: { handler: 'Switch', label: L._('Do you want to display a minimap?') },
+ scaleControl: {
+ handler: 'Switch',
+ label: L._('Do you want to display the scale control?'),
+ },
+ onLoadPanel: {
+ handler: 'onLoadPanel',
+ label: L._('Do you want to display a panel on load?'),
+ },
+ displayPopupFooter: {
+ handler: 'Switch',
+ label: L._('Do you want to display popup footer?'),
+ },
+ captionBar: {
+ handler: 'Switch',
+ label: L._('Do you want to display a caption bar?'),
+ },
+ captionMenus: {
+ handler: 'Switch',
+ label: L._('Do you want to display caption menus?'),
+ },
+ zoomTo: {
+ handler: 'IntInput',
+ placeholder: L._('Inherit'),
+ helpEntries: 'zoomTo',
+ label: L._('Default zoom level'),
+ inheritable: true,
+ },
+ showLabel: {
+ handler: 'LabelChoice',
+ label: L._('Display label'),
+ inheritable: true,
+ },
+ labelDirection: {
+ handler: 'LabelDirection',
+ label: L._('Label direction'),
+ inheritable: true,
+ },
+ labelInteractive: {
+ handler: 'Switch',
+ label: L._('Labels are clickable'),
+ inheritable: true,
+ },
+ labelKey: {
+ helpEntries: 'labelKey',
+ placeholder: L._('Default: name'),
+ label: L._('Label key'),
+ inheritable: true,
+ },
+ zoomControl: { handler: 'ControlChoice', label: L._('Display the zoom control') },
+ searchControl: {
+ handler: 'ControlChoice',
+ label: L._('Display the search control'),
+ },
+ fullscreenControl: {
+ handler: 'ControlChoice',
+ label: L._('Display the fullscreen control'),
+ },
+ embedControl: { handler: 'ControlChoice', label: L._('Display the embed control') },
+ locateControl: {
+ handler: 'ControlChoice',
+ label: L._('Display the locate control'),
+ },
+ measureControl: {
+ handler: 'ControlChoice',
+ label: L._('Display the measure control'),
+ },
+ tilelayersControl: {
+ handler: 'ControlChoice',
+ label: L._('Display the tile layers control'),
+ },
+ editinosmControl: {
+ handler: 'ControlChoice',
+ label: L._('Display the control to open OpenStreetMap editor'),
+ },
+ datalayersControl: {
+ handler: 'DataLayersControl',
+ label: L._('Display the data layers control'),
+ },
+ },
- finish: function () {
- this.map.ui.closePanel();
- }
+ initialize: function (obj, fields, options) {
+ this.map = obj.getMap()
+ L.FormBuilder.prototype.initialize.call(this, obj, fields, options)
+ this.on('finish', this.finish)
+ },
-});
+ setter: function (field, value) {
+ L.FormBuilder.prototype.setter.call(this, field, value)
+ this.obj.isDirty = true
+ },
+
+ finish: function () {
+ this.map.ui.closePanel()
+ },
+})
diff --git a/umap/static/umap/js/umap.icon.js b/umap/static/umap/js/umap.icon.js
index 2faad1b3..475501d0 100644
--- a/umap/static/umap/js/umap.icon.js
+++ b/umap/static/umap/js/umap.icon.js
@@ -1,189 +1,199 @@
L.U.Icon = L.DivIcon.extend({
- initialize: function(map, options) {
- this.map = map;
- var default_options = {
- iconSize: null, // Made in css
- iconUrl: this.map.getDefaultOption('iconUrl'),
- feature: null
- };
- options = L.Util.extend({}, default_options, options);
- L.Icon.prototype.initialize.call(this, options);
- this.feature = this.options.feature;
- if (this.feature && this.feature.isReadOnly()) {
- this.options.className += ' readonly';
- }
- },
-
- _getIconUrl: function (name) {
- var url;
- if(this.feature && this.feature._getIconUrl(name)) url = this.feature._getIconUrl(name);
- else url = this.options[name + 'Url'];
- return this.formatUrl(url, this.feature);
- },
-
- _getColor: function () {
- var color;
- if(this.feature) color = this.feature.getOption('color');
- else if (this.options.color) color = this.options.color;
- else color = this.map.getDefaultOption('color');
- return color;
- },
-
- formatUrl: function (url, feature) {
- return L.Util.greedyTemplate(url || '', feature ? feature.extendedProperties() : {});
+ initialize: function (map, options) {
+ this.map = map
+ var default_options = {
+ iconSize: null, // Made in css
+ iconUrl: this.map.getDefaultOption('iconUrl'),
+ feature: null,
}
+ options = L.Util.extend({}, default_options, options)
+ L.Icon.prototype.initialize.call(this, options)
+ this.feature = this.options.feature
+ if (this.feature && this.feature.isReadOnly()) {
+ this.options.className += ' readonly'
+ }
+ },
-});
+ _getIconUrl: function (name) {
+ var url
+ if (this.feature && this.feature._getIconUrl(name))
+ url = this.feature._getIconUrl(name)
+ else url = this.options[name + 'Url']
+ return this.formatUrl(url, this.feature)
+ },
+
+ _getColor: function () {
+ var color
+ if (this.feature) color = this.feature.getOption('color')
+ else if (this.options.color) color = this.options.color
+ else color = this.map.getDefaultOption('color')
+ return color
+ },
+
+ formatUrl: function (url, feature) {
+ return L.Util.greedyTemplate(url || '', feature ? feature.extendedProperties() : {})
+ },
+})
L.U.Icon.Default = L.U.Icon.extend({
- default_options: {
- iconAnchor: new L.Point(16, 40),
- popupAnchor: new L.Point(0, -40),
- tooltipAnchor: new L.Point(16, -24),
- className: 'umap-div-icon'
- },
+ default_options: {
+ iconAnchor: new L.Point(16, 40),
+ popupAnchor: new L.Point(0, -40),
+ tooltipAnchor: new L.Point(16, -24),
+ className: 'umap-div-icon',
+ },
- initialize: function(map, options) {
- options = L.Util.extend({}, this.default_options, options);
- L.U.Icon.prototype.initialize.call(this, map, options);
- },
+ initialize: function (map, options) {
+ options = L.Util.extend({}, this.default_options, options)
+ L.U.Icon.prototype.initialize.call(this, map, options)
+ },
- _setColor: function() {
- var color = this._getColor();
- this.elements.container.style.backgroundColor = color;
- this.elements.arrow.style.borderTopColor = color;
- },
+ _setColor: function () {
+ var color = this._getColor()
+ this.elements.container.style.backgroundColor = color
+ this.elements.arrow.style.borderTopColor = color
+ },
- createIcon: function() {
- this.elements = {};
- this.elements.main = L.DomUtil.create('div');
- this.elements.container = L.DomUtil.create('div', 'icon_container', this.elements.main);
- this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main);
- var src = this._getIconUrl('icon');
- if (src) {
- // An url.
- if (src.indexOf('http') === 0 || src.indexOf('/') === 0 || src.indexOf('data:image') === 0) {
- this.elements.img = L.DomUtil.create('img', null, this.elements.container);
- this.elements.img.src = src;
- } else {
- this.elements.span = L.DomUtil.create('span', null, this.elements.container)
- this.elements.span.textContent = src;
- }
- }
- this._setColor();
- this._setIconStyles(this.elements.main, 'icon');
- return this.elements.main;
+ createIcon: function () {
+ this.elements = {}
+ this.elements.main = L.DomUtil.create('div')
+ this.elements.container = L.DomUtil.create(
+ 'div',
+ 'icon_container',
+ this.elements.main
+ )
+ this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main)
+ var src = this._getIconUrl('icon')
+ if (src) {
+ // An url.
+ if (
+ src.indexOf('http') === 0 ||
+ src.indexOf('/') === 0 ||
+ src.indexOf('data:image') === 0
+ ) {
+ this.elements.img = L.DomUtil.create('img', null, this.elements.container)
+ this.elements.img.src = src
+ } else {
+ this.elements.span = L.DomUtil.create('span', null, this.elements.container)
+ this.elements.span.textContent = src
+ }
}
-
-});
+ this._setColor()
+ this._setIconStyles(this.elements.main, 'icon')
+ return this.elements.main
+ },
+})
L.U.Icon.Circle = L.U.Icon.extend({
- initialize: function(map, options) {
- var default_options = {
- iconAnchor: new L.Point(6, 6),
- popupAnchor: new L.Point(0, -6),
- tooltipAnchor: new L.Point(6, 0),
- className: 'umap-circle-icon'
- };
- options = L.Util.extend({}, default_options, options);
- L.U.Icon.prototype.initialize.call(this, map, options);
- },
-
- _setColor: function() {
- this.elements.main.style.backgroundColor = this._getColor();
- },
-
- createIcon: function() {
- this.elements = {};
- this.elements.main = L.DomUtil.create('div');
- this.elements.main.innerHTML = ' ';
- this._setColor();
- this._setIconStyles(this.elements.main, 'icon');
- return this.elements.main;
+ initialize: function (map, options) {
+ var default_options = {
+ iconAnchor: new L.Point(6, 6),
+ popupAnchor: new L.Point(0, -6),
+ tooltipAnchor: new L.Point(6, 0),
+ className: 'umap-circle-icon',
}
+ options = L.Util.extend({}, default_options, options)
+ L.U.Icon.prototype.initialize.call(this, map, options)
+ },
-});
+ _setColor: function () {
+ this.elements.main.style.backgroundColor = this._getColor()
+ },
+
+ createIcon: function () {
+ this.elements = {}
+ this.elements.main = L.DomUtil.create('div')
+ this.elements.main.innerHTML = ' '
+ this._setColor()
+ this._setIconStyles(this.elements.main, 'icon')
+ return this.elements.main
+ },
+})
L.U.Icon.Drop = L.U.Icon.Default.extend({
- default_options: {
- iconAnchor: new L.Point(16, 42),
- popupAnchor: new L.Point(0, -42),
- tooltipAnchor: new L.Point(16, -24),
- className: 'umap-drop-icon'
- }
-});
+ default_options: {
+ iconAnchor: new L.Point(16, 42),
+ popupAnchor: new L.Point(0, -42),
+ tooltipAnchor: new L.Point(16, -24),
+ className: 'umap-drop-icon',
+ },
+})
L.U.Icon.Ball = L.U.Icon.Default.extend({
- default_options: {
- iconAnchor: new L.Point(8, 30),
- popupAnchor: new L.Point(0, -28),
- tooltipAnchor: new L.Point(8, -23),
- className: 'umap-ball-icon'
- },
+ default_options: {
+ iconAnchor: new L.Point(8, 30),
+ popupAnchor: new L.Point(0, -28),
+ tooltipAnchor: new L.Point(8, -23),
+ className: 'umap-ball-icon',
+ },
- createIcon: function() {
- this.elements = {};
- this.elements.main = L.DomUtil.create('div');
- this.elements.container = L.DomUtil.create('div', 'icon_container', this.elements.main);
- this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main);
- this._setColor();
- this._setIconStyles(this.elements.main, 'icon');
- return this.elements.main;
- },
+ createIcon: function () {
+ this.elements = {}
+ this.elements.main = L.DomUtil.create('div')
+ this.elements.container = L.DomUtil.create(
+ 'div',
+ 'icon_container',
+ this.elements.main
+ )
+ this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main)
+ this._setColor()
+ this._setIconStyles(this.elements.main, 'icon')
+ return this.elements.main
+ },
- _setColor: function() {
- var color = this._getColor('color'),
- background;
- if (L.Browser.ielt9) {
- background = color;
- }
- else if (L.Browser.webkit) {
- background = '-webkit-gradient( radial, 6 38%, 0, 6 38%, 8, from(white), to(' + color + ') )';
- }
- else {
- background = 'radial-gradient(circle at 6px 38% , white -4px, ' + color + ' 8px) repeat scroll 0 0 transparent';
- }
- this.elements.container.style.background = background;
+ _setColor: function () {
+ var color = this._getColor('color'),
+ background
+ if (L.Browser.ielt9) {
+ background = color
+ } else if (L.Browser.webkit) {
+ background =
+ '-webkit-gradient( radial, 6 38%, 0, 6 38%, 8, from(white), to(' + color + ') )'
+ } else {
+ background =
+ 'radial-gradient(circle at 6px 38% , white -4px, ' +
+ color +
+ ' 8px) repeat scroll 0 0 transparent'
}
+ this.elements.container.style.background = background
+ },
+})
-});
-
-var _CACHE_COLOR = {};
+var _CACHE_COLOR = {}
L.U.Icon.Cluster = L.DivIcon.extend({
- options: {
- iconSize: [40, 40]
- },
+ options: {
+ iconSize: [40, 40],
+ },
- initialize: function (datalayer, cluster) {
- this.datalayer = datalayer;
- this.cluster = cluster;
- },
+ initialize: function (datalayer, cluster) {
+ this.datalayer = datalayer
+ this.cluster = cluster
+ },
- createIcon: function () {
- var container = L.DomUtil.create('div', 'leaflet-marker-icon marker-cluster'),
- div = L.DomUtil.create('div', '', container),
- span = L.DomUtil.create('span', '', div),
- backgroundColor = this.datalayer.getColor();
- span.textContent = this.cluster.getChildCount();
- div.style.backgroundColor = backgroundColor;
- return container;
- },
+ createIcon: function () {
+ var container = L.DomUtil.create('div', 'leaflet-marker-icon marker-cluster'),
+ div = L.DomUtil.create('div', '', container),
+ span = L.DomUtil.create('span', '', div),
+ backgroundColor = this.datalayer.getColor()
+ span.textContent = this.cluster.getChildCount()
+ div.style.backgroundColor = backgroundColor
+ return container
+ },
- computeTextColor: function (el) {
- var color,
- backgroundColor = this.datalayer.getColor();
- if (this.datalayer.options.cluster && this.datalayer.options.cluster.textColor) {
- color = this.datalayer.options.cluster.textColor;
- }
- if (!color) {
- if (typeof _CACHE_COLOR[backgroundColor] === 'undefined') {
- color = L.DomUtil.TextColorFromBackgroundColor(el);
- _CACHE_COLOR[backgroundColor] = color;
- } else {
- color = _CACHE_COLOR[backgroundColor];
- }
- }
- return color;
+ computeTextColor: function (el) {
+ var color,
+ backgroundColor = this.datalayer.getColor()
+ if (this.datalayer.options.cluster && this.datalayer.options.cluster.textColor) {
+ color = this.datalayer.options.cluster.textColor
}
-
-});
+ if (!color) {
+ if (typeof _CACHE_COLOR[backgroundColor] === 'undefined') {
+ color = L.DomUtil.TextColorFromBackgroundColor(el)
+ _CACHE_COLOR[backgroundColor] = color
+ } else {
+ color = _CACHE_COLOR[backgroundColor]
+ }
+ }
+ return color
+ },
+})
diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js
index e58226a9..d9479f0e 100644
--- a/umap/static/umap/js/umap.js
+++ b/umap/static/umap/js/umap.js
@@ -1,1819 +1,2187 @@
L.Map.mergeOptions({
- overlay: null,
- datalayers: [],
- center: [4, 50],
- zoom: 6,
- hash: true,
- default_color: 'DarkBlue',
- default_smoothFactor: 1.0,
- default_opacity: 0.5,
- default_fillOpacity: 0.3,
- default_stroke: true,
- default_fill: true,
- default_weight: 3,
- default_iconClass: 'Default',
- default_popupContentTemplate: '# {name}\n{description}',
- default_interactive: true,
- default_labelDirection: 'auto',
- attributionControl: false,
- allowEdit: true,
- embedControl: true,
- zoomControl: true,
- datalayersControl: true,
- searchControl: true,
- editInOSMControl: false,
- editInOSMControlOptions: false,
- scaleControl: true,
- noControl: false, // Do not render any control.
- miniMap: false,
- name: '',
- description: '',
- displayPopupFooter: false,
- demoTileInfos: {s: 'a', z: 9, x: 265, y: 181, r: ''},
- licences: [],
- licence: '',
- enableMarkerDraw: true,
- enablePolygonDraw: true,
- enablePolylineDraw: true,
- limitBounds: {},
- importPresets: [
- // {url: 'http://localhost:8019/en/datalayer/1502/', label: 'Simplified World Countries', format: 'geojson'}
- ],
- moreControl: true,
- captionBar: false,
- captionMenus: true,
- slideshow: {},
- clickable: true,
- easing: false,
- permissions: {},
- permanentCreditBackground: true,
-});
+ overlay: null,
+ datalayers: [],
+ center: [4, 50],
+ zoom: 6,
+ hash: true,
+ default_color: 'DarkBlue',
+ default_smoothFactor: 1.0,
+ default_opacity: 0.5,
+ default_fillOpacity: 0.3,
+ default_stroke: true,
+ default_fill: true,
+ default_weight: 3,
+ default_iconClass: 'Default',
+ default_popupContentTemplate: '# {name}\n{description}',
+ default_interactive: true,
+ default_labelDirection: 'auto',
+ attributionControl: false,
+ allowEdit: true,
+ embedControl: true,
+ zoomControl: true,
+ datalayersControl: true,
+ searchControl: true,
+ editInOSMControl: false,
+ editInOSMControlOptions: false,
+ scaleControl: true,
+ noControl: false, // Do not render any control.
+ miniMap: false,
+ name: '',
+ description: '',
+ displayPopupFooter: false,
+ demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, r: '' },
+ licences: [],
+ licence: '',
+ enableMarkerDraw: true,
+ enablePolygonDraw: true,
+ enablePolylineDraw: true,
+ limitBounds: {},
+ importPresets: [
+ // {url: 'http://localhost:8019/en/datalayer/1502/', label: 'Simplified World Countries', format: 'geojson'}
+ ],
+ moreControl: true,
+ captionBar: false,
+ captionMenus: true,
+ slideshow: {},
+ clickable: true,
+ easing: false,
+ permissions: {},
+ permanentCreditBackground: true,
+})
L.U.Map.include({
-
- HIDDABLE_CONTROLS: ['zoom', 'search', 'fullscreen', 'embed', 'locate', 'measure', 'tilelayers', 'editinosm', 'datalayers'],
-
- initialize: function (el, geojson) {
-
- if (geojson.properties && geojson.properties.locale) L.setLocale(geojson.properties.locale);
-
- // Don't let default autocreation of controls
- var zoomControl = typeof geojson.properties.zoomControl !== 'undefined' ? geojson.properties.zoomControl : true;
- geojson.properties.zoomControl = false;
- var fullscreenControl = typeof geojson.properties.fullscreenControl !== 'undefined' ? geojson.properties.fullscreenControl : true;
- geojson.properties.fullscreenControl = false;
- L.Util.setBooleanFromQueryString(geojson.properties, 'scrollWheelZoom');
- L.Map.prototype.initialize.call(this, el, geojson.properties);
-
- this.ui = new L.U.UI(this._container);
- this.xhr = new L.U.Xhr(this.ui);
- this.xhr.on('dataloding', function (e) {
- this.fire('dataloding', e);
- });
- this.xhr.on('datalaod', function (e) {
- this.fire('datalaod', e);
- });
-
- this.initLoader();
- this.name = this.options.name;
- this.description = this.options.description;
- this.demoTileInfos = this.options.demoTileInfos;
- if (geojson.geometry) this.options.center = geojson.geometry;
- this.options.zoomControl = zoomControl;
- this.options.fullscreenControl = fullscreenControl;
- L.Util.setBooleanFromQueryString(this.options, 'moreControl');
- L.Util.setBooleanFromQueryString(this.options, 'scaleControl');
- L.Util.setBooleanFromQueryString(this.options, 'miniMap');
- L.Util.setBooleanFromQueryString(this.options, 'allowEdit');
- L.Util.setBooleanFromQueryString(this.options, 'displayDataBrowserOnLoad');
- L.Util.setBooleanFromQueryString(this.options, 'displayCaptionOnLoad');
- L.Util.setBooleanFromQueryString(this.options, 'captionBar');
- L.Util.setBooleanFromQueryString(this.options, 'captionMenus');
- for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
- L.Util.setNullableBooleanFromQueryString(this.options, this.HIDDABLE_CONTROLS[i] + 'Control');
- }
- this.datalayersOnLoad = L.Util.queryString('datalayers');
- this.options.onLoadPanel = L.Util.queryString('onLoadPanel', this.options.onLoadPanel);
- if (this.datalayersOnLoad) this.datalayersOnLoad = this.datalayersOnLoad.toString().split(',');
-
- if (L.Browser.ielt9) this.options.allowEdit = false; // TODO include ie9
-
- var editedFeature = null,
- self = this;
- try {
- Object.defineProperty(this, 'editedFeature', {
- get: function () {
- return editedFeature;
- },
- set: function (feature) {
- if (editedFeature && editedFeature !== feature) {
- editedFeature.endEdit();
- }
- editedFeature = feature;
- self.fire('seteditedfeature');
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
-
- if (this.options.hash) this.addHash();
- this.initCenter();
- this.handleLimitBounds();
-
- this.initTileLayers(this.options.tilelayers);
-
- // Global storage for retrieving datalayers and features
- this.datalayers = {};
- this.datalayers_index = [];
- this.dirty_datalayers = [];
- this.features_index = {};
-
- // Retrocompat
- if (this.options.slideshow && this.options.slideshow.delay && this.options.slideshow.active === undefined) this.options.slideshow.active = true;
-
- this.initControls();
-
- // create datalayers
- this.initDatalayers();
-
- if (this.options.displayCaptionOnLoad) {
- // Retrocompat
- if (!this.options.onLoadPanel) {
- this.options.onLoadPanel = 'caption';
- }
- delete this.options.displayCaptionOnLoad;
- }
- if (this.options.displayDataBrowserOnLoad) {
- // Retrocompat
- if (!this.options.onLoadPanel) {
- this.options.onLoadPanel = 'databrowser';
- }
- delete this.options.displayDataBrowserOnLoad;
- }
-
- this.ui.on('panel:closed', function () {
- this.invalidateSize({pan: false});
- }, this);
-
- var isDirty = false; // global status
- try {
- Object.defineProperty(this, 'isDirty', {
- get: function () {
- return isDirty || this.dirty_datalayers.length;
- },
- set: function (status) {
- if (!isDirty && status) self.fire('isdirty');
- isDirty = status;
- self.checkDirty();
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
- this.on('baselayerchange', function (e) {
- if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e);
- }, this);
-
- // Creation mode
- if (!this.options.umap_id) {
- this.isDirty = true;
- this._default_extent = true;
- this.options.name = L._('Untitled map');
- this.options.allowEdit = true;
- var datalayer = this.createDataLayer();
- datalayer.connectToMap();
- this.enableEdit();
- var dataUrl = L.Util.queryString('dataUrl', null),
- dataFormat = L.Util.queryString('dataFormat', 'geojson');
- if (dataUrl) {
- dataUrl = decodeURIComponent(dataUrl);
- dataUrl = this.localizeUrl(dataUrl);
- dataUrl = this.proxyUrl(dataUrl);
- datalayer.importFromUrl(dataUrl, dataFormat);
- }
- }
-
- this.help = new L.U.Help(this);
- this.slideshow = new L.U.Slideshow(this, this.options.slideshow);
- this.permissions = new L.U.MapPermissions(this);
- this.initCaptionBar();
- if (this.options.allowEdit) {
- this.editTools = new L.U.Editable(this);
- this.ui.on('panel:closed panel:open', function () {
- this.editedFeature = null;
- }, this);
- this.initEditBar();
- }
- this.initShortcuts();
- this.onceDatalayersLoaded(function () {
- if (this.options.onLoadPanel === 'databrowser') this.openBrowser();
- else if (this.options.onLoadPanel === 'caption') this.displayCaption();
- else if (this.options.onLoadPanel === 'datafilters') this.openFilter();
- });
- this.onceDataLoaded(function () {
- const slug = L.Util.queryString('feature');
- if (slug && this.features_index[slug]) this.features_index[slug].view();
- });
-
-
- window.onbeforeunload = function (e) {
- var msg = L._('You have unsaved changes.');
- if (self.isDirty) {
- e.returnValue = msg;
- return msg;
- }
- };
- this.backup();
- this.initContextMenu();
- this.on('click contextmenu.show', this.closeInplaceToolbar);
- },
-
- initControls: function () {
- this.helpMenuActions = {};
- this._controls = {};
-
- if (this.options.allowEdit && !this.options.noControl) {
- new L.U.EditControl(this).addTo(this);
-
- new L.U.DrawToolbar({map: this}).addTo(this);
-
- var editActions = [
- L.U.ImportAction,
- L.U.EditPropertiesAction,
- L.U.ChangeTileLayerAction,
- L.U.ManageDatalayersAction,
- L.U.UpdateExtentAction,
- L.U.UpdatePermsAction
- ];
- new L.U.SettingsToolbar({actions: editActions}).addTo(this);
- }
- this._controls.zoom = new L.Control.Zoom({zoomInTitle: L._('Zoom in'), zoomOutTitle: L._('Zoom out')});
- this._controls.datalayers = new L.U.DataLayersControl(this);
- this._controls.locate = L.control.locate({
- strings: {
- title: L._('Center map on your location'),
- },
- showPopup: false,
- // We style this control in our own CSS for consistency with other controls,
- // but the control breaks if we don't specify a class here, so a fake class
- // will do.
- icon: 'umap-fake-class',
- iconLoading: 'umap-fake-class',
- flyTo: this.options.easing
- });
- this._controls.fullscreen = new L.Control.Fullscreen({title: {'false': L._('View Fullscreen'), 'true': L._('Exit Fullscreen')}});
- this._controls.search = new L.U.SearchControl();
- this._controls.embed = new L.Control.Embed(this, this.options.embedOptions);
- this._controls.tilelayers = new L.U.TileLayerControl(this);
- this._controls.editinosm = new L.Control.EditInOSM({
- position: 'topleft',
- widgetOptions: {helpText: L._('Open this map extent in a map editor to provide more accurate data to OpenStreetMap')}
- });
- this._controls.measure = (new L.MeasureControl()).initHandler(this);
- this._controls.more = new L.U.MoreControls();
- this._controls.scale = L.control.scale();
- this._controls.permanentCredit = new L.U.PermanentCreditsControl(this);
- if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable();
- else this.scrollWheelZoom.disable();
- this.renderControls();
- },
-
- renderControls: function () {
- L.DomUtil.classIf(document.body, 'umap-caption-bar-enabled', this.options.captionBar || (this.options.slideshow && this.options.slideshow.active));
- L.DomUtil.classIf(document.body, 'umap-slideshow-enabled', this.options.slideshow && this.options.slideshow.active);
- for (var i in this._controls) {
- this.removeControl(this._controls[i]);
- }
- if (this.options.noControl) return;
-
- this._controls.attribution = (new L.U.AttributionControl()).addTo(this);
- if (this.options.miniMap && !this.options.noControl) {
- this.whenReady(function () {
- if (this.selected_tilelayer) {
- this._controls.miniMap = new L.Control.MiniMap(this.selected_tilelayer).addTo(this);
- this._controls.miniMap._miniMap.invalidateSize();
- }
- });
- }
- var name, status, control;
- for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
- name = this.HIDDABLE_CONTROLS[i];
- status = this.options[name + 'Control'];
- if (status === false) continue;
- control = this._controls[name];
- control.addTo(this);
- if (status === undefined || status === null) L.DomUtil.addClass(control._container, 'display-on-more');
- else L.DomUtil.removeClass(control._container, 'display-on-more');
- }
- if (this.options.permanentCredit) this._controls.permanentCredit.addTo(this);
- if (this.options.moreControl) this._controls.more.addTo(this);
- if (this.options.scaleControl) this._controls.scale.addTo(this);
- },
-
- initDatalayers: function () {
- var toload = dataToload = seen = this.options.datalayers.length,
- self = this,
- datalayer;
- var loaded = function () {
- self.datalayersLoaded = true;
- self.fire('datalayersloaded');
- };
- var decrementToLoad = function () {
- toload--;
- if (toload === 0) loaded();
- };
- var dataLoaded = function () {
- self.dataLoaded = true;
- self.fire('dataloaded');
- };
- var decrementDataToLoad = function () {
- dataToload--;
- if (dataToload === 0) dataLoaded();
- };
- for (var j = 0; j < this.options.datalayers.length; j++) {
- datalayer = this.createDataLayer(this.options.datalayers[j]);
- if (datalayer.displayedOnLoad()) datalayer.onceLoaded(decrementToLoad);
- else decrementToLoad();
- if (datalayer.displayedOnLoad()) datalayer.onceDataLoaded(decrementDataToLoad);
- else decrementDataToLoad();
- }
- if (seen === 0) loaded() && dataLoaded(); // no datalayer
- },
-
- indexDatalayers: function () {
- var panes = this.getPane('overlayPane'),
- pane;
- this.datalayers_index = [];
- for (var i = 0; i < panes.children.length; i++) {
- pane = panes.children[i];
- if (!pane.dataset || !pane.dataset.id) continue;
- this.datalayers_index.push(this.datalayers[pane.dataset.id]);
- }
- this.updateDatalayersControl();
- },
-
- ensurePanesOrder: function () {
- this.eachDataLayer(function (datalayer) {
- datalayer.bringToTop();
- });
- },
-
- onceDatalayersLoaded: function (callback, context) {
- // Once datalayers **metadata** have been loaded
- if (this.datalayersLoaded) {
- callback.call(context || this, this);
- } else {
- this.once('datalayersloaded', callback, context);
- }
- return this;
- },
-
- onceDataLoaded: function (callback, context) {
- // Once datalayers **data** have been loaded
- if (this.dataLoaded) {
- callback.call(context || this, this);
- } else {
- this.once('dataloaded', callback, context);
- }
- return this;
- },
-
- updateDatalayersControl: function () {
- if (this._controls.datalayers) this._controls.datalayers.update();
- },
-
- backupOptions: function () {
- this._backupOptions = L.extend({}, this.options);
- this._backupOptions.tilelayer = L.extend({}, this.options.tilelayer);
- this._backupOptions.limitBounds = L.extend({}, this.options.limitBounds);
- this._backupOptions.permissions = L.extend({}, this.permissions.options);
- },
-
- resetOptions: function () {
- this.options = L.extend({}, this._backupOptions);
- this.options.tilelayer = L.extend({}, this._backupOptions.tilelayer);
- this.permissions.options = L.extend({}, this._backupOptions.permissions);
- },
-
- initShortcuts: function () {
- var globalShortcuts = function (e) {
- var key = e.keyCode,
- modifierKey = e.ctrlKey || e.metaKey;
-
- /* Generic shortcuts */
- if (key === L.U.Keys.F && modifierKey) {
- L.DomEvent.stop(e);
- this.search();
- } else if (e.keyCode === L.U.Keys.ESC) {
- if (this.help.visible()) this.help.hide();
- else this.ui.closePanel();
- }
-
- if (!this.options.allowEdit) return;
-
- /* Edit mode only shortcuts */
- if (key === L.U.Keys.E && modifierKey && !this.editEnabled) {
- L.DomEvent.stop(e);
- this.enableEdit();
- } else if (key === L.U.Keys.E && modifierKey && this.editEnabled && !this.isDirty) {
- L.DomEvent.stop(e);
- this.disableEdit();
- this.ui.closePanel();
- }
- if (key === L.U.Keys.S && modifierKey) {
- L.DomEvent.stop(e);
- if (this.isDirty) {
- this.save();
- }
- }
- if (key === L.U.Keys.Z && modifierKey && this.isDirty) {
- L.DomEvent.stop(e);
- this.askForReset();
- }
- if (key === L.U.Keys.M && modifierKey && this.editEnabled) {
- L.DomEvent.stop(e);
- this.editTools.startMarker();
- }
- if (key === L.U.Keys.P && modifierKey && this.editEnabled) {
- L.DomEvent.stop(e);
- this.editTools.startPolygon();
- }
- if (key === L.U.Keys.L && modifierKey && this.editEnabled) {
- L.DomEvent.stop(e);
- this.editTools.startPolyline();
- }
- if (key === L.U.Keys.I && modifierKey && this.editEnabled) {
- L.DomEvent.stop(e);
- this.importPanel();
- }
- if (key === L.U.Keys.H && modifierKey && this.editEnabled) {
- L.DomEvent.stop(e);
- this.help.show('edit');
- }
- if (e.keyCode === L.U.Keys.ESC) {
- if (this.editEnabled) this.editTools.stopDrawing();
- if (this.measureTools.enabled()) this.measureTools.stopDrawing();
- }
- };
- L.DomEvent.addListener(document, 'keydown', globalShortcuts, this);
- },
-
- initTileLayers: function () {
- this.tilelayers = [];
- for(var i in this.options.tilelayers) {
- if(this.options.tilelayers.hasOwnProperty(i)) {
- this.tilelayers.push(this.createTileLayer(this.options.tilelayers[i]));
- if (this.options.tilelayer && this.options.tilelayer.url_template === this.options.tilelayers[i].url_template) {
- // Keep control over the displayed attribution for non custom tilelayers
- this.options.tilelayer.attribution = this.options.tilelayers[i].attribution;
- }
- }
- }
- if (this.options.tilelayer && this.options.tilelayer.url_template && this.options.tilelayer.attribution) {
- this.customTilelayer = this.createTileLayer(this.options.tilelayer);
- this.selectTileLayer(this.customTilelayer);
- } else {
- this.selectTileLayer(this.tilelayers[0]);
- }
- },
-
- createTileLayer: function (tilelayer) {
- return new L.TileLayer(tilelayer.url_template, tilelayer);
- },
-
- selectTileLayer: function (tilelayer) {
- if (tilelayer === this.selected_tilelayer) { return; }
- try {
- this.addLayer(tilelayer);
- this.fire('baselayerchange', {layer: tilelayer});
- if (this.selected_tilelayer) {
- this.removeLayer(this.selected_tilelayer);
- }
- this.selected_tilelayer = tilelayer;
- if (!isNaN(this.selected_tilelayer.options.minZoom) && this.getZoom() < this.selected_tilelayer.options.minZoom) {
- this.setZoom(this.selected_tilelayer.options.minZoom);
- }
- if (!isNaN(this.selected_tilelayer.options.maxZoom) && this.getZoom() > this.selected_tilelayer.options.maxZoom) {
- this.setZoom(this.selected_tilelayer.options.maxZoom);
- }
- } catch (e) {
- this.removeLayer(tilelayer);
- this.ui.alert({content: L._('Error in the tilelayer URL') + ': ' + tilelayer._url, level: 'error'});
- // Users can put tilelayer URLs by hand, and if they add wrong {variable},
- // Leaflet throw an error, and then the map is no more editable
- }
- this.setOverlay()
- },
-
- eachTileLayer: function (method, context) {
- var urls = [];
- for (var i in this.tilelayers) {
- if (this.tilelayers.hasOwnProperty(i)) {
- method.call(context, this.tilelayers[i]);
- urls.push(this.tilelayers[i]._url);
- }
- }
- if (this.customTilelayer && (Array.prototype.indexOf && urls.indexOf(this.customTilelayer._url) === -1)) {
- method.call(context || this, this.customTilelayer);
- }
- },
-
- setOverlay: function () {
- if (!this.options.overlay || !this.options.overlay.url_template) return;
- var overlay = this.createTileLayer(this.options.overlay);
- try {
- this.addLayer(overlay);
- if (this.overlay) this.removeLayer(this.overlay);
- this.overlay = overlay;
- } catch (e) {
- this.removeLayer(overlay);
- console.error(e);
- this.ui.alert({content: L._('Error in the overlay URL') + ': ' + overlay._url, level: 'error'});
- }
- },
-
- initCenter: function () {
- if (this.options.hash && this._hash.parseHash(location.hash)) {
- // FIXME An invalid hash will cause the load to fail
- this._hash.update();
- }
- else if(this.options.locate && this.options.locate.setView) {
- // Prevent from making two setViews at init
- // which is not very fluid...
- this.locate(this.options.locate);
- }
- else {
- this.options.center = this.latLng(this.options.center);
- this.setView(this.options.center, this.options.zoom);
- }
- },
-
- latLng: function(a, b, c) {
- // manage geojson case and call original method
- if (!(a instanceof L.LatLng) && a.coordinates) {
- // Guess it's a geojson
- a = [a.coordinates[1], a.coordinates[0]];
- }
- return L.latLng(a, b, c);
- },
-
- handleLimitBounds: function () {
- var south = parseFloat(this.options.limitBounds.south),
- west = parseFloat(this.options.limitBounds.west),
- north = parseFloat(this.options.limitBounds.north),
- east = parseFloat(this.options.limitBounds.east);
- if (!isNaN(south) && !isNaN(west) && !isNaN(north) && !isNaN(east)) {
- var bounds = L.latLngBounds([[south, west], [north, east]]);
- this.options.minZoom = this.getBoundsZoom(bounds, false);
- try {
- this.setMaxBounds(bounds);
- } catch (e) {
- // Unusable bounds, like -2 -2 -2 -2?
- console.error('Error limiting bounds', e);
- }
- } else {
- this.options.minZoom = 0;
- this.setMaxBounds();
- }
- },
-
- setMaxBounds: function (bounds) {
- // Hack. Remove me when fix is released:
- // https://github.com/Leaflet/Leaflet/pull/4494
- bounds = L.latLngBounds(bounds);
-
- if (!bounds.isValid()) {
- this.options.maxBounds = null;
- return this.off('moveend', this._panInsideMaxBounds);
- }
- return L.Map.prototype.setMaxBounds.call(this, bounds);
- },
-
- createDataLayer: function(datalayer) {
- datalayer = datalayer || {name: L._('Layer') + ' ' + (this.datalayers_index.length + 1)};
- return new L.U.DataLayer(this, datalayer);
- },
-
- getDefaultOption: function (option) {
- return this.options['default_' + option];
- },
-
- getOption: function (option) {
- if (L.Util.usableOption(this.options, option)) return this.options[option];
- return this.getDefaultOption(option);
- },
-
- updateExtent: function() {
- this.options.center = this.getCenter();
- this.options.zoom = this.getZoom();
- this.isDirty = true;
- this._default_extent = false;
- this.ui.alert({content: L._('The zoom and center have been set.'), 'level': 'info'});
- },
-
- updateTileLayers: function () {
- var self = this,
- callback = function (tilelayer) {
- self.options.tilelayer = tilelayer.toJSON();
- self.isDirty = true;
- };
- if (this._controls.tilelayers) this._controls.tilelayers.openSwitcher({callback: callback, className: 'dark'});
- },
-
- manageDatalayers: function () {
- if (this._controls.datalayers) this._controls.datalayers.openPanel();
- },
-
- renderShareBox: function () {
- var container = L.DomUtil.create('div', 'umap-share'),
- embedTitle = L.DomUtil.add('h4', '', container, L._('Embed the map')),
- iframe = L.DomUtil.create('textarea', 'umap-share-iframe', container),
- option;
- var UIFields = [
- ['dimensions.width', {handler: 'Input', label: L._('width')}],
- ['dimensions.height', {handler: 'Input', label: L._('height')}],
- ['options.includeFullScreenLink', {handler: 'Switch', label: L._('Include full screen link?')}],
- ['options.currentView', {handler: 'Switch', label: L._('Current view instead of default map view?')}],
- ['options.keepCurrentDatalayers', {handler: 'Switch', label: L._('Keep current visible layers')}],
- ['options.viewCurrentFeature', {handler: 'Switch', label: L._('Open current feature on load')}],
- 'queryString.moreControl',
- 'queryString.scrollWheelZoom',
- 'queryString.miniMap',
- 'queryString.scaleControl',
- 'queryString.onLoadPanel',
- 'queryString.captionBar',
- 'queryString.captionMenus'
- ];
- for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
- UIFields.push('queryString.' + this.HIDDABLE_CONTROLS[i] + 'Control');
- }
- var iframeExporter = new L.U.IframeExporter(this);
- var buildIframeCode = function () {
- iframe.innerHTML = iframeExporter.build();
- };
- buildIframeCode();
- var builder = new L.U.FormBuilder(iframeExporter, UIFields, {
- callback: buildIframeCode
- });
- var iframeOptions = L.DomUtil.createFieldset(container, L._('Iframe export options'));
- iframeOptions.appendChild(builder.build());
- if (this.options.shortUrl) {
- L.DomUtil.create('hr', '', container);
- L.DomUtil.add('h4', '', container, L._('Short URL'));
- var shortUrl = L.DomUtil.create('input', 'umap-short-url', container);
- shortUrl.type = 'text';
- shortUrl.value = this.options.shortUrl;
- }
- L.DomUtil.create('hr', '', container);
- L.DomUtil.add('h4', '', container, L._('Download data'));
- var typeInput = L.DomUtil.create('select', '', container);
- typeInput.name = 'format';
- var exportCaveat = L.DomUtil.add('small', 'help-text', container, L._('Only visible features will be downloaded.'));
- exportCaveat.id = 'export_caveat_text';
- var toggleCaveat = function () {
- if (typeInput.value === 'umap') exportCaveat.style.display = 'none';
- else exportCaveat.style.display = 'inherit';
- }
- L.DomEvent.on(typeInput, 'change', toggleCaveat);
- var types = {
- geojson: {
- formatter: function (map) {return JSON.stringify(map.toGeoJSON(), null, 2);},
- ext: '.geojson',
- filetype: 'application/json'
- },
- gpx: {
- formatter: function (map) {return togpx(map.toGeoJSON());},
- ext: '.gpx',
- filetype: 'application/xml'
- },
- kml: {
- formatter: function (map) {return tokml(map.toGeoJSON());},
- ext: '.kml',
- filetype: 'application/vnd.google-earth.kml+xml'
- },
- umap: {
- name: L._('Full map data'),
- formatter: function (map) {return map.serialize();},
- ext: '.umap',
- filetype: 'application/json',
- selected: true
- }
- };
- for (var key in types) {
- if (types.hasOwnProperty(key)) {
- option = L.DomUtil.create('option', '', typeInput);
- option.value = key;
- option.textContent = types[key].name || key;
- if (types[key].selected) option.selected = true;
- }
- }
- toggleCaveat();
- var download = L.DomUtil.create('a', 'button', container);
- download.textContent = L._('Download data');
- L.DomEvent.on(download, 'click', function () {
- var type = types[typeInput.value],
- content = type.formatter(this),
- name = this.options.name || 'data';
- name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
- download.download = name + type.ext;
- window.URL = window.URL || window.webkitURL;
- var blob = new Blob([content], {type: type.filetype});
- download.href = window.URL.createObjectURL(blob);
- }, this);
- this.ui.openPanel({data: {html: container}});
- },
-
- toGeoJSON: function () {
- var features = [];
- this.eachDataLayer(function (datalayer) {
- if (datalayer.isVisible()) {
- features = features.concat(datalayer.featuresToGeoJSON());
- }
- });
- var geojson = {
- type: 'FeatureCollection',
- features: features
- };
- return geojson;
- },
-
- importPanel: function () {
- var container = L.DomUtil.create('div', 'umap-upload'),
- title = L.DomUtil.create('h4', '', container),
- presetBox = L.DomUtil.create('div', 'formbox', container),
- presetSelect = L.DomUtil.create('select', '', presetBox),
- fileBox = L.DomUtil.create('div', 'formbox', container),
- fileInput = L.DomUtil.create('input', '', fileBox),
- urlInput = L.DomUtil.create('input', '', container),
- rawInput = L.DomUtil.create('textarea', '', container),
- typeLabel = L.DomUtil.create('label', '', container),
- layerLabel = L.DomUtil.create('label', '', container),
- clearLabel = L.DomUtil.create('label', '', container),
- submitInput = L.DomUtil.create('input', '', container),
- map = this, option,
- types = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap'];
- title.textContent = L._('Import data');
- fileInput.type = 'file';
- fileInput.multiple = 'multiple';
- submitInput.type = 'button';
- submitInput.value = L._('Import');
- submitInput.className = 'button';
- typeLabel.textContent = L._('Choose the format of the data to import');
- this.help.button(typeLabel, 'importFormats');
- var typeInput = L.DomUtil.create('select', '', typeLabel);
- typeInput.name = 'format';
- layerLabel.textContent = L._('Choose the layer to import in');
- var layerInput = L.DomUtil.create('select', '', layerLabel);
- layerInput.name = 'datalayer';
- urlInput.type = 'text';
- urlInput.placeholder = L._('Provide an URL here');
- rawInput.placeholder = L._('Paste your data here');
- clearLabel.textContent = L._('Replace layer content');
- var clearFlag = L.DomUtil.create('input', '', clearLabel);
- clearFlag.type = 'checkbox';
- clearFlag.name = 'clear';
- this.eachDataLayerReverse(function (datalayer) {
- if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
- var id = L.stamp(datalayer);
- option = L.DomUtil.create('option', '', layerInput);
- option.value = id;
- option.textContent = datalayer.options.name;
- }
- });
- L.DomUtil.element('option', {value: '', textContent: L._('Import in a new layer')}, layerInput);
- L.DomUtil.element('option', {value: '', textContent: L._('Choose the data format')}, typeInput);
- for (var i = 0; i < types.length; i++) {
- option = L.DomUtil.create('option', '', typeInput);
- option.value = option.textContent = types[i];
- }
- if (this.options.importPresets.length) {
- var noPreset = L.DomUtil.create('option', '', presetSelect);
- noPreset.value = noPreset.textContent = L._('Choose a preset');
- for (var j = 0; j < this.options.importPresets.length; j++) {
- option = L.DomUtil.create('option', '', presetSelect);
- option.value = this.options.importPresets[j].url;
- option.textContent = this.options.importPresets[j].label;
- }
- } else {
- presetBox.style.display = 'none';
- }
-
- var submit = function () {
- var type = typeInput.value,
- layerId = layerInput[layerInput.selectedIndex].value,
- layer;
- if (type === 'umap') {
- this.once('postsync', function () {
- this.setView(this.latLng(this.options.center), this.options.zoom);
- });
- }
- if (layerId) layer = map.datalayers[layerId];
- if (layer && clearFlag.checked) layer.empty();
- if (fileInput.files.length) {
- var file;
- for (var i = 0, file; file = fileInput.files[i]; i++) {
- type = type || L.Util.detectFileType(file);
- if (!type) {
- this.ui.alert({content: L._('Unable to detect format of file {filename}', {filename: file.name}), level: 'error'});
- continue;
- }
- if (type === 'umap') {
- this.importFromFile(file, 'umap');
- } else {
- var importLayer = layer;
- if (!layer) importLayer = this.createDataLayer({name: file.name});
- importLayer.importFromFile(file, type);
- }
- }
- } else {
- if (!type) return this.ui.alert({content: L._('Please choose a format'), level: 'error'});
- if (rawInput.value && type === 'umap') {
- try {
- this.importRaw(rawInput.value, type);
- } catch (e) {
- this.ui.alert({content: L._('Invalid umap data'), level: 'error'});
- console.error(e);
- }
- } else {
- if (!layer) layer = this.createDataLayer();
- if (rawInput.value) layer.importRaw(rawInput.value, type);
- else if (urlInput.value) layer.importFromUrl(urlInput.value, type);
- else if (presetSelect.selectedIndex > 0) layer.importFromUrl(presetSelect[presetSelect.selectedIndex].value, type);
- }
- }
- };
- L.DomEvent.on(submitInput, 'click', submit, this);
- L.DomEvent.on(fileInput, 'change', function (e) {
- var type = '', newType;
- for (var i = 0; i < e.target.files.length; i++) {
- newType = L.Util.detectFileType(e.target.files[i]);
- if (!type && newType) type = newType;
- if (type && newType !== type) {
- type = '';
- break;
- }
- }
- typeInput.value = type;
- }, this);
- this.ui.openPanel({data: {html: container}, className: 'dark'});
- },
-
- importRaw: function(rawData) {
- var importedData = JSON.parse(rawData);
-
- var mustReindex = false;
-
- for (var i = 0; i < this.editableOptions.length; i++) {
- var option = this.editableOptions[i];
- if (typeof importedData.properties[option] !== 'undefined') {
- this.options[option] = importedData.properties[option];
- if (option === 'sortKey') mustReindex = true;
- }
- }
-
- if (importedData.geometry) this.options.center = this.latLng(importedData.geometry);
- var self = this;
- importedData.layers.forEach( function (geojson) {
- var dataLayer = self.createDataLayer();
- dataLayer.fromUmapGeoJSON(geojson);
- });
-
- this.initTileLayers();
- this.renderControls();
- this.handleLimitBounds();
- this.eachDataLayer(function (datalayer) {
- if (mustReindex) datalayer.reindex();
- datalayer.redraw();
- });
- this.fire('postsync');
- this.isDirty = true;
- },
-
- importFromFile: function (file) {
- var reader = new FileReader();
- reader.readAsText(file);
- var self = this;
- reader.onload = function (e) {
- var rawData = e.target.result;
- try {
- self.importRaw(rawData);
- } catch (e) {
- console.error('Error importing data', e);
- self.ui.alert({content: L._('Invalid umap data in {filename}', {filename: file.name}), level: 'error'});
- }
- };
- },
-
- openBrowser: function () {
- this.onceDatalayersLoaded(function () {
- this._openBrowser();
- });
- },
-
- openFilter: function () {
- this.onceDatalayersLoaded(function () {
- this._openFilter();
- });
- },
-
- displayCaption: function () {
- var container = L.DomUtil.create('div', 'umap-caption'),
- title = L.DomUtil.create('h3', '', container);
- title.textContent = this.options.name;
- this.permissions.addOwnerLink('h5', container);
- if (this.options.description) {
- var description = L.DomUtil.create('div', 'umap-map-description', container);
- description.innerHTML = L.Util.toHTML(this.options.description);
- }
- var datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container);
- this.eachVisibleDataLayer(function (datalayer) {
- var p = L.DomUtil.create('p', '', datalayerContainer),
- color = L.DomUtil.create('span', 'datalayer-color', p),
- headline = L.DomUtil.create('strong', '', p),
- description = L.DomUtil.create('span', '', p);
- datalayer.onceLoaded(function () {
- color.style.backgroundColor = this.getColor();
- if (datalayer.options.description) {
- description.innerHTML = L.Util.toHTML(datalayer.options.description);
- }
- });
- datalayer.renderToolbox(headline);
- L.DomUtil.add('span', '', headline, datalayer.options.name + ' ');
- });
- var creditsContainer = L.DomUtil.create('div', 'credits-container', container),
- credits = L.DomUtil.createFieldset(creditsContainer, L._('Credits'));
- title = L.DomUtil.add('h5', '', credits, L._('User content credits'));
- if (this.options.shortCredit || this.options.longCredit) {
- L.DomUtil.add('p', '', credits, L.Util.toHTML(this.options.longCredit || this.options.shortCredit));
- }
- if (this.options.licence) {
- var licence = L.DomUtil.add('p', '', credits, L._('Map user content has been published under licence') + ' '),
- link = L.DomUtil.add('a', '', licence, this.options.licence.name);
- link.href = this.options.licence.url;
- } else {
- L.DomUtil.add('p', '', credits, L._('No licence has been set'));
- }
- L.DomUtil.create('hr', '', credits);
- title = L.DomUtil.create('h5', '', credits);
- title.textContent = L._('Map background credits');
- var tilelayerCredit = L.DomUtil.create('p', '', credits),
- name = L.DomUtil.create('strong', '', tilelayerCredit),
- attribution = L.DomUtil.create('span', '', tilelayerCredit);
- name.textContent = this.selected_tilelayer.options.name + ' ';
- attribution.innerHTML = this.selected_tilelayer.getAttribution();
- L.DomUtil.create('hr', '', credits);
- var umapCredit = L.DomUtil.create('p', '', credits),
- urls = {
- leaflet: 'http://leafletjs.com',
- django: 'https://www.djangoproject.com',
- umap: 'http://wiki.openstreetmap.org/wiki/UMap'
- };
- umapCredit.innerHTML = L._('Powered by Leaflet and Django, glued by uMap project.', urls);
- var browser = L.DomUtil.create('li', '');
- L.DomUtil.create('i', 'umap-icon-16 umap-list', browser);
- var labelBrowser = L.DomUtil.create('span', '', browser);
- labelBrowser.textContent = labelBrowser.title = L._('Browse data');
- L.DomEvent.on(browser, 'click', this.openBrowser, this);
- var actions = [browser];
- if (this.options.advancedFilterKey) {
- var filter = L.DomUtil.create('li', '');
- L.DomUtil.create('i', 'umap-icon-16 umap-add', filter);
- var labelFilter = L.DomUtil.create('span', '', filter);
- labelFilter.textContent = labelFilter.title = L._('Select data');
- L.DomEvent.on(filter, 'click', this.openFilter, this);
- actions.push(filter)
- }
- this.ui.openPanel({data: {html: container}, actions: actions});
- },
-
- eachDataLayer: function (method, context) {
- for (var i = 0; i < this.datalayers_index.length; i++) {
- method.call(context, this.datalayers_index[i]);
- }
- },
-
- eachDataLayerReverse: function (method, context, filter) {
- for (var i = this.datalayers_index.length - 1; i >= 0; i--) {
- if (filter && !filter.call(context, this.datalayers_index[i])) continue;
- method.call(context, this.datalayers_index[i]);
- }
- },
-
- eachBrowsableDataLayer: function (method, context) {
- this.eachDataLayerReverse(method, context, function (d) { return d.allowBrowse(); });
- },
-
- eachVisibleDataLayer: function (method, context) {
- this.eachDataLayerReverse(method, context, function (d) { return d.isVisible(); });
- },
-
- findDataLayer: function (method, context) {
- for (var i = this.datalayers_index.length - 1; i >= 0; i--) {
- if (method.call(context, this.datalayers_index[i])) return this.datalayers_index[i];
- }
- },
-
- backup: function () {
- this.backupOptions();
- this._datalayers_index_bk = [].concat(this.datalayers_index);
- },
-
- reset: function () {
- if (this.editTools) this.editTools.stopDrawing();
- this.resetOptions();
- this.datalayers_index = [].concat(this._datalayers_index_bk);
- this.dirty_datalayers.slice().forEach(function (datalayer) {
- if (datalayer.isDeleted) datalayer.connectToMap();
- datalayer.reset();
- });
- this.ensurePanesOrder();
- this.dirty_datalayers = [];
- this.updateDatalayersControl();
- this.initTileLayers();
- this.isDirty = false;
- },
-
- checkDirty: function () {
- L.DomUtil.classIf(this._container, 'umap-is-dirty', this.isDirty);
- },
-
- addDirtyDatalayer: function (datalayer) {
- if (this.dirty_datalayers.indexOf(datalayer) === -1) {
- this.dirty_datalayers.push(datalayer);
- this.isDirty = true;
- }
- },
-
- removeDirtyDatalayer: function (datalayer) {
- if (this.dirty_datalayers.indexOf(datalayer) !== -1) {
- this.dirty_datalayers.splice(this.dirty_datalayers.indexOf(datalayer), 1);
- this.checkDirty();
- }
- },
-
- continueSaving: function () {
- if (this.dirty_datalayers.length) this.dirty_datalayers[0].save();
- else this.fire('saved');
- },
-
- editableOptions: [
- 'zoom',
- 'scrollWheelZoom',
- 'scaleControl',
- 'moreControl',
- 'miniMap',
- 'displayPopupFooter',
- 'onLoadPanel',
- 'tilelayersControl',
- 'name',
- 'description',
- 'licence',
- 'tilelayer',
- 'overlay',
- 'limitBounds',
- 'color',
- 'iconClass',
- 'iconUrl',
- 'smoothFactor',
- 'opacity',
- 'weight',
- 'fill',
- 'fillColor',
- 'fillOpacity',
- 'dashArray',
- 'popupShape',
- 'popupTemplate',
- 'popupContentTemplate',
- 'zoomTo',
- 'captionBar',
- 'captionMenus',
- 'slideshow',
- 'sortKey',
- 'labelKey',
- 'filterKey',
- 'advancedFilterKey',
- 'slugKey',
- 'showLabel',
- 'labelDirection',
- 'labelInteractive',
- 'shortCredit',
- 'longCredit',
- 'permanentCredit',
- 'permanentCreditBackground',
- 'zoomControl',
- 'datalayersControl',
- 'searchControl',
- 'locateControl',
- 'fullscreenControl',
- 'editinosmControl',
- 'embedControl',
- 'measureControl',
- 'tilelayersControl',
- 'easing'
- ],
-
- exportOptions: function () {
- var properties = {};
- for (var i = this.editableOptions.length - 1; i >= 0; i--) {
- if (typeof this.options[this.editableOptions[i]] !== 'undefined') {
- properties[this.editableOptions[i]] = this.options[this.editableOptions[i]];
- }
- }
- return properties;
- },
-
- serialize: function () {
- var umapfile = {
- type: 'umap',
- uri: window.location.href,
- properties: this.exportOptions(),
- geometry: this.geometry(),
- layers: []
- };
-
- this.eachDataLayer(function (datalayer) {
- umapfile.layers.push(datalayer.umapGeoJSON());
- });
-
- return JSON.stringify(umapfile, null, 2);
- },
-
- save: function () {
- if (!this.isDirty) return;
- if (this._default_extent) this.updateExtent();
- var geojson = {
- type: 'Feature',
- geometry: this.geometry(),
- properties: this.exportOptions()
- };
- this.backup();
- var formData = new FormData();
- formData.append('name', this.options.name);
- formData.append('center', JSON.stringify(this.geometry()));
- formData.append('settings', JSON.stringify(geojson));
- this.post(this.getSaveUrl(), {
- data: formData,
- context: this,
- callback: function (data) {
- var duration = 3000;
- if (!this.options.umap_id) {
- duration = 100000; // we want a longer message at map creation (TODO UGLY)
- this.options.umap_id = data.id;
- this.permissions.setOptions(data.permissions)
- } else if (!this.permissions.isDirty) {
- // Do not override local changes to permissions,
- // but update in case some other editors changed them in the meantime.
- this.permissions.setOptions(data.permissions)
- }
- // Update URL in case the name has changed.
- if (history && history.pushState) history.pushState({}, this.options.name, data.url);
- else window.location = data.url;
- if (data.info) msg = data.info;
- else msg = L._('Map has been saved!');
- this.once('saved', function () {
- this.isDirty = false;
- this.ui.alert({content: msg, level: 'info', duration: duration});
- });
- this.ui.closePanel();
- this.permissions.save();
- }
- });
- },
-
- 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();
- },
-
- geometry: function() {
- /* Return a GeoJSON geometry Object */
- var latlng = this.latLng(this.options.center || this.getCenter());
- return {
- type: 'Point',
- coordinates: [
- latlng.lng,
- latlng.lat
- ]
- };
- },
-
- defaultDataLayer: function () {
- var datalayer, fallback;
- datalayer = this.lastUsedDataLayer;
- if (datalayer && !datalayer.isRemoteLayer() && datalayer.canBrowse() && datalayer.isVisible()) {
- return datalayer;
- }
- datalayer = this.findDataLayer(function (datalayer) {
- if (!datalayer.isRemoteLayer() && datalayer.canBrowse()) {
- fallback = datalayer;
- if (datalayer.isVisible()) return true;
- }
- });
- if (datalayer) return datalayer;
- if (fallback) {
- // No datalayer visible, let's force one
- this.addLayer(fallback.layer);
- return fallback;
- }
- return this.createDataLayer();
- },
-
- getDataLayerByUmapId: function (umap_id) {
- return this.findDataLayer(function (d) { return d.umap_id == umap_id; });
- },
-
- _editControls: function (container) {
- var UIFields = [];
- for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
- UIFields.push('options.' + this.HIDDABLE_CONTROLS[i] + 'Control');
- }
- UIFields = UIFields.concat([
- 'options.moreControl',
- 'options.scrollWheelZoom',
- 'options.miniMap',
- 'options.scaleControl',
- 'options.onLoadPanel',
- 'options.displayPopupFooter',
- 'options.captionBar',
- 'options.captionMenus'
- ]);
- builder = new L.U.FormBuilder(this, UIFields, {
- callback: function() {
- this.renderControls();
- this.initCaptionBar();
- },
- callbackContext: this
- });
- var controlsOptions = L.DomUtil.createFieldset(container, L._('User interface options'));
- controlsOptions.appendChild(builder.build());
- },
-
- _editShapeProperties: function (container) {
- var shapeOptions = [
- 'options.color',
- 'options.iconClass',
- 'options.iconUrl',
- 'options.opacity',
- 'options.weight',
- 'options.fill',
- 'options.fillColor',
- 'options.fillOpacity'
- ];
-
- builder = new L.U.FormBuilder(this, shapeOptions, {
- callback: function (e) {
- this.eachDataLayer(function (datalayer) {
- datalayer.redraw();
- });
- }
- });
- var defaultShapeProperties = L.DomUtil.createFieldset(container, L._('Default shape properties'));
- defaultShapeProperties.appendChild(builder.build());
- },
-
- _editDefaultProperties: function (container) {
- var optionsFields = [
- 'options.smoothFactor',
- 'options.dashArray',
- 'options.zoomTo',
- ['options.easing', {handler: 'Switch', label: L._('Animated transitions')}],
- 'options.labelKey',
- ['options.sortKey', {handler: 'BlurInput', helpEntries: 'sortKey', placeholder: L._('Default: name'), label: L._('Sort key'), inheritable: true}],
- ['options.filterKey', {handler: 'Input', helpEntries: 'filterKey', placeholder: L._('Default: name'), label: L._('Filter keys'), inheritable: true}],
- ['options.advancedFilterKey', {handler: 'Input', helpEntries: 'advancedFilterKey', placeholder: L._('Example: key1,key2,key3'), label: L._('Advanced filter keys'), inheritable: true}],
- ['options.slugKey', {handler: 'BlurInput', helpEntries: 'slugKey', placeholder: L._('Default: name'), label: L._('Feature identifier key')}]
- ];
-
- builder = new L.U.FormBuilder(this, optionsFields, {
- callback: function (e) {
- this.initCaptionBar();
- this.eachDataLayer(function (datalayer) {
- if (e.helper.field === 'options.sortKey') datalayer.reindex();
- datalayer.redraw();
- });
- }
- });
- var defaultProperties = L.DomUtil.createFieldset(container, L._('Default properties'));
- defaultProperties.appendChild(builder.build());
- },
-
- _editInteractionsProperties: function (container) {
- var popupFields = [
- 'options.popupShape',
- 'options.popupTemplate',
- 'options.popupContentTemplate',
- 'options.showLabel',
- 'options.labelDirection',
- 'options.labelInteractive'
- ];
- builder = new L.U.FormBuilder(this, popupFields, {
- callback: function (e) {
- if (e.helper.field === 'options.popupTemplate' || e.helper.field === 'options.popupContentTemplate' || e.helper.field === 'options.popupShape') return;
- this.eachDataLayer(function (datalayer) {
- datalayer.redraw();
- })
- }
- });
- var popupFieldset = L.DomUtil.createFieldset(container, L._('Default interaction options'));
- popupFieldset.appendChild(builder.build());
- },
-
- _editTilelayer: function (container) {
- if (!L.Util.isObject(this.options.tilelayer)) {
- this.options.tilelayer = {};
- }
- var tilelayerFields = [
- ['options.tilelayer.name', {handler: 'BlurInput', placeholder: L._('display name')}],
- ['options.tilelayer.url_template', {handler: 'BlurInput', helpText: L._('Supported scheme') + ': http://{s}.domain.com/{z}/{x}/{y}.png', placeholder: 'url'}],
- ['options.tilelayer.maxZoom', {handler: 'BlurIntInput', placeholder: L._('max zoom')}],
- ['options.tilelayer.minZoom', {handler: 'BlurIntInput', placeholder: L._('min zoom')}],
- ['options.tilelayer.attribution', {handler: 'BlurInput', placeholder: L._('attribution')}],
- ['options.tilelayer.tms', {handler: 'Switch', label: L._('TMS format')}]
- ];
- var customTilelayer = L.DomUtil.createFieldset(container, L._('Custom background'));
- builder = new L.U.FormBuilder(this, tilelayerFields, {
- callback: this.initTileLayers,
- callbackContext: this
- });
- customTilelayer.appendChild(builder.build());
- },
-
- _editOverlay: function (container) {
- if (!L.Util.isObject(this.options.overlay)) {
- this.options.overlay = {};
- }
- var overlayFields = [
- ['options.overlay.url_template', {handler: 'BlurInput', helpText: L._('Supported scheme') + ': http://{s}.domain.com/{z}/{x}/{y}.png', placeholder: 'url', helpText: L._('Background overlay url')}],
- ['options.overlay.maxZoom', {handler: 'BlurIntInput', placeholder: L._('max zoom')}],
- ['options.overlay.minZoom', {handler: 'BlurIntInput', placeholder: L._('min zoom')}],
- ['options.overlay.attribution', {handler: 'BlurInput', placeholder: L._('attribution')}],
- ['options.overlay.opacity', {handler: 'Range', min: 0, max: 1, step: 'any', placeholder: L._('opacity')}],
- ['options.overlay.tms', {handler: 'Switch', label: L._('TMS format')}]
- ];
- var overlay = L.DomUtil.createFieldset(container, L._('Custom overlay'));
- builder = new L.U.FormBuilder(this, overlayFields, {
- callback: this.initTileLayers,
- callbackContext: this
- });
- overlay.appendChild(builder.build());
- },
-
- _editBounds: function (container) {
- if (!L.Util.isObject(this.options.limitBounds)) {
- this.options.limitBounds = {};
- }
- var limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'));
- var boundsFields = [
- ['options.limitBounds.south', {handler: 'BlurFloatInput', placeholder: L._('max South')}],
- ['options.limitBounds.west', {handler: 'BlurFloatInput', placeholder: L._('max West')}],
- ['options.limitBounds.north', {handler: 'BlurFloatInput', placeholder: L._('max North')}],
- ['options.limitBounds.east', {handler: 'BlurFloatInput', placeholder: L._('max East')}]
- ];
- var boundsBuilder = new L.U.FormBuilder(this, boundsFields, {
- callback: this.handleLimitBounds,
- callbackContext: this
- });
- limitBounds.appendChild(boundsBuilder.build());
- var boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds);
- var setCurrentButton = L.DomUtil.add('a', 'button', boundsButtons, L._('Use current bounds'));
- setCurrentButton.href = '#';
- L.DomEvent.on(setCurrentButton, 'click', function () {
- var 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());
- boundsBuilder.fetchAll();
- this.isDirty = true;
- this.handleLimitBounds();
- }, this);
- var emptyBounds = L.DomUtil.add('a', 'button', boundsButtons, L._('Empty'));
- emptyBounds.href = '#';
- L.DomEvent.on(emptyBounds, 'click', function () {
- this.options.limitBounds.south = null;
- this.options.limitBounds.west = null;
- this.options.limitBounds.north = null;
- this.options.limitBounds.east = null;
- boundsBuilder.fetchAll();
- this.isDirty = true;
- this.handleLimitBounds();
- }, this);
- },
-
- _editSlideshow: function (container) {
- var slideshow = L.DomUtil.createFieldset(container, L._('Slideshow'));
- var slideshowFields = [
- ['options.slideshow.active', {handler: 'Switch', label: L._('Activate slideshow mode')}],
- ['options.slideshow.delay', {handler: 'SlideshowDelay', helpText: L._('Delay between two transitions when in play mode')}],
- ['options.slideshow.easing', {handler: 'Switch', label: L._('Animated transitions'), inheritable: true}],
- ['options.slideshow.autoplay', {handler: 'Switch', label: L._('Autostart when map is loaded')}]
- ];
- var slideshowHandler = function () {
- this.slideshow.setOptions(this.options.slideshow);
- this.renderControls();
- };
- var slideshowBuilder = new L.U.FormBuilder(this, slideshowFields, {
- callback: slideshowHandler,
- callbackContext: this
- });
- slideshow.appendChild(slideshowBuilder.build());
- },
-
- _editCredits: function (container) {
- var credits = L.DomUtil.createFieldset(container, L._('Credits'));
- var creditsFields = [
- ['options.licence', {handler: 'LicenceChooser', label: L._('licence')}],
- ['options.shortCredit', {handler: 'Input', label: L._('Short credits'), helpEntries: ['shortCredit', 'textFormatting']}],
- ['options.longCredit', {handler: 'Textarea', label: L._('Long credits'), helpEntries: ['longCredit', 'textFormatting']}],
- ['options.permanentCredit', {handler: 'Textarea', label: L._('Permanent credits'), helpEntries: ['permanentCredit', 'textFormatting']}],
- ['options.permanentCreditBackground', {handler: 'Switch', label: L._('Permanent credits background')}]
- ];
- var creditsBuilder = new L.U.FormBuilder(this, creditsFields, {
- callback: this.renderControls,
- callbackContext: this
- });
- credits.appendChild(creditsBuilder.build());
- },
-
- _advancedActions: function (container) {
- var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'));
- var advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions);
- var del = L.DomUtil.create('a', 'button umap-delete', advancedButtons);
- del.href = '#';
- del.textContent = L._('Delete');
- L.DomEvent
- .on(del, 'click', L.DomEvent.stop)
- .on(del, 'click', this.del, this);
- var clone = L.DomUtil.create('a', 'button umap-clone', advancedButtons);
- clone.href = '#';
- clone.textContent = L._('Clone');
- clone.title = L._('Clone this map');
- L.DomEvent
- .on(clone, 'click', L.DomEvent.stop)
- .on(clone, 'click', this.clone, this);
- var empty = L.DomUtil.create('a', 'button umap-empty', advancedButtons);
- empty.href = '#';
- empty.textContent = L._('Empty');
- empty.title = L._('Delete all layers');
- L.DomEvent
- .on(empty, 'click', L.DomEvent.stop)
- .on(empty, 'click', this.empty, this);
- var download = L.DomUtil.create('a', 'button umap-download', advancedButtons);
- download.href = '#';
- download.textContent = L._('Download');
- download.title = L._('Open download panel');
- L.DomEvent
- .on(download, 'click', L.DomEvent.stop)
- .on(download, 'click', this.renderShareBox, this);
- },
-
- edit: function () {
- if(!this.editEnabled) return;
- var container = L.DomUtil.create('div','umap-edit-container'),
- metadataFields = [
- 'options.name',
- 'options.description'
- ],
- title = L.DomUtil.create('h4', '', container);
- title.textContent = L._('Edit map properties');
- var builder = new L.U.FormBuilder(this, metadataFields);
- var form = builder.build();
- container.appendChild(form);
- this._editControls(container);
- this._editShapeProperties(container);
- this._editDefaultProperties(container);
- this._editInteractionsProperties(container);
- this._editTilelayer(container);
- this._editOverlay(container);
- this._editBounds(container);
- this._editSlideshow(container);
- this._editCredits(container);
- this._advancedActions(container);
-
- this.ui.openPanel({data: {html: container}, className: 'dark'});
- },
-
- enableEdit: function() {
- L.DomUtil.addClass(document.body, 'umap-edit-enabled');
- this.editEnabled = true;
- this.fire('edit:enabled');
- },
-
- disableEdit: function() {
- if (this.isDirty) return;
- L.DomUtil.removeClass(document.body, 'umap-edit-enabled');
- this.editedFeature = null;
- this.editEnabled = false;
- this.fire('edit:disabled');
- },
-
- getDisplayName: function () {
- return this.options.name || L._('Untitled map');
- },
-
- initCaptionBar: function () {
- var container = L.DomUtil.create('div', 'umap-caption-bar', this._controlContainer),
- name = L.DomUtil.create('h3', '', container);
- L.DomEvent.disableClickPropagation(container);
- this.permissions.addOwnerLink('span', container);
- if (this.options.captionMenus) {
- var about = L.DomUtil.add('a', 'umap-about-link', container, ' — ' + L._('About'));
- about.href = '#';
- L.DomEvent.on(about, 'click', this.displayCaption, this);
- var browser = L.DomUtil.add('a', 'umap-open-browser-link', container, ' | ' + L._('Browse data'));
- browser.href = '#';
- L.DomEvent.on(browser, 'click', L.DomEvent.stop)
- .on(browser, 'click', this.openBrowser, this);
- if (this.options.advancedFilterKey) {
- var filter = L.DomUtil.add('a', 'umap-open-filter-link', container, ' | ' + L._('Select data'));
- filter.href = '#';
- L.DomEvent.on(filter, 'click', L.DomEvent.stop)
- .on(filter, 'click', this.openFilter, this);
- }
- }
- var setName = function () {
- name.textContent = this.getDisplayName();
- };
- L.bind(setName, this)();
- this.on('postsync', L.bind(setName, this));
- this.onceDatalayersLoaded(function () {
- this.slideshow.renderToolbox(container);
- });
- },
-
- initEditBar: function () {
- var container = L.DomUtil.create('div', 'umap-main-edit-toolbox with-transition dark', this._controlContainer),
- title = L.DomUtil.add('h3', '', container, L._('Editing') + ' '),
- name = L.DomUtil.create('a', 'umap-click-to-edit', title),
- setName = function () {
- name.textContent = this.getDisplayName();
- };
- L.bind(setName, this)();
- L.DomEvent.on(name, 'click', this.edit, this);
- this.on('postsync', L.bind(setName, this));
- this.help.button(title, 'edit');
- var save = L.DomUtil.create('a', 'leaflet-control-edit-save button', container);
- save.href = '#';
- save.title = L._('Save current edits') + ' (Ctrl+S)';
- save.textContent = L._('Save');
- var cancel = L.DomUtil.create('a', 'leaflet-control-edit-cancel button', container);
- cancel.href = '#';
- cancel.title = L._('Cancel edits');
- cancel.textContent = L._('Cancel');
- var disable = L.DomUtil.create('a', 'leaflet-control-edit-disable', container);
- disable.href = '#';
- disable.title = disable.textContent = L._('Disable editing');
-
-
- L.DomEvent
- .addListener(disable, 'click', L.DomEvent.stop)
- .addListener(disable, 'click', function (e) {
- this.disableEdit(e);
- this.ui.closePanel();
- }, this);
-
- L.DomEvent
- .addListener(save, 'click', L.DomEvent.stop)
- .addListener(save, 'click', this.save, this);
-
- L.DomEvent
- .addListener(cancel, 'click', L.DomEvent.stop)
- .addListener(cancel, 'click', this.askForReset, this);
- },
-
- askForReset: function (e) {
- if (!confirm(L._('Are you sure you want to cancel your changes?'))) return;
- this.reset();
- this.disableEdit(e);
- this.ui.closePanel();
- },
-
- startMarker: function () {
- return this.editTools.startMarker();
- },
-
- startPolyline: function () {
- return this.editTools.startPolyline();
- },
-
- startPolygon: function () {
- return this.editTools.startPolygon();
- },
-
- del: function () {
- if (confirm(L._('Are you sure you want to delete this map?'))) {
- var url = L.Util.template(this.options.urls.map_delete, {'map_id': this.options.umap_id});
- this.post(url);
- }
- },
-
- clone: function () {
- if (confirm(L._('Are you sure you want to clone this map and all its datalayers?'))) {
- var url = L.Util.template(this.options.urls.map_clone, {'map_id': this.options.umap_id});
- this.post(url);
- }
- },
-
- empty: function () {
- this.eachDataLayerReverse(function (datalayer) {
- datalayer._delete();
- });
- },
-
- initLoader: function () {
- this.loader = new L.Control.Loading();
- this.loader.onAdd(this);
- },
-
- post: function (url, options) {
- options = options || {};
- options.listener = this;
- this.xhr.post(url, options);
- },
-
- get: function (url, options) {
- options = options || {};
- options.listener = this;
- this.xhr.get(url, options);
- },
-
- ajax: function (options) {
- options.listener = this;
- this.xhr._ajax(options);
- },
-
- initContextMenu: function () {
- this.contextmenu = new L.U.ContextMenu(this);
- this.contextmenu.enable();
- },
-
- setContextMenuItems: function (e) {
- var items = [];
- if (this._zoom !== this.getMaxZoom()) {
- items.push({
- text: L._('Zoom in'),
- callback: function () {this.zoomIn();}
- });
- }
- if (this._zoom !== this.getMinZoom()) {
- items.push({
- text: L._('Zoom out'),
- callback: function () {this.zoomOut();}
- });
- }
- if (e && e.relatedTarget) {
- if (e.relatedTarget.getContextMenuItems) {
- items = items.concat(e.relatedTarget.getContextMenuItems(e));
- }
- }
- if (this.options.allowEdit) {
- items.push('-');
- if (this.editEnabled) {
- if (!this.isDirty) {
- items.push({
- text: L._('Stop editing') + ' (Ctrl+E)',
- callback: this.disableEdit
- });
- }
- if (this.options.enableMarkerDraw) {
- items.push(
- {
- text: L._('Draw a marker') + ' (Ctrl+M)',
- callback: this.startMarker,
- context: this
- });
- }
- if (this.options.enablePolylineDraw) {
- items.push(
- {
- text: L._('Draw a polygon') + ' (Ctrl+P)',
- callback: this.startPolygon,
- context: this
- });
- }
- if (this.options.enablePolygonDraw) {
- items.push(
- {
- text: L._('Draw a line') + ' (Ctrl+L)',
- callback: this.startPolyline,
- context: this
- });
- }
- items.push('-');
- items.push({
- text: L._('Help'),
- callback: function () { this.help.show('edit');}
- });
- } else {
- items.push({
- text: L._('Start editing') + ' (Ctrl+E)',
- callback: this.enableEdit
- });
- }
- }
- items.push('-', {
- text: L._('Browse data'),
- callback: this.openBrowser
- });
- if (this.options.advancedFilterKey) {
- items.push({
- text: L._('Select data'),
- callback: this.openFilter
- })
- }
- items.push(
- {
- text: L._('About'),
- callback: this.displayCaption
- },
- {
- text: L._('Search location'),
- callback: this.search
- }
- );
- if (this.options.urls.routing) {
- items.push('-',
- {
- text: L._('Directions from here'),
- callback: this.openExternalRouting
- }
- );
- }
- this.options.contextmenuItems = items;
- },
-
- openExternalRouting: function (e) {
- var url = this.options.urls.routing;
- if (url) {
- var params = {
- lat: e.latlng.lat,
- lng: e.latlng.lng,
- locale: L.locale,
- zoom: this.getZoom()
- };
- window.open(L.Util.template(url, params));
- }
- return;
- },
-
- getMap: function () {
- return this;
- },
-
- getGeoContext: function () {
- var context = {
- bbox: this.getBounds().toBBoxString(),
- north: this.getBounds().getNorthEast().lat,
- east: this.getBounds().getNorthEast().lng,
- south: this.getBounds().getSouthWest().lat,
- west: this.getBounds().getSouthWest().lng,
- lat: this.getCenter().lat,
- lng: this.getCenter().lng,
- zoom: this.getZoom()
- };
- context.left = context.west;
- context.bottom = context.south;
- context.right = context.east;
- context.top = context.north;
- return context;
- },
-
- localizeUrl: function (url) {
- return L.Util.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: encodeURIComponent(url), ttl: ttl});
- }
- return url;
- },
-
- closeInplaceToolbar: function () {
- var toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id];
- if (toolbar) toolbar.remove();
- },
-
- search: function () {
- if (this._controls.search) this._controls.search.openPanel(this);
- },
-
- getFilterKeys: function () {
- return (this.options.filterKey || this.options.sortKey || 'name').split(',');
- },
-
- getAdvancedFilterKeys: function () {
- return (this.options.advancedFilterKey || '').split(",");
+ HIDDABLE_CONTROLS: [
+ 'zoom',
+ 'search',
+ 'fullscreen',
+ 'embed',
+ 'locate',
+ 'measure',
+ 'tilelayers',
+ 'editinosm',
+ 'datalayers',
+ ],
+
+ initialize: function (el, geojson) {
+ if (geojson.properties && geojson.properties.locale)
+ L.setLocale(geojson.properties.locale)
+
+ // Don't let default autocreation of controls
+ var zoomControl =
+ typeof geojson.properties.zoomControl !== 'undefined'
+ ? geojson.properties.zoomControl
+ : true
+ geojson.properties.zoomControl = false
+ var fullscreenControl =
+ typeof geojson.properties.fullscreenControl !== 'undefined'
+ ? geojson.properties.fullscreenControl
+ : true
+ geojson.properties.fullscreenControl = false
+ L.Util.setBooleanFromQueryString(geojson.properties, 'scrollWheelZoom')
+ L.Map.prototype.initialize.call(this, el, geojson.properties)
+
+ this.ui = new L.U.UI(this._container)
+ this.xhr = new L.U.Xhr(this.ui)
+ this.xhr.on('dataloding', function (e) {
+ this.fire('dataloding', e)
+ })
+ this.xhr.on('datalaod', function (e) {
+ this.fire('datalaod', e)
+ })
+
+ this.initLoader()
+ this.name = this.options.name
+ this.description = this.options.description
+ this.demoTileInfos = this.options.demoTileInfos
+ if (geojson.geometry) this.options.center = geojson.geometry
+ this.options.zoomControl = zoomControl
+ this.options.fullscreenControl = fullscreenControl
+ L.Util.setBooleanFromQueryString(this.options, 'moreControl')
+ L.Util.setBooleanFromQueryString(this.options, 'scaleControl')
+ L.Util.setBooleanFromQueryString(this.options, 'miniMap')
+ L.Util.setBooleanFromQueryString(this.options, 'allowEdit')
+ L.Util.setBooleanFromQueryString(this.options, 'displayDataBrowserOnLoad')
+ L.Util.setBooleanFromQueryString(this.options, 'displayCaptionOnLoad')
+ L.Util.setBooleanFromQueryString(this.options, 'captionBar')
+ L.Util.setBooleanFromQueryString(this.options, 'captionMenus')
+ for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
+ L.Util.setNullableBooleanFromQueryString(
+ this.options,
+ this.HIDDABLE_CONTROLS[i] + 'Control'
+ )
+ }
+ this.datalayersOnLoad = L.Util.queryString('datalayers')
+ this.options.onLoadPanel = L.Util.queryString(
+ 'onLoadPanel',
+ this.options.onLoadPanel
+ )
+ if (this.datalayersOnLoad)
+ this.datalayersOnLoad = this.datalayersOnLoad.toString().split(',')
+
+ if (L.Browser.ielt9) this.options.allowEdit = false // TODO include ie9
+
+ var editedFeature = null,
+ self = this
+ try {
+ Object.defineProperty(this, 'editedFeature', {
+ get: function () {
+ return editedFeature
+ },
+ set: function (feature) {
+ if (editedFeature && editedFeature !== feature) {
+ editedFeature.endEdit()
+ }
+ editedFeature = feature
+ self.fire('seteditedfeature')
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
}
-});
+ if (this.options.hash) this.addHash()
+ this.initCenter()
+ this.handleLimitBounds()
+
+ this.initTileLayers(this.options.tilelayers)
+
+ // Global storage for retrieving datalayers and features
+ this.datalayers = {}
+ this.datalayers_index = []
+ this.dirty_datalayers = []
+ this.features_index = {}
+
+ // Retrocompat
+ if (
+ this.options.slideshow &&
+ this.options.slideshow.delay &&
+ this.options.slideshow.active === undefined
+ )
+ this.options.slideshow.active = true
+
+ this.initControls()
+
+ // create datalayers
+ this.initDatalayers()
+
+ if (this.options.displayCaptionOnLoad) {
+ // Retrocompat
+ if (!this.options.onLoadPanel) {
+ this.options.onLoadPanel = 'caption'
+ }
+ delete this.options.displayCaptionOnLoad
+ }
+ if (this.options.displayDataBrowserOnLoad) {
+ // Retrocompat
+ if (!this.options.onLoadPanel) {
+ this.options.onLoadPanel = 'databrowser'
+ }
+ delete this.options.displayDataBrowserOnLoad
+ }
+
+ this.ui.on(
+ 'panel:closed',
+ function () {
+ this.invalidateSize({ pan: false })
+ },
+ this
+ )
+
+ var isDirty = false // global status
+ try {
+ Object.defineProperty(this, 'isDirty', {
+ get: function () {
+ return isDirty || this.dirty_datalayers.length
+ },
+ set: function (status) {
+ if (!isDirty && status) self.fire('isdirty')
+ isDirty = status
+ self.checkDirty()
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
+ }
+ this.on(
+ 'baselayerchange',
+ function (e) {
+ if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
+ },
+ this
+ )
+
+ // Creation mode
+ if (!this.options.umap_id) {
+ this.isDirty = true
+ this._default_extent = true
+ this.options.name = L._('Untitled map')
+ this.options.allowEdit = true
+ var datalayer = this.createDataLayer()
+ datalayer.connectToMap()
+ this.enableEdit()
+ var dataUrl = L.Util.queryString('dataUrl', null),
+ dataFormat = L.Util.queryString('dataFormat', 'geojson')
+ if (dataUrl) {
+ dataUrl = decodeURIComponent(dataUrl)
+ dataUrl = this.localizeUrl(dataUrl)
+ dataUrl = this.proxyUrl(dataUrl)
+ datalayer.importFromUrl(dataUrl, dataFormat)
+ }
+ }
+
+ this.help = new L.U.Help(this)
+ this.slideshow = new L.U.Slideshow(this, this.options.slideshow)
+ this.permissions = new L.U.MapPermissions(this)
+ this.initCaptionBar()
+ if (this.options.allowEdit) {
+ this.editTools = new L.U.Editable(this)
+ this.ui.on(
+ 'panel:closed panel:open',
+ function () {
+ this.editedFeature = null
+ },
+ this
+ )
+ this.initEditBar()
+ }
+ this.initShortcuts()
+ this.onceDatalayersLoaded(function () {
+ if (this.options.onLoadPanel === 'databrowser') this.openBrowser()
+ else if (this.options.onLoadPanel === 'caption') this.displayCaption()
+ else if (this.options.onLoadPanel === 'datafilters') this.openFilter()
+ })
+ this.onceDataLoaded(function () {
+ const slug = L.Util.queryString('feature')
+ if (slug && this.features_index[slug]) this.features_index[slug].view()
+ })
+
+ window.onbeforeunload = function (e) {
+ var msg = L._('You have unsaved changes.')
+ if (self.isDirty) {
+ e.returnValue = msg
+ return msg
+ }
+ }
+ this.backup()
+ this.initContextMenu()
+ this.on('click contextmenu.show', this.closeInplaceToolbar)
+ },
+
+ initControls: function () {
+ this.helpMenuActions = {}
+ this._controls = {}
+
+ if (this.options.allowEdit && !this.options.noControl) {
+ new L.U.EditControl(this).addTo(this)
+
+ new L.U.DrawToolbar({ map: this }).addTo(this)
+
+ var editActions = [
+ L.U.ImportAction,
+ L.U.EditPropertiesAction,
+ L.U.ChangeTileLayerAction,
+ L.U.ManageDatalayersAction,
+ L.U.UpdateExtentAction,
+ L.U.UpdatePermsAction,
+ ]
+ new L.U.SettingsToolbar({ actions: editActions }).addTo(this)
+ }
+ this._controls.zoom = new L.Control.Zoom({
+ zoomInTitle: L._('Zoom in'),
+ zoomOutTitle: L._('Zoom out'),
+ })
+ this._controls.datalayers = new L.U.DataLayersControl(this)
+ this._controls.locate = L.control.locate({
+ strings: {
+ title: L._('Center map on your location'),
+ },
+ showPopup: false,
+ // We style this control in our own CSS for consistency with other controls,
+ // but the control breaks if we don't specify a class here, so a fake class
+ // will do.
+ icon: 'umap-fake-class',
+ iconLoading: 'umap-fake-class',
+ flyTo: this.options.easing,
+ })
+ this._controls.fullscreen = new L.Control.Fullscreen({
+ title: { false: L._('View Fullscreen'), true: L._('Exit Fullscreen') },
+ })
+ this._controls.search = new L.U.SearchControl()
+ this._controls.embed = new L.Control.Embed(this, this.options.embedOptions)
+ this._controls.tilelayers = new L.U.TileLayerControl(this)
+ this._controls.editinosm = new L.Control.EditInOSM({
+ position: 'topleft',
+ widgetOptions: {
+ helpText: L._(
+ 'Open this map extent in a map editor to provide more accurate data to OpenStreetMap'
+ ),
+ },
+ })
+ this._controls.measure = new L.MeasureControl().initHandler(this)
+ this._controls.more = new L.U.MoreControls()
+ this._controls.scale = L.control.scale()
+ this._controls.permanentCredit = new L.U.PermanentCreditsControl(this)
+ if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable()
+ else this.scrollWheelZoom.disable()
+ this.renderControls()
+ },
+
+ renderControls: function () {
+ L.DomUtil.classIf(
+ document.body,
+ 'umap-caption-bar-enabled',
+ this.options.captionBar ||
+ (this.options.slideshow && this.options.slideshow.active)
+ )
+ L.DomUtil.classIf(
+ document.body,
+ 'umap-slideshow-enabled',
+ this.options.slideshow && this.options.slideshow.active
+ )
+ for (var i in this._controls) {
+ this.removeControl(this._controls[i])
+ }
+ if (this.options.noControl) return
+
+ this._controls.attribution = new L.U.AttributionControl().addTo(this)
+ if (this.options.miniMap && !this.options.noControl) {
+ this.whenReady(function () {
+ if (this.selected_tilelayer) {
+ this._controls.miniMap = new L.Control.MiniMap(this.selected_tilelayer).addTo(
+ this
+ )
+ this._controls.miniMap._miniMap.invalidateSize()
+ }
+ })
+ }
+ var name, status, control
+ for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
+ name = this.HIDDABLE_CONTROLS[i]
+ status = this.options[name + 'Control']
+ if (status === false) continue
+ control = this._controls[name]
+ control.addTo(this)
+ if (status === undefined || status === null)
+ L.DomUtil.addClass(control._container, 'display-on-more')
+ else L.DomUtil.removeClass(control._container, 'display-on-more')
+ }
+ if (this.options.permanentCredit) this._controls.permanentCredit.addTo(this)
+ if (this.options.moreControl) this._controls.more.addTo(this)
+ if (this.options.scaleControl) this._controls.scale.addTo(this)
+ },
+
+ initDatalayers: function () {
+ var toload = (dataToload = seen = this.options.datalayers.length),
+ self = this,
+ datalayer
+ var loaded = function () {
+ self.datalayersLoaded = true
+ self.fire('datalayersloaded')
+ }
+ var decrementToLoad = function () {
+ toload--
+ if (toload === 0) loaded()
+ }
+ var dataLoaded = function () {
+ self.dataLoaded = true
+ self.fire('dataloaded')
+ }
+ var decrementDataToLoad = function () {
+ dataToload--
+ if (dataToload === 0) dataLoaded()
+ }
+ for (var j = 0; j < this.options.datalayers.length; j++) {
+ datalayer = this.createDataLayer(this.options.datalayers[j])
+ if (datalayer.displayedOnLoad()) datalayer.onceLoaded(decrementToLoad)
+ else decrementToLoad()
+ if (datalayer.displayedOnLoad()) datalayer.onceDataLoaded(decrementDataToLoad)
+ else decrementDataToLoad()
+ }
+ if (seen === 0) loaded() && dataLoaded() // no datalayer
+ },
+
+ indexDatalayers: function () {
+ var panes = this.getPane('overlayPane'),
+ pane
+ this.datalayers_index = []
+ for (var i = 0; i < panes.children.length; i++) {
+ pane = panes.children[i]
+ if (!pane.dataset || !pane.dataset.id) continue
+ this.datalayers_index.push(this.datalayers[pane.dataset.id])
+ }
+ this.updateDatalayersControl()
+ },
+
+ ensurePanesOrder: function () {
+ this.eachDataLayer(function (datalayer) {
+ datalayer.bringToTop()
+ })
+ },
+
+ onceDatalayersLoaded: function (callback, context) {
+ // Once datalayers **metadata** have been loaded
+ if (this.datalayersLoaded) {
+ callback.call(context || this, this)
+ } else {
+ this.once('datalayersloaded', callback, context)
+ }
+ return this
+ },
+
+ onceDataLoaded: function (callback, context) {
+ // Once datalayers **data** have been loaded
+ if (this.dataLoaded) {
+ callback.call(context || this, this)
+ } else {
+ this.once('dataloaded', callback, context)
+ }
+ return this
+ },
+
+ updateDatalayersControl: function () {
+ if (this._controls.datalayers) this._controls.datalayers.update()
+ },
+
+ backupOptions: function () {
+ this._backupOptions = L.extend({}, this.options)
+ this._backupOptions.tilelayer = L.extend({}, this.options.tilelayer)
+ this._backupOptions.limitBounds = L.extend({}, this.options.limitBounds)
+ this._backupOptions.permissions = L.extend({}, this.permissions.options)
+ },
+
+ resetOptions: function () {
+ this.options = L.extend({}, this._backupOptions)
+ this.options.tilelayer = L.extend({}, this._backupOptions.tilelayer)
+ this.permissions.options = L.extend({}, this._backupOptions.permissions)
+ },
+
+ initShortcuts: function () {
+ var globalShortcuts = function (e) {
+ var key = e.keyCode,
+ modifierKey = e.ctrlKey || e.metaKey
+
+ /* Generic shortcuts */
+ if (key === L.U.Keys.F && modifierKey) {
+ L.DomEvent.stop(e)
+ this.search()
+ } else if (e.keyCode === L.U.Keys.ESC) {
+ if (this.help.visible()) this.help.hide()
+ else this.ui.closePanel()
+ }
+
+ if (!this.options.allowEdit) return
+
+ /* Edit mode only shortcuts */
+ if (key === L.U.Keys.E && modifierKey && !this.editEnabled) {
+ L.DomEvent.stop(e)
+ this.enableEdit()
+ } else if (
+ key === L.U.Keys.E &&
+ modifierKey &&
+ this.editEnabled &&
+ !this.isDirty
+ ) {
+ L.DomEvent.stop(e)
+ this.disableEdit()
+ this.ui.closePanel()
+ }
+ if (key === L.U.Keys.S && modifierKey) {
+ L.DomEvent.stop(e)
+ if (this.isDirty) {
+ this.save()
+ }
+ }
+ if (key === L.U.Keys.Z && modifierKey && this.isDirty) {
+ L.DomEvent.stop(e)
+ this.askForReset()
+ }
+ if (key === L.U.Keys.M && modifierKey && this.editEnabled) {
+ L.DomEvent.stop(e)
+ this.editTools.startMarker()
+ }
+ if (key === L.U.Keys.P && modifierKey && this.editEnabled) {
+ L.DomEvent.stop(e)
+ this.editTools.startPolygon()
+ }
+ if (key === L.U.Keys.L && modifierKey && this.editEnabled) {
+ L.DomEvent.stop(e)
+ this.editTools.startPolyline()
+ }
+ if (key === L.U.Keys.I && modifierKey && this.editEnabled) {
+ L.DomEvent.stop(e)
+ this.importPanel()
+ }
+ if (key === L.U.Keys.H && modifierKey && this.editEnabled) {
+ L.DomEvent.stop(e)
+ this.help.show('edit')
+ }
+ if (e.keyCode === L.U.Keys.ESC) {
+ if (this.editEnabled) this.editTools.stopDrawing()
+ if (this.measureTools.enabled()) this.measureTools.stopDrawing()
+ }
+ }
+ L.DomEvent.addListener(document, 'keydown', globalShortcuts, this)
+ },
+
+ initTileLayers: function () {
+ this.tilelayers = []
+ for (var i in this.options.tilelayers) {
+ if (this.options.tilelayers.hasOwnProperty(i)) {
+ this.tilelayers.push(this.createTileLayer(this.options.tilelayers[i]))
+ if (
+ this.options.tilelayer &&
+ this.options.tilelayer.url_template ===
+ this.options.tilelayers[i].url_template
+ ) {
+ // Keep control over the displayed attribution for non custom tilelayers
+ this.options.tilelayer.attribution = this.options.tilelayers[i].attribution
+ }
+ }
+ }
+ if (
+ this.options.tilelayer &&
+ this.options.tilelayer.url_template &&
+ this.options.tilelayer.attribution
+ ) {
+ this.customTilelayer = this.createTileLayer(this.options.tilelayer)
+ this.selectTileLayer(this.customTilelayer)
+ } else {
+ this.selectTileLayer(this.tilelayers[0])
+ }
+ },
+
+ createTileLayer: function (tilelayer) {
+ return new L.TileLayer(tilelayer.url_template, tilelayer)
+ },
+
+ selectTileLayer: function (tilelayer) {
+ if (tilelayer === this.selected_tilelayer) {
+ return
+ }
+ try {
+ this.addLayer(tilelayer)
+ this.fire('baselayerchange', { layer: tilelayer })
+ if (this.selected_tilelayer) {
+ this.removeLayer(this.selected_tilelayer)
+ }
+ this.selected_tilelayer = tilelayer
+ if (
+ !isNaN(this.selected_tilelayer.options.minZoom) &&
+ this.getZoom() < this.selected_tilelayer.options.minZoom
+ ) {
+ this.setZoom(this.selected_tilelayer.options.minZoom)
+ }
+ if (
+ !isNaN(this.selected_tilelayer.options.maxZoom) &&
+ this.getZoom() > this.selected_tilelayer.options.maxZoom
+ ) {
+ this.setZoom(this.selected_tilelayer.options.maxZoom)
+ }
+ } catch (e) {
+ this.removeLayer(tilelayer)
+ this.ui.alert({
+ content: L._('Error in the tilelayer URL') + ': ' + tilelayer._url,
+ level: 'error',
+ })
+ // Users can put tilelayer URLs by hand, and if they add wrong {variable},
+ // Leaflet throw an error, and then the map is no more editable
+ }
+ this.setOverlay()
+ },
+
+ eachTileLayer: function (method, context) {
+ var urls = []
+ for (var i in this.tilelayers) {
+ if (this.tilelayers.hasOwnProperty(i)) {
+ method.call(context, this.tilelayers[i])
+ urls.push(this.tilelayers[i]._url)
+ }
+ }
+ if (
+ this.customTilelayer &&
+ Array.prototype.indexOf &&
+ urls.indexOf(this.customTilelayer._url) === -1
+ ) {
+ method.call(context || this, this.customTilelayer)
+ }
+ },
+
+ setOverlay: function () {
+ if (!this.options.overlay || !this.options.overlay.url_template) return
+ var overlay = this.createTileLayer(this.options.overlay)
+ try {
+ this.addLayer(overlay)
+ if (this.overlay) this.removeLayer(this.overlay)
+ this.overlay = overlay
+ } catch (e) {
+ this.removeLayer(overlay)
+ console.error(e)
+ this.ui.alert({
+ content: L._('Error in the overlay URL') + ': ' + overlay._url,
+ level: 'error',
+ })
+ }
+ },
+
+ initCenter: function () {
+ if (this.options.hash && this._hash.parseHash(location.hash)) {
+ // FIXME An invalid hash will cause the load to fail
+ this._hash.update()
+ } else if (this.options.locate && this.options.locate.setView) {
+ // Prevent from making two setViews at init
+ // which is not very fluid...
+ this.locate(this.options.locate)
+ } else {
+ this.options.center = this.latLng(this.options.center)
+ this.setView(this.options.center, this.options.zoom)
+ }
+ },
+
+ latLng: function (a, b, c) {
+ // manage geojson case and call original method
+ if (!(a instanceof L.LatLng) && a.coordinates) {
+ // Guess it's a geojson
+ a = [a.coordinates[1], a.coordinates[0]]
+ }
+ return L.latLng(a, b, c)
+ },
+
+ handleLimitBounds: function () {
+ var south = parseFloat(this.options.limitBounds.south),
+ west = parseFloat(this.options.limitBounds.west),
+ north = parseFloat(this.options.limitBounds.north),
+ east = parseFloat(this.options.limitBounds.east)
+ if (!isNaN(south) && !isNaN(west) && !isNaN(north) && !isNaN(east)) {
+ var bounds = L.latLngBounds([
+ [south, west],
+ [north, east],
+ ])
+ this.options.minZoom = this.getBoundsZoom(bounds, false)
+ try {
+ this.setMaxBounds(bounds)
+ } catch (e) {
+ // Unusable bounds, like -2 -2 -2 -2?
+ console.error('Error limiting bounds', e)
+ }
+ } else {
+ this.options.minZoom = 0
+ this.setMaxBounds()
+ }
+ },
+
+ setMaxBounds: function (bounds) {
+ // Hack. Remove me when fix is released:
+ // https://github.com/Leaflet/Leaflet/pull/4494
+ bounds = L.latLngBounds(bounds)
+
+ if (!bounds.isValid()) {
+ this.options.maxBounds = null
+ return this.off('moveend', this._panInsideMaxBounds)
+ }
+ return L.Map.prototype.setMaxBounds.call(this, bounds)
+ },
+
+ createDataLayer: function (datalayer) {
+ datalayer = datalayer || {
+ name: L._('Layer') + ' ' + (this.datalayers_index.length + 1),
+ }
+ return new L.U.DataLayer(this, datalayer)
+ },
+
+ getDefaultOption: function (option) {
+ return this.options['default_' + option]
+ },
+
+ getOption: function (option) {
+ if (L.Util.usableOption(this.options, option)) return this.options[option]
+ return this.getDefaultOption(option)
+ },
+
+ updateExtent: function () {
+ this.options.center = this.getCenter()
+ this.options.zoom = this.getZoom()
+ this.isDirty = true
+ this._default_extent = false
+ this.ui.alert({ content: L._('The zoom and center have been set.'), level: 'info' })
+ },
+
+ updateTileLayers: function () {
+ var self = this,
+ callback = function (tilelayer) {
+ self.options.tilelayer = tilelayer.toJSON()
+ self.isDirty = true
+ }
+ if (this._controls.tilelayers)
+ this._controls.tilelayers.openSwitcher({ callback: callback, className: 'dark' })
+ },
+
+ manageDatalayers: function () {
+ if (this._controls.datalayers) this._controls.datalayers.openPanel()
+ },
+
+ renderShareBox: function () {
+ var container = L.DomUtil.create('div', 'umap-share'),
+ embedTitle = L.DomUtil.add('h4', '', container, L._('Embed the map')),
+ iframe = L.DomUtil.create('textarea', 'umap-share-iframe', container),
+ option
+ var UIFields = [
+ ['dimensions.width', { handler: 'Input', label: L._('width') }],
+ ['dimensions.height', { handler: 'Input', label: L._('height') }],
+ [
+ 'options.includeFullScreenLink',
+ { handler: 'Switch', label: L._('Include full screen link?') },
+ ],
+ [
+ 'options.currentView',
+ { handler: 'Switch', label: L._('Current view instead of default map view?') },
+ ],
+ [
+ 'options.keepCurrentDatalayers',
+ { handler: 'Switch', label: L._('Keep current visible layers') },
+ ],
+ [
+ 'options.viewCurrentFeature',
+ { handler: 'Switch', label: L._('Open current feature on load') },
+ ],
+ 'queryString.moreControl',
+ 'queryString.scrollWheelZoom',
+ 'queryString.miniMap',
+ 'queryString.scaleControl',
+ 'queryString.onLoadPanel',
+ 'queryString.captionBar',
+ 'queryString.captionMenus',
+ ]
+ for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
+ UIFields.push('queryString.' + this.HIDDABLE_CONTROLS[i] + 'Control')
+ }
+ var iframeExporter = new L.U.IframeExporter(this)
+ var buildIframeCode = function () {
+ iframe.innerHTML = iframeExporter.build()
+ }
+ buildIframeCode()
+ var builder = new L.U.FormBuilder(iframeExporter, UIFields, {
+ callback: buildIframeCode,
+ })
+ var iframeOptions = L.DomUtil.createFieldset(
+ container,
+ L._('Iframe export options')
+ )
+ iframeOptions.appendChild(builder.build())
+ if (this.options.shortUrl) {
+ L.DomUtil.create('hr', '', container)
+ L.DomUtil.add('h4', '', container, L._('Short URL'))
+ var shortUrl = L.DomUtil.create('input', 'umap-short-url', container)
+ shortUrl.type = 'text'
+ shortUrl.value = this.options.shortUrl
+ }
+ L.DomUtil.create('hr', '', container)
+ L.DomUtil.add('h4', '', container, L._('Download data'))
+ var typeInput = L.DomUtil.create('select', '', container)
+ typeInput.name = 'format'
+ var exportCaveat = L.DomUtil.add(
+ 'small',
+ 'help-text',
+ container,
+ L._('Only visible features will be downloaded.')
+ )
+ exportCaveat.id = 'export_caveat_text'
+ var toggleCaveat = function () {
+ if (typeInput.value === 'umap') exportCaveat.style.display = 'none'
+ else exportCaveat.style.display = 'inherit'
+ }
+ L.DomEvent.on(typeInput, 'change', toggleCaveat)
+ var types = {
+ geojson: {
+ formatter: function (map) {
+ return JSON.stringify(map.toGeoJSON(), null, 2)
+ },
+ ext: '.geojson',
+ filetype: 'application/json',
+ },
+ gpx: {
+ formatter: function (map) {
+ return togpx(map.toGeoJSON())
+ },
+ ext: '.gpx',
+ filetype: 'application/xml',
+ },
+ kml: {
+ formatter: function (map) {
+ return tokml(map.toGeoJSON())
+ },
+ ext: '.kml',
+ filetype: 'application/vnd.google-earth.kml+xml',
+ },
+ umap: {
+ name: L._('Full map data'),
+ formatter: function (map) {
+ return map.serialize()
+ },
+ ext: '.umap',
+ filetype: 'application/json',
+ selected: true,
+ },
+ }
+ for (var key in types) {
+ if (types.hasOwnProperty(key)) {
+ option = L.DomUtil.create('option', '', typeInput)
+ option.value = key
+ option.textContent = types[key].name || key
+ if (types[key].selected) option.selected = true
+ }
+ }
+ toggleCaveat()
+ var download = L.DomUtil.create('a', 'button', container)
+ download.textContent = L._('Download data')
+ L.DomEvent.on(
+ download,
+ 'click',
+ function () {
+ var type = types[typeInput.value],
+ content = type.formatter(this),
+ name = this.options.name || 'data'
+ name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
+ download.download = name + type.ext
+ window.URL = window.URL || window.webkitURL
+ var blob = new Blob([content], { type: type.filetype })
+ download.href = window.URL.createObjectURL(blob)
+ },
+ this
+ )
+ this.ui.openPanel({ data: { html: container } })
+ },
+
+ toGeoJSON: function () {
+ var features = []
+ this.eachDataLayer(function (datalayer) {
+ if (datalayer.isVisible()) {
+ features = features.concat(datalayer.featuresToGeoJSON())
+ }
+ })
+ var geojson = {
+ type: 'FeatureCollection',
+ features: features,
+ }
+ return geojson
+ },
+
+ importPanel: function () {
+ var container = L.DomUtil.create('div', 'umap-upload'),
+ title = L.DomUtil.create('h4', '', container),
+ presetBox = L.DomUtil.create('div', 'formbox', container),
+ presetSelect = L.DomUtil.create('select', '', presetBox),
+ fileBox = L.DomUtil.create('div', 'formbox', container),
+ fileInput = L.DomUtil.create('input', '', fileBox),
+ urlInput = L.DomUtil.create('input', '', container),
+ rawInput = L.DomUtil.create('textarea', '', container),
+ typeLabel = L.DomUtil.create('label', '', container),
+ layerLabel = L.DomUtil.create('label', '', container),
+ clearLabel = L.DomUtil.create('label', '', container),
+ submitInput = L.DomUtil.create('input', '', container),
+ map = this,
+ option,
+ types = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
+ title.textContent = L._('Import data')
+ fileInput.type = 'file'
+ fileInput.multiple = 'multiple'
+ submitInput.type = 'button'
+ submitInput.value = L._('Import')
+ submitInput.className = 'button'
+ typeLabel.textContent = L._('Choose the format of the data to import')
+ this.help.button(typeLabel, 'importFormats')
+ var typeInput = L.DomUtil.create('select', '', typeLabel)
+ typeInput.name = 'format'
+ layerLabel.textContent = L._('Choose the layer to import in')
+ var layerInput = L.DomUtil.create('select', '', layerLabel)
+ layerInput.name = 'datalayer'
+ urlInput.type = 'text'
+ urlInput.placeholder = L._('Provide an URL here')
+ rawInput.placeholder = L._('Paste your data here')
+ clearLabel.textContent = L._('Replace layer content')
+ var clearFlag = L.DomUtil.create('input', '', clearLabel)
+ clearFlag.type = 'checkbox'
+ clearFlag.name = 'clear'
+ this.eachDataLayerReverse(function (datalayer) {
+ if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
+ var id = L.stamp(datalayer)
+ option = L.DomUtil.create('option', '', layerInput)
+ option.value = id
+ option.textContent = datalayer.options.name
+ }
+ })
+ L.DomUtil.element(
+ 'option',
+ { value: '', textContent: L._('Import in a new layer') },
+ layerInput
+ )
+ L.DomUtil.element(
+ 'option',
+ { value: '', textContent: L._('Choose the data format') },
+ typeInput
+ )
+ for (var i = 0; i < types.length; i++) {
+ option = L.DomUtil.create('option', '', typeInput)
+ option.value = option.textContent = types[i]
+ }
+ if (this.options.importPresets.length) {
+ var noPreset = L.DomUtil.create('option', '', presetSelect)
+ noPreset.value = noPreset.textContent = L._('Choose a preset')
+ for (var j = 0; j < this.options.importPresets.length; j++) {
+ option = L.DomUtil.create('option', '', presetSelect)
+ option.value = this.options.importPresets[j].url
+ option.textContent = this.options.importPresets[j].label
+ }
+ } else {
+ presetBox.style.display = 'none'
+ }
+
+ var submit = function () {
+ var type = typeInput.value,
+ layerId = layerInput[layerInput.selectedIndex].value,
+ layer
+ if (type === 'umap') {
+ this.once('postsync', function () {
+ this.setView(this.latLng(this.options.center), this.options.zoom)
+ })
+ }
+ if (layerId) layer = map.datalayers[layerId]
+ if (layer && clearFlag.checked) layer.empty()
+ if (fileInput.files.length) {
+ var file
+ for (var i = 0, file; (file = fileInput.files[i]); i++) {
+ type = type || L.Util.detectFileType(file)
+ if (!type) {
+ this.ui.alert({
+ content: L._('Unable to detect format of file {filename}', {
+ filename: file.name,
+ }),
+ level: 'error',
+ })
+ continue
+ }
+ if (type === 'umap') {
+ this.importFromFile(file, 'umap')
+ } else {
+ var importLayer = layer
+ if (!layer) importLayer = this.createDataLayer({ name: file.name })
+ importLayer.importFromFile(file, type)
+ }
+ }
+ } else {
+ if (!type)
+ return this.ui.alert({
+ content: L._('Please choose a format'),
+ level: 'error',
+ })
+ if (rawInput.value && type === 'umap') {
+ try {
+ this.importRaw(rawInput.value, type)
+ } catch (e) {
+ this.ui.alert({ content: L._('Invalid umap data'), level: 'error' })
+ console.error(e)
+ }
+ } else {
+ if (!layer) layer = this.createDataLayer()
+ if (rawInput.value) layer.importRaw(rawInput.value, type)
+ else if (urlInput.value) layer.importFromUrl(urlInput.value, type)
+ else if (presetSelect.selectedIndex > 0)
+ layer.importFromUrl(presetSelect[presetSelect.selectedIndex].value, type)
+ }
+ }
+ }
+ L.DomEvent.on(submitInput, 'click', submit, this)
+ L.DomEvent.on(
+ fileInput,
+ 'change',
+ function (e) {
+ var type = '',
+ newType
+ for (var i = 0; i < e.target.files.length; i++) {
+ newType = L.Util.detectFileType(e.target.files[i])
+ if (!type && newType) type = newType
+ if (type && newType !== type) {
+ type = ''
+ break
+ }
+ }
+ typeInput.value = type
+ },
+ this
+ )
+ this.ui.openPanel({ data: { html: container }, className: 'dark' })
+ },
+
+ importRaw: function (rawData) {
+ var importedData = JSON.parse(rawData)
+
+ var mustReindex = false
+
+ for (var i = 0; i < this.editableOptions.length; i++) {
+ var option = this.editableOptions[i]
+ if (typeof importedData.properties[option] !== 'undefined') {
+ this.options[option] = importedData.properties[option]
+ if (option === 'sortKey') mustReindex = true
+ }
+ }
+
+ if (importedData.geometry) this.options.center = this.latLng(importedData.geometry)
+ var self = this
+ importedData.layers.forEach(function (geojson) {
+ var dataLayer = self.createDataLayer()
+ dataLayer.fromUmapGeoJSON(geojson)
+ })
+
+ this.initTileLayers()
+ this.renderControls()
+ this.handleLimitBounds()
+ this.eachDataLayer(function (datalayer) {
+ if (mustReindex) datalayer.reindex()
+ datalayer.redraw()
+ })
+ this.fire('postsync')
+ this.isDirty = true
+ },
+
+ importFromFile: function (file) {
+ var reader = new FileReader()
+ reader.readAsText(file)
+ var self = this
+ reader.onload = function (e) {
+ var rawData = e.target.result
+ try {
+ self.importRaw(rawData)
+ } catch (e) {
+ console.error('Error importing data', e)
+ self.ui.alert({
+ content: L._('Invalid umap data in {filename}', { filename: file.name }),
+ level: 'error',
+ })
+ }
+ }
+ },
+
+ openBrowser: function () {
+ this.onceDatalayersLoaded(function () {
+ this._openBrowser()
+ })
+ },
+
+ openFilter: function () {
+ this.onceDatalayersLoaded(function () {
+ this._openFilter()
+ })
+ },
+
+ displayCaption: function () {
+ var container = L.DomUtil.create('div', 'umap-caption'),
+ title = L.DomUtil.create('h3', '', container)
+ title.textContent = this.options.name
+ this.permissions.addOwnerLink('h5', container)
+ if (this.options.description) {
+ var description = L.DomUtil.create('div', 'umap-map-description', container)
+ description.innerHTML = L.Util.toHTML(this.options.description)
+ }
+ var datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container)
+ this.eachVisibleDataLayer(function (datalayer) {
+ var p = L.DomUtil.create('p', '', datalayerContainer),
+ color = L.DomUtil.create('span', 'datalayer-color', p),
+ headline = L.DomUtil.create('strong', '', p),
+ description = L.DomUtil.create('span', '', p)
+ datalayer.onceLoaded(function () {
+ color.style.backgroundColor = this.getColor()
+ if (datalayer.options.description) {
+ description.innerHTML = L.Util.toHTML(datalayer.options.description)
+ }
+ })
+ datalayer.renderToolbox(headline)
+ L.DomUtil.add('span', '', headline, datalayer.options.name + ' ')
+ })
+ var creditsContainer = L.DomUtil.create('div', 'credits-container', container),
+ credits = L.DomUtil.createFieldset(creditsContainer, L._('Credits'))
+ title = L.DomUtil.add('h5', '', credits, L._('User content credits'))
+ if (this.options.shortCredit || this.options.longCredit) {
+ L.DomUtil.add(
+ 'p',
+ '',
+ credits,
+ L.Util.toHTML(this.options.longCredit || this.options.shortCredit)
+ )
+ }
+ if (this.options.licence) {
+ var licence = L.DomUtil.add(
+ 'p',
+ '',
+ credits,
+ L._('Map user content has been published under licence') + ' '
+ ),
+ link = L.DomUtil.add('a', '', licence, this.options.licence.name)
+ link.href = this.options.licence.url
+ } else {
+ L.DomUtil.add('p', '', credits, L._('No licence has been set'))
+ }
+ L.DomUtil.create('hr', '', credits)
+ title = L.DomUtil.create('h5', '', credits)
+ title.textContent = L._('Map background credits')
+ var tilelayerCredit = L.DomUtil.create('p', '', credits),
+ name = L.DomUtil.create('strong', '', tilelayerCredit),
+ attribution = L.DomUtil.create('span', '', tilelayerCredit)
+ name.textContent = this.selected_tilelayer.options.name + ' '
+ attribution.innerHTML = this.selected_tilelayer.getAttribution()
+ L.DomUtil.create('hr', '', credits)
+ var umapCredit = L.DomUtil.create('p', '', credits),
+ urls = {
+ leaflet: 'http://leafletjs.com',
+ django: 'https://www.djangoproject.com',
+ umap: 'http://wiki.openstreetmap.org/wiki/UMap',
+ }
+ umapCredit.innerHTML = L._(
+ 'Powered by Leaflet and Django, glued by uMap project.',
+ urls
+ )
+ var browser = L.DomUtil.create('li', '')
+ L.DomUtil.create('i', 'umap-icon-16 umap-list', browser)
+ var labelBrowser = L.DomUtil.create('span', '', browser)
+ labelBrowser.textContent = labelBrowser.title = L._('Browse data')
+ L.DomEvent.on(browser, 'click', this.openBrowser, this)
+ var actions = [browser]
+ if (this.options.advancedFilterKey) {
+ var filter = L.DomUtil.create('li', '')
+ L.DomUtil.create('i', 'umap-icon-16 umap-add', filter)
+ var labelFilter = L.DomUtil.create('span', '', filter)
+ labelFilter.textContent = labelFilter.title = L._('Select data')
+ L.DomEvent.on(filter, 'click', this.openFilter, this)
+ actions.push(filter)
+ }
+ this.ui.openPanel({ data: { html: container }, actions: actions })
+ },
+
+ eachDataLayer: function (method, context) {
+ for (var i = 0; i < this.datalayers_index.length; i++) {
+ method.call(context, this.datalayers_index[i])
+ }
+ },
+
+ eachDataLayerReverse: function (method, context, filter) {
+ for (var i = this.datalayers_index.length - 1; i >= 0; i--) {
+ if (filter && !filter.call(context, this.datalayers_index[i])) continue
+ method.call(context, this.datalayers_index[i])
+ }
+ },
+
+ eachBrowsableDataLayer: function (method, context) {
+ this.eachDataLayerReverse(method, context, function (d) {
+ return d.allowBrowse()
+ })
+ },
+
+ eachVisibleDataLayer: function (method, context) {
+ this.eachDataLayerReverse(method, context, function (d) {
+ return d.isVisible()
+ })
+ },
+
+ findDataLayer: function (method, context) {
+ for (var i = this.datalayers_index.length - 1; i >= 0; i--) {
+ if (method.call(context, this.datalayers_index[i]))
+ return this.datalayers_index[i]
+ }
+ },
+
+ backup: function () {
+ this.backupOptions()
+ this._datalayers_index_bk = [].concat(this.datalayers_index)
+ },
+
+ reset: function () {
+ if (this.editTools) this.editTools.stopDrawing()
+ this.resetOptions()
+ this.datalayers_index = [].concat(this._datalayers_index_bk)
+ this.dirty_datalayers.slice().forEach(function (datalayer) {
+ if (datalayer.isDeleted) datalayer.connectToMap()
+ datalayer.reset()
+ })
+ this.ensurePanesOrder()
+ this.dirty_datalayers = []
+ this.updateDatalayersControl()
+ this.initTileLayers()
+ this.isDirty = false
+ },
+
+ checkDirty: function () {
+ L.DomUtil.classIf(this._container, 'umap-is-dirty', this.isDirty)
+ },
+
+ addDirtyDatalayer: function (datalayer) {
+ if (this.dirty_datalayers.indexOf(datalayer) === -1) {
+ this.dirty_datalayers.push(datalayer)
+ this.isDirty = true
+ }
+ },
+
+ removeDirtyDatalayer: function (datalayer) {
+ if (this.dirty_datalayers.indexOf(datalayer) !== -1) {
+ this.dirty_datalayers.splice(this.dirty_datalayers.indexOf(datalayer), 1)
+ this.checkDirty()
+ }
+ },
+
+ continueSaving: function () {
+ if (this.dirty_datalayers.length) this.dirty_datalayers[0].save()
+ else this.fire('saved')
+ },
+
+ editableOptions: [
+ 'zoom',
+ 'scrollWheelZoom',
+ 'scaleControl',
+ 'moreControl',
+ 'miniMap',
+ 'displayPopupFooter',
+ 'onLoadPanel',
+ 'tilelayersControl',
+ 'name',
+ 'description',
+ 'licence',
+ 'tilelayer',
+ 'overlay',
+ 'limitBounds',
+ 'color',
+ 'iconClass',
+ 'iconUrl',
+ 'smoothFactor',
+ 'opacity',
+ 'weight',
+ 'fill',
+ 'fillColor',
+ 'fillOpacity',
+ 'dashArray',
+ 'popupShape',
+ 'popupTemplate',
+ 'popupContentTemplate',
+ 'zoomTo',
+ 'captionBar',
+ 'captionMenus',
+ 'slideshow',
+ 'sortKey',
+ 'labelKey',
+ 'filterKey',
+ 'advancedFilterKey',
+ 'slugKey',
+ 'showLabel',
+ 'labelDirection',
+ 'labelInteractive',
+ 'shortCredit',
+ 'longCredit',
+ 'permanentCredit',
+ 'permanentCreditBackground',
+ 'zoomControl',
+ 'datalayersControl',
+ 'searchControl',
+ 'locateControl',
+ 'fullscreenControl',
+ 'editinosmControl',
+ 'embedControl',
+ 'measureControl',
+ 'tilelayersControl',
+ 'easing',
+ ],
+
+ exportOptions: function () {
+ var properties = {}
+ for (var i = this.editableOptions.length - 1; i >= 0; i--) {
+ if (typeof this.options[this.editableOptions[i]] !== 'undefined') {
+ properties[this.editableOptions[i]] = this.options[this.editableOptions[i]]
+ }
+ }
+ return properties
+ },
+
+ serialize: function () {
+ var umapfile = {
+ type: 'umap',
+ uri: window.location.href,
+ properties: this.exportOptions(),
+ geometry: this.geometry(),
+ layers: [],
+ }
+
+ this.eachDataLayer(function (datalayer) {
+ umapfile.layers.push(datalayer.umapGeoJSON())
+ })
+
+ return JSON.stringify(umapfile, null, 2)
+ },
+
+ save: function () {
+ if (!this.isDirty) return
+ if (this._default_extent) this.updateExtent()
+ var geojson = {
+ type: 'Feature',
+ geometry: this.geometry(),
+ properties: this.exportOptions(),
+ }
+ this.backup()
+ var formData = new FormData()
+ formData.append('name', this.options.name)
+ formData.append('center', JSON.stringify(this.geometry()))
+ formData.append('settings', JSON.stringify(geojson))
+ this.post(this.getSaveUrl(), {
+ data: formData,
+ context: this,
+ callback: function (data) {
+ var duration = 3000
+ if (!this.options.umap_id) {
+ duration = 100000 // we want a longer message at map creation (TODO UGLY)
+ this.options.umap_id = data.id
+ this.permissions.setOptions(data.permissions)
+ } else if (!this.permissions.isDirty) {
+ // Do not override local changes to permissions,
+ // but update in case some other editors changed them in the meantime.
+ this.permissions.setOptions(data.permissions)
+ }
+ // Update URL in case the name has changed.
+ if (history && history.pushState)
+ history.pushState({}, this.options.name, data.url)
+ else window.location = data.url
+ if (data.info) msg = data.info
+ else msg = L._('Map has been saved!')
+ this.once('saved', function () {
+ this.isDirty = false
+ this.ui.alert({ content: msg, level: 'info', duration: duration })
+ })
+ this.ui.closePanel()
+ this.permissions.save()
+ },
+ })
+ },
+
+ 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()
+ },
+
+ geometry: function () {
+ /* Return a GeoJSON geometry Object */
+ var latlng = this.latLng(this.options.center || this.getCenter())
+ return {
+ type: 'Point',
+ coordinates: [latlng.lng, latlng.lat],
+ }
+ },
+
+ defaultDataLayer: function () {
+ var datalayer, fallback
+ datalayer = this.lastUsedDataLayer
+ if (
+ datalayer &&
+ !datalayer.isRemoteLayer() &&
+ datalayer.canBrowse() &&
+ datalayer.isVisible()
+ ) {
+ return datalayer
+ }
+ datalayer = this.findDataLayer(function (datalayer) {
+ if (!datalayer.isRemoteLayer() && datalayer.canBrowse()) {
+ fallback = datalayer
+ if (datalayer.isVisible()) return true
+ }
+ })
+ if (datalayer) return datalayer
+ if (fallback) {
+ // No datalayer visible, let's force one
+ this.addLayer(fallback.layer)
+ return fallback
+ }
+ return this.createDataLayer()
+ },
+
+ getDataLayerByUmapId: function (umap_id) {
+ return this.findDataLayer(function (d) {
+ return d.umap_id == umap_id
+ })
+ },
+
+ _editControls: function (container) {
+ var UIFields = []
+ for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
+ UIFields.push('options.' + this.HIDDABLE_CONTROLS[i] + 'Control')
+ }
+ UIFields = UIFields.concat([
+ 'options.moreControl',
+ 'options.scrollWheelZoom',
+ 'options.miniMap',
+ 'options.scaleControl',
+ 'options.onLoadPanel',
+ 'options.displayPopupFooter',
+ 'options.captionBar',
+ 'options.captionMenus',
+ ])
+ builder = new L.U.FormBuilder(this, UIFields, {
+ callback: function () {
+ this.renderControls()
+ this.initCaptionBar()
+ },
+ callbackContext: this,
+ })
+ var controlsOptions = L.DomUtil.createFieldset(
+ container,
+ L._('User interface options')
+ )
+ controlsOptions.appendChild(builder.build())
+ },
+
+ _editShapeProperties: function (container) {
+ var shapeOptions = [
+ 'options.color',
+ 'options.iconClass',
+ 'options.iconUrl',
+ 'options.opacity',
+ 'options.weight',
+ 'options.fill',
+ 'options.fillColor',
+ 'options.fillOpacity',
+ ]
+
+ builder = new L.U.FormBuilder(this, shapeOptions, {
+ callback: function (e) {
+ this.eachDataLayer(function (datalayer) {
+ datalayer.redraw()
+ })
+ },
+ })
+ var defaultShapeProperties = L.DomUtil.createFieldset(
+ container,
+ L._('Default shape properties')
+ )
+ defaultShapeProperties.appendChild(builder.build())
+ },
+
+ _editDefaultProperties: function (container) {
+ var optionsFields = [
+ 'options.smoothFactor',
+ 'options.dashArray',
+ 'options.zoomTo',
+ ['options.easing', { handler: 'Switch', label: L._('Animated transitions') }],
+ 'options.labelKey',
+ [
+ 'options.sortKey',
+ {
+ handler: 'BlurInput',
+ helpEntries: 'sortKey',
+ placeholder: L._('Default: name'),
+ label: L._('Sort key'),
+ inheritable: true,
+ },
+ ],
+ [
+ 'options.filterKey',
+ {
+ handler: 'Input',
+ helpEntries: 'filterKey',
+ placeholder: L._('Default: name'),
+ label: L._('Filter keys'),
+ inheritable: true,
+ },
+ ],
+ [
+ 'options.advancedFilterKey',
+ {
+ handler: 'Input',
+ helpEntries: 'advancedFilterKey',
+ placeholder: L._('Example: key1,key2,key3'),
+ label: L._('Advanced filter keys'),
+ inheritable: true,
+ },
+ ],
+ [
+ 'options.slugKey',
+ {
+ handler: 'BlurInput',
+ helpEntries: 'slugKey',
+ placeholder: L._('Default: name'),
+ label: L._('Feature identifier key'),
+ },
+ ],
+ ]
+
+ builder = new L.U.FormBuilder(this, optionsFields, {
+ callback: function (e) {
+ this.initCaptionBar()
+ this.eachDataLayer(function (datalayer) {
+ if (e.helper.field === 'options.sortKey') datalayer.reindex()
+ datalayer.redraw()
+ })
+ },
+ })
+ var defaultProperties = L.DomUtil.createFieldset(
+ container,
+ L._('Default properties')
+ )
+ defaultProperties.appendChild(builder.build())
+ },
+
+ _editInteractionsProperties: function (container) {
+ var popupFields = [
+ 'options.popupShape',
+ 'options.popupTemplate',
+ 'options.popupContentTemplate',
+ 'options.showLabel',
+ 'options.labelDirection',
+ 'options.labelInteractive',
+ ]
+ builder = new L.U.FormBuilder(this, popupFields, {
+ callback: function (e) {
+ if (
+ e.helper.field === 'options.popupTemplate' ||
+ e.helper.field === 'options.popupContentTemplate' ||
+ e.helper.field === 'options.popupShape'
+ )
+ return
+ this.eachDataLayer(function (datalayer) {
+ datalayer.redraw()
+ })
+ },
+ })
+ var popupFieldset = L.DomUtil.createFieldset(
+ container,
+ L._('Default interaction options')
+ )
+ popupFieldset.appendChild(builder.build())
+ },
+
+ _editTilelayer: function (container) {
+ if (!L.Util.isObject(this.options.tilelayer)) {
+ this.options.tilelayer = {}
+ }
+ var tilelayerFields = [
+ [
+ 'options.tilelayer.name',
+ { handler: 'BlurInput', placeholder: L._('display name') },
+ ],
+ [
+ 'options.tilelayer.url_template',
+ {
+ handler: 'BlurInput',
+ helpText: L._('Supported scheme') + ': http://{s}.domain.com/{z}/{x}/{y}.png',
+ placeholder: 'url',
+ },
+ ],
+ [
+ 'options.tilelayer.maxZoom',
+ { handler: 'BlurIntInput', placeholder: L._('max zoom') },
+ ],
+ [
+ 'options.tilelayer.minZoom',
+ { handler: 'BlurIntInput', placeholder: L._('min zoom') },
+ ],
+ [
+ 'options.tilelayer.attribution',
+ { handler: 'BlurInput', placeholder: L._('attribution') },
+ ],
+ ['options.tilelayer.tms', { handler: 'Switch', label: L._('TMS format') }],
+ ]
+ var customTilelayer = L.DomUtil.createFieldset(container, L._('Custom background'))
+ builder = new L.U.FormBuilder(this, tilelayerFields, {
+ callback: this.initTileLayers,
+ callbackContext: this,
+ })
+ customTilelayer.appendChild(builder.build())
+ },
+
+ _editOverlay: function (container) {
+ if (!L.Util.isObject(this.options.overlay)) {
+ this.options.overlay = {}
+ }
+ var overlayFields = [
+ [
+ 'options.overlay.url_template',
+ {
+ handler: 'BlurInput',
+ helpText: L._('Supported scheme') + ': http://{s}.domain.com/{z}/{x}/{y}.png',
+ placeholder: 'url',
+ helpText: L._('Background overlay url'),
+ },
+ ],
+ [
+ 'options.overlay.maxZoom',
+ { handler: 'BlurIntInput', placeholder: L._('max zoom') },
+ ],
+ [
+ 'options.overlay.minZoom',
+ { handler: 'BlurIntInput', placeholder: L._('min zoom') },
+ ],
+ [
+ 'options.overlay.attribution',
+ { handler: 'BlurInput', placeholder: L._('attribution') },
+ ],
+ [
+ 'options.overlay.opacity',
+ { handler: 'Range', min: 0, max: 1, step: 'any', placeholder: L._('opacity') },
+ ],
+ ['options.overlay.tms', { handler: 'Switch', label: L._('TMS format') }],
+ ]
+ var overlay = L.DomUtil.createFieldset(container, L._('Custom overlay'))
+ builder = new L.U.FormBuilder(this, overlayFields, {
+ callback: this.initTileLayers,
+ callbackContext: this,
+ })
+ overlay.appendChild(builder.build())
+ },
+
+ _editBounds: function (container) {
+ if (!L.Util.isObject(this.options.limitBounds)) {
+ this.options.limitBounds = {}
+ }
+ var limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'))
+ var boundsFields = [
+ [
+ 'options.limitBounds.south',
+ { handler: 'BlurFloatInput', placeholder: L._('max South') },
+ ],
+ [
+ 'options.limitBounds.west',
+ { handler: 'BlurFloatInput', placeholder: L._('max West') },
+ ],
+ [
+ 'options.limitBounds.north',
+ { handler: 'BlurFloatInput', placeholder: L._('max North') },
+ ],
+ [
+ 'options.limitBounds.east',
+ { handler: 'BlurFloatInput', placeholder: L._('max East') },
+ ],
+ ]
+ var boundsBuilder = new L.U.FormBuilder(this, boundsFields, {
+ callback: this.handleLimitBounds,
+ callbackContext: this,
+ })
+ limitBounds.appendChild(boundsBuilder.build())
+ var boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds)
+ var setCurrentButton = L.DomUtil.add(
+ 'a',
+ 'button',
+ boundsButtons,
+ L._('Use current bounds')
+ )
+ setCurrentButton.href = '#'
+ L.DomEvent.on(
+ setCurrentButton,
+ 'click',
+ function () {
+ var 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())
+ boundsBuilder.fetchAll()
+ this.isDirty = true
+ this.handleLimitBounds()
+ },
+ this
+ )
+ var emptyBounds = L.DomUtil.add('a', 'button', boundsButtons, L._('Empty'))
+ emptyBounds.href = '#'
+ L.DomEvent.on(
+ emptyBounds,
+ 'click',
+ function () {
+ this.options.limitBounds.south = null
+ this.options.limitBounds.west = null
+ this.options.limitBounds.north = null
+ this.options.limitBounds.east = null
+ boundsBuilder.fetchAll()
+ this.isDirty = true
+ this.handleLimitBounds()
+ },
+ this
+ )
+ },
+
+ _editSlideshow: function (container) {
+ var slideshow = L.DomUtil.createFieldset(container, L._('Slideshow'))
+ var slideshowFields = [
+ [
+ 'options.slideshow.active',
+ { handler: 'Switch', label: L._('Activate slideshow mode') },
+ ],
+ [
+ 'options.slideshow.delay',
+ {
+ handler: 'SlideshowDelay',
+ helpText: L._('Delay between two transitions when in play mode'),
+ },
+ ],
+ [
+ 'options.slideshow.easing',
+ { handler: 'Switch', label: L._('Animated transitions'), inheritable: true },
+ ],
+ [
+ 'options.slideshow.autoplay',
+ { handler: 'Switch', label: L._('Autostart when map is loaded') },
+ ],
+ ]
+ var slideshowHandler = function () {
+ this.slideshow.setOptions(this.options.slideshow)
+ this.renderControls()
+ }
+ var slideshowBuilder = new L.U.FormBuilder(this, slideshowFields, {
+ callback: slideshowHandler,
+ callbackContext: this,
+ })
+ slideshow.appendChild(slideshowBuilder.build())
+ },
+
+ _editCredits: function (container) {
+ var credits = L.DomUtil.createFieldset(container, L._('Credits'))
+ var creditsFields = [
+ ['options.licence', { handler: 'LicenceChooser', label: L._('licence') }],
+ [
+ 'options.shortCredit',
+ {
+ handler: 'Input',
+ label: L._('Short credits'),
+ helpEntries: ['shortCredit', 'textFormatting'],
+ },
+ ],
+ [
+ 'options.longCredit',
+ {
+ handler: 'Textarea',
+ label: L._('Long credits'),
+ helpEntries: ['longCredit', 'textFormatting'],
+ },
+ ],
+ [
+ 'options.permanentCredit',
+ {
+ handler: 'Textarea',
+ label: L._('Permanent credits'),
+ helpEntries: ['permanentCredit', 'textFormatting'],
+ },
+ ],
+ [
+ 'options.permanentCreditBackground',
+ { handler: 'Switch', label: L._('Permanent credits background') },
+ ],
+ ]
+ var creditsBuilder = new L.U.FormBuilder(this, creditsFields, {
+ callback: this.renderControls,
+ callbackContext: this,
+ })
+ credits.appendChild(creditsBuilder.build())
+ },
+
+ _advancedActions: function (container) {
+ var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
+ var advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions)
+ var del = L.DomUtil.create('a', 'button umap-delete', advancedButtons)
+ del.href = '#'
+ del.textContent = L._('Delete')
+ L.DomEvent.on(del, 'click', L.DomEvent.stop).on(del, 'click', this.del, this)
+ var clone = L.DomUtil.create('a', 'button umap-clone', advancedButtons)
+ clone.href = '#'
+ clone.textContent = L._('Clone')
+ clone.title = L._('Clone this map')
+ L.DomEvent.on(clone, 'click', L.DomEvent.stop).on(clone, 'click', this.clone, this)
+ var empty = L.DomUtil.create('a', 'button umap-empty', advancedButtons)
+ empty.href = '#'
+ empty.textContent = L._('Empty')
+ empty.title = L._('Delete all layers')
+ L.DomEvent.on(empty, 'click', L.DomEvent.stop).on(empty, 'click', this.empty, this)
+ var download = L.DomUtil.create('a', 'button umap-download', advancedButtons)
+ download.href = '#'
+ download.textContent = L._('Download')
+ download.title = L._('Open download panel')
+ L.DomEvent.on(download, 'click', L.DomEvent.stop).on(
+ download,
+ 'click',
+ this.renderShareBox,
+ this
+ )
+ },
+
+ edit: function () {
+ if (!this.editEnabled) return
+ var container = L.DomUtil.create('div', 'umap-edit-container'),
+ metadataFields = ['options.name', 'options.description'],
+ title = L.DomUtil.create('h4', '', container)
+ title.textContent = L._('Edit map properties')
+ var builder = new L.U.FormBuilder(this, metadataFields)
+ var form = builder.build()
+ container.appendChild(form)
+ this._editControls(container)
+ this._editShapeProperties(container)
+ this._editDefaultProperties(container)
+ this._editInteractionsProperties(container)
+ this._editTilelayer(container)
+ this._editOverlay(container)
+ this._editBounds(container)
+ this._editSlideshow(container)
+ this._editCredits(container)
+ this._advancedActions(container)
+
+ this.ui.openPanel({ data: { html: container }, className: 'dark' })
+ },
+
+ enableEdit: function () {
+ L.DomUtil.addClass(document.body, 'umap-edit-enabled')
+ this.editEnabled = true
+ this.fire('edit:enabled')
+ },
+
+ disableEdit: function () {
+ if (this.isDirty) return
+ L.DomUtil.removeClass(document.body, 'umap-edit-enabled')
+ this.editedFeature = null
+ this.editEnabled = false
+ this.fire('edit:disabled')
+ },
+
+ getDisplayName: function () {
+ return this.options.name || L._('Untitled map')
+ },
+
+ initCaptionBar: function () {
+ var container = L.DomUtil.create('div', 'umap-caption-bar', this._controlContainer),
+ name = L.DomUtil.create('h3', '', container)
+ L.DomEvent.disableClickPropagation(container)
+ this.permissions.addOwnerLink('span', container)
+ if (this.options.captionMenus) {
+ var about = L.DomUtil.add('a', 'umap-about-link', container, ' — ' + L._('About'))
+ about.href = '#'
+ L.DomEvent.on(about, 'click', this.displayCaption, this)
+ var browser = L.DomUtil.add(
+ 'a',
+ 'umap-open-browser-link',
+ container,
+ ' | ' + L._('Browse data')
+ )
+ browser.href = '#'
+ L.DomEvent.on(browser, 'click', L.DomEvent.stop).on(
+ browser,
+ 'click',
+ this.openBrowser,
+ this
+ )
+ if (this.options.advancedFilterKey) {
+ var filter = L.DomUtil.add(
+ 'a',
+ 'umap-open-filter-link',
+ container,
+ ' | ' + L._('Select data')
+ )
+ filter.href = '#'
+ L.DomEvent.on(filter, 'click', L.DomEvent.stop).on(
+ filter,
+ 'click',
+ this.openFilter,
+ this
+ )
+ }
+ }
+ var setName = function () {
+ name.textContent = this.getDisplayName()
+ }
+ L.bind(setName, this)()
+ this.on('postsync', L.bind(setName, this))
+ this.onceDatalayersLoaded(function () {
+ this.slideshow.renderToolbox(container)
+ })
+ },
+
+ initEditBar: function () {
+ var container = L.DomUtil.create(
+ 'div',
+ 'umap-main-edit-toolbox with-transition dark',
+ this._controlContainer
+ ),
+ title = L.DomUtil.add('h3', '', container, L._('Editing') + ' '),
+ name = L.DomUtil.create('a', 'umap-click-to-edit', title),
+ setName = function () {
+ name.textContent = this.getDisplayName()
+ }
+ L.bind(setName, this)()
+ L.DomEvent.on(name, 'click', this.edit, this)
+ this.on('postsync', L.bind(setName, this))
+ this.help.button(title, 'edit')
+ var save = L.DomUtil.create('a', 'leaflet-control-edit-save button', container)
+ save.href = '#'
+ save.title = L._('Save current edits') + ' (Ctrl+S)'
+ save.textContent = L._('Save')
+ var cancel = L.DomUtil.create('a', 'leaflet-control-edit-cancel button', container)
+ cancel.href = '#'
+ cancel.title = L._('Cancel edits')
+ cancel.textContent = L._('Cancel')
+ var disable = L.DomUtil.create('a', 'leaflet-control-edit-disable', container)
+ disable.href = '#'
+ disable.title = disable.textContent = L._('Disable editing')
+
+ L.DomEvent.addListener(disable, 'click', L.DomEvent.stop).addListener(
+ disable,
+ 'click',
+ function (e) {
+ this.disableEdit(e)
+ this.ui.closePanel()
+ },
+ this
+ )
+
+ L.DomEvent.addListener(save, 'click', L.DomEvent.stop).addListener(
+ save,
+ 'click',
+ this.save,
+ this
+ )
+
+ L.DomEvent.addListener(cancel, 'click', L.DomEvent.stop).addListener(
+ cancel,
+ 'click',
+ this.askForReset,
+ this
+ )
+ },
+
+ askForReset: function (e) {
+ if (!confirm(L._('Are you sure you want to cancel your changes?'))) return
+ this.reset()
+ this.disableEdit(e)
+ this.ui.closePanel()
+ },
+
+ startMarker: function () {
+ return this.editTools.startMarker()
+ },
+
+ startPolyline: function () {
+ return this.editTools.startPolyline()
+ },
+
+ startPolygon: function () {
+ return this.editTools.startPolygon()
+ },
+
+ del: function () {
+ if (confirm(L._('Are you sure you want to delete this map?'))) {
+ var url = L.Util.template(this.options.urls.map_delete, {
+ map_id: this.options.umap_id,
+ })
+ this.post(url)
+ }
+ },
+
+ clone: function () {
+ if (
+ confirm(L._('Are you sure you want to clone this map and all its datalayers?'))
+ ) {
+ var url = L.Util.template(this.options.urls.map_clone, {
+ map_id: this.options.umap_id,
+ })
+ this.post(url)
+ }
+ },
+
+ empty: function () {
+ this.eachDataLayerReverse(function (datalayer) {
+ datalayer._delete()
+ })
+ },
+
+ initLoader: function () {
+ this.loader = new L.Control.Loading()
+ this.loader.onAdd(this)
+ },
+
+ post: function (url, options) {
+ options = options || {}
+ options.listener = this
+ this.xhr.post(url, options)
+ },
+
+ get: function (url, options) {
+ options = options || {}
+ options.listener = this
+ this.xhr.get(url, options)
+ },
+
+ ajax: function (options) {
+ options.listener = this
+ this.xhr._ajax(options)
+ },
+
+ initContextMenu: function () {
+ this.contextmenu = new L.U.ContextMenu(this)
+ this.contextmenu.enable()
+ },
+
+ setContextMenuItems: function (e) {
+ var items = []
+ if (this._zoom !== this.getMaxZoom()) {
+ items.push({
+ text: L._('Zoom in'),
+ callback: function () {
+ this.zoomIn()
+ },
+ })
+ }
+ if (this._zoom !== this.getMinZoom()) {
+ items.push({
+ text: L._('Zoom out'),
+ callback: function () {
+ this.zoomOut()
+ },
+ })
+ }
+ if (e && e.relatedTarget) {
+ if (e.relatedTarget.getContextMenuItems) {
+ items = items.concat(e.relatedTarget.getContextMenuItems(e))
+ }
+ }
+ if (this.options.allowEdit) {
+ items.push('-')
+ if (this.editEnabled) {
+ if (!this.isDirty) {
+ items.push({
+ text: L._('Stop editing') + ' (Ctrl+E)',
+ callback: this.disableEdit,
+ })
+ }
+ if (this.options.enableMarkerDraw) {
+ items.push({
+ text: L._('Draw a marker') + ' (Ctrl+M)',
+ callback: this.startMarker,
+ context: this,
+ })
+ }
+ if (this.options.enablePolylineDraw) {
+ items.push({
+ text: L._('Draw a polygon') + ' (Ctrl+P)',
+ callback: this.startPolygon,
+ context: this,
+ })
+ }
+ if (this.options.enablePolygonDraw) {
+ items.push({
+ text: L._('Draw a line') + ' (Ctrl+L)',
+ callback: this.startPolyline,
+ context: this,
+ })
+ }
+ items.push('-')
+ items.push({
+ text: L._('Help'),
+ callback: function () {
+ this.help.show('edit')
+ },
+ })
+ } else {
+ items.push({
+ text: L._('Start editing') + ' (Ctrl+E)',
+ callback: this.enableEdit,
+ })
+ }
+ }
+ items.push('-', {
+ text: L._('Browse data'),
+ callback: this.openBrowser,
+ })
+ if (this.options.advancedFilterKey) {
+ items.push({
+ text: L._('Select data'),
+ callback: this.openFilter,
+ })
+ }
+ items.push(
+ {
+ text: L._('About'),
+ callback: this.displayCaption,
+ },
+ {
+ text: L._('Search location'),
+ callback: this.search,
+ }
+ )
+ if (this.options.urls.routing) {
+ items.push('-', {
+ text: L._('Directions from here'),
+ callback: this.openExternalRouting,
+ })
+ }
+ this.options.contextmenuItems = items
+ },
+
+ openExternalRouting: function (e) {
+ var url = this.options.urls.routing
+ if (url) {
+ var params = {
+ lat: e.latlng.lat,
+ lng: e.latlng.lng,
+ locale: L.locale,
+ zoom: this.getZoom(),
+ }
+ window.open(L.Util.template(url, params))
+ }
+ return
+ },
+
+ getMap: function () {
+ return this
+ },
+
+ getGeoContext: function () {
+ var context = {
+ bbox: this.getBounds().toBBoxString(),
+ north: this.getBounds().getNorthEast().lat,
+ east: this.getBounds().getNorthEast().lng,
+ south: this.getBounds().getSouthWest().lat,
+ west: this.getBounds().getSouthWest().lng,
+ lat: this.getCenter().lat,
+ lng: this.getCenter().lng,
+ zoom: this.getZoom(),
+ }
+ context.left = context.west
+ context.bottom = context.south
+ context.right = context.east
+ context.top = context.north
+ return context
+ },
+
+ localizeUrl: function (url) {
+ return L.Util.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: encodeURIComponent(url),
+ ttl: ttl,
+ })
+ }
+ return url
+ },
+
+ closeInplaceToolbar: function () {
+ var toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id]
+ if (toolbar) toolbar.remove()
+ },
+
+ search: function () {
+ if (this._controls.search) this._controls.search.openPanel(this)
+ },
+
+ getFilterKeys: function () {
+ return (this.options.filterKey || this.options.sortKey || 'name').split(',')
+ },
+
+ getAdvancedFilterKeys: function () {
+ return (this.options.advancedFilterKey || '').split(',')
+ },
+})
diff --git a/umap/static/umap/js/umap.layer.js b/umap/static/umap/js/umap.layer.js
index 208ff571..cae8ba3b 100644
--- a/umap/static/umap/js/umap.layer.js
+++ b/umap/static/umap/js/umap.layer.js
@@ -1,1080 +1,1220 @@
L.U.Layer = {
- canBrowse: true,
+ canBrowse: true,
- getFeatures: function () {
- return this._layers;
- },
+ getFeatures: function () {
+ return this._layers
+ },
- getEditableOptions: function () {return [];},
+ getEditableOptions: function () {
+ return []
+ },
- postUpdate: function () {}
-
-};
+ postUpdate: function () {},
+}
L.U.Layer.Default = L.FeatureGroup.extend({
- _type: 'Default',
- includes: [L.U.Layer],
-
- initialize: function (datalayer) {
- this.datalayer = datalayer;
- L.FeatureGroup.prototype.initialize.call(this);
- }
-
-});
+ _type: 'Default',
+ includes: [L.U.Layer],
+ initialize: function (datalayer) {
+ this.datalayer = datalayer
+ L.FeatureGroup.prototype.initialize.call(this)
+ },
+})
L.U.MarkerCluster = L.MarkerCluster.extend({
-// Custom class so we can call computeTextColor
-// when element is already on the DOM.
+ // Custom class so we can call computeTextColor
+ // when element is already on the DOM.
- _initIcon: function () {
- L.MarkerCluster.prototype._initIcon.call(this);
- var div = this._icon.querySelector('div');
- // Compute text color only when icon is added to the DOM.
- div.style.color = this._iconObj.computeTextColor(div);
- }
-
-});
+ _initIcon: function () {
+ L.MarkerCluster.prototype._initIcon.call(this)
+ var div = this._icon.querySelector('div')
+ // Compute text color only when icon is added to the DOM.
+ div.style.color = this._iconObj.computeTextColor(div)
+ },
+})
L.U.Layer.Cluster = L.MarkerClusterGroup.extend({
- _type: 'Cluster',
- includes: [L.U.Layer],
+ _type: 'Cluster',
+ includes: [L.U.Layer],
- initialize: function (datalayer) {
- this.datalayer = datalayer;
- var options = {
- polygonOptions: {
- color: this.datalayer.getColor()
- },
- iconCreateFunction: function (cluster) {
- return new L.U.Icon.Cluster(datalayer, cluster);
- }
- };
- if (this.datalayer.options.cluster && this.datalayer.options.cluster.radius) {
- options.maxClusterRadius = this.datalayer.options.cluster.radius;
- }
- L.MarkerClusterGroup.prototype.initialize.call(this, options);
- this._markerCluster = L.U.MarkerCluster;
- },
-
- getEditableOptions: function () {
- if (!L.Util.isObject(this.datalayer.options.cluster)) {
- this.datalayer.options.cluster = {};
- }
- return [
- ['options.cluster.radius', {handler: 'BlurIntInput', placeholder: L._('Clustering radius'), helpText: L._('Override clustering radius (default 80)')}],
- ['options.cluster.textColor', {handler: 'TextColorPicker', placeholder: L._('Auto'), helpText: L._('Text color for the cluster label')}],
- ];
-
- },
-
- postUpdate: function (e) {
- if (e.helper.field === 'options.cluster.radius') {
- // No way to reset radius of an already instanciated MarkerClusterGroup...
- this.datalayer.resetLayer(true);
- return;
- }
- if (e.helper.field === 'options.color') {
- this.options.polygonOptions.color = this.datalayer.getColor();
- }
+ initialize: function (datalayer) {
+ this.datalayer = datalayer
+ var options = {
+ polygonOptions: {
+ color: this.datalayer.getColor(),
+ },
+ iconCreateFunction: function (cluster) {
+ return new L.U.Icon.Cluster(datalayer, cluster)
+ },
}
+ if (this.datalayer.options.cluster && this.datalayer.options.cluster.radius) {
+ options.maxClusterRadius = this.datalayer.options.cluster.radius
+ }
+ L.MarkerClusterGroup.prototype.initialize.call(this, options)
+ this._markerCluster = L.U.MarkerCluster
+ },
-});
+ getEditableOptions: function () {
+ if (!L.Util.isObject(this.datalayer.options.cluster)) {
+ this.datalayer.options.cluster = {}
+ }
+ return [
+ [
+ 'options.cluster.radius',
+ {
+ handler: 'BlurIntInput',
+ placeholder: L._('Clustering radius'),
+ helpText: L._('Override clustering radius (default 80)'),
+ },
+ ],
+ [
+ 'options.cluster.textColor',
+ {
+ handler: 'TextColorPicker',
+ placeholder: L._('Auto'),
+ helpText: L._('Text color for the cluster label'),
+ },
+ ],
+ ]
+ },
+
+ postUpdate: function (e) {
+ if (e.helper.field === 'options.cluster.radius') {
+ // No way to reset radius of an already instanciated MarkerClusterGroup...
+ this.datalayer.resetLayer(true)
+ return
+ }
+ if (e.helper.field === 'options.color') {
+ this.options.polygonOptions.color = this.datalayer.getColor()
+ }
+ },
+})
L.U.Layer.Heat = L.HeatLayer.extend({
- _type: 'Heat',
- includes: [L.U.Layer],
- canBrowse: false,
+ _type: 'Heat',
+ includes: [L.U.Layer],
+ canBrowse: false,
- initialize: function (datalayer) {
- this.datalayer = datalayer;
- L.HeatLayer.prototype.initialize.call(this, [], this.datalayer.options.heat);
- },
+ initialize: function (datalayer) {
+ this.datalayer = datalayer
+ L.HeatLayer.prototype.initialize.call(this, [], this.datalayer.options.heat)
+ },
- addLayer: function (layer) {
- if (layer instanceof L.Marker) {
- var latlng = layer.getLatLng(), alt;
- if (this.datalayer.options.heat && this.datalayer.options.heat.intensityProperty) {
- alt = parseFloat(layer.properties[this.datalayer.options.heat.intensityProperty || 0]);
- latlng = new L.LatLng(latlng.lat, latlng.lng, alt);
- }
- this.addLatLng(latlng);
- }
- },
-
- clearLayers: function () {
- this.setLatLngs([]);
- },
-
- redraw: function () {
- // setlalngs call _redraw through setAnimFrame, thus async, so this
- // can ends with race condition if we remove the layer very faslty after.
- // Remove me when https://github.com/Leaflet/Leaflet.heat/pull/53 is released.
- if (!this._map) return;
- L.HeatLayer.prototype.redraw.call(this);
- },
-
- getFeatures: function () {
- return {};
- },
-
- getBounds: function () {
- return L.latLngBounds(this._latlngs);
- },
-
- getEditableOptions: function () {
- if (!L.Util.isObject(this.datalayer.options.heat)) {
- this.datalayer.options.heat = {};
- }
- return [
- ['options.heat.radius', {handler: 'BlurIntInput', placeholder: L._('Heatmap radius'), helpText: L._('Override heatmap radius (default 25)')}],
- ['options.heat.intensityProperty', {handler: 'BlurInput', placeholder: L._('Heatmap intensity property'), helpText: L._('Optional intensity property for heatmap')}]
- ];
-
- },
-
- postUpdate: function (e) {
- if (e.helper.field === 'options.heat.intensityProperty') {
- this.datalayer.resetLayer(true); // We need to repopulate the latlngs
- return;
- }
- if (e.helper.field === 'options.heat.radius') {
- this.options.radius = this.datalayer.options.heat.radius;
- }
- this._updateOptions();
+ addLayer: function (layer) {
+ if (layer instanceof L.Marker) {
+ var latlng = layer.getLatLng(),
+ alt
+ if (
+ this.datalayer.options.heat &&
+ this.datalayer.options.heat.intensityProperty
+ ) {
+ alt = parseFloat(
+ layer.properties[this.datalayer.options.heat.intensityProperty || 0]
+ )
+ latlng = new L.LatLng(latlng.lat, latlng.lng, alt)
+ }
+ this.addLatLng(latlng)
}
+ },
-});
+ clearLayers: function () {
+ this.setLatLngs([])
+ },
+
+ redraw: function () {
+ // setlalngs call _redraw through setAnimFrame, thus async, so this
+ // can ends with race condition if we remove the layer very faslty after.
+ // Remove me when https://github.com/Leaflet/Leaflet.heat/pull/53 is released.
+ if (!this._map) return
+ L.HeatLayer.prototype.redraw.call(this)
+ },
+
+ getFeatures: function () {
+ return {}
+ },
+
+ getBounds: function () {
+ return L.latLngBounds(this._latlngs)
+ },
+
+ getEditableOptions: function () {
+ if (!L.Util.isObject(this.datalayer.options.heat)) {
+ this.datalayer.options.heat = {}
+ }
+ return [
+ [
+ 'options.heat.radius',
+ {
+ handler: 'BlurIntInput',
+ placeholder: L._('Heatmap radius'),
+ helpText: L._('Override heatmap radius (default 25)'),
+ },
+ ],
+ [
+ 'options.heat.intensityProperty',
+ {
+ handler: 'BlurInput',
+ placeholder: L._('Heatmap intensity property'),
+ helpText: L._('Optional intensity property for heatmap'),
+ },
+ ],
+ ]
+ },
+
+ postUpdate: function (e) {
+ if (e.helper.field === 'options.heat.intensityProperty') {
+ this.datalayer.resetLayer(true) // We need to repopulate the latlngs
+ return
+ }
+ if (e.helper.field === 'options.heat.radius') {
+ this.options.radius = this.datalayer.options.heat.radius
+ }
+ this._updateOptions()
+ },
+})
L.U.DataLayer = L.Evented.extend({
-
- options: {
- displayOnLoad: true,
- browsable: true
- },
-
- initialize: function (map, data) {
- this.map = map;
- this._index = Array();
- this._layers = {};
- this._geojson = null;
- this._propertiesIndex = [];
-
- this.parentPane = this.map.getPane('overlayPane');
- this.pane = this.map.createPane('datalayer' + L.stamp(this), this.parentPane);
- this.pane.dataset.id = L.stamp(this);
- this.renderer = L.svg({pane: this.pane});
-
- var isDirty = false,
- isDeleted = false,
- self = this;
- try {
- Object.defineProperty(this, 'isDirty', {
- get: function () {
- return isDirty;
- },
- set: function (status) {
- if (!isDirty && status) self.fire('dirty');
- isDirty = status;
- if (status) {
- self.map.addDirtyDatalayer(self);
- // A layer can be made dirty by indirect action (like dragging layers)
- // we need to have it loaded before saving it.
- if (!self.isLoaded()) self.fetchData();
- } else {
- self.map.removeDirtyDatalayer(self);
- self.isDeleted = false;
- }
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
- try {
- Object.defineProperty(this, 'isDeleted', {
- get: function () {
- return isDeleted;
- },
- set: function (status) {
- if (!isDeleted && status) self.fire('deleted');
- isDeleted = status;
- if (status) self.isDirty = status;
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
- this.setUmapId(data.id);
- this.setOptions(data);
- this.backupOptions();
- this.connectToMap();
- if (this.displayedOnLoad()) this.show();
- if (!this.umap_id) this.isDirty = true;
- this.onceLoaded(function () {
- this.map.on('moveend', this.fetchRemoteData, this);
- });
- },
-
- displayedOnLoad: function () {
- return ((this.map.datalayersOnLoad && this.umap_id && this.map.datalayersOnLoad.indexOf(this.umap_id.toString()) !== -1) ||
- (!this.map.datalayersOnLoad && this.options.displayOnLoad));
- },
-
- insertBefore: function (other) {
- if (!other) return;
- this.parentPane.insertBefore(this.pane, other.pane);
- },
-
- insertAfter: function (other) {
- if (!other) return;
- this.parentPane.insertBefore(this.pane, other.pane.nextSibling);
- },
-
- bringToTop: function () {
- this.parentPane.appendChild(this.pane);
- },
-
- resetLayer: function (force) {
- if (this.layer && this.options.type === this.layer._type && !force) return;
- var visible = this.isVisible();
- if (this.layer) this.layer.clearLayers();
- // delete this.layer?
- if (visible) this.map.removeLayer(this.layer);
- var Class = L.U.Layer[this.options.type] || L.U.Layer.Default;
- this.layer = new Class(this);
- var filterKeys = this.map.getFilterKeys(),
- filter = this.map.options.filter;
- this.eachLayer(function (layer) {
- if (filter && !layer.matchFilter(filter, filterKeys)) return;
- this.layer.addLayer(layer);
- });
- if (visible) this.map.addLayer(this.layer);
- this.propagateRemote();
- },
-
- eachLayer: function (method, context) {
- for (var i in this._layers) {
- method.call(context || this, this._layers[i]);
- }
- return this;
- },
-
- eachFeature: function (method, context) {
- if (this.layer && this.layer.canBrowse) {
- for (var i = 0; i < this._index.length; i++) {
- method.call(context || this, this._layers[this._index[i]]);
- }
- }
- return this;
- },
-
- fetchData: function () {
- if (!this.umap_id) return;
- this.map.get(this._dataUrl(), {
- callback: function (geojson, response) {
- this._last_modified = response.getResponseHeader('Last-Modified');
- this.fromUmapGeoJSON(geojson);
- this.backupOptions();
- this.fire('loaded');
- },
- context: this
- });
- },
-
- fromGeoJSON: function (geojson) {
- this.addData(geojson);
- this._geojson = geojson;
- this.fire('dataloaded');
- this.fire('datachanged');
- },
-
- fromUmapGeoJSON: function (geojson) {
- if (geojson._storage) geojson._umap_options = geojson._storage; // Retrocompat
- if (geojson._umap_options) this.setOptions(geojson._umap_options);
- if (this.isRemoteLayer()) this.fetchRemoteData();
- else this.fromGeoJSON(geojson);
- this._loaded = true;
- },
-
- clear: function () {
- this.layer.clearLayers();
- this._layers = {};
- this._index = Array();
- if (this._geojson) {
- this.backupData();
- this._geojson = null;
- }
- },
-
- backupData: function () {
- this._geojson_bk = L.Util.CopyJSON(this._geojson);
- },
-
- reindex: function () {
- var features = [];
- this.eachFeature(function (feature) {
- features.push(feature);
- });
- L.Util.sortFeatures(features, this.map.getOption('sortKey'));
- this._index = [];
- for (var i = 0; i < features.length; i++) {
- this._index.push(L.Util.stamp(features[i]));
- }
- },
-
- fetchRemoteData: function () {
- if (!this.isRemoteLayer()) return;
- var from = parseInt(this.options.remoteData.from, 10),
- to = parseInt(this.options.remoteData.to, 10);
- if ((!isNaN(from) && this.map.getZoom() < from) ||
- (!isNaN(to) && this.map.getZoom() > to) ) {
- this.clear();
- return;
- }
- if (!this.options.remoteData.dynamic && this.hasDataLoaded()) return;
- if (!this.isVisible()) return;
- var self = this,
- url = this.map.localizeUrl(this.options.remoteData.url);
- if (this.options.remoteData.proxy) url = this.map.proxyUrl(url, this.options.remoteData.ttl);
- this.map.ajax({
- uri: url,
- verb: 'GET',
- callback: function (raw) {
- self.clear();
- self.rawToGeoJSON(raw, self.options.remoteData.format, function (geojson) {self.fromGeoJSON(geojson);});
- }
- });
- },
-
- onceLoaded: function (callback, context) {
- if (this.isLoaded()) callback.call(context || this, this);
- else this.once('loaded', callback, context);
- return this;
- },
-
- onceDataLoaded: function (callback, context) {
- if (this.hasDataLoaded()) callback.call(context || this, this);
- else this.once('dataloaded', callback, context);
- return this;
- },
-
- isLoaded: function () {
- return !this.umap_id || this._loaded;
- },
-
- hasDataLoaded: function () {
- return this._geojson !== null;
- },
-
- setUmapId: function (id) {
- // Datalayer is null when listening creation form
- if (!this.umap_id && id) this.umap_id = id;
- },
-
- backupOptions: function () {
- this._backupOptions = L.Util.CopyJSON(this.options);
- },
-
- resetOptions: function () {
- this.options = L.Util.CopyJSON(this._backupOptions);
- },
-
- setOptions: function (options) {
- this.options = L.Util.CopyJSON(L.U.DataLayer.prototype.options); // Start from fresh.
- this.updateOptions(options);
- },
-
- updateOptions: function (options) {
- L.Util.setOptions(this, options);
- this.resetLayer();
- },
-
- connectToMap: function () {
- var id = L.stamp(this);
- if (!this.map.datalayers[id]) {
- this.map.datalayers[id] = this;
- if (L.Util.indexOf(this.map.datalayers_index, this) === -1) this.map.datalayers_index.push(this);
- }
- this.map.updateDatalayersControl();
- },
-
- _dataUrl: function() {
- var template = this.map.options.urls.datalayer_view,
- url = L.Util.template(template, {'pk': this.umap_id, 'map_id': this.map.options.umap_id});
- // No browser cache for owners/editors.
- if (this.map.options.allowEdit) url = url + '?' + Date.now();
- return url;
- },
-
- isRemoteLayer: function () {
- return !!(this.options.remoteData && this.options.remoteData.url && this.options.remoteData.format);
- },
-
- isClustered: function () {
- return this.options.type === 'Cluster';
- },
-
- addLayer: function (feature) {
- var id = L.stamp(feature);
- feature.connectToDataLayer(this);
- this._index.push(id);
- this._layers[id] = feature;
- this.layer.addLayer(feature);
- this.indexProperties(feature);
- if (this.hasDataLoaded()) this.fire('datachanged');
- this.map.features_index[feature.getSlug()] = feature;
- },
-
- removeLayer: function (feature) {
- var id = L.stamp(feature);
- feature.disconnectFromDataLayer(this);
- this._index.splice(this._index.indexOf(id), 1);
- delete this._layers[id];
- this.layer.removeLayer(feature);
- delete this.map.features_index[feature.getSlug()];
- if (this.hasDataLoaded()) this.fire('datachanged');
- },
-
- indexProperties: function (feature) {
- for (var i in feature.properties) if (typeof feature.properties[i] !== 'object') this.indexProperty(i);
- },
-
- indexProperty: function (name) {
- if (!name) return;
- if (name.indexOf('_') === 0) return;
- if (L.Util.indexOf(this._propertiesIndex, name) !== -1) return;
- this._propertiesIndex.push(name);
- },
-
- deindexProperty: function (name) {
- var idx = this._propertiesIndex.indexOf(name);
- if (idx !== -1) this._propertiesIndex.splice(idx, 1);
- },
-
- addData: function (geojson) {
- try {
- // Do not fail if remote data is somehow invalid,
- // otherwise the layer becomes uneditable.
- this.geojsonToFeatures(geojson);
- } catch (err) {
- console.log("Error with DataLayer", this.umap_id);
- console.error(err);
- }
- },
-
- addRawData: function (c, type) {
- var self = this;
- this.rawToGeoJSON(c, type, function (geojson) {
- self.addData(geojson);
- });
- },
-
- rawToGeoJSON: function (c, type, callback) {
- var self = this;
- var toDom = function (x) {
- return (new DOMParser()).parseFromString(x, 'text/xml');
- };
-
- // TODO add a duck typing guessType
- if (type === 'csv') {
- csv2geojson.csv2geojson(c, {
- delimiter: 'auto',
- includeLatLon: false
- }, function(err, result) {
- if (err) {
- var message;
- if (err.type === 'Error') {
- message = err.message;
- } else {
- message = L._('{count} errors during import: {message}', {count: err.length, message: err[0].message});
- }
- self.map.ui.alert({content: message, level: 'error', duration: 10000});
- console.log(err);
- }
- if (result && result.features.length) {
- callback(result);
- }
- });
- } else if (type === 'gpx') {
- callback(toGeoJSON.gpx(toDom(c)));
- } else if (type === 'georss') {
- callback(GeoRSSToGeoJSON(toDom(c)));
- } else if (type === 'kml') {
- callback(toGeoJSON.kml(toDom(c)));
- } else if (type === 'osm') {
- var d;
- try {
- d = JSON.parse(c);
- } catch (e) {
- d = toDom(c);
- }
- callback(osmtogeojson(d, {flatProperties: true}));
- } else if (type === 'geojson') {
- try {
- var gj = JSON.parse(c);
- callback(gj);
- } catch(err) {
- self.map.ui.alert({content: 'Invalid JSON file: ' + err});
- return;
- }
- }
- },
-
- geojsonToFeatures: function (geojson) {
- if (!geojson) return;
- var features = geojson instanceof Array ? geojson : geojson.features,
- i, len, latlng, latlngs;
-
- if (features) {
- L.Util.sortFeatures(features, this.map.getOption('sortKey'));
- for (i = 0, len = features.length; i < len; i++) {
- this.geojsonToFeatures(features[i]);
- }
- return this;
- }
-
- var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson;
- if (!geometry) return; // null geometry is valid geojson.
- var coords = geometry.coordinates,
- layer, tmp;
-
- switch (geometry.type) {
- case 'Point':
- try {
- latlng = L.GeoJSON.coordsToLatLng(coords);
- } catch (e) {
- console.error('Invalid latlng object from', coords);
- break;
- }
- layer = this._pointToLayer(geojson, latlng);
- break;
-
- case 'MultiLineString':
- case 'LineString':
- latlngs = L.GeoJSON.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1);
- if (!latlngs.length) break;
- layer = this._lineToLayer(geojson, latlngs);
- break;
-
- case 'MultiPolygon':
- case 'Polygon':
- latlngs = L.GeoJSON.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2);
- layer = this._polygonToLayer(geojson, latlngs);
- break;
- case 'GeometryCollection':
- return this.geojsonToFeatures(geometry.geometries);
-
- default:
- this.map.ui.alert({content: L._('Skipping unknown geometry.type: {type}', {type: geometry.type || 'undefined'}), level: 'error'});
- }
- if (layer) {
- this.addLayer(layer);
- return layer;
- }
- },
-
- _pointToLayer: function(geojson, latlng) {
- return new L.U.Marker(
- this.map,
- latlng,
- {'geojson': geojson, 'datalayer': this}
- );
- },
-
- _lineToLayer: function(geojson, latlngs) {
- return new L.U.Polyline(
- this.map,
- latlngs,
- {'geojson': geojson, 'datalayer': this, color: null}
- );
- },
-
- _polygonToLayer: function(geojson, latlngs) {
- // Ensure no empty hole
- // for (var i = latlngs.length - 1; i > 0; i--) {
- // if (!latlngs.slice()[i].length) latlngs.splice(i, 1);
- // }
- return new L.U.Polygon(
- this.map,
- latlngs,
- {'geojson': geojson, 'datalayer': this}
- );
- },
-
- importRaw: function (raw, type) {
- this.addRawData(raw, type);
- this.isDirty = true;
- this.zoomTo();
- },
-
- importFromFiles: function (files, type) {
- for (var i = 0, f; f = files[i]; i++) {
- this.importFromFile(f, type);
- }
- },
-
- importFromFile: function (f, type) {
- var reader = new FileReader(),
- self = this;
- type = type || L.Util.detectFileType(f);
- reader.readAsText(f);
- reader.onload = function (e) {
- self.importRaw(e.target.result, type);
- };
- },
-
- importFromUrl: function (url, type) {
- url = this.map.localizeUrl(url);
- var self = this;
- this.map.xhr._ajax({verb: 'GET', uri: url, callback: function (data) {
- self.importRaw(data, type);
- }});
- },
-
- 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');
- },
-
- getDeleteUrl: function () {
- return L.Util.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, {'pk': this.umap_id, 'map_id': this.map.options.umap_id});
- },
-
- getVersionUrl: function (name) {
- return L.Util.template(this.map.options.urls.datalayer_version, {'pk': this.umap_id, 'map_id': this.map.options.umap_id, name: name});
- },
-
- _delete: function () {
- this.isDeleted = true;
- this.erase();
- },
-
- empty: function () {
- if (this.isRemoteLayer()) return;
- this.clear();
- this.isDirty = true;
- },
-
- clone: function () {
- var options = L.Util.CopyJSON(this.options);
- options.name = L._('Clone of {name}', {name: this.options.name});
- delete options.id;
- var geojson = L.Util.CopyJSON(this._geojson),
- datalayer = this.map.createDataLayer(options);
- datalayer.fromGeoJSON(geojson);
- return datalayer;
- },
-
- erase: function () {
- this.hide();
- delete this.map.datalayers[L.stamp(this)];
- this.map.datalayers_index.splice(this.getRank(), 1);
- this.parentPane.removeChild(this.pane);
- this.map.updateDatalayersControl();
- this.fire('erase');
- this._leaflet_events_bk = this._leaflet_events;
- this.off();
- this.clear();
- delete this._loaded;
- },
-
- reset: function () {
- if (!this.umap_id) this.erase();
-
- this.resetOptions();
- this.parentPane.appendChild(this.pane);
- if (this._leaflet_events_bk && !this._leaflet_events) {
- this._leaflet_events = this._leaflet_events_bk;
- }
- this.clear();
- this.hide();
- if (this.isRemoteLayer()) this.fetchRemoteData();
- else if (this._geojson_bk) this.fromGeoJSON(this._geojson_bk);
- this._loaded = true;
- this.show();
- this.isDirty = false;
- },
-
- redraw: function () {
- this.hide();
- this.show();
- },
-
- edit: function () {
- if(!this.map.editEnabled || !this.isLoaded()) {return;}
- var container = L.DomUtil.create('div', 'umap-layer-properties-container'),
- metadataFields = [
- 'options.name',
- 'options.description',
- ['options.type', {handler: 'LayerTypeChooser', label: L._('Type of layer')}],
- ['options.displayOnLoad', {label: L._('Display on load'), handler: 'Switch'}],
- ['options.browsable', {label: L._('Data is browsable'), handler: 'Switch', helpEntries: 'browsable'}]
- ];
- var title = L.DomUtil.add('h3', '', container, L._('Layer properties'));
- var builder = new L.U.FormBuilder(this, metadataFields, {
- callback: function (e) {
- this.map.updateDatalayersControl();
- if (e.helper.field === 'options.type') {
- this.resetLayer();
- this.edit();
- }
- }
- });
- container.appendChild(builder.build());
-
- var shapeOptions = [
- 'options.color',
- 'options.iconClass',
- 'options.iconUrl',
- 'options.opacity',
- 'options.stroke',
- 'options.weight',
- 'options.fill',
- 'options.fillColor',
- 'options.fillOpacity',
- ];
-
- shapeOptions = shapeOptions.concat(this.layer.getEditableOptions());
-
- var redrawCallback = function (field) {
- this.hide();
- this.layer.postUpdate(field);
- this.show();
- };
-
- builder = new L.U.FormBuilder(this, shapeOptions, {
- id: 'datalayer-advanced-properties',
- callback: redrawCallback
- });
- var shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'));
- shapeProperties.appendChild(builder.build());
-
- var optionsFields = [
- 'options.smoothFactor',
- 'options.dashArray',
- 'options.zoomTo',
- 'options.labelKey'
- ];
-
- optionsFields = optionsFields.concat(this.layer.getEditableOptions());
-
- builder = new L.U.FormBuilder(this, optionsFields, {
- id: 'datalayer-advanced-properties',
- callback: redrawCallback
- });
- var advancedProperties = L.DomUtil.createFieldset(container, L._('Advanced properties'));
- advancedProperties.appendChild(builder.build());
-
- var popupFields = [
- 'options.popupShape',
- 'options.popupTemplate',
- 'options.popupContentTemplate',
- 'options.showLabel',
- 'options.labelDirection',
- 'options.labelInteractive',
- ];
- builder = new L.U.FormBuilder(this, popupFields, {callback: redrawCallback});
- var popupFieldset = L.DomUtil.createFieldset(container, L._('Interaction options'));
- popupFieldset.appendChild(builder.build());
-
- if (!L.Util.isObject(this.options.remoteData)) {
- this.options.remoteData = {};
- }
- var remoteDataFields = [
- ['options.remoteData.url', {handler: 'Url', label: L._('Url'), helpEntries: 'formatURL'}],
- ['options.remoteData.format', {handler: 'DataFormat', label: L._('Format')}],
- ['options.remoteData.from', {label: L._('From zoom'), helpText: L._('Optional.')}],
- ['options.remoteData.to', {label: L._('To zoom'), helpText: L._('Optional.')}],
- ['options.remoteData.dynamic', {handler: 'Switch', label: L._('Dynamic'), helpEntries: 'dynamicRemoteData'}],
- ['options.remoteData.licence', {label: L._('Licence'), helpText: L._('Please be sure the licence is compliant with your use.')}]
- ];
- if (this.map.options.urls.ajax_proxy) {
- remoteDataFields.push(['options.remoteData.proxy', {handler: 'Switch', label: L._('Proxy request'), helpEntries: 'proxyRemoteData'}]);
- remoteDataFields.push(['options.remoteData.ttl', {handler: 'ProxyTTLSelect', label: L._('Cache proxied request')}]);
- }
-
- var remoteDataContainer = L.DomUtil.createFieldset(container, L._('Remote data'));
- builder = new L.U.FormBuilder(this, remoteDataFields);
- remoteDataContainer.appendChild(builder.build());
-
- if (this.map.options.urls.datalayer_versions) this.buildVersionsFieldset(container);
-
- var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'));
- var advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions);
- var deleteLink = L.DomUtil.create('a', 'button delete_datalayer_button umap-delete', advancedButtons);
- deleteLink.textContent = L._('Delete');
- deleteLink.href = '#';
- L.DomEvent.on(deleteLink, 'click', L.DomEvent.stop)
- .on(deleteLink, 'click', function () {
- this._delete();
- this.map.ui.closePanel();
- }, this);
- if (!this.isRemoteLayer()) {
- var emptyLink = L.DomUtil.create('a', 'button umap-empty', advancedButtons);
- emptyLink.textContent = L._('Empty');
- emptyLink.href = '#';
- L.DomEvent.on(emptyLink, 'click', L.DomEvent.stop)
- .on(emptyLink, 'click', this.empty, this);
- }
- var cloneLink = L.DomUtil.create('a', 'button umap-clone', advancedButtons);
- cloneLink.textContent = L._('Clone');
- cloneLink.href = '#';
- L.DomEvent.on(cloneLink, 'click', L.DomEvent.stop)
- .on(cloneLink, 'click', function () {
- var datalayer = this.clone();
- datalayer.edit();
- }, this);
- if (this.umap_id) {
- var download = L.DomUtil.create('a', 'button umap-download', advancedButtons);
- download.textContent = L._('Download');
- download.href = this._dataUrl();
- download.target = '_blank';
- }
- this.map.ui.openPanel({data: {html: container}, className: 'dark'});
-
- },
-
- getOption: function (option) {
- if (L.Util.usableOption(this.options, option)) return this.options[option];
- else return this.map.getOption(option);
- },
-
- buildVersionsFieldset: function (container) {
-
- var appendVersion = function (data) {
- var date = new Date(parseInt(data.at, 10));
- var content = date.toLocaleDateString(L.locale) + ' (' + parseInt(data.size) / 1000 + 'Kb)';
- var el = L.DomUtil.create('div', 'umap-datalayer-version', versionsContainer);
- var a = L.DomUtil.create('a', '', el);
- L.DomUtil.add('span', '', el, content);
- a.href = '#';
- a.title = L._('Restore this version');
- L.DomEvent.on(a, 'click', L.DomEvent.stop)
- .on(a, 'click', function () {
- this.restore(data.name);
- }, this);
- };
-
- var versionsContainer = L.DomUtil.createFieldset(container, L._('Versions'), {callback: function () {
- this.map.xhr.get(this.getVersionsUrl(), {
- callback: function (data) {
- for (var i = 0; i < data.versions.length; i++) {
- appendVersion.call(this, data.versions[i]);
- };
- },
- context: this
- });
- }, context: this});
- },
-
- restore: function (version) {
- if (!this.map.editEnabled) return;
- if (!confirm(L._('Are you sure you want to restore this version?'))) return;
- this.map.xhr.get(this.getVersionUrl(version), {
- callback: function (geojson) {
- if (geojson._storage) geojson._umap_options = geojson._storage; // Retrocompat.
- if (geojson._umap_options) this.setOptions(geojson._umap_options);
- this.empty();
- if (this.isRemoteLayer()) this.fetchRemoteData();
- else this.addData(geojson);
- this.isDirty = true;
- },
- context: this
+ options: {
+ displayOnLoad: true,
+ browsable: true,
+ },
+
+ initialize: function (map, data) {
+ this.map = map
+ this._index = Array()
+ this._layers = {}
+ this._geojson = null
+ this._propertiesIndex = []
+
+ this.parentPane = this.map.getPane('overlayPane')
+ this.pane = this.map.createPane('datalayer' + L.stamp(this), this.parentPane)
+ this.pane.dataset.id = L.stamp(this)
+ this.renderer = L.svg({ pane: this.pane })
+
+ var isDirty = false,
+ isDeleted = false,
+ self = this
+ try {
+ Object.defineProperty(this, 'isDirty', {
+ get: function () {
+ return isDirty
+ },
+ set: function (status) {
+ if (!isDirty && status) self.fire('dirty')
+ isDirty = status
+ if (status) {
+ self.map.addDirtyDatalayer(self)
+ // A layer can be made dirty by indirect action (like dragging layers)
+ // we need to have it loaded before saving it.
+ if (!self.isLoaded()) self.fetchData()
+ } else {
+ self.map.removeDirtyDatalayer(self)
+ self.isDeleted = false
+ }
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
+ }
+ try {
+ Object.defineProperty(this, 'isDeleted', {
+ get: function () {
+ return isDeleted
+ },
+ set: function (status) {
+ if (!isDeleted && status) self.fire('deleted')
+ isDeleted = status
+ if (status) self.isDirty = status
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
+ }
+ this.setUmapId(data.id)
+ this.setOptions(data)
+ this.backupOptions()
+ this.connectToMap()
+ if (this.displayedOnLoad()) this.show()
+ if (!this.umap_id) this.isDirty = true
+ this.onceLoaded(function () {
+ this.map.on('moveend', this.fetchRemoteData, this)
+ })
+ },
+
+ displayedOnLoad: function () {
+ return (
+ (this.map.datalayersOnLoad &&
+ this.umap_id &&
+ this.map.datalayersOnLoad.indexOf(this.umap_id.toString()) !== -1) ||
+ (!this.map.datalayersOnLoad && this.options.displayOnLoad)
+ )
+ },
+
+ insertBefore: function (other) {
+ if (!other) return
+ this.parentPane.insertBefore(this.pane, other.pane)
+ },
+
+ insertAfter: function (other) {
+ if (!other) return
+ this.parentPane.insertBefore(this.pane, other.pane.nextSibling)
+ },
+
+ bringToTop: function () {
+ this.parentPane.appendChild(this.pane)
+ },
+
+ resetLayer: function (force) {
+ if (this.layer && this.options.type === this.layer._type && !force) return
+ var visible = this.isVisible()
+ if (this.layer) this.layer.clearLayers()
+ // delete this.layer?
+ if (visible) this.map.removeLayer(this.layer)
+ var Class = L.U.Layer[this.options.type] || L.U.Layer.Default
+ this.layer = new Class(this)
+ var filterKeys = this.map.getFilterKeys(),
+ filter = this.map.options.filter
+ this.eachLayer(function (layer) {
+ if (filter && !layer.matchFilter(filter, filterKeys)) return
+ this.layer.addLayer(layer)
+ })
+ if (visible) this.map.addLayer(this.layer)
+ this.propagateRemote()
+ },
+
+ eachLayer: function (method, context) {
+ for (var i in this._layers) {
+ method.call(context || this, this._layers[i])
+ }
+ return this
+ },
+
+ eachFeature: function (method, context) {
+ if (this.layer && this.layer.canBrowse) {
+ for (var i = 0; i < this._index.length; i++) {
+ method.call(context || this, this._layers[this._index[i]])
+ }
+ }
+ return this
+ },
+
+ fetchData: function () {
+ if (!this.umap_id) return
+ this.map.get(this._dataUrl(), {
+ callback: function (geojson, response) {
+ this._last_modified = response.getResponseHeader('Last-Modified')
+ this.fromUmapGeoJSON(geojson)
+ this.backupOptions()
+ this.fire('loaded')
+ },
+ context: this,
+ })
+ },
+
+ fromGeoJSON: function (geojson) {
+ this.addData(geojson)
+ this._geojson = geojson
+ this.fire('dataloaded')
+ this.fire('datachanged')
+ },
+
+ fromUmapGeoJSON: function (geojson) {
+ if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat
+ if (geojson._umap_options) this.setOptions(geojson._umap_options)
+ if (this.isRemoteLayer()) this.fetchRemoteData()
+ else this.fromGeoJSON(geojson)
+ this._loaded = true
+ },
+
+ clear: function () {
+ this.layer.clearLayers()
+ this._layers = {}
+ this._index = Array()
+ if (this._geojson) {
+ this.backupData()
+ this._geojson = null
+ }
+ },
+
+ backupData: function () {
+ this._geojson_bk = L.Util.CopyJSON(this._geojson)
+ },
+
+ reindex: function () {
+ var features = []
+ this.eachFeature(function (feature) {
+ features.push(feature)
+ })
+ L.Util.sortFeatures(features, this.map.getOption('sortKey'))
+ this._index = []
+ for (var i = 0; i < features.length; i++) {
+ this._index.push(L.Util.stamp(features[i]))
+ }
+ },
+
+ fetchRemoteData: function () {
+ if (!this.isRemoteLayer()) return
+ var from = parseInt(this.options.remoteData.from, 10),
+ to = parseInt(this.options.remoteData.to, 10)
+ if (
+ (!isNaN(from) && this.map.getZoom() < from) ||
+ (!isNaN(to) && this.map.getZoom() > to)
+ ) {
+ this.clear()
+ return
+ }
+ if (!this.options.remoteData.dynamic && this.hasDataLoaded()) return
+ if (!this.isVisible()) return
+ var self = this,
+ url = this.map.localizeUrl(this.options.remoteData.url)
+ if (this.options.remoteData.proxy)
+ url = this.map.proxyUrl(url, this.options.remoteData.ttl)
+ this.map.ajax({
+ uri: url,
+ verb: 'GET',
+ callback: function (raw) {
+ self.clear()
+ self.rawToGeoJSON(raw, self.options.remoteData.format, function (geojson) {
+ self.fromGeoJSON(geojson)
})
- },
+ },
+ })
+ },
- featuresToGeoJSON: function () {
- var features = [];
- this.eachLayer(function (layer) {
- features.push(layer.toGeoJSON());
- });
- return features;
- },
+ onceLoaded: function (callback, context) {
+ if (this.isLoaded()) callback.call(context || this, this)
+ else this.once('loaded', callback, context)
+ return this
+ },
- show: function () {
- if(!this.isLoaded()) this.fetchData();
- this.map.addLayer(this.layer);
- this.fire('show');
- },
+ onceDataLoaded: function (callback, context) {
+ if (this.hasDataLoaded()) callback.call(context || this, this)
+ else this.once('dataloaded', callback, context)
+ return this
+ },
- hide: function () {
- this.map.removeLayer(this.layer);
- this.fire('hide');
- },
+ isLoaded: function () {
+ return !this.umap_id || this._loaded
+ },
- toggle: function () {
- if (!this.isVisible()) this.show();
- else this.hide();
- },
+ hasDataLoaded: function () {
+ return this._geojson !== null
+ },
- zoomTo: function () {
- if (!this.isVisible()) return;
- var bounds = this.layer.getBounds();
- if (bounds.isValid()) this.map.fitBounds(bounds);
- },
+ setUmapId: function (id) {
+ // Datalayer is null when listening creation form
+ if (!this.umap_id && id) this.umap_id = id
+ },
- allowBrowse: function () {
- return !!this.options.browsable && this.canBrowse() && this.isVisible();
- },
+ backupOptions: function () {
+ this._backupOptions = L.Util.CopyJSON(this.options)
+ },
- hasData: function () {
- return !!this._index.length;
- },
+ resetOptions: function () {
+ this.options = L.Util.CopyJSON(this._backupOptions)
+ },
- isVisible: function () {
- return this.map.hasLayer(this.layer);
- },
+ setOptions: function (options) {
+ this.options = L.Util.CopyJSON(L.U.DataLayer.prototype.options) // Start from fresh.
+ this.updateOptions(options)
+ },
- canBrowse: function () {
- return this.layer && this.layer.canBrowse;
- },
+ updateOptions: function (options) {
+ L.Util.setOptions(this, options)
+ this.resetLayer()
+ },
- getFeatureByIndex: function (index) {
- if (index === -1) index = this._index.length - 1;
- var id = this._index[index];
- return this._layers[id];
- },
+ connectToMap: function () {
+ var id = L.stamp(this)
+ if (!this.map.datalayers[id]) {
+ this.map.datalayers[id] = this
+ if (L.Util.indexOf(this.map.datalayers_index, this) === -1)
+ this.map.datalayers_index.push(this)
+ }
+ this.map.updateDatalayersControl()
+ },
- getNextFeature: function (feature) {
- var id = this._index.indexOf(L.stamp(feature)),
- nextId = this._index[id + 1];
- return nextId? this._layers[nextId]: this.getNextBrowsable().getFeatureByIndex(0);
- },
+ _dataUrl: function () {
+ var template = this.map.options.urls.datalayer_view,
+ url = L.Util.template(template, {
+ pk: this.umap_id,
+ map_id: this.map.options.umap_id,
+ })
+ // No browser cache for owners/editors.
+ if (this.map.options.allowEdit) url = url + '?' + Date.now()
+ return url
+ },
- getPreviousFeature: function (feature) {
- if (this._index <= 1) { return null; }
- var id = this._index.indexOf(L.stamp(feature)),
- previousId = this._index[id - 1];
- return previousId? this._layers[previousId]: this.getPreviousBrowsable().getFeatureByIndex(-1);
- },
+ isRemoteLayer: function () {
+ return !!(
+ this.options.remoteData &&
+ this.options.remoteData.url &&
+ this.options.remoteData.format
+ )
+ },
- getPreviousBrowsable: function () {
- var id = this.getRank(), next, index = this.map.datalayers_index;
- while(id = index[++id] ? id : 0, next = index[id]) {
- if (next === this || (next.allowBrowse() && next.hasData())) break;
- }
- return next;
- },
+ isClustered: function () {
+ return this.options.type === 'Cluster'
+ },
- getNextBrowsable: function () {
- var id = this.getRank(), prev, index = this.map.datalayers_index;
- while(id = index[--id] ? id : index.length - 1, prev = index[id]) {
- if (prev === this || (prev.allowBrowse() && prev.hasData())) break;
- }
- return prev;
- },
+ addLayer: function (feature) {
+ var id = L.stamp(feature)
+ feature.connectToDataLayer(this)
+ this._index.push(id)
+ this._layers[id] = feature
+ this.layer.addLayer(feature)
+ this.indexProperties(feature)
+ if (this.hasDataLoaded()) this.fire('datachanged')
+ this.map.features_index[feature.getSlug()] = feature
+ },
- umapGeoJSON: function () {
- return {
- type: 'FeatureCollection',
- features: this.isRemoteLayer() ? [] : this.featuresToGeoJSON(),
- _umap_options: this.options
- };
- },
+ removeLayer: function (feature) {
+ var id = L.stamp(feature)
+ feature.disconnectFromDataLayer(this)
+ this._index.splice(this._index.indexOf(id), 1)
+ delete this._layers[id]
+ this.layer.removeLayer(feature)
+ delete this.map.features_index[feature.getSlug()]
+ if (this.hasDataLoaded()) this.fire('datachanged')
+ },
- metadata: function () {
- return {
- id: this.umap_id,
- name: this.options.name,
- displayOnLoad: this.options.displayOnLoad
- }
- },
+ indexProperties: function (feature) {
+ for (var i in feature.properties)
+ if (typeof feature.properties[i] !== 'object') this.indexProperty(i)
+ },
- getRank: function () {
- return this.map.datalayers_index.indexOf(this);
- },
+ indexProperty: function (name) {
+ if (!name) return
+ if (name.indexOf('_') === 0) return
+ if (L.Util.indexOf(this._propertiesIndex, name) !== -1) return
+ this._propertiesIndex.push(name)
+ },
- save: function () {
- if (this.isDeleted) return this.saveDelete();
- if (!this.isLoaded()) {return;}
- var geojson = this.umapGeoJSON();
- var formData = new FormData();
- formData.append('name', this.options.name);
- formData.append('display_on_load', !!this.options.displayOnLoad);
- formData.append('rank', this.getRank());
- // Filename support is shaky, don't do it for now.
- var blob = new Blob([JSON.stringify(geojson)], {type: 'application/json'});
- formData.append('geojson', blob);
- this.map.post(this.getSaveUrl(), {
- data: formData,
- callback: function (data, response) {
- this._geojson = geojson;
- this._last_modified = response.getResponseHeader('Last-Modified');
- this.setUmapId(data.id);
- this.updateOptions(data);
- this.backupOptions();
- this.connectToMap();
- this._loaded = true;
- this.redraw(); // Needed for reordering features
- this.isDirty = false;
- this.map.continueSaving();
- },
- context: this,
- headers: this._last_modified ? {'If-Unmodified-Since': this._last_modified} : {}
- });
- },
+ deindexProperty: function (name) {
+ var idx = this._propertiesIndex.indexOf(name)
+ if (idx !== -1) this._propertiesIndex.splice(idx, 1)
+ },
- saveDelete: function () {
- var callback = function () {
- this.isDirty = false;
- this.map.continueSaving();
- }
- if (!this.umap_id) return callback.call(this);
- this.map.xhr.post(this.getDeleteUrl(), {
- callback: callback,
- context: this
- });
- },
+ addData: function (geojson) {
+ try {
+ // Do not fail if remote data is somehow invalid,
+ // otherwise the layer becomes uneditable.
+ this.geojsonToFeatures(geojson)
+ } catch (err) {
+ console.log('Error with DataLayer', this.umap_id)
+ console.error(err)
+ }
+ },
- getMap: function () {
- return this.map;
- },
+ addRawData: function (c, type) {
+ var self = this
+ this.rawToGeoJSON(c, type, function (geojson) {
+ self.addData(geojson)
+ })
+ },
- getName: function () {
- return this.options.name || L._('Untitled layer');
- },
-
- tableEdit: function () {
- if (this.isRemoteLayer() || !this.isVisible()) return;
- var editor = new L.U.TableEditor(this);
- editor.edit();
+ rawToGeoJSON: function (c, type, callback) {
+ var self = this
+ var toDom = function (x) {
+ return new DOMParser().parseFromString(x, 'text/xml')
}
-});
+ // TODO add a duck typing guessType
+ if (type === 'csv') {
+ csv2geojson.csv2geojson(
+ c,
+ {
+ delimiter: 'auto',
+ includeLatLon: false,
+ },
+ function (err, result) {
+ if (err) {
+ var message
+ if (err.type === 'Error') {
+ message = err.message
+ } else {
+ message = L._('{count} errors during import: {message}', {
+ count: err.length,
+ message: err[0].message,
+ })
+ }
+ self.map.ui.alert({ content: message, level: 'error', duration: 10000 })
+ console.log(err)
+ }
+ if (result && result.features.length) {
+ callback(result)
+ }
+ }
+ )
+ } else if (type === 'gpx') {
+ callback(toGeoJSON.gpx(toDom(c)))
+ } else if (type === 'georss') {
+ callback(GeoRSSToGeoJSON(toDom(c)))
+ } else if (type === 'kml') {
+ callback(toGeoJSON.kml(toDom(c)))
+ } else if (type === 'osm') {
+ var d
+ try {
+ d = JSON.parse(c)
+ } catch (e) {
+ d = toDom(c)
+ }
+ callback(osmtogeojson(d, { flatProperties: true }))
+ } else if (type === 'geojson') {
+ try {
+ var gj = JSON.parse(c)
+ callback(gj)
+ } catch (err) {
+ self.map.ui.alert({ content: 'Invalid JSON file: ' + err })
+ return
+ }
+ }
+ },
+
+ geojsonToFeatures: function (geojson) {
+ if (!geojson) return
+ var features = geojson instanceof Array ? geojson : geojson.features,
+ i,
+ len,
+ latlng,
+ latlngs
+
+ if (features) {
+ L.Util.sortFeatures(features, this.map.getOption('sortKey'))
+ for (i = 0, len = features.length; i < len; i++) {
+ this.geojsonToFeatures(features[i])
+ }
+ return this
+ }
+
+ var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson
+ if (!geometry) return // null geometry is valid geojson.
+ var coords = geometry.coordinates,
+ layer,
+ tmp
+
+ switch (geometry.type) {
+ case 'Point':
+ try {
+ latlng = L.GeoJSON.coordsToLatLng(coords)
+ } catch (e) {
+ console.error('Invalid latlng object from', coords)
+ break
+ }
+ layer = this._pointToLayer(geojson, latlng)
+ break
+
+ case 'MultiLineString':
+ case 'LineString':
+ latlngs = L.GeoJSON.coordsToLatLngs(
+ coords,
+ geometry.type === 'LineString' ? 0 : 1
+ )
+ if (!latlngs.length) break
+ layer = this._lineToLayer(geojson, latlngs)
+ break
+
+ case 'MultiPolygon':
+ case 'Polygon':
+ latlngs = L.GeoJSON.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2)
+ layer = this._polygonToLayer(geojson, latlngs)
+ break
+ case 'GeometryCollection':
+ return this.geojsonToFeatures(geometry.geometries)
+
+ default:
+ this.map.ui.alert({
+ content: L._('Skipping unknown geometry.type: {type}', {
+ type: geometry.type || 'undefined',
+ }),
+ level: 'error',
+ })
+ }
+ if (layer) {
+ this.addLayer(layer)
+ return layer
+ }
+ },
+
+ _pointToLayer: function (geojson, latlng) {
+ return new L.U.Marker(this.map, latlng, { geojson: geojson, datalayer: this })
+ },
+
+ _lineToLayer: function (geojson, latlngs) {
+ return new L.U.Polyline(this.map, latlngs, {
+ geojson: geojson,
+ datalayer: this,
+ color: null,
+ })
+ },
+
+ _polygonToLayer: function (geojson, latlngs) {
+ // Ensure no empty hole
+ // for (var i = latlngs.length - 1; i > 0; i--) {
+ // if (!latlngs.slice()[i].length) latlngs.splice(i, 1);
+ // }
+ return new L.U.Polygon(this.map, latlngs, { geojson: geojson, datalayer: this })
+ },
+
+ importRaw: function (raw, type) {
+ this.addRawData(raw, type)
+ this.isDirty = true
+ this.zoomTo()
+ },
+
+ importFromFiles: function (files, type) {
+ for (var i = 0, f; (f = files[i]); i++) {
+ this.importFromFile(f, type)
+ }
+ },
+
+ importFromFile: function (f, type) {
+ var reader = new FileReader(),
+ self = this
+ type = type || L.Util.detectFileType(f)
+ reader.readAsText(f)
+ reader.onload = function (e) {
+ self.importRaw(e.target.result, type)
+ }
+ },
+
+ importFromUrl: function (url, type) {
+ url = this.map.localizeUrl(url)
+ var self = this
+ this.map.xhr._ajax({
+ verb: 'GET',
+ uri: url,
+ callback: function (data) {
+ self.importRaw(data, type)
+ },
+ })
+ },
+
+ 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')
+ },
+
+ getDeleteUrl: function () {
+ return L.Util.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, {
+ pk: this.umap_id,
+ map_id: this.map.options.umap_id,
+ })
+ },
+
+ getVersionUrl: function (name) {
+ return L.Util.template(this.map.options.urls.datalayer_version, {
+ pk: this.umap_id,
+ map_id: this.map.options.umap_id,
+ name: name,
+ })
+ },
+
+ _delete: function () {
+ this.isDeleted = true
+ this.erase()
+ },
+
+ empty: function () {
+ if (this.isRemoteLayer()) return
+ this.clear()
+ this.isDirty = true
+ },
+
+ clone: function () {
+ var options = L.Util.CopyJSON(this.options)
+ options.name = L._('Clone of {name}', { name: this.options.name })
+ delete options.id
+ var geojson = L.Util.CopyJSON(this._geojson),
+ datalayer = this.map.createDataLayer(options)
+ datalayer.fromGeoJSON(geojson)
+ return datalayer
+ },
+
+ erase: function () {
+ this.hide()
+ delete this.map.datalayers[L.stamp(this)]
+ this.map.datalayers_index.splice(this.getRank(), 1)
+ this.parentPane.removeChild(this.pane)
+ this.map.updateDatalayersControl()
+ this.fire('erase')
+ this._leaflet_events_bk = this._leaflet_events
+ this.off()
+ this.clear()
+ delete this._loaded
+ },
+
+ reset: function () {
+ if (!this.umap_id) this.erase()
+
+ this.resetOptions()
+ this.parentPane.appendChild(this.pane)
+ if (this._leaflet_events_bk && !this._leaflet_events) {
+ this._leaflet_events = this._leaflet_events_bk
+ }
+ this.clear()
+ this.hide()
+ if (this.isRemoteLayer()) this.fetchRemoteData()
+ else if (this._geojson_bk) this.fromGeoJSON(this._geojson_bk)
+ this._loaded = true
+ this.show()
+ this.isDirty = false
+ },
+
+ redraw: function () {
+ this.hide()
+ this.show()
+ },
+
+ edit: function () {
+ if (!this.map.editEnabled || !this.isLoaded()) {
+ return
+ }
+ var container = L.DomUtil.create('div', 'umap-layer-properties-container'),
+ metadataFields = [
+ 'options.name',
+ 'options.description',
+ ['options.type', { handler: 'LayerTypeChooser', label: L._('Type of layer') }],
+ ['options.displayOnLoad', { label: L._('Display on load'), handler: 'Switch' }],
+ [
+ 'options.browsable',
+ {
+ label: L._('Data is browsable'),
+ handler: 'Switch',
+ helpEntries: 'browsable',
+ },
+ ],
+ ]
+ var title = L.DomUtil.add('h3', '', container, L._('Layer properties'))
+ var builder = new L.U.FormBuilder(this, metadataFields, {
+ callback: function (e) {
+ this.map.updateDatalayersControl()
+ if (e.helper.field === 'options.type') {
+ this.resetLayer()
+ this.edit()
+ }
+ },
+ })
+ container.appendChild(builder.build())
+
+ var shapeOptions = [
+ 'options.color',
+ 'options.iconClass',
+ 'options.iconUrl',
+ 'options.opacity',
+ 'options.stroke',
+ 'options.weight',
+ 'options.fill',
+ 'options.fillColor',
+ 'options.fillOpacity',
+ ]
+
+ shapeOptions = shapeOptions.concat(this.layer.getEditableOptions())
+
+ var redrawCallback = function (field) {
+ this.hide()
+ this.layer.postUpdate(field)
+ this.show()
+ }
+
+ builder = new L.U.FormBuilder(this, shapeOptions, {
+ id: 'datalayer-advanced-properties',
+ callback: redrawCallback,
+ })
+ var shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'))
+ shapeProperties.appendChild(builder.build())
+
+ var optionsFields = [
+ 'options.smoothFactor',
+ 'options.dashArray',
+ 'options.zoomTo',
+ 'options.labelKey',
+ ]
+
+ optionsFields = optionsFields.concat(this.layer.getEditableOptions())
+
+ builder = new L.U.FormBuilder(this, optionsFields, {
+ id: 'datalayer-advanced-properties',
+ callback: redrawCallback,
+ })
+ var advancedProperties = L.DomUtil.createFieldset(
+ container,
+ L._('Advanced properties')
+ )
+ advancedProperties.appendChild(builder.build())
+
+ var popupFields = [
+ 'options.popupShape',
+ 'options.popupTemplate',
+ 'options.popupContentTemplate',
+ 'options.showLabel',
+ 'options.labelDirection',
+ 'options.labelInteractive',
+ ]
+ builder = new L.U.FormBuilder(this, popupFields, { callback: redrawCallback })
+ var popupFieldset = L.DomUtil.createFieldset(container, L._('Interaction options'))
+ popupFieldset.appendChild(builder.build())
+
+ if (!L.Util.isObject(this.options.remoteData)) {
+ this.options.remoteData = {}
+ }
+ var remoteDataFields = [
+ [
+ 'options.remoteData.url',
+ { handler: 'Url', label: L._('Url'), helpEntries: 'formatURL' },
+ ],
+ ['options.remoteData.format', { handler: 'DataFormat', label: L._('Format') }],
+ [
+ 'options.remoteData.from',
+ { label: L._('From zoom'), helpText: L._('Optional.') },
+ ],
+ ['options.remoteData.to', { label: L._('To zoom'), helpText: L._('Optional.') }],
+ [
+ 'options.remoteData.dynamic',
+ { handler: 'Switch', label: L._('Dynamic'), helpEntries: 'dynamicRemoteData' },
+ ],
+ [
+ 'options.remoteData.licence',
+ {
+ label: L._('Licence'),
+ helpText: L._('Please be sure the licence is compliant with your use.'),
+ },
+ ],
+ ]
+ if (this.map.options.urls.ajax_proxy) {
+ remoteDataFields.push([
+ 'options.remoteData.proxy',
+ {
+ handler: 'Switch',
+ label: L._('Proxy request'),
+ helpEntries: 'proxyRemoteData',
+ },
+ ])
+ remoteDataFields.push([
+ 'options.remoteData.ttl',
+ { handler: 'ProxyTTLSelect', label: L._('Cache proxied request') },
+ ])
+ }
+
+ var remoteDataContainer = L.DomUtil.createFieldset(container, L._('Remote data'))
+ builder = new L.U.FormBuilder(this, remoteDataFields)
+ remoteDataContainer.appendChild(builder.build())
+
+ if (this.map.options.urls.datalayer_versions) this.buildVersionsFieldset(container)
+
+ var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
+ var advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions)
+ var deleteLink = L.DomUtil.create(
+ 'a',
+ 'button delete_datalayer_button umap-delete',
+ advancedButtons
+ )
+ deleteLink.textContent = L._('Delete')
+ deleteLink.href = '#'
+ L.DomEvent.on(deleteLink, 'click', L.DomEvent.stop).on(
+ deleteLink,
+ 'click',
+ function () {
+ this._delete()
+ this.map.ui.closePanel()
+ },
+ this
+ )
+ if (!this.isRemoteLayer()) {
+ var emptyLink = L.DomUtil.create('a', 'button umap-empty', advancedButtons)
+ emptyLink.textContent = L._('Empty')
+ emptyLink.href = '#'
+ L.DomEvent.on(emptyLink, 'click', L.DomEvent.stop).on(
+ emptyLink,
+ 'click',
+ this.empty,
+ this
+ )
+ }
+ var cloneLink = L.DomUtil.create('a', 'button umap-clone', advancedButtons)
+ cloneLink.textContent = L._('Clone')
+ cloneLink.href = '#'
+ L.DomEvent.on(cloneLink, 'click', L.DomEvent.stop).on(
+ cloneLink,
+ 'click',
+ function () {
+ var datalayer = this.clone()
+ datalayer.edit()
+ },
+ this
+ )
+ if (this.umap_id) {
+ var download = L.DomUtil.create('a', 'button umap-download', advancedButtons)
+ download.textContent = L._('Download')
+ download.href = this._dataUrl()
+ download.target = '_blank'
+ }
+ this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
+ },
+
+ getOption: function (option) {
+ if (L.Util.usableOption(this.options, option)) return this.options[option]
+ else return this.map.getOption(option)
+ },
+
+ buildVersionsFieldset: function (container) {
+ var appendVersion = function (data) {
+ var date = new Date(parseInt(data.at, 10))
+ var content =
+ date.toLocaleDateString(L.locale) + ' (' + parseInt(data.size) / 1000 + 'Kb)'
+ var el = L.DomUtil.create('div', 'umap-datalayer-version', versionsContainer)
+ var a = L.DomUtil.create('a', '', el)
+ L.DomUtil.add('span', '', el, content)
+ a.href = '#'
+ a.title = L._('Restore this version')
+ L.DomEvent.on(a, 'click', L.DomEvent.stop).on(
+ a,
+ 'click',
+ function () {
+ this.restore(data.name)
+ },
+ this
+ )
+ }
+
+ var versionsContainer = L.DomUtil.createFieldset(container, L._('Versions'), {
+ callback: function () {
+ this.map.xhr.get(this.getVersionsUrl(), {
+ callback: function (data) {
+ for (var i = 0; i < data.versions.length; i++) {
+ appendVersion.call(this, data.versions[i])
+ }
+ },
+ context: this,
+ })
+ },
+ context: this,
+ })
+ },
+
+ restore: function (version) {
+ if (!this.map.editEnabled) return
+ if (!confirm(L._('Are you sure you want to restore this version?'))) return
+ this.map.xhr.get(this.getVersionUrl(version), {
+ callback: function (geojson) {
+ if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat.
+ if (geojson._umap_options) this.setOptions(geojson._umap_options)
+ this.empty()
+ if (this.isRemoteLayer()) this.fetchRemoteData()
+ else this.addData(geojson)
+ this.isDirty = true
+ },
+ context: this,
+ })
+ },
+
+ featuresToGeoJSON: function () {
+ var features = []
+ this.eachLayer(function (layer) {
+ features.push(layer.toGeoJSON())
+ })
+ return features
+ },
+
+ show: function () {
+ if (!this.isLoaded()) this.fetchData()
+ this.map.addLayer(this.layer)
+ this.fire('show')
+ },
+
+ hide: function () {
+ this.map.removeLayer(this.layer)
+ this.fire('hide')
+ },
+
+ toggle: function () {
+ if (!this.isVisible()) this.show()
+ else this.hide()
+ },
+
+ zoomTo: function () {
+ if (!this.isVisible()) return
+ var bounds = this.layer.getBounds()
+ if (bounds.isValid()) this.map.fitBounds(bounds)
+ },
+
+ allowBrowse: function () {
+ return !!this.options.browsable && this.canBrowse() && this.isVisible()
+ },
+
+ hasData: function () {
+ return !!this._index.length
+ },
+
+ isVisible: function () {
+ return this.map.hasLayer(this.layer)
+ },
+
+ canBrowse: function () {
+ return this.layer && this.layer.canBrowse
+ },
+
+ getFeatureByIndex: function (index) {
+ if (index === -1) index = this._index.length - 1
+ var id = this._index[index]
+ return this._layers[id]
+ },
+
+ getNextFeature: function (feature) {
+ var id = this._index.indexOf(L.stamp(feature)),
+ nextId = this._index[id + 1]
+ return nextId ? this._layers[nextId] : this.getNextBrowsable().getFeatureByIndex(0)
+ },
+
+ getPreviousFeature: function (feature) {
+ if (this._index <= 1) {
+ return null
+ }
+ var id = this._index.indexOf(L.stamp(feature)),
+ previousId = this._index[id - 1]
+ return previousId
+ ? this._layers[previousId]
+ : this.getPreviousBrowsable().getFeatureByIndex(-1)
+ },
+
+ getPreviousBrowsable: function () {
+ var id = this.getRank(),
+ next,
+ index = this.map.datalayers_index
+ while (((id = index[++id] ? id : 0), (next = index[id]))) {
+ if (next === this || (next.allowBrowse() && next.hasData())) break
+ }
+ return next
+ },
+
+ getNextBrowsable: function () {
+ var id = this.getRank(),
+ prev,
+ index = this.map.datalayers_index
+ while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) {
+ if (prev === this || (prev.allowBrowse() && prev.hasData())) break
+ }
+ return prev
+ },
+
+ umapGeoJSON: function () {
+ return {
+ type: 'FeatureCollection',
+ features: this.isRemoteLayer() ? [] : this.featuresToGeoJSON(),
+ _umap_options: this.options,
+ }
+ },
+
+ metadata: function () {
+ return {
+ id: this.umap_id,
+ name: this.options.name,
+ displayOnLoad: this.options.displayOnLoad,
+ }
+ },
+
+ getRank: function () {
+ return this.map.datalayers_index.indexOf(this)
+ },
+
+ save: function () {
+ if (this.isDeleted) return this.saveDelete()
+ if (!this.isLoaded()) {
+ return
+ }
+ var geojson = this.umapGeoJSON()
+ var formData = new FormData()
+ formData.append('name', this.options.name)
+ formData.append('display_on_load', !!this.options.displayOnLoad)
+ formData.append('rank', this.getRank())
+ // Filename support is shaky, don't do it for now.
+ var blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
+ formData.append('geojson', blob)
+ this.map.post(this.getSaveUrl(), {
+ data: formData,
+ callback: function (data, response) {
+ this._geojson = geojson
+ this._last_modified = response.getResponseHeader('Last-Modified')
+ this.setUmapId(data.id)
+ this.updateOptions(data)
+ this.backupOptions()
+ this.connectToMap()
+ this._loaded = true
+ this.redraw() // Needed for reordering features
+ this.isDirty = false
+ this.map.continueSaving()
+ },
+ context: this,
+ headers: this._last_modified
+ ? { 'If-Unmodified-Since': this._last_modified }
+ : {},
+ })
+ },
+
+ saveDelete: function () {
+ var callback = function () {
+ this.isDirty = false
+ this.map.continueSaving()
+ }
+ if (!this.umap_id) return callback.call(this)
+ this.map.xhr.post(this.getDeleteUrl(), {
+ callback: callback,
+ context: this,
+ })
+ },
+
+ getMap: function () {
+ return this.map
+ },
+
+ getName: function () {
+ return this.options.name || L._('Untitled layer')
+ },
+
+ tableEdit: function () {
+ if (this.isRemoteLayer() || !this.isVisible()) return
+ var editor = new L.U.TableEditor(this)
+ editor.edit()
+ },
+})
L.TileLayer.include({
-
- toJSON: function () {
- return {
- minZoom: this.options.minZoom,
- maxZoom: this.options.maxZoom,
- attribution: this.options.attribution,
- url_template: this._url,
- name: this.options.name,
- tms: this.options.tms
- };
- },
-
- getAttribution: function () {
- return L.Util.toHTML(this.options.attribution);
+ toJSON: function () {
+ return {
+ minZoom: this.options.minZoom,
+ maxZoom: this.options.maxZoom,
+ attribution: this.options.attribution,
+ url_template: this._url,
+ name: this.options.name,
+ tms: this.options.tms,
}
+ },
-});
+ getAttribution: function () {
+ return L.Util.toHTML(this.options.attribution)
+ },
+})
diff --git a/umap/static/umap/js/umap.permissions.js b/umap/static/umap/js/umap.permissions.js
index 6d6cc977..2592c32d 100644
--- a/umap/static/umap/js/umap.permissions.js
+++ b/umap/static/umap/js/umap.permissions.js
@@ -1,143 +1,196 @@
// Dedicated object so we can deal with a separate dirty status, and thus
// call the endpoint only when needed, saving one call at each save.
L.U.MapPermissions = L.Class.extend({
+ options: {
+ owner: null,
+ editors: [],
+ share_status: null,
+ edit_status: null,
+ },
- options: {
- owner: null,
- editors: [],
- share_status: null,
- edit_status: null
- },
-
- initialize: function (map) {
- this.setOptions(map.options.permissions);
- this.map = map;
- var isDirty = false,
- self = this;
- try {
- Object.defineProperty(this, 'isDirty', {
- get: function () {
- return isDirty;
- },
- set: function (status) {
- isDirty = status;
- if (status) self.map.isDirty = status;
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
-
- },
-
- setOptions: function (options) {
- this.options = L.Util.setOptions(this, options);
- },
-
- isOwner: function () {
- return this.map.options.user && this.map.options.permissions.owner && this.map.options.user.id == this.map.options.permissions.owner.id;
- },
-
- isAnonymousMap: function () {
- return !this.map.options.permissions.owner;
- },
-
- getMap: function () {
- return this.map;
- },
-
- edit: function () {
- if (!this.map.options.umap_id) return this.map.ui.alert({content: L._('Please save the map first'), level: 'info'});
- var container = L.DomUtil.create('div', 'permissions-panel'),
- fields = [],
- title = L.DomUtil.create('h4', '', container);
- if (this.isAnonymousMap()) {
- if (this.options.anonymous_edit_url) {
- var helpText = L._('Secret edit link is:
{link}', {link: this.options.anonymous_edit_url});
- fields.push(['options.edit_status', {handler: 'IntSelect', label: L._('Who can edit'), selectOptions: this.map.options.anonymous_edit_statuses, helpText: helpText}]);
- }
- } else {
- if (this.isOwner()) {
- fields.push(['options.edit_status', {handler: 'IntSelect', label: L._('Who can edit'), selectOptions: this.map.options.edit_statuses}]);
- fields.push(['options.share_status', {handler: 'IntSelect', label: L._('Who can view'), selectOptions: this.map.options.share_statuses}]);
- fields.push(['options.owner', {handler: 'ManageOwner', label: L._("Map's owner")}]);
- }
- fields.push(['options.editors', {handler: 'ManageEditors', label: L._("Map's editors")}]);
- }
- title.textContent = L._('Update permissions');
- var builder = new L.U.FormBuilder(this, fields);
- var form = builder.build();
- container.appendChild(form);
- if (this.isAnonymousMap() && this.map.options.user) {
- // We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
- // Note: real check is made on the back office anyway.
- var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'));
- var advancedButtons = L.DomUtil.create('div', 'button-bar', advancedActions);
- var download = L.DomUtil.create('a', 'button', advancedButtons);
- download.href = '#';
- download.textContent = L._('Attach the map to my account');
- L.DomEvent
- .on(download, 'click', L.DomEvent.stop)
- .on(download, 'click', this.attach, this);
- }
- this.map.ui.openPanel({data: {html: container}, className: 'dark'});
- },
-
- attach: function () {
- this.map.post(this.getAttachUrl(), {
- callback: function () {
- this.options.owner = this.map.options.user;
- this.map.ui.alert({content: L._("Map has been attached to your account"), level: 'info'});
- this.map.ui.closePanel();
- },
- context: this
- })
- },
-
- save: function () {
- if (!this.isDirty) return this.map.continueSaving();
- var formData = new FormData();
- if (!this.isAnonymousMap() && this.options.editors) {
- var editors = this.options.editors.map(function (u) {return u.id});
- for (var i = 0; i < this.options.editors.length; i++) formData.append('editors', this.options.editors[i].id);
- }
- if (this.isOwner() || this.isAnonymousMap()) formData.append('edit_status', this.options.edit_status);
- if (this.isOwner()) {
- formData.append('owner', this.options.owner && this.options.owner.id);
- formData.append('share_status', this.options.share_status);
- }
- this.map.post(this.getUrl(), {
- data: formData,
- context: this,
- callback: function (data) {
- this.commit();
- this.isDirty = false;
- this.map.continueSaving();
- }
- });
- },
-
- getUrl: function () {
- return L.Util.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, {'map_id': this.map.options.umap_id});
- },
-
- addOwnerLink: function (element, container) {
- if (this.options.owner && this.options.owner.name && this.options.owner.url) {
- var ownerContainer = L.DomUtil.add(element, 'umap-map-owner', container, ' ' + L._('by') + ' '),
- owner = L.DomUtil.create('a');
- owner.href = this.options.owner.url;
- owner.textContent = this.options.owner.name;
- ownerContainer.appendChild(owner);
- }
- },
-
- commit: function () {
- L.Util.extend(this.map.options.permissions, this.options);
+ initialize: function (map) {
+ this.setOptions(map.options.permissions)
+ this.map = map
+ var isDirty = false,
+ self = this
+ try {
+ Object.defineProperty(this, 'isDirty', {
+ get: function () {
+ return isDirty
+ },
+ set: function (status) {
+ isDirty = status
+ if (status) self.map.isDirty = status
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
}
+ },
-});
+ setOptions: function (options) {
+ this.options = L.Util.setOptions(this, options)
+ },
+
+ isOwner: function () {
+ return (
+ this.map.options.user &&
+ this.map.options.permissions.owner &&
+ this.map.options.user.id == this.map.options.permissions.owner.id
+ )
+ },
+
+ isAnonymousMap: function () {
+ return !this.map.options.permissions.owner
+ },
+
+ getMap: function () {
+ return this.map
+ },
+
+ edit: function () {
+ if (!this.map.options.umap_id)
+ return this.map.ui.alert({
+ content: L._('Please save the map first'),
+ level: 'info',
+ })
+ var container = L.DomUtil.create('div', 'permissions-panel'),
+ fields = [],
+ title = L.DomUtil.create('h4', '', container)
+ if (this.isAnonymousMap()) {
+ if (this.options.anonymous_edit_url) {
+ var helpText = L._('Secret edit link is:
{link}', {
+ link: this.options.anonymous_edit_url,
+ })
+ fields.push([
+ 'options.edit_status',
+ {
+ handler: 'IntSelect',
+ label: L._('Who can edit'),
+ selectOptions: this.map.options.anonymous_edit_statuses,
+ helpText: helpText,
+ },
+ ])
+ }
+ } else {
+ if (this.isOwner()) {
+ fields.push([
+ 'options.edit_status',
+ {
+ handler: 'IntSelect',
+ label: L._('Who can edit'),
+ selectOptions: this.map.options.edit_statuses,
+ },
+ ])
+ fields.push([
+ 'options.share_status',
+ {
+ handler: 'IntSelect',
+ label: L._('Who can view'),
+ selectOptions: this.map.options.share_statuses,
+ },
+ ])
+ fields.push([
+ 'options.owner',
+ { handler: 'ManageOwner', label: L._("Map's owner") },
+ ])
+ }
+ fields.push([
+ 'options.editors',
+ { handler: 'ManageEditors', label: L._("Map's editors") },
+ ])
+ }
+ title.textContent = L._('Update permissions')
+ var builder = new L.U.FormBuilder(this, fields)
+ var form = builder.build()
+ container.appendChild(form)
+ if (this.isAnonymousMap() && this.map.options.user) {
+ // We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
+ // Note: real check is made on the back office anyway.
+ var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
+ var advancedButtons = L.DomUtil.create('div', 'button-bar', advancedActions)
+ var download = L.DomUtil.create('a', 'button', advancedButtons)
+ download.href = '#'
+ download.textContent = L._('Attach the map to my account')
+ L.DomEvent.on(download, 'click', L.DomEvent.stop).on(
+ download,
+ 'click',
+ this.attach,
+ this
+ )
+ }
+ this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
+ },
+
+ attach: function () {
+ this.map.post(this.getAttachUrl(), {
+ callback: function () {
+ this.options.owner = this.map.options.user
+ this.map.ui.alert({
+ content: L._('Map has been attached to your account'),
+ level: 'info',
+ })
+ this.map.ui.closePanel()
+ },
+ context: this,
+ })
+ },
+
+ save: function () {
+ if (!this.isDirty) return this.map.continueSaving()
+ var formData = new FormData()
+ if (!this.isAnonymousMap() && this.options.editors) {
+ var editors = this.options.editors.map(function (u) {
+ return u.id
+ })
+ for (var i = 0; i < this.options.editors.length; i++)
+ formData.append('editors', this.options.editors[i].id)
+ }
+ if (this.isOwner() || this.isAnonymousMap())
+ formData.append('edit_status', this.options.edit_status)
+ if (this.isOwner()) {
+ formData.append('owner', this.options.owner && this.options.owner.id)
+ formData.append('share_status', this.options.share_status)
+ }
+ this.map.post(this.getUrl(), {
+ data: formData,
+ context: this,
+ callback: function (data) {
+ this.commit()
+ this.isDirty = false
+ this.map.continueSaving()
+ },
+ })
+ },
+
+ getUrl: function () {
+ return L.Util.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, {
+ map_id: this.map.options.umap_id,
+ })
+ },
+
+ addOwnerLink: function (element, container) {
+ if (this.options.owner && this.options.owner.name && this.options.owner.url) {
+ var ownerContainer = L.DomUtil.add(
+ element,
+ 'umap-map-owner',
+ container,
+ ' ' + L._('by') + ' '
+ ),
+ owner = L.DomUtil.create('a')
+ owner.href = this.options.owner.url
+ owner.textContent = this.options.owner.name
+ ownerContainer.appendChild(owner)
+ }
+ },
+
+ commit: function () {
+ L.Util.extend(this.map.options.permissions, this.options)
+ },
+})
diff --git a/umap/static/umap/js/umap.popup.js b/umap/static/umap/js/umap.popup.js
index aca9d533..0bcd2baa 100644
--- a/umap/static/umap/js/umap.popup.js
+++ b/umap/static/umap/js/umap.popup.js
@@ -1,224 +1,239 @@
/* Shapes */
L.U.Popup = L.Popup.extend({
+ options: {
+ parseTemplate: true,
+ },
- options: {
- parseTemplate: true
- },
+ initialize: function (feature) {
+ this.feature = feature
+ this.container = L.DomUtil.create('div', 'umap-popup')
+ this.format()
+ L.Popup.prototype.initialize.call(this, {}, feature)
+ this.setContent(this.container)
+ },
- initialize: function (feature) {
- this.feature = feature;
- this.container = L.DomUtil.create('div', 'umap-popup');
- this.format();
- L.Popup.prototype.initialize.call(this, {}, feature);
- this.setContent(this.container);
- },
-
- format: function () {
- var mode = this.feature.getOption('popupTemplate') || 'Default',
- klass = L.U.PopupTemplate[mode] || L.U.PopupTemplate.Default;
- this.content = new klass(this.feature, this.container);
- this.content.render();
- var els = this.container.querySelectorAll('img,iframe');
- for (var i = 0; i < els.length; i++) {
- this.onElementLoaded(els[i]);
- }
- if (!els.length && this.container.textContent.replace('\n', '') === '') {
- this.container.innerHTML = '';
- L.DomUtil.add('h3', '', this.container, this.feature.getDisplayName());
- }
- },
-
- onElementLoaded: function (el) {
- L.DomEvent.on(el, 'load', function () {
- this._updateLayout();
- this._updatePosition();
- this._adjustPan();
- }, this);
+ format: function () {
+ var mode = this.feature.getOption('popupTemplate') || 'Default',
+ klass = L.U.PopupTemplate[mode] || L.U.PopupTemplate.Default
+ this.content = new klass(this.feature, this.container)
+ this.content.render()
+ var els = this.container.querySelectorAll('img,iframe')
+ for (var i = 0; i < els.length; i++) {
+ this.onElementLoaded(els[i])
}
+ if (!els.length && this.container.textContent.replace('\n', '') === '') {
+ this.container.innerHTML = ''
+ L.DomUtil.add('h3', '', this.container, this.feature.getDisplayName())
+ }
+ },
-});
+ onElementLoaded: function (el) {
+ L.DomEvent.on(
+ el,
+ 'load',
+ function () {
+ this._updateLayout()
+ this._updatePosition()
+ this._adjustPan()
+ },
+ this
+ )
+ },
+})
L.U.Popup.Large = L.U.Popup.extend({
- options: {
- maxWidth: 500,
- className: 'umap-popup-large'
- }
-});
-
+ options: {
+ maxWidth: 500,
+ className: 'umap-popup-large',
+ },
+})
L.U.Popup.Panel = L.U.Popup.extend({
+ options: {
+ zoomAnimation: false,
+ },
- options: {
- zoomAnimation: false
- },
+ allButton: function () {
+ var button = L.DomUtil.create('li', '')
+ L.DomUtil.create('i', 'umap-icon-16 umap-list', button)
+ var label = L.DomUtil.create('span', '', button)
+ label.textContent = label.title = L._('See all')
+ L.DomEvent.on(button, 'click', this.feature.map.openBrowser, this.feature.map)
+ return button
+ },
- allButton: function () {
- var button = L.DomUtil.create('li', '');
- L.DomUtil.create('i', 'umap-icon-16 umap-list', button);
- var label = L.DomUtil.create('span', '', button);
- label.textContent = label.title = L._('See all');
- L.DomEvent.on(button, 'click', this.feature.map.openBrowser, this.feature.map);
- return button;
- },
+ update: function () {
+ this.feature.map.ui.openPanel({
+ data: { html: this._content },
+ actions: [this.allButton()],
+ })
+ },
- update: function () {
- this.feature.map.ui.openPanel({data: {html: this._content}, actions: [this.allButton()]});
- },
+ onRemove: function (map) {
+ map.ui.closePanel()
+ L.U.Popup.prototype.onRemove.call(this, map)
+ },
- onRemove: function (map) {
- map.ui.closePanel();
- L.U.Popup.prototype.onRemove.call(this, map);
- },
-
- _initLayout: function () {this._container = L.DomUtil.create('span');},
- _updateLayout: function () {},
- _updatePosition: function () {},
- _adjustPan: function () {}
-
-});
-L.U.Popup.SimplePanel = L.U.Popup.Panel; // Retrocompat.
+ _initLayout: function () {
+ this._container = L.DomUtil.create('span')
+ },
+ _updateLayout: function () {},
+ _updatePosition: function () {},
+ _adjustPan: function () {},
+})
+L.U.Popup.SimplePanel = L.U.Popup.Panel // Retrocompat.
/* Content templates */
-L.U.PopupTemplate = {};
+L.U.PopupTemplate = {}
L.U.PopupTemplate.Default = L.Class.extend({
+ initialize: function (feature, container) {
+ this.feature = feature
+ this.container = container
+ },
- initialize: function (feature, container) {
- this.feature = feature;
- this.container = container;
- },
+ renderTitle: function () {},
- renderTitle: function () {},
+ renderBody: function () {
+ var template = this.feature.getOption('popupContentTemplate'),
+ container = L.DomUtil.create('div', 'umap-popup-container'),
+ content = '',
+ properties,
+ center
+ properties = this.feature.extendedProperties()
+ // Resolve properties inside description
+ properties.description = L.Util.greedyTemplate(
+ this.feature.properties.description || '',
+ properties
+ )
+ content = L.Util.greedyTemplate(template, properties)
+ content = L.Util.toHTML(content)
+ container.innerHTML = content
+ return container
+ },
- renderBody: function () {
- var template = this.feature.getOption('popupContentTemplate'),
- container = L.DomUtil.create('div', 'umap-popup-container'),
- content = '', properties, center;
- properties = this.feature.extendedProperties();
- // Resolve properties inside description
- properties.description = L.Util.greedyTemplate(this.feature.properties.description || '', properties);
- content = L.Util.greedyTemplate(template, properties);
- content = L.Util.toHTML(content);
- container.innerHTML = content;
- return container;
- },
-
- renderFooter: function () {
- if (this.feature.hasPopupFooter()) {
- var footerContainer = L.DomUtil.create('div', 'umap-footer-container', this.container),
- footer = L.DomUtil.create('ul', 'umap-popup-footer', footerContainer),
- previousLi = L.DomUtil.create('li', 'previous', footer),
- zoomLi = L.DomUtil.create('li', 'zoom', footer),
- nextLi = L.DomUtil.create('li', 'next', footer),
- next = this.feature.getNext(),
- prev = this.feature.getPrevious();
- if (next) nextLi.title = L._('Go to «{feature}»', {feature: next.properties.name || L._('next')});
- if (prev) previousLi.title = L._('Go to «{feature}»', {feature: prev.properties.name || L._('previous')});
- zoomLi.title = L._('Zoom to this feature');
- L.DomEvent.on(nextLi, 'click', function () {
- if (next) next.zoomTo({callback: next.view});
- });
- L.DomEvent.on(previousLi, 'click', function () {
- if (prev) prev.zoomTo({callback: prev.view});
- });
- L.DomEvent.on(zoomLi, 'click', function () {
- this.zoomTo();
- }, this.feature);
- }
- },
-
- render: function () {
- var title = this.renderTitle();
- if (title) this.container.appendChild(title);
- var body = this.renderBody();
- if (body) L.DomUtil.add('div', 'umap-popup-content', this.container, body);
- this.renderFooter();
+ renderFooter: function () {
+ if (this.feature.hasPopupFooter()) {
+ var footerContainer = L.DomUtil.create(
+ 'div',
+ 'umap-footer-container',
+ this.container
+ ),
+ footer = L.DomUtil.create('ul', 'umap-popup-footer', footerContainer),
+ previousLi = L.DomUtil.create('li', 'previous', footer),
+ zoomLi = L.DomUtil.create('li', 'zoom', footer),
+ nextLi = L.DomUtil.create('li', 'next', footer),
+ next = this.feature.getNext(),
+ prev = this.feature.getPrevious()
+ if (next)
+ nextLi.title = L._('Go to «{feature}»', {
+ feature: next.properties.name || L._('next'),
+ })
+ if (prev)
+ previousLi.title = L._('Go to «{feature}»', {
+ feature: prev.properties.name || L._('previous'),
+ })
+ zoomLi.title = L._('Zoom to this feature')
+ L.DomEvent.on(nextLi, 'click', function () {
+ if (next) next.zoomTo({ callback: next.view })
+ })
+ L.DomEvent.on(previousLi, 'click', function () {
+ if (prev) prev.zoomTo({ callback: prev.view })
+ })
+ L.DomEvent.on(
+ zoomLi,
+ 'click',
+ function () {
+ this.zoomTo()
+ },
+ this.feature
+ )
}
+ },
-});
+ render: function () {
+ var title = this.renderTitle()
+ if (title) this.container.appendChild(title)
+ var body = this.renderBody()
+ if (body) L.DomUtil.add('div', 'umap-popup-content', this.container, body)
+ this.renderFooter()
+ },
+})
L.U.PopupTemplate.BaseWithTitle = L.U.PopupTemplate.Default.extend({
-
- renderTitle: function () {
- var title;
- if (this.feature.getDisplayName()) {
- title = L.DomUtil.create('h3', 'popup-title');
- title.textContent = this.feature.getDisplayName();
- }
- return title;
+ renderTitle: function () {
+ var title
+ if (this.feature.getDisplayName()) {
+ title = L.DomUtil.create('h3', 'popup-title')
+ title.textContent = this.feature.getDisplayName()
}
-
-});
-
+ return title
+ },
+})
L.U.PopupTemplate.Table = L.U.PopupTemplate.BaseWithTitle.extend({
-
- formatRow: function (key, value) {
- if (value.indexOf('http') === 0) {
- value = '' + value + '';
- }
- return value;
- },
-
- addRow: function (container, key, value) {
- var tr = L.DomUtil.create('tr', '', container);
- L.DomUtil.add('th', '', tr, key);
- L.DomUtil.add('td', '', tr, this.formatRow(key, value));
- },
-
- renderBody: function () {
- var table = L.DomUtil.create('table');
-
- for (var 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());
- }
- return table;
+ formatRow: function (key, value) {
+ if (value.indexOf('http') === 0) {
+ value = '' + value + ''
}
+ return value
+ },
-});
+ addRow: function (container, key, value) {
+ var tr = L.DomUtil.create('tr', '', container)
+ L.DomUtil.add('th', '', tr, key)
+ L.DomUtil.add('td', '', tr, this.formatRow(key, value))
+ },
+
+ renderBody: function () {
+ var table = L.DomUtil.create('table')
+
+ for (var 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())
+ }
+ return table
+ },
+})
L.U.PopupTemplate.GeoRSSImage = L.U.PopupTemplate.BaseWithTitle.extend({
+ options: {
+ minWidth: 300,
+ maxWidth: 500,
+ className: 'umap-popup-large umap-georss-image',
+ },
- options: {
- minWidth: 300,
- maxWidth: 500,
- className: 'umap-popup-large umap-georss-image'
- },
-
- renderBody: function () {
- var container = L.DomUtil.create('a');
- container.href = this.feature.properties.link;
- container.target = '_blank';
- if (this.feature.properties.img) {
- var img = L.DomUtil.create('img', '', container);
- img.src = this.feature.properties.img;
- // Sadly, we are unable to override this from JS the clean way
- // See https://github.com/Leaflet/Leaflet/commit/61d746818b99d362108545c151a27f09d60960ee#commitcomment-6061847
- img.style.maxWidth = this.options.maxWidth + 'px';
- img.style.maxHeight = this.options.maxWidth + 'px';
- this.onElementLoaded(img);
- }
- return container;
+ renderBody: function () {
+ var container = L.DomUtil.create('a')
+ container.href = this.feature.properties.link
+ container.target = '_blank'
+ if (this.feature.properties.img) {
+ var img = L.DomUtil.create('img', '', container)
+ img.src = this.feature.properties.img
+ // Sadly, we are unable to override this from JS the clean way
+ // See https://github.com/Leaflet/Leaflet/commit/61d746818b99d362108545c151a27f09d60960ee#commitcomment-6061847
+ img.style.maxWidth = this.options.maxWidth + 'px'
+ img.style.maxHeight = this.options.maxWidth + 'px'
+ this.onElementLoaded(img)
}
-
-});
+ return container
+ },
+})
L.U.PopupTemplate.GeoRSSLink = L.U.PopupTemplate.Default.extend({
+ options: {
+ className: 'umap-georss-link',
+ },
- options: {
- className: 'umap-georss-link'
- },
-
- renderBody: function () {
- var title = this.renderTitle(this),
- a = L.DomUtil.add('a');
- a.href = this.feature.properties.link;
- a.target = '_blank';
- a.appendChild(title);
- return a;
- }
-});
+ renderBody: function () {
+ var title = this.renderTitle(this),
+ a = L.DomUtil.add('a')
+ a.href = this.feature.properties.link
+ a.target = '_blank'
+ a.appendChild(title)
+ return a
+ },
+})
diff --git a/umap/static/umap/js/umap.slideshow.js b/umap/static/umap/js/umap.slideshow.js
index e9d42cd4..54d89c54 100644
--- a/umap/static/umap/js/umap.slideshow.js
+++ b/umap/static/umap/js/umap.slideshow.js
@@ -1,164 +1,163 @@
L.U.Slideshow = L.Class.extend({
+ statics: {
+ CLASSNAME: 'umap-slideshow-active',
+ },
- statics: {
- CLASSNAME: 'umap-slideshow-active'
- },
+ options: {
+ delay: 5000,
+ autoplay: false,
+ },
- options: {
- delay: 5000,
- autoplay: false
- },
-
- initialize: function (map, options) {
- this.setOptions(options);
- this.map = map;
- this._id = null;
- var current = null, // current feature
- self = this;
- try {
- Object.defineProperty(this, 'current', {
- get: function () {
- if (!current) {
- var datalayer = this.defaultDatalayer();
- if (datalayer) current = datalayer.getFeatureByIndex(0);
- }
- return current;
- },
- set: function (feature) {
- current = feature;
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
- try {
- Object.defineProperty(this, 'next', {
- get: function () {
- if (!current) {
- return self.current;
- }
- return current.getNext();
- }
- });
- }
- catch (e) {
- // Certainly IE8, which has a limited version of defineProperty
- }
- if (this.options.autoplay) {
- this.map.onceDataLoaded(function () {
- this.play();
- }, this);
- }
- this.map.on('edit:enabled', function () {
- this.stop();
- }, this);
- },
-
- setOptions: function (options) {
- L.setOptions(this, options);
- this.timeSpinner();
- },
-
- defaultDatalayer: function () {
- return this.map.findDataLayer(function (d) { return d.allowBrowse() && d.hasData(); });
- },
-
- timeSpinner: function () {
- var time = parseInt(this.options.delay, 10);
- if (!time) return;
- var css = 'rotation ' + time / 1000 + 's infinite linear',
- spinners = document.querySelectorAll('.umap-slideshow-toolbox .play .spinner');
- for (var i = 0; i < spinners.length; i++) {
- spinners[i].style.animation = css;
- spinners[i].style['-webkit-animation'] = css;
- spinners[i].style['-moz-animation'] = css;
- spinners[i].style['-o-animation'] = css;
- }
- },
-
- resetSpinners: function () {
- // Make that animnation is coordinated with user actions
- var spinners = document.querySelectorAll('.umap-slideshow-toolbox .play .spinner'),
- el, newOne;
- for (var i = 0; i < spinners.length; i++) {
- el = spinners[i];
- newOne = el.cloneNode(true);
- el.parentNode.replaceChild(newOne, el);
- }
- },
-
- play: function () {
- if (this._id) return;
- if (this.map.editEnabled || !this.map.options.slideshow.active) return;
- L.DomUtil.addClass(document.body, L.U.Slideshow.CLASSNAME);
- this._id = window.setInterval(L.bind(this.loop, this), this.options.delay);
- this.resetSpinners();
- this.loop();
- },
-
- loop: function () {
- this.current = this.next;
- this.step();
- },
-
- pause: function () {
- if (this._id) {
- L.DomUtil.removeClass(document.body, L.U.Slideshow.CLASSNAME);
- window.clearInterval(this._id);
- this._id = null;
- }
- },
-
- stop: function () {
- this.pause();
- this.current = null;
- },
-
- forward: function () {
- this.pause();
- this.current = this.next;
- this.step();
- },
-
- backward: function () {
- this.pause();
- if (this.current) this.current = this.current.getPrevious();
- this.step();
- },
-
- step: function () {
- if(!this.current) return this.stop();
- this.current.zoomTo({easing: this.options.easing});
- this.current.view();
- },
-
- renderToolbox: function (container) {
- var box = L.DomUtil.create('ul', 'umap-slideshow-toolbox'),
- play = L.DomUtil.create('li', 'play', box),
- stop = L.DomUtil.create('li', 'stop', box),
- prev = L.DomUtil.create('li', 'prev', box),
- next = L.DomUtil.create('li', 'next', box);
- L.DomUtil.create('div', 'spinner', play);
- play.title = L._('Start slideshow');
- stop.title = L._('Stop slideshow');
- next.title = L._('Zoom to the next');
- prev.title = L._('Zoom to the previous');
- var toggle = function () {
- if (this._id) this.pause();
- else this.play();
- };
- L.DomEvent.on(play, 'click', L.DomEvent.stop)
- .on(play, 'click', toggle, this);
- L.DomEvent.on(stop, 'click', L.DomEvent.stop)
- .on(stop, 'click', this.stop, this);
- L.DomEvent.on(prev, 'click', L.DomEvent.stop)
- .on(prev, 'click', this.backward, this);
- L.DomEvent.on(next, 'click', L.DomEvent.stop)
- .on(next, 'click', this.forward, this);
- container.appendChild(box);
- this.timeSpinner();
- return box;
+ initialize: function (map, options) {
+ this.setOptions(options)
+ this.map = map
+ this._id = null
+ var current = null, // current feature
+ self = this
+ try {
+ Object.defineProperty(this, 'current', {
+ get: function () {
+ if (!current) {
+ var datalayer = this.defaultDatalayer()
+ if (datalayer) current = datalayer.getFeatureByIndex(0)
+ }
+ return current
+ },
+ set: function (feature) {
+ current = feature
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
}
+ try {
+ Object.defineProperty(this, 'next', {
+ get: function () {
+ if (!current) {
+ return self.current
+ }
+ return current.getNext()
+ },
+ })
+ } catch (e) {
+ // Certainly IE8, which has a limited version of defineProperty
+ }
+ if (this.options.autoplay) {
+ this.map.onceDataLoaded(function () {
+ this.play()
+ }, this)
+ }
+ this.map.on(
+ 'edit:enabled',
+ function () {
+ this.stop()
+ },
+ this
+ )
+ },
-});
+ setOptions: function (options) {
+ L.setOptions(this, options)
+ this.timeSpinner()
+ },
+
+ defaultDatalayer: function () {
+ return this.map.findDataLayer(function (d) {
+ return d.allowBrowse() && d.hasData()
+ })
+ },
+
+ timeSpinner: function () {
+ var time = parseInt(this.options.delay, 10)
+ if (!time) return
+ var css = 'rotation ' + time / 1000 + 's infinite linear',
+ spinners = document.querySelectorAll('.umap-slideshow-toolbox .play .spinner')
+ for (var i = 0; i < spinners.length; i++) {
+ spinners[i].style.animation = css
+ spinners[i].style['-webkit-animation'] = css
+ spinners[i].style['-moz-animation'] = css
+ spinners[i].style['-o-animation'] = css
+ }
+ },
+
+ resetSpinners: function () {
+ // Make that animnation is coordinated with user actions
+ var spinners = document.querySelectorAll('.umap-slideshow-toolbox .play .spinner'),
+ el,
+ newOne
+ for (var i = 0; i < spinners.length; i++) {
+ el = spinners[i]
+ newOne = el.cloneNode(true)
+ el.parentNode.replaceChild(newOne, el)
+ }
+ },
+
+ play: function () {
+ if (this._id) return
+ if (this.map.editEnabled || !this.map.options.slideshow.active) return
+ L.DomUtil.addClass(document.body, L.U.Slideshow.CLASSNAME)
+ this._id = window.setInterval(L.bind(this.loop, this), this.options.delay)
+ this.resetSpinners()
+ this.loop()
+ },
+
+ loop: function () {
+ this.current = this.next
+ this.step()
+ },
+
+ pause: function () {
+ if (this._id) {
+ L.DomUtil.removeClass(document.body, L.U.Slideshow.CLASSNAME)
+ window.clearInterval(this._id)
+ this._id = null
+ }
+ },
+
+ stop: function () {
+ this.pause()
+ this.current = null
+ },
+
+ forward: function () {
+ this.pause()
+ this.current = this.next
+ this.step()
+ },
+
+ backward: function () {
+ this.pause()
+ if (this.current) this.current = this.current.getPrevious()
+ this.step()
+ },
+
+ step: function () {
+ if (!this.current) return this.stop()
+ this.current.zoomTo({ easing: this.options.easing })
+ this.current.view()
+ },
+
+ renderToolbox: function (container) {
+ var box = L.DomUtil.create('ul', 'umap-slideshow-toolbox'),
+ play = L.DomUtil.create('li', 'play', box),
+ stop = L.DomUtil.create('li', 'stop', box),
+ prev = L.DomUtil.create('li', 'prev', box),
+ next = L.DomUtil.create('li', 'next', box)
+ L.DomUtil.create('div', 'spinner', play)
+ play.title = L._('Start slideshow')
+ stop.title = L._('Stop slideshow')
+ next.title = L._('Zoom to the next')
+ prev.title = L._('Zoom to the previous')
+ var toggle = function () {
+ if (this._id) this.pause()
+ else this.play()
+ }
+ L.DomEvent.on(play, 'click', L.DomEvent.stop).on(play, 'click', toggle, this)
+ L.DomEvent.on(stop, 'click', L.DomEvent.stop).on(stop, 'click', this.stop, this)
+ L.DomEvent.on(prev, 'click', L.DomEvent.stop).on(prev, 'click', this.backward, this)
+ L.DomEvent.on(next, 'click', L.DomEvent.stop).on(next, 'click', this.forward, this)
+ container.appendChild(box)
+ this.timeSpinner()
+ return box
+ },
+})
diff --git a/umap/static/umap/js/umap.tableeditor.js b/umap/static/umap/js/umap.tableeditor.js
index 66aca2a5..423d73ce 100644
--- a/umap/static/umap/js/umap.tableeditor.js
+++ b/umap/static/umap/js/umap.tableeditor.js
@@ -1,107 +1,121 @@
L.U.TableEditor = L.Class.extend({
+ initialize: function (datalayer) {
+ this.datalayer = datalayer
+ this.table = L.DomUtil.create('div', 'table')
+ this.header = L.DomUtil.create('div', 'thead', this.table)
+ this.body = L.DomUtil.create('div', 'tbody', this.table)
+ this.resetProperties()
+ },
- initialize: function (datalayer) {
- this.datalayer = datalayer;
- this.table = L.DomUtil.create('div', 'table');
- this.header = L.DomUtil.create('div', 'thead', this.table);
- this.body = L.DomUtil.create('div', 'tbody', this.table);
- this.resetProperties();
- },
-
- renderHeaders: function () {
- this.header.innerHTML = '';
- for (var i = 0; i < this.properties.length; i++) {
- this.renderHeader(this.properties[i]);
- }
- },
-
- renderHeader: function (property) {
- var container = L.DomUtil.create('div', 'tcell', this.header),
- title = L.DomUtil.add('span', '', container, property),
- del = L.DomUtil.create('i', 'umap-delete', container),
- rename = L.DomUtil.create('i', 'umap-edit', container);
- del.title = L._('Delete this property on all the features');
- rename.title = L._('Rename this property on all the features');
- var doDelete = function () {
- if (confirm(L._('Are you sure you want to delete this property on all the features?'))) {
- this.datalayer.eachLayer(function (feature) {
- feature.deleteProperty(property);
- });
- this.datalayer.deindexProperty(property);
- this.resetProperties();
- this.edit();
- }
- };
- var doRename = function () {
- var newName = prompt(L._('Please enter the new name of this property'), property);
- if (!newName || !this.validateName(newName)) return;
- this.datalayer.eachLayer(function (feature) {
- feature.renameProperty(property, newName);
- });
- this.datalayer.deindexProperty(property);
- this.datalayer.indexProperty(newName);
- this.resetProperties();
- this.edit();
- };
- L.DomEvent.on(del, 'click', doDelete, this);
- L.DomEvent.on(rename, 'click', doRename, this);
- },
-
- renderRow: function (feature) {
- var builder = new L.U.FormBuilder(feature, this.field_properties,
- {
- id: 'umap-feature-properties_' + L.stamp(feature),
- className: 'trow',
- callback: feature.resetTooltip
- }
- );
- this.body.appendChild(builder.build());
- },
-
- compileProperties: function () {
- if (this.properties.length === 0) this.properties = ['name'];
- // description is a forced textarea, don't edit it in a text input, or you lose cariage returns
- if (this.properties.indexOf('description') !== -1) this.properties.splice(this.properties.indexOf('description'), 1);
- this.properties.sort();
- this.field_properties = [];
- for (var i = 0; i < this.properties.length; i++) {
- this.field_properties.push(['properties.' + this.properties[i], {wrapper: 'div', wrapperClass: 'tcell'}]);
- }
- },
-
- resetProperties: function () {
- this.properties = this.datalayer._propertiesIndex;
- },
-
- validateName: function (name) {
- if (name.indexOf(".") !== -1) {
- this.datalayer.map.ui.alert({content: L._('Invalide property name: {name}', {name: name}), level: 'error'});
- return false;
- }
- return true;
- },
-
- edit: function () {
- var id = 'tableeditor:edit';
- this.datalayer.map.fire('dataloading', {id: id});
- this.compileProperties();
- this.renderHeaders();
- this.body.innerHTML = '';
- this.datalayer.eachLayer(this.renderRow, this);
- var addButton = L.DomUtil.create('li', 'add-property');
- L.DomUtil.create('i', 'umap-icon-16 umap-add', addButton);
- var label = L.DomUtil.create('span', '', addButton);
- label.textContent = label.title = L._('Add a new property');
- var addProperty = function () {
- var newName = prompt(L._('Please enter the name of the property'));
- if (!newName || !this.validateName(newName)) return;
- this.datalayer.indexProperty(newName);
- this.edit();
- };
- L.DomEvent.on(addButton, 'click', addProperty, this);
- var className = (this.properties.length > 2) ? 'umap-table-editor fullwidth dark' : 'umap-table-editor dark';
- this.datalayer.map.ui.openPanel({data: {html: this.table}, className: className, actions: [addButton]});
- this.datalayer.map.fire('dataload', {id: id});
+ renderHeaders: function () {
+ this.header.innerHTML = ''
+ for (var i = 0; i < this.properties.length; i++) {
+ this.renderHeader(this.properties[i])
}
+ },
-});
+ renderHeader: function (property) {
+ var container = L.DomUtil.create('div', 'tcell', this.header),
+ title = L.DomUtil.add('span', '', container, property),
+ del = L.DomUtil.create('i', 'umap-delete', container),
+ rename = L.DomUtil.create('i', 'umap-edit', container)
+ del.title = L._('Delete this property on all the features')
+ rename.title = L._('Rename this property on all the features')
+ var doDelete = function () {
+ if (
+ confirm(
+ L._('Are you sure you want to delete this property on all the features?')
+ )
+ ) {
+ this.datalayer.eachLayer(function (feature) {
+ feature.deleteProperty(property)
+ })
+ this.datalayer.deindexProperty(property)
+ this.resetProperties()
+ this.edit()
+ }
+ }
+ var doRename = function () {
+ var newName = prompt(L._('Please enter the new name of this property'), property)
+ if (!newName || !this.validateName(newName)) return
+ this.datalayer.eachLayer(function (feature) {
+ feature.renameProperty(property, newName)
+ })
+ this.datalayer.deindexProperty(property)
+ this.datalayer.indexProperty(newName)
+ this.resetProperties()
+ this.edit()
+ }
+ L.DomEvent.on(del, 'click', doDelete, this)
+ L.DomEvent.on(rename, 'click', doRename, this)
+ },
+
+ renderRow: function (feature) {
+ var builder = new L.U.FormBuilder(feature, this.field_properties, {
+ id: 'umap-feature-properties_' + L.stamp(feature),
+ className: 'trow',
+ callback: feature.resetTooltip,
+ })
+ this.body.appendChild(builder.build())
+ },
+
+ compileProperties: function () {
+ if (this.properties.length === 0) this.properties = ['name']
+ // description is a forced textarea, don't edit it in a text input, or you lose cariage returns
+ if (this.properties.indexOf('description') !== -1)
+ this.properties.splice(this.properties.indexOf('description'), 1)
+ this.properties.sort()
+ this.field_properties = []
+ for (var i = 0; i < this.properties.length; i++) {
+ this.field_properties.push([
+ 'properties.' + this.properties[i],
+ { wrapper: 'div', wrapperClass: 'tcell' },
+ ])
+ }
+ },
+
+ resetProperties: function () {
+ this.properties = this.datalayer._propertiesIndex
+ },
+
+ validateName: function (name) {
+ if (name.indexOf('.') !== -1) {
+ this.datalayer.map.ui.alert({
+ content: L._('Invalide property name: {name}', { name: name }),
+ level: 'error',
+ })
+ return false
+ }
+ return true
+ },
+
+ edit: function () {
+ var id = 'tableeditor:edit'
+ this.datalayer.map.fire('dataloading', { id: id })
+ this.compileProperties()
+ this.renderHeaders()
+ this.body.innerHTML = ''
+ this.datalayer.eachLayer(this.renderRow, this)
+ var addButton = L.DomUtil.create('li', 'add-property')
+ L.DomUtil.create('i', 'umap-icon-16 umap-add', addButton)
+ var label = L.DomUtil.create('span', '', addButton)
+ label.textContent = label.title = L._('Add a new property')
+ var addProperty = function () {
+ var newName = prompt(L._('Please enter the name of the property'))
+ if (!newName || !this.validateName(newName)) return
+ this.datalayer.indexProperty(newName)
+ this.edit()
+ }
+ L.DomEvent.on(addButton, 'click', addProperty, this)
+ var className =
+ this.properties.length > 2
+ ? 'umap-table-editor fullwidth dark'
+ : 'umap-table-editor dark'
+ this.datalayer.map.ui.openPanel({
+ data: { html: this.table },
+ className: className,
+ actions: [addButton],
+ })
+ this.datalayer.map.fire('dataload', { id: id })
+ },
+})
diff --git a/umap/static/umap/js/umap.ui.js b/umap/static/umap/js/umap.ui.js
index cb78ca36..05425b7e 100644
--- a/umap/static/umap/js/umap.ui.js
+++ b/umap/static/umap/js/umap.ui.js
@@ -1,176 +1,209 @@
/*
-* Modals
-*/
+ * Modals
+ */
L.U.UI = L.Evented.extend({
+ ALERTS: Array(),
+ ALERT_ID: null,
+ TOOLTIP_ID: null,
- ALERTS: Array(),
- ALERT_ID: null,
- TOOLTIP_ID: null,
+ initialize: function (parent) {
+ this.parent = parent
+ this.container = L.DomUtil.create('div', 'leaflet-ui-container', this.parent)
+ L.DomEvent.disableClickPropagation(this.container)
+ L.DomEvent.on(this.container, 'contextmenu', L.DomEvent.stopPropagation) // Do not activate our custom context menu.
+ L.DomEvent.on(this.container, 'mousewheel', L.DomEvent.stopPropagation)
+ L.DomEvent.on(this.container, 'MozMousePixelScroll', L.DomEvent.stopPropagation)
+ this._panel = L.DomUtil.create('div', '', this.container)
+ this._panel.id = 'umap-ui-container'
+ this._alert = L.DomUtil.create('div', 'with-transition', this.container)
+ this._alert.id = 'umap-alert-container'
+ this._tooltip = L.DomUtil.create('div', '', this.container)
+ this._tooltip.id = 'umap-tooltip-container'
+ },
- initialize: function (parent) {
- this.parent = parent;
- this.container = L.DomUtil.create('div', 'leaflet-ui-container', this.parent);
- L.DomEvent.disableClickPropagation(this.container);
- L.DomEvent.on(this.container, 'contextmenu', L.DomEvent.stopPropagation); // Do not activate our custom context menu.
- L.DomEvent.on(this.container, 'mousewheel', L.DomEvent.stopPropagation);
- L.DomEvent.on(this.container, 'MozMousePixelScroll', L.DomEvent.stopPropagation);
- this._panel = L.DomUtil.create('div', '', this.container);
- this._panel.id = 'umap-ui-container';
- this._alert = L.DomUtil.create('div', 'with-transition', this.container);
- this._alert.id = 'umap-alert-container';
- this._tooltip = L.DomUtil.create('div', '', this.container);
- this._tooltip.id = 'umap-tooltip-container';
- },
+ resetPanelClassName: function () {
+ this._panel.className = 'with-transition'
+ },
- resetPanelClassName: function () {
- this._panel.className = 'with-transition';
- },
+ openPanel: function (e) {
+ this.fire('panel:open')
+ // We reset all because we can't know which class has been added
+ // by previous ui processes...
+ this.resetPanelClassName()
+ this._panel.innerHTML = ''
+ var actionsContainer = L.DomUtil.create('ul', 'toolbox', this._panel)
+ var body = L.DomUtil.create('div', 'body', this._panel)
+ if (e.data.html.nodeType && e.data.html.nodeType === 1)
+ body.appendChild(e.data.html)
+ else body.innerHTML = e.data.html
+ var closeLink = L.DomUtil.create('li', 'umap-close-link', actionsContainer)
+ L.DomUtil.add('i', 'umap-close-icon', closeLink)
+ var label = L.DomUtil.create('span', '', closeLink)
+ label.title = label.textContent = L._('Close')
+ if (e.actions) {
+ for (var i = 0; i < e.actions.length; i++) {
+ actionsContainer.appendChild(e.actions[i])
+ }
+ }
+ if (e.className) L.DomUtil.addClass(this._panel, e.className)
+ if (L.DomUtil.hasClass(this.parent, 'umap-ui')) {
+ // Already open.
+ this.fire('panel:ready')
+ } else {
+ L.DomEvent.once(
+ this._panel,
+ 'transitionend',
+ function (e) {
+ this.fire('panel:ready')
+ },
+ this
+ )
+ L.DomUtil.addClass(this.parent, 'umap-ui')
+ }
+ L.DomEvent.on(closeLink, 'click', this.closePanel, this)
+ },
- openPanel: function (e) {
- this.fire('panel:open');
- // We reset all because we can't know which class has been added
- // by previous ui processes...
- this.resetPanelClassName();
- this._panel.innerHTML = '';
- var actionsContainer = L.DomUtil.create('ul', 'toolbox', this._panel);
- var body = L.DomUtil.create('div', 'body', this._panel);
- if (e.data.html.nodeType && e.data.html.nodeType === 1) body.appendChild(e.data.html);
- else body.innerHTML = e.data.html;
- var closeLink = L.DomUtil.create('li', 'umap-close-link', actionsContainer);
- L.DomUtil.add('i', 'umap-close-icon', closeLink);
- var label = L.DomUtil.create('span', '', closeLink);
- label.title = label.textContent = L._('Close');
- if (e.actions) {
- for (var i = 0; i < e.actions.length; i++) {
- actionsContainer.appendChild(e.actions[i]);
- }
- }
- if (e.className) L.DomUtil.addClass(this._panel, e.className);
- if (L.DomUtil.hasClass(this.parent, 'umap-ui')) {
- // Already open.
- this.fire('panel:ready');
- } else {
- L.DomEvent.once(this._panel, 'transitionend', function (e) {
- this.fire('panel:ready');
- }, this);
- L.DomUtil.addClass(this.parent, 'umap-ui');
- }
- L.DomEvent.on(closeLink, 'click', this.closePanel, this);
- },
+ closePanel: function () {
+ this.resetPanelClassName()
+ L.DomUtil.removeClass(this.parent, 'umap-ui')
+ this.fire('panel:closed')
+ },
- closePanel: function () {
- this.resetPanelClassName();
- L.DomUtil.removeClass(this.parent, 'umap-ui');
- this.fire('panel:closed');
- },
+ alert: function (e) {
+ if (L.DomUtil.hasClass(this.parent, 'umap-alert')) this.ALERTS.push(e)
+ else this.popAlert(e)
+ },
- alert: function (e) {
- if (L.DomUtil.hasClass(this.parent, 'umap-alert')) this.ALERTS.push(e);
- else this.popAlert(e);
- },
+ popAlert: function (e) {
+ var self = this
+ if (!e) {
+ if (this.ALERTS.length) e = this.ALERTS.pop()
+ else return
+ }
+ var timeoutID,
+ level_class = e.level && e.level == 'info' ? 'info' : 'error'
+ this._alert.innerHTML = ''
+ L.DomUtil.addClass(this.parent, 'umap-alert')
+ L.DomUtil.addClass(this._alert, level_class)
+ var close = function () {
+ if (timeoutID !== this.ALERT_ID) {
+ return
+ } // Another alert has been forced
+ this._alert.innerHTML = ''
+ L.DomUtil.removeClass(this.parent, 'umap-alert')
+ L.DomUtil.removeClass(this._alert, level_class)
+ if (timeoutID) window.clearTimeout(timeoutID)
+ this.popAlert()
+ }
+ var closeLink = L.DomUtil.create('a', 'umap-close-link', this._alert)
+ closeLink.href = '#'
+ L.DomUtil.add('i', 'umap-close-icon', closeLink)
+ var label = L.DomUtil.create('span', '', closeLink)
+ label.title = label.textContent = L._('Close')
+ L.DomEvent.on(closeLink, 'click', L.DomEvent.stop).on(
+ closeLink,
+ 'click',
+ close,
+ this
+ )
+ L.DomUtil.add('div', '', this._alert, e.content)
+ if (e.actions) {
+ var action, el
+ for (var i = 0; i < e.actions.length; i++) {
+ action = e.actions[i]
+ el = L.DomUtil.element('a', { className: 'umap-action' }, this._alert)
+ el.href = '#'
+ el.textContent = action.label
+ L.DomEvent.on(el, 'click', L.DomEvent.stop).on(el, 'click', close, this)
+ if (action.callback)
+ L.DomEvent.on(
+ el,
+ 'click',
+ action.callback,
+ action.callbackContext || this.map
+ )
+ }
+ }
+ self.ALERT_ID = timeoutID = window.setTimeout(
+ L.bind(close, this),
+ e.duration || 3000
+ )
+ },
- popAlert: function (e) {
- var self = this;
- if(!e) {
- if (this.ALERTS.length) e = this.ALERTS.pop();
- else return;
- }
- var timeoutID,
- level_class = e.level && e.level == 'info'? 'info': 'error';
- this._alert.innerHTML = '';
- L.DomUtil.addClass(this.parent, 'umap-alert');
- L.DomUtil.addClass(this._alert, level_class);
- var close = function () {
- if (timeoutID !== this.ALERT_ID) { return;} // Another alert has been forced
- this._alert.innerHTML = '';
- L.DomUtil.removeClass(this.parent, 'umap-alert');
- L.DomUtil.removeClass(this._alert, level_class);
- if (timeoutID) window.clearTimeout(timeoutID);
- this.popAlert();
- };
- var closeLink = L.DomUtil.create('a', 'umap-close-link', this._alert);
- closeLink.href = '#';
- L.DomUtil.add('i', 'umap-close-icon', closeLink);
- var label = L.DomUtil.create('span', '', closeLink);
- label.title = label.textContent = L._('Close');
- L.DomEvent.on(closeLink, 'click', L.DomEvent.stop)
- .on(closeLink, 'click', close, this);
- L.DomUtil.add('div', '', this._alert, e.content);
- if (e.actions) {
- var action, el;
- for (var i = 0; i < e.actions.length; i++) {
- action = e.actions[i];
- el = L.DomUtil.element('a', {'className': 'umap-action'}, this._alert);
- el.href = '#';
- el.textContent = action.label;
- L.DomEvent.on(el, 'click', L.DomEvent.stop)
- .on(el, 'click', close, this);
- if (action.callback) L.DomEvent.on(el, 'click', action.callback, action.callbackContext || this.map);
- }
- }
- self.ALERT_ID = timeoutID = window.setTimeout(L.bind(close, this), e.duration || 3000);
- },
+ tooltip: function (e) {
+ this.TOOLTIP_ID = Math.random()
+ var id = this.TOOLTIP_ID
+ L.DomUtil.addClass(this.parent, 'umap-tooltip')
+ if (e.anchor && e.position === 'top') this.anchorTooltipTop(e.anchor)
+ else if (e.anchor && e.position === 'left') this.anchorTooltipLeft(e.anchor)
+ else this.anchorTooltipAbsolute()
+ this._tooltip.innerHTML = e.content
+ function closeIt() {
+ this.closeTooltip(id)
+ }
+ if (e.anchor) L.DomEvent.once(e.anchor, 'mouseout', closeIt, this)
+ if (e.duration !== Infinity)
+ window.setTimeout(L.bind(closeIt, this), e.duration || 3000)
+ },
- tooltip: function (e) {
- this.TOOLTIP_ID = Math.random();
- var id = this.TOOLTIP_ID;
- L.DomUtil.addClass(this.parent, 'umap-tooltip');
- if (e.anchor && e.position === 'top') this.anchorTooltipTop(e.anchor);
- else if (e.anchor && e.position === 'left') this.anchorTooltipLeft(e.anchor);
- else this.anchorTooltipAbsolute();
- this._tooltip.innerHTML = e.content;
- function closeIt () { this.closeTooltip(id); }
- if (e.anchor) L.DomEvent.once(e.anchor, 'mouseout', closeIt, this);
- if (e.duration !== Infinity) window.setTimeout(L.bind(closeIt, this), e.duration || 3000);
- },
+ anchorTooltipAbsolute: function () {
+ this._tooltip.className = ''
+ var left =
+ this.parent.offsetLeft +
+ this.parent.clientWidth / 2 -
+ this._tooltip.clientWidth / 2,
+ top = this.parent.offsetTop + 75
+ this.setTooltipPosition({ top: top, left: left })
+ },
- anchorTooltipAbsolute: function () {
- this._tooltip.className = '';
- var left = this.parent.offsetLeft + (this.parent.clientWidth / 2) - (this._tooltip.clientWidth / 2),
- top = this.parent.offsetTop + 75;
- this.setTooltipPosition({top: top, left: left});
- },
+ anchorTooltipTop: function (el) {
+ this._tooltip.className = 'tooltip-top'
+ var coords = this.getPosition(el)
+ this.setTooltipPosition({
+ left: coords.left - 10,
+ bottom: this.getDocHeight() - coords.top + 11,
+ })
+ },
- anchorTooltipTop: function (el) {
- this._tooltip.className = 'tooltip-top';
- var coords = this.getPosition(el);
- this.setTooltipPosition({left: coords.left - 10, bottom: this.getDocHeight() - coords.top + 11});
- },
+ anchorTooltipLeft: function (el) {
+ this._tooltip.className = 'tooltip-left'
+ var coords = this.getPosition(el)
+ this.setTooltipPosition({
+ top: coords.top,
+ right: document.documentElement.offsetWidth - coords.left + 11,
+ })
+ },
- anchorTooltipLeft: function (el) {
- this._tooltip.className = 'tooltip-left';
- var coords = this.getPosition(el);
- this.setTooltipPosition({top: coords.top, right: document.documentElement.offsetWidth - coords.left + 11});
- },
+ closeTooltip: function (id) {
+ if (id && id !== this.TOOLTIP_ID) return
+ this._tooltip.innerHTML = ''
+ L.DomUtil.removeClass(this.parent, 'umap-tooltip')
+ },
- closeTooltip: function (id) {
- if (id && id !== this.TOOLTIP_ID) return;
- this._tooltip.innerHTML = '';
- L.DomUtil.removeClass(this.parent, 'umap-tooltip');
- },
+ getPosition: function (el) {
+ return el.getBoundingClientRect()
+ },
- getPosition: function (el) {
- return el.getBoundingClientRect();
- },
+ setTooltipPosition: function (coords) {
+ if (coords.left) this._tooltip.style.left = coords.left + 'px'
+ else this._tooltip.style.left = 'initial'
+ if (coords.right) this._tooltip.style.right = coords.right + 'px'
+ else this._tooltip.style.right = 'initial'
+ if (coords.top) this._tooltip.style.top = coords.top + 'px'
+ else this._tooltip.style.top = 'initial'
+ if (coords.bottom) this._tooltip.style.bottom = coords.bottom + 'px'
+ else this._tooltip.style.bottom = 'initial'
+ },
- setTooltipPosition: function (coords) {
- if (coords.left) this._tooltip.style.left = coords.left + 'px';
- else this._tooltip.style.left = 'initial';
- if (coords.right) this._tooltip.style.right = coords.right + 'px';
- else this._tooltip.style.right = 'initial';
- if (coords.top) this._tooltip.style.top = coords.top + 'px';
- else this._tooltip.style.top = 'initial';
- if (coords.bottom) this._tooltip.style.bottom = coords.bottom + 'px';
- else this._tooltip.style.bottom = 'initial';
- },
-
- getDocHeight: function () {
- var D = document;
- return Math.max(
- D.body.scrollHeight, D.documentElement.scrollHeight,
- D.body.offsetHeight, D.documentElement.offsetHeight,
- D.body.clientHeight, D.documentElement.clientHeight
- );
- },
-
-});
+ getDocHeight: function () {
+ var D = document
+ return Math.max(
+ D.body.scrollHeight,
+ D.documentElement.scrollHeight,
+ D.body.offsetHeight,
+ D.documentElement.offsetHeight,
+ D.body.clientHeight,
+ D.documentElement.clientHeight
+ )
+ },
+})
diff --git a/umap/static/umap/js/umap.xhr.js b/umap/static/umap/js/umap.xhr.js
index 126f61a9..7e0ca806 100644
--- a/umap/static/umap/js/umap.xhr.js
+++ b/umap/static/umap/js/umap.xhr.js
@@ -1,285 +1,296 @@
L.U.Xhr = L.Evented.extend({
+ initialize: function (ui) {
+ this.ui = ui
+ },
- initialize: function (ui) {
- this.ui = ui;
- },
-
- _wrapper: function () {
- var wrapper;
- if (window.XMLHttpRequest === undefined) {
- wrapper = function() {
- try {
- return new window.ActiveXObject('Microsoft.XMLHTTP.6.0');
- }
- catch (e1) {
- try {
- return new window.ActiveXObject('Microsoft.XMLHTTP.3.0');
- }
- catch (e2) {
- throw new Error('XMLHttpRequest is not supported');
- }
- }
- };
- }
- else {
- wrapper = window.XMLHttpRequest;
- }
- return new wrapper();
- },
-
- _ajax: function (settings) {
- var xhr = this._wrapper(), id = Math.random(), self = this;
- this.fire('dataloading', {id: id});
- var loaded = function () {self.fire('dataload', {id: id});};
-
+ _wrapper: function () {
+ var wrapper
+ if (window.XMLHttpRequest === undefined) {
+ wrapper = function () {
try {
- xhr.open(settings.verb, settings.uri, true);
- } catch (err) {
- // Unknown protocol?
- this.ui.alert({content: L._('Error while fetching {url}', {url: settings.uri}), level: 'error'});
- loaded();
- return
+ return new window.ActiveXObject('Microsoft.XMLHTTP.6.0')
+ } catch (e1) {
+ try {
+ return new window.ActiveXObject('Microsoft.XMLHTTP.3.0')
+ } catch (e2) {
+ throw new Error('XMLHttpRequest is not supported')
+ }
}
+ }
+ } else {
+ wrapper = window.XMLHttpRequest
+ }
+ return new wrapper()
+ },
- if (settings.uri.indexOf('http') !== 0 || settings.uri.indexOf(window.location.origin) === 0) {
- // "X-" mode headers cause the request to be in preflight mode,
- // we don"t want that by default for CORS requests
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- }
- if (settings.headers) {
- for (var name in settings.headers) {
- xhr.setRequestHeader(name, settings.headers[name]);
- }
- }
-
-
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- if (xhr.status == 200) {
- settings.callback.call(settings.context || xhr, xhr.responseText, xhr);
- }
- else if (xhr.status === 403) {
- self.ui.alert({content: xhr.responseText || L._('Action not allowed :('), level: 'error'});
- }
- else if (xhr.status === 412) {
- var msg = L._('Woops! Someone else seems to have edited the data. You can save anyway, but this will erase the changes made by others.');
- var actions = [
- {
- label: L._('Save anyway'),
- callback: function () {
- delete settings.headers['If-Match'];
- self._ajax(settings);
- },
- callbackContext: self
- },
- {
- label: L._('Cancel')
- }
- ];
- self.ui.alert({content: msg, level: 'error', duration: 100000, actions: actions});
- }
- else {
- if (xhr.status !== 0) { // 0 === request cut by user
- self.ui.alert({'content': L._('Problem in the response'), 'level': 'error'});
- }
- }
- loaded();
- }
- };
-
- try {
- xhr.send(settings.data);
- } catch (e) {
- // Pass
- loaded();
- console.error('Bad Request', e);
- }
- },
-
- // supports only JSON as response data type
- _json: function (verb, uri, options) {
- var args = arguments,
- self = this;
- var default_options = {
- 'async': true,
- 'callback': null,
- 'responseType': 'text',
- 'data': null,
- 'listen_form': null // optional form to listen in default callback
- };
- var settings = L.Util.extend({}, default_options, options);
-
- if (verb === 'POST') {
- // find a way not to make this django specific
- var token = document.cookie.replace(/(?:(?:^|.*;\s*)csrftoken\s*\=\s*([^;]*).*$)|^.*$/, '$1');
- if (token) {
- settings.headers = settings.headers || {};
- settings.headers['X-CSRFToken'] = token;
- }
- }
-
- var callback = function(responseText, response) {
- var data;
- try {
- data = JSON.parse(responseText);
- }
- catch (err) {
- console.log(err);
- self.ui.alert({content: L._('Problem in the response format'), level: 'error'});
- return;
- }
- if (data.errors) {
- console.log(data.errors);
- self.ui.alert({content: L._('An error occured'), level: 'error'});
- } else if (data.login_required) {
- // login_required should be an URL for the login form
- if (settings.login_callback) settings.login_callback(data);
- else self.login(data, args);
- }
- else {
- if (settings.callback) L.bind(settings.callback, settings.context || this)(data, response);
- else self.default_callback(data, settings, response);
- }
- };
-
- this._ajax({
- verb: verb,
- uri: uri,
- data: settings.data,
- callback: callback,
- headers: settings.headers,
- listener: settings.listener
- });
- },
-
- get: function(uri, options) {
- this._json('GET', uri, options);
- },
-
- post: function(uri, options) {
- this._json('POST', uri, options);
- },
-
- submit_form: function(form_id, options) {
- if(typeof options === 'undefined') options = {};
- var form = L.DomUtil.get(form_id);
- var formData = new FormData(form);
- if(options.extraFormData) formData.append(options.extraFormData);
- options.data = formData;
- this.post(form.action, options);
- return false;
- },
-
- listen_form: function (form_id, options) {
- var form = L.DomUtil.get(form_id), self = this;
- if (!form) return;
- L.DomEvent
- .on(form, 'submit', L.DomEvent.stopPropagation)
- .on(form, 'submit', L.DomEvent.preventDefault)
- .on(form, 'submit', function () {
- self.submit_form(form_id, options);
- });
- },
-
- listen_link: function (link_id, options) {
- var link = L.DomUtil.get(link_id), self = this;
- if (link) {
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', function () {
- if (options.confirm && !confirm(options.confirm)) { return;}
- self.get(link.href, options);
- });
- }
- },
-
- default_callback: function (data, options) {
- // default callback, to avoid boilerplate
- if (data.redirect) {
- var newPath = data.redirect;
- if (window.location.pathname == newPath) window.location.reload(); // Keep the hash, so the current view
- else window.location = newPath;
- }
- else if (data.info) {
- this.ui.alert({content: data.info, level: 'info'});
- this.ui.closePanel();
- }
- else if (data.error) {
- this.ui.alert({content: data.error, level: 'error'});
- }
- else if (data.html) {
- var ui_options = {'data': data},
- listen_options;
- if (options.className) ui_options.className = options.className;
- this.ui.openPanel(ui_options);
- // To low boilerplate, if there is a form, listen it
- if (options.listen_form) {
- // Listen form again
- listen_options = L.Util.extend({}, options, options.listen_form.options);
- this.listen_form(options.listen_form.id, listen_options);
- }
- if (options.listen_link) {
- for (var i=0, l=options.listen_link.length; i