kopia lustrzana https://github.com/openstreetmap-polska/aed-mapa
commit
a1535f7bf6
132
index.html
132
index.html
|
@ -1,10 +1,10 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="pl">
|
<html lang="pl">
|
||||||
<head>
|
<head>
|
||||||
<title>AED - mapa defibrylatorów</title>
|
<title>AED - mapa defibrylatorów</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="title" content="AED - mapa defibrylatorów">
|
<meta name="title" content="AED - mapa defibrylatorów">
|
||||||
<meta name="description" content="Mapa defibrylatorów AED w Polsce oparta o dane OpenStreetMap">
|
<meta name="description" content="Mapa defibrylatorów AED w Polsce oparta o dane OpenStreetMap">
|
||||||
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
|
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="src/favicon/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="src/favicon/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="./src/favicon/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="./src/favicon/favicon-32x32.png">
|
||||||
|
@ -25,23 +25,31 @@
|
||||||
<meta property="twitter:title" content="AED - mapa defibrylatorów">
|
<meta property="twitter:title" content="AED - mapa defibrylatorów">
|
||||||
<meta property="twitter:description" content="Mapa defibrylatorów AED w Polsce oparta o dane OpenStreetMap">
|
<meta property="twitter:description" content="Mapa defibrylatorów AED w Polsce oparta o dane OpenStreetMap">
|
||||||
<meta property="twitter:image" content="https://aed.openstreetmap.org.pl/src/img/meta-image.png">
|
<meta property="twitter:image" content="https://aed.openstreetmap.org.pl/src/img/meta-image.png">
|
||||||
|
<script src='./src/osmauth.min.js'></script>
|
||||||
<script src="https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.js"></script>
|
<script src="https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.js"></script>
|
||||||
<script src="https://openingh.openstreetmap.de/opening_hours.js/opening_hours+deps.min.js" defer></script>
|
<script src="https://openingh.openstreetmap.de/opening_hours.js/opening_hours+deps.min.js" defer></script>
|
||||||
<link href="https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.css" rel="stylesheet" />
|
<link href="https://unpkg.com/maplibre-gl@1.15.2/dist/maplibre-gl.css" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css" />
|
||||||
<link rel="stylesheet" href="./src/nasz.css" />
|
<link rel="stylesheet" href="./src/nasz.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-success p-1 has-shadow" role="navigation" aria-label="main navigation">
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar has-background-green p-1" role="navigation" aria-label="main navigation">
|
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="has-text-weight-light navbar-item has-text-white-ter is-size-4 is-size-5-mobile" href="#">
|
<a class="has-text-weight-light navbar-item has-text-white-ter is-size-4 is-size-5-mobile" href="#">
|
||||||
Mapa <span class="has-text-weight-semibold pl-1 r-1">AED</span>
|
Mapa <span class="has-text-weight-semibold pl-1 r-1">AED</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<span class="has-text-success-light has-text-weight-light navbar-item is-size-6 is-size-7-touch pl-0">
|
<span class="has-text-success-light has-text-weight-light navbar-item is-size-6 is-size-7-touch pl-0">
|
||||||
<span class="has-text-grey-light mr-2">|</span>tworzona z ❤️ przez
|
<span class="has-text-grey-light mr-2">|</span>tworzona z ❤️
|
||||||
<span class="has-text-weight-medium"> <a href="https://openstreetmap.org.pl/" target="_blank" title="Odwiedź stronę polskiej społeczności OSM" rel="noopener" style="color:#effaf5;">OpenStreetMap Polska</a></span>
|
przez
|
||||||
|
<span class="has-text-weight-medium"> <a href="https://openstreetmap.org.pl/" target="_blank"
|
||||||
|
title="Odwiedź stronę polskiej społeczności OSM"
|
||||||
|
rel="noopener" style="color:#effaf5;">OpenStreetMap Polska</a>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a role="button" class="navbar-burger has-text-white-ter is-transparent" data-target="navMenu" aria-label="menu" aria-expanded="false">
|
<a role="button" class="navbar-burger has-text-white-ter is-transparent" data-target="navMenu" aria-label="menu" aria-expanded="false">
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
|
@ -50,44 +58,98 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="navMenu" class="navbar-menu">
|
<div id="navMenu" class="navbar-menu">
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<span class="has-text-white has-text-weight-light mr-1">dodaliśmy do mapy już ⚡</span>
|
<svg class="icon" style="width:24px;height:24px" viewBox="0 0 24 24">
|
||||||
<span class="has-text-white-ter is-size-5 has-text-weight-semibold" id="aed-number"></span>
|
<path fill="#ffc83d" d="M11 15H6L13 1V9H18L11 23V15Z" />
|
||||||
<span class="has-text-white has-text-weight-light ml-1">AED</span>
|
</svg>
|
||||||
</div>
|
<span class="has-text-white has-text-weight-light ">dodaliśmy już </span>
|
||||||
<div class="navbar-item pb-0">
|
<span class="has-text-white-ter has-text-weight-semibold" id="aed-number">
|
||||||
<a href="https://github.com/openstreetmap-polska/aed-mapa" target="_blank" rel="noopener">
|
<button class="button is-loading is-rounded"></button>
|
||||||
<img title="Odwiedź nasz Github" class="github" src="./src/img/github-image.png" alt="Github" width="24" height="24"></a>
|
</span>
|
||||||
|
<span class="has-text-white has-text-weight-light ml-1">AED</span>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item" id="span-login"></div>
|
||||||
|
<div id="navbar-logged" class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a id="navbar-username" class="navbar-link has-text-white-ter has-text-weight-light is-arrowless">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="navbar-dropdown has-background-green">
|
||||||
|
<a class="navbar-item" id="logout">
|
||||||
|
<svg class="icon mr-1" style="width:24px;height:24px" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M16,17V14H9V10H16V7L21,12L16,17M14,2A2,2 0 0,1 16,4V6H14V4H5V20H14V18H16V20A2,2 0 0,1 14,22H5A2,2 0 0,1 3,20V4A2,2 0 0,1 5,2H14Z" />
|
||||||
|
</svg> Wyloguj
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<div class="navbar-item pb-0">
|
||||||
|
<a href="https://github.com/openstreetmap-polska/aed-mapa" target="_blank" rel="noopener">
|
||||||
|
<img title="Odwiedź nasz Github" class="github" src="./src/img/github-image.png" alt="Github" width="24" height="24"></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
<div class="sidebar is-invisible">
|
|
||||||
<div id="poi-sidebar" class="card">
|
<div id="sidebar-div" class="sidebar is-invisible">
|
||||||
|
<div class="card">
|
||||||
|
|
||||||
<div id="sidebar-header">
|
<div id="sidebar-header">
|
||||||
<div class="columns is-vcentered is-flex p-1 mr-0">
|
<div class="columns is-vcentered is-flex p-1 mr-0">
|
||||||
<div class="column is-one-quarter is-one-fifth-mobile "><img class="image" src="./src/img/card-image.png" alt="Placeholder image" id="sidebar-card-image" ></div>
|
|
||||||
<div class="column"><p class="title has-text-white-ter has-text-weight-light py-2" id="sidebar-caption"></p></div>
|
<div class="column is-one-quarter is-one-fifth-mobile ">
|
||||||
<button class="delete is-medium is-hidden-touch is-pulled-right close-button mr-2 mt-4" onclick="hideSidebar()" aria-label="Close button"></button>
|
<img class="image" src="./src/img/card-image.png" alt="Placeholder image" id="sidebar-card-image">
|
||||||
<button class="delete is-large is-hidden-desktop is-pulled-right close-button mr-2 mt-4" onclick="hideSidebar()" aria-label="Close button"></button>
|
</div>
|
||||||
</div>
|
<div class="column">
|
||||||
</div>
|
<p id="sidebar-caption" class="title has-text-white-ter has-text-weight-light py-2"></p>
|
||||||
<div class="card-content">
|
</div>
|
||||||
<div class="content">
|
<button id="sidebar-button-close-touch" aria-label="Zamknij ekran boczny"
|
||||||
|
class="delete is-medium is-hidden-touch is-pulled-right close-button mr-2 mt-4"></button>
|
||||||
|
<button id="sidebar-button-close-desktop" aria-label="Zamknij ekran boczny"
|
||||||
|
class="delete is-large is-hidden-desktop is-pulled-right close-button mr-2 mt-4"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card-content">
|
||||||
|
<div id="sidebar-content-div" class="content"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a href="" class="has-background-success-light card-footer-item has-text-centered is-size-7 has-text-weight-semibold" target="_blank" rel="noopener" >Dodaj brakujące informacje</a>
|
<div class="has-background-success-light card-footer-item has-text-centered is-size-7 has-text-weight-semibold" id="sidebar-footer-button-left">
|
||||||
<a href="https://wiki.openstreetmap.org/wiki/Pl:Przewodnik_dla_pocz%C4%85tkuj%C4%85cych"
|
<a href="" target="_blank" rel="noopener"
|
||||||
class="has-background-success-light card-footer-item has-text-centered is-size-7 has-text-weight-semibold" target="_blank" rel="noopener" >Przewodnik OSM</a>
|
class="has-background-success-light card-footer-item has-text-centered is-size-7 has-text-weight-semibold"></a>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="modal-div" class="modal">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<!-- <p class="modal-card-title">Modal title</p>-->
|
||||||
|
<button class="delete" aria-label="close" onclick="closeModal()"></button>
|
||||||
|
</header>
|
||||||
|
<section id="modal-content" class="modal-card-body">
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<button class="modal-close is-large" aria-label="close" onclick="closeModal()"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
|
<button id="addNode" class="button is-floating is-medium is-success" style="bottom: 8px;left: 16px;" aria-label="Wymaga zalogowania" disabled>
|
||||||
|
<svg class="icon" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<script src="./src/other-ui-stuff.js"></script>
|
||||||
<script src="./src/map.js"></script>
|
<script src="./src/map.js"></script>
|
||||||
|
<script src="./src/osm-integration.js"></script>
|
||||||
|
|
||||||
<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "117bf6ce871a40ac9bf5a7330f8538eb"}'></script><!-- End Cloudflare Web Analytics -->
|
<!-- Cloudflare Web Analytics -->
|
||||||
</body>
|
<script defer src='https://static.cloudflareinsights.com/beacon.min.js'
|
||||||
|
data-cf-beacon='{"token": "117bf6ce871a40ac9bf5a7330f8538eb"}'></script>
|
||||||
|
<!-- End Cloudflare Web Analytics -->
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html><head></head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
opener.authComplete(window.location.href);
|
||||||
|
window.close();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
213
src/map.js
213
src/map.js
|
@ -1,22 +1,7 @@
|
||||||
let aedSource = './aed_poland.geojson';
|
const aedSource = './aed_poland.geojson';
|
||||||
let aedMetadata = './aed_poland_metadata.json';
|
const aedMetadata = './aed_poland_metadata.json';
|
||||||
let aedNumber = document.getElementById('aed-number');
|
let aedNumber = document.getElementById('aed-number');
|
||||||
|
|
||||||
fetch(aedMetadata)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
aedNumber.innerHTML = data.number_of_elements;
|
|
||||||
let refreshTimeValue = new Date(data.data_download_ts_utc);
|
|
||||||
let refreshTimeValueLocale = new Date(data.data_download_ts_utc).toLocaleString('pl-PL');
|
|
||||||
let currentDate = new Date();
|
|
||||||
let dateDiff = Math.abs(currentDate - refreshTimeValue);
|
|
||||||
let dateDiffMinutes = Math.round(dateDiff / 60000);
|
|
||||||
let refreshTime = document.getElementById('refresh-time');
|
|
||||||
refreshTime.innerHTML = `Synchronizacja z bazą OSM: <span class="has-text-grey-dark" title="${refreshTimeValueLocale}">${dateDiffMinutes} minut temu</span> | `;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
var map = new maplibregl.Map({
|
var map = new maplibregl.Map({
|
||||||
'container': 'map', // container id
|
'container': 'map', // container id
|
||||||
'center': [20, 52], // starting position [lng, lat]
|
'center': [20, 52], // starting position [lng, lat]
|
||||||
|
@ -43,7 +28,7 @@ var map = new maplibregl.Map({
|
||||||
'type': 'geojson',
|
'type': 'geojson',
|
||||||
'data': aedSource,
|
'data': aedSource,
|
||||||
'cluster': true,
|
'cluster': true,
|
||||||
'clusterRadius': 30,
|
'clusterRadius': 32,
|
||||||
'maxzoom': 14
|
'maxzoom': 14
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -68,146 +53,24 @@ let geolocate = new maplibregl.GeolocateControl({
|
||||||
});
|
});
|
||||||
map.addControl(geolocate, 'bottom-right');
|
map.addControl(geolocate, 'bottom-right');
|
||||||
|
|
||||||
function defineColor(access) {
|
|
||||||
accessValues = {
|
|
||||||
'yes': 'has-background-green',
|
|
||||||
'no': 'has-background-grey',
|
|
||||||
'private': 'has-background-grey',
|
|
||||||
'permissive': 'has-background-link-dark',
|
|
||||||
'default': 'has-background-grey'
|
|
||||||
};
|
|
||||||
|
|
||||||
accessClass = accessValues[access] || accessValues['default'];
|
|
||||||
return accessClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineAccessDescription(access) {
|
|
||||||
accessValues = {
|
|
||||||
'yes': 'ogólnodostępny',
|
|
||||||
'no': 'prywatny',
|
|
||||||
'private': 'prywatny',
|
|
||||||
'permissive': 'o ograniczonym dostępie',
|
|
||||||
'permit': 'o ograniczonym dostępie',
|
|
||||||
'default': ''
|
|
||||||
};
|
|
||||||
|
|
||||||
accessClass = accessValues[access] || accessValues['default'];
|
|
||||||
return accessClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseOpeningHours(openingHours) {
|
|
||||||
|
|
||||||
if (openingHours) {
|
|
||||||
if (openingHours.includes('24/7')) {
|
|
||||||
return 'całodobowo';
|
|
||||||
} else {
|
|
||||||
let hoursPrettified;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let hours = openingHours.toString();
|
|
||||||
let oh = new opening_hours(hours, undefined, 2);
|
|
||||||
isOpen = oh.getState();
|
|
||||||
hoursPrettified = oh.prettifyValue({
|
|
||||||
conf: {
|
|
||||||
locale: 'pl'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Error when parsing opening hours');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hoursPrettified;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCurrentlyOpen(openingHours) {
|
|
||||||
if (openingHours) {
|
|
||||||
if (openingHours.includes('24/7')) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
let hours = openingHours.toString();
|
|
||||||
let oh = new opening_hours(hours, undefined, 2);
|
|
||||||
isOpen = oh.getState();
|
|
||||||
return isOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineIndoor(indoor) {
|
|
||||||
if (indoor == 'yes') {
|
|
||||||
return 'tak';
|
|
||||||
} else if (indoor == 'no') {
|
|
||||||
return 'nie';
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showSidebar(properties) {
|
|
||||||
// SIDEBAR - UI
|
|
||||||
let sidebar = document.getElementsByClassName('sidebar')[0];
|
|
||||||
if (sidebar) {
|
|
||||||
sidebar.classList.remove('is-invisible');
|
|
||||||
createSidebar(properties);
|
|
||||||
} else {
|
|
||||||
console.log('Sidebar not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideSidebar() {
|
|
||||||
let sidebar = document.getElementsByClassName('sidebar')[0];
|
|
||||||
if (sidebar) {
|
|
||||||
sidebar.classList.add('is-invisible');
|
|
||||||
} else {
|
|
||||||
console.log('Sidebar not found.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOsmEditLink(id) {
|
|
||||||
return `https://www.openstreetmap.org/edit?editor=id&node=${id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createSidebar(properties) {
|
|
||||||
let sidebarHeader = document.getElementById('sidebar-header');
|
|
||||||
let sidebarCaption = document.getElementById('sidebar-caption');
|
|
||||||
let sidebarContent = document.getElementsByClassName('content')[0];
|
|
||||||
let sidebarLink = document.getElementsByClassName('card-footer-item')[0];
|
|
||||||
var isCurrOpen = '';
|
|
||||||
|
|
||||||
sidebarHeader.classList = [];
|
|
||||||
sidebarHeader.classList.add(defineColor(properties.access));
|
|
||||||
sidebarCaption.innerHTML = `defibrylator AED ${defineAccessDescription(properties.access)}`;
|
|
||||||
|
|
||||||
if (isCurrentlyOpen(properties.opening_hours)) {
|
|
||||||
isCurrOpen = '<sup><span class="tag is-success is-light">Dostępny</span></sup>';
|
|
||||||
} else if (isCurrentlyOpen(properties.opening_hours) == false) {
|
|
||||||
isCurrOpen = '<sup><span class="tag is-danger is-light">Niedostępny</span></sup>';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sidebarContent.innerHTML = '';
|
|
||||||
sidebarContent.innerHTML = `
|
|
||||||
<p class="has-text-weight-light">Wewnątrz budynku?: <span class="add-new has-text-weight-medium">${defineIndoor(properties.indoor) || `<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>`}</span></p>
|
|
||||||
<p class="has-text-weight-light">Dokładna lokalizacja: <span class="add-new has-text-weight-medium">${properties['defibrillator:location:pl'] || properties['defibrillator:location'] || `<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>`}</span></p>
|
|
||||||
<p class="has-text-weight-light">Dostępny w godzinach: <span class="add-new has-text-weight-medium">${parseOpeningHours(properties.opening_hours) || `<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>`} ${isCurrOpen || '' }</span></p>
|
|
||||||
<p class="has-text-weight-light">Opis: <span class="add-new has-text-weight-medium">${properties['description:pl'] || properties.description || `<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>`}</span></p>
|
|
||||||
<p class="has-text-weight-light">Numer kontaktowy: <span class="add-new has-text-weight-medium">${properties.phone || `<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>`}</span></p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (properties.note || properties['note:pl']) {
|
|
||||||
sidebarContent.innerHTML += `<p class="has-text-weight-light">Uwagi: <span class="add-new has-text-weight-medium">${properties['note:pl'] || properties.note || 'brak uwag'}</span></p>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
sidebarLink.setAttribute("href", getOsmEditLink(properties.osm_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
map.on('load', () => {
|
map.on('load', () => {
|
||||||
|
|
||||||
|
// get metadata and fill page with info about number of defibrillators and last refresh time
|
||||||
|
fetch(aedMetadata)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
// number of defibrillators
|
||||||
|
aedNumber.innerHTML = data.number_of_elements;
|
||||||
|
// last refresh time
|
||||||
|
let refreshTimeValue = new Date(data.data_download_ts_utc);
|
||||||
|
let refreshTimeValueLocale = new Date(data.data_download_ts_utc).toLocaleString('pl-PL');
|
||||||
|
let currentDate = new Date();
|
||||||
|
let dateDiff = Math.abs(currentDate - refreshTimeValue);
|
||||||
|
let dateDiffMinutes = Math.round(dateDiff / 60000);
|
||||||
|
let refreshTime = document.getElementById('refresh-time');
|
||||||
|
refreshTime.innerHTML = `Ostatnia aktualizacja danych OSM: <span class="has-text-grey-dark" title="${refreshTimeValueLocale}">${dateDiffMinutes} minut temu </span>`;
|
||||||
|
});
|
||||||
|
|
||||||
console.log('Loading icon...');
|
console.log('Loading icon...');
|
||||||
|
|
||||||
map.loadImage('./src/img/marker-image_50.png', (error, image) => {
|
map.loadImage('./src/img/marker-image_50.png', (error, image) => {
|
||||||
|
@ -253,9 +116,14 @@ map.on('load', () => {
|
||||||
},
|
},
|
||||||
'filter': ['has', 'point_count'],
|
'filter': ['has', 'point_count'],
|
||||||
});
|
});
|
||||||
|
|
||||||
map.on('click', 'unclustered', function (e) {
|
map.on('click', 'unclustered', function (e) {
|
||||||
if (e.features[0].properties !== undefined) {
|
if (e.features[0].properties !== undefined) {
|
||||||
showSidebar(e.features[0].properties);
|
let properties = {
|
||||||
|
action: "showDetails",
|
||||||
|
data: e.features[0].properties,
|
||||||
|
};
|
||||||
|
showSidebar(properties);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -293,33 +161,6 @@ map.on('load', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Ready.');
|
console.log('Map ready.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Bulma controls
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
|
|
||||||
// Get all "navbar-burger" elements
|
|
||||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
|
||||||
|
|
||||||
// Check if there are any navbar burgers
|
|
||||||
if ($navbarBurgers.length > 0) {
|
|
||||||
|
|
||||||
// Add a click event on each of them
|
|
||||||
$navbarBurgers.forEach( el => {
|
|
||||||
el.addEventListener('click', () => {
|
|
||||||
|
|
||||||
// Get the target from the "data-target" attribute
|
|
||||||
const target = el.dataset.target;
|
|
||||||
const $target = document.getElementById(target);
|
|
||||||
|
|
||||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
|
||||||
el.classList.toggle('is-active');
|
|
||||||
$target.classList.toggle('is-active');
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
89
src/nasz.css
89
src/nasz.css
|
@ -29,33 +29,28 @@
|
||||||
|
|
||||||
@media (min-width: 500px) {
|
@media (min-width: 500px) {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
margin: 0.5rem !important;
|
margin: 0.5rem !important;
|
||||||
width: 408px;
|
width: 408px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 .5em 1em -0.125em rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.02);
|
box-shadow: 0 .5em 1em -0.125em rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.02);
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
overflow-y: auto !important;
|
overflow: auto !important;
|
||||||
|
z-index: 10 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
@media (max-width: 500px) {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
position: absolute;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
z-index: 2 !important;
|
z-index: 10 !important;
|
||||||
overflow-y: auto !important;
|
overflow: auto !important;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
|
||||||
.navbar {
|
|
||||||
height: 1vh;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,29 +82,67 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar
|
.navbar.is-success {
|
||||||
{
|
background-color: #008954eb !important;
|
||||||
box-shadow: inset 0 0 1em #0000000c;
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
border-left: 5px solid hsl(0, 0%, 96%);
|
||||||
|
border-right: 5px solid hsl(0, 0%, 96%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-background-green {
|
.has-background-green {
|
||||||
background-color: #008954eb !important;
|
background-color: #008954eb !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-menu.is-active
|
.navbar-menu.is-active {
|
||||||
{
|
|
||||||
background-color: #008954eb !important;
|
background-color: #008954eb !important;
|
||||||
}
|
}
|
||||||
.mapboxgl-ctrl-attrib-inner {
|
|
||||||
font-size: 0.6rem !important;
|
.button.is-floating {
|
||||||
font-weight: 100 !important;
|
position: fixed;
|
||||||
}
|
width: 60px;
|
||||||
.mapboxgl-ctrl.mapboxgl-ctrl-attrib
|
height: 60px;
|
||||||
{
|
bottom: 40px;
|
||||||
border-top-left-radius: 10px;
|
right: 40px;
|
||||||
|
border-radius: 100px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
box-shadow: 0 .0625em .125em rgba(10, 10, 10, .05);
|
||||||
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item
|
.button.is-floating.is-large {
|
||||||
{
|
width: 90px;
|
||||||
padding-right: 7px !important;
|
height: 90px;
|
||||||
}
|
font-size: 2.6rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.is-floating.is-medium {
|
||||||
|
width: 75px;
|
||||||
|
height: 75px;
|
||||||
|
font-size: 2.2rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.is-floating.is-small {
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
border-radius: 50px
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*=" icon"], [class^=icon] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
stroke-width: 0;
|
||||||
|
stroke: currentColor;
|
||||||
|
fill: currentColor;
|
||||||
|
line-height: 1;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-item, .navbar-link {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,295 @@
|
||||||
|
// osm integration stuff
|
||||||
|
var auth = osmAuth({
|
||||||
|
oauth_consumer_key: 'SVN3D2Q8ciaIbHCdHbhuiG7mEwvOGbnSDcy1ZgnV',
|
||||||
|
oauth_secret: 'alqjD88o2qtdN9ZwtOfanqqu5Rbp2lhIxbGFukTD',
|
||||||
|
url: "https://master.apis.dev.openstreetmap.org",
|
||||||
|
landing: 'land.html',
|
||||||
|
|
||||||
|
});
|
||||||
|
var openChangesetId = null;
|
||||||
|
var marker = null;
|
||||||
|
|
||||||
|
function getOpenChangesetId() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (openChangesetId !== null) {
|
||||||
|
resolve(openChangesetId);
|
||||||
|
} else {
|
||||||
|
let data = '<osm><changeset>' +
|
||||||
|
'<tag k="comment" v="Defibrillator added via https://aed.openstreetmap.org.pl #aed"/>' +
|
||||||
|
'<tag k="created_by" v="https://aed.openstreetmap.org.pl"/>' +
|
||||||
|
'<tag k="locale" v="pl"/>' +
|
||||||
|
'<tag k="hashtags" v="#aed"/>' +
|
||||||
|
'</changeset></osm>';
|
||||||
|
auth.xhr({
|
||||||
|
method: 'PUT',
|
||||||
|
path: '/api/0.6/changeset/create',
|
||||||
|
content: data,
|
||||||
|
options: {
|
||||||
|
header: {
|
||||||
|
"Content-Type": "text/xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
openChangesetId = res;
|
||||||
|
console.log('Api returned changeset id: ' + res);
|
||||||
|
resolve(res);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNodeUrl(nodeId) {
|
||||||
|
return `${auth.options().url}/node/${nodeId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderModalMessage(newNodeUrl) {
|
||||||
|
return `
|
||||||
|
<p>AED dodany z powodzeniem:
|
||||||
|
<a target="_blank" rel="noopener" href="${newNodeUrl}">${newNodeUrl}</a>
|
||||||
|
</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderModalErrorMessage(message) {
|
||||||
|
return `<p>Wystąpił błąd: ${message}</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderModalNeedLoginMessage() {
|
||||||
|
return `<p>Żeby dodawać obiekty za pomocą długiego dotknięcia/prawego przycisku myszy musisz się zalogować.</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderModalNeedMoreZoomMessage() {
|
||||||
|
return `<p>Żeby dodawać obiekty za pomocą długiego dotknięcia/prawego przycisku myszy musisz bardziej przybliżyć mapę, żeby podana lokalizacja była możliwie dokładna.</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNeedMoreZoomModal() {
|
||||||
|
let modalContent = document.getElementById('modal-content');
|
||||||
|
modalContent.innerHTML = renderModalNeedMoreZoomMessage();
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSuccessModal(newNodeId) {
|
||||||
|
let modalContent = document.getElementById('modal-content');
|
||||||
|
modalContent.innerHTML = renderModalMessage(getNodeUrl(newNodeId));
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showFailureModal(message) {
|
||||||
|
let modalContent = document.getElementById('modal-content');
|
||||||
|
modalContent.innerHTML = renderModalErrorMessage(message);
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
let modal = document.getElementById('modal-div');
|
||||||
|
modal.classList.add('is-clipped');
|
||||||
|
modal.classList.add('is-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
// close modal
|
||||||
|
let modal = document.getElementById('modal-div');
|
||||||
|
modal.classList.remove('is-clipped');
|
||||||
|
modal.classList.remove('is-active');
|
||||||
|
// remove marker and close sidebar too
|
||||||
|
let sidebar = document.getElementById('sidebar-div');
|
||||||
|
if (sidebar) {
|
||||||
|
sidebar.classList.add('is-invisible');
|
||||||
|
if (marker !== null) {
|
||||||
|
marker.remove();
|
||||||
|
marker = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('sidebar not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDefibrillatorToOSM(changesetId, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log('sending request to create node in changeset: ' + changesetId);
|
||||||
|
var xml = `<osm><node changeset="${changesetId}" lat="${data.lat}" lon="${data.lng}">`;
|
||||||
|
xml += `<tag k="emergency" v="defibrillator"/>`;
|
||||||
|
xml += Object.entries(data.tags).map(arr => `<tag k="${arr[0]}" v="${arr[1]}"/>`).join('');
|
||||||
|
xml += `</node></osm>`;
|
||||||
|
console.log('payload: ' + xml);
|
||||||
|
auth.xhr({
|
||||||
|
method: 'PUT',
|
||||||
|
path: '/api/0.6/node/create',
|
||||||
|
content: xml,
|
||||||
|
options: {
|
||||||
|
header: {
|
||||||
|
"Content-Type": "text/xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, (err, res) => {
|
||||||
|
if (err) reject(err);
|
||||||
|
else {
|
||||||
|
resolve(res);
|
||||||
|
console.log(`response: ${res}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function startSaveButtonAnimation() {
|
||||||
|
let saveButton = document.getElementById('sidebar-save-button');
|
||||||
|
saveButton.classList.add('is-loading');
|
||||||
|
saveButton.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopSaveButtonAnimation() {
|
||||||
|
let saveButton = document.getElementById('sidebar-save-button');
|
||||||
|
saveButton.classList.remove('is-loading');
|
||||||
|
saveButton.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveNode(data) {
|
||||||
|
startSaveButtonAnimation();
|
||||||
|
getOpenChangesetId()
|
||||||
|
.then(changesetId => {
|
||||||
|
return addDefibrillatorToOSM(changesetId, data);
|
||||||
|
})
|
||||||
|
.then(newNodeId => {
|
||||||
|
stopSaveButtonAnimation();
|
||||||
|
showSuccessModal(newNodeId);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
stopSaveButtonAnimation();
|
||||||
|
console.log(err);
|
||||||
|
showFailureModal(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('addNode').onclick = function () {
|
||||||
|
// add marker
|
||||||
|
const mapCenter = map.getCenter();
|
||||||
|
const initialCoordinates = [mapCenter.lng, mapCenter.lat];
|
||||||
|
if (marker !== null) marker.remove();
|
||||||
|
marker = new maplibregl.Marker({
|
||||||
|
draggable: true,
|
||||||
|
color: "#e81224",
|
||||||
|
})
|
||||||
|
.setLngLat(initialCoordinates);
|
||||||
|
marker.addTo(map);
|
||||||
|
// show sidebar
|
||||||
|
let properties = {
|
||||||
|
action: "addNode",
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
showSidebar(properties);
|
||||||
|
};
|
||||||
|
|
||||||
|
map.on('contextmenu', function(e) {
|
||||||
|
// only trigger when logged in
|
||||||
|
if (auth.authenticated()) {
|
||||||
|
if (map.getZoom() < 15) {
|
||||||
|
showNeedMoreZoomModal();
|
||||||
|
} else {
|
||||||
|
// add marker
|
||||||
|
const clickLocation = e.lngLat;
|
||||||
|
const initialCoordinates = [clickLocation.lng, clickLocation.lat];
|
||||||
|
if (marker !== null) marker.remove();
|
||||||
|
marker = new maplibregl.Marker({
|
||||||
|
draggable: true,
|
||||||
|
color: "#e81224",
|
||||||
|
})
|
||||||
|
.setLngLat(initialCoordinates);
|
||||||
|
marker.addTo(map);
|
||||||
|
// show sidebar
|
||||||
|
let properties = {
|
||||||
|
action: "addNode",
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
showSidebar(properties);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('You need to be logged in to add new nodes.');
|
||||||
|
showNeedLoginModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateNavbarLoggedUserState() {
|
||||||
|
let navbar = document.getElementById('navbar-logged');
|
||||||
|
|
||||||
|
if (!auth.authenticated()) {
|
||||||
|
navbar.classList.add('is-hidden');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
navbar.classList.remove('is-hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('logout').onclick = function () {
|
||||||
|
auth.logout();
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
|
||||||
|
function authenticateAction() {
|
||||||
|
if (!auth.bringPopupWindowToFront()) {
|
||||||
|
auth.authenticate(function() {
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLoginButton() {
|
||||||
|
return '<button class="button is-success has-text-weight-light is-outlined" id="authenticate" onclick="authenticateAction()">Zaloguj kontem OSM</button>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderUserLoggedIn(username) {
|
||||||
|
return `<svg class="icon mr-1" style="width:24px;height:24px" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" />
|
||||||
|
</svg> ${username}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderErrorLoggingIn() {
|
||||||
|
return '<p>Problem podczas logowania. Spróbuj wyczyścić cache (ctrl+f5).</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAddNodeButtonState() {
|
||||||
|
let addNodeButton = document.getElementById('addNode');
|
||||||
|
addNodeButton.disabled = false;
|
||||||
|
addNodeButton.title = "";
|
||||||
|
if (!auth.authenticated()) {
|
||||||
|
addNodeButton.disabled = true;
|
||||||
|
addNodeButton.title = "Zaloguj się aby móc dodawać obiekty";
|
||||||
|
}
|
||||||
|
if (map.getZoom() < 15) {
|
||||||
|
addNodeButton.disabled = true;
|
||||||
|
addNodeButton.title = "Zbyt duże oddalenie mapy";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map.on('zoomend', updateAddNodeButtonState);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
if (auth.authenticated()) {
|
||||||
|
auth.xhr({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/api/0.6/user/details'
|
||||||
|
}, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
updateAddNodeButtonState();
|
||||||
|
showFailureModal(err);
|
||||||
|
} else {
|
||||||
|
const u = res.getElementsByTagName('user')[0];
|
||||||
|
const user_name = u.getAttribute('display_name');
|
||||||
|
const user_with_id = `${user_name}`;
|
||||||
|
document.getElementById('span-login').innerHTML = '';
|
||||||
|
document.getElementById('span-login').classList.add('is-hidden');
|
||||||
|
document.getElementById('navbar-username').innerHTML = renderUserLoggedIn(user_with_id);
|
||||||
|
updateAddNodeButtonState();
|
||||||
|
updateNavbarLoggedUserState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.getElementById('span-login').innerHTML = renderLoginButton();
|
||||||
|
updateAddNodeButtonState();
|
||||||
|
updateNavbarLoggedUserState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,358 @@
|
||||||
|
const sidebarDivId = 'sidebar-div';
|
||||||
|
const sidebarHeaderId = 'sidebar-header';
|
||||||
|
const sidebarCaptionId = 'sidebar-caption';
|
||||||
|
const sidebarContentDivId = 'sidebar-content-div';
|
||||||
|
const sidebarFooterButtonLeftId = 'sidebar-footer-button-left';
|
||||||
|
const sidebarButtonCloseIds = ['sidebar-button-close-touch', 'sidebar-button-close-desktop'];
|
||||||
|
const formPhoneFieldId = 'form-phone';
|
||||||
|
const formAccessFieldId = 'form-access';
|
||||||
|
const formLocationFieldId = 'form-location';
|
||||||
|
const formLocationEnFieldId = 'form-location-en';
|
||||||
|
const formIndoorFieldId = 'form-indoor';
|
||||||
|
const formEmergencyPhoneFieldId = 'form-emergency-phone';
|
||||||
|
|
||||||
|
let sidebarHeader = document.getElementById(sidebarHeaderId);
|
||||||
|
let sidebarCaption = document.getElementById(sidebarCaptionId);
|
||||||
|
let sidebarContent = document.getElementById(sidebarContentDivId);
|
||||||
|
let sidebarLink = document.getElementById(sidebarFooterButtonLeftId);
|
||||||
|
|
||||||
|
const accessToColourMapping = {
|
||||||
|
'yes': 'has-background-green',
|
||||||
|
'no': 'has-background-grey',
|
||||||
|
'private': 'has-background-grey',
|
||||||
|
'permissive': 'has-background-link-dark',
|
||||||
|
'default': 'has-background-grey',
|
||||||
|
};
|
||||||
|
|
||||||
|
const accessToDescriptionMapping = {
|
||||||
|
'yes': 'ogólnodostępny',
|
||||||
|
'no': 'prywatny',
|
||||||
|
'private': 'prywatny',
|
||||||
|
'permissive': 'o ograniczonym dostępie',
|
||||||
|
'default': '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const indoorMapping = {
|
||||||
|
'yes': 'tak',
|
||||||
|
'no': 'nie',
|
||||||
|
'default': '',
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
function defineColor(access) {
|
||||||
|
return accessToColourMapping[access] || accessToColourMapping['default'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineAccessDescription(access) {
|
||||||
|
return accessToDescriptionMapping[access] || accessToDescriptionMapping['default'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineIndoor(indoor) {
|
||||||
|
return indoorMapping[indoor] || indoorMapping['default'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOsmEditLink(id) {
|
||||||
|
return `https://www.openstreetmap.org/edit?editor=id&node=${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOpeningHours(openingHours) {
|
||||||
|
|
||||||
|
if (openingHours) {
|
||||||
|
if (openingHours.includes('24/7')) {
|
||||||
|
return 'całodobowo';
|
||||||
|
} else {
|
||||||
|
let hoursPrettified;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let hours = openingHours.toString();
|
||||||
|
let oh = new opening_hours(hours, undefined, 2);
|
||||||
|
isOpen = oh.getState();
|
||||||
|
hoursPrettified = oh.prettifyValue({
|
||||||
|
conf: {
|
||||||
|
locale: 'pl'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Error when parsing opening hours');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hoursPrettified;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCurrentlyOpen(openingHours) {
|
||||||
|
if (openingHours) {
|
||||||
|
if (openingHours.includes('24/7')) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
let hours = openingHours.toString();
|
||||||
|
let oh = new opening_hours(hours, undefined, 2);
|
||||||
|
isOpen = oh.getState();
|
||||||
|
return isOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCurrentlyOpenStatus(openingHours) {
|
||||||
|
if (isCurrentlyOpen(openingHours)) {
|
||||||
|
return '<sup><span class="tag is-success is-light">Dostępny</span></sup>';
|
||||||
|
} else {
|
||||||
|
return '<sup><span class="tag is-danger is-light">Niedostępny</span></sup>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderIfIndoor(indoor) {
|
||||||
|
let beginning = '<p class="has-text-weight-light">Wewnątrz budynku?: <span class="add-new has-text-weight-medium">';
|
||||||
|
let middle = defineIndoor(indoor) || '<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>';
|
||||||
|
let end = '</span></p>';
|
||||||
|
return beginning + middle + end;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLocation(properties) {
|
||||||
|
let beginning = '<p class="has-text-weight-light">Dokładna lokalizacja: <span class="add-new has-text-weight-medium">';
|
||||||
|
let middle = properties['defibrillator:location:pl'] || properties['defibrillator:location'] || '<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>';
|
||||||
|
let end = '</span></p>';
|
||||||
|
return beginning + middle + end;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDescription(properties) {
|
||||||
|
let beginning = '<p class="has-text-weight-light">Opis: <span class="add-new has-text-weight-medium">';
|
||||||
|
let middle = properties['description:pl'] || properties.description || '<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>';
|
||||||
|
let end = '</span></p>';
|
||||||
|
return beginning + middle + end;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderContactNumber(phone) {
|
||||||
|
let beginning = '<p class="has-text-weight-light">Numer kontaktowy: <span class="add-new has-text-weight-medium">';
|
||||||
|
let middle = phone || '<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji</span>';
|
||||||
|
let end = '</span></p>';
|
||||||
|
return beginning + middle + end;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAccessibleTime(openingHours) {
|
||||||
|
if (openingHours) {
|
||||||
|
let beginning = '<p class="has-text-weight-light">Dostępny w godzinach: <span class="add-new has-text-weight-medium">';
|
||||||
|
let middle = parseOpeningHours(openingHours) || '<span class="has-text-grey-light is-italic has-text-weight-light">brak informacji </span>';
|
||||||
|
let end = (renderCurrentlyOpenStatus(openingHours) || '') + '</span></p>';
|
||||||
|
return beginning + middle + end;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNotes(properties) {
|
||||||
|
if (properties.note || properties['note:pl']) {
|
||||||
|
let beginning = '<p class="has-text-weight-light">Uwagi: <span class="add-new has-text-weight-medium">';
|
||||||
|
let middle = properties['note:pl'] || properties.note || 'brak uwag';
|
||||||
|
let end = '</span></p>';
|
||||||
|
return beginning + middle + end;
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSidebarContent(properties) {
|
||||||
|
let content = '';
|
||||||
|
content += renderIfIndoor(properties.indoor);
|
||||||
|
content += renderLocation(properties);
|
||||||
|
content += renderAccessibleTime(properties.opening_hours);
|
||||||
|
content += renderDescription(properties);
|
||||||
|
content += renderContactNumber(properties.phone);
|
||||||
|
content += renderNotes(properties);
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSidebarForm() {
|
||||||
|
let content = `
|
||||||
|
<form>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label has-text-weight-semibold">Rodzaj dostępu</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select is-success">
|
||||||
|
<select id="${formAccessFieldId}" tag="access">
|
||||||
|
<option val="">Wybierz z listy</option>
|
||||||
|
<option val="yes">Publicznie dostępny</option>
|
||||||
|
<option val="private">Dostępny za zgodą właściciela</option>
|
||||||
|
<option val="customers">Dostępny tylko w godzinach pracy</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label has-text-weight-semibold">Czy wewnątrz budynku?</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select is-success">
|
||||||
|
<select id="${formIndoorFieldId}" tag="location">
|
||||||
|
<option val="">Wybierz z listy</option>
|
||||||
|
<option val="outdoor">Na zewnątrz</option>
|
||||||
|
<option val="indoor">W budynku</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label has-text-weight-semibold">Opis lokalizacji defibrylatora</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea id="${formLocationFieldId}" tag="defibrillator:location" class="textarea is-success" rows="2"
|
||||||
|
placeholder="Na przykład: Na ścianie przy wejściu"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label has-text-weight-semibold">Telefon kontaktowy operatora</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="${formPhoneFieldId}" tag="phone" class="input is-success" type="text" placeholder="+48 123 456 789"
|
||||||
|
pattern="^[+][0-9]{2}[ ]?((?:[0-9]{9})|(?:[0-9]{3} [0-9]{3} [0-9]{3})|(?:[0-9]{2} [0-9]{3} [0-9]{2} [0-9]{2}))$">
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-weight-light">Pole opcjonalne</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label has-text-weight-semibold">Numer ratunkowy danego obszaru</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="${formEmergencyPhoneFieldId}" tag="emergency:phone" class="input is-success" type="text" placeholder="+48 123 456 789"
|
||||||
|
pattern="^[+][0-9]{2}[ ]?((?:[0-9]{9})|(?:[0-9]{3} [0-9]{3} [0-9]{3})|(?:[0-9]{2} [0-9]{3} [0-9]{2} [0-9]{2}))$">
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-weight-light">Pole opcjonalne. Wypełnij tylko, jeżeli jest inny niż 112/999.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label has-text-weight-semibold">Opis lokalizacji defibrylatora po angielsku</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea id="${formLocationEnFieldId}" tag="defibrillator:location:en" class="textarea is-success" rows="2"
|
||||||
|
placeholder="For example: On the wall near entrance"></textarea>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-weight-light">Pole opcjonalne</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
`;
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEditButton(osm_id) {
|
||||||
|
return `<a href="${getOsmEditLink(osm_id)}" target="_blank" rel="noopener"
|
||||||
|
class="has-background-success-light card-footer-item has-text-centered is-size-7 has-text-weight-semibold"
|
||||||
|
>Dodaj brakujące informacje w OSM</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSaveButton() {
|
||||||
|
return `<button id="sidebar-save-button" class="button is-success is-fullwidth" onclick="saveNode(prepareNodeData())">Dodaj AED</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------
|
||||||
|
function prepareNodeData() {
|
||||||
|
let data = {};
|
||||||
|
// get coordinates
|
||||||
|
let markerPosition = marker.getLngLat();
|
||||||
|
data.lng = markerPosition.lng;
|
||||||
|
data.lat = markerPosition.lat;
|
||||||
|
// get additional tags
|
||||||
|
data.tags = {};
|
||||||
|
let formPhoneField = document.getElementById(formPhoneFieldId);
|
||||||
|
let formLocationField = document.getElementById(formLocationFieldId);
|
||||||
|
let formEmergencyPhoneField = document.getElementById(formEmergencyPhoneFieldId);
|
||||||
|
let formLocationEnField = document.getElementById(formLocationEnFieldId);
|
||||||
|
let formAccessField = document.getElementById(formAccessFieldId);
|
||||||
|
let formIndoorField = document.getElementById(formIndoorFieldId);
|
||||||
|
if (formIndoorField.selectedOptions[0].getAttribute('val'))
|
||||||
|
data.tags[formIndoorField.getAttribute('tag')] = formIndoorField.selectedOptions[0].getAttribute('val');
|
||||||
|
if (formPhoneField.value) data.tags[formPhoneField.getAttribute('tag')] = formPhoneField.value;
|
||||||
|
if (formLocationField.value) data.tags[formLocationField.getAttribute('tag')] = formLocationField.value;
|
||||||
|
if (formEmergencyPhoneField.value) data.tags[formEmergencyPhoneField.getAttribute('tag')] = formEmergencyPhoneField.value;
|
||||||
|
if (formLocationEnField.value) data.tags[formLocationEnField.getAttribute('tag')] = formLocationEnField.value;
|
||||||
|
if (formAccessField.selectedOptions[0].getAttribute('val'))
|
||||||
|
data.tags[formAccessField.getAttribute('tag')] = formAccessField.selectedOptions[0].getAttribute('val');
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMarkerIfExists() {
|
||||||
|
if (marker !== null) {
|
||||||
|
marker.remove();
|
||||||
|
marker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSidebar(properties) {
|
||||||
|
let sidebar = document.getElementById(sidebarDivId);
|
||||||
|
if (sidebar) {
|
||||||
|
sidebar.classList.remove('is-invisible');
|
||||||
|
if (properties.action === "showDetails") {
|
||||||
|
prepareSidebarShowingObjectInfo(properties.data);
|
||||||
|
removeMarkerIfExists();
|
||||||
|
} else if (properties.action === "addNode") {
|
||||||
|
prepareSidebarAddingNode(properties.data);
|
||||||
|
} else
|
||||||
|
console.log(`Unknown action: ${properties.action}.`);
|
||||||
|
} else
|
||||||
|
console.log('Sidebar not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSidebar() {
|
||||||
|
let sidebar = document.getElementById(sidebarDivId);
|
||||||
|
if (sidebar) {
|
||||||
|
sidebar.classList.add('is-invisible');
|
||||||
|
removeMarkerIfExists();
|
||||||
|
} else {
|
||||||
|
console.log('Sidebar not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareSidebarShowingObjectInfo(properties) {
|
||||||
|
sidebarHeader.classList = [];
|
||||||
|
sidebarHeader.classList.add(defineColor(properties.access));
|
||||||
|
sidebarCaption.innerHTML = `defibrylator AED ${defineAccessDescription(properties.access)}`;
|
||||||
|
|
||||||
|
sidebarContent.innerHTML = renderSidebarContent(properties);
|
||||||
|
|
||||||
|
sidebarLink.innerHTML = renderEditButton(properties.osm_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareSidebarAddingNode(properties) {
|
||||||
|
sidebarHeader.classList = [];
|
||||||
|
sidebarHeader.classList.add(defineColor('default'));
|
||||||
|
sidebarCaption.innerHTML = 'Dodaj defibrylator';
|
||||||
|
|
||||||
|
sidebarContent.innerHTML = renderSidebarForm();
|
||||||
|
|
||||||
|
sidebarLink.innerHTML = renderSaveButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// Bulma controls
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
|
// Get all "navbar-burger" elements
|
||||||
|
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||||
|
|
||||||
|
// Check if there are any navbar burgers
|
||||||
|
if ($navbarBurgers.length > 0) {
|
||||||
|
|
||||||
|
// Add a click event on each of them
|
||||||
|
$navbarBurgers.forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
|
||||||
|
// Get the target from the "data-target" attribute
|
||||||
|
const target = el.dataset.target;
|
||||||
|
const $target = document.getElementById(target);
|
||||||
|
|
||||||
|
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||||
|
el.classList.toggle('is-active');
|
||||||
|
$target.classList.toggle('is-active');
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// button listeners
|
||||||
|
sidebarButtonCloseIds.forEach(id => {
|
||||||
|
document.getElementById(id).addEventListener('click', hideSidebar);
|
||||||
|
});
|
Ładowanie…
Reference in New Issue