Sebastiankay commited on
Commit
3af080a
·
verified ·
1 Parent(s): 295ec9d

Upload 118 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +26 -0
  2. data.json +3 -0
  3. server.py +71 -0
  4. static/Woods.svg +0 -0
  5. static/audio.html +103 -0
  6. static/bootstrap-catppuccin-mocha.css +0 -0
  7. static/bootstrap-dark.min.css +0 -0
  8. static/favicon.ico +0 -0
  9. static/images/image-1.png +3 -0
  10. static/images/image-2.png +3 -0
  11. static/index.html +16 -0
  12. static/js/leaflet-control-raid-info.js +63 -0
  13. static/js/leaflet.controlCoordinates.js +356 -0
  14. static/js/leaflet.controlGroupedlayer.js +572 -0
  15. static/leaflet-control-groupedlayer.js +571 -0
  16. static/maps/customs-2d.jpg +3 -0
  17. static/maps/customs-2d_thumb.jpg +0 -0
  18. static/maps/customs-3d.jpg +3 -0
  19. static/maps/customs-3d_thumb.jpg +0 -0
  20. static/maps/factory-2d.jpg +3 -0
  21. static/maps/factory-2d_thumb.jpg +0 -0
  22. static/maps/ground-zero-2d.jpg +3 -0
  23. static/maps/ground-zero-2d_thumb.jpg +0 -0
  24. static/maps/ground-zero-3d.jpg +3 -0
  25. static/maps/ground-zero-3d_thumb.jpg +0 -0
  26. static/maps/interactive/container_buried-barrel-cache.png +0 -0
  27. static/maps/interactive/container_cash-register.png +0 -0
  28. static/maps/interactive/container_crate.png +0 -0
  29. static/maps/interactive/container_dead-scav.png +0 -0
  30. static/maps/interactive/container_drawer.png +0 -0
  31. static/maps/interactive/container_duffle-bag.png +0 -0
  32. static/maps/interactive/container_festive-airdrop-supply-crate.png +0 -0
  33. static/maps/interactive/container_grenade-box.png +0 -0
  34. static/maps/interactive/container_ground-cache.png +0 -0
  35. static/maps/interactive/container_jacket.png +0 -0
  36. static/maps/interactive/container_medbag-smu06.png +0 -0
  37. static/maps/interactive/container_medcase.png +0 -0
  38. static/maps/interactive/container_pc-block.png +0 -0
  39. static/maps/interactive/container_plastic-suitcase.png +0 -0
  40. static/maps/interactive/container_safe.png +0 -0
  41. static/maps/interactive/container_toolbox.png +0 -0
  42. static/maps/interactive/container_weapon-box.png +0 -0
  43. static/maps/interactive/container_wooden-ammo-box.png +0 -0
  44. static/maps/interactive/container_wooden-crate.png +0 -0
  45. static/maps/interactive/extract_pmc.png +0 -0
  46. static/maps/interactive/extract_scav.png +0 -0
  47. static/maps/interactive/extract_shared.png +0 -0
  48. static/maps/interactive/extract_transit.png +0 -0
  49. static/maps/interactive/hazard.png +0 -0
  50. static/maps/interactive/hazard_mortar.png +0 -0
.gitattributes CHANGED
@@ -33,3 +33,29 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ data.json filter=lfs diff=lfs merge=lfs -text
37
+ static/images/image-1.png filter=lfs diff=lfs merge=lfs -text
38
+ static/images/image-2.png filter=lfs diff=lfs merge=lfs -text
39
+ static/maps/customs-2d.jpg filter=lfs diff=lfs merge=lfs -text
40
+ static/maps/customs-3d.jpg filter=lfs diff=lfs merge=lfs -text
41
+ static/maps/factory-2d.jpg filter=lfs diff=lfs merge=lfs -text
42
+ static/maps/ground-zero-2d.jpg filter=lfs diff=lfs merge=lfs -text
43
+ static/maps/ground-zero-3d.jpg filter=lfs diff=lfs merge=lfs -text
44
+ static/maps/interchange-2d.jpg filter=lfs diff=lfs merge=lfs -text
45
+ static/maps/labs-2d.jpg filter=lfs diff=lfs merge=lfs -text
46
+ static/maps/lighthouse-2d-landscape.jpg filter=lfs diff=lfs merge=lfs -text
47
+ static/maps/lighthouse-2d.jpg filter=lfs diff=lfs merge=lfs -text
48
+ static/maps/lighthouse-3d.jpg filter=lfs diff=lfs merge=lfs -text
49
+ static/maps/openworld-2d.jpg filter=lfs diff=lfs merge=lfs -text
50
+ static/maps/reserve-2d-tunnels.jpg filter=lfs diff=lfs merge=lfs -text
51
+ static/maps/reserve-2d.jpg filter=lfs diff=lfs merge=lfs -text
52
+ static/maps/reserve-3d.jpg filter=lfs diff=lfs merge=lfs -text
53
+ static/maps/shoreline-2d.jpg filter=lfs diff=lfs merge=lfs -text
54
+ static/maps/shoreline-3d.jpg filter=lfs diff=lfs merge=lfs -text
55
+ static/maps/streets-2d.jpg filter=lfs diff=lfs merge=lfs -text
56
+ static/maps/streets-3d-caches.jpg filter=lfs diff=lfs merge=lfs -text
57
+ static/maps/streets-3d-lexos.jpg filter=lfs diff=lfs merge=lfs -text
58
+ static/maps/streets-3d.jpg filter=lfs diff=lfs merge=lfs -text
59
+ static/maps/woods-2d.jpg filter=lfs diff=lfs merge=lfs -text
60
+ static/maps/woods-3d.jpg filter=lfs diff=lfs merge=lfs -text
61
+ static/test.js filter=lfs diff=lfs merge=lfs -text
data.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ced7c1e4667a83ff39997575c3d7eb28dc3e8bb68fb58f28b0bf53a5f0c9702f
3
+ size 18019170
server.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
2
+ from fastapi.responses import HTMLResponse
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.templating import Jinja2Templates
5
+ import json
6
+ import uvicorn
7
+
8
+ app = FastAPI()
9
+
10
+ # Serve static files from the "static" directory
11
+ app.mount("/static", StaticFiles(directory="static"), name="static")
12
+
13
+ # Set up Jinja2 templates
14
+ templates = Jinja2Templates(directory="templates")
15
+
16
+ with open("data.json", "r", encoding="utf8") as file:
17
+ maps_data = json.load(file)
18
+
19
+
20
+ @app.get("/", response_class=HTMLResponse)
21
+ async def read_index(request: Request):
22
+ return templates.TemplateResponse("index.html", {"request": request, "maps": maps_data})
23
+
24
+
25
+ @app.get("/map/{normalized_name}", response_class=HTMLResponse)
26
+ async def get_map_by_normalized_name(request: Request, normalized_name: str):
27
+ map_entry = maps_data.get(normalized_name)
28
+ if map_entry:
29
+ filtered_maps = [map_entry]
30
+ else:
31
+ filtered_maps = []
32
+ return templates.TemplateResponse("map.html", {"request": request, "maps": filtered_maps})
33
+
34
+
35
+ class ConnectionManager:
36
+ def __init__(self):
37
+ self.active_connections: list[WebSocket] = []
38
+
39
+ async def connect(self, websocket: WebSocket):
40
+ await websocket.accept()
41
+ self.active_connections.append(websocket)
42
+
43
+ def disconnect(self, websocket: WebSocket):
44
+ self.active_connections.remove(websocket)
45
+
46
+ async def send_personal_message(self, message: str, websocket: WebSocket):
47
+ await websocket.send_text(message)
48
+
49
+ async def broadcast(self, message: str):
50
+ for connection in self.active_connections:
51
+ # print(f"Broadcast: {message}")
52
+ await connection.send_text(message)
53
+
54
+
55
+ manager = ConnectionManager()
56
+
57
+
58
+ @app.websocket("/{client_id}")
59
+ async def websocket_endpoint(websocket: WebSocket, client_id: int):
60
+ await manager.connect(websocket)
61
+ try:
62
+ while True:
63
+ data = await websocket.receive_text()
64
+ await manager.broadcast(data) # Direktes Broadcasten der Daten
65
+ except WebSocketDisconnect:
66
+ manager.disconnect(websocket)
67
+ await manager.broadcast(f"Client #{client_id} left the chat")
68
+
69
+
70
+ if __name__ == "__main__":
71
+ uvicorn.run(app, host="0.0.0.0", port=8000)
static/Woods.svg ADDED
static/audio.html ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html lang="en">
2
+ <head>
3
+ <meta charset="UTF-8" />
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
5
+ <title>A.I. Songs</title>
6
+ <link rel="stylesheet" href="/static/css/bulma.min.css" />
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css" />
8
+ <style>
9
+ .card {
10
+ /*overflow: hidden;*/
11
+ position: relative;
12
+ }
13
+ .card .card-image {
14
+ position: relative;
15
+ aspect-ratio: 1 / 1;
16
+ background-size: cover;
17
+ background-position: center;
18
+ background-size: 100%;
19
+ transition: background 0.4s ease-in-out;
20
+ }
21
+ .card .card-image-2 {
22
+ top: -40px;
23
+ left: -40px;
24
+ opacity: 0;
25
+ filter: blur(80px);
26
+ position: absolute;
27
+ width: calc(100% + 80px);
28
+ aspect-ratio: 1/1;
29
+ z-Index: -10;
30
+ transition: background 0.4s ease-in-out, filter 0.4s ease-out, opacity 0.4s ease-out;
31
+ }
32
+
33
+ .card:hover .card-image {
34
+ background-size: 110%;
35
+ }
36
+ .card:hover .card-image-2 {
37
+ background-size: 110%;
38
+ opacity: 0.2;
39
+ }
40
+
41
+ </style>
42
+ <script>
43
+ function copyURL(url) {
44
+ const encodedURL = encodeURI(url)
45
+ navigator.clipboard.writeText(encodedURL).then(() => {
46
+ alert("URL copied to clipboard!")
47
+ })
48
+ }
49
+ </script>
50
+ </head>
51
+ <body data-theme="dark">
52
+ <section class="section">
53
+ <div class="container">
54
+ <h1 class="title">Hier eine Quelle für (bald) alle von meinen A.I. Songs.</h1>
55
+ <p class="subtitle">Bisher noch alles unsortiert und nicht <strong>Schön</strong>!</p>
56
+
57
+ <div class="columns is-multiline">
58
+ <div class="column is-two-fifths">
59
+ <div class="card">
60
+ <div class="card-image" style="background-image: url(/static/images/image-2.png);"></div>
61
+ <div class="card-image-2" style="background-image: url(/static/images/image-2.png);"></div>
62
+ <div class="card-content">
63
+ <div class="media">
64
+ <div class="media-content">
65
+ <p class="title is-4">Ein neuer Zyklus (2025) (ist nur Computer-Code)</p>
66
+ </div>
67
+ </div>
68
+ <div class="content">
69
+ <p><strong>Genre:</strong> Blues, Soul</p>
70
+ <p><strong>Duration:</strong> 8:49</p>
71
+ <a class="button is-small is-primary" href="http://sebastiankay-files.hf.space/getfile/Ein neuer Zyklus (2025) (ist nur Computer-Code)(320k).mp3">Download</a>
72
+ <button class="button is-small" onclick="copyURL('http://sebastiankay-files.hf.space/getfile/Ein neuer Zyklus (2025) (ist nur Computer-Code)(320k).mp3')">Copy URL</button>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+
78
+ <div class="column is-two-fifths">
79
+ <div class="card">
80
+ <div class="card-image" style="background-image: url(/static/images/image-1.png);"></div>
81
+ <div class="card-image-2" style="background-image: url(/static/images/image-1.png);"></div>
82
+ <div class="card-content">
83
+ <div class="media">
84
+ <div class="media-content">
85
+ <p class="title is-4">Marco, König von Hagen (Finale Master)</p>
86
+ </div>
87
+ </div>
88
+ <div class="content">
89
+ <p><strong>Genre:</strong> Gregorian Chant, Techno, Trance</p>
90
+ <p><strong>Duration:</strong> 5:49</p>
91
+ <a class="button is-small is-primary" href="http://sebastiankay-files.hf.space/getfile/Marco, König von Hagen Finale (Master Full).wav">Download</a>
92
+ <button class="button is-small" onclick="copyURL('http://sebastiankay-files.hf.space/getfile/Marco, König von Hagen Finale (Master Full).wav')">Copy URL</button>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </section>
102
+ </body>
103
+ </html>
static/bootstrap-catppuccin-mocha.css ADDED
The diff for this file is too large to render. See raw diff
 
static/bootstrap-dark.min.css ADDED
The diff for this file is too large to render. See raw diff
 
static/favicon.ico ADDED
static/images/image-1.png ADDED

Git LFS Details

  • SHA256: 830f970ae23af872a8f267c1e4e867bcf311c1dfe8a5a7b6663351d8f7d949de
  • Pointer size: 131 Bytes
  • Size of remote file: 354 kB
static/images/image-2.png ADDED

Git LFS Details

  • SHA256: 03e2b09865b2dfc6c9b15bd158a66e6b0c2727a7999be34aab734b2923024fdd
  • Pointer size: 131 Bytes
  • Size of remote file: 181 kB
static/index.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FastAPI Maps</title>
7
+ </head>
8
+ <body>
9
+ <h1>Maps Data</h1>
10
+ <ul>
11
+ {% for map in maps %}
12
+ <li>{{ map.normalizedName }}</li>
13
+ {% endfor %}
14
+ </ul>
15
+ </body>
16
+ </html>
static/js/leaflet-control-raid-info.js ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import L from 'leaflet';
2
+
3
+ import { formattedTarkovTime } from '../components/Time.jsx';
4
+
5
+ let updateTimeInterval = false;
6
+
7
+ L.Control.RaidInfo = L.Control.extend({
8
+ onAdd: function(map) {
9
+ clearInterval(updateTimeInterval);
10
+ const wrapper = L.DomUtil.create('div');
11
+ wrapper.classList.add('time-wrapper', 'leaflet-control-raid-info');
12
+ if (this.options.map.normalizedName !== 'the-lab') {
13
+ const timeLeftDiv = L.DomUtil.create('div');
14
+ const timeRightDiv = L.DomUtil.create('div');
15
+
16
+ if (this.options.map.normalizedName === 'factory' || this.options.map.normalizedName === 'night-factory') {
17
+ timeLeftDiv.innerText = '15:28:00';
18
+ timeRightDiv.innerText = '03:28:00';
19
+ } else {
20
+ const updateTimes = () => {
21
+ timeLeftDiv.innerText = formattedTarkovTime(true);
22
+ timeRightDiv.innerText = formattedTarkovTime(false);
23
+ };
24
+ updateTimeInterval = setInterval(updateTimes, 50);
25
+ }
26
+
27
+ wrapper.append(timeLeftDiv);
28
+ wrapper.append(timeRightDiv);
29
+ }
30
+ const duration = L.DomUtil.create('div');
31
+ const durLabel = this.options.durationLabel ? this.options.durationLabel : 'Duration';
32
+ duration.innerText = `${durLabel}: ${this.options.map.duration}`;
33
+ wrapper.append(duration);
34
+
35
+ const players = L.DomUtil.create('div');
36
+ const playersLabel = this.options.playersLabel ? this.options.playersLabel : 'Players';
37
+ players.innerText = `${playersLabel}: ${this.options.map.players}`;
38
+ wrapper.append(players);
39
+
40
+ if (this.options.map.author) {
41
+ const author = L.DomUtil.create('div');
42
+ const byLabel = this.options.byLabel ? this.options.byLabel : 'By';
43
+ author.innerText = `${byLabel}: `;
44
+ const authorLink = L.DomUtil.create('a');
45
+ authorLink.setAttribute('href', this.options.map.authorLink);
46
+ authorLink.setAttribute('target', '_blank');
47
+ authorLink.setAttribute('re', 'noopener noreferrer');
48
+ authorLink.innerText = this.options.map.author;
49
+ author.append(authorLink);
50
+ wrapper.append(author);
51
+ }
52
+
53
+ return wrapper;
54
+ },
55
+
56
+ onRemove: function(map) {
57
+ // Nothing to do here
58
+ }
59
+ });
60
+
61
+ L.control.raidInfo = function(opts) {
62
+ return new L.Control.RaidInfo(opts);
63
+ }
static/js/leaflet.controlCoordinates.js ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * L.Control.Coordinates is used for displaying current mouse coordinates on the map.
3
+ */
4
+
5
+ // credit https://github.com/Low-power/Leaflet.Coordinates/tree/wrap-coordinate-option
6
+
7
+ // import L from 'leaflet';
8
+
9
+ L.Control.Coordinates = L.Control.extend({
10
+ options: {
11
+ position: 'bottomright',
12
+ //decimals used if not using DMS or labelFormatter functions
13
+ decimals: 4,
14
+ //decimalseperator used if not using DMS or labelFormatter functions
15
+ decimalSeperator: ".",
16
+ //label templates for usage if no labelFormatter function is defined
17
+ labelTemplateLat: "Lat: {y}",
18
+ labelTemplateLng: "Lng: {x}",
19
+ //label formatter functions
20
+ labelFormatterLat: undefined,
21
+ labelFormatterLng: undefined,
22
+ //switch on/off input fields on click
23
+ enableUserInput: true,
24
+ //use Degree-Minute-Second
25
+ useDMS: false,
26
+ //should coordinate be wrapped as on earth
27
+ wrapCoordinate: true,
28
+ //if true lat-lng instead of lng-lat label ordering is used
29
+ useLatLngOrder: false,
30
+ //if true user given coordinates are centered directly
31
+ centerUserCoordinates: false,
32
+ //leaflet marker type
33
+ markerType: L.marker,
34
+ //leaflet marker properties
35
+ markerProps: {}
36
+ },
37
+
38
+ onAdd: function(map) {
39
+ this._map = map;
40
+
41
+ var className = 'leaflet-control-coordinates',
42
+ container = this._container = L.DomUtil.create('div', className),
43
+ options = this.options;
44
+
45
+ //label containers
46
+ this._labelcontainer = L.DomUtil.create("div", "uiElement label", container);
47
+ this._label = L.DomUtil.create("span", "labelFirst", this._labelcontainer);
48
+
49
+
50
+ //input containers
51
+ this._inputcontainer = L.DomUtil.create("div", "uiElement input uiHidden", container);
52
+ var xSpan, ySpan;
53
+ if (options.useLatLngOrder) {
54
+ ySpan = L.DomUtil.create("span", "", this._inputcontainer);
55
+ this._inputY = this._createInput("inputY", this._inputcontainer);
56
+ xSpan = L.DomUtil.create("span", "", this._inputcontainer);
57
+ this._inputX = this._createInput("inputX", this._inputcontainer);
58
+ } else {
59
+ xSpan = L.DomUtil.create("span", "", this._inputcontainer);
60
+ this._inputX = this._createInput("inputX", this._inputcontainer);
61
+ ySpan = L.DomUtil.create("span", "", this._inputcontainer);
62
+ this._inputY = this._createInput("inputY", this._inputcontainer);
63
+ }
64
+ xSpan.innerHTML = options.labelTemplateLng.replace("{x}", "");
65
+ ySpan.innerHTML = options.labelTemplateLat.replace("{y}", "");
66
+
67
+ L.DomEvent.on(this._inputX, 'keyup', this._handleKeypress, this);
68
+ L.DomEvent.on(this._inputY, 'keyup', this._handleKeypress, this);
69
+
70
+ //connect to mouseevents
71
+ map.on("mousemove", this._update, this);
72
+ map.on('dragstart', this.collapse, this);
73
+
74
+ map.whenReady(this._update, this);
75
+
76
+ this._showsCoordinates = true;
77
+ //wether or not to show inputs on click
78
+ if (options.enableUserInput) {
79
+ L.DomEvent.addListener(this._container, "click", this._switchUI, this);
80
+ }
81
+
82
+ return container;
83
+ },
84
+
85
+ /**
86
+ * Creates an input HTML element in given container with given classname
87
+ */
88
+ _createInput: function(classname, container) {
89
+ var input = L.DomUtil.create("input", classname, container);
90
+ input.type = "text";
91
+ L.DomEvent.disableClickPropagation(input);
92
+ return input;
93
+ },
94
+
95
+ _clearMarker: function() {
96
+ this._map.removeLayer(this._marker);
97
+ },
98
+
99
+ /**
100
+ * Called onkeyup of input fields
101
+ */
102
+ _handleKeypress: function(e) {
103
+ switch (e.keyCode) {
104
+ case 27: //Esc
105
+ this.collapse();
106
+ break;
107
+ case 13: //Enter
108
+ this._handleSubmit();
109
+ this.collapse();
110
+ break;
111
+ default: //All keys
112
+ this._handleSubmit();
113
+ break;
114
+ }
115
+ },
116
+
117
+ /**
118
+ * Called on each keyup except ESC
119
+ */
120
+ _handleSubmit: function() {
121
+ var x = L.NumberFormatter.createValidNumber(this._inputX.value, this.options.decimalSeperator);
122
+ var y = L.NumberFormatter.createValidNumber(this._inputY.value, this.options.decimalSeperator);
123
+ if (x !== undefined && y !== undefined) {
124
+ var marker = this._marker;
125
+ if (!marker) {
126
+ marker = this._marker = this._createNewMarker();
127
+ marker.on("click", this._clearMarker, this);
128
+ }
129
+ var ll = new L.LatLng(y, x);
130
+ marker.setLatLng(ll);
131
+ marker.addTo(this._map);
132
+ if (this.options.centerUserCoordinates) {
133
+ this._map.setView(ll, this._map.getZoom());
134
+ }
135
+ }
136
+ },
137
+
138
+ /**
139
+ * Shows inputs fields
140
+ */
141
+ expand: function() {
142
+ this._showsCoordinates = false;
143
+
144
+ this._map.off("mousemove", this._update, this);
145
+
146
+ L.DomEvent.addListener(this._container, "mousemove", L.DomEvent.stop);
147
+ L.DomEvent.removeListener(this._container, "click", this._switchUI, this);
148
+
149
+ L.DomUtil.addClass(this._labelcontainer, "uiHidden");
150
+ L.DomUtil.removeClass(this._inputcontainer, "uiHidden");
151
+ },
152
+
153
+ /**
154
+ * Creates the label according to given options and formatters
155
+ */
156
+ _createCoordinateLabel: function(ll) {
157
+ var opts = this.options,
158
+ x, y;
159
+ if (opts.customLabelFcn) {
160
+ return opts.customLabelFcn(ll, opts);
161
+ }
162
+ if (opts.labelFormatterLng) {
163
+ x = opts.labelFormatterLng(ll.lng);
164
+ } else {
165
+ x = L.Util.template(opts.labelTemplateLng, {
166
+ x: this._getNumber(ll.lng, opts)
167
+ });
168
+ }
169
+ if (opts.labelFormatterLat) {
170
+ y = opts.labelFormatterLat(ll.lat);
171
+ } else {
172
+ y = L.Util.template(opts.labelTemplateLat, {
173
+ y: this._getNumber(ll.lat, opts)
174
+ });
175
+ }
176
+ if (opts.useLatLngOrder) {
177
+ return y + " " + x;
178
+ }
179
+ return x + " " + y;
180
+ },
181
+
182
+ /**
183
+ * Returns a Number according to options (DMS or decimal)
184
+ */
185
+ _getNumber: function(n, opts) {
186
+ var res;
187
+ if (opts.useDMS) {
188
+ res = L.NumberFormatter.toDMS(n);
189
+ } else {
190
+ res = L.NumberFormatter.round(n, opts.decimals, opts.decimalSeperator);
191
+ }
192
+ return res;
193
+ },
194
+
195
+ /**
196
+ * Shows coordinate labels after user input has ended. Also
197
+ * displays a marker with popup at the last input position.
198
+ */
199
+ collapse: function() {
200
+ if (!this._showsCoordinates) {
201
+ this._map.on("mousemove", this._update, this);
202
+ this._showsCoordinates = true;
203
+ //var opts = this.options;
204
+ L.DomEvent.addListener(this._container, "click", this._switchUI, this);
205
+ L.DomEvent.removeListener(this._container, "mousemove", L.DomEvent.stop);
206
+
207
+ L.DomUtil.addClass(this._inputcontainer, "uiHidden");
208
+ L.DomUtil.removeClass(this._labelcontainer, "uiHidden");
209
+
210
+ if (this._marker) {
211
+ var m = this._createNewMarker(),
212
+ ll = this._marker.getLatLng();
213
+ m.setLatLng(ll);
214
+
215
+ var container = L.DomUtil.create("div", "");
216
+ var label = L.DomUtil.create("div", "", container);
217
+ label.innerHTML = this._ordinateLabel(ll);
218
+
219
+ var close = L.DomUtil.create("a", "", container);
220
+ close.innerHTML = "Remove";
221
+ close.href = "#";
222
+ var stop = L.DomEvent.stopPropagation;
223
+
224
+ L.DomEvent
225
+ .on(close, 'click', stop)
226
+ .on(close, 'mousedown', stop)
227
+ .on(close, 'dblclick', stop)
228
+ .on(close, 'click', L.DomEvent.preventDefault)
229
+ .on(close, 'click', function() {
230
+ this._map.removeLayer(m);
231
+ }, this);
232
+
233
+ m.bindPopup(container);
234
+ m.addTo(this._map);
235
+ this._map.removeLayer(this._marker);
236
+ this._marker = null;
237
+ }
238
+ }
239
+ },
240
+
241
+ /**
242
+ * Click callback for UI
243
+ */
244
+ _switchUI: function(evt) {
245
+ L.DomEvent.stop(evt);
246
+ L.DomEvent.stopPropagation(evt);
247
+ L.DomEvent.preventDefault(evt);
248
+ if (this._showsCoordinates) {
249
+ //show textfields
250
+ this.expand();
251
+ } else {
252
+ //show coordinates
253
+ this.collapse();
254
+ }
255
+ },
256
+
257
+ onRemove: function(map) {
258
+ map.off("mousemove", this._update, this);
259
+ },
260
+
261
+ /**
262
+ * Mousemove callback function updating labels and input elements
263
+ */
264
+ _update: function(evt) {
265
+ var pos = evt.latlng;
266
+ if (pos) {
267
+ var opts = this.options;
268
+ if(opts.wrapCoordinate || opts.useDMS) {
269
+ pos = pos.wrap();
270
+ }
271
+ this._currentPos = pos;
272
+ this._inputY.value = L.NumberFormatter.round(pos.lat, opts.decimals, opts.decimalSeperator);
273
+ this._inputX.value = L.NumberFormatter.round(pos.lng, opts.decimals, opts.decimalSeperator);
274
+ this._label.innerHTML = this._createCoordinateLabel(pos);
275
+ }
276
+ },
277
+
278
+ _createNewMarker: function() {
279
+ return this.options.markerType(null, this.options.markerProps);
280
+ }
281
+
282
+ });
283
+
284
+ //constructor registration
285
+ L.control.coordinates = function(options) {
286
+ return new L.Control.Coordinates(options);
287
+ };
288
+
289
+ //map init hook
290
+ L.Map.mergeOptions({
291
+ coordinateControl: false
292
+ });
293
+
294
+ L.Map.addInitHook(function() {
295
+ if (this.options.coordinateControl) {
296
+ this.coordinateControl = new L.Control.Coordinates();
297
+ this.addControl(this.coordinateControl);
298
+ }
299
+ });
300
+ L.NumberFormatter = {
301
+ round: function(num, dec, sep) {
302
+ var res = L.Util.formatNum(num, dec) + "",
303
+ numbers = res.split(".");
304
+ if (numbers[1]) {
305
+ var d = dec - numbers[1].length;
306
+ for (; d > 0; d--) {
307
+ numbers[1] += "0";
308
+ }
309
+ res = numbers.join(sep || ".");
310
+ }
311
+ return res;
312
+ },
313
+
314
+ toDMS: function(deg) {
315
+ var d = Math.floor(Math.abs(deg));
316
+ var minfloat = (Math.abs(deg) - d) * 60;
317
+ var m = Math.floor(minfloat);
318
+ var secfloat = (minfloat - m) * 60;
319
+ var s = Math.round(secfloat);
320
+ if (s === 60) {
321
+ m++;
322
+ s = "00";
323
+ }
324
+ if (m === 60) {
325
+ d++;
326
+ m = "00";
327
+ }
328
+ if (s < 10) {
329
+ s = "0" + s;
330
+ }
331
+ if (m < 10) {
332
+ m = "0" + m;
333
+ }
334
+ var dir = "";
335
+ if (deg < 0) {
336
+ dir = "-";
337
+ }
338
+ return ("" + dir + d + "&deg; " + m + "' " + s + "''");
339
+ },
340
+
341
+ createValidNumber: function(num, sep) {
342
+ if (num && num.length > 0) {
343
+ var numbers = num.split(sep || ".");
344
+ try {
345
+ var numRes = Number(numbers.join("."));
346
+ if (isNaN(numRes)) {
347
+ return undefined;
348
+ }
349
+ return numRes;
350
+ } catch (e) {
351
+ return undefined;
352
+ }
353
+ }
354
+ return undefined;
355
+ }
356
+ };
static/js/leaflet.controlGroupedlayer.js ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* global L */
2
+
3
+ let controlContainer;
4
+
5
+ // A layer control which provides for layer groupings.
6
+ // Author: Ishmael Smyrnow
7
+ L.Control.GroupedLayers = L.Control.extend({
8
+ options: {
9
+ sortLayers: true,
10
+ sortGroups: true,
11
+ sortBaseLayers: false,
12
+ collapsed: true,
13
+ position: 'topright',
14
+ autoZIndex: true,
15
+ exclusiveGroups: [],
16
+ exclusiveOptionalGroups: [],
17
+ groupCheckboxes: false,
18
+ groupsCollapsable: false,
19
+ groupsExpandedClass: "leaflet-control-layers-group-collapse-default",
20
+ groupsCollapsedClass: "leaflet-control-layers-group-expand-default",
21
+ sortFunction: function (nameA, nameB) {
22
+ return nameA.localeCompare(nameB);
23
+ }
24
+ },
25
+
26
+ initialize: function (baseLayers, groupedOverlays, options) {
27
+ var i, j;
28
+ L.Util.setOptions(this, options);
29
+
30
+ this._layers = [];
31
+ this._lastZIndex = 0;
32
+ this._handlingClick = false;
33
+ this._groupList = [];
34
+ this._domGroups = [];
35
+
36
+ for (i in baseLayers) {
37
+ this._addLayer(baseLayers[i], i);
38
+ }
39
+
40
+ for (i in groupedOverlays) {
41
+ for (j in groupedOverlays[i]) {
42
+ this._addLayer(groupedOverlays[i][j], j, i, true);
43
+ }
44
+ }
45
+ },
46
+
47
+ onAdd: function (map) {
48
+ this._initLayout();
49
+ this._update();
50
+
51
+ map
52
+ .on('layeradd', this._onLayerChange, this)
53
+ .on('layerremove', this._onLayerChange, this);
54
+
55
+ return this._container;
56
+ },
57
+
58
+ addTo: function (map) {
59
+ L.Control.prototype.addTo.call(this, map);
60
+ // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
61
+ return this._expandIfNotCollapsed();
62
+ },
63
+
64
+ onRemove: function (map) {
65
+ map
66
+ .off('layeradd', this._onLayerChange, this)
67
+ .off('layerremove', this._onLayerChange, this);
68
+ },
69
+
70
+ addBaseLayer: function (layer, name) {
71
+ this._addLayer(layer, name);
72
+ this._update();
73
+ return this;
74
+ },
75
+
76
+ addOverlay: function (layer, name, options) {
77
+ this._addLayer(layer, name, {...options, overlay: true});
78
+ this._update();
79
+ return this;
80
+ },
81
+
82
+ removeLayer: function (layer) {
83
+ var id = L.Util.stamp(layer);
84
+ var _layer = this._getLayer(id);
85
+ if (_layer) {
86
+ this._layers.splice(this._layers.indexOf(_layer), 1);
87
+ }
88
+ this._update();
89
+ return this;
90
+ },
91
+
92
+ _getLayer: function (id) {
93
+ for (var layer of this._layers) {
94
+ if (layer && L.stamp(layer.layer) === id) {
95
+ return layer;
96
+ }
97
+ }
98
+ },
99
+
100
+ _initLayout: function () {
101
+ var className = 'leaflet-control-layers',
102
+ container = this._container = L.DomUtil.create('div', className),
103
+ collapsed = this.options.collapsed;
104
+
105
+ // Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
106
+ container.setAttribute('aria-haspopup', true);
107
+
108
+ L.DomEvent.disableClickPropagation(container);
109
+ L.DomEvent.disableScrollPropagation(container);
110
+
111
+ var form = this._form = L.DomUtil.create('form', className + '-list');
112
+
113
+ if (collapsed) {
114
+ this._map.on('click', this._collapse, this);
115
+
116
+ if (!L.Browser.android) {
117
+ L.DomEvent.on(container, {
118
+ mouseenter: this._expand,
119
+ mouseleave: this._collapse
120
+ }, this);
121
+ }
122
+ }
123
+
124
+ var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
125
+ link.href = '#';
126
+ link.title = 'Layers';
127
+
128
+ if (L.Browser.touch) {
129
+ L.DomEvent.on(link, 'click', L.DomEvent.stop);
130
+ L.DomEvent.on(link, 'click', this._expand, this);
131
+ } else {
132
+ L.DomEvent.on(link, 'focus', this._expand, this);
133
+ }
134
+
135
+ if (!collapsed) {
136
+ this._expand();
137
+ }
138
+
139
+ this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
140
+ this._separator = L.DomUtil.create('div', className + '-separator', form);
141
+ this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
142
+
143
+ container.appendChild(form);
144
+
145
+ controlContainer = container;
146
+ },
147
+
148
+ _addLayer: function (layer, name, options) {
149
+ options = {...options};
150
+ if (options.image) {
151
+ //console.log(options)
152
+ name = `<img src="/static/maps/interactive/${layer.key}.png" class='control-item-image' /> ${name}`;
153
+ }
154
+
155
+ var _layer = {
156
+ layer: layer,
157
+ name: name,
158
+ overlay: options.overlay,
159
+ key: options.layerKey,
160
+ };
161
+ this._layers.push(_layer);
162
+
163
+ const group = options.groupName || '';
164
+ var groupId = this._indexOf(this._groupList, group);
165
+
166
+ if (groupId === -1) {
167
+ groupId = this._groupList.push(group) - 1;
168
+ }
169
+
170
+ var exclusive = (this._indexOf(this.options.exclusiveGroups, group) !== -1);
171
+ var exclusiveOptional = (this._indexOf(this.options.exclusiveOptionalGroups, group) !== -1);
172
+
173
+ _layer.group = {
174
+ name: group,
175
+ id: groupId,
176
+ exclusive: exclusive,
177
+ exclusiveOptional: exclusiveOptional,
178
+ key: options.groupKey,
179
+ collapsed: options.groupCollapsed,
180
+ };
181
+
182
+ if (this.options.autoZIndex && layer.setZIndex) {
183
+ this._lastZIndex++;
184
+ layer.setZIndex(this._lastZIndex);
185
+ }
186
+
187
+ if (this.options.sortLayers) {
188
+ this._layers.sort(L.bind(function (a, b) {
189
+ if (a.overlay === true && b.overlay === true) {
190
+ return this.options.sortFunction(a.name, b.name);
191
+ }
192
+ }, this));
193
+ }
194
+
195
+ if (this.options.sortBaseLayers) {
196
+ this._layers.sort(L.bind(function (a, b) {
197
+ if (a.overlay === undefined && b.overlay === undefined) {
198
+ return this.options.sortFunction(a.name, b.name);
199
+ }
200
+ }, this));
201
+ }
202
+
203
+ if (this.options.sortGroups) {
204
+ this._layers.sort(L.bind(function (a, b) {
205
+ if (a.group.exclusiveOptional && !b.group.exclusiveOptional) {
206
+ return -1;
207
+ }
208
+ if (!a.group.exclusiveOptional && b.group.exclusiveOptional) {
209
+ return 1;
210
+ }
211
+ return this.options.sortFunction(a.group.name, b.group.name);
212
+ }, this));
213
+ }
214
+
215
+ this._expandIfNotCollapsed();
216
+ },
217
+
218
+ _update: function () {
219
+ if (!this._container) {
220
+ return;
221
+ }
222
+
223
+ this._baseLayersList.innerHTML = '';
224
+ this._overlaysList.innerHTML = '';
225
+ this._domGroups.length = 0;
226
+
227
+ var baseLayersPresent = false,
228
+ overlaysPresent = false;
229
+
230
+ for (var obj of this._layers) {
231
+ this._addItem(obj);
232
+ overlaysPresent = overlaysPresent || obj.overlay;
233
+ baseLayersPresent = baseLayersPresent || !obj.overlay;
234
+ }
235
+
236
+ if (this.options.groupCheckboxes) {
237
+ this._refreshGroupsCheckStates();
238
+ }
239
+
240
+ this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
241
+ },
242
+
243
+ _onLayerChange: function (e) {
244
+ var obj = this._getLayer(L.Util.stamp(e.layer)),
245
+ type;
246
+
247
+ if (!obj) {
248
+ return;
249
+ }
250
+
251
+ if (!this._handlingClick) {
252
+ this._update();
253
+ }
254
+
255
+ if (obj.overlay) {
256
+ type = e.type === 'layeradd' ? 'overlayadd' : 'overlayremove';
257
+ } else {
258
+ type = e.type === 'layeradd' ? 'baselayerchange' : null;
259
+ }
260
+
261
+ if (type) {
262
+ this._map.fire(type, obj);
263
+ }
264
+ },
265
+
266
+ _createRadioElement: function (name, checked) {
267
+ var radio = document.createElement('input');
268
+ radio.type = 'radio';
269
+ radio.name = name;
270
+ radio.className = 'leaflet-control-layers-selector';
271
+ radio.checked = checked;
272
+
273
+ return radio;
274
+ },
275
+
276
+ _createExclusiveCheckElement: function (name, checked) {
277
+ var radio = document.createElement('input');
278
+ radio.type = 'checkbox';
279
+ radio.name = name;
280
+ radio.className = `leaflet-control-layers-selector ${name}`;
281
+ radio.checked = checked;
282
+ radio.onchange = e => {
283
+ if (!e.target.checked) {
284
+ return;
285
+ }
286
+ var radios = document.getElementsByClassName(name);
287
+ for (var r of radios) {
288
+ if (r === e.target || !r.checked) {
289
+ continue;
290
+ }
291
+ r.checked = false;
292
+ this._onInputClick();
293
+ }
294
+ };
295
+
296
+ return radio;
297
+ },
298
+
299
+ _addItem: function (obj) {
300
+ var label = document.createElement('label'),
301
+ input,
302
+ checked = this._map.hasLayer(obj.layer),
303
+ container,
304
+ groupRadioName;
305
+
306
+ if (obj.overlay) {
307
+ if (obj.group.exclusive) {
308
+ groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id;
309
+ input = this._createRadioElement(groupRadioName, checked);
310
+ } else if (obj.group.exclusiveOptional) {
311
+ groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id;
312
+ input = this._createExclusiveCheckElement(groupRadioName, checked);
313
+ } else {
314
+ input = document.createElement('input');
315
+ input.type = 'checkbox';
316
+ input.className = 'leaflet-control-layers-selector';
317
+ input.defaultChecked = checked;
318
+ }
319
+ } else {
320
+ input = this._createRadioElement('leaflet-base-layers', checked);
321
+ }
322
+
323
+ input.layerId = L.Util.stamp(obj.layer);
324
+ input.groupID = obj.group.id;
325
+ L.DomEvent.on(input, 'click', this._onInputClick, this);
326
+ L.DomEvent.on(input, 'click', () => {
327
+ if (obj.layer.key && !obj.layer.options.overlay) {
328
+ controlContainer.dispatchEvent(new CustomEvent('layerToggle', {
329
+ bubbles: false,
330
+ detail: {
331
+ key: obj.layer.key,
332
+ checked: input.checked,
333
+ },
334
+ }));
335
+ } else if (obj.layer.options.overlay) {
336
+ controlContainer.dispatchEvent(new CustomEvent('overlayToggle', {
337
+ bubbles: false,
338
+ detail: {
339
+ key: obj.layer.options.name,
340
+ checked: input.checked,
341
+ },
342
+ }));
343
+ }
344
+ }, this);
345
+
346
+ var name = document.createElement('span');
347
+ name.innerHTML = ' ' + obj.name;
348
+
349
+ label.appendChild(input);
350
+ label.appendChild(name);
351
+
352
+ if (obj.overlay) {
353
+ container = this._overlaysList;
354
+
355
+ var groupContainer = this._domGroups[obj.group.id];
356
+
357
+ // Create the group container if it doesn't exist
358
+ if (!groupContainer) {
359
+ groupContainer = document.createElement('div');
360
+ groupContainer.className = 'leaflet-control-layers-group';
361
+ groupContainer.id = 'leaflet-control-layers-group-' + obj.group.id;
362
+ groupContainer.dataset.key = obj.group.key;
363
+
364
+ var groupLabel = document.createElement('label');
365
+ groupLabel.className = 'leaflet-control-layers-group-label';
366
+
367
+ if (obj.group.name !== '' && !obj.group.exclusive && !obj.group.exclusiveOptional) {
368
+ // ------ add a group checkbox with an _onInputClickGroup function
369
+ if (this.options.groupCheckboxes) {
370
+ var groupInput = document.createElement('input');
371
+ groupInput.type = 'checkbox';
372
+ groupInput.className = 'leaflet-control-layers-group-selector';
373
+ groupInput.groupID = obj.group.id;
374
+ groupInput.legend = this;
375
+ groupInput.dataset.key = obj.group.key;
376
+ L.DomEvent.on(groupInput, 'click', this._onGroupInputClick, groupInput);
377
+ groupLabel.appendChild(groupInput);
378
+ }
379
+ }
380
+
381
+ if (this.options.groupsCollapsable){
382
+ groupContainer.classList.add("group-collapsable");
383
+ if (obj.group.collapsed) {
384
+ groupContainer.classList.add("collapsed");
385
+ }
386
+
387
+ var groupMin = document.createElement('span');
388
+ groupMin.className = 'leaflet-control-layers-group-collapse '+this.options.groupsExpandedClass;
389
+ groupLabel.appendChild(groupMin);
390
+
391
+ var groupMax = document.createElement('span');
392
+ groupMax.className = 'leaflet-control-layers-group-expand '+this.options.groupsCollapsedClass;
393
+ groupLabel.appendChild(groupMax);
394
+
395
+ L.DomEvent.on(groupLabel, 'click', this._onGroupCollapseToggle, groupContainer);
396
+ }
397
+
398
+ var groupName = document.createElement('span');
399
+ groupName.className = 'leaflet-control-layers-group-name';
400
+ groupName.innerHTML = obj.group.name;
401
+ groupLabel.appendChild(groupName);
402
+
403
+ groupContainer.appendChild(groupLabel);
404
+ container.appendChild(groupContainer);
405
+
406
+ this._domGroups[obj.group.id] = groupContainer;
407
+ }
408
+
409
+ container = groupContainer;
410
+ } else {
411
+ container = this._baseLayersList;
412
+ }
413
+
414
+ container.appendChild(label);
415
+
416
+ return label;
417
+ },
418
+
419
+ _onGroupCollapseToggle: function (event) {
420
+ L.DomEvent.stopPropagation(event);
421
+ L.DomEvent.preventDefault(event);
422
+ if (this.classList.contains("group-collapsable") && this.classList.contains("collapsed")){
423
+ this.classList.remove("collapsed");
424
+ } else if (this.classList.contains("group-collapsable") && !this.classList.contains("collapsed")) {
425
+ this.classList.add("collapsed");
426
+ }
427
+ if (this.dataset.key) {
428
+ controlContainer.dispatchEvent(new CustomEvent('groupCollapseToggle', {
429
+ bubbles: false,
430
+ detail: {
431
+ key: this.dataset.key,
432
+ collapsed: this.classList.contains("collapsed"),
433
+ },
434
+ }));
435
+ }
436
+ },
437
+
438
+ _onGroupInputClick: function (event) {
439
+ L.DomEvent.stopPropagation(event);
440
+ var obj;
441
+
442
+ var this_legend = this.legend;
443
+ this_legend._handlingClick = true;
444
+
445
+ var inputs = this_legend._form.getElementsByTagName('input');
446
+
447
+ for (var input of inputs) {
448
+ if (input.groupID === this.groupID && input.className === 'leaflet-control-layers-selector') {
449
+ input.checked = this.checked;
450
+ obj = this_legend._getLayer(input.layerId);
451
+ if (input.checked && !this_legend._map.hasLayer(obj.layer)) {
452
+ this_legend._map.addLayer(obj.layer);
453
+ } else if (!input.checked && this_legend._map.hasLayer(obj.layer)) {
454
+ this_legend._map.removeLayer(obj.layer);
455
+ }
456
+ }
457
+ }
458
+
459
+ if (this.dataset.key) {
460
+ controlContainer.dispatchEvent(new CustomEvent('groupToggle', {
461
+ bubbles: false,
462
+ detail: {
463
+ key: this.dataset.key,
464
+ checked: this.checked,
465
+ },
466
+ }));
467
+ }
468
+
469
+ this_legend._handlingClick = false;
470
+ },
471
+
472
+ _onInputClick: function () {
473
+ var obj,
474
+ inputs = this._form.getElementsByClassName('leaflet-control-layers-selector'),
475
+ toBeRemoved,
476
+ toBeAdded;
477
+
478
+ this._handlingClick = true;
479
+
480
+ for (var input of inputs) {
481
+ obj = this._getLayer(input.layerId);
482
+ if (input.checked && !this._map.hasLayer(obj.layer)) {
483
+ toBeAdded = obj.layer;
484
+ } else if (!input.checked && this._map.hasLayer(obj.layer)) {
485
+ toBeRemoved = obj.layer;
486
+ }
487
+ }
488
+
489
+ if (toBeRemoved !== undefined) {
490
+ this._map.removeLayer(toBeRemoved);
491
+ }
492
+ if (toBeAdded !== undefined) {
493
+ this._map.addLayer(toBeAdded);
494
+ }
495
+
496
+ if (this.options.groupCheckboxes) {
497
+ this._refreshGroupsCheckStates();
498
+ }
499
+
500
+ this._handlingClick = false;
501
+ },
502
+
503
+ _refreshGroupsCheckStates: function () {
504
+ for (var i = 0; i < this._domGroups.length; i++) {
505
+ var groupContainer = this._domGroups[i];
506
+ if (groupContainer) {
507
+ var groupInput = groupContainer.getElementsByClassName('leaflet-control-layers-group-selector')[0];
508
+ var groupItemInputs = groupContainer.querySelectorAll('input.leaflet-control-layers-selector');
509
+ var checkedGroupItemInputs = groupContainer.querySelectorAll('input.leaflet-control-layers-selector:checked');
510
+
511
+ if (groupInput) {
512
+ groupInput.indeterminate = false;
513
+ if (checkedGroupItemInputs.length === groupItemInputs.length) {
514
+ groupInput.checked = true;
515
+ } else if (checkedGroupItemInputs.length === 0) {
516
+ groupInput.checked = false;
517
+ } else {
518
+ groupInput.indeterminate = true;
519
+ }
520
+ }
521
+ }
522
+ }
523
+ },
524
+
525
+ _expand: function () {
526
+ L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
527
+ this._form.style.height = null;
528
+ var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
529
+ if (acceptableHeight < this._form.clientHeight) {
530
+ L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
531
+ this._form.style.height = acceptableHeight + 'px';
532
+ } else {
533
+ L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
534
+ }
535
+
536
+ return this;
537
+ },
538
+
539
+ _expandIfNotCollapsed: function () {
540
+ if (this._map && !this.options.collapsed) {
541
+ this._expand();
542
+ }
543
+ return this;
544
+ },
545
+
546
+ _collapse: function () {
547
+ this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
548
+ },
549
+
550
+ _indexOf: function (arr, obj) {
551
+ for (var i = 0, j = arr.length; i < j; i++) {
552
+ if (arr[i] === obj) {
553
+ return i;
554
+ }
555
+ }
556
+ return -1;
557
+ },
558
+ on: (eventName, handler, options) => {
559
+ controlContainer.addEventListener(eventName, handler, options);
560
+ },
561
+ once: (eventName, handler, options) => {
562
+ controlContainer.addEventListener(eventName, handler, {...options, once: true});
563
+ },
564
+ off: (eventName, handler, options) => {
565
+ controlContainer.removeEventListener(eventName, handler, options);
566
+ },
567
+ });
568
+
569
+ L.control.groupedLayers = function (baseLayers, groupedOverlays, options) {
570
+ return new L.Control.GroupedLayers(baseLayers, groupedOverlays, options);
571
+ };
572
+
static/leaflet-control-groupedlayer.js ADDED
@@ -0,0 +1,571 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* global L */
2
+
3
+ let controlContainer;
4
+
5
+ // A layer control which provides for layer groupings.
6
+ // Author: Ishmael Smyrnow
7
+ L.Control.GroupedLayers = L.Control.extend({
8
+ options: {
9
+ sortLayers: true,
10
+ sortGroups: true,
11
+ sortBaseLayers: false,
12
+ collapsed: true,
13
+ position: 'topright',
14
+ autoZIndex: true,
15
+ exclusiveGroups: [],
16
+ exclusiveOptionalGroups: [],
17
+ groupCheckboxes: false,
18
+ groupsCollapsable: false,
19
+ groupsExpandedClass: "leaflet-control-layers-group-collapse-default",
20
+ groupsCollapsedClass: "leaflet-control-layers-group-expand-default",
21
+ sortFunction: function (nameA, nameB) {
22
+ return nameA.localeCompare(nameB);
23
+ }
24
+ },
25
+
26
+ initialize: function (baseLayers, groupedOverlays, options) {
27
+ var i, j;
28
+ L.Util.setOptions(this, options);
29
+
30
+ this._layers = [];
31
+ this._lastZIndex = 0;
32
+ this._handlingClick = false;
33
+ this._groupList = [];
34
+ this._domGroups = [];
35
+
36
+ for (i in baseLayers) {
37
+ this._addLayer(baseLayers[i], i);
38
+ }
39
+
40
+ for (i in groupedOverlays) {
41
+ for (j in groupedOverlays[i]) {
42
+ this._addLayer(groupedOverlays[i][j], j, i, true);
43
+ }
44
+ }
45
+ },
46
+
47
+ onAdd: function (map) {
48
+ this._initLayout();
49
+ this._update();
50
+
51
+ map
52
+ .on('layeradd', this._onLayerChange, this)
53
+ .on('layerremove', this._onLayerChange, this);
54
+
55
+ return this._container;
56
+ },
57
+
58
+ addTo: function (map) {
59
+ L.Control.prototype.addTo.call(this, map);
60
+ // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
61
+ return this._expandIfNotCollapsed();
62
+ },
63
+
64
+ onRemove: function (map) {
65
+ map
66
+ .off('layeradd', this._onLayerChange, this)
67
+ .off('layerremove', this._onLayerChange, this);
68
+ },
69
+
70
+ addBaseLayer: function (layer, name) {
71
+ this._addLayer(layer, name);
72
+ this._update();
73
+ return this;
74
+ },
75
+
76
+ addOverlay: function (layer, name, options) {
77
+ this._addLayer(layer, name, {...options, overlay: true});
78
+ this._update();
79
+ return this;
80
+ },
81
+
82
+ removeLayer: function (layer) {
83
+ var id = L.Util.stamp(layer);
84
+ var _layer = this._getLayer(id);
85
+ if (_layer) {
86
+ this._layers.splice(this._layers.indexOf(_layer), 1);
87
+ }
88
+ this._update();
89
+ return this;
90
+ },
91
+
92
+ _getLayer: function (id) {
93
+ for (var layer of this._layers) {
94
+ if (layer && L.stamp(layer.layer) === id) {
95
+ return layer;
96
+ }
97
+ }
98
+ },
99
+
100
+ _initLayout: function () {
101
+ var className = 'leaflet-control-layers',
102
+ container = this._container = L.DomUtil.create('div', className),
103
+ collapsed = this.options.collapsed;
104
+
105
+ // Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
106
+ container.setAttribute('aria-haspopup', true);
107
+
108
+ L.DomEvent.disableClickPropagation(container);
109
+ L.DomEvent.disableScrollPropagation(container);
110
+
111
+ var form = this._form = L.DomUtil.create('form', className + '-list');
112
+
113
+ if (collapsed) {
114
+ this._map.on('click', this._collapse, this);
115
+
116
+ if (!L.Browser.android) {
117
+ L.DomEvent.on(container, {
118
+ mouseenter: this._expand,
119
+ mouseleave: this._collapse
120
+ }, this);
121
+ }
122
+ }
123
+
124
+ var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
125
+ link.href = '#';
126
+ link.title = 'Layers';
127
+
128
+ if (L.Browser.touch) {
129
+ L.DomEvent.on(link, 'click', L.DomEvent.stop);
130
+ L.DomEvent.on(link, 'click', this._expand, this);
131
+ } else {
132
+ L.DomEvent.on(link, 'focus', this._expand, this);
133
+ }
134
+
135
+ if (!collapsed) {
136
+ this._expand();
137
+ }
138
+
139
+ this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
140
+ this._separator = L.DomUtil.create('div', className + '-separator', form);
141
+ this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
142
+
143
+ container.appendChild(form);
144
+
145
+ controlContainer = container;
146
+ },
147
+
148
+ _addLayer: function (layer, name, options) {
149
+ options = {...options};
150
+ if (options.image) {
151
+ name = `<img src="${options.image}" class='control-item-image' /> ${name}`;
152
+ }
153
+
154
+ var _layer = {
155
+ layer: layer,
156
+ name: name,
157
+ overlay: options.overlay,
158
+ key: options.layerKey,
159
+ };
160
+ this._layers.push(_layer);
161
+
162
+ const group = options.groupName || '';
163
+ var groupId = this._indexOf(this._groupList, group);
164
+
165
+ if (groupId === -1) {
166
+ groupId = this._groupList.push(group) - 1;
167
+ }
168
+
169
+ var exclusive = (this._indexOf(this.options.exclusiveGroups, group) !== -1);
170
+ var exclusiveOptional = (this._indexOf(this.options.exclusiveOptionalGroups, group) !== -1);
171
+
172
+ _layer.group = {
173
+ name: group,
174
+ id: groupId,
175
+ exclusive: exclusive,
176
+ exclusiveOptional: exclusiveOptional,
177
+ key: options.groupKey,
178
+ collapsed: options.groupCollapsed,
179
+ };
180
+
181
+ if (this.options.autoZIndex && layer.setZIndex) {
182
+ this._lastZIndex++;
183
+ layer.setZIndex(this._lastZIndex);
184
+ }
185
+
186
+ if (this.options.sortLayers) {
187
+ this._layers.sort(L.bind(function (a, b) {
188
+ if (a.overlay === true && b.overlay === true) {
189
+ return this.options.sortFunction(a.name, b.name);
190
+ }
191
+ }, this));
192
+ }
193
+
194
+ if (this.options.sortBaseLayers) {
195
+ this._layers.sort(L.bind(function (a, b) {
196
+ if (a.overlay === undefined && b.overlay === undefined) {
197
+ return this.options.sortFunction(a.name, b.name);
198
+ }
199
+ }, this));
200
+ }
201
+
202
+ if (this.options.sortGroups) {
203
+ this._layers.sort(L.bind(function (a, b) {
204
+ if (a.group.exclusiveOptional && !b.group.exclusiveOptional) {
205
+ return -1;
206
+ }
207
+ if (!a.group.exclusiveOptional && b.group.exclusiveOptional) {
208
+ return 1;
209
+ }
210
+ return this.options.sortFunction(a.group.name, b.group.name);
211
+ }, this));
212
+ }
213
+
214
+ this._expandIfNotCollapsed();
215
+ },
216
+
217
+ _update: function () {
218
+ if (!this._container) {
219
+ return;
220
+ }
221
+
222
+ this._baseLayersList.innerHTML = '';
223
+ this._overlaysList.innerHTML = '';
224
+ this._domGroups.length = 0;
225
+
226
+ var baseLayersPresent = false,
227
+ overlaysPresent = false;
228
+
229
+ for (var obj of this._layers) {
230
+ this._addItem(obj);
231
+ overlaysPresent = overlaysPresent || obj.overlay;
232
+ baseLayersPresent = baseLayersPresent || !obj.overlay;
233
+ }
234
+
235
+ if (this.options.groupCheckboxes) {
236
+ this._refreshGroupsCheckStates();
237
+ }
238
+
239
+ this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
240
+ },
241
+
242
+ _onLayerChange: function (e) {
243
+ var obj = this._getLayer(L.Util.stamp(e.layer)),
244
+ type;
245
+
246
+ if (!obj) {
247
+ return;
248
+ }
249
+
250
+ if (!this._handlingClick) {
251
+ this._update();
252
+ }
253
+
254
+ if (obj.overlay) {
255
+ type = e.type === 'layeradd' ? 'overlayadd' : 'overlayremove';
256
+ } else {
257
+ type = e.type === 'layeradd' ? 'baselayerchange' : null;
258
+ }
259
+
260
+ if (type) {
261
+ this._map.fire(type, obj);
262
+ }
263
+ },
264
+
265
+ _createRadioElement: function (name, checked) {
266
+ var radio = document.createElement('input');
267
+ radio.type = 'radio';
268
+ radio.name = name;
269
+ radio.className = 'leaflet-control-layers-selector';
270
+ radio.checked = checked;
271
+
272
+ return radio;
273
+ },
274
+
275
+ _createExclusiveCheckElement: function (name, checked) {
276
+ var radio = document.createElement('input');
277
+ radio.type = 'checkbox';
278
+ radio.name = name;
279
+ radio.className = `leaflet-control-layers-selector ${name}`;
280
+ radio.checked = checked;
281
+ radio.onchange = e => {
282
+ if (!e.target.checked) {
283
+ return;
284
+ }
285
+ var radios = document.getElementsByClassName(name);
286
+ for (var r of radios) {
287
+ if (r === e.target || !r.checked) {
288
+ continue;
289
+ }
290
+ r.checked = false;
291
+ this._onInputClick();
292
+ }
293
+ };
294
+
295
+ return radio;
296
+ },
297
+
298
+ _addItem: function (obj) {
299
+ var label = document.createElement('label'),
300
+ input,
301
+ checked = this._map.hasLayer(obj.layer),
302
+ container,
303
+ groupRadioName;
304
+
305
+ if (obj.overlay) {
306
+ if (obj.group.exclusive) {
307
+ groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id;
308
+ input = this._createRadioElement(groupRadioName, checked);
309
+ } else if (obj.group.exclusiveOptional) {
310
+ groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id;
311
+ input = this._createExclusiveCheckElement(groupRadioName, checked);
312
+ } else {
313
+ input = document.createElement('input');
314
+ input.type = 'checkbox';
315
+ input.className = 'leaflet-control-layers-selector';
316
+ input.defaultChecked = checked;
317
+ }
318
+ } else {
319
+ input = this._createRadioElement('leaflet-base-layers', checked);
320
+ }
321
+
322
+ input.layerId = L.Util.stamp(obj.layer);
323
+ input.groupID = obj.group.id;
324
+ L.DomEvent.on(input, 'click', this._onInputClick, this);
325
+ L.DomEvent.on(input, 'click', () => {
326
+ if (obj.layer.key && !obj.layer.options.overlay) {
327
+ controlContainer.dispatchEvent(new CustomEvent('layerToggle', {
328
+ bubbles: false,
329
+ detail: {
330
+ key: obj.layer.key,
331
+ checked: input.checked,
332
+ },
333
+ }));
334
+ } else if (obj.layer.options.overlay) {
335
+ controlContainer.dispatchEvent(new CustomEvent('overlayToggle', {
336
+ bubbles: false,
337
+ detail: {
338
+ key: obj.layer.options.name,
339
+ checked: input.checked,
340
+ },
341
+ }));
342
+ }
343
+ }, this);
344
+
345
+ var name = document.createElement('span');
346
+ name.innerHTML = ' ' + obj.name;
347
+
348
+ label.appendChild(input);
349
+ label.appendChild(name);
350
+
351
+ if (obj.overlay) {
352
+ container = this._overlaysList;
353
+
354
+ var groupContainer = this._domGroups[obj.group.id];
355
+
356
+ // Create the group container if it doesn't exist
357
+ if (!groupContainer) {
358
+ groupContainer = document.createElement('div');
359
+ groupContainer.className = 'leaflet-control-layers-group';
360
+ groupContainer.id = 'leaflet-control-layers-group-' + obj.group.id;
361
+ groupContainer.dataset.key = obj.group.key;
362
+
363
+ var groupLabel = document.createElement('label');
364
+ groupLabel.className = 'leaflet-control-layers-group-label';
365
+
366
+ if (obj.group.name !== '' && !obj.group.exclusive && !obj.group.exclusiveOptional) {
367
+ // ------ add a group checkbox with an _onInputClickGroup function
368
+ if (this.options.groupCheckboxes) {
369
+ var groupInput = document.createElement('input');
370
+ groupInput.type = 'checkbox';
371
+ groupInput.className = 'leaflet-control-layers-group-selector';
372
+ groupInput.groupID = obj.group.id;
373
+ groupInput.legend = this;
374
+ groupInput.dataset.key = obj.group.key;
375
+ L.DomEvent.on(groupInput, 'click', this._onGroupInputClick, groupInput);
376
+ groupLabel.appendChild(groupInput);
377
+ }
378
+ }
379
+
380
+ if (this.options.groupsCollapsable){
381
+ groupContainer.classList.add("group-collapsable");
382
+ if (obj.group.collapsed) {
383
+ groupContainer.classList.add("collapsed");
384
+ }
385
+
386
+ var groupMin = document.createElement('span');
387
+ groupMin.className = 'leaflet-control-layers-group-collapse '+this.options.groupsExpandedClass;
388
+ groupLabel.appendChild(groupMin);
389
+
390
+ var groupMax = document.createElement('span');
391
+ groupMax.className = 'leaflet-control-layers-group-expand '+this.options.groupsCollapsedClass;
392
+ groupLabel.appendChild(groupMax);
393
+
394
+ L.DomEvent.on(groupLabel, 'click', this._onGroupCollapseToggle, groupContainer);
395
+ }
396
+
397
+ var groupName = document.createElement('span');
398
+ groupName.className = 'leaflet-control-layers-group-name';
399
+ groupName.innerHTML = obj.group.name;
400
+ groupLabel.appendChild(groupName);
401
+
402
+ groupContainer.appendChild(groupLabel);
403
+ container.appendChild(groupContainer);
404
+
405
+ this._domGroups[obj.group.id] = groupContainer;
406
+ }
407
+
408
+ container = groupContainer;
409
+ } else {
410
+ container = this._baseLayersList;
411
+ }
412
+
413
+ container.appendChild(label);
414
+
415
+ return label;
416
+ },
417
+
418
+ _onGroupCollapseToggle: function (event) {
419
+ L.DomEvent.stopPropagation(event);
420
+ L.DomEvent.preventDefault(event);
421
+ if (this.classList.contains("group-collapsable") && this.classList.contains("collapsed")){
422
+ this.classList.remove("collapsed");
423
+ } else if (this.classList.contains("group-collapsable") && !this.classList.contains("collapsed")) {
424
+ this.classList.add("collapsed");
425
+ }
426
+ if (this.dataset.key) {
427
+ controlContainer.dispatchEvent(new CustomEvent('groupCollapseToggle', {
428
+ bubbles: false,
429
+ detail: {
430
+ key: this.dataset.key,
431
+ collapsed: this.classList.contains("collapsed"),
432
+ },
433
+ }));
434
+ }
435
+ },
436
+
437
+ _onGroupInputClick: function (event) {
438
+ L.DomEvent.stopPropagation(event);
439
+ var obj;
440
+
441
+ var this_legend = this.legend;
442
+ this_legend._handlingClick = true;
443
+
444
+ var inputs = this_legend._form.getElementsByTagName('input');
445
+
446
+ for (var input of inputs) {
447
+ if (input.groupID === this.groupID && input.className === 'leaflet-control-layers-selector') {
448
+ input.checked = this.checked;
449
+ obj = this_legend._getLayer(input.layerId);
450
+ if (input.checked && !this_legend._map.hasLayer(obj.layer)) {
451
+ this_legend._map.addLayer(obj.layer);
452
+ } else if (!input.checked && this_legend._map.hasLayer(obj.layer)) {
453
+ this_legend._map.removeLayer(obj.layer);
454
+ }
455
+ }
456
+ }
457
+
458
+ if (this.dataset.key) {
459
+ controlContainer.dispatchEvent(new CustomEvent('groupToggle', {
460
+ bubbles: false,
461
+ detail: {
462
+ key: this.dataset.key,
463
+ checked: this.checked,
464
+ },
465
+ }));
466
+ }
467
+
468
+ this_legend._handlingClick = false;
469
+ },
470
+
471
+ _onInputClick: function () {
472
+ var obj,
473
+ inputs = this._form.getElementsByClassName('leaflet-control-layers-selector'),
474
+ toBeRemoved,
475
+ toBeAdded;
476
+
477
+ this._handlingClick = true;
478
+
479
+ for (var input of inputs) {
480
+ obj = this._getLayer(input.layerId);
481
+ if (input.checked && !this._map.hasLayer(obj.layer)) {
482
+ toBeAdded = obj.layer;
483
+ } else if (!input.checked && this._map.hasLayer(obj.layer)) {
484
+ toBeRemoved = obj.layer;
485
+ }
486
+ }
487
+
488
+ if (toBeRemoved !== undefined) {
489
+ this._map.removeLayer(toBeRemoved);
490
+ }
491
+ if (toBeAdded !== undefined) {
492
+ this._map.addLayer(toBeAdded);
493
+ }
494
+
495
+ if (this.options.groupCheckboxes) {
496
+ this._refreshGroupsCheckStates();
497
+ }
498
+
499
+ this._handlingClick = false;
500
+ },
501
+
502
+ _refreshGroupsCheckStates: function () {
503
+ for (var i = 0; i < this._domGroups.length; i++) {
504
+ var groupContainer = this._domGroups[i];
505
+ if (groupContainer) {
506
+ var groupInput = groupContainer.getElementsByClassName('leaflet-control-layers-group-selector')[0];
507
+ var groupItemInputs = groupContainer.querySelectorAll('input.leaflet-control-layers-selector');
508
+ var checkedGroupItemInputs = groupContainer.querySelectorAll('input.leaflet-control-layers-selector:checked');
509
+
510
+ if (groupInput) {
511
+ groupInput.indeterminate = false;
512
+ if (checkedGroupItemInputs.length === groupItemInputs.length) {
513
+ groupInput.checked = true;
514
+ } else if (checkedGroupItemInputs.length === 0) {
515
+ groupInput.checked = false;
516
+ } else {
517
+ groupInput.indeterminate = true;
518
+ }
519
+ }
520
+ }
521
+ }
522
+ },
523
+
524
+ _expand: function () {
525
+ L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
526
+ this._form.style.height = null;
527
+ var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
528
+ if (acceptableHeight < this._form.clientHeight) {
529
+ L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
530
+ this._form.style.height = acceptableHeight + 'px';
531
+ } else {
532
+ L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
533
+ }
534
+
535
+ return this;
536
+ },
537
+
538
+ _expandIfNotCollapsed: function () {
539
+ if (this._map && !this.options.collapsed) {
540
+ this._expand();
541
+ }
542
+ return this;
543
+ },
544
+
545
+ _collapse: function () {
546
+ this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
547
+ },
548
+
549
+ _indexOf: function (arr, obj) {
550
+ for (var i = 0, j = arr.length; i < j; i++) {
551
+ if (arr[i] === obj) {
552
+ return i;
553
+ }
554
+ }
555
+ return -1;
556
+ },
557
+ on: (eventName, handler, options) => {
558
+ controlContainer.addEventListener(eventName, handler, options);
559
+ },
560
+ once: (eventName, handler, options) => {
561
+ controlContainer.addEventListener(eventName, handler, {...options, once: true});
562
+ },
563
+ off: (eventName, handler, options) => {
564
+ controlContainer.removeEventListener(eventName, handler, options);
565
+ },
566
+ });
567
+
568
+ L.control.groupedLayers = function (baseLayers, groupedOverlays, options) {
569
+ return new L.Control.GroupedLayers(baseLayers, groupedOverlays, options);
570
+ };
571
+
static/maps/customs-2d.jpg ADDED

Git LFS Details

  • SHA256: c83ebe14c1e8c265f528416e756a784ed73bbf83d1c66080b4e5143f29867a96
  • Pointer size: 132 Bytes
  • Size of remote file: 2.42 MB
static/maps/customs-2d_thumb.jpg ADDED
static/maps/customs-3d.jpg ADDED

Git LFS Details

  • SHA256: 8a81649efb1c94c52af631158b8f0de843b8863fde0c7492e5dd56f5b311078c
  • Pointer size: 131 Bytes
  • Size of remote file: 770 kB
static/maps/customs-3d_thumb.jpg ADDED
static/maps/factory-2d.jpg ADDED

Git LFS Details

  • SHA256: da04a119ab5afd9c1b1059ca1109d91142b347c1a49f07e0cfd46156b3f40d09
  • Pointer size: 132 Bytes
  • Size of remote file: 1.99 MB
static/maps/factory-2d_thumb.jpg ADDED
static/maps/ground-zero-2d.jpg ADDED

Git LFS Details

  • SHA256: 010566a8b5e0e63ad234556b6c9cfe7a3cdd7ef5e98317733730e44c4deec7bb
  • Pointer size: 132 Bytes
  • Size of remote file: 1.56 MB
static/maps/ground-zero-2d_thumb.jpg ADDED
static/maps/ground-zero-3d.jpg ADDED

Git LFS Details

  • SHA256: 47aed9f8d698d245457c3cfd993c4519c8acca025e0b0609b1b754b92165e4a9
  • Pointer size: 131 Bytes
  • Size of remote file: 496 kB
static/maps/ground-zero-3d_thumb.jpg ADDED
static/maps/interactive/container_buried-barrel-cache.png ADDED
static/maps/interactive/container_cash-register.png ADDED
static/maps/interactive/container_crate.png ADDED
static/maps/interactive/container_dead-scav.png ADDED
static/maps/interactive/container_drawer.png ADDED
static/maps/interactive/container_duffle-bag.png ADDED
static/maps/interactive/container_festive-airdrop-supply-crate.png ADDED
static/maps/interactive/container_grenade-box.png ADDED
static/maps/interactive/container_ground-cache.png ADDED
static/maps/interactive/container_jacket.png ADDED
static/maps/interactive/container_medbag-smu06.png ADDED
static/maps/interactive/container_medcase.png ADDED
static/maps/interactive/container_pc-block.png ADDED
static/maps/interactive/container_plastic-suitcase.png ADDED
static/maps/interactive/container_safe.png ADDED
static/maps/interactive/container_toolbox.png ADDED
static/maps/interactive/container_weapon-box.png ADDED
static/maps/interactive/container_wooden-ammo-box.png ADDED
static/maps/interactive/container_wooden-crate.png ADDED
static/maps/interactive/extract_pmc.png ADDED
static/maps/interactive/extract_scav.png ADDED
static/maps/interactive/extract_shared.png ADDED
static/maps/interactive/extract_transit.png ADDED
static/maps/interactive/hazard.png ADDED
static/maps/interactive/hazard_mortar.png ADDED