Compare commits
16 commits
03f6080148
...
2b4a9738a3
Author | SHA1 | Date | |
---|---|---|---|
|
2b4a9738a3 | ||
|
c10bc27fed | ||
|
a6a31a19a9 | ||
|
ebf9be296d | ||
|
109545d006 | ||
|
0c9c79195a | ||
|
1836647c00 | ||
|
d6a20b3dda | ||
|
ef705a862e | ||
|
d4830f6128 | ||
|
5f29b8b0d5 | ||
|
5b624167c0 | ||
|
776d92e7cc | ||
|
8e446dbe70 | ||
|
2ed9bc65ee | ||
|
8ddc570e23 |
35 changed files with 943 additions and 912 deletions
|
@ -12,7 +12,7 @@
|
||||||
data-url="https://umap.openstreetmap.fr/fr/map/new/"
|
data-url="https://umap.openstreetmap.fr/fr/map/new/"
|
||||||
data-alt="Panneau d’aide au formatage."
|
data-alt="Panneau d’aide au formatage."
|
||||||
data-caption="Panneau d’aide au formatage."
|
data-caption="Panneau d’aide au formatage."
|
||||||
data-selector=".umap-help-box"
|
data-selector=".umap-dialog"
|
||||||
data-width="510"
|
data-width="510"
|
||||||
data-height="326"
|
data-height="326"
|
||||||
data-padding="5"
|
data-padding="5"
|
||||||
|
|
|
@ -756,146 +756,6 @@ input[type=hidden].blur + [type="button"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* *********** */
|
|
||||||
/* Alerts */
|
|
||||||
/* *********** */
|
|
||||||
#umap-alert-container {
|
|
||||||
min-height: 46px;
|
|
||||||
line-height: 46px;
|
|
||||||
padding-left: 10px;
|
|
||||||
width: calc(100% - 500px);
|
|
||||||
position: absolute;
|
|
||||||
top: -46px;
|
|
||||||
left: 250px; /* Keep save/cancel button accessible. */
|
|
||||||
right: 250px;
|
|
||||||
box-shadow: 0 1px 7px #999999;
|
|
||||||
visibility: hidden;
|
|
||||||
background: none repeat scroll 0 0 rgba(20, 22, 23, 0.8);
|
|
||||||
font-weight: bold;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 0.8em;
|
|
||||||
z-index: 1012;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
#umap-alert-container.error {
|
|
||||||
background-color: #c60f13;
|
|
||||||
}
|
|
||||||
.umap-alert #umap-alert-container {
|
|
||||||
visibility: visible;
|
|
||||||
top: 23px;
|
|
||||||
}
|
|
||||||
.umap-alert-container .umap-action {
|
|
||||||
margin-left: 10px;
|
|
||||||
background-color: #fff;
|
|
||||||
color: #000;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.umap-alert-container .umap-action:hover {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
.umap-alert-container .error .umap-action {
|
|
||||||
background-color: #666;
|
|
||||||
color: #eee;
|
|
||||||
}
|
|
||||||
.umap-alert-container .error .umap-action:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.umap-alert-container input {
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 4px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* *********** */
|
|
||||||
/* Tooltip */
|
|
||||||
/* *********** */
|
|
||||||
#umap-tooltip-container {
|
|
||||||
line-height: 20px;
|
|
||||||
padding: 5px 10px;
|
|
||||||
width: auto;
|
|
||||||
position: absolute;
|
|
||||||
box-shadow: 0 1px 7px #999999;
|
|
||||||
display: none;
|
|
||||||
background-color: rgba(40, 40, 40, 0.8);
|
|
||||||
color: #eeeeec;
|
|
||||||
font-size: 0.8em;
|
|
||||||
border-radius: 2px;
|
|
||||||
z-index: 1011;
|
|
||||||
font-weight: normal;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
.umap-tooltip #umap-tooltip-container {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
#umap-tooltip-container.tooltip-top:after {
|
|
||||||
top: 100%;
|
|
||||||
left: calc(50% - 11px);
|
|
||||||
border: solid transparent;
|
|
||||||
content: " ";
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
border-top-color: rgba(30, 30, 30, 0.8);
|
|
||||||
border-width: 11px;
|
|
||||||
margin-left: calc(-50% + 21px);
|
|
||||||
}
|
|
||||||
#umap-tooltip-container.tooltip-bottom:before {
|
|
||||||
top: -22px;
|
|
||||||
left: calc(50% - 11px);
|
|
||||||
border: solid transparent;
|
|
||||||
content: " ";
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
border-top-color: rgba(30, 30, 30, 0.7);
|
|
||||||
border-width: 11px;
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
#umap-tooltip-container.tooltip.tooltip-left:after {
|
|
||||||
left: 100%;
|
|
||||||
top: 50%;
|
|
||||||
border: solid transparent;
|
|
||||||
content: " ";
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
border-color: rgba(136, 183, 213, 0);
|
|
||||||
border-left-color: #333;
|
|
||||||
border-width: 11px;
|
|
||||||
margin-top: -10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* *********** */
|
|
||||||
/* Close link */
|
|
||||||
/* *********** */
|
|
||||||
#umap-alert-container .umap-close-link {
|
|
||||||
color: #fff;
|
|
||||||
float: right;
|
|
||||||
padding-right: 10px;
|
|
||||||
width: 100px;
|
|
||||||
line-height: 1;
|
|
||||||
margin: .5rem;
|
|
||||||
background-color: #202425;
|
|
||||||
font-size: .7rem;
|
|
||||||
}
|
|
||||||
#umap-alert-container .umap-close-icon {
|
|
||||||
background-position: -74px -55px;
|
|
||||||
}
|
|
||||||
#umap-alert-container .umap-alert-actions {
|
|
||||||
display: flex;
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
||||||
#umap-alert-container .umap-alert-actions .umap-action {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* *********** */
|
/* *********** */
|
||||||
/* Various */
|
/* Various */
|
||||||
/* *********** */
|
/* *********** */
|
||||||
|
@ -913,15 +773,3 @@ input[type=hidden].blur + [type="button"] {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* *********** */
|
|
||||||
/* Mobile */
|
|
||||||
/* *********** */
|
|
||||||
@media all and (orientation:portrait) {
|
|
||||||
#umap-alert-container {
|
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
75
umap/static/umap/css/alert.css
Normal file
75
umap/static/umap/css/alert.css
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#umap-alert-container {
|
||||||
|
min-height: 46px;
|
||||||
|
line-height: 46px;
|
||||||
|
padding-left: 10px;
|
||||||
|
width: calc(100% - 500px);
|
||||||
|
position: absolute;
|
||||||
|
top: -46px;
|
||||||
|
left: 250px; /* Keep save/cancel button accessible. */
|
||||||
|
right: 250px;
|
||||||
|
box-shadow: 0 1px 7px #999999;
|
||||||
|
visibility: hidden;
|
||||||
|
background: none repeat scroll 0 0 rgba(20, 22, 23, 0.8);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.8em;
|
||||||
|
z-index: 1012;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
#umap-alert-container.error {
|
||||||
|
background-color: #c60f13;
|
||||||
|
}
|
||||||
|
.umap-alert #umap-alert-container {
|
||||||
|
visibility: visible;
|
||||||
|
top: 23px;
|
||||||
|
}
|
||||||
|
#umap-alert-container .umap-action {
|
||||||
|
margin-left: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #000;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
#umap-alert-container .umap-action:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
#umap-alert-container .error .umap-action {
|
||||||
|
background-color: #666;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
#umap-alert-container .error .umap-action:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#umap-alert-container input {
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#umap-alert-container .umap-close-link {
|
||||||
|
color: #fff;
|
||||||
|
float: right;
|
||||||
|
padding-right: 10px;
|
||||||
|
width: 100px;
|
||||||
|
line-height: 1;
|
||||||
|
margin: .5rem;
|
||||||
|
background-color: #202425;
|
||||||
|
font-size: .7rem;
|
||||||
|
}
|
||||||
|
#umap-alert-container .umap-close-icon {
|
||||||
|
background-position: -74px -55px;
|
||||||
|
}
|
||||||
|
#umap-alert-container .umap-alert-actions {
|
||||||
|
display: flex;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
#umap-alert-container .umap-alert-actions .umap-action {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (orientation:portrait) {
|
||||||
|
#umap-alert-container {
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
17
umap/static/umap/css/dialog.css
Normal file
17
umap/static/umap/css/dialog.css
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.umap-dialog {
|
||||||
|
z-index: 10001;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 100px;
|
||||||
|
width: 50vw;
|
||||||
|
max-width: 100vw;
|
||||||
|
max-height: 50vh;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #222;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.umap-dialog .umap-close-link {
|
||||||
|
float: right;
|
||||||
|
width: 100px;
|
||||||
|
}
|
|
@ -7,14 +7,16 @@
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
z-index: 1010;
|
z-index: 1010;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
opacity: 0.98;
|
opacity: 0.98;
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid var(--color-lightGray);
|
border: 1px solid var(--color-lightGray);
|
||||||
|
bottom: calc(var(--current-footer-height) + var(--panel-bottom));
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.panel.dark {
|
.panel.dark {
|
||||||
border: 1px solid #222;
|
border: 1px solid #222;
|
||||||
color: #efefef;
|
|
||||||
}
|
}
|
||||||
.panel.full {
|
.panel.full {
|
||||||
width: initial;
|
width: initial;
|
||||||
|
@ -25,16 +27,9 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
right: var(--panel-gutter);
|
right: var(--panel-gutter);
|
||||||
left: var(--panel-gutter);
|
left: var(--panel-gutter);
|
||||||
top: var(--header-height);
|
|
||||||
height: initial;
|
height: initial;
|
||||||
max-height: initial;
|
max-height: initial;
|
||||||
}
|
}
|
||||||
.umap-caption-bar-enabled .panel {
|
|
||||||
bottom: calc(var(--footer-height) + var(--panel-bottom));
|
|
||||||
}
|
|
||||||
.panel {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.panel .umap-popup-content img {
|
.panel .umap-popup-content img {
|
||||||
/* See https://github.com/Leaflet/Leaflet/commit/61d746818b99d362108545c151a27f09d60960ee#commitcomment-6061847 */
|
/* See https://github.com/Leaflet/Leaflet/commit/61d746818b99d362108545c151a27f09d60960ee#commitcomment-6061847 */
|
||||||
max-width: 99% !important;
|
max-width: 99% !important;
|
||||||
|
@ -86,13 +81,13 @@
|
||||||
}
|
}
|
||||||
@media all and (orientation:landscape) {
|
@media all and (orientation:landscape) {
|
||||||
.panel {
|
.panel {
|
||||||
top: 0;
|
top: var(--current-header-height);
|
||||||
margin-top: var(--panel-gutter);
|
margin-top: var(--panel-gutter);
|
||||||
width: var(--panel-width);
|
width: var(--panel-width);
|
||||||
max-width: calc(100% - var(--panel-gutter) * 2 - var(--control-size))
|
max-width: calc(100% - var(--panel-gutter) * 2 - var(--control-size))
|
||||||
}
|
}
|
||||||
.panel.condensed {
|
.panel.condensed {
|
||||||
max-height: 500px;
|
max-height: calc(min(500px, 100% - var(--current-header-height) - var(--current-footer-height) - var(--panel-gutter) * 2));
|
||||||
bottom: initial;
|
bottom: initial;
|
||||||
}
|
}
|
||||||
.panel.right {
|
.panel.right {
|
||||||
|
@ -109,16 +104,13 @@
|
||||||
right: calc(var(--panel-gutter) * 2 + var(--control-size));
|
right: calc(var(--panel-gutter) * 2 + var(--control-size));
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
.umap-edit-enabled .panel {
|
|
||||||
top: var(--header-height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@media all and (orientation:portrait) {
|
@media all and (orientation:portrait) {
|
||||||
.panel {
|
.panel {
|
||||||
height: 50%;
|
height: 50%;
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
bottom: 0;
|
bottom: var(--current-footer-height);
|
||||||
right: -100%;
|
right: -100%;
|
||||||
}
|
}
|
||||||
.panel.left {
|
.panel.left {
|
||||||
|
@ -130,16 +122,11 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
.panel.expanded {
|
.panel.expanded {
|
||||||
height: 100%;
|
height: calc(100% - var(--current-footer-height));
|
||||||
max-height: 100%;
|
max-height: calc(100% - var(--current-footer-height));
|
||||||
}
|
}
|
||||||
.umap-caption-bar-enabled .panel {
|
.umap-caption-bar-enabled .panel {
|
||||||
bottom: var(--footer-height);
|
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
}
|
}
|
||||||
.umap-caption-bar-enabled .panel.expanded {
|
|
||||||
height: calc(100% - var(--footer-height));
|
|
||||||
max-height: calc(100% - var(--footer-height));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
59
umap/static/umap/css/tooltip.css
Normal file
59
umap/static/umap/css/tooltip.css
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#umap-tooltip-container {
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
width: auto;
|
||||||
|
position: absolute;
|
||||||
|
box-shadow: 0 1px 7px #999999;
|
||||||
|
display: none;
|
||||||
|
background-color: rgba(40, 40, 40, 0.8);
|
||||||
|
color: #eeeeec;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 2px;
|
||||||
|
z-index: 1011;
|
||||||
|
font-weight: normal;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
.umap-tooltip #umap-tooltip-container {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#umap-tooltip-container.tooltip-top:after {
|
||||||
|
top: 100%;
|
||||||
|
left: calc(50% - 11px);
|
||||||
|
border: solid transparent;
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border-top-color: rgba(30, 30, 30, 0.8);
|
||||||
|
border-width: 11px;
|
||||||
|
margin-left: calc(-50% + 21px);
|
||||||
|
}
|
||||||
|
#umap-tooltip-container.tooltip-bottom:before {
|
||||||
|
top: -22px;
|
||||||
|
left: calc(50% - 11px);
|
||||||
|
border: solid transparent;
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border-top-color: rgba(30, 30, 30, 0.7);
|
||||||
|
border-width: 11px;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
#umap-tooltip-container.tooltip.tooltip-left:after {
|
||||||
|
left: 100%;
|
||||||
|
top: 50%;
|
||||||
|
border: solid transparent;
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border-color: rgba(136, 183, 213, 0);
|
||||||
|
border-left-color: #333;
|
||||||
|
border-width: 11px;
|
||||||
|
margin-top: -10px;
|
||||||
|
}
|
||||||
|
|
309
umap/static/umap/js/modules/autocomplete.js
Normal file
309
umap/static/umap/js/modules/autocomplete.js
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
import { DomUtil, DomEvent, setOptions } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
|
import { translate } from './i18n.js'
|
||||||
|
import { ServerRequest } from './request.js'
|
||||||
|
import Alert from './ui/alert.js'
|
||||||
|
|
||||||
|
export class BaseAutocomplete {
|
||||||
|
constructor(el, options) {
|
||||||
|
this.el = el
|
||||||
|
this.options = {
|
||||||
|
placeholder: translate('Start typing...'),
|
||||||
|
emptyMessage: translate('No result'),
|
||||||
|
allowFree: true,
|
||||||
|
minChar: 2,
|
||||||
|
maxResults: 5,
|
||||||
|
}
|
||||||
|
this.cache = ''
|
||||||
|
this.results = []
|
||||||
|
this._current = null
|
||||||
|
setOptions(this, options)
|
||||||
|
this.createInput()
|
||||||
|
this.createContainer()
|
||||||
|
this.selectedContainer = this.initSelectedContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
get current() {
|
||||||
|
return this._current
|
||||||
|
}
|
||||||
|
|
||||||
|
set current(index) {
|
||||||
|
if (typeof index === 'object') {
|
||||||
|
index = this.resultToIndex(index)
|
||||||
|
}
|
||||||
|
this._current = index
|
||||||
|
}
|
||||||
|
|
||||||
|
createInput() {
|
||||||
|
this.input = DomUtil.element({
|
||||||
|
tagName: 'input',
|
||||||
|
type: 'text',
|
||||||
|
parent: this.el,
|
||||||
|
placeholder: this.options.placeholder,
|
||||||
|
autocomplete: 'off',
|
||||||
|
className: this.options.className,
|
||||||
|
})
|
||||||
|
DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
|
||||||
|
DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
|
||||||
|
DomEvent.on(this.input, 'blur', this.onBlur, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
createContainer() {
|
||||||
|
this.container = DomUtil.element({
|
||||||
|
tagName: 'ul',
|
||||||
|
parent: document.body,
|
||||||
|
className: 'umap-autocomplete',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeContainer() {
|
||||||
|
const l = this.getLeft(this.input)
|
||||||
|
const t = this.getTop(this.input) + this.input.offsetHeight
|
||||||
|
this.container.style.left = `${l}px`
|
||||||
|
this.container.style.top = `${t}px`
|
||||||
|
const width = this.options.width ? this.options.width : this.input.offsetWidth - 2
|
||||||
|
this.container.style.width = `${width}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(e) {
|
||||||
|
switch (e.key) {
|
||||||
|
case 'Tab':
|
||||||
|
if (this.current !== null) this.setChoice()
|
||||||
|
DomEvent.stop(e)
|
||||||
|
break
|
||||||
|
case 'Enter':
|
||||||
|
DomEvent.stop(e)
|
||||||
|
this.setChoice()
|
||||||
|
break
|
||||||
|
case 'Escape':
|
||||||
|
DomEvent.stop(e)
|
||||||
|
this.hide()
|
||||||
|
break
|
||||||
|
case 'ArrowDown':
|
||||||
|
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 'ArrowUp':
|
||||||
|
if (this.current !== null) {
|
||||||
|
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(e) {
|
||||||
|
const special = [
|
||||||
|
'Tab',
|
||||||
|
'Enter',
|
||||||
|
'ArrowLeft',
|
||||||
|
'ArrowRight',
|
||||||
|
'ArrowDown',
|
||||||
|
'ArrowUp',
|
||||||
|
'Meta',
|
||||||
|
'Shift',
|
||||||
|
'Alt',
|
||||||
|
'Control',
|
||||||
|
]
|
||||||
|
if (!special.includes(e.key)) {
|
||||||
|
this.search()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur() {
|
||||||
|
setTimeout(() => this.hide(), 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.results = []
|
||||||
|
this.current = null
|
||||||
|
this.cache = ''
|
||||||
|
this.container.innerHTML = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.clear()
|
||||||
|
this.container.style.display = 'none'
|
||||||
|
this.input.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
setChoice(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) {
|
||||||
|
this.options.callback.bind(this)(choice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createResult(item) {
|
||||||
|
const el = DomUtil.element({
|
||||||
|
tagName: 'li',
|
||||||
|
parent: this.container,
|
||||||
|
textContent: item.label,
|
||||||
|
})
|
||||||
|
const result = {
|
||||||
|
item: item,
|
||||||
|
el: el,
|
||||||
|
}
|
||||||
|
DomEvent.on(el, 'mouseover', () => {
|
||||||
|
this.current = result
|
||||||
|
this.highlight()
|
||||||
|
})
|
||||||
|
DomEvent.on(el, 'mousedown', () => this.setChoice())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
resultToIndex(result) {
|
||||||
|
return this.results.findIndex((item) => item.item.value === result.item.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResults(data) {
|
||||||
|
this.clear()
|
||||||
|
this.container.style.display = 'block'
|
||||||
|
this.resizeContainer()
|
||||||
|
data.forEach((item) => {
|
||||||
|
this.results.push(this.createResult(item))
|
||||||
|
})
|
||||||
|
this.current = 0
|
||||||
|
this.highlight()
|
||||||
|
//TODO manage no results
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight() {
|
||||||
|
this.results.forEach((result, index) => {
|
||||||
|
if (index === this.current) DomUtil.addClass(result.el, 'on')
|
||||||
|
else DomUtil.removeClass(result.el, 'on')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getLeft(el) {
|
||||||
|
let tmp = el.offsetLeft
|
||||||
|
el = el.offsetParent
|
||||||
|
while (el) {
|
||||||
|
tmp += el.offsetLeft
|
||||||
|
el = el.offsetParent
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
getTop(el) {
|
||||||
|
let tmp = el.offsetTop
|
||||||
|
el = el.offsetParent
|
||||||
|
while (el) {
|
||||||
|
tmp += el.offsetTop
|
||||||
|
el = el.offsetParent
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseAjax extends BaseAutocomplete {
|
||||||
|
constructor(el, options) {
|
||||||
|
super(el, options)
|
||||||
|
const alert = new Alert(document.querySelector('header'))
|
||||||
|
this.server = new ServerRequest(alert)
|
||||||
|
}
|
||||||
|
optionToResult(option) {
|
||||||
|
return {
|
||||||
|
value: option.value,
|
||||||
|
label: option.innerHTML,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async search() {
|
||||||
|
let val = this.input.value
|
||||||
|
if (val.length < this.options.minChar) {
|
||||||
|
this.clear()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (val === this.cache) return
|
||||||
|
else this.cache = val
|
||||||
|
val = val.toLowerCase()
|
||||||
|
const [{ data }, response] = await this.server.get(
|
||||||
|
`/agnocomplete/AutocompleteUser/?q=${encodeURIComponent(val)}`
|
||||||
|
)
|
||||||
|
this.handleResults(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AjaxAutocompleteMultiple extends BaseAjax {
|
||||||
|
initSelectedContainer() {
|
||||||
|
return DomUtil.after(
|
||||||
|
this.input,
|
||||||
|
DomUtil.element({ tagName: 'ul', className: 'umap-multiresult' })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
displaySelected(result) {
|
||||||
|
const result_el = DomUtil.element({
|
||||||
|
tagName: 'li',
|
||||||
|
parent: this.selectedContainer,
|
||||||
|
})
|
||||||
|
result_el.textContent = result.item.label
|
||||||
|
const close = DomUtil.element({
|
||||||
|
tagName: 'span',
|
||||||
|
parent: result_el,
|
||||||
|
className: 'close',
|
||||||
|
textContent: '×',
|
||||||
|
})
|
||||||
|
DomEvent.on(close, 'click', () => {
|
||||||
|
this.selectedContainer.removeChild(result_el)
|
||||||
|
this.options.on_unselect(result)
|
||||||
|
})
|
||||||
|
this.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AjaxAutocomplete extends BaseAjax {
|
||||||
|
initSelectedContainer() {
|
||||||
|
return DomUtil.after(
|
||||||
|
this.input,
|
||||||
|
DomUtil.element({ tagName: 'div', className: 'umap-singleresult' })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
displaySelected(result) {
|
||||||
|
const result_el = DomUtil.element({
|
||||||
|
tagName: 'div',
|
||||||
|
parent: this.selectedContainer,
|
||||||
|
})
|
||||||
|
result_el.textContent = result.item.label
|
||||||
|
const close = DomUtil.element({
|
||||||
|
tagName: 'span',
|
||||||
|
parent: result_el,
|
||||||
|
className: 'close',
|
||||||
|
textContent: '×',
|
||||||
|
})
|
||||||
|
this.input.style.display = 'none'
|
||||||
|
DomEvent.on(
|
||||||
|
close,
|
||||||
|
'click',
|
||||||
|
function () {
|
||||||
|
this.selectedContainer.innerHTML = ''
|
||||||
|
this.input.style.display = 'block'
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
this.hide()
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,16 @@ import URLs from './urls.js'
|
||||||
import Browser from './browser.js'
|
import Browser from './browser.js'
|
||||||
import Facets from './facets.js'
|
import Facets from './facets.js'
|
||||||
import Caption from './caption.js'
|
import Caption from './caption.js'
|
||||||
import { Panel, EditPanel, FullPanel } from './panel.js'
|
import { Panel, EditPanel, FullPanel } from './ui/panel.js'
|
||||||
|
import Alert from './ui/alert.js'
|
||||||
|
import Dialog from './ui/dialog.js'
|
||||||
|
import Tooltip from './ui/tooltip.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import { SCHEMA } from './schema.js'
|
import { SCHEMA } from './schema.js'
|
||||||
import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
|
import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
|
||||||
|
import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
|
||||||
import Orderable from './orderable.js'
|
import Orderable from './orderable.js'
|
||||||
|
import Importer from './importer.js'
|
||||||
|
|
||||||
// Import modules and export them to the global scope.
|
// Import modules and export them to the global scope.
|
||||||
// For the not yet module-compatible JS out there.
|
// For the not yet module-compatible JS out there.
|
||||||
|
@ -21,10 +26,16 @@ window.U = {
|
||||||
Browser,
|
Browser,
|
||||||
Facets,
|
Facets,
|
||||||
Panel,
|
Panel,
|
||||||
|
Alert,
|
||||||
|
Dialog,
|
||||||
|
Tooltip,
|
||||||
EditPanel,
|
EditPanel,
|
||||||
FullPanel,
|
FullPanel,
|
||||||
Utils,
|
Utils,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
|
Importer,
|
||||||
Orderable,
|
Orderable,
|
||||||
Caption,
|
Caption,
|
||||||
|
AjaxAutocomplete,
|
||||||
|
AjaxAutocompleteMultiple,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,104 +1,107 @@
|
||||||
U.Importer = L.Class.extend({
|
import { DomUtil, DomEvent } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
TYPES: ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap'],
|
import { translate } from './i18n.js'
|
||||||
initialize: function (map) {
|
|
||||||
|
export default class Importer {
|
||||||
|
constructor(map) {
|
||||||
this.map = map
|
this.map = map
|
||||||
this.presets = map.options.importPresets
|
this.presets = map.options.importPresets
|
||||||
},
|
this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
|
||||||
|
}
|
||||||
|
|
||||||
build: function () {
|
build() {
|
||||||
this.container = L.DomUtil.create('div', 'umap-upload')
|
this.container = DomUtil.create('div', 'umap-upload')
|
||||||
this.title = L.DomUtil.createTitle(
|
this.title = DomUtil.createTitle(
|
||||||
this.container,
|
this.container,
|
||||||
L._('Import data'),
|
translate('Import data'),
|
||||||
'icon-upload'
|
'icon-upload'
|
||||||
)
|
)
|
||||||
this.presetBox = L.DomUtil.create('div', 'formbox', this.container)
|
this.presetBox = DomUtil.create('div', 'formbox', this.container)
|
||||||
this.presetSelect = L.DomUtil.create('select', '', this.presetBox)
|
this.presetSelect = DomUtil.create('select', '', this.presetBox)
|
||||||
this.fileBox = L.DomUtil.create('div', 'formbox', this.container)
|
this.fileBox = DomUtil.create('div', 'formbox', this.container)
|
||||||
this.fileInput = L.DomUtil.element({
|
this.fileInput = DomUtil.element({
|
||||||
tagName: 'input',
|
tagName: 'input',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
parent: this.fileBox,
|
parent: this.fileBox,
|
||||||
multiple: 'multiple',
|
multiple: 'multiple',
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
})
|
})
|
||||||
this.urlInput = L.DomUtil.element({
|
this.urlInput = DomUtil.element({
|
||||||
tagName: 'input',
|
tagName: 'input',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
parent: this.container,
|
parent: this.container,
|
||||||
placeholder: L._('Provide an URL here'),
|
placeholder: translate('Provide an URL here'),
|
||||||
})
|
})
|
||||||
this.rawInput = L.DomUtil.element({
|
this.rawInput = DomUtil.element({
|
||||||
tagName: 'textarea',
|
tagName: 'textarea',
|
||||||
parent: this.container,
|
parent: this.container,
|
||||||
placeholder: L._('Paste your data here'),
|
placeholder: translate('Paste your data here'),
|
||||||
})
|
})
|
||||||
this.typeLabel = L.DomUtil.add(
|
this.typeLabel = DomUtil.add(
|
||||||
'label',
|
'label',
|
||||||
'',
|
'',
|
||||||
this.container,
|
this.container,
|
||||||
L._('Choose the format of the data to import')
|
translate('Choose the format of the data to import')
|
||||||
)
|
)
|
||||||
this.layerLabel = L.DomUtil.add(
|
this.layerLabel = DomUtil.add(
|
||||||
'label',
|
'label',
|
||||||
'',
|
'',
|
||||||
this.container,
|
this.container,
|
||||||
L._('Choose the layer to import in')
|
translate('Choose the layer to import in')
|
||||||
)
|
)
|
||||||
this.clearLabel = L.DomUtil.element({
|
this.clearLabel = DomUtil.element({
|
||||||
tagName: 'label',
|
tagName: 'label',
|
||||||
parent: this.container,
|
parent: this.container,
|
||||||
textContent: L._('Replace layer content'),
|
textContent: translate('Replace layer content'),
|
||||||
for: 'datalayer-clear-check',
|
for: 'datalayer-clear-check',
|
||||||
})
|
})
|
||||||
this.submitInput = L.DomUtil.element({
|
this.submitInput = DomUtil.element({
|
||||||
tagName: 'input',
|
tagName: 'input',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
parent: this.container,
|
parent: this.container,
|
||||||
value: L._('Import'),
|
value: translate('Import'),
|
||||||
className: 'button',
|
className: 'button',
|
||||||
})
|
})
|
||||||
this.map.help.button(this.typeLabel, 'importFormats')
|
this.map.help.button(this.typeLabel, 'importFormats')
|
||||||
this.typeInput = L.DomUtil.element({
|
this.typeInput = DomUtil.element({
|
||||||
tagName: 'select',
|
tagName: 'select',
|
||||||
name: 'format',
|
name: 'format',
|
||||||
parent: this.typeLabel,
|
parent: this.typeLabel,
|
||||||
})
|
})
|
||||||
this.layerInput = L.DomUtil.element({
|
this.layerInput = DomUtil.element({
|
||||||
tagName: 'select',
|
tagName: 'select',
|
||||||
name: 'datalayer',
|
name: 'datalayer',
|
||||||
parent: this.layerLabel,
|
parent: this.layerLabel,
|
||||||
})
|
})
|
||||||
this.clearFlag = L.DomUtil.element({
|
this.clearFlag = DomUtil.element({
|
||||||
tagName: 'input',
|
tagName: 'input',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
name: 'clear',
|
name: 'clear',
|
||||||
id: 'datalayer-clear-check',
|
id: 'datalayer-clear-check',
|
||||||
parent: this.clearLabel,
|
parent: this.clearLabel,
|
||||||
})
|
})
|
||||||
L.DomUtil.element({
|
DomUtil.element({
|
||||||
tagName: 'option',
|
tagName: 'option',
|
||||||
value: '',
|
value: '',
|
||||||
textContent: L._('Choose the data format'),
|
textContent: translate('Choose the data format'),
|
||||||
parent: this.typeInput,
|
parent: this.typeInput,
|
||||||
})
|
})
|
||||||
for (let i = 0; i < this.TYPES.length; i++) {
|
for (const type of this.TYPES) {
|
||||||
option = L.DomUtil.create('option', '', this.typeInput)
|
const option = DomUtil.create('option', '', this.typeInput)
|
||||||
option.value = option.textContent = this.TYPES[i]
|
option.value = option.textContent = type
|
||||||
}
|
}
|
||||||
if (this.presets.length) {
|
if (this.presets.length) {
|
||||||
const noPreset = L.DomUtil.create('option', '', this.presetSelect)
|
const noPreset = DomUtil.create('option', '', this.presetSelect)
|
||||||
noPreset.value = noPreset.textContent = L._('Choose a preset')
|
noPreset.value = noPreset.textContent = translate('Choose a preset')
|
||||||
for (let j = 0; j < this.presets.length; j++) {
|
for (const preset of this.presets) {
|
||||||
option = L.DomUtil.create('option', '', presetSelect)
|
option = DomUtil.create('option', '', presetSelect)
|
||||||
option.value = this.presets[j].url
|
option.value = preset.url
|
||||||
option.textContent = this.presets[j].label
|
option.textContent = preset.label
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.presetBox.style.display = 'none'
|
this.presetBox.style.display = 'none'
|
||||||
}
|
}
|
||||||
L.DomEvent.on(this.submitInput, 'click', this.submit, this)
|
DomEvent.on(this.submitInput, 'click', this.submit, this)
|
||||||
L.DomEvent.on(
|
DomEvent.on(
|
||||||
this.fileInput,
|
this.fileInput,
|
||||||
'change',
|
'change',
|
||||||
(e) => {
|
(e) => {
|
||||||
|
@ -116,9 +119,9 @@ U.Importer = L.Class.extend({
|
||||||
},
|
},
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
|
|
||||||
open: function () {
|
open() {
|
||||||
if (!this.container) this.build()
|
if (!this.container) this.build()
|
||||||
const onLoad = this.map.editPanel.open({ content: this.container })
|
const onLoad = this.map.editPanel.open({ content: this.container })
|
||||||
onLoad.then(() => {
|
onLoad.then(() => {
|
||||||
|
@ -128,25 +131,25 @@ U.Importer = L.Class.extend({
|
||||||
this.map.eachDataLayerReverse((datalayer) => {
|
this.map.eachDataLayerReverse((datalayer) => {
|
||||||
if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
|
if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
|
||||||
const id = L.stamp(datalayer)
|
const id = L.stamp(datalayer)
|
||||||
option = L.DomUtil.add('option', '', this.layerInput, datalayer.options.name)
|
option = DomUtil.add('option', '', this.layerInput, datalayer.options.name)
|
||||||
option.value = id
|
option.value = id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
L.DomUtil.element({
|
DomUtil.element({
|
||||||
tagName: 'option',
|
tagName: 'option',
|
||||||
value: '',
|
value: '',
|
||||||
textContent: L._('Import in a new layer'),
|
textContent: translate('Import in a new layer'),
|
||||||
parent: this.layerInput,
|
parent: this.layerInput,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
openFiles: function () {
|
openFiles() {
|
||||||
this.open()
|
this.open()
|
||||||
this.fileInput.showPicker()
|
this.fileInput.showPicker()
|
||||||
},
|
}
|
||||||
|
|
||||||
submit: function () {
|
submit() {
|
||||||
let type = this.typeInput.value
|
let type = this.typeInput.value
|
||||||
const layerId = this.layerInput[this.layerInput.selectedIndex].value
|
const layerId = this.layerInput[this.layerInput.selectedIndex].value
|
||||||
let layer
|
let layer
|
||||||
|
@ -161,15 +164,15 @@ U.Importer = L.Class.extend({
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!type)
|
if (!type)
|
||||||
return this.map.ui.alert({
|
return this.map.alert.open({
|
||||||
content: L._('Please choose a format'),
|
content: translate('Please choose a format'),
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
if (this.rawInput.value && type === 'umap') {
|
if (this.rawInput.value && type === 'umap') {
|
||||||
try {
|
try {
|
||||||
this.map.importRaw(this.rawInput.value, type)
|
this.map.importRaw(this.rawInput.value, type)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.ui.alert({ content: L._('Invalid umap data'), level: 'error' })
|
this.alert.open({ content: translate('Invalid umap data'), level: 'error' })
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -183,5 +186,5 @@ U.Importer = L.Class.extend({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
}
|
|
@ -47,14 +47,18 @@ class BaseRequest {
|
||||||
// In case of error, an alert is sent, but non 20X status are not handled
|
// In case of error, an alert is sent, but non 20X status are not handled
|
||||||
// The consumer must check the response status by hand
|
// The consumer must check the response status by hand
|
||||||
export class Request extends BaseRequest {
|
export class Request extends BaseRequest {
|
||||||
constructor(ui) {
|
constructor(alert) {
|
||||||
super()
|
super()
|
||||||
this.ui = ui
|
this.alert = alert
|
||||||
|
}
|
||||||
|
|
||||||
|
fire(name, params) {
|
||||||
|
document.body.dispatchEvent(new CustomEvent(name, params))
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetch(method, uri, headers, data) {
|
async _fetch(method, uri, headers, data) {
|
||||||
const id = Math.random()
|
const id = Math.random()
|
||||||
this.ui.fire('dataloading', { id: id })
|
this.fire('dataloading', { id: id })
|
||||||
try {
|
try {
|
||||||
const response = await BaseRequest.prototype._fetch.call(
|
const response = await BaseRequest.prototype._fetch.call(
|
||||||
this,
|
this,
|
||||||
|
@ -68,7 +72,7 @@ export class Request extends BaseRequest {
|
||||||
if (error instanceof NOKError) return this._onNOK(error)
|
if (error instanceof NOKError) return this._onNOK(error)
|
||||||
return this._onError(error)
|
return this._onError(error)
|
||||||
} finally {
|
} finally {
|
||||||
this.ui.fire('dataload', { id: id })
|
this.fire('dataload', { id: id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +85,7 @@ export class Request extends BaseRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onError(error) {
|
_onError(error) {
|
||||||
this.ui.alert({ content: L._('Problem in the response'), level: 'error' })
|
this.alert.open({ content: L._('Problem in the response'), level: 'error' })
|
||||||
}
|
}
|
||||||
|
|
||||||
_onNOK(error) {
|
_onNOK(error) {
|
||||||
|
@ -127,9 +131,9 @@ export class ServerRequest extends Request {
|
||||||
try {
|
try {
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
if (data.info) {
|
if (data.info) {
|
||||||
this.ui.alert({ content: data.info, level: 'info' })
|
this.alert.open({ content: data.info, level: 'info' })
|
||||||
} else if (data.error) {
|
} else if (data.error) {
|
||||||
this.ui.alert({ content: data.error, level: 'error' })
|
this.alert.open({ content: data.error, level: 'error' })
|
||||||
return this._onError(new Error(data.error))
|
return this._onError(new Error(data.error))
|
||||||
}
|
}
|
||||||
return [data, response, null]
|
return [data, response, null]
|
||||||
|
@ -144,7 +148,7 @@ export class ServerRequest extends Request {
|
||||||
|
|
||||||
_onNOK(error) {
|
_onNOK(error) {
|
||||||
if (error.status === 403) {
|
if (error.status === 403) {
|
||||||
this.ui.alert({
|
this.alert.open({
|
||||||
content: error.message || L._('Action not allowed :('),
|
content: error.message || L._('Action not allowed :('),
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
|
|
82
umap/static/umap/js/modules/ui/alert.js
Normal file
82
umap/static/umap/js/modules/ui/alert.js
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
|
import { translate } from '../i18n.js'
|
||||||
|
|
||||||
|
const ALERTS = []
|
||||||
|
let ALERT_ID = null
|
||||||
|
|
||||||
|
export default class Alert {
|
||||||
|
constructor(parent) {
|
||||||
|
this.parent = parent
|
||||||
|
this.container = DomUtil.create('div', 'with-transition', this.parent)
|
||||||
|
this.container.id = 'umap-alert-container'
|
||||||
|
DomEvent.disableClickPropagation(this.container)
|
||||||
|
DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu.
|
||||||
|
DomEvent.on(this.container, 'wheel', DomEvent.stopPropagation)
|
||||||
|
DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation)
|
||||||
|
}
|
||||||
|
|
||||||
|
open(params) {
|
||||||
|
if (DomUtil.hasClass(this.parent, 'umap-alert')) ALERTS.push(params)
|
||||||
|
else this._open(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
_open(params) {
|
||||||
|
if (!params) {
|
||||||
|
if (ALERTS.length) params = ALERTS.pop()
|
||||||
|
else return
|
||||||
|
}
|
||||||
|
let timeoutID
|
||||||
|
const level_class = params.level && params.level == 'info' ? 'info' : 'error'
|
||||||
|
this.container.innerHTML = ''
|
||||||
|
DomUtil.addClass(this.parent, 'umap-alert')
|
||||||
|
DomUtil.addClass(this.container, level_class)
|
||||||
|
const close = () => {
|
||||||
|
if (timeoutID && timeoutID !== ALERT_ID) {
|
||||||
|
return
|
||||||
|
} // Another alert has been forced
|
||||||
|
this.container.innerHTML = ''
|
||||||
|
DomUtil.removeClass(this.parent, 'umap-alert')
|
||||||
|
DomUtil.removeClass(this.container, level_class)
|
||||||
|
if (timeoutID) window.clearTimeout(timeoutID)
|
||||||
|
this._open()
|
||||||
|
}
|
||||||
|
const closeButton = DomUtil.createButton(
|
||||||
|
'umap-close-link',
|
||||||
|
this.container,
|
||||||
|
'',
|
||||||
|
close,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
DomUtil.create('i', 'umap-close-icon', closeButton)
|
||||||
|
const label = DomUtil.create('span', '', closeButton)
|
||||||
|
label.title = label.textContent = translate('Close')
|
||||||
|
DomUtil.element({
|
||||||
|
tagName: 'div',
|
||||||
|
innerHTML: params.content,
|
||||||
|
parent: this.container,
|
||||||
|
})
|
||||||
|
let action, el, input
|
||||||
|
const form = DomUtil.create('div', 'umap-alert-actions', this.container)
|
||||||
|
for (let action of params.actions || []) {
|
||||||
|
if (action.input) {
|
||||||
|
input = DomUtil.element({
|
||||||
|
tagName: 'input',
|
||||||
|
parent: form,
|
||||||
|
className: 'umap-alert-input',
|
||||||
|
placeholder: action.input,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
el = DomUtil.createButton(
|
||||||
|
'umap-action',
|
||||||
|
form,
|
||||||
|
action.label,
|
||||||
|
action.callback,
|
||||||
|
action.callbackContext
|
||||||
|
)
|
||||||
|
DomEvent.on(el, 'click', close, this)
|
||||||
|
}
|
||||||
|
if (params.duration !== Infinity) {
|
||||||
|
ALERT_ID = timeoutID = window.setTimeout(close, params.duration || 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
umap/static/umap/js/modules/ui/dialog.js
Normal file
40
umap/static/umap/js/modules/ui/dialog.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
|
import { translate } from '../i18n.js'
|
||||||
|
|
||||||
|
export default class Dialog {
|
||||||
|
constructor(parent) {
|
||||||
|
this.parent = parent
|
||||||
|
this.container = DomUtil.create('dialog', 'umap-dialog', this.parent)
|
||||||
|
DomEvent.disableClickPropagation(this.container)
|
||||||
|
DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu.
|
||||||
|
DomEvent.on(this.container, 'wheel', DomEvent.stopPropagation)
|
||||||
|
DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation)
|
||||||
|
}
|
||||||
|
|
||||||
|
get visible() {
|
||||||
|
return this.container.open
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.container.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
open({ className, content, modal } = {}) {
|
||||||
|
this.container.innerHTML = ''
|
||||||
|
if (modal) this.container.showModal()
|
||||||
|
else this.container.show()
|
||||||
|
if (className) {
|
||||||
|
this.container.classList.add(className)
|
||||||
|
}
|
||||||
|
const closeButton = DomUtil.createButton(
|
||||||
|
'umap-close-link',
|
||||||
|
this.container,
|
||||||
|
'',
|
||||||
|
() => this.container.close()
|
||||||
|
)
|
||||||
|
DomUtil.createIcon(closeButton, 'icon-close')
|
||||||
|
const label = DomUtil.create('span', '', closeButton)
|
||||||
|
label.title = label.textContent = translate('Close')
|
||||||
|
this.container.appendChild(content)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { DomUtil, DomEvent } from '../../vendors/leaflet/leaflet-src.esm.js'
|
import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from '../i18n.js'
|
||||||
|
|
||||||
export class Panel {
|
export class Panel {
|
||||||
constructor(map) {
|
constructor(map) {
|
116
umap/static/umap/js/modules/ui/tooltip.js
Normal file
116
umap/static/umap/js/modules/ui/tooltip.js
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
|
import { translate } from '../i18n.js'
|
||||||
|
|
||||||
|
export default class Tooltip {
|
||||||
|
constructor(parent) {
|
||||||
|
this.parent = parent
|
||||||
|
this.container = DomUtil.create('div', 'with-transition', this.parent)
|
||||||
|
this.container.id = 'umap-tooltip-container'
|
||||||
|
DomEvent.disableClickPropagation(this.container)
|
||||||
|
DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu.
|
||||||
|
DomEvent.on(this.container, 'wheel', DomEvent.stopPropagation)
|
||||||
|
DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation)
|
||||||
|
}
|
||||||
|
|
||||||
|
open(opts) {
|
||||||
|
function showIt() {
|
||||||
|
if (opts.anchor && opts.position === 'top') {
|
||||||
|
this.anchorTop(opts.anchor)
|
||||||
|
} else if (opts.anchor && opts.position === 'left') {
|
||||||
|
this.anchorLeft(opts.anchor)
|
||||||
|
} else if (opts.anchor && opts.position === 'bottom') {
|
||||||
|
this.anchorBottom(opts.anchor)
|
||||||
|
} else {
|
||||||
|
this.anchorAbsolute()
|
||||||
|
}
|
||||||
|
L.DomUtil.addClass(this.parent, 'umap-tooltip')
|
||||||
|
this.container.innerHTML = U.Utils.escapeHTML(opts.content)
|
||||||
|
}
|
||||||
|
this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0)
|
||||||
|
const id = this.TOOLTIP_ID
|
||||||
|
const closeIt = () => {
|
||||||
|
this.close(id)
|
||||||
|
}
|
||||||
|
if (opts.anchor) {
|
||||||
|
L.DomEvent.once(opts.anchor, 'mouseout', closeIt)
|
||||||
|
}
|
||||||
|
if (opts.duration !== Infinity) {
|
||||||
|
window.setTimeout(closeIt, opts.duration || 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchorAbsolute() {
|
||||||
|
this.container.className = ''
|
||||||
|
const left =
|
||||||
|
this.parent.offsetLeft +
|
||||||
|
this.parent.clientWidth / 2 -
|
||||||
|
this.container.clientWidth / 2,
|
||||||
|
top = this.parent.offsetTop + 75
|
||||||
|
this.setPosition({ top: top, left: left })
|
||||||
|
}
|
||||||
|
|
||||||
|
anchorTop(el) {
|
||||||
|
this.container.className = 'tooltip-top'
|
||||||
|
const coords = this.getPosition(el)
|
||||||
|
this.setPosition({
|
||||||
|
left: coords.left - 10,
|
||||||
|
bottom: this.getDocHeight() - coords.top + 11,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
anchorBottom(el) {
|
||||||
|
this.container.className = 'tooltip-bottom'
|
||||||
|
const coords = this.getPosition(el)
|
||||||
|
this.setPosition({
|
||||||
|
left: coords.left,
|
||||||
|
top: coords.bottom + 11,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
anchorLeft(el) {
|
||||||
|
this.container.className = 'tooltip-left'
|
||||||
|
const coords = this.getPosition(el)
|
||||||
|
this.setPosition({
|
||||||
|
top: coords.top,
|
||||||
|
right: document.documentElement.offsetWidth - coords.left + 11,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
close(id) {
|
||||||
|
// Clear timetout even if a new tooltip has been added
|
||||||
|
// in the meantime. Eg. after a mouseout from the anchor.
|
||||||
|
window.clearTimeout(id)
|
||||||
|
if (id && id !== this.TOOLTIP_ID) return
|
||||||
|
this.container.className = ''
|
||||||
|
this.container.innerHTML = ''
|
||||||
|
this.setPosition({})
|
||||||
|
L.DomUtil.removeClass(this.parent, 'umap-tooltip')
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(el) {
|
||||||
|
return el.getBoundingClientRect()
|
||||||
|
}
|
||||||
|
|
||||||
|
setPosition(coords) {
|
||||||
|
if (coords.left) this.container.style.left = `${coords.left}px`
|
||||||
|
else this.container.style.left = 'initial'
|
||||||
|
if (coords.right) this.container.style.right = `${coords.right}px`
|
||||||
|
else this.container.style.right = 'initial'
|
||||||
|
if (coords.top) this.container.style.top = `${coords.top}px`
|
||||||
|
else this.container.style.top = 'initial'
|
||||||
|
if (coords.bottom) this.container.style.bottom = `${coords.bottom}px`
|
||||||
|
else this.container.style.bottom = 'initial'
|
||||||
|
}
|
||||||
|
|
||||||
|
getDocHeight() {
|
||||||
|
const D = document
|
||||||
|
return Math.max(
|
||||||
|
D.body.scrollHeight,
|
||||||
|
D.documentElement.scrollHeight,
|
||||||
|
D.body.offsetHeight,
|
||||||
|
D.documentElement.offsetHeight,
|
||||||
|
D.body.clientHeight,
|
||||||
|
D.documentElement.clientHeight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,11 +84,21 @@ export function escapeHTML(s) {
|
||||||
'div',
|
'div',
|
||||||
'iframe',
|
'iframe',
|
||||||
'img',
|
'img',
|
||||||
|
'audio',
|
||||||
|
'video',
|
||||||
|
'source',
|
||||||
'br',
|
'br',
|
||||||
'span',
|
'span',
|
||||||
],
|
],
|
||||||
ADD_ATTR: ['target', 'allow', 'allowfullscreen', 'frameborder', 'scrolling'],
|
ADD_ATTR: [
|
||||||
ALLOWED_ATTR: ['href', 'src', 'width', 'height', 'style', 'dir', 'title'],
|
'target',
|
||||||
|
'allow',
|
||||||
|
'allowfullscreen',
|
||||||
|
'frameborder',
|
||||||
|
'scrolling',
|
||||||
|
'controls',
|
||||||
|
],
|
||||||
|
ALLOWED_ATTR: ['href', 'src', 'width', 'height', 'style', 'dir', 'title', 'type'],
|
||||||
// Added: `geo:` URL scheme as defined in RFC5870:
|
// Added: `geo:` URL scheme as defined in RFC5870:
|
||||||
// https://www.rfc-editor.org/rfc/rfc5870.html
|
// https://www.rfc-editor.org/rfc/rfc5870.html
|
||||||
// The base RegExp comes from:
|
// The base RegExp comes from:
|
||||||
|
|
|
@ -1,341 +0,0 @@
|
||||||
U.AutoComplete = L.Class.extend({
|
|
||||||
options: {
|
|
||||||
placeholder: 'Start typing...',
|
|
||||||
emptyMessage: 'No result',
|
|
||||||
allowFree: true,
|
|
||||||
minChar: 2,
|
|
||||||
maxResults: 5,
|
|
||||||
},
|
|
||||||
|
|
||||||
CACHE: '',
|
|
||||||
RESULTS: [],
|
|
||||||
|
|
||||||
initialize: function (el, options) {
|
|
||||||
this.el = el
|
|
||||||
const ui = new U.UI(document.querySelector('header'))
|
|
||||||
this.server = new U.ServerRequest(ui)
|
|
||||||
L.setOptions(this, options)
|
|
||||||
let 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({
|
|
||||||
tagName: 'input',
|
|
||||||
type: 'text',
|
|
||||||
parent: this.el,
|
|
||||||
placeholder: this.options.placeholder,
|
|
||||||
autocomplete: 'off',
|
|
||||||
className: this.options.className,
|
|
||||||
})
|
|
||||||
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({
|
|
||||||
tagName: 'ul',
|
|
||||||
parent: document.body,
|
|
||||||
className: 'umap-autocomplete',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
resizeContainer: function () {
|
|
||||||
const l = this.getLeft(this.input)
|
|
||||||
const t = this.getTop(this.input) + this.input.offsetHeight
|
|
||||||
this.container.style.left = `${l}px`
|
|
||||||
this.container.style.top = `${t}px`
|
|
||||||
const width = this.options.width ? this.options.width : this.input.offsetWidth - 2
|
|
||||||
this.container.style.width = `${width}px`
|
|
||||||
},
|
|
||||||
|
|
||||||
onKeyDown: function (e) {
|
|
||||||
switch (e.keyCode) {
|
|
||||||
case U.Keys.TAB:
|
|
||||||
if (this.CURRENT !== null) this.setChoice()
|
|
||||||
L.DomEvent.stop(e)
|
|
||||||
break
|
|
||||||
case U.Keys.ENTER:
|
|
||||||
L.DomEvent.stop(e)
|
|
||||||
this.setChoice()
|
|
||||||
break
|
|
||||||
case U.Keys.ESC:
|
|
||||||
L.DomEvent.stop(e)
|
|
||||||
this.hide()
|
|
||||||
break
|
|
||||||
case 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 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) {
|
|
||||||
const special = [
|
|
||||||
U.Keys.TAB,
|
|
||||||
U.Keys.ENTER,
|
|
||||||
U.Keys.LEFT,
|
|
||||||
U.Keys.RIGHT,
|
|
||||||
U.Keys.DOWN,
|
|
||||||
U.Keys.UP,
|
|
||||||
U.Keys.APPLE,
|
|
||||||
U.Keys.SHIFT,
|
|
||||||
U.Keys.ALT,
|
|
||||||
U.Keys.CTRL,
|
|
||||||
]
|
|
||||||
if (special.indexOf(e.keyCode) === -1) {
|
|
||||||
this.search()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onBlur: function () {
|
|
||||||
setTimeout(() => this.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: async function () {
|
|
||||||
let val = this.input.value
|
|
||||||
if (val.length < this.options.minChar) {
|
|
||||||
this.clear()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (`${val}` === `${this.CACHE}`) return
|
|
||||||
else this.CACHE = val
|
|
||||||
val = val.toLowerCase()
|
|
||||||
const [{ data }, response] = await this.server.get(
|
|
||||||
`/agnocomplete/AutocompleteUser/?q=${encodeURIComponent(val)}`
|
|
||||||
)
|
|
||||||
this.handleResults(data)
|
|
||||||
},
|
|
||||||
|
|
||||||
createResult: function (item) {
|
|
||||||
const el = L.DomUtil.element({
|
|
||||||
tagName: 'li',
|
|
||||||
parent: this.container,
|
|
||||||
textContent: item.label,
|
|
||||||
})
|
|
||||||
const 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) {
|
|
||||||
let out = null
|
|
||||||
this.forEach(this.RESULTS, (item, index) => {
|
|
||||||
if (item.item.value == result.item.value) {
|
|
||||||
out = index
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return out
|
|
||||||
},
|
|
||||||
|
|
||||||
handleResults: function (data) {
|
|
||||||
this.clear()
|
|
||||||
this.container.style.display = 'block'
|
|
||||||
this.resizeContainer()
|
|
||||||
this.forEach(data, (item) => {
|
|
||||||
this.RESULTS.push(this.createResult(item))
|
|
||||||
})
|
|
||||||
this.CURRENT = 0
|
|
||||||
this.highlight()
|
|
||||||
//TODO manage no results
|
|
||||||
},
|
|
||||||
|
|
||||||
highlight: function () {
|
|
||||||
this.forEach(this.RESULTS, (result, index) => {
|
|
||||||
if (index === this.CURRENT) L.DomUtil.addClass(result.el, 'on')
|
|
||||||
else L.DomUtil.removeClass(result.el, 'on')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getLeft: function (el) {
|
|
||||||
let tmp = el.offsetLeft
|
|
||||||
el = el.offsetParent
|
|
||||||
while (el) {
|
|
||||||
tmp += el.offsetLeft
|
|
||||||
el = el.offsetParent
|
|
||||||
}
|
|
||||||
return tmp
|
|
||||||
},
|
|
||||||
|
|
||||||
getTop: function (el) {
|
|
||||||
let 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)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
U.AutoComplete.Ajax = U.AutoComplete.extend({
|
|
||||||
initialize: function (el, options) {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
U.AutoComplete.Ajax.SelectMultiple = U.AutoComplete.Ajax.extend({
|
|
||||||
initSelectedContainer: function () {
|
|
||||||
return L.DomUtil.after(
|
|
||||||
this.input,
|
|
||||||
L.DomUtil.element({ tagName: 'ul', className: 'umap-multiresult' })
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
displaySelected: function (result) {
|
|
||||||
const result_el = L.DomUtil.element({
|
|
||||||
tagName: 'li',
|
|
||||||
parent: this.selected_container,
|
|
||||||
})
|
|
||||||
result_el.textContent = result.item.label
|
|
||||||
const close = L.DomUtil.element({
|
|
||||||
tagName: 'span',
|
|
||||||
parent: result_el,
|
|
||||||
className: 'close',
|
|
||||||
textContent: '×',
|
|
||||||
})
|
|
||||||
L.DomEvent.on(
|
|
||||||
close,
|
|
||||||
'click',
|
|
||||||
function () {
|
|
||||||
this.selected_container.removeChild(result_el)
|
|
||||||
this.options.on_unselect(result)
|
|
||||||
},
|
|
||||||
this
|
|
||||||
)
|
|
||||||
this.hide()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
U.AutoComplete.Ajax.Select = U.AutoComplete.Ajax.extend({
|
|
||||||
initSelectedContainer: function () {
|
|
||||||
return L.DomUtil.after(
|
|
||||||
this.input,
|
|
||||||
L.DomUtil.element({ tagName: 'div', className: 'umap-singleresult' })
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
displaySelected: function (result) {
|
|
||||||
const result_el = L.DomUtil.element({
|
|
||||||
tagName: 'div',
|
|
||||||
parent: this.selected_container,
|
|
||||||
})
|
|
||||||
result_el.textContent = result.item.label
|
|
||||||
const close = L.DomUtil.element({
|
|
||||||
tagName: 'span',
|
|
||||||
parent: result_el,
|
|
||||||
className: '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()
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -394,7 +394,7 @@ U.EditControl = L.Control.extend({
|
||||||
enableEditing,
|
enableEditing,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
function () {
|
function () {
|
||||||
map.ui.tooltip({
|
map.tooltip.open({
|
||||||
content: map.help.displayLabel('TOGGLE_EDIT'),
|
content: map.help.displayLabel('TOGGLE_EDIT'),
|
||||||
anchor: enableEditing,
|
anchor: enableEditing,
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
|
@ -693,7 +693,7 @@ const ControlsMixin = {
|
||||||
nameButton,
|
nameButton,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
function () {
|
function () {
|
||||||
this.ui.tooltip({
|
this.tooltip.open({
|
||||||
content: L._('Edit the title of the map'),
|
content: L._('Edit the title of the map'),
|
||||||
anchor: nameButton,
|
anchor: nameButton,
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
|
@ -714,7 +714,7 @@ const ControlsMixin = {
|
||||||
shareStatusButton,
|
shareStatusButton,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
function () {
|
function () {
|
||||||
this.ui.tooltip({
|
this.tooltip.open({
|
||||||
content: L._('Update who can see and edit the map'),
|
content: L._('Update who can see and edit the map'),
|
||||||
anchor: shareStatusButton,
|
anchor: shareStatusButton,
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
|
@ -763,7 +763,7 @@ const ControlsMixin = {
|
||||||
controlEditCancel,
|
controlEditCancel,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
function () {
|
function () {
|
||||||
this.ui.tooltip({
|
this.tooltip.open({
|
||||||
content: this.help.displayLabel('CANCEL'),
|
content: this.help.displayLabel('CANCEL'),
|
||||||
anchor: controlEditCancel,
|
anchor: controlEditCancel,
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
|
@ -784,7 +784,7 @@ const ControlsMixin = {
|
||||||
controlEditDisable,
|
controlEditDisable,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
function () {
|
function () {
|
||||||
this.ui.tooltip({
|
this.tooltip.open({
|
||||||
content: this.help.displayLabel('PREVIEW'),
|
content: this.help.displayLabel('PREVIEW'),
|
||||||
anchor: controlEditDisable,
|
anchor: controlEditDisable,
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
|
@ -805,7 +805,7 @@ const ControlsMixin = {
|
||||||
controlEditSave,
|
controlEditSave,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
function () {
|
function () {
|
||||||
this.ui.tooltip({
|
this.tooltip.open({
|
||||||
content: this.help.displayLabel('SAVE'),
|
content: this.help.displayLabel('SAVE'),
|
||||||
anchor: controlEditSave,
|
anchor: controlEditSave,
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
|
@ -1048,7 +1048,6 @@ U.Locate = L.Control.Locate.extend({
|
||||||
if (!this._container || !this._container.parentNode) return
|
if (!this._container || !this._container.parentNode) return
|
||||||
return L.Control.Locate.prototype.remove.call(this)
|
return L.Control.Locate.prototype.remove.call(this)
|
||||||
},
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
U.Search = L.PhotonSearch.extend({
|
U.Search = L.PhotonSearch.extend({
|
||||||
|
@ -1087,7 +1086,7 @@ U.Search = L.PhotonSearch.extend({
|
||||||
if (latlng.isValid()) {
|
if (latlng.isValid()) {
|
||||||
this.reverse.doReverse(latlng)
|
this.reverse.doReverse(latlng)
|
||||||
} else {
|
} else {
|
||||||
this.map.ui.alert({ content: 'Invalid latitude or longitude', mode: 'error' })
|
this.map.alert.open({ content: 'Invalid latitude or longitude', mode: 'error' })
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1250,7 +1249,7 @@ U.Editable = L.Editable.extend({
|
||||||
L.Editable.prototype.initialize.call(this, map, options)
|
L.Editable.prototype.initialize.call(this, map, options)
|
||||||
this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip)
|
this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip)
|
||||||
this.on('editable:drawing:end', (e) => {
|
this.on('editable:drawing:end', (e) => {
|
||||||
this.closeTooltip()
|
this.map.tooltip.close()
|
||||||
// Leaflet.Editable will delete the drawn shape if invalid
|
// Leaflet.Editable will delete the drawn shape if invalid
|
||||||
// (eg. line has only one drawn point)
|
// (eg. line has only one drawn point)
|
||||||
// So let's check if the layer has no more shape
|
// So let's check if the layer has no more shape
|
||||||
|
@ -1314,7 +1313,7 @@ U.Editable = L.Editable.extend({
|
||||||
|
|
||||||
drawingTooltip: function (e) {
|
drawingTooltip: function (e) {
|
||||||
if (e.layer instanceof L.Marker && e.type == 'editable:drawing:start') {
|
if (e.layer instanceof L.Marker && e.type == 'editable:drawing:start') {
|
||||||
this.map.ui.tooltip({ content: L._('Click to add a marker') })
|
this.map.tooltip.open({ content: L._('Click to add a marker') })
|
||||||
}
|
}
|
||||||
if (!(e.layer instanceof L.Polyline)) {
|
if (!(e.layer instanceof L.Polyline)) {
|
||||||
// only continue with Polylines and Polygons
|
// only continue with Polylines and Polygons
|
||||||
|
@ -1357,7 +1356,7 @@ U.Editable = L.Editable.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (content) {
|
if (content) {
|
||||||
this.map.ui.tooltip({ content: content })
|
this.map.tooltip.open({ content: content })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -346,22 +346,6 @@ U.Help = L.Class.extend({
|
||||||
|
|
||||||
initialize: function (map) {
|
initialize: function (map) {
|
||||||
this.map = map
|
this.map = map
|
||||||
this.box = L.DomUtil.create(
|
|
||||||
'div',
|
|
||||||
'umap-help-box with-transition dark',
|
|
||||||
document.body
|
|
||||||
)
|
|
||||||
const closeButton = L.DomUtil.createButton(
|
|
||||||
'umap-close-link',
|
|
||||||
this.box,
|
|
||||||
'',
|
|
||||||
this.hide,
|
|
||||||
this
|
|
||||||
)
|
|
||||||
L.DomUtil.add('i', 'umap-close-icon', closeButton)
|
|
||||||
const label = L.DomUtil.create('span', '', closeButton)
|
|
||||||
label.title = label.textContent = L._('Close')
|
|
||||||
this.content = L.DomUtil.create('div', 'umap-help-content', this.box)
|
|
||||||
this.isMacOS = /mac/i.test(
|
this.isMacOS = /mac/i.test(
|
||||||
// eslint-disable-next-line compat/compat -- Fallback available.
|
// eslint-disable-next-line compat/compat -- Fallback available.
|
||||||
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
|
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
|
||||||
|
@ -377,20 +361,12 @@ U.Help = L.Class.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
show: function () {
|
show: function () {
|
||||||
this.content.innerHTML = ''
|
const container = L.DomUtil.add('div')
|
||||||
for (let i = 0, name; i < arguments.length; i++) {
|
for (let i = 0, name; i < arguments.length; i++) {
|
||||||
name = arguments[i]
|
name = arguments[i]
|
||||||
L.DomUtil.add('div', 'umap-help-entry', this.content, this.resolve(name))
|
L.DomUtil.add('div', 'umap-help-entry', container, this.resolve(name))
|
||||||
}
|
}
|
||||||
L.DomUtil.addClass(document.body, 'umap-help-on')
|
this.map.dialog.open({ content: container, className: 'dark' })
|
||||||
},
|
|
||||||
|
|
||||||
hide: function () {
|
|
||||||
L.DomUtil.removeClass(document.body, 'umap-help-on')
|
|
||||||
},
|
|
||||||
|
|
||||||
visible: function () {
|
|
||||||
return L.DomUtil.hasClass(document.body, 'umap-help-on')
|
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: function (name) {
|
resolve: function (name) {
|
||||||
|
@ -424,16 +400,15 @@ U.Help = L.Class.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
edit: function () {
|
edit: function () {
|
||||||
const container = L.DomUtil.create('div', ''),
|
const container = L.DomUtil.create('div', '')
|
||||||
self = this,
|
const title = L.DomUtil.create('h3', '', container)
|
||||||
title = L.DomUtil.create('h3', '', container),
|
const actionsContainer = L.DomUtil.create('ul', 'umap-edit-actions', container)
|
||||||
actionsContainer = L.DomUtil.create('ul', 'umap-edit-actions', container)
|
|
||||||
const addAction = (action) => {
|
const addAction = (action) => {
|
||||||
const actionContainer = L.DomUtil.add('li', '', actionsContainer)
|
const actionContainer = L.DomUtil.add('li', '', actionsContainer)
|
||||||
L.DomUtil.add('i', action.options.className, actionContainer),
|
L.DomUtil.add('i', action.options.className, actionContainer),
|
||||||
L.DomUtil.add('span', '', actionContainer, action.options.tooltip)
|
L.DomUtil.add('span', '', actionContainer, action.options.tooltip)
|
||||||
L.DomEvent.on(actionContainer, 'click', action.addHooks, action)
|
L.DomEvent.on(actionContainer, 'click', action.addHooks, action)
|
||||||
L.DomEvent.on(actionContainer, 'click', self.hide, self)
|
L.DomEvent.on(actionContainer, 'click', this.map.dialog.close, this.map.dialog)
|
||||||
}
|
}
|
||||||
title.textContent = L._('Where do we go from here?')
|
title.textContent = L._('Where do we go from here?')
|
||||||
for (const id in this.map.helpMenuActions) {
|
for (const id in this.map.helpMenuActions) {
|
||||||
|
|
|
@ -726,7 +726,7 @@ U.Marker = L.Marker.extend({
|
||||||
const builder = new U.FormBuilder(this, coordinatesOptions, {
|
const builder = new U.FormBuilder(this, coordinatesOptions, {
|
||||||
callback: function () {
|
callback: function () {
|
||||||
if (!this._latlng.isValid()) {
|
if (!this._latlng.isValid()) {
|
||||||
this.map.ui.alert({
|
this.map.alert.open({
|
||||||
content: L._('Invalid latitude or longitude'),
|
content: L._('Invalid latitude or longitude'),
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
|
@ -878,9 +878,9 @@ U.PathMixin = {
|
||||||
|
|
||||||
_onMouseOver: function () {
|
_onMouseOver: function () {
|
||||||
if (this.map.measureTools && this.map.measureTools.enabled()) {
|
if (this.map.measureTools && this.map.measureTools.enabled()) {
|
||||||
this.map.ui.tooltip({ content: this.getMeasure(), anchor: this })
|
this.map.tooltip.open({ content: this.getMeasure(), anchor: this })
|
||||||
} else if (this.map.editEnabled && !this.map.editedFeature) {
|
} else if (this.map.editEnabled && !this.map.editedFeature) {
|
||||||
this.map.ui.tooltip({ content: L._('Click to edit'), anchor: this })
|
this.map.tooltip.open({ content: L._('Click to edit'), anchor: this })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -928,7 +928,7 @@ U.PathMixin = {
|
||||||
items.push({
|
items.push({
|
||||||
text: L._('Display measure'),
|
text: L._('Display measure'),
|
||||||
callback: function () {
|
callback: function () {
|
||||||
this.map.ui.alert({ content: this.getMeasure(), level: 'info' })
|
this.map.alert.open({ content: this.getMeasure(), level: 'info' })
|
||||||
},
|
},
|
||||||
context: this,
|
context: this,
|
||||||
})
|
})
|
||||||
|
|
|
@ -78,7 +78,7 @@ L.FormBuilder.Element.include({
|
||||||
info,
|
info,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
function () {
|
function () {
|
||||||
this.builder.map.ui.tooltip({
|
this.builder.map.tooltip.open({
|
||||||
anchor: info,
|
anchor: info,
|
||||||
content: this.options.helpTooltip,
|
content: this.options.helpTooltip,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
|
@ -1067,7 +1067,7 @@ L.FormBuilder.ManageOwner = L.FormBuilder.Element.extend({
|
||||||
className: 'edit-owner',
|
className: 'edit-owner',
|
||||||
on_select: L.bind(this.onSelect, this),
|
on_select: L.bind(this.onSelect, this),
|
||||||
}
|
}
|
||||||
this.autocomplete = new U.AutoComplete.Ajax.Select(this.parentNode, options)
|
this.autocomplete = new U.AjaxAutocomplete(this.parentNode, options)
|
||||||
const owner = this.toHTML()
|
const owner = this.toHTML()
|
||||||
if (owner)
|
if (owner)
|
||||||
this.autocomplete.displaySelected({
|
this.autocomplete.displaySelected({
|
||||||
|
@ -1096,7 +1096,7 @@ L.FormBuilder.ManageEditors = L.FormBuilder.Element.extend({
|
||||||
on_select: L.bind(this.onSelect, this),
|
on_select: L.bind(this.onSelect, this),
|
||||||
on_unselect: L.bind(this.onUnselect, this),
|
on_unselect: L.bind(this.onUnselect, this),
|
||||||
}
|
}
|
||||||
this.autocomplete = new U.AutoComplete.Ajax.SelectMultiple(this.parentNode, options)
|
this.autocomplete = new U.AjaxAutocompleteMultiple(this.parentNode, options)
|
||||||
this._values = this.toHTML()
|
this._values = this.toHTML()
|
||||||
if (this._values)
|
if (this._values)
|
||||||
for (let i = 0; i < this._values.length; i++)
|
for (let i = 0; i < this._values.length; i++)
|
||||||
|
|
|
@ -57,15 +57,17 @@ U.Map = L.Map.extend({
|
||||||
this.urls = new U.URLs(this.options.urls)
|
this.urls = new U.URLs(this.options.urls)
|
||||||
|
|
||||||
this.panel = new U.Panel(this)
|
this.panel = new U.Panel(this)
|
||||||
|
this.alert = new U.Alert(this._controlContainer)
|
||||||
|
this.tooltip = new U.Tooltip(this._controlContainer)
|
||||||
|
this.dialog = new U.Dialog(this._controlContainer)
|
||||||
if (this.hasEditMode()) {
|
if (this.hasEditMode()) {
|
||||||
this.editPanel = new U.EditPanel(this)
|
this.editPanel = new U.EditPanel(this)
|
||||||
this.fullPanel = new U.FullPanel(this)
|
this.fullPanel = new U.FullPanel(this)
|
||||||
}
|
}
|
||||||
this.ui = new U.UI(this._container)
|
L.DomEvent.on(document.body, 'dataloading', (e) => this.fire('dataloading', e))
|
||||||
this.ui.on('dataloading', (e) => this.fire('dataloading', e))
|
L.DomEvent.on(document.body, 'dataload', (e) => this.fire('dataload', e))
|
||||||
this.ui.on('dataload', (e) => this.fire('dataload', e))
|
this.server = new U.ServerRequest(this.alert)
|
||||||
this.server = new U.ServerRequest(this.ui)
|
this.request = new U.Request(this.alert)
|
||||||
this.request = new U.Request(this.ui)
|
|
||||||
|
|
||||||
this.initLoader()
|
this.initLoader()
|
||||||
this.name = this.options.name
|
this.name = this.options.name
|
||||||
|
@ -359,7 +361,7 @@ U.Map = L.Map.extend({
|
||||||
icon: 'umap-fake-class',
|
icon: 'umap-fake-class',
|
||||||
iconLoading: 'umap-fake-class',
|
iconLoading: 'umap-fake-class',
|
||||||
flyTo: this.options.easing,
|
flyTo: this.options.easing,
|
||||||
onLocationError: (err) => this.ui.alert({ content: err.message }),
|
onLocationError: (err) => this.alert.open({ content: err.message }),
|
||||||
})
|
})
|
||||||
this._controls.fullscreen = new L.Control.Fullscreen({
|
this._controls.fullscreen = new L.Control.Fullscreen({
|
||||||
title: { false: L._('View Fullscreen'), true: L._('Exit Fullscreen') },
|
title: { false: L._('View Fullscreen'), true: L._('Exit Fullscreen') },
|
||||||
|
@ -392,7 +394,9 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
renderControls: function () {
|
renderControls: function () {
|
||||||
const hasSlideshow = Boolean(this.options.slideshow && this.options.slideshow.active)
|
const hasSlideshow = Boolean(
|
||||||
|
this.options.slideshow && this.options.slideshow.active
|
||||||
|
)
|
||||||
const barEnabled = this.options.captionBar || hasSlideshow
|
const barEnabled = this.options.captionBar || hasSlideshow
|
||||||
document.body.classList.toggle('umap-caption-bar-enabled', barEnabled)
|
document.body.classList.toggle('umap-caption-bar-enabled', barEnabled)
|
||||||
document.body.classList.toggle('umap-slideshow-enabled', hasSlideshow)
|
document.body.classList.toggle('umap-slideshow-enabled', hasSlideshow)
|
||||||
|
@ -522,8 +526,8 @@ U.Map = L.Map.extend({
|
||||||
L.DomEvent.stop(e)
|
L.DomEvent.stop(e)
|
||||||
this.search()
|
this.search()
|
||||||
} else if (e.keyCode === U.Keys.ESC) {
|
} else if (e.keyCode === U.Keys.ESC) {
|
||||||
if (this.help.visible()) {
|
if (this.dialog.visible) {
|
||||||
this.help.hide()
|
this.dialog.close()
|
||||||
} else {
|
} else {
|
||||||
this.panel.close()
|
this.panel.close()
|
||||||
this.editPanel?.close()
|
this.editPanel?.close()
|
||||||
|
@ -641,7 +645,7 @@ U.Map = L.Map.extend({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
this.removeLayer(tilelayer)
|
this.removeLayer(tilelayer)
|
||||||
this.ui.alert({
|
this.alert.open({
|
||||||
content: `${L._('Error in the tilelayer URL')}: ${tilelayer._url}`,
|
content: `${L._('Error in the tilelayer URL')}: ${tilelayer._url}`,
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
|
@ -676,7 +680,7 @@ U.Map = L.Map.extend({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.removeLayer(overlay)
|
this.removeLayer(overlay)
|
||||||
console.error(e)
|
console.error(e)
|
||||||
this.ui.alert({
|
this.alert.open({
|
||||||
content: `${L._('Error in the overlay URL')}: ${overlay._url}`,
|
content: `${L._('Error in the overlay URL')}: ${overlay._url}`,
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
|
@ -799,7 +803,7 @@ U.Map = L.Map.extend({
|
||||||
if (this.options.umap_id) {
|
if (this.options.umap_id) {
|
||||||
// We do not want an extra message during the map creation
|
// We do not want an extra message during the map creation
|
||||||
// to avoid the double notification/alert.
|
// to avoid the double notification/alert.
|
||||||
this.ui.alert({
|
this.alert.open({
|
||||||
content: L._('The zoom and center have been modified.'),
|
content: L._('The zoom and center have been modified.'),
|
||||||
level: 'info',
|
level: 'info',
|
||||||
})
|
})
|
||||||
|
@ -842,7 +846,7 @@ U.Map = L.Map.extend({
|
||||||
processFileToImport: function (file, layer, type) {
|
processFileToImport: function (file, layer, type) {
|
||||||
type = type || U.Utils.detectFileType(file)
|
type = type || U.Utils.detectFileType(file)
|
||||||
if (!type) {
|
if (!type) {
|
||||||
this.ui.alert({
|
this.alert.open({
|
||||||
content: L._('Unable to detect format of file {filename}', {
|
content: L._('Unable to detect format of file {filename}', {
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
}),
|
}),
|
||||||
|
@ -899,7 +903,7 @@ U.Map = L.Map.extend({
|
||||||
self.importRaw(rawData)
|
self.importRaw(rawData)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error importing data', e)
|
console.error('Error importing data', e)
|
||||||
self.ui.alert({
|
self.alert.open({
|
||||||
content: L._('Invalid umap data in {filename}', { filename: file.name }),
|
content: L._('Invalid umap data in {filename}', { filename: file.name }),
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
|
@ -1030,7 +1034,7 @@ U.Map = L.Map.extend({
|
||||||
label: L._('Copy link'),
|
label: L._('Copy link'),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
L.Util.copyToClipboard(data.permissions.anonymous_edit_url)
|
L.Util.copyToClipboard(data.permissions.anonymous_edit_url)
|
||||||
this.ui.alert({
|
this.alert.open({
|
||||||
content: L._('Secret edit link copied to clipboard!'),
|
content: L._('Secret edit link copied to clipboard!'),
|
||||||
level: 'info',
|
level: 'info',
|
||||||
})
|
})
|
||||||
|
@ -1058,7 +1062,7 @@ U.Map = L.Map.extend({
|
||||||
history.pushState({}, this.options.name, data.url)
|
history.pushState({}, this.options.name, data.url)
|
||||||
else window.location = data.url
|
else window.location = data.url
|
||||||
alert.content = data.info || alert.content
|
alert.content = data.info || alert.content
|
||||||
this.once('saved', () => this.ui.alert(alert))
|
this.once('saved', () => this.alert.open(alert))
|
||||||
this.permissions.save()
|
this.permissions.save()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1079,7 +1083,7 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
sendEditLink: async function () {
|
sendEditLink: async function () {
|
||||||
const input = this.ui._alert.querySelector('input')
|
const input = this.alert.container.querySelector('input')
|
||||||
const email = input.value
|
const email = input.value
|
||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
@ -1091,7 +1095,7 @@ U.Map = L.Map.extend({
|
||||||
|
|
||||||
star: async function () {
|
star: async function () {
|
||||||
if (!this.options.umap_id)
|
if (!this.options.umap_id)
|
||||||
return this.ui.alert({
|
return this.alert.open({
|
||||||
content: L._('Please save the map first'),
|
content: L._('Please save the map first'),
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
|
@ -1102,7 +1106,7 @@ U.Map = L.Map.extend({
|
||||||
let msg = data.starred
|
let msg = data.starred
|
||||||
? L._('Map has been starred')
|
? L._('Map has been starred')
|
||||||
: L._('Map has been unstarred')
|
: L._('Map has been unstarred')
|
||||||
this.ui.alert({ content: msg, level: 'info' })
|
this.alert.open({ content: msg, level: 'info' })
|
||||||
this.renderControls()
|
this.renderControls()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -865,8 +865,8 @@ U.DataLayer = L.Evented.extend({
|
||||||
isRemoteLayer: function () {
|
isRemoteLayer: function () {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
this.options.remoteData &&
|
this.options.remoteData &&
|
||||||
this.options.remoteData.url &&
|
this.options.remoteData.url &&
|
||||||
this.options.remoteData.format
|
this.options.remoteData.format
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -965,7 +965,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
message: err[0].message,
|
message: err[0].message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.map.ui.alert({ content: message, level: 'error', duration: 10000 })
|
this.map.alert.open({ content: message, level: 'error', duration: 10000 })
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
if (result && result.features.length) {
|
if (result && result.features.length) {
|
||||||
|
@ -992,7 +992,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
const gj = JSON.parse(c)
|
const gj = JSON.parse(c)
|
||||||
callback(gj)
|
callback(gj)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.map.ui.alert({ content: `Invalid JSON file: ${err}` })
|
this.map.alert.open({ content: `Invalid JSON file: ${err}` })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1050,7 +1050,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
return this.geojsonToFeatures(geometry.geometries)
|
return this.geojsonToFeatures(geometry.geometries)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this.map.ui.alert({
|
this.map.alert.open({
|
||||||
content: L._('Skipping unknown geometry.type: {type}', {
|
content: L._('Skipping unknown geometry.type: {type}', {
|
||||||
type: geometry.type || 'undefined',
|
type: geometry.type || 'undefined',
|
||||||
}),
|
}),
|
||||||
|
@ -1641,7 +1641,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
label: L._('Cancel'),
|
label: L._('Cancel'),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
this.map.ui.alert({
|
this.map.alert.open({
|
||||||
content: msg,
|
content: msg,
|
||||||
level: 'error',
|
level: 'error',
|
||||||
duration: 100000,
|
duration: 100000,
|
||||||
|
|
|
@ -53,7 +53,7 @@ U.MapPermissions = L.Class.extend({
|
||||||
edit: function () {
|
edit: function () {
|
||||||
if (this.map.options.editMode !== 'advanced') return
|
if (this.map.options.editMode !== 'advanced') return
|
||||||
if (!this.map.options.umap_id)
|
if (!this.map.options.umap_id)
|
||||||
return this.map.ui.alert({
|
return this.map.alert.open({
|
||||||
content: L._('Please save the map first'),
|
content: L._('Please save the map first'),
|
||||||
level: 'info',
|
level: 'info',
|
||||||
})
|
})
|
||||||
|
@ -139,7 +139,7 @@ U.MapPermissions = L.Class.extend({
|
||||||
const [data, response, error] = await this.map.server.post(this.getAttachUrl())
|
const [data, response, error] = await this.map.server.post(this.getAttachUrl())
|
||||||
if (!error) {
|
if (!error) {
|
||||||
this.options.owner = this.map.options.user
|
this.options.owner = this.map.options.user
|
||||||
this.map.ui.alert({
|
this.map.alert.open({
|
||||||
content: L._('Map has been attached to your account'),
|
content: L._('Map has been attached to your account'),
|
||||||
level: 'info',
|
level: 'info',
|
||||||
})
|
})
|
||||||
|
|
|
@ -83,7 +83,7 @@ U.TableEditor = L.Class.extend({
|
||||||
|
|
||||||
validateName: function (name) {
|
validateName: function (name) {
|
||||||
if (name.indexOf('.') !== -1) {
|
if (name.indexOf('.') !== -1) {
|
||||||
this.datalayer.map.ui.alert({
|
this.datalayer.map.alert.open({
|
||||||
content: L._('Invalide property name: {name}', { name: name }),
|
content: L._('Invalide property name: {name}', { name: name }),
|
||||||
level: 'error',
|
level: 'error',
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,190 +0,0 @@
|
||||||
/*
|
|
||||||
* Modals
|
|
||||||
*/
|
|
||||||
U.UI = L.Evented.extend({
|
|
||||||
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, 'wheel', L.DomEvent.stopPropagation)
|
|
||||||
L.DomEvent.on(this.container, 'MozMousePixelScroll', L.DomEvent.stopPropagation)
|
|
||||||
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'
|
|
||||||
},
|
|
||||||
|
|
||||||
alert: function (e) {
|
|
||||||
if (L.DomUtil.hasClass(this.parent, 'umap-alert')) this.ALERTS.push(e)
|
|
||||||
else this.popAlert(e)
|
|
||||||
},
|
|
||||||
|
|
||||||
popAlert: function (e) {
|
|
||||||
if (!e) {
|
|
||||||
if (this.ALERTS.length) e = this.ALERTS.pop()
|
|
||||||
else return
|
|
||||||
}
|
|
||||||
let timeoutID
|
|
||||||
const 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)
|
|
||||||
const close = () => {
|
|
||||||
if (timeoutID && 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()
|
|
||||||
}
|
|
||||||
const closeButton = L.DomUtil.createButton(
|
|
||||||
'umap-close-link',
|
|
||||||
this._alert,
|
|
||||||
'',
|
|
||||||
close,
|
|
||||||
this
|
|
||||||
)
|
|
||||||
L.DomUtil.create('i', 'umap-close-icon', closeButton)
|
|
||||||
const label = L.DomUtil.create('span', '', closeButton)
|
|
||||||
label.title = label.textContent = L._('Close')
|
|
||||||
L.DomUtil.element({ tagName: 'div', innerHTML: e.content, parent: this._alert })
|
|
||||||
if (e.actions) {
|
|
||||||
let action, el, input
|
|
||||||
const form = L.DomUtil.create('div', 'umap-alert-actions', this._alert)
|
|
||||||
for (let i = 0; i < e.actions.length; i++) {
|
|
||||||
action = e.actions[i]
|
|
||||||
if (action.input) {
|
|
||||||
input = L.DomUtil.element({
|
|
||||||
tagName: 'input',
|
|
||||||
parent: form,
|
|
||||||
className: 'umap-alert-input',
|
|
||||||
placeholder: action.input,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
el = L.DomUtil.createButton(
|
|
||||||
'umap-action',
|
|
||||||
form,
|
|
||||||
action.label,
|
|
||||||
action.callback,
|
|
||||||
action.callbackContext || this.map
|
|
||||||
)
|
|
||||||
L.DomEvent.on(el, 'click', close, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e.duration !== Infinity) {
|
|
||||||
this.ALERT_ID = timeoutID = window.setTimeout(
|
|
||||||
L.bind(close, this),
|
|
||||||
e.duration || 3000
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
tooltip: function (opts) {
|
|
||||||
function showIt() {
|
|
||||||
if (opts.anchor && opts.position === 'top') {
|
|
||||||
this.anchorTooltipTop(opts.anchor)
|
|
||||||
} else if (opts.anchor && opts.position === 'left') {
|
|
||||||
this.anchorTooltipLeft(opts.anchor)
|
|
||||||
} else if (opts.anchor && opts.position === 'bottom') {
|
|
||||||
this.anchorTooltipBottom(opts.anchor)
|
|
||||||
} else {
|
|
||||||
this.anchorTooltipAbsolute()
|
|
||||||
}
|
|
||||||
L.DomUtil.addClass(this.parent, 'umap-tooltip')
|
|
||||||
this._tooltip.innerHTML = U.Utils.escapeHTML(opts.content)
|
|
||||||
}
|
|
||||||
this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0)
|
|
||||||
const id = this.TOOLTIP_ID
|
|
||||||
function closeIt() {
|
|
||||||
this.closeTooltip(id)
|
|
||||||
}
|
|
||||||
if (opts.anchor) {
|
|
||||||
L.DomEvent.once(opts.anchor, 'mouseout', closeIt, this)
|
|
||||||
}
|
|
||||||
if (opts.duration !== Infinity) {
|
|
||||||
window.setTimeout(L.bind(closeIt, this), opts.duration || 3000)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
anchorTooltipAbsolute: function () {
|
|
||||||
this._tooltip.className = ''
|
|
||||||
const 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'
|
|
||||||
const coords = this.getPosition(el)
|
|
||||||
this.setTooltipPosition({
|
|
||||||
left: coords.left - 10,
|
|
||||||
bottom: this.getDocHeight() - coords.top + 11,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
anchorTooltipBottom: function (el) {
|
|
||||||
this._tooltip.className = 'tooltip-bottom'
|
|
||||||
const coords = this.getPosition(el)
|
|
||||||
this.setTooltipPosition({
|
|
||||||
left: coords.left,
|
|
||||||
top: coords.bottom + 11,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
anchorTooltipLeft: function (el) {
|
|
||||||
this._tooltip.className = 'tooltip-left'
|
|
||||||
const coords = this.getPosition(el)
|
|
||||||
this.setTooltipPosition({
|
|
||||||
top: coords.top,
|
|
||||||
right: document.documentElement.offsetWidth - coords.left + 11,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
closeTooltip: function (id) {
|
|
||||||
// Clear timetout even if a new tooltip has been added
|
|
||||||
// in the meantime. Eg. after a mouseout from the anchor.
|
|
||||||
window.clearTimeout(id)
|
|
||||||
if (id && id !== this.TOOLTIP_ID) return
|
|
||||||
this._tooltip.className = ''
|
|
||||||
this._tooltip.innerHTML = ''
|
|
||||||
this.setTooltipPosition({})
|
|
||||||
L.DomUtil.removeClass(this.parent, 'umap-tooltip')
|
|
||||||
},
|
|
||||||
|
|
||||||
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'
|
|
||||||
},
|
|
||||||
|
|
||||||
getDocHeight: function () {
|
|
||||||
const D = document
|
|
||||||
return Math.max(
|
|
||||||
D.body.scrollHeight,
|
|
||||||
D.documentElement.scrollHeight,
|
|
||||||
D.body.offsetHeight,
|
|
||||||
D.documentElement.offsetHeight,
|
|
||||||
D.body.clientHeight,
|
|
||||||
D.documentElement.clientHeight
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -17,6 +17,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* *********** */
|
||||||
|
/* Structure */
|
||||||
|
/* *********** */
|
||||||
|
|
||||||
|
.umap-edit-enabled {
|
||||||
|
--current-header-height: var(--header-height);
|
||||||
|
}
|
||||||
|
.umap-caption-bar-enabled {
|
||||||
|
--current-footer-height: var(--footer-height);
|
||||||
|
}
|
||||||
|
.leaflet-top {
|
||||||
|
top: var(--current-header-height);
|
||||||
|
}
|
||||||
|
.leaflet-bottom {
|
||||||
|
bottom: var(--current-footer-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* *********** */
|
/* *********** */
|
||||||
/* Controls */
|
/* Controls */
|
||||||
/* *********** */
|
/* *********** */
|
||||||
|
@ -99,10 +117,10 @@
|
||||||
box-shadow: 0 0 4px 0 black inset;
|
box-shadow: 0 0 4px 0 black inset;
|
||||||
}
|
}
|
||||||
.leaflet-control-star [type="button"] {
|
.leaflet-control-star [type="button"] {
|
||||||
background-position: -108px -144px;
|
background-position: -144px -144px;
|
||||||
}
|
}
|
||||||
.leaflet-control-star.starred [type="button"] {
|
.leaflet-control-star.starred [type="button"] {
|
||||||
background-position: -144px -144px;
|
background-position: -108px -144px;
|
||||||
}
|
}
|
||||||
.leaflet-control-search [type="button"] {
|
.leaflet-control-search [type="button"] {
|
||||||
background-position: -36px -108px;
|
background-position: -36px -108px;
|
||||||
|
@ -390,24 +408,6 @@ ul.photon-autocomplete {
|
||||||
/* ********************************* */
|
/* ********************************* */
|
||||||
/* Help Lightbox */
|
/* Help Lightbox */
|
||||||
/* ********************************* */
|
/* ********************************* */
|
||||||
.umap-help-box {
|
|
||||||
z-index: 10001;
|
|
||||||
position: absolute;
|
|
||||||
margin: 0 calc(50% - 500px/2);
|
|
||||||
width: 500px;
|
|
||||||
max-width: 100vw;
|
|
||||||
padding: 40px 20px;
|
|
||||||
border: 1px solid #222;
|
|
||||||
background-color: var(--color-darkGray);
|
|
||||||
color: #efefef;
|
|
||||||
font-size: 0.8em;
|
|
||||||
visibility: hidden;
|
|
||||||
top: -100%;
|
|
||||||
}
|
|
||||||
.umap-help-box .umap-close-link {
|
|
||||||
float: right;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
.umap-help-button {
|
.umap-help-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
|
@ -426,10 +426,6 @@ ul.photon-autocomplete {
|
||||||
.dark .umap-help-button {
|
.dark .umap-help-button {
|
||||||
background-image: url('./img/16-white.svg');
|
background-image: url('./img/16-white.svg');
|
||||||
}
|
}
|
||||||
.umap-help-on .umap-help-box {
|
|
||||||
visibility: visible;
|
|
||||||
top: 100px;
|
|
||||||
}
|
|
||||||
.umap-help-entry + .umap-help-entry {
|
.umap-help-entry + .umap-help-entry {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
border-top: 1px solid #aaa;
|
border-top: 1px solid #aaa;
|
||||||
|
@ -639,9 +635,6 @@ ul.photon-autocomplete {
|
||||||
.umap-edit-enabled .umap-main-edit-toolbox {
|
.umap-edit-enabled .umap-main-edit-toolbox {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
.umap-edit-enabled .umap-caption-bar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.umap-caption-bar h3,
|
.umap-caption-bar h3,
|
||||||
.umap-main-edit-toolbox h3 {
|
.umap-main-edit-toolbox h3 {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
@ -664,12 +657,9 @@ ul.photon-autocomplete {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
display: inline-block; /* Prevents underline on hover. */
|
display: inline-block; /* Prevents underline on hover. */
|
||||||
}
|
}
|
||||||
.umap-edit-enabled .leaflet-top {
|
|
||||||
top: 46px;
|
|
||||||
}
|
|
||||||
.umap-caption-bar-enabled .umap-caption-bar {
|
.umap-caption-bar-enabled .umap-caption-bar {
|
||||||
display: block;
|
display: block;
|
||||||
height: 46px;
|
height: var(--header-height);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -678,15 +668,12 @@ ul.photon-autocomplete {
|
||||||
right: 0;
|
right: 0;
|
||||||
padding: 0 0 0 5px;
|
padding: 0 0 0 5px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 46px;
|
line-height: 100%;
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
border-top: 1px solid var(--color-lightGray);
|
border-top: 1px solid var(--color-lightGray);
|
||||||
opacity: 0.93;
|
opacity: 0.93;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
.umap-caption-bar-enabled .leaflet-bottom {
|
|
||||||
bottom: 46px;
|
|
||||||
}
|
|
||||||
.umap-help {
|
.umap-help {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,24 @@ describe('Utils', function () {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not escape video tag with dedicated attributes', function () {
|
||||||
|
assert.equal(
|
||||||
|
Utils.escapeHTML(
|
||||||
|
'<video width="100%" height="281" controls><source type="video/mp4" src="movie.mp4"></video>'
|
||||||
|
),
|
||||||
|
'<video controls="" height="281" width="100%"><source src="movie.mp4" type="video/mp4"></video>'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not escape audio tag with dedicated attributes', function () {
|
||||||
|
assert.equal(
|
||||||
|
Utils.escapeHTML(
|
||||||
|
'<audio controls><source type="audio/ogg" src="horse.ogg"></audio>'
|
||||||
|
),
|
||||||
|
'<audio controls=""><source src="horse.ogg" type="audio/ogg"></audio>'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('should not fail with int value', function () {
|
it('should not fail with int value', function () {
|
||||||
assert.equal(Utils.escapeHTML(25), '25')
|
assert.equal(Utils.escapeHTML(25), '25')
|
||||||
})
|
})
|
||||||
|
@ -461,13 +479,12 @@ describe('Utils', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#normalize()', function () {
|
describe('#normalize()', function () {
|
||||||
it('should remove accents',
|
it('should remove accents', function () {
|
||||||
function () {
|
// French é
|
||||||
// French é
|
assert.equal(Utils.normalize('aéroport'), 'aeroport')
|
||||||
assert.equal(Utils.normalize('aéroport'), 'aeroport')
|
// American é
|
||||||
// American é
|
assert.equal(Utils.normalize('aéroport'), 'aeroport')
|
||||||
assert.equal(Utils.normalize('aéroport'), 'aeroport')
|
})
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#sortFeatures()', function () {
|
describe('#sortFeatures()', function () {
|
||||||
|
@ -530,17 +547,17 @@ describe('Utils', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("#copyJSON", function () {
|
describe('#copyJSON', function () {
|
||||||
it('should actually copy the JSON', function () {
|
it('should actually copy the JSON', function () {
|
||||||
let originalJSON = { "some": "json" }
|
let originalJSON = { some: 'json' }
|
||||||
let returned = Utils.CopyJSON(originalJSON)
|
let returned = Utils.CopyJSON(originalJSON)
|
||||||
|
|
||||||
// Change the original JSON
|
// Change the original JSON
|
||||||
originalJSON["anotherKey"] = "value"
|
originalJSON['anotherKey'] = 'value'
|
||||||
|
|
||||||
// ensure the two aren't the same object
|
// ensure the two aren't the same object
|
||||||
assert.notEqual(returned, originalJSON)
|
assert.notEqual(returned, originalJSON)
|
||||||
assert.deepEqual(returned, { "some": "json" })
|
assert.deepEqual(returned, { some: 'json' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -599,19 +616,34 @@ describe('Utils', function () {
|
||||||
})
|
})
|
||||||
describe('parseNaiveDate', () => {
|
describe('parseNaiveDate', () => {
|
||||||
it('should parse a date', () => {
|
it('should parse a date', () => {
|
||||||
assert.equal(Utils.parseNaiveDate("2024/03/04").toISOString(), "2024-03-04T00:00:00.000Z")
|
assert.equal(
|
||||||
|
Utils.parseNaiveDate('2024/03/04').toISOString(),
|
||||||
|
'2024-03-04T00:00:00.000Z'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
it('should parse a datetime', () => {
|
it('should parse a datetime', () => {
|
||||||
assert.equal(Utils.parseNaiveDate("2024/03/04 12:13:14").toISOString(), "2024-03-04T00:00:00.000Z")
|
assert.equal(
|
||||||
|
Utils.parseNaiveDate('2024/03/04 12:13:14').toISOString(),
|
||||||
|
'2024-03-04T00:00:00.000Z'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
it('should parse an iso datetime', () => {
|
it('should parse an iso datetime', () => {
|
||||||
assert.equal(Utils.parseNaiveDate("2024-03-04T00:00:00.000Z").toISOString(), "2024-03-04T00:00:00.000Z")
|
assert.equal(
|
||||||
|
Utils.parseNaiveDate('2024-03-04T00:00:00.000Z').toISOString(),
|
||||||
|
'2024-03-04T00:00:00.000Z'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
it('should parse a GMT time', () => {
|
it('should parse a GMT time', () => {
|
||||||
assert.equal(Utils.parseNaiveDate("04 Mar 2024 00:12:00 GMT").toISOString(), "2024-03-04T00:00:00.000Z")
|
assert.equal(
|
||||||
|
Utils.parseNaiveDate('04 Mar 2024 00:12:00 GMT').toISOString(),
|
||||||
|
'2024-03-04T00:00:00.000Z'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
it('should parse a GMT time with explicit timezone', () => {
|
it('should parse a GMT time with explicit timezone', () => {
|
||||||
assert.equal(Utils.parseNaiveDate("Thu, 04 Mar 2024 00:00:00 GMT+0300").toISOString(), "2024-03-03T00:00:00.000Z")
|
assert.equal(
|
||||||
|
Utils.parseNaiveDate('Thu, 04 Mar 2024 00:00:00 GMT+0300').toISOString(),
|
||||||
|
'2024-03-03T00:00:00.000Z'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
--background-color: var(--color-light);
|
--background-color: var(--color-light);
|
||||||
--color-accent: var(--color-brightCyan);
|
--color-accent: var(--color-brightCyan);
|
||||||
|
--text-color: black;
|
||||||
|
|
||||||
/* Buttons. */
|
/* Buttons. */
|
||||||
--button-primary-background: var(--color-waterMint);
|
--button-primary-background: var(--color-waterMint);
|
||||||
|
@ -24,9 +25,12 @@
|
||||||
--panel-header-height: 36px;
|
--panel-header-height: 36px;
|
||||||
--panel-width: 400px;
|
--panel-width: 400px;
|
||||||
--header-height: 46px;
|
--header-height: 46px;
|
||||||
|
--current-header-height: 0px;
|
||||||
--footer-height: 46px;
|
--footer-height: 46px;
|
||||||
|
--current-footer-height: 0px;
|
||||||
--control-size: 36px;
|
--control-size: 36px;
|
||||||
}
|
}
|
||||||
.dark {
|
.dark {
|
||||||
--background-color: var(--color-darkGray);
|
--background-color: var(--color-darkGray);
|
||||||
|
--text-color: #efefef;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.addEventListener('DOMContentLoaded', event => {
|
window.addEventListener('DOMContentLoaded', event => {
|
||||||
const ui = new U.UI(document.querySelector('header'))
|
const alert = new U.Alert(document.querySelector('header'))
|
||||||
const server = new U.ServerRequest(ui)
|
const server = new U.ServerRequest(alert)
|
||||||
const getMore = async function (e) {
|
const getMore = async function (e) {
|
||||||
L.DomEvent.stop(e)
|
L.DomEvent.stop(e)
|
||||||
const [{html}, response, error] = await server.get(this.href)
|
const [{html}, response, error] = await server.get(this.href)
|
||||||
|
|
|
@ -29,4 +29,7 @@
|
||||||
<link rel="stylesheet" href="{% static 'umap/nav.css' %}" />
|
<link rel="stylesheet" href="{% static 'umap/nav.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'umap/map.css' %}" />
|
<link rel="stylesheet" href="{% static 'umap/map.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'umap/css/panel.css' %}" />
|
<link rel="stylesheet" href="{% static 'umap/css/panel.css' %}" />
|
||||||
|
<link rel="stylesheet" href="{% static 'umap/css/alert.css' %}" />
|
||||||
|
<link rel="stylesheet" href="{% static 'umap/css/tooltip.css' %}" />
|
||||||
|
<link rel="stylesheet" href="{% static 'umap/css/dialog.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'umap/theme.css' %}" />
|
<link rel="stylesheet" href="{% static 'umap/theme.css' %}" />
|
||||||
|
|
|
@ -46,7 +46,6 @@
|
||||||
<script src="{% static 'umap/vendors/simple-statistics/simple-statistics.min.js' %}"
|
<script src="{% static 'umap/vendors/simple-statistics/simple-statistics.min.js' %}"
|
||||||
defer></script>
|
defer></script>
|
||||||
<script src="{% static 'umap/js/umap.core.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.core.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.autocomplete.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/js/umap.popup.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.popup.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.forms.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.forms.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.icon.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.icon.js' %}" defer></script>
|
||||||
|
@ -57,8 +56,6 @@
|
||||||
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.slideshow.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.slideshow.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.tableeditor.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.tableeditor.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.importer.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/js/umap.share.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.share.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.ui.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script>
|
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
U.MAP = new U.Map("map", {{ map_settings|notag|safe }})
|
U.MAP = new U.Map("map", {{ map_settings|notag|safe }})
|
||||||
{% for m in messages %}
|
{% for m in messages %}
|
||||||
{# We have just one, but we need to loop, as for messages API #}
|
{# We have just one, but we need to loop, as for messages API #}
|
||||||
U.MAP.ui.alert({
|
U.MAP.alert.open({
|
||||||
content: "{{ m }}",
|
content: "{{ m }}",
|
||||||
level: "{{ m.tags }}",
|
level: "{{ m.tags }}",
|
||||||
duration: 100000
|
duration: 100000
|
||||||
|
|
|
@ -164,7 +164,7 @@ def test_alert_message_after_create(
|
||||||
page.goto(f"{live_server.url}/en/map/new")
|
page.goto(f"{live_server.url}/en/map/new")
|
||||||
save = page.get_by_role("button", name="Save")
|
save = page.get_by_role("button", name="Save")
|
||||||
expect(save).to_be_visible()
|
expect(save).to_be_visible()
|
||||||
alert = page.locator(".umap-alert")
|
alert = page.locator("#umap-alert-container")
|
||||||
expect(alert).to_be_hidden()
|
expect(alert).to_be_hidden()
|
||||||
with page.expect_response(re.compile(r".*/map/create/")):
|
with page.expect_response(re.compile(r".*/map/create/")):
|
||||||
save.click()
|
save.click()
|
||||||
|
@ -194,7 +194,7 @@ def test_alert_message_after_create(
|
||||||
|
|
||||||
def test_email_sending_error_are_catched(tilelayer, page, live_server):
|
def test_email_sending_error_are_catched(tilelayer, page, live_server):
|
||||||
page.goto(f"{live_server.url}/en/map/new")
|
page.goto(f"{live_server.url}/en/map/new")
|
||||||
alert = page.locator(".umap-alert")
|
alert = page.locator("#umap-alert-container")
|
||||||
with page.expect_response(re.compile(r".*/map/create/")):
|
with page.expect_response(re.compile(r".*/map/create/")):
|
||||||
page.get_by_role("button", name="Save").click()
|
page.get_by_role("button", name="Save").click()
|
||||||
alert.get_by_placeholder("Email").fill("foo@bar.com")
|
alert.get_by_placeholder("Email").fill("foo@bar.com")
|
||||||
|
@ -214,7 +214,7 @@ def test_alert_message_after_create_show_link_even_without_mail(
|
||||||
page.goto(f"{live_server.url}/en/map/new")
|
page.goto(f"{live_server.url}/en/map/new")
|
||||||
with page.expect_response(re.compile(r".*/map/create/")):
|
with page.expect_response(re.compile(r".*/map/create/")):
|
||||||
page.get_by_role("button", name="Save").click()
|
page.get_by_role("button", name="Save").click()
|
||||||
alert = page.locator(".umap-alert")
|
alert = page.locator("#umap-alert-container")
|
||||||
expect(alert).to_be_visible()
|
expect(alert).to_be_visible()
|
||||||
expect(
|
expect(
|
||||||
alert.get_by_text(
|
alert.get_by_text(
|
||||||
|
|
|
@ -28,7 +28,7 @@ def test_owner_can_delete_map_after_confirmation(map, live_server, login):
|
||||||
def test_dashboard_map_preview(map, live_server, datalayer, login):
|
def test_dashboard_map_preview(map, live_server, datalayer, login):
|
||||||
page = login(map.owner)
|
page = login(map.owner)
|
||||||
page.goto(f"{live_server.url}/en/me")
|
page.goto(f"{live_server.url}/en/me")
|
||||||
dialog = page.locator("dialog")
|
dialog = page.get_by_role("dialog")
|
||||||
expect(dialog).to_be_hidden()
|
expect(dialog).to_be_hidden()
|
||||||
button = page.get_by_role("button", name="Open preview")
|
button = page.get_by_role("button", name="Open preview")
|
||||||
expect(button).to_be_visible()
|
expect(button).to_be_visible()
|
||||||
|
|
|
@ -448,4 +448,4 @@ def test_import_csv_without_valid_latlon_headers(tilelayer, live_server, page):
|
||||||
# FIXME do not create a layer
|
# FIXME do not create a layer
|
||||||
expect(layers).to_have_count(1)
|
expect(layers).to_have_count(1)
|
||||||
expect(markers).to_have_count(0)
|
expect(markers).to_have_count(0)
|
||||||
expect(page.locator(".umap-alert")).to_be_visible()
|
expect(page.locator("#umap-alert-container")).to_be_visible()
|
||||||
|
|
Loading…
Reference in a new issue