Compare commits
21 commits
main
...
frontend_s
Author | SHA1 | Date | |
---|---|---|---|
Gandalf | fa788644be | ||
Gandalf | 0b52b51d45 | ||
Gandalf | 6635036974 | ||
Gandalf | b7f5b6b4ee | ||
Gandalf | d25dab2b58 | ||
Gandalf | be29ec7527 | ||
Gandalf | 39e055592a | ||
Gandalf | 56f4940d63 | ||
Gandalf | c521e1cb1a | ||
Gandalf | 09e9d27269 | ||
Gandalf | 29ce681599 | ||
Gandalf | b1bfab6bbc | ||
Gandalf | e815686509 | ||
Gandalf | 88c7a49235 | ||
Gandalf | bf9a3961a8 | ||
Gandalf | 56b3ac2550 | ||
Gandalf | 2ebf8ab55e | ||
Gandalf | 20a6d1e114 | ||
Gandalf | 1a5003347c | ||
Gandalf | 89f8d1a23c | ||
Gandalf | 9dc56c9c7f |
3
.gitignore
vendored
|
@ -1 +1,4 @@
|
|||
/target
|
||||
/leaflet
|
||||
|
||||
.*.sw[po]
|
||||
|
|
1795
Cargo.lock
generated
Normal file
27
Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "HambiMap"
|
||||
version = "0.1.0"
|
||||
authors = ["Gandalf <gandalfderbunte@riseup.net>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2", features = ["macros"] }
|
||||
warp = "0.2"
|
||||
sqlx =
|
||||
ormx =
|
||||
serde = {version = "1.0", features = ["derive"] }
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
# tokio is our async runtime, which we need to execute futures
|
||||
# warp is our web framework
|
||||
# mobc / mobc-postgres represents an asynchronous connection pool for our database connections
|
||||
# serde is for serializing and deserializing objects (e.g., to/from JSON)
|
||||
# thiserror is a utility library we’ll use for error handling
|
||||
# chrono represents time and date utilities
|
||||
|
||||
# Questions: async vs sync
|
||||
# which database to use?
|
||||
# will probably substitute mobc* for sqlx and keep working with tokio.
|
|
@ -1,3 +1,8 @@
|
|||
# HambiMap
|
||||
|
||||
Code for a Website that collects stories and photos from 11 years of Hambach Forest occupation, with the goal of producing info tables to put up in the forest
|
||||
Code for a Website that collects stories and photos from 11 years of Hambach Forest occupation, with the goal of producing info tables to put up in the forest
|
||||
|
||||
## Branch tutorial1
|
||||
After the first tutorial (branch 'tutorial') was incomplete and unfunctional, let's try [this one](https://blog.logrocket.com/async-crud-web-service-rust-warp/#handling-errors-with-warp) just for the backend.
|
||||
|
||||
|
||||
|
|
20
sketch/HambiMap
Normal file
|
@ -0,0 +1,20 @@
|
|||
server {
|
||||
listen 8080;
|
||||
server_name HambiMapAlt;
|
||||
location / {
|
||||
root /home/bernhardt/Entwicklung/HambiMap/sketch/alt;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 1314;
|
||||
server_name HambiMap;
|
||||
root /home/bernhardt/Entwicklung/HambiMap;
|
||||
location /leaflet/ {
|
||||
}
|
||||
location /fontawesome/ {
|
||||
}
|
||||
location / {
|
||||
root /home/bernhardt/Entwicklung/HambiMap/sketch/html;
|
||||
}
|
||||
}
|
237
sketch/alt/app.js
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Déclaration de la map
|
||||
var map = L.map('map').setView([50.880301, 6.560531], 13,);
|
||||
|
||||
//Déclaration du calque
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 20, }).addTo(map);
|
||||
|
||||
// Création de la classe threeIcon
|
||||
// TODO: Adjust all the values
|
||||
var threeIcon = L.Icon.extend({
|
||||
options: {
|
||||
iconSize: [53, 75],
|
||||
iconAnchor: [53 / 2, 75],
|
||||
shadowAnchor: [4, 62],
|
||||
popupAnchor: [-3, -76]
|
||||
}
|
||||
});
|
||||
|
||||
// Création de l'icon BAUM
|
||||
var baumIcon = new threeIcon({ iconUrl: 'baum.png' });
|
||||
// Création de l'icon BOUDOIR
|
||||
var boudoirIcon = new threeIcon({ iconUrl: 'boudoir.png' });
|
||||
|
||||
//Création d'un objet cluster
|
||||
//var cluster1 = L.markerClusterGroup();
|
||||
//
|
||||
//Import des données JSON du fichier historiqueMarqueurs.json (fonctionne si et seulement si le serveur est online)
|
||||
// (à l'avenir mettre en place une petite base de données type Firebase)
|
||||
fetch('historiqueMarqueurs.json')
|
||||
.then(response => response.json())
|
||||
// Ajout des marqueurs du fichier JSON grâce à la fonction addMarkersFromJson
|
||||
.then(json => addMarkersFromJson(json))
|
||||
//Erreur
|
||||
.catch(error => console.error('Could not load markers', error));
|
||||
|
||||
|
||||
// Fonction ajout des marqueurs depuis le fichier JSON
|
||||
function addMarkersFromJson(json) {
|
||||
for (var i = 0; i < json.length; i++) {
|
||||
var markerData = json[i];
|
||||
var markerIcon = markerData.icon === 'baum' ? baumIcon : boudoirIcon;
|
||||
var marker = L.marker([markerData.latitude, markerData.longitude], {icon : markerIcon}).addTo(map);
|
||||
marker.bindPopup('<h2>' + markerData.title + '</h2>' + '<p><b>' + markerData.date + '</b></p>' + '<p>' + markerData.desc + '</p>');
|
||||
//cluster1.addLayer(marker)
|
||||
//map.addLayer(cluster1);
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction ajout marqueur
|
||||
function addMarker(e) {
|
||||
alert("coucou");
|
||||
|
||||
// Récupérer la valeur du select-icon pour ajouter la bonne icône
|
||||
var select = document.getElementById('three-select').value;
|
||||
|
||||
// Récupérer latitude et longitude
|
||||
var latitude = e.latlng.lat;
|
||||
var longitude = e.latlng.lng;
|
||||
|
||||
// Création d'un formulaire lors de l'ajout d'un marqueur
|
||||
var form = document.createElement('form');
|
||||
form.id = "myForm"
|
||||
|
||||
// Format du formulaire
|
||||
form.innerHTML =
|
||||
'<b><label for="marker-title">Name of the tree :</label>' +
|
||||
'<br><input style="bottom: 10px" type="text" id="marker-title" name="marker-title" required>' +
|
||||
'<div></div>' +
|
||||
|
||||
|
||||
'<br><b><label for="marker-date">Date of Beginning</label>' +
|
||||
'<br><textarea id="marker-date" name="marker-date" required></textarea>' +
|
||||
'<div></div>' +
|
||||
'<br><b><label for="marker-date2">Date of End : (put xxx if none) </label>' +
|
||||
'<br><textarea id="marker-date2" name="marker-date2" required></textarea>' +
|
||||
'<div></div>' +
|
||||
|
||||
'<br><b><label for="marker-desc">Description :</label>' +
|
||||
'<br><textarea id="marker-desc" name="marker-desc" required></textarea required>' +
|
||||
'<div></div>' +
|
||||
|
||||
'<br><button style="background: none" type="submit" required>Add on map</button>';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Sélection de la bonne icône en fonction du select
|
||||
switch (select) {
|
||||
case 'boudoir':
|
||||
//Ajout de l'icône à la carte
|
||||
var marker = L.marker([e.latlng.lat, e.latlng.lng], { icon: boudoirIcon }).addTo(map).bindPopup(form);
|
||||
//Ajout de du marqueur au cluster
|
||||
break;
|
||||
|
||||
case 'baum':
|
||||
//Ajout de l'icône à la carte
|
||||
var marker = L.marker([e.latlng.lat, e.latlng.lng], { icon: baumIcon }).addTo(map).bindPopup(form);
|
||||
//Ajout de du marqueur au cluster
|
||||
break;
|
||||
case 'default':
|
||||
break;
|
||||
}
|
||||
|
||||
// Ajout de l'action pour le formulaire
|
||||
form.action = "ecrire_json.php";
|
||||
|
||||
// Lors du submit
|
||||
form.addEventListener('submit', function (event) {
|
||||
// Empêche le rechargement de la page
|
||||
event.preventDefault();
|
||||
|
||||
//Récupérer les valeurs des champs précedémment remplis
|
||||
var title = document.getElementById('marker-title').value;
|
||||
var date = document.getElementById('marker-date').value;
|
||||
var date2 = document.getElementById ('marker-date2').value;
|
||||
var desc = document.getElementById('marker-desc').value;
|
||||
|
||||
//Actualisation de la pop-up avec les informations
|
||||
marker.setPopupContent('<h2>' + title + '</h2>' + '<p><b>' + date + '</b></p>' + '<p><b>' + date2 + '</b></p>' + '<p>' + desc + '</p>');
|
||||
|
||||
|
||||
|
||||
// Création d'un objet contenant les données à envoyer
|
||||
var data = {
|
||||
"lat": latitude,
|
||||
"lng": longitude,
|
||||
"category": select,
|
||||
"title": title,
|
||||
"date": date,
|
||||
"date2": date2,
|
||||
"description": desc
|
||||
};
|
||||
|
||||
// Conversion des données en chaîne JSON
|
||||
var jsonData = JSON.stringify(data);
|
||||
|
||||
// Envoi des données au serveur via une requête AJAX
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "enregistrer_donnees.php", true);
|
||||
xhr.setRequestHeader("Accept", "application/json");
|
||||
xhr.send(jsonData);
|
||||
});
|
||||
|
||||
//map.addLayer(cluster1);
|
||||
|
||||
// Conversion des données en chaîne JSON
|
||||
var jsonData = JSON.stringify(data);
|
||||
|
||||
// Envoi des données au serveur via une requête AJAX
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "enregistrer_donnees.php", true);
|
||||
xhr.setRequestHeader("Accept", "application/json");
|
||||
xhr.send(jsonData);
|
||||
|
||||
}
|
||||
|
||||
map.on("click", addMarker);
|
||||
|
||||
|
||||
|
||||
|
||||
// Récupération des données entrées par l'utilisateur
|
||||
//var data = {
|
||||
// "latitude": document.getElementById("latitude").value,
|
||||
// "longitude": document.getElementById("longitude").value,
|
||||
// "icon": document.getElementById("icon").value,
|
||||
// "title": document.getElementById("title").value,
|
||||
// "date2": document.getElementById("date2").value,
|
||||
// "date": document.getElementById("date").value,
|
||||
// "desc": document.getElementById("desc").value
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//// Récupération des données du formulaire
|
||||
//var title = document.getElementById('marker-title').value;
|
||||
//var date = document.getElementById('marker-date').value;
|
||||
//var date2 = document.getElementById('marker-date2').value;
|
||||
//var desc = document.getElementById('marker-desc').value;
|
||||
//
|
||||
//
|
||||
//
|
||||
/////test openai
|
||||
//
|
||||
//document.getElementById("myForm").addEventListener("submit", function(event) {
|
||||
// event.preventDefault();
|
||||
// sendData();
|
||||
// });
|
||||
//
|
||||
//
|
||||
// function sendData() {
|
||||
// const form = document.getElementById("myForm");
|
||||
// const formData = new FormData(form);
|
||||
//
|
||||
// fetch("ecrire_json.php", {
|
||||
// method: "POST",
|
||||
// body: formData
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .then(data => console.log(data))
|
||||
// .catch(error => console.error(error));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Pour ajouter un commentaire, ne fonctionne pas
|
||||
// document.getElementById('addComment').addEventListener('click', function (e){
|
||||
// // Empêche le rechargement de la page
|
||||
// event.preventDefault();
|
||||
|
||||
// // Création d'un formulaire lors de l'ajout d'un commentaire
|
||||
// var form2 = document.createElement('form');
|
||||
// // Format du formulaire
|
||||
// form2.innerHTML =
|
||||
// '<br><b><label for="marker-date2">Date :</label>' +
|
||||
// '<br><textarea id="marker-date2" name="marker-desc"></textarea>' +
|
||||
// '<div></div>' +
|
||||
// '<br><b><label for="marker-desc2">Description :</label>' +
|
||||
// '<br><textarea id="marker-desc2" name="marker-desc"></textarea>' +
|
||||
// '<div></div>' +
|
||||
// '<br><button style="background: none" type="submit">Ajouter le commentaire</button>';
|
||||
|
||||
// form2.addEventListener('submit', function (event) {
|
||||
// // Empêche le rechargement de la page
|
||||
// event.preventDefault();
|
||||
|
||||
// //Récupérer les valeurs des champs précedémment remplis
|
||||
// var date2 = document.getElementById('marker-date2').value;
|
||||
// var desc2 = document.getElementById('marker-desc2').value;
|
||||
|
||||
// marker.setPopupContent('<h2>' + title + '</h2>' + '<p><b>' + date + '</b></p>' + '<p>' + desc + '</p>' + '<p><b>' + date2 + '</b></p>' + '<p>' + desc2 + '</p>' + '<button id="addComment">Ajouter un témoignage</button>');
|
||||
// });
|
||||
// });
|
BIN
sketch/alt/baum.png
Normal file
After Width: | Height: | Size: 928 KiB |
BIN
sketch/alt/boudoir.png
Normal file
After Width: | Height: | Size: 832 KiB |
1
sketch/alt/historiqueMarqueurs.json
Normal file
|
@ -0,0 +1 @@
|
|||
[{"latitude":"50.87309052784303","longitude":"6.544160842895509","icon":"baum","title":"Baum","date2":"test","date":"29\/08\/2012","desc":"opokooii"},{"latitude":"50.87309052784303","longitude":"6.544160842895509","icon":"boudoir","title":"Baum","date2":"test","date":"29\/07\/2012","desc":"opokooii"},{"latitude":"50.89896224098888","longitude":"6.559009552001954","icon":"boudoir","title":"Baum","date2":"test","date":"29\/07\/2012","desc":"niiiikeooii"},{"marqueurs":[]},null,null,null,null,null,null,{"lat":50.88614189051132,"lng":6.516437530517579,"category":"baum","title":"jjj","date":"jjj","date2":"jj","description":"jjj"},null,null,null,null,null,{"lat":50.86566965874546,"lng":6.57634735107422,"category":"baum","title":"okasokc\u0138","date":"kkk","date2":"pok","description":"kkpok\n"},null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,{"lat":50.88592530021674,"lng":6.583213806152345,"category":"boudoir","title":"e","date":"e","date2":"x","description":"x"},null,{"lat":50.8822431111397,"lng":6.467170715332032,"category":"boudoir","title":"a","date":"b","date2":"c","description":"d"}]
|
37
sketch/alt/index.html
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr-FR">
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<!-- Lien vers fiche Leaflet CSS -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css"
|
||||
integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=" crossorigin="" />
|
||||
<!-- Import bibliothèque Leaflet.js -->
|
||||
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"
|
||||
integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=" crossorigin=""></script>
|
||||
<!-- Lien vers doc style CSS -->
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
|
||||
|
||||
|
||||
<!-- Lien vers librairie Cluster (fonctionne si et seulement si le serveur est online) -->
|
||||
<link rel="stylesheet" href="Leaflet.markercluster-1.4.1\dist\MarkerCluster.css"/>
|
||||
<link rel="stylesheet" href="Leaflet.markercluster-1.4.1\dist\MarkerCluster.Default.css"/>
|
||||
<script src="Leaflet.markercluster-1.4.1/dist/leaflet.markercluster.js" crossorigin=""></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
|
||||
<select id="three-select">
|
||||
<option value="default">Veuillez choisir une arbre</option>
|
||||
<option value="baum">Baum</option>
|
||||
<option value="boudoir">Boudoir</option>
|
||||
</select>
|
||||
|
||||
<!-- Lien vers doc JS -->
|
||||
<script type="module"src="app.js"> </script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
9
sketch/alt/style.css
Normal file
|
@ -0,0 +1,9 @@
|
|||
#map {
|
||||
height: 750px;
|
||||
}
|
||||
|
||||
select{
|
||||
position: relative;
|
||||
top: 10px;
|
||||
}
|
||||
|
BIN
sketch/html/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
sketch/html/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 337 KiB |
73
sketch/html/app.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
|
||||
var map = L.map('map').setView([50.880301, 6.560531], 13,);
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { minZoom: 12, maxZoom: 19, attribution: 'Map data: © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>' }).addTo(map);
|
||||
let editButton = L.control.editButton({position: 'topright'});
|
||||
map.addControl(editButton,);
|
||||
|
||||
var barrio_markers = L.layerGroup(); //overlay where all barrio markers will be added
|
||||
var tree_markers = L.layerGroup(); //overlay where all tree(house) markers will be added
|
||||
var test_tree_marker = L.marker([50.880301, 6.560531]).addTo(tree_markers);
|
||||
map.addLayer(tree_markers);
|
||||
|
||||
|
||||
function onMapClick(e){ //for adding a tree(house)
|
||||
if (editing && map.getZoom() > max_barrio_zoom) {
|
||||
sidebar.open('tree');
|
||||
}
|
||||
}
|
||||
function onMapZoom(e){ //for deciding wether to show barrios or treehouses
|
||||
if (map.getZoom() > max_barrio_zoom) {
|
||||
map.addLayer(tree_markers);
|
||||
map.removeLayer(barrio_markers);
|
||||
|
||||
}
|
||||
if (map.getZoom() <= max_barrio_zoom) {
|
||||
map.addLayer(barrio_markers);
|
||||
map.removeLayer(tree_markers);
|
||||
}
|
||||
}
|
||||
function onSelectAreaFeatureEnabled(e){
|
||||
}
|
||||
function onSelectAreaFeatureDisabled(e){
|
||||
}
|
||||
function onMapMouseDown(e){} //probably needed for Barrio creation -> nope.
|
||||
function onMapMouseUp(e){
|
||||
console.log(e);
|
||||
if (editing && (map.getZoom() <= max_barrio_zoom)) {
|
||||
// use selectfeature.getAreaLatLng() to fill in hidden fields of the form
|
||||
// add a new panel
|
||||
let panelContent = {
|
||||
id: 'barrio_form', // UID, used to access the panel
|
||||
tab: '<i class="fa fa-tents"></i>', // content can be passed as HTML string,
|
||||
pane: 'TODO: put form for barrio input here',//someDomNode.innerHTML, // DOM elements can be passed, too
|
||||
title: 'Barrio Form', // an optional pane header
|
||||
};
|
||||
sidebar.addPanel(panelContent);
|
||||
sidebar.open('barrio_form');
|
||||
// TODO disable drawing feature until sidebar panel is removed. In other words: You may only draw if there is no barrio form already open
|
||||
if(document.getElementById('barrio_form'))
|
||||
map.selectAreaFeature.disable();
|
||||
}
|
||||
}
|
||||
function onMapContextMenu(e){}//probably neat for something
|
||||
|
||||
var sidebar = L.control.sidebar({
|
||||
container: 'sidebar', // the DOM container or #ID of a predefined sidebar container that should be used
|
||||
}).addTo(map).open('home');
|
||||
|
||||
map.on('click', onMapClick);
|
||||
map.on('zoomend', onMapZoom);
|
||||
map.on('mousedown', onMapMouseDown);
|
||||
map.on('drawend', onMapMouseUp); // because registering a listener on mouseup or layeradd breaks the SelectFeature plugin
|
||||
map.on('contextMenu', onMapContextMenu);
|
||||
map.on('selectareadisabled', onSelectAreaFeatureDisabled);
|
||||
map.on('selectareaenabled', onSelectAreaFeatureEnabled);
|
||||
sidebar.on('closing', function(e) {
|
||||
if(document.getElementById('barrio_form')) {
|
||||
sidebar.removePanel('barrio_form');
|
||||
// this seems redundant, but I want to check that we removed *the last* of (hopefully, but not guaranteed i guess, only one) barrio_form(s)
|
||||
if(!document.getElementById('barrio_form') && editing && map.getZoom() <= max_barrio_zoom)
|
||||
map.selectAreaFeature.enable();
|
||||
}
|
||||
});
|
||||
|
BIN
sketch/html/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 49 KiB |
0
sketch/html/barrio_form.html
Normal file
4
sketch/html/barrio_view.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
||||
|
4
sketch/html/comment_form.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
||||
|
56
sketch/html/editButton.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
|
||||
L.Control.EditButton = L.Control.extend({
|
||||
title: {
|
||||
inactive_treehouse: 'Add a treehouse in the current map area',
|
||||
inactive_barrio: 'Add a barrio in the current map area',
|
||||
active: 'Disable edit mode to drag the map',
|
||||
inactive: function(){
|
||||
let zoom = map.getZoom();
|
||||
if (zoom <= max_barrio_zoom) return this.inactive_barrio;
|
||||
// if (layer == 'treehouse') // uncomment if you ever introduce another layer or default value
|
||||
return this.inactive_treehouse;
|
||||
}
|
||||
},
|
||||
|
||||
editButton: null,
|
||||
|
||||
toggle_style: function(){
|
||||
if (editing) {
|
||||
this.editButton.className = this.editButton.className.replace("w3-white", "w3-blue");
|
||||
this.editButton.title = this.title.active;
|
||||
} else {
|
||||
this.editButton.className = this.editButton.className.replace("w3-blue", "w3-white");
|
||||
this.editButton.title = this.title.inactive();
|
||||
}
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
let ts = this;
|
||||
this.editButton = L.DomUtil.create('button', 'w3-button w3-white w3-hover-light-blue w3-small leaflet-bar');
|
||||
|
||||
this.editButton.innerHTML = 'edit';
|
||||
this.editButton.title = this.title.inactive();
|
||||
|
||||
this.editButton.onclick = function(){
|
||||
let zoom = map.getZoom();
|
||||
if (editing) {
|
||||
map.selectAreaFeature.disable();
|
||||
editing = false;
|
||||
} else {
|
||||
if(!document.getElementById('barrio_form') && zoom <= max_barrio_zoom) map.selectAreaFeature.enable();
|
||||
editing = true;
|
||||
}
|
||||
ts.toggle_style();
|
||||
};
|
||||
|
||||
return this.editButton;
|
||||
},
|
||||
|
||||
onRemove: function(map) {
|
||||
// Nothing to do here
|
||||
}
|
||||
});
|
||||
|
||||
L.control.editButton = function(opts) {
|
||||
return new L.Control.EditButton(opts);
|
||||
}
|
BIN
sketch/html/favicon-16x16.png
Normal file
After Width: | Height: | Size: 718 B |
BIN
sketch/html/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
sketch/html/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
4
sketch/html/gallery.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
||||
|
2
sketch/html/globals.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
const max_barrio_zoom = 16;
|
||||
let editing = false;
|
4
sketch/html/house_form.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
||||
|
BIN
sketch/html/images/buche_gesund_haus.png
Normal file
After Width: | Height: | Size: 928 KiB |
BIN
sketch/html/images/buche_gesund_kuppel.png
Normal file
After Width: | Height: | Size: 887 KiB |
BIN
sketch/html/images/buche_gesund_portaledge.png
Normal file
After Width: | Height: | Size: 916 KiB |
BIN
sketch/html/images/buche_krank_haus.png
Normal file
After Width: | Height: | Size: 854 KiB |
BIN
sketch/html/images/buche_krank_kuppel.png
Normal file
After Width: | Height: | Size: 827 KiB |
BIN
sketch/html/images/buche_krank_portaledge.png
Normal file
After Width: | Height: | Size: 842 KiB |
BIN
sketch/html/images/buche_tot_haus.png
Normal file
After Width: | Height: | Size: 738 KiB |
BIN
sketch/html/images/buche_tot_kuppel.png
Normal file
After Width: | Height: | Size: 721 KiB |
BIN
sketch/html/images/buche_tot_portaledge.png
Normal file
After Width: | Height: | Size: 725 KiB |
BIN
sketch/html/images/eiche_gesund_haus.png
Normal file
After Width: | Height: | Size: 827 KiB |
BIN
sketch/html/images/eiche_gesund_kuppel.png
Normal file
After Width: | Height: | Size: 785 KiB |
BIN
sketch/html/images/eiche_grün_portaledge.png
Normal file
After Width: | Height: | Size: 791 KiB |
BIN
sketch/html/images/eiche_krank_haus.png
Normal file
After Width: | Height: | Size: 694 KiB |
BIN
sketch/html/images/eiche_krank_kuppel.png
Normal file
After Width: | Height: | Size: 676 KiB |
BIN
sketch/html/images/eiche_krank_portaledge.png
Normal file
After Width: | Height: | Size: 681 KiB |
BIN
sketch/html/images/eiche_tot_haus.png
Normal file
After Width: | Height: | Size: 630 KiB |
BIN
sketch/html/images/eiche_tot_kuppel.png
Normal file
After Width: | Height: | Size: 611 KiB |
BIN
sketch/html/images/eiche_tot_portaledge.png
Normal file
After Width: | Height: | Size: 615 KiB |
BIN
sketch/html/images/fichte_gesund_haus.png
Normal file
After Width: | Height: | Size: 748 KiB |
BIN
sketch/html/images/fichte_gesund_kuppel.png
Normal file
After Width: | Height: | Size: 692 KiB |
BIN
sketch/html/images/fichte_gesund_portaledge.png
Normal file
After Width: | Height: | Size: 695 KiB |
BIN
sketch/html/images/fichte_krank_haus.png
Normal file
After Width: | Height: | Size: 682 KiB |
BIN
sketch/html/images/fichte_krank_kuppel.png
Normal file
After Width: | Height: | Size: 622 KiB |
BIN
sketch/html/images/fichte_krank_portaledge.png
Normal file
After Width: | Height: | Size: 626 KiB |
BIN
sketch/html/images/fichte_tot_haus.png
Normal file
After Width: | Height: | Size: 604 KiB |
BIN
sketch/html/images/fichte_tot_kuppel.png
Normal file
After Width: | Height: | Size: 552 KiB |
BIN
sketch/html/images/fichte_tot_portaledge.png
Normal file
After Width: | Height: | Size: 554 KiB |
1
sketch/html/index.html
Symbolic link
|
@ -0,0 +1 @@
|
|||
main_with_sidebar.html
|
78
sketch/html/main_with_sidebar.html
Normal file
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sketch HambiMap</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="w3.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link href="fontawesome/6.4.0/css/all.css" rel="stylesheet">
|
||||
<!--self-served leaflet-->
|
||||
<link rel="stylesheet" href="leaflet/Leaflet-1.9.4/leaflet.css">
|
||||
<link rel="stylesheet" href="leaflet/leaflet-sidebar-v2/css/leaflet-sidebar.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="sidebar" class="leaflet-sidebar collapsed">
|
||||
|
||||
<!-- nav tabs -->
|
||||
<div class="leaflet-sidebar-tabs">
|
||||
<!-- top aligned tabs -->
|
||||
<ul role="tablist">
|
||||
<li><a href="#home" role="tab"><i class="fa fa-bars active"></i></a></li>
|
||||
<li><a href="#tree" role="tab"><i class="fa fa-tree"></i></a></li>
|
||||
<li><a href="#barrio" role="tab"><i class="fa fa-tents"></i></a></li>
|
||||
</ul>
|
||||
<ul role="tablist">
|
||||
<li><a href="#user" role="tab"><i class="fa fa-user"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- panel content -->
|
||||
<div class="leaflet-sidebar-content">
|
||||
<div class="leaflet-sidebar-pane" id="home">
|
||||
<h1 class="leaflet-sidebar-header">
|
||||
HambiMap Sketch
|
||||
<span class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></span>
|
||||
</h1>
|
||||
|
||||
<p>A map designed to collect histories of Hambi treehouses</p>
|
||||
</div><!--sidebar-pane home-->
|
||||
<div class="leaflet-sidebar-pane" id="tree">
|
||||
<h1 class="leaflet-sidebar-header">
|
||||
Treehouse (form)
|
||||
<span class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></span>
|
||||
</h1>
|
||||
<p>Here you'll see a form to enter a new treehouse or the data about existing ones, depending on mode</p>
|
||||
<iframe src="treehouse_form.html"></iframe>
|
||||
</div><!--sidebar-pane tree_form-->
|
||||
<div class="leaflet-sidebar-pane" id="barrio">
|
||||
<h1 class="leaflet-sidebar-header">
|
||||
Barrio (form)
|
||||
<span class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></span>
|
||||
</h1>
|
||||
|
||||
<p>Here you'll see a form to enter a new barrio or the data about existing ones, depending on mode</p>
|
||||
</div><!--sidebar-pane barrio_form-->
|
||||
<div class="leaflet-sidebar-pane" id="user">
|
||||
<h1 class="leaflet-sidebar-header">
|
||||
Login
|
||||
<span class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></span>
|
||||
</h1>
|
||||
|
||||
<p>here you'll see a login or user registration form, that we need for moderation privileges, and can use to save color/author name settings</p>
|
||||
</div><!--sidebar-pane barrio_form-->
|
||||
</div><!--sidebar-content-->
|
||||
</div><!--sidebar-->
|
||||
|
||||
<div id="map" style="height: 100vh;"></div>
|
||||
|
||||
<script src="leaflet/Leaflet-1.9.4/leaflet.js">
|
||||
<!--integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="-->
|
||||
</script>
|
||||
<script src="leaflet/leaflet-sidebar-v2/js/leaflet-sidebar.js"></script>
|
||||
<script src="leaflet/Leaflet.SelectAreaFeature/src/Leaflet.SelectAreaFeature.js"></script>
|
||||
<script src="globals.js"></script>
|
||||
<script src="editButton.js"></script>
|
||||
<script src="app.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
181
sketch/html/main_with_title.html
Normal file
|
@ -0,0 +1,181 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>Sketch HambiMap</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="w3.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<!--leaflet from CDN
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||
crossorigin=""/>
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>-->
|
||||
<!--self-served leaflet-->
|
||||
<link rel="stylesheet" href="leaflet/Leaflet-1.9.4/leaflet.css">
|
||||
<!--integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="-->
|
||||
<script src="leaflet/Leaflet-1.9.4/leaflet.js">
|
||||
<!--integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="-->
|
||||
</script>
|
||||
<script>
|
||||
var modals=document.getElementsByClassName('w3-modal');
|
||||
|
||||
window.onclick = function(event) {
|
||||
for(var j = 0; j < modals.length; ++j){
|
||||
if (event.target == modals[j]) {
|
||||
modals[j].style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function switch_modal(id){
|
||||
for(var i = 0; i < modals.length; ++i){
|
||||
modals[i].style.display = 'none';
|
||||
}
|
||||
if (id!='none'){
|
||||
document.getElementById(id).style.display='block';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<body>
|
||||
|
||||
<div id="modals">
|
||||
<div id="modal_view_treehouse" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="treehouse_view.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- The Modals -->
|
||||
<div id="modal_view_tree" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="tree_view.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_view_barrio" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="barrio_view.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_form_treehouse" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="treehouse_form.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_form_tree" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="tree_form.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_form_barrio" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="barrio_form.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_form_story" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="story_form.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_form_photo" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="photo_form.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_form_comment" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="comment_form.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_form_house" class="w3-modal">
|
||||
<div class="w3-modal-content">
|
||||
<div class="w3-container">
|
||||
<span onclick="switch_modal('none');"
|
||||
class="w3-button w3-display-topright">×</span>
|
||||
<iframe src="house_form.html"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w3-container" id="site_body">
|
||||
<h2>Stories of Hambi</h2>
|
||||
<!-- Trigger/Open the Modals -->
|
||||
<button onclick="switch_modal('modal_view_treehouse')" class="w3-button">View Treehouse</button>
|
||||
<button onclick="switch_modal('modal_view_barrio')" class="w3-button">View Barrio</button>
|
||||
<button onclick="switch_modal('modal_view_tree')" class="w3-button">View Tree</button>
|
||||
<button onclick="switch_modal('modal_form_treehouse')" class="w3-button">Add Treehouse</button>
|
||||
<button onclick="switch_modal('modal_form_barrio')" class="w3-button">Add Barrio</button>
|
||||
<a href="user_form.html" class="w3-button">Register</a>
|
||||
<a href="login_form.html" class="w3-button">Login</a>
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var map = L.map('map').setView([50.880301, 6.560531], 13,);
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { minZoom: 12, maxZoom: 19, attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' }).addTo(map);
|
||||
|
||||
var barrio_markers = L.layerGroup(); //overlay where all barrio markers will be added
|
||||
var tree_markers = L.layerGroup(); //overlay where all tree(house) markers will be added
|
||||
var test_tree_marker = L.marker([50.880301, 6.560531]).addTo(tree_markers);
|
||||
map.addLayer(tree_markers);
|
||||
|
||||
function onMapClick(e){} //for adding a tree(house)
|
||||
function onMapZoom(e){ //for deciding wether to show barrios or treehouses
|
||||
if (map.getZoom() > 16) {
|
||||
map.addLayer(barrio_markers);
|
||||
map.removeLayer(tree_markers);
|
||||
}
|
||||
if (map.getZoom() <= 16) {
|
||||
map.addLayer(tree_markers);
|
||||
map.removeLayer(barrio_markers);
|
||||
}
|
||||
}
|
||||
function onMapMouseDown(e){} //probably needed for Barrio creation
|
||||
function onMapMouseUp(e){} //^^
|
||||
function onMapContextMenu(e){}//probably neat for something
|
||||
|
||||
map.on('click', onMapClick);
|
||||
map.on('zoom', onMapZoom);
|
||||
map.on('mousedown', onMapMouseDown);
|
||||
map.on('mouseup', onMapMouseUp);
|
||||
map.on('contextMenu', onMapContextMenu);
|
||||
L.DomUtil.toBack(map);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
10
sketch/html/overview.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>Sketch HambiMap</title>
|
||||
<body>
|
||||
<h2>Sketch HambiMap</h2>
|
||||
<a href="main_with_title.html">Sketch with a title area above the map</a>
|
||||
<br/>
|
||||
<a href="main_with_sidebar.html">Sketch where I moved all the stuff from the title area to a sidebar</a>
|
||||
</body>
|
||||
</html>
|
1
sketch/html/photo_form.html
Normal file
|
@ -0,0 +1 @@
|
|||
<!DOCTYPE html>
|
1
sketch/html/site.webmanifest
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
3
sketch/html/story_form.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
9
sketch/html/style.css
Normal file
|
@ -0,0 +1,9 @@
|
|||
#map {
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
select{
|
||||
position: relative;
|
||||
top: 10px;
|
||||
}
|
||||
|
3
sketch/html/tree_view.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
44
sketch/html/treehouse_form.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="w3.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link href="fontawesome/5.3.1/css/all.css" rel="stylesheet">
|
||||
</head>
|
||||
<div id='iconpicker' style='width:200px;'>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const iconpicker = document.getElementById('iconpicker');
|
||||
const icon = [0,0,0];
|
||||
const icons = [['buche','eiche','fichte'],['gesund','krank','tot'],['portaledge','kuppel','haus']];
|
||||
|
||||
iconpicker.innerHTML=`
|
||||
<div class='w3-container w3-row'>
|
||||
<div class='w3-col s2 m2 l2'>
|
||||
<div class='w3-row'><button id='species_left' onclick='rotate(0,-1)'><i class="fa fa-caret-left"></i></button></div>
|
||||
<div class='w3-row'><button id='state_left' onclick='rotate(1,-1)'><i class="fa fa-caret-left"></i></button></div>
|
||||
<div class='w3-row'><button id='plattform_left' onclick='rotate(2,-1)'><i class="fa fa-caret-left"></i></button></div>
|
||||
</div>
|
||||
<div class='w3-col s8 m8 l8'>
|
||||
<img id='icon' src='images/${icons[0][0]}_${icons[1][0]}_${icons[2][0]}.png' width='90%'/>
|
||||
</div>
|
||||
<div class='w3-col s2 m2 l2'>
|
||||
<div class='w3-row'><button id='species_right' onclick='rotate(0,1)'><i class="fa fa-caret-right"></i></button></div>
|
||||
<div class='w3-row'><button id='state_right' onclick='rotate(1,1)'><i class="fa fa-caret-right"></i></button></div>
|
||||
<div class='w3-row'><button id='plattform_right' onclick='rotate(2,1)'><i class="fa fa-caret-right"></i></button></div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
const icon_elem = document.getElementById('icon');
|
||||
|
||||
function rotate(pos,dir){
|
||||
icon[pos] += dir;
|
||||
if (icon[pos]<0) icon[pos]=icons[pos].length-1;
|
||||
if (icon[pos]>=icons[pos].length) icon[pos]=0;
|
||||
const image = 'images/'+icons[0][icon[0]]+'_'+icons[1][icon[1]]+'_'+icons[2][icon[2]]+'.png';
|
||||
icon_elem.src=image;
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
4
sketch/html/treehouse_teaser.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
||||
|
4
sketch/html/treehouse_view.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<h3>Treehouse View</h3>
|
||||
</html>
|
4
sketch/html/user_form.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
</html>
|
||||
|
235
sketch/html/w3.css
Normal file
|
@ -0,0 +1,235 @@
|
|||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
45
src/db.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use crate::{DBCon, DBPool};
|
||||
use mobc::{Pool};
|
||||
use mobc_postgres::{tokio_postgres, PgConnectionManager};
|
||||
use tokio_postgres::{Config, Error, NoTls};
|
||||
use std::fs;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use crate::error::Error::{*};
|
||||
use crate::error;
|
||||
|
||||
type Result<T> = std::result::Result<T, error::Error>;
|
||||
|
||||
const DB_POOL_MAX_OPEN: u64 = 32;
|
||||
const DB_POOL_MAX_IDLE: u64 = 8;
|
||||
const DB_POOL_TIMEOUT_SECONDS: u64 = 15;
|
||||
const INIT_SQL: &str = "./db.sql";
|
||||
|
||||
pub async fn init_db(db_pool: &DBPool) -> Result<()> {
|
||||
let init_file = fs::read_to_string(INIT_SQL)?;
|
||||
let con = get_db_con(db_pool).await?;
|
||||
con.batch_execute(init_file.as_str())
|
||||
.await
|
||||
.map_err(DBInitError)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_db_con(db_pool: &DBPool) -> Result<DBCon> {
|
||||
db_pool.get().await.map_err(DBPoolError)
|
||||
}
|
||||
|
||||
pub fn create_pool() -> std::result::Result<DBPool, mobc::Error<Error>> {
|
||||
let config = Config::from_str("postgres://postgres@127.0.0.1:7878/postgres")?;
|
||||
|
||||
let manager = PgConnectionManager::new(config, NoTls);
|
||||
Ok(Pool::builder()
|
||||
.max_open(DB_POOL_MAX_OPEN)
|
||||
.max_idle(DB_POOL_MAX_IDLE)
|
||||
.get_timeout(Some(Duration::from_secs(DB_POOL_TIMEOUT_SECONDS)))
|
||||
.build(manager))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
64
src/error.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use mobc_postgres::tokio_postgres;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use thiserror::Error;
|
||||
use warp::{http::StatusCode, Filter,Rejection,Reply};
|
||||
use crate::Infallible;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("error getting connection from DB pool: {0}")]
|
||||
DBPoolError(mobc::Error<tokio_postgres::Error>),
|
||||
#[error("error executing DB query: {0}")]
|
||||
DBQueryError(#[from] tokio_postgres::Error),
|
||||
#[error("error creating table: {0}")]
|
||||
DBInitError(tokio_postgres::Error),
|
||||
#[error("error reading file: {0}")]
|
||||
ReadFileError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
impl warp::reject::Reject for Error {}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ErrorResponse {
|
||||
message: String,
|
||||
}
|
||||
|
||||
pub async fn handle_rejection(err: Rejection) -> std::result::Result<impl Reply, Infallible> {
|
||||
let code;
|
||||
let message;
|
||||
|
||||
if err.is_not_found() {
|
||||
code = StatusCode::NOT_FOUND;
|
||||
message = "Not Found";
|
||||
} else if let Some(_) = err.find::<warp::filters::body::BodyDeserializeError>() {
|
||||
code = StatusCode::BAD_REQUEST;
|
||||
message = "Invalid Body";
|
||||
} else if let Some(e) = err.find::<Error>() {
|
||||
match e {
|
||||
Error::DBQueryError(_) => {
|
||||
code = StatusCode::BAD_REQUEST;
|
||||
message = "Could not Execute request";
|
||||
}
|
||||
_ => {
|
||||
eprintln!("unhandled application error: {:?}", err);
|
||||
code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
message = "Internal Server Error";
|
||||
}
|
||||
}
|
||||
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
|
||||
code = StatusCode::METHOD_NOT_ALLOWED;
|
||||
message = "Method Not Allowed";
|
||||
} else {
|
||||
eprintln!("unhandled error: {:?}", err);
|
||||
code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
message = "Internal Server Error";
|
||||
}
|
||||
|
||||
let json = warp::reply::json(&ErrorResponse {
|
||||
message: message.into(),
|
||||
});
|
||||
|
||||
Ok(warp::reply::with_status(json, code))
|
||||
}
|
||||
|
||||
|
14
src/handler.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use crate::{db, DBPool};
|
||||
use crate::error::Error::{*};
|
||||
use warp::{http::StatusCode, reject, Reply, Rejection};
|
||||
|
||||
pub async fn health_handler(db_pool: DBPool) -> std::result::Result<impl Reply, Rejection> {
|
||||
let db = db::get_db_con(&db_pool)
|
||||
.await
|
||||
.map_err(|e| reject::custom(e))?;
|
||||
|
||||
db.execute("SELECT 1", &[])
|
||||
.await
|
||||
.map_err(|e| reject::custom(DBQueryError(e)))?;
|
||||
Ok(StatusCode::OK)
|
||||
}
|
35
src/main.rs
|
@ -1,3 +1,34 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
// mod data;
|
||||
mod db;
|
||||
mod error;
|
||||
mod handler;
|
||||
|
||||
use warp::{http::StatusCode, Filter,Rejection};
|
||||
use mobc::{Connection, Pool};
|
||||
use mobc_postgres::{tokio_postgres, PgConnectionManager};
|
||||
use tokio_postgres::NoTls;
|
||||
use std::convert::Infallible;
|
||||
|
||||
type DBCon = Connection<PgConnectionManager<NoTls>>;
|
||||
type DBPool = Pool<PgConnectionManager<NoTls>>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let db_pool = db::create_pool().expect("database pool can be created");
|
||||
|
||||
db::init_db(&db_pool)
|
||||
.await
|
||||
.expect("database can be initialized");
|
||||
|
||||
let health_route = warp::path!("health")
|
||||
.and(with_db(db_pool.clone()))
|
||||
.and_then(handler::health_handler);
|
||||
let routes = health_route
|
||||
.with(warp::cors().allow_any_origin())
|
||||
.recover(error::handle_rejection);
|
||||
warp::serve(routes).run(([127, 0, 0, 1], 8000)).await;
|
||||
}
|
||||
|
||||
fn with_db(db_pool: DBPool) -> impl Filter<Extract = (DBPool,), Error = Infallible> + Clone {
|
||||
warp::any().map(move || db_pool.clone())
|
||||
}
|
||||
|
|
26
src/types.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
#[derive(ormx::Table)]
|
||||
#[ormx(table = "users", id = user_id, insertable)]
|
||||
struct User {
|
||||
#[ormx(column = "id")]
|
||||
user_id: u32,
|
||||
name: String,
|
||||
#[ormx(get_optional(&str))]
|
||||
email: String,
|
||||
#[ormx(default, set)]
|
||||
last_login: Option<NaiveDateTime>,
|
||||
password: String, // sure about the type?
|
||||
}
|
||||
|
||||
|
||||
#[derive(ormx::Table)]
|
||||
#[ormx(table = "trees", insertable)]
|
||||
struct Tree {
|
||||
id: u32,
|
||||
lat: i32,
|
||||
lon: i32,
|
||||
#[ormx(get_optional(&str))]
|
||||
species: String,
|
||||
age: u32,
|
||||
health: String,
|
||||
}
|
||||
|