initial commit
This commit is contained in:
commit
13263c19fd
26 changed files with 1312 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
out/
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2023 CCC Basel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
52
README.md
Normal file
52
README.md
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
# chaostreff.ch
|
||||||
|
|
||||||
|
Source code and build scripts for [chaostreff.ch](https://chaostreff.ch/)
|
||||||
|
|
||||||
|
## Local Development Setup
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ python3 -m virtualenv venv
|
||||||
|
$ . venv/bin/activate
|
||||||
|
(venv) $ pip install -r requirements.txt
|
||||||
|
(venv) $ ./build.py
|
||||||
|
(venv) $ ./run.py
|
||||||
|
```
|
||||||
|
|
||||||
|
run.py includes a SpaceAPI proxy to work around missing CORS headers.
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ python3 -m virtualenv venv
|
||||||
|
$ . venv/bin/activate
|
||||||
|
(venv) $ pip install -r requirements.txt
|
||||||
|
(venv) $ ./build.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Then deploy the contents of `out/` to the webroot.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
With exception of the sources listed below, this project is licensed under the [MIT License](LICENSE).
|
||||||
|
|
||||||
|
### `static/*/bootstrap*`
|
||||||
|
|
||||||
|
* Copyright (c) 2011-2023 The Bootstrap Authors
|
||||||
|
* MIT License
|
||||||
|
* https://github.com/twbs/bootstrap
|
||||||
|
|
||||||
|
### `static/{css,js}/leaflet*`
|
||||||
|
|
||||||
|
* Copyright (c) 2010-2022, Volodymyr Agafonkin
|
||||||
|
* Copyright (c) 2010-2011, CloudMade
|
||||||
|
* BSD-2-Clause license
|
||||||
|
* https://github.com/Leaflet/Leaflet
|
||||||
|
|
||||||
|
### `static/img/leaflet-marker-*`
|
||||||
|
|
||||||
|
* Copyright (c) 2010-2019, Vladimir Agafonkin
|
||||||
|
* Copyright (c) 2010-2011, CloudMade
|
||||||
|
* Copyright (c) 2013-2020, Thomas Pointhuber
|
||||||
|
* BSD-2-Clause license
|
||||||
|
* https://github.com/pointhi/leaflet-color-markers
|
29
build.py
Executable file
29
build.py
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import jinja2
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
with open('config.yml', 'r') as f:
|
||||||
|
config = yaml.safe_load(f)
|
||||||
|
|
||||||
|
os.makedirs('out', exist_ok = True)
|
||||||
|
shutil.copytree('static', 'out/static', dirs_exist_ok=True)
|
||||||
|
|
||||||
|
for lang in config['languages'].keys():
|
||||||
|
with open(f'l10n/{lang}.yml', 'r') as l:
|
||||||
|
l10n = yaml.safe_load(l)
|
||||||
|
|
||||||
|
env = jinja2.Environment(loader=jinja2.FileSystemLoader("src"))
|
||||||
|
env.globals.update(config['jinja_environment'])
|
||||||
|
env.globals.update(l10n)
|
||||||
|
env.globals['languages'] = config['languages']
|
||||||
|
env.globals['lang'] = lang
|
||||||
|
|
||||||
|
os.makedirs(os.path.join('out', lang), exist_ok = True)
|
||||||
|
for fname in os.listdir('src'):
|
||||||
|
tmpl = env.get_template(fname)
|
||||||
|
with open(os.path.join('out', lang, fname), 'w') as of:
|
||||||
|
of.write(tmpl.render())
|
129
config.yml
Normal file
129
config.yml
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
languages:
|
||||||
|
de: Deutsch
|
||||||
|
fr: Français
|
||||||
|
it: Italiano
|
||||||
|
en: English
|
||||||
|
|
||||||
|
jinja_environment:
|
||||||
|
|
||||||
|
baseurl: /
|
||||||
|
spaceapi_proxy_endpoint: /spaceapi
|
||||||
|
|
||||||
|
hackerspaces:
|
||||||
|
|
||||||
|
- name: 'CCC Basel'
|
||||||
|
address: 'Birsfelderstrasse 6, 4132 Muttenz'
|
||||||
|
lat: 47.53230
|
||||||
|
lon: 7.63421
|
||||||
|
open:
|
||||||
|
de: 'Dienstags ab 19:30'
|
||||||
|
fr: 'Mardi à partir de 19:30'
|
||||||
|
it: 'Martedì da 19:30'
|
||||||
|
en: 'Every Tuesday from 19:30'
|
||||||
|
contact:
|
||||||
|
web: 'https://ccc-basel.ch/'
|
||||||
|
spaceapi: '/https/spaceapi.kabelsalat.ch/'
|
||||||
|
|
||||||
|
- name: 'CCC Zürich'
|
||||||
|
address: 'Neue Hard 12, 8005 Zürich'
|
||||||
|
lat: 47.3869804
|
||||||
|
lon: 8.5201701
|
||||||
|
open:
|
||||||
|
de: 'Mittwochs ab 19:00'
|
||||||
|
fr: 'Mercredi à partir de 19:00'
|
||||||
|
it: 'Mercoledì da 19:00'
|
||||||
|
en: 'Every Wednesday from 19:00'
|
||||||
|
contact:
|
||||||
|
web: 'https://ccczh.ch/'
|
||||||
|
spaceapi: '/https/www.ccczh.ch/api/v13/'
|
||||||
|
|
||||||
|
- name: 'Chaostreff Bern'
|
||||||
|
address: 'Kyburgstrasse 13, 3013 Bern'
|
||||||
|
lat: 46.9563416
|
||||||
|
lon: 7.4483308
|
||||||
|
open:
|
||||||
|
de: 'Dienstags ab 19:00'
|
||||||
|
fr: 'Mardi à partir de 19:00'
|
||||||
|
it: 'Martedì da 19:00'
|
||||||
|
en: 'Every Tuesday from 19:00'
|
||||||
|
contact:
|
||||||
|
web: 'https://chaostreffbern.ch/'
|
||||||
|
spaceapi: '/https/www.chaosbern.ch/spaceapi.json'
|
||||||
|
|
||||||
|
- name: 'Coredump'
|
||||||
|
address: 'Eichwiesstrasse 4, 8645 Jona'
|
||||||
|
lat: 47.2250881
|
||||||
|
lon: 8.8338872
|
||||||
|
open:
|
||||||
|
de: 'Montags ab 20:00'
|
||||||
|
fr: 'Lundi à partir de 20:00'
|
||||||
|
it: 'Lunedì da 20:00'
|
||||||
|
en: 'Every Monday from 20:00'
|
||||||
|
contact:
|
||||||
|
web: 'https://coredump.ch/'
|
||||||
|
spaceapi: '/https/status.crdmp.ch/'
|
||||||
|
|
||||||
|
- name: 'FIXME Lausanne'
|
||||||
|
address: 'Les Ateliers de Renens, Chemin du Closel 3, 1020 Renens'
|
||||||
|
lat: 46.532372
|
||||||
|
lon: 6.591292
|
||||||
|
open:
|
||||||
|
de: 'Mittwochs ab 19:00'
|
||||||
|
fr: 'Mercredi à partir de 19:00'
|
||||||
|
it: 'Mercoledì da 19:00'
|
||||||
|
en: 'Every Wednesday from 19:00'
|
||||||
|
contact:
|
||||||
|
web: 'https://fixme.ch/'
|
||||||
|
spaceapi: '/https/fixme.ch/cgi-bin/spaceapi.py'
|
||||||
|
|
||||||
|
- name: 'LuXeria'
|
||||||
|
address: 'Ebikonerstrasse 75, CH-6043 Adligenswil'
|
||||||
|
lat: 47.073
|
||||||
|
lon: 8.357
|
||||||
|
open:
|
||||||
|
de: 'Mittwochs ab 20:00 <span class="badge bg-primary">Videokonferenz</span>'
|
||||||
|
fr: 'Mercredi à partir de 20:00 <span class="badge bg-primary">visioconférence</span>'
|
||||||
|
it: 'Mercoledì da 20:00 <span class="badge bg-primary">videoconferenza</span>'
|
||||||
|
en: 'Every Wednesday from 20:00 <span class="badge bg-primary">video conference</span>'
|
||||||
|
contact:
|
||||||
|
web: 'https://luxeria.ch/'
|
||||||
|
spaceapi: '/https/luxeria.ch/spaceapi.json'
|
||||||
|
|
||||||
|
- name: 'ODENWILUSENZ'
|
||||||
|
address: 'Hardmorgenweg 21, 8222 Beringen'
|
||||||
|
lat: 47.6951648
|
||||||
|
lon: 8.5806972
|
||||||
|
open:
|
||||||
|
de: 'Mittwochs ab 19:00'
|
||||||
|
fr: 'Mercredi à partir de 19:00'
|
||||||
|
it: 'Mercoledì da 19:00'
|
||||||
|
en: 'Every Wednesday from 19:00'
|
||||||
|
contact:
|
||||||
|
web: '/odenwilusenz.ch/'
|
||||||
|
|
||||||
|
- name: 'Post Tenebras Lab'
|
||||||
|
address: 'Avenue de la Praille 36, 1227 Carouge'
|
||||||
|
lat: 46.187134981155396
|
||||||
|
lon: 6.133659482002258
|
||||||
|
open:
|
||||||
|
de: 'Dienstags ab 19:00'
|
||||||
|
fr: 'Mardi à partir de 19:00'
|
||||||
|
it: 'Martedì da 19:00'
|
||||||
|
en: 'Every Tuesday from 19:00'
|
||||||
|
contact:
|
||||||
|
web: 'https://www.posttenebraslab.ch'
|
||||||
|
spaceapi: '/https/www.posttenebraslab.ch/status/status.json'
|
||||||
|
|
||||||
|
- name: 'Ruum42'
|
||||||
|
address: 'Andreasstrasse 5, 9000 St. Gallen'
|
||||||
|
lat: 47.4207758
|
||||||
|
lon: 9.3555784
|
||||||
|
open:
|
||||||
|
de: 'Dienstags ab 18:30'
|
||||||
|
fr: 'Mardi à partir de 18:30'
|
||||||
|
it: 'Martedì da 18:30'
|
||||||
|
en: 'Every Tuesday from 18:30'
|
||||||
|
contact:
|
||||||
|
web: 'https://ruum42.ch/'
|
40
l10n/de.yml
Normal file
40
l10n/de.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
h_pagetitle: "Chaostreffs und Hackerspaces in der Schweiz"
|
||||||
|
h_spaces: "Chaostreffs & Hackerspaces"
|
||||||
|
h_contact: "Kontakt"
|
||||||
|
h_services: "Dienste"
|
||||||
|
h_language: "Sprache"
|
||||||
|
|
||||||
|
legend_marker_header: 'Die Farben der Marker auf der Karte geben Echtzeitinformationen (gemäss <a href="https://spaceapi.io">SpaceAPI</a>) der einzelnen Hackerspaces wieder:'
|
||||||
|
legend_marker_green: "Aktuell für Besucher geöffnet"
|
||||||
|
legend_marker_red: "Aktuell für Besucher geschlossen"
|
||||||
|
legend_marker_blue: "Keine Information vorhanden"
|
||||||
|
|
||||||
|
|
||||||
|
th_name: "Name"
|
||||||
|
th_address: "Adresse"
|
||||||
|
th_hours: "Öffnungszeiten"
|
||||||
|
th_contact: "Kontakt"
|
||||||
|
|
||||||
|
contact_web: Web
|
||||||
|
|
||||||
|
table_footer_missing: "Euer Chaostreff oder Hackerspace fehlt in dieser Liste? Lasst es uns wissen, und wir fügen euch gerne hinzu."
|
||||||
|
|
||||||
|
contact_table_link: "Die Kontaktinformationen der einzelnen Spaces & Treffs sind in der obenstehenden Tabelle verlinkt."
|
||||||
|
contact_swiss_chaos: >-
|
||||||
|
Wenn du generell zum schweizer Chaos Kontakt aufnehmen willst, machst du das am besten über die Mailingliste
|
||||||
|
<a href="https://lists.chaostreff.ch/postorius/lists/swiss-chaos.chaostreff.ch/">swiss-chaos</a>.
|
||||||
|
|
||||||
|
services_header: "Wir betreiben für die Chaostreffs in der Schweiz folgende Dienste:"
|
||||||
|
services_ml: "<strong>Mailinglisten</strong> unter der Domain <tt>chaostreff.ch</tt>"
|
||||||
|
services_dns: "<strong>DNS-Delegation</strong> für <tt>$kanton.chaostreff.ch</tt>"
|
||||||
|
services_irc: "<strong>IRC-Server</strong> <tt>irc.chaostreff.ch</tt>"
|
||||||
|
services_footer: >-
|
||||||
|
Falls ihr für euren neuen Chaostreff einen dieser Dienste benutzen möchtet,
|
||||||
|
<a href="#contact">kontaktiert uns</a> am besten einfach.
|
||||||
|
|
||||||
|
footer: 'chaostreff.ch wird betrieben vom <a href="https://ccc-basel.ch/">CCC Basel</a>. Diese Seite ist freie Software lizensiert unter der MIT-Lizenz.'
|
||||||
|
|
||||||
|
marker_popup_open: Aktuell geöffnet
|
||||||
|
marker_popup_close: Aktuell geschlossen
|
39
l10n/en.yml
Normal file
39
l10n/en.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
h_pagetitle: "Chaostreffs and Hackerspaces in Switzerland"
|
||||||
|
h_spaces: "Chaostreffs & Hackerspaces"
|
||||||
|
h_contact: "Contact"
|
||||||
|
h_services: "Services"
|
||||||
|
h_language: "Language"
|
||||||
|
|
||||||
|
legend_marker_header: 'The colors of the markers on the map indicate real-time status information (according to <a href="https://spaceapi.io">SpaceAPI</a>) of each hackerspace:'
|
||||||
|
legend_marker_green: "Currently open for visitors"
|
||||||
|
legend_marker_red: "Currently closed for visitors"
|
||||||
|
legend_marker_blue: "No information available"
|
||||||
|
|
||||||
|
th_name: "Name"
|
||||||
|
th_address: "Address"
|
||||||
|
th_hours: "Opening Hours"
|
||||||
|
th_contact: "Contact"
|
||||||
|
|
||||||
|
contact_web: Web
|
||||||
|
|
||||||
|
table_footer_missing: "Your Chaostreff or hackerspace is missing from this list? Let us know, we'll gladly add you to the list."
|
||||||
|
|
||||||
|
contact_table_link: "The contact information of individual hackerspaces can be found in the above table."
|
||||||
|
contact_swiss_chaos: >-
|
||||||
|
If you want to get in touch with the swiss chaos in general, your best approach would be the
|
||||||
|
<a href="https://lists.chaostreff.ch/postorius/lists/swiss-chaos.chaostreff.ch/">swiss-chaos</a> mailing list.
|
||||||
|
|
||||||
|
services_header: "We operate the following services for Chaostreffs in Switzerland:"
|
||||||
|
services_ml: "<strong>Mailing lists</strong> on the domain <tt>chaostreff.ch</tt>"
|
||||||
|
services_dns: "<strong>DNS delegation</strong> for <tt>$canton.chaostreff.ch</tt>"
|
||||||
|
services_irc: "The <strong>IRC server</strong> <tt>irc.chaostreff.ch</tt>"
|
||||||
|
services_footer: >-
|
||||||
|
If you want to make use of one of these services, please
|
||||||
|
<a href="#contact">get in touch</a>.
|
||||||
|
|
||||||
|
footer: 'chaostreff.ch is operated by <a href="https://ccc-basel.ch/">CCC Basel</a>. This page is free software licensed under the MIT license.'
|
||||||
|
|
||||||
|
marker_popup_open: Currently open
|
||||||
|
marker_popup_close: Currently closed
|
40
l10n/fr.yml
Normal file
40
l10n/fr.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
h_pagetitle: "Chaostreffs et Hackerspaces en Suisse"
|
||||||
|
h_spaces: "Chaostreffs & Hackerspaces"
|
||||||
|
h_contact: "Contact"
|
||||||
|
h_services: "Services"
|
||||||
|
h_language: "Langue"
|
||||||
|
|
||||||
|
legend_marker_header: 'Die Farben der Marker auf der Karte geben Echtzeitinformationen (gemäss <a href="https://spaceapi.io">SpaceAPI</a>) der einzelnen Hackerspaces wieder:'
|
||||||
|
legend_marker_green: "Ouvert maintenant pour les visiteurs"
|
||||||
|
legend_marker_red: "Fermé maintenant pour les visiteurs"
|
||||||
|
legend_marker_blue: "Pas de information"
|
||||||
|
|
||||||
|
|
||||||
|
th_name: "Nom"
|
||||||
|
th_address: "Adresse"
|
||||||
|
th_hours: "Heures d'ouverture"
|
||||||
|
th_contact: "Contact"
|
||||||
|
|
||||||
|
contact_web: Web
|
||||||
|
|
||||||
|
table_footer_missing: "Euer Chaostreff oder Hackerspace fehlt in dieser Liste? Lasst es uns wissen, und wir fügen euch gerne hinzu."
|
||||||
|
|
||||||
|
contact_table_link: "Die Kontaktinformationen der einzelnen Spaces & Treffs sind in der obenstehenden Tabelle verlinkt."
|
||||||
|
contact_swiss_chaos: >-
|
||||||
|
Wenn du generell zum schweizer Chaos Kontakt aufnehmen willst, machst du das am besten über die Mailingliste
|
||||||
|
<a href="https://lists.chaostreff.ch/postorius/lists/swiss-chaos.chaostreff.ch/">swiss-chaos</a>.
|
||||||
|
|
||||||
|
services_header: "Wir betreiben für die Chaostreffs in der Schweiz folgende Dienste:"
|
||||||
|
services_ml: "<strong>Mailinglisten</strong> unter der Domain <tt>chaostreff.ch</tt>"
|
||||||
|
services_dns: "<strong>DNS-Delegation</strong> für <tt>$kanton.chaostreff.ch</tt>"
|
||||||
|
services_irc: "<strong>IRC-Server</strong> <tt>irc.chaostreff.ch</tt>"
|
||||||
|
services_footer: >-
|
||||||
|
Falls ihr für euren neuen Chaostreff einen dieser Dienste benutzen möchtet,
|
||||||
|
<a href="#contact">kontaktiert uns</a> am besten einfach.
|
||||||
|
|
||||||
|
footer: 'chaostreff.ch wird betrieben vom <a href="https://ccc-basel.ch/">CCC Basel</a>. Diese Seite ist freie Software lizensiert unter der MIT-Lizenz.'
|
||||||
|
|
||||||
|
marker_popup_open: Ouvert maintenant
|
||||||
|
marker_popup_close: Fermé maintenant
|
41
l10n/it.yml
Normal file
41
l10n/it.yml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
h_pagetitle: "Chaostreffs e Hackerspaces in Svizzera"
|
||||||
|
h_spaces: "Chaostreffs & Hackerspaces"
|
||||||
|
h_contact: "Contatto"
|
||||||
|
h_services: "Servizi"
|
||||||
|
h_language: "Lingua"
|
||||||
|
|
||||||
|
legend_marker_header: >-
|
||||||
|
I colori degli spilli nella mappa stanno indicando le condizioni degli hackerspaces in tempo reale
|
||||||
|
(secondo lo <a href="https://spaceapi.io">SpaceAPI</a>):
|
||||||
|
legend_marker_green: "Ora aperto per i visitatori"
|
||||||
|
legend_marker_red: "Ora chiuso per i visitatori"
|
||||||
|
legend_marker_blue: "Nessuna informazione"
|
||||||
|
|
||||||
|
|
||||||
|
th_name: "Nome"
|
||||||
|
th_address: "Indirizzo"
|
||||||
|
th_hours: "Orario"
|
||||||
|
th_contact: "Contatto"
|
||||||
|
|
||||||
|
contact_web: web
|
||||||
|
|
||||||
|
table_footer_missing: "Il vostro hackerspace manca nella lista lassù? Contattaci per completare la lista per favore."
|
||||||
|
|
||||||
|
contact_table_link: "I contatti degli hackerspaces sono linkato nella tabella lassù."
|
||||||
|
contact_swiss_chaos: >-
|
||||||
|
Se vuoi contattare generalmente il caos svizzero, usa la mailing list
|
||||||
|
<a href="https://lists.chaostreff.ch/postorius/lists/swiss-chaos.chaostreff.ch/">swiss-chaos</a>.
|
||||||
|
|
||||||
|
services_header: "Eserciamo i seguiti servizi per gli Chaostreffs in Svizzera:"
|
||||||
|
services_ml: "<strong>Mailing lists </strong> sotto la domain <tt>chaostreff.ch</tt>"
|
||||||
|
services_dns: "<strong>Delegazioni di DNS</strong> per <tt>$cantone.chaostreff.ch</tt>"
|
||||||
|
services_irc: "<strong>Server IRC</strong> <tt>irc.chaostreff.ch</tt>"
|
||||||
|
services_footer: >-
|
||||||
|
Se vuoi utilizzare uno die quesi servizi, per favore <a href="#contact">contattaci</a>.
|
||||||
|
|
||||||
|
footer: 'chaostreff.ch è esercita del <a href="https://ccc-basel.ch/">CCC Basel</a>. Questa pagina è software libero licenzato per la licenza MIT.'
|
||||||
|
|
||||||
|
marker_popup_open: Ora operto
|
||||||
|
marker_popup_close: Ora chiuso
|
39
run.py
Executable file
39
run.py
Executable file
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import bottle
|
||||||
|
import urllib.parse
|
||||||
|
import requests
|
||||||
|
|
||||||
|
@bottle.get('/spaceapi/<schema>/<host>')
|
||||||
|
@bottle.get('/spaceapi/<schema>/<host>/')
|
||||||
|
@bottle.get('/spaceapi/<schema>/<host>/<path:path>')
|
||||||
|
def spaceapi_proxy(schema, host, path=''):
|
||||||
|
url = urllib.parse.urlunsplit((schema, host, path, '', ''))
|
||||||
|
import logging
|
||||||
|
logging.error(url)
|
||||||
|
r = requests.get(url)
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
@bottle.get('/static/<path:path>')
|
||||||
|
def static(path):
|
||||||
|
return bottle.static_file(path, root='out/static')
|
||||||
|
|
||||||
|
@bottle.get('/<lang>')
|
||||||
|
def langred(lang):
|
||||||
|
bottle.redirect(f'/{lang}/')
|
||||||
|
|
||||||
|
@bottle.get('/<lang>/')
|
||||||
|
def lang(lang):
|
||||||
|
return bottle.static_file('index.html', root=os.path.join('out', lang))
|
||||||
|
|
||||||
|
@bottle.get('/<lang>/<path:path>')
|
||||||
|
def langpath(lang, path):
|
||||||
|
return bottle.static_file(path, root=os.path.join('out', lang))
|
||||||
|
|
||||||
|
@bottle.get('/')
|
||||||
|
def index():
|
||||||
|
bottle.redirect('/de/')
|
||||||
|
|
||||||
|
bottle.debug()
|
||||||
|
bottle.run()
|
51
src/chaostreff.js
Normal file
51
src/chaostreff.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
window.onload = (ev) => {
|
||||||
|
|
||||||
|
let spaces = [
|
||||||
|
{% for space in hackerspaces %}
|
||||||
|
{ name: '{{ space.name }}', address: '{{ space.address }}', lat: {{ space.lat }}, lon: {{ space.lon }}, open: '{{ space.open[lang] }}', web: '{{ space.contact.web }}', spaceapi: {% if 'spaceapi' in space %}'{{ space.spaceapi }}'{% else %}null{% endif %} },
|
||||||
|
{% endfor %}
|
||||||
|
];
|
||||||
|
let SPACEAPI_PROXY = '{{ spaceapi_proxy_endpoint }}';
|
||||||
|
|
||||||
|
let blueIcon = new L.Icon({
|
||||||
|
iconUrl: '{{ baseurl }}static/img/leaflet-marker-blue.png',
|
||||||
|
shadowUrl: '{{ baseurl }}static/img/leaflet-marker-shadow.png',
|
||||||
|
iconSize: [25, 41],
|
||||||
|
iconAnchor: [12, 41],
|
||||||
|
popupAnchor: [1, -34],
|
||||||
|
shadowSize: [41, 41]
|
||||||
|
});
|
||||||
|
|
||||||
|
let map = L.map('leaflet-map').setView([46.925, 8.224], 8);
|
||||||
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
maxZoom: 19,
|
||||||
|
minZoom: 6,
|
||||||
|
maxBounds: L.latLngBounds(L.latLng(45.817936, 5.956135), L.latLng(47.8, 10.49234)),
|
||||||
|
maxBoundsViscosity: 1.0,
|
||||||
|
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||||
|
}).addTo(map);
|
||||||
|
let newtable = '';
|
||||||
|
for (let i = 0; i < spaces.length; ++i) {
|
||||||
|
let marker = L.marker([spaces[i].lat, spaces[i].lon], {icon: blueIcon})
|
||||||
|
.bindPopup(`<b>${spaces[i].name}</b><address>${spaces[i].address.replaceAll(',', '<br/>')}</address><a href="${spaces[i].web}">${spaces[i].web}</a>`)
|
||||||
|
.addTo(map);
|
||||||
|
if (spaces[i].spaceapi !== undefined) {
|
||||||
|
fetch(SPACEAPI_PROXY + spaces[i].spaceapi, {
|
||||||
|
headers: { 'Accept': 'application/json' },
|
||||||
|
}).then(resp => resp.json()).then(json => {
|
||||||
|
if (json.state == undefined || json.state.open == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(json);
|
||||||
|
if (json.state.open === true) {
|
||||||
|
marker._icon.src = '{{ baseurl }}static/img/leaflet-marker-green.png';
|
||||||
|
marker._popup._content += '<br/><b>{{ marker_popup_open }}</b>';
|
||||||
|
} else if (json.state.open === false) {
|
||||||
|
marker._icon.src = '{{ baseurl }}static/img/leaflet-marker-red.png';
|
||||||
|
marker._popup._content += '<br/><b>{{ marker_popup_close }}</b>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
119
src/index.html
Normal file
119
src/index.html
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta http-equiv="encoding" charset="utf-8" />
|
||||||
|
<title>{{ h_pagetitle }}</title>
|
||||||
|
<link rel="stylesheet" href="{{ baseurl }}static/css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="{{ baseurl }}static/css/leaflet.css" />
|
||||||
|
<link rel="stylesheet" href="{{ baseurl }}static/css/chaostreff.css" />
|
||||||
|
<link rel="prefetch" href="{{ baseurl }}static/img/leaflet-marker-blue.png" />
|
||||||
|
<link rel="prefetch" href="{{ baseurl }}static/img/leaflet-marker-green.png" />
|
||||||
|
<link rel="prefetch" href="{{ baseurl }}static/img/leaflet-marker-red.png" />
|
||||||
|
<script src="{{ baseurl }}static/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="{{ baseurl }}static/js/leaflet.js"></script>
|
||||||
|
<script src="chaostreff.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="navbar navbar-expand-sm navbar-dark bg-dark">
|
||||||
|
<nav class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">{{ h_pagetitle }}</a>
|
||||||
|
<ul class="navbar-nav navbar-expand me-auto">
|
||||||
|
<li class="nav-item"><a href="#spaces" class="nav-link">{{ h_spaces }}</a></li>
|
||||||
|
<li class="nav-item"><a href="#contact" class="nav-link">{{ h_contact }}</a></li>
|
||||||
|
<li class="nav-item me-auto"><a href="#services" class="nav-link">{{ h_services }}</a></li>
|
||||||
|
<li class="nav-item dropdown ms-auto">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="navbar-dropdown-language" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-translate" viewBox="0 0 16 16">
|
||||||
|
<!--
|
||||||
|
https://icons.getbootstrap.com/icons/translate/
|
||||||
|
Copyright (c) 2011-2023 The Bootstrap Authors
|
||||||
|
Licensed under the MIT license
|
||||||
|
https://github.com/twbs/bootstrap/blob/main/LICENSE
|
||||||
|
-->
|
||||||
|
<path d="M4.545 6.714 4.11 8H3l1.862-5h1.284L8 8H6.833l-.435-1.286H4.545zm1.634-.736L5.5 3.956h-.049l-.679 2.022H6.18z"/>
|
||||||
|
<path d="M0 2a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v3h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-3H2a2 2 0 0 1-2-2V2zm2-1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H2zm7.138 9.995c.193.301.402.583.63.846-.748.575-1.673 1.001-2.768 1.292.178.217.451.635.555.867 1.125-.359 2.08-.844 2.886-1.494.777.665 1.739 1.165 2.93 1.472.133-.254.414-.673.629-.89-1.125-.253-2.057-.694-2.82-1.284.681-.747 1.222-1.651 1.621-2.757H14V8h-3v1.047h.765c-.318.844-.74 1.546-1.272 2.13a6.066 6.066 0 0 1-.415-.492 1.988 1.988 0 0 1-.94.31z"/>
|
||||||
|
</svg>
|
||||||
|
{{ h_language }}
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end" aria-labelledby="navbar-dropdown-language">
|
||||||
|
{% for language, name in languages.items() %}
|
||||||
|
<li><a rel="self" href="/{{ language }}" class="{% if language == lang %}active{% endif %} dropdown-item" alt="{{ name }}" title="{{ name }}">{{ name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main class="container-fluid">
|
||||||
|
<section id="map">
|
||||||
|
<div id="leaflet-map" class=""></div>
|
||||||
|
<aside class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 col-md-12 col-sm-12">
|
||||||
|
{{ legend_marker_header }}
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-2 col-md-4 col-sm-12">
|
||||||
|
<img class="leaflet-marker-inline" src="{{ baseurl }}static/img/leaflet-marker-green.png" /> {{ legend_marker_green }}
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-2 col-md-4 col-sm-12">
|
||||||
|
<img class="leaflet-marker-inline" src="{{ baseurl }}static/img/leaflet-marker-red.png" /> {{ legend_marker_red }}
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-2 col-md-4 col-sm-12">
|
||||||
|
<img class="leaflet-marker-inline" src="{{ baseurl }}static/img/leaflet-marker-blue.png" /> {{ legend_marker_blue }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
<section id="spaces">
|
||||||
|
<h2 class="mt-2-lg mb-1-lg">{{ h_spaces }}</h2>
|
||||||
|
<div class="table-responsive-lg">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead class="thead">
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{{ th_name }}</th>
|
||||||
|
<th scope="col">{{ th_address }}</th>
|
||||||
|
<th scope="col">{{ th_hours }}</th>
|
||||||
|
<th scope="col">{{ th_contact }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="hackerspaces-table">
|
||||||
|
{% for space in hackerspaces %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ space.name }}</td>
|
||||||
|
<td><address>{{ space.address }}</address></td>
|
||||||
|
<td>{{ space.open[lang] }}</td>
|
||||||
|
<td><a href="{{ space.contact.web }}">{{ contact_web }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p>{{ table_footer_missing }}</p>
|
||||||
|
</section>
|
||||||
|
<section id="contact">
|
||||||
|
<h2 class="mt-2-lg mb-1-lg">{{ h_contact }}</h2>
|
||||||
|
<p>
|
||||||
|
{{ contact_table_link}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ contact_swiss_chaos }}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section id="services">
|
||||||
|
<h2 class="mt-2-lg mb-1-lg">{{ h_services }}</h2>
|
||||||
|
<p>{{ services_header }}</p>
|
||||||
|
<ul>
|
||||||
|
<li>{{ services_ml }}</li>
|
||||||
|
<li>{{ services_dns }}</li>
|
||||||
|
<li>{{ services_irc }}</li>
|
||||||
|
</ul>
|
||||||
|
<p>{{ services_footer }}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<footer class="text-center p-3 bg-light">
|
||||||
|
<p>{{ footer }}</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
src/sitemap.xml
Normal file
12
src/sitemap.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
|
||||||
|
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
{% for language in languages $}
|
||||||
|
<url>
|
||||||
|
<loc>{{ baseurl }}{{ language }}/</loc>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>0.5</priority>
|
||||||
|
</url>
|
||||||
|
{% endfor %}
|
||||||
|
</urlset>
|
7
static/css/bootstrap.min.css
vendored
Normal file
7
static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/css/bootstrap.min.css.map
Normal file
1
static/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
10
static/css/chaostreff.css
Normal file
10
static/css/chaostreff.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
img.leaflet-marker-inline {
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#leaflet-map {
|
||||||
|
height: 500px;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
656
static/css/leaflet.css
Normal file
656
static/css/leaflet.css
Normal file
|
@ -0,0 +1,656 @@
|
||||||
|
/* required styles */
|
||||||
|
|
||||||
|
.leaflet-pane,
|
||||||
|
.leaflet-tile,
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow,
|
||||||
|
.leaflet-tile-container,
|
||||||
|
.leaflet-pane > svg,
|
||||||
|
.leaflet-pane > canvas,
|
||||||
|
.leaflet-zoom-box,
|
||||||
|
.leaflet-image-layer,
|
||||||
|
.leaflet-layer {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.leaflet-container {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.leaflet-tile,
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
/* Prevents IE11 from highlighting tiles in blue */
|
||||||
|
.leaflet-tile::selection {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||||
|
.leaflet-safari .leaflet-tile {
|
||||||
|
image-rendering: -webkit-optimize-contrast;
|
||||||
|
}
|
||||||
|
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||||
|
.leaflet-safari .leaflet-tile-container {
|
||||||
|
width: 1600px;
|
||||||
|
height: 1600px;
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||||
|
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||||
|
.leaflet-container .leaflet-overlay-pane svg {
|
||||||
|
max-width: none !important;
|
||||||
|
max-height: none !important;
|
||||||
|
}
|
||||||
|
.leaflet-container .leaflet-marker-pane img,
|
||||||
|
.leaflet-container .leaflet-shadow-pane img,
|
||||||
|
.leaflet-container .leaflet-tile-pane img,
|
||||||
|
.leaflet-container img.leaflet-image-layer,
|
||||||
|
.leaflet-container .leaflet-tile {
|
||||||
|
max-width: none !important;
|
||||||
|
max-height: none !important;
|
||||||
|
width: auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-container.leaflet-touch-zoom {
|
||||||
|
-ms-touch-action: pan-x pan-y;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
}
|
||||||
|
.leaflet-container.leaflet-touch-drag {
|
||||||
|
-ms-touch-action: pinch-zoom;
|
||||||
|
/* Fallback for FF which doesn't support pinch-zoom */
|
||||||
|
touch-action: none;
|
||||||
|
touch-action: pinch-zoom;
|
||||||
|
}
|
||||||
|
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.leaflet-container {
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.leaflet-container a {
|
||||||
|
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||||
|
}
|
||||||
|
.leaflet-tile {
|
||||||
|
filter: inherit;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.leaflet-tile-loaded {
|
||||||
|
visibility: inherit;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-box {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 800;
|
||||||
|
}
|
||||||
|
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||||
|
.leaflet-overlay-pane svg {
|
||||||
|
-moz-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-pane { z-index: 400; }
|
||||||
|
|
||||||
|
.leaflet-tile-pane { z-index: 200; }
|
||||||
|
.leaflet-overlay-pane { z-index: 400; }
|
||||||
|
.leaflet-shadow-pane { z-index: 500; }
|
||||||
|
.leaflet-marker-pane { z-index: 600; }
|
||||||
|
.leaflet-tooltip-pane { z-index: 650; }
|
||||||
|
.leaflet-popup-pane { z-index: 700; }
|
||||||
|
|
||||||
|
.leaflet-map-pane canvas { z-index: 100; }
|
||||||
|
.leaflet-map-pane svg { z-index: 200; }
|
||||||
|
|
||||||
|
.leaflet-vml-shape {
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
.lvml {
|
||||||
|
behavior: url(#default#VML);
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* control positioning */
|
||||||
|
|
||||||
|
.leaflet-control {
|
||||||
|
position: relative;
|
||||||
|
z-index: 800;
|
||||||
|
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.leaflet-top,
|
||||||
|
.leaflet-bottom {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.leaflet-top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.leaflet-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.leaflet-bottom {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.leaflet-left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.leaflet-control {
|
||||||
|
float: left;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.leaflet-right .leaflet-control {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.leaflet-top .leaflet-control {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-bottom .leaflet-control {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-left .leaflet-control {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-right .leaflet-control {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* zoom and fade animations */
|
||||||
|
|
||||||
|
.leaflet-fade-anim .leaflet-popup {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: opacity 0.2s linear;
|
||||||
|
-moz-transition: opacity 0.2s linear;
|
||||||
|
transition: opacity 0.2s linear;
|
||||||
|
}
|
||||||
|
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-animated {
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
-ms-transform-origin: 0 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
svg.leaflet-zoom-animated {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||||
|
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
}
|
||||||
|
.leaflet-zoom-anim .leaflet-tile,
|
||||||
|
.leaflet-pan-anim .leaflet-tile {
|
||||||
|
-webkit-transition: none;
|
||||||
|
-moz-transition: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* cursors */
|
||||||
|
|
||||||
|
.leaflet-interactive {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.leaflet-grab {
|
||||||
|
cursor: -webkit-grab;
|
||||||
|
cursor: -moz-grab;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
.leaflet-crosshair,
|
||||||
|
.leaflet-crosshair .leaflet-interactive {
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
.leaflet-popup-pane,
|
||||||
|
.leaflet-control {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
.leaflet-dragging .leaflet-grab,
|
||||||
|
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||||
|
.leaflet-dragging .leaflet-marker-draggable {
|
||||||
|
cursor: move;
|
||||||
|
cursor: -webkit-grabbing;
|
||||||
|
cursor: -moz-grabbing;
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* marker & overlays interactivity */
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow,
|
||||||
|
.leaflet-image-layer,
|
||||||
|
.leaflet-pane > svg path,
|
||||||
|
.leaflet-tile-container {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-marker-icon.leaflet-interactive,
|
||||||
|
.leaflet-image-layer.leaflet-interactive,
|
||||||
|
.leaflet-pane > svg path.leaflet-interactive,
|
||||||
|
svg.leaflet-image-layer.leaflet-interactive path {
|
||||||
|
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* visual tweaks */
|
||||||
|
|
||||||
|
.leaflet-container {
|
||||||
|
background: #ddd;
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
.leaflet-container a {
|
||||||
|
color: #0078A8;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-box {
|
||||||
|
border: 2px dotted #38f;
|
||||||
|
background: rgba(255,255,255,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* general typography */
|
||||||
|
.leaflet-container {
|
||||||
|
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* general toolbar styles */
|
||||||
|
|
||||||
|
.leaflet-bar {
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.leaflet-bar a {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.leaflet-bar a,
|
||||||
|
.leaflet-control-layers-toggle {
|
||||||
|
background-position: 50% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:hover,
|
||||||
|
.leaflet-bar a:focus {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:first-child {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:last-child {
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.leaflet-bar a.leaflet-disabled {
|
||||||
|
cursor: default;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-bar a {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-bar a:first-child {
|
||||||
|
border-top-left-radius: 2px;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-bar a:last-child {
|
||||||
|
border-bottom-left-radius: 2px;
|
||||||
|
border-bottom-right-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* zoom control */
|
||||||
|
|
||||||
|
.leaflet-control-zoom-in,
|
||||||
|
.leaflet-control-zoom-out {
|
||||||
|
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||||
|
text-indent: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* layers control */
|
||||||
|
|
||||||
|
.leaflet-control-layers {
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-toggle {
|
||||||
|
background-image: url(images/layers.png);
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
.leaflet-retina .leaflet-control-layers-toggle {
|
||||||
|
background-image: url(images/layers-2x.png);
|
||||||
|
background-size: 26px 26px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-control-layers-toggle {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers .leaflet-control-layers-list,
|
||||||
|
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-expanded {
|
||||||
|
padding: 6px 10px 6px 6px;
|
||||||
|
color: #333;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-scrollbar {
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-selector {
|
||||||
|
margin-top: 2px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers label {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
font-size: 1.08333em;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-separator {
|
||||||
|
height: 0;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
margin: 5px -10px 5px -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default icon URLs */
|
||||||
|
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||||
|
background-image: url(images/marker-icon.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* attribution and scale controls */
|
||||||
|
|
||||||
|
.leaflet-container .leaflet-control-attribution {
|
||||||
|
background: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution,
|
||||||
|
.leaflet-control-scale-line {
|
||||||
|
padding: 0 5px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution a:hover,
|
||||||
|
.leaflet-control-attribution a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.leaflet-attribution-flag {
|
||||||
|
display: inline !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
width: 1em;
|
||||||
|
height: 0.6669em;
|
||||||
|
}
|
||||||
|
.leaflet-left .leaflet-control-scale {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-bottom .leaflet-control-scale {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line {
|
||||||
|
border: 2px solid #777;
|
||||||
|
border-top: none;
|
||||||
|
line-height: 1.1;
|
||||||
|
padding: 2px 5px 1px;
|
||||||
|
white-space: nowrap;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
text-shadow: 1px 1px #fff;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line:not(:first-child) {
|
||||||
|
border-top: 2px solid #777;
|
||||||
|
border-bottom: none;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||||
|
border-bottom: 2px solid #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-control-attribution,
|
||||||
|
.leaflet-touch .leaflet-control-layers,
|
||||||
|
.leaflet-touch .leaflet-bar {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-control-layers,
|
||||||
|
.leaflet-touch .leaflet-bar {
|
||||||
|
border: 2px solid rgba(0,0,0,0.2);
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* popup */
|
||||||
|
|
||||||
|
.leaflet-popup {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content-wrapper {
|
||||||
|
padding: 1px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content {
|
||||||
|
margin: 13px 24px 13px 20px;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-size: 13px;
|
||||||
|
font-size: 1.08333em;
|
||||||
|
min-height: 1px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content p {
|
||||||
|
margin: 17px 0;
|
||||||
|
margin: 1.3em 0;
|
||||||
|
}
|
||||||
|
.leaflet-popup-tip-container {
|
||||||
|
width: 40px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-left: -20px;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
padding: 1px;
|
||||||
|
|
||||||
|
margin: -10px auto 0;
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-moz-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
.leaflet-popup-content-wrapper,
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
background: white;
|
||||||
|
color: #333;
|
||||||
|
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.leaflet-container a.leaflet-popup-close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
border: none;
|
||||||
|
text-align: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||||
|
color: #757575;
|
||||||
|
text-decoration: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||||
|
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||||
|
color: #585858;
|
||||||
|
}
|
||||||
|
.leaflet-popup-scrolled {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||||
|
-ms-zoom: 1;
|
||||||
|
}
|
||||||
|
.leaflet-oldie .leaflet-popup-tip {
|
||||||
|
width: 24px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||||
|
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-oldie .leaflet-control-zoom,
|
||||||
|
.leaflet-oldie .leaflet-control-layers,
|
||||||
|
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||||
|
.leaflet-oldie .leaflet-popup-tip {
|
||||||
|
border: 1px solid #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* div icon */
|
||||||
|
|
||||||
|
.leaflet-div-icon {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tooltip */
|
||||||
|
/* Base styles for the element that has a tooltip */
|
||||||
|
.leaflet-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
padding: 6px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #222;
|
||||||
|
white-space: nowrap;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.leaflet-tooltip.leaflet-interactive {
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top:before,
|
||||||
|
.leaflet-tooltip-bottom:before,
|
||||||
|
.leaflet-tooltip-left:before,
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border: 6px solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Directions */
|
||||||
|
|
||||||
|
.leaflet-tooltip-bottom {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top {
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-bottom:before,
|
||||||
|
.leaflet-tooltip-top:before {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top:before {
|
||||||
|
bottom: 0;
|
||||||
|
margin-bottom: -12px;
|
||||||
|
border-top-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-bottom:before {
|
||||||
|
top: 0;
|
||||||
|
margin-top: -12px;
|
||||||
|
margin-left: -6px;
|
||||||
|
border-bottom-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left {
|
||||||
|
margin-left: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-right {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left:before,
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left:before {
|
||||||
|
right: 0;
|
||||||
|
margin-right: -12px;
|
||||||
|
border-left-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
left: 0;
|
||||||
|
margin-left: -12px;
|
||||||
|
border-right-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Printing */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
/* Prevent printers from removing background-images of controls. */
|
||||||
|
.leaflet-control {
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
}
|
10
static/img/bootstrap-icon-translate.svg
Normal file
10
static/img/bootstrap-icon-translate.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-translate" viewBox="0 0 16 16">
|
||||||
|
<!--
|
||||||
|
https://icons.getbootstrap.com/icons/translate/
|
||||||
|
Copyright (c) 2011-2023 The Bootstrap Authors
|
||||||
|
Licensed under the MIT license
|
||||||
|
https://github.com/twbs/bootstrap/blob/main/LICENSE
|
||||||
|
-->
|
||||||
|
<path d="M4.545 6.714 4.11 8H3l1.862-5h1.284L8 8H6.833l-.435-1.286H4.545zm1.634-.736L5.5 3.956h-.049l-.679 2.022H6.18z"/>
|
||||||
|
<path d="M0 2a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v3h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-3H2a2 2 0 0 1-2-2V2zm2-1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H2zm7.138 9.995c.193.301.402.583.63.846-.748.575-1.673 1.001-2.768 1.292.178.217.451.635.555.867 1.125-.359 2.08-.844 2.886-1.494.777.665 1.739 1.165 2.93 1.472.133-.254.414-.673.629-.89-1.125-.253-2.057-.694-2.82-1.284.681-.747 1.222-1.651 1.621-2.757H14V8h-3v1.047h.765c-.318.844-.74 1.546-1.272 2.13a6.066 6.066 0 0 1-.415-.492 1.988 1.988 0 0 1-.94.31z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1,004 B |
BIN
static/img/leaflet-marker-blue.png
Normal file
BIN
static/img/leaflet-marker-blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
static/img/leaflet-marker-green.png
Normal file
BIN
static/img/leaflet-marker-green.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
static/img/leaflet-marker-red.png
Normal file
BIN
static/img/leaflet-marker-red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
static/img/leaflet-marker-shadow.png
Normal file
BIN
static/img/leaflet-marker-shadow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 608 B |
7
static/js/bootstrap.bundle.min.js
vendored
Normal file
7
static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/js/bootstrap.bundle.min.js.map
Normal file
1
static/js/bootstrap.bundle.min.js.map
Normal file
File diff suppressed because one or more lines are too long
6
static/js/leaflet.js
Normal file
6
static/js/leaflet.js
Normal file
File diff suppressed because one or more lines are too long
1
static/js/leaflet.js.map
Normal file
1
static/js/leaflet.js.map
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue