Merge pull request #1085 from umap-project/prettierjs

Apply PrettierJS to the whole codebase
This commit is contained in:
David Larlet 2023-05-12 14:10:07 -04:00 committed by GitHub
commit db2da8f407
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 8789 additions and 7615 deletions

6
.prettierrc.yaml Normal file
View file

@ -0,0 +1,6 @@
trailingComma: "es5"
tabWidth: 2
semi: false
singleQuote: true
printWidth: 88
quoteProps: "consistent"

View file

@ -52,3 +52,9 @@ tx_push:
tx push -s tx push -s
tx_pull: tx_pull:
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}

22
package-lock.json generated
View file

@ -40,6 +40,7 @@
"mocha-phantomjs": "^4.0.1", "mocha-phantomjs": "^4.0.1",
"optimist": "~0.4.0", "optimist": "~0.4.0",
"phantomjs": "^1.9.18", "phantomjs": "^1.9.18",
"prettier": "^2.8.8",
"sinon": "^1.10.3", "sinon": "^1.10.3",
"uglify-js": "~3.17.4" "uglify-js": "~3.17.4"
} }
@ -1920,6 +1921,21 @@
"node": ">=0.10.0" "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": { "node_modules/process-nextick-args": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
@ -4071,6 +4087,12 @@
"pinkie": "^2.0.0" "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": { "process-nextick-args": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",

View file

@ -12,6 +12,7 @@
"mocha-phantomjs": "^4.0.1", "mocha-phantomjs": "^4.0.1",
"optimist": "~0.4.0", "optimist": "~0.4.0",
"phantomjs": "^1.9.18", "phantomjs": "^1.9.18",
"prettier": "^2.8.8",
"sinon": "^1.10.3", "sinon": "^1.10.3",
"uglify-js": "~3.17.4" "uglify-js": "~3.17.4"
}, },

View file

@ -1,314 +1,341 @@
L.U.AutoComplete = L.Class.extend({ L.U.AutoComplete = L.Class.extend({
options: {
placeholder: 'Start typing...',
emptyMessage: 'No result',
allowFree: true,
minChar: 2,
maxResults: 5,
},
options: { CACHE: '',
placeholder: 'Start typing...', RESULTS: [],
emptyMessage: 'No result',
allowFree: true,
minChar: 2,
maxResults: 5
},
CACHE: '', initialize: function (el, options) {
RESULTS: [], this.el = el
var ui = new L.U.UI(document.querySelector('header'))
initialize: function (el, options) { this.xhr = new L.U.Xhr(ui)
this.el = el; L.setOptions(this, options)
var ui = new L.U.UI(document.querySelector('header')); var CURRENT = null
this.xhr = new L.U.Xhr(ui); try {
L.setOptions(this, options); Object.defineProperty(this, 'CURRENT', {
var CURRENT = null; get: function () {
try { return CURRENT
Object.defineProperty(this, 'CURRENT', { },
get: function () { set: function (index) {
return CURRENT; if (typeof index === 'object') {
}, index = this.resultToIndex(index)
set: function (index) { }
if (typeof index === 'object') { CURRENT = index
index = this.resultToIndex(index); },
} })
CURRENT = index; } catch (e) {
} // Hello IE8
});
} 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);
} }
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({ 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) { optionToResult: function (option) {
L.U.AutoComplete.prototype.initialize.call(this, el, options); return {
if (!this.el) return this; value: option.value,
this.createInput(); label: option.innerHTML,
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});
} }
},
}); _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({ 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 () { displaySelected: function (result) {
return L.DomUtil.after(this.input, L.DomUtil.element('ul', {className: 'umap-multiresult'})); 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)
displaySelected: function (result) { close.textContent = '×'
var result_el = L.DomUtil.element('li', {}, this.selected_container); L.DomEvent.on(
result_el.textContent = result.item.label; close,
var close = L.DomUtil.element('span', {className: 'close'}, result_el); 'click',
close.textContent = '×'; function () {
L.DomEvent.on(close, 'click', function () { this.selected_container.removeChild(result_el)
this.selected_container.removeChild(result_el); this.options.on_unselect(result)
this.options.on_unselect(result); },
}, this); this
this.hide(); )
} this.hide()
},
}); })
L.U.AutoComplete.Ajax.Select = L.U.AutoComplete.Ajax.extend({ 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 () { displaySelected: function (result) {
return L.DomUtil.after(this.input, L.DomUtil.element('div', {className: 'umap-singleresult'})); 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)
displaySelected: function (result) { close.textContent = '×'
var result_el = L.DomUtil.element('div', {}, this.selected_container); this.input.style.display = 'none'
result_el.textContent = result.item.label; L.DomEvent.on(
var close = L.DomUtil.element('span', {className: 'close'}, result_el); close,
close.textContent = '×'; 'click',
this.input.style.display = 'none'; function () {
L.DomEvent.on(close, 'click', function () { this.selected_container.innerHTML = ''
this.selected_container.innerHTML = ''; this.input.style.display = 'block'
this.input.style.display = 'block'; },
}, this); this
this.hide(); )
} this.hide()
},
}); })

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,189 +1,199 @@
L.U.Icon = L.DivIcon.extend({ L.U.Icon = L.DivIcon.extend({
initialize: function(map, options) { initialize: function (map, options) {
this.map = map; this.map = map
var default_options = { var default_options = {
iconSize: null, // Made in css iconSize: null, // Made in css
iconUrl: this.map.getDefaultOption('iconUrl'), iconUrl: this.map.getDefaultOption('iconUrl'),
feature: null 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() : {});
} }
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({ L.U.Icon.Default = L.U.Icon.extend({
default_options: { default_options: {
iconAnchor: new L.Point(16, 40), iconAnchor: new L.Point(16, 40),
popupAnchor: new L.Point(0, -40), popupAnchor: new L.Point(0, -40),
tooltipAnchor: new L.Point(16, -24), tooltipAnchor: new L.Point(16, -24),
className: 'umap-div-icon' className: 'umap-div-icon',
}, },
initialize: function(map, options) { initialize: function (map, options) {
options = L.Util.extend({}, this.default_options, options); options = L.Util.extend({}, this.default_options, options)
L.U.Icon.prototype.initialize.call(this, map, options); L.U.Icon.prototype.initialize.call(this, map, options)
}, },
_setColor: function() { _setColor: function () {
var color = this._getColor(); var color = this._getColor()
this.elements.container.style.backgroundColor = color; this.elements.container.style.backgroundColor = color
this.elements.arrow.style.borderTopColor = color; this.elements.arrow.style.borderTopColor = color
}, },
createIcon: function() { createIcon: function () {
this.elements = {}; this.elements = {}
this.elements.main = L.DomUtil.create('div'); this.elements.main = L.DomUtil.create('div')
this.elements.container = L.DomUtil.create('div', 'icon_container', this.elements.main); this.elements.container = L.DomUtil.create(
this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main); 'div',
var src = this._getIconUrl('icon'); 'icon_container',
if (src) { this.elements.main
// An url. )
if (src.indexOf('http') === 0 || src.indexOf('/') === 0 || src.indexOf('data:image') === 0) { this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main)
this.elements.img = L.DomUtil.create('img', null, this.elements.container); var src = this._getIconUrl('icon')
this.elements.img.src = src; if (src) {
} else { // An url.
this.elements.span = L.DomUtil.create('span', null, this.elements.container) if (
this.elements.span.textContent = src; src.indexOf('http') === 0 ||
} src.indexOf('/') === 0 ||
} src.indexOf('data:image') === 0
this._setColor(); ) {
this._setIconStyles(this.elements.main, 'icon'); this.elements.img = L.DomUtil.create('img', null, this.elements.container)
return this.elements.main; 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({ L.U.Icon.Circle = L.U.Icon.extend({
initialize: function(map, options) { initialize: function (map, options) {
var default_options = { var default_options = {
iconAnchor: new L.Point(6, 6), iconAnchor: new L.Point(6, 6),
popupAnchor: new L.Point(0, -6), popupAnchor: new L.Point(0, -6),
tooltipAnchor: new L.Point(6, 0), tooltipAnchor: new L.Point(6, 0),
className: 'umap-circle-icon' 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 = '&nbsp;';
this._setColor();
this._setIconStyles(this.elements.main, 'icon');
return this.elements.main;
} }
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 = '&nbsp;'
this._setColor()
this._setIconStyles(this.elements.main, 'icon')
return this.elements.main
},
})
L.U.Icon.Drop = L.U.Icon.Default.extend({ L.U.Icon.Drop = L.U.Icon.Default.extend({
default_options: { default_options: {
iconAnchor: new L.Point(16, 42), iconAnchor: new L.Point(16, 42),
popupAnchor: new L.Point(0, -42), popupAnchor: new L.Point(0, -42),
tooltipAnchor: new L.Point(16, -24), tooltipAnchor: new L.Point(16, -24),
className: 'umap-drop-icon' className: 'umap-drop-icon',
} },
}); })
L.U.Icon.Ball = L.U.Icon.Default.extend({ L.U.Icon.Ball = L.U.Icon.Default.extend({
default_options: { default_options: {
iconAnchor: new L.Point(8, 30), iconAnchor: new L.Point(8, 30),
popupAnchor: new L.Point(0, -28), popupAnchor: new L.Point(0, -28),
tooltipAnchor: new L.Point(8, -23), tooltipAnchor: new L.Point(8, -23),
className: 'umap-ball-icon' className: 'umap-ball-icon',
}, },
createIcon: function() { createIcon: function () {
this.elements = {}; this.elements = {}
this.elements.main = L.DomUtil.create('div'); this.elements.main = L.DomUtil.create('div')
this.elements.container = L.DomUtil.create('div', 'icon_container', this.elements.main); this.elements.container = L.DomUtil.create(
this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main); 'div',
this._setColor(); 'icon_container',
this._setIconStyles(this.elements.main, 'icon'); this.elements.main
return 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() { _setColor: function () {
var color = this._getColor('color'), var color = this._getColor('color'),
background; background
if (L.Browser.ielt9) { if (L.Browser.ielt9) {
background = color; background = color
} } else if (L.Browser.webkit) {
else if (L.Browser.webkit) { background =
background = '-webkit-gradient( radial, 6 38%, 0, 6 38%, 8, from(white), to(' + color + ') )'; '-webkit-gradient( radial, 6 38%, 0, 6 38%, 8, from(white), to(' + color + ') )'
} } else {
else { background =
background = 'radial-gradient(circle at 6px 38% , white -4px, ' + color + ' 8px) repeat scroll 0 0 transparent'; 'radial-gradient(circle at 6px 38% , white -4px, ' +
} color +
this.elements.container.style.background = background; ' 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({ L.U.Icon.Cluster = L.DivIcon.extend({
options: { options: {
iconSize: [40, 40] iconSize: [40, 40],
}, },
initialize: function (datalayer, cluster) { initialize: function (datalayer, cluster) {
this.datalayer = datalayer; this.datalayer = datalayer
this.cluster = cluster; this.cluster = cluster
}, },
createIcon: function () { createIcon: function () {
var container = L.DomUtil.create('div', 'leaflet-marker-icon marker-cluster'), var container = L.DomUtil.create('div', 'leaflet-marker-icon marker-cluster'),
div = L.DomUtil.create('div', '', container), div = L.DomUtil.create('div', '', container),
span = L.DomUtil.create('span', '', div), span = L.DomUtil.create('span', '', div),
backgroundColor = this.datalayer.getColor(); backgroundColor = this.datalayer.getColor()
span.textContent = this.cluster.getChildCount(); span.textContent = this.cluster.getChildCount()
div.style.backgroundColor = backgroundColor; div.style.backgroundColor = backgroundColor
return container; return container
}, },
computeTextColor: function (el) { computeTextColor: function (el) {
var color, var color,
backgroundColor = this.datalayer.getColor(); backgroundColor = this.datalayer.getColor()
if (this.datalayer.options.cluster && this.datalayer.options.cluster.textColor) { if (this.datalayer.options.cluster && this.datalayer.options.cluster.textColor) {
color = 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;
} }
if (!color) {
}); if (typeof _CACHE_COLOR[backgroundColor] === 'undefined') {
color = L.DomUtil.TextColorFromBackgroundColor(el)
_CACHE_COLOR[backgroundColor] = color
} else {
color = _CACHE_COLOR[backgroundColor]
}
}
return color
},
})

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,143 +1,196 @@
// Dedicated object so we can deal with a separate dirty status, and thus // 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. // call the endpoint only when needed, saving one call at each save.
L.U.MapPermissions = L.Class.extend({ L.U.MapPermissions = L.Class.extend({
options: {
owner: null,
editors: [],
share_status: null,
edit_status: null,
},
options: { initialize: function (map) {
owner: null, this.setOptions(map.options.permissions)
editors: [], this.map = map
share_status: null, var isDirty = false,
edit_status: null self = this
}, try {
Object.defineProperty(this, 'isDirty', {
initialize: function (map) { get: function () {
this.setOptions(map.options.permissions); return isDirty
this.map = map; },
var isDirty = false, set: function (status) {
self = this; isDirty = status
try { if (status) self.map.isDirty = status
Object.defineProperty(this, 'isDirty', { },
get: function () { })
return isDirty; } catch (e) {
}, // Certainly IE8, which has a limited version of defineProperty
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:<br>{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);
} }
},
}); 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:<br>{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)
},
})

View file

@ -1,224 +1,239 @@
/* Shapes */ /* Shapes */
L.U.Popup = L.Popup.extend({ L.U.Popup = L.Popup.extend({
options: {
parseTemplate: true,
},
options: { initialize: function (feature) {
parseTemplate: true 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) { format: function () {
this.feature = feature; var mode = this.feature.getOption('popupTemplate') || 'Default',
this.container = L.DomUtil.create('div', 'umap-popup'); klass = L.U.PopupTemplate[mode] || L.U.PopupTemplate.Default
this.format(); this.content = new klass(this.feature, this.container)
L.Popup.prototype.initialize.call(this, {}, feature); this.content.render()
this.setContent(this.container); var els = this.container.querySelectorAll('img,iframe')
}, for (var i = 0; i < els.length; i++) {
this.onElementLoaded(els[i])
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);
} }
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({ L.U.Popup.Large = L.U.Popup.extend({
options: { options: {
maxWidth: 500, maxWidth: 500,
className: 'umap-popup-large' className: 'umap-popup-large',
} },
}); })
L.U.Popup.Panel = L.U.Popup.extend({ L.U.Popup.Panel = L.U.Popup.extend({
options: {
zoomAnimation: false,
},
options: { allButton: function () {
zoomAnimation: false 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 () { update: function () {
var button = L.DomUtil.create('li', ''); this.feature.map.ui.openPanel({
L.DomUtil.create('i', 'umap-icon-16 umap-list', button); data: { html: this._content },
var label = L.DomUtil.create('span', '', button); actions: [this.allButton()],
label.textContent = label.title = L._('See all'); })
L.DomEvent.on(button, 'click', this.feature.map.openBrowser, this.feature.map); },
return button;
},
update: function () { onRemove: function (map) {
this.feature.map.ui.openPanel({data: {html: this._content}, actions: [this.allButton()]}); map.ui.closePanel()
}, L.U.Popup.prototype.onRemove.call(this, map)
},
onRemove: function (map) { _initLayout: function () {
map.ui.closePanel(); this._container = L.DomUtil.create('span')
L.U.Popup.prototype.onRemove.call(this, map); },
}, _updateLayout: function () {},
_updatePosition: function () {},
_initLayout: function () {this._container = L.DomUtil.create('span');}, _adjustPan: function () {},
_updateLayout: function () {}, })
_updatePosition: function () {}, L.U.Popup.SimplePanel = L.U.Popup.Panel // Retrocompat.
_adjustPan: function () {}
});
L.U.Popup.SimplePanel = L.U.Popup.Panel; // Retrocompat.
/* Content templates */ /* Content templates */
L.U.PopupTemplate = {}; L.U.PopupTemplate = {}
L.U.PopupTemplate.Default = L.Class.extend({ L.U.PopupTemplate.Default = L.Class.extend({
initialize: function (feature, container) {
this.feature = feature
this.container = container
},
initialize: function (feature, container) { renderTitle: function () {},
this.feature = feature;
this.container = container;
},
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 () { renderFooter: function () {
var template = this.feature.getOption('popupContentTemplate'), if (this.feature.hasPopupFooter()) {
container = L.DomUtil.create('div', 'umap-popup-container'), var footerContainer = L.DomUtil.create(
content = '', properties, center; 'div',
properties = this.feature.extendedProperties(); 'umap-footer-container',
// Resolve properties inside description this.container
properties.description = L.Util.greedyTemplate(this.feature.properties.description || '', properties); ),
content = L.Util.greedyTemplate(template, properties); footer = L.DomUtil.create('ul', 'umap-popup-footer', footerContainer),
content = L.Util.toHTML(content); previousLi = L.DomUtil.create('li', 'previous', footer),
container.innerHTML = content; zoomLi = L.DomUtil.create('li', 'zoom', footer),
return container; nextLi = L.DomUtil.create('li', 'next', footer),
}, next = this.feature.getNext(),
prev = this.feature.getPrevious()
renderFooter: function () { if (next)
if (this.feature.hasPopupFooter()) { nextLi.title = L._('Go to «{feature}»', {
var footerContainer = L.DomUtil.create('div', 'umap-footer-container', this.container), feature: next.properties.name || L._('next'),
footer = L.DomUtil.create('ul', 'umap-popup-footer', footerContainer), })
previousLi = L.DomUtil.create('li', 'previous', footer), if (prev)
zoomLi = L.DomUtil.create('li', 'zoom', footer), previousLi.title = L._('Go to «{feature}»', {
nextLi = L.DomUtil.create('li', 'next', footer), feature: prev.properties.name || L._('previous'),
next = this.feature.getNext(), })
prev = this.feature.getPrevious(); zoomLi.title = L._('Zoom to this feature')
if (next) nextLi.title = L._('Go to «{feature}»', {feature: next.properties.name || L._('next')}); L.DomEvent.on(nextLi, 'click', function () {
if (prev) previousLi.title = L._('Go to «{feature}»', {feature: prev.properties.name || L._('previous')}); if (next) next.zoomTo({ callback: next.view })
zoomLi.title = L._('Zoom to this feature'); })
L.DomEvent.on(nextLi, 'click', function () { L.DomEvent.on(previousLi, 'click', function () {
if (next) next.zoomTo({callback: next.view}); if (prev) prev.zoomTo({ callback: prev.view })
}); })
L.DomEvent.on(previousLi, 'click', function () { L.DomEvent.on(
if (prev) prev.zoomTo({callback: prev.view}); zoomLi,
}); 'click',
L.DomEvent.on(zoomLi, 'click', function () { function () {
this.zoomTo(); this.zoomTo()
}, this.feature); },
} 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();
} }
},
}); 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({ L.U.PopupTemplate.BaseWithTitle = L.U.PopupTemplate.Default.extend({
renderTitle: function () {
renderTitle: function () { var title
var title; if (this.feature.getDisplayName()) {
if (this.feature.getDisplayName()) { title = L.DomUtil.create('h3', 'popup-title')
title = L.DomUtil.create('h3', 'popup-title'); title.textContent = this.feature.getDisplayName()
title.textContent = this.feature.getDisplayName();
}
return title;
} }
return title
}); },
})
L.U.PopupTemplate.Table = L.U.PopupTemplate.BaseWithTitle.extend({ L.U.PopupTemplate.Table = L.U.PopupTemplate.BaseWithTitle.extend({
formatRow: function (key, value) {
formatRow: function (key, value) { if (value.indexOf('http') === 0) {
if (value.indexOf('http') === 0) { value = '<a href="' + value + '" target="_blank">' + value + '</a>'
value = '<a href="' + value + '" target="_blank">' + value + '</a>';
}
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;
} }
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({ L.U.PopupTemplate.GeoRSSImage = L.U.PopupTemplate.BaseWithTitle.extend({
options: {
minWidth: 300,
maxWidth: 500,
className: 'umap-popup-large umap-georss-image',
},
options: { renderBody: function () {
minWidth: 300, var container = L.DomUtil.create('a')
maxWidth: 500, container.href = this.feature.properties.link
className: 'umap-popup-large umap-georss-image' container.target = '_blank'
}, if (this.feature.properties.img) {
var img = L.DomUtil.create('img', '', container)
renderBody: function () { img.src = this.feature.properties.img
var container = L.DomUtil.create('a'); // Sadly, we are unable to override this from JS the clean way
container.href = this.feature.properties.link; // See https://github.com/Leaflet/Leaflet/commit/61d746818b99d362108545c151a27f09d60960ee#commitcomment-6061847
container.target = '_blank'; img.style.maxWidth = this.options.maxWidth + 'px'
if (this.feature.properties.img) { img.style.maxHeight = this.options.maxWidth + 'px'
var img = L.DomUtil.create('img', '', container); this.onElementLoaded(img)
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;
} }
return container
}); },
})
L.U.PopupTemplate.GeoRSSLink = L.U.PopupTemplate.Default.extend({ L.U.PopupTemplate.GeoRSSLink = L.U.PopupTemplate.Default.extend({
options: {
className: 'umap-georss-link',
},
options: { renderBody: function () {
className: 'umap-georss-link' var title = this.renderTitle(this),
}, a = L.DomUtil.add('a')
a.href = this.feature.properties.link
renderBody: function () { a.target = '_blank'
var title = this.renderTitle(this), a.appendChild(title)
a = L.DomUtil.add('a'); return a
a.href = this.feature.properties.link; },
a.target = '_blank'; })
a.appendChild(title);
return a;
}
});

View file

@ -1,164 +1,163 @@
L.U.Slideshow = L.Class.extend({ L.U.Slideshow = L.Class.extend({
statics: {
CLASSNAME: 'umap-slideshow-active',
},
statics: { options: {
CLASSNAME: 'umap-slideshow-active' delay: 5000,
}, autoplay: false,
},
options: { initialize: function (map, options) {
delay: 5000, this.setOptions(options)
autoplay: false this.map = map
}, this._id = null
var current = null, // current feature
initialize: function (map, options) { self = this
this.setOptions(options); try {
this.map = map; Object.defineProperty(this, 'current', {
this._id = null; get: function () {
var current = null, // current feature if (!current) {
self = this; var datalayer = this.defaultDatalayer()
try { if (datalayer) current = datalayer.getFeatureByIndex(0)
Object.defineProperty(this, 'current', { }
get: function () { return current
if (!current) { },
var datalayer = this.defaultDatalayer(); set: function (feature) {
if (datalayer) current = datalayer.getFeatureByIndex(0); current = feature
} },
return current; })
}, } catch (e) {
set: function (feature) { // Certainly IE8, which has a limited version of defineProperty
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;
} }
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
},
})

View file

@ -1,107 +1,121 @@
L.U.TableEditor = L.Class.extend({ 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) { renderHeaders: function () {
this.datalayer = datalayer; this.header.innerHTML = ''
this.table = L.DomUtil.create('div', 'table'); for (var i = 0; i < this.properties.length; i++) {
this.header = L.DomUtil.create('div', 'thead', this.table); this.renderHeader(this.properties[i])
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});
} }
},
}); 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 })
},
})

View file

@ -1,176 +1,209 @@
/* /*
* Modals * Modals
*/ */
L.U.UI = L.Evented.extend({ L.U.UI = L.Evented.extend({
ALERTS: Array(),
ALERT_ID: null,
TOOLTIP_ID: null,
ALERTS: Array(), initialize: function (parent) {
ALERT_ID: null, this.parent = parent
TOOLTIP_ID: null, 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) { resetPanelClassName: function () {
this.parent = parent; this._panel.className = 'with-transition'
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 () { openPanel: function (e) {
this._panel.className = 'with-transition'; 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) { closePanel: function () {
this.fire('panel:open'); this.resetPanelClassName()
// We reset all because we can't know which class has been added L.DomUtil.removeClass(this.parent, 'umap-ui')
// by previous ui processes... this.fire('panel:closed')
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 () { alert: function (e) {
this.resetPanelClassName(); if (L.DomUtil.hasClass(this.parent, 'umap-alert')) this.ALERTS.push(e)
L.DomUtil.removeClass(this.parent, 'umap-ui'); else this.popAlert(e)
this.fire('panel:closed'); },
},
alert: function (e) { popAlert: function (e) {
if (L.DomUtil.hasClass(this.parent, 'umap-alert')) this.ALERTS.push(e); var self = this
else this.popAlert(e); 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) { tooltip: function (e) {
var self = this; this.TOOLTIP_ID = Math.random()
if(!e) { var id = this.TOOLTIP_ID
if (this.ALERTS.length) e = this.ALERTS.pop(); L.DomUtil.addClass(this.parent, 'umap-tooltip')
else return; if (e.anchor && e.position === 'top') this.anchorTooltipTop(e.anchor)
} else if (e.anchor && e.position === 'left') this.anchorTooltipLeft(e.anchor)
var timeoutID, else this.anchorTooltipAbsolute()
level_class = e.level && e.level == 'info'? 'info': 'error'; this._tooltip.innerHTML = e.content
this._alert.innerHTML = ''; function closeIt() {
L.DomUtil.addClass(this.parent, 'umap-alert'); this.closeTooltip(id)
L.DomUtil.addClass(this._alert, level_class); }
var close = function () { if (e.anchor) L.DomEvent.once(e.anchor, 'mouseout', closeIt, this)
if (timeoutID !== this.ALERT_ID) { return;} // Another alert has been forced if (e.duration !== Infinity)
this._alert.innerHTML = ''; window.setTimeout(L.bind(closeIt, this), e.duration || 3000)
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) { anchorTooltipAbsolute: function () {
this.TOOLTIP_ID = Math.random(); this._tooltip.className = ''
var id = this.TOOLTIP_ID; var left =
L.DomUtil.addClass(this.parent, 'umap-tooltip'); this.parent.offsetLeft +
if (e.anchor && e.position === 'top') this.anchorTooltipTop(e.anchor); this.parent.clientWidth / 2 -
else if (e.anchor && e.position === 'left') this.anchorTooltipLeft(e.anchor); this._tooltip.clientWidth / 2,
else this.anchorTooltipAbsolute(); top = this.parent.offsetTop + 75
this._tooltip.innerHTML = e.content; this.setTooltipPosition({ top: top, left: left })
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 () { anchorTooltipTop: function (el) {
this._tooltip.className = ''; this._tooltip.className = 'tooltip-top'
var left = this.parent.offsetLeft + (this.parent.clientWidth / 2) - (this._tooltip.clientWidth / 2), var coords = this.getPosition(el)
top = this.parent.offsetTop + 75; this.setTooltipPosition({
this.setTooltipPosition({top: top, left: left}); left: coords.left - 10,
}, bottom: this.getDocHeight() - coords.top + 11,
})
},
anchorTooltipTop: function (el) { anchorTooltipLeft: function (el) {
this._tooltip.className = 'tooltip-top'; this._tooltip.className = 'tooltip-left'
var coords = this.getPosition(el); var coords = this.getPosition(el)
this.setTooltipPosition({left: coords.left - 10, bottom: this.getDocHeight() - coords.top + 11}); this.setTooltipPosition({
}, top: coords.top,
right: document.documentElement.offsetWidth - coords.left + 11,
})
},
anchorTooltipLeft: function (el) { closeTooltip: function (id) {
this._tooltip.className = 'tooltip-left'; if (id && id !== this.TOOLTIP_ID) return
var coords = this.getPosition(el); this._tooltip.innerHTML = ''
this.setTooltipPosition({top: coords.top, right: document.documentElement.offsetWidth - coords.left + 11}); L.DomUtil.removeClass(this.parent, 'umap-tooltip')
}, },
closeTooltip: function (id) { getPosition: function (el) {
if (id && id !== this.TOOLTIP_ID) return; return el.getBoundingClientRect()
this._tooltip.innerHTML = ''; },
L.DomUtil.removeClass(this.parent, 'umap-tooltip');
},
getPosition: function (el) { setTooltipPosition: function (coords) {
return el.getBoundingClientRect(); 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) { getDocHeight: function () {
if (coords.left) this._tooltip.style.left = coords.left + 'px'; var D = document
else this._tooltip.style.left = 'initial'; return Math.max(
if (coords.right) this._tooltip.style.right = coords.right + 'px'; D.body.scrollHeight,
else this._tooltip.style.right = 'initial'; D.documentElement.scrollHeight,
if (coords.top) this._tooltip.style.top = coords.top + 'px'; D.body.offsetHeight,
else this._tooltip.style.top = 'initial'; D.documentElement.offsetHeight,
if (coords.bottom) this._tooltip.style.bottom = coords.bottom + 'px'; D.body.clientHeight,
else this._tooltip.style.bottom = 'initial'; 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
);
},
});

View file

@ -1,285 +1,296 @@
L.U.Xhr = L.Evented.extend({ L.U.Xhr = L.Evented.extend({
initialize: function (ui) {
this.ui = ui
},
initialize: function (ui) { _wrapper: function () {
this.ui = ui; var wrapper
}, if (window.XMLHttpRequest === undefined) {
wrapper = function () {
_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});};
try { try {
xhr.open(settings.verb, settings.uri, true); return new window.ActiveXObject('Microsoft.XMLHTTP.6.0')
} catch (err) { } catch (e1) {
// Unknown protocol? try {
this.ui.alert({content: L._('Error while fetching {url}', {url: settings.uri}), level: 'error'}); return new window.ActiveXObject('Microsoft.XMLHTTP.3.0')
loaded(); } catch (e2) {
return 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) { _ajax: function (settings) {
// "X-" mode headers cause the request to be in preflight mode, var xhr = this._wrapper(),
// we don"t want that by default for CORS requests id = Math.random(),
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); self = this
} this.fire('dataloading', { id: id })
if (settings.headers) { var loaded = function () {
for (var name in settings.headers) { self.fire('dataload', { id: id })
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<l; i++) {
// Listen link again
listen_options = L.Util.extend({}, options, options.listen_link[i].options);
this.listen_link(options.listen_link[i].id, listen_options);
}
}
}
else if (options.success) {
// Success is called only if data contain no msg and no html
options.success(data);
}
},
login: function (data, args) {
// data.html: login form
// args: args of the first _json call, to call again at process end
var self = this;
var proceed = function () {
self.ui.closePanel();
if (typeof args !== 'undefined') self._json.apply(self, args);
else self.default_callback(data, {});
};
var ask_for_login = function (data) {
self.ui.openPanel({'data': data, className: 'login-panel'});
self.listen_form('login_form', {
'callback': function (data) {
if (data.html) ask_for_login(data); // Problem in the login - ask again
else proceed();
}
});
// Auth links
var links = document.getElementsByClassName('umap-login-popup');
Object.keys(links).forEach(function (el) {
var link = links[el];
L.DomEvent
.on(link, 'click', L.DomEvent.stop)
.on(link, 'click', function () {
self.ui.closePanel();
var win = window.open(link.href);
window.umap_proceed = function () {
proceed();
win.close();
};
});
});
};
if (data.login_required) {
this.get(data.login_required, {
'callback': function (data) {
ask_for_login(data);
}
});
}
else {
ask_for_login(data);
}
},
logout: function(url) {
this.get(url);
} }
}); 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
}
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 < l; i++) {
// Listen link again
listen_options = L.Util.extend({}, options, options.listen_link[i].options)
this.listen_link(options.listen_link[i].id, listen_options)
}
}
} else if (options.success) {
// Success is called only if data contain no msg and no html
options.success(data)
}
},
login: function (data, args) {
// data.html: login form
// args: args of the first _json call, to call again at process end
var self = this
var proceed = function () {
self.ui.closePanel()
if (typeof args !== 'undefined') self._json.apply(self, args)
else self.default_callback(data, {})
}
var ask_for_login = function (data) {
self.ui.openPanel({ data: data, className: 'login-panel' })
self.listen_form('login_form', {
callback: function (data) {
if (data.html) ask_for_login(data) // Problem in the login - ask again
else proceed()
},
})
// Auth links
var links = document.getElementsByClassName('umap-login-popup')
Object.keys(links).forEach(function (el) {
var link = links[el]
L.DomEvent.on(link, 'click', L.DomEvent.stop).on(link, 'click', function () {
self.ui.closePanel()
var win = window.open(link.href)
window.umap_proceed = function () {
proceed()
win.close()
}
})
})
}
if (data.login_required) {
this.get(data.login_required, {
callback: function (data) {
ask_for_login(data)
},
})
} else {
ask_for_login(data)
}
},
logout: function (url) {
this.get(url)
},
})