From 063d8ef9eaf874a941f4459e831057dd0a1b7ddd Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 5 Jul 2022 21:55:41 +0200 Subject: Rewrite in TS --- .gitignore | 7 +- .gitlab-ci.yml | 10 - .tmuxinator.yml | 11 - LICENSE | 24 + Makefile | 24 +- README.md | 28 +- bin/dev-server | 16 + bsconfig.json | 22 - deploy | 12 - dev | 28 - flake.lock | 42 + flake.nix | 21 + package-lock.json | 167 -- package.json | 13 - public/font-awesome-4.7.0/css/font-awesome.min.css | 4 - public/font-awesome-4.7.0/fonts/FontAwesome.otf | Bin 134808 -> 0 bytes .../fonts/fontawesome-webfont.eot | Bin 165742 -> 0 bytes .../fonts/fontawesome-webfont.svg | 2671 -------------------- .../fonts/fontawesome-webfont.ttf | Bin 165548 -> 0 bytes .../fonts/fontawesome-webfont.woff | Bin 98024 -> 0 bytes .../fonts/fontawesome-webfont.woff2 | Bin 77160 -> 0 bytes public/index.html | 30 +- public/leaflet/images/layers-2x.png | Bin 1259 -> 0 bytes public/leaflet/images/layers.png | Bin 696 -> 0 bytes public/leaflet/images/marker-icon-2x.png | Bin 2464 -> 0 bytes public/leaflet/images/marker-icon.png | Bin 1466 -> 0 bytes public/leaflet/images/marker-shadow.png | Bin 618 -> 0 bytes public/leaflet/leaflet.css | 102 +- public/leaflet/leaflet.js | 7 +- public/leaflet/leaflet.js.map | 1 - public/main.css | 143 +- rollup.config.js | 13 - shell.nix | 19 - src/Color.ml | 38 - src/Lib/CSV.ml | 76 - src/Lib/ContextMenu.ml | 40 - src/Lib/Dom/Document.ml | 20 - src/Lib/Dom/Element.ml | 51 - src/Lib/Dom/Event.ml | 17 - src/Lib/Dom/H.ml | 65 - src/Lib/Dom/HA.ml | 43 - src/Lib/Dom/HE.ml | 13 - src/Lib/Dom/History.ml | 2 - src/Lib/Dom/Location.ml | 7 - src/Lib/Dom/Window.ml | 2 - src/Lib/File.ml | 21 - src/Lib/FontAwesome.ml | 788 ------ src/Lib/Fun.ml | 2 - src/Lib/Leaflet.ml | 89 - src/Lib/Modal.ml | 25 - src/Lib/Option.ml | 9 - src/Lib/String.ml | 35 - src/Lib/URI.ml | 2 - src/Main.ml | 3 - src/State.ml | 119 - src/View/Button.ml | 19 - src/View/Form.ml | 65 - src/View/Form/Autocomplete.ml | 80 - src/View/Layout.ml | 9 - src/View/Map.ml | 131 - src/View/Map/Icon.ml | 32 - src/View/Map/Marker.ml | 105 - src/lib/autoComplete.ts | 115 + src/lib/base.ts | 32 + src/lib/button.ts | 29 + src/lib/color.ts | 36 + src/lib/contextMenu.ts | 35 + src/lib/dom.ts | 6 + src/lib/form.ts | 54 + src/lib/h.ts | 31 + src/lib/icons.ts | 66 + src/lib/layout.ts | 15 + src/lib/modal.ts | 28 + src/main.ts | 3 + src/map.ts | 131 + src/marker.ts | 171 ++ src/markerForm.ts | 116 + src/serialization.ts | 44 + src/serialization/utils.ts | 9 + src/serialization/v0.ts | 122 + src/state.ts | 65 + src/types/leaflet.d.ts | 95 + tsconfig.json | 13 + 83 files changed, 1453 insertions(+), 5086 deletions(-) delete mode 100644 .gitlab-ci.yml delete mode 100644 .tmuxinator.yml create mode 100644 LICENSE create mode 100755 bin/dev-server delete mode 100644 bsconfig.json delete mode 100755 deploy delete mode 100755 dev create mode 100644 flake.lock create mode 100644 flake.nix delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 public/font-awesome-4.7.0/css/font-awesome.min.css delete mode 100644 public/font-awesome-4.7.0/fonts/FontAwesome.otf delete mode 100644 public/font-awesome-4.7.0/fonts/fontawesome-webfont.eot delete mode 100644 public/font-awesome-4.7.0/fonts/fontawesome-webfont.svg delete mode 100644 public/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf delete mode 100644 public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff delete mode 100644 public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 delete mode 100644 public/leaflet/images/layers-2x.png delete mode 100644 public/leaflet/images/layers.png delete mode 100644 public/leaflet/images/marker-icon-2x.png delete mode 100644 public/leaflet/images/marker-icon.png delete mode 100644 public/leaflet/images/marker-shadow.png delete mode 100644 public/leaflet/leaflet.js.map delete mode 100644 rollup.config.js delete mode 100644 shell.nix delete mode 100644 src/Color.ml delete mode 100644 src/Lib/CSV.ml delete mode 100644 src/Lib/ContextMenu.ml delete mode 100644 src/Lib/Dom/Document.ml delete mode 100644 src/Lib/Dom/Element.ml delete mode 100644 src/Lib/Dom/Event.ml delete mode 100644 src/Lib/Dom/H.ml delete mode 100644 src/Lib/Dom/HA.ml delete mode 100644 src/Lib/Dom/HE.ml delete mode 100644 src/Lib/Dom/History.ml delete mode 100644 src/Lib/Dom/Location.ml delete mode 100644 src/Lib/Dom/Window.ml delete mode 100644 src/Lib/File.ml delete mode 100644 src/Lib/FontAwesome.ml delete mode 100644 src/Lib/Fun.ml delete mode 100644 src/Lib/Leaflet.ml delete mode 100644 src/Lib/Modal.ml delete mode 100644 src/Lib/Option.ml delete mode 100644 src/Lib/String.ml delete mode 100644 src/Lib/URI.ml delete mode 100644 src/Main.ml delete mode 100644 src/State.ml delete mode 100644 src/View/Button.ml delete mode 100644 src/View/Form.ml delete mode 100644 src/View/Form/Autocomplete.ml delete mode 100644 src/View/Layout.ml delete mode 100644 src/View/Map.ml delete mode 100644 src/View/Map/Icon.ml delete mode 100644 src/View/Map/Marker.ml create mode 100644 src/lib/autoComplete.ts create mode 100644 src/lib/base.ts create mode 100644 src/lib/button.ts create mode 100644 src/lib/color.ts create mode 100644 src/lib/contextMenu.ts create mode 100644 src/lib/dom.ts create mode 100644 src/lib/form.ts create mode 100644 src/lib/h.ts create mode 100644 src/lib/icons.ts create mode 100644 src/lib/layout.ts create mode 100644 src/lib/modal.ts create mode 100644 src/main.ts create mode 100644 src/map.ts create mode 100644 src/marker.ts create mode 100644 src/markerForm.ts create mode 100644 src/serialization.ts create mode 100644 src/serialization/utils.ts create mode 100644 src/serialization/v0.ts create mode 100644 src/state.ts create mode 100644 src/types/leaflet.d.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 05e0f57..b62f3b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1 @@ -node_modules/ -lib/ -.merlin -*.bs.js -public/main.js -.bsb.lock +/public/main.js diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index a872f80..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,10 +0,0 @@ -image: alpine:latest -pages: - stage: deploy - script: - - echo 'Nothing to do...' - artifacts: - paths: - - public - only: - - pages diff --git a/.tmuxinator.yml b/.tmuxinator.yml deleted file mode 100644 index 45f4dd4..0000000 --- a/.tmuxinator.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: map - -windows: - - main: - panes: - - ocaml: - - ./dev watch-ocaml - - js: - - ./dev watch-js - - server: - - python -m http.server --directory public diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to diff --git a/Makefile b/Makefile index c40999d..593752d 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,9 @@ -export PATH := node_modules/.bin:$(PATH) - -build: public/main.js - -public/main.js: install $(shell find src \( -type d -o \( -type f -a -regex ".*\.ml" \) \)) - @echo "Building $@" - @bsb -make-world - @rollup --config rollup.config.js - @terser $@ --output $@ --compress --mangle - -install: - @npm install - @bsb -init init - @mv init/node_modules/bs-platform node_modules - @rm -rf init +build: + @esbuild \ + --bundle src/main.ts \ + --minify \ + --target=es2017 \ + --outdir=public clean: - @echo "Cleaning" @rm -f public/main.js - @rm -rf node_modules lib - @find src -name '*.bs.js' -exec rm {} \; diff --git a/README.md b/README.md index 8442abb..880f174 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,15 @@ # Map -Available at [https://guyonvarch.gitlab.io/map](https://guyonvarch.gitlab.io/map). +Add markers on a map and save state in URL. -## Gettings started +# Getting started -Start the environment with: +Enter nix shell: -```bash -./dev start -``` + nix develop -Later, stop the environment with: +Then start the dev server: -```bash -./dev stop -``` + bin/dev-server -## Deploy - -```bash -nix-shell --run ./deploy -``` - -## Bucklescript links - -- [Documentation](https://bucklescript.github.io/docs/en/interop-overview) -- [Ocaml std API](https://caml.inria.fr/pub/docs/manual-ocaml-4.02/stdlib.html) -- [Libraries](https://bucklescript.github.io/bucklescript/api/index.html) +Finally, open your browser at `http://localhost:8000`. diff --git a/bin/dev-server b/bin/dev-server new file mode 100755 index 0000000..82686ae --- /dev/null +++ b/bin/dev-server @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Run server + +python -m http.server --directory public 8000 & +trap "fuser -k 8000/tcp" EXIT + +# Watch TypeScript + +CHECK="echo Checking TypeScript… && tsc --checkJs" +BUILD="esbuild --bundle src/main.ts --target=es2017 --outdir=public" +watchexec \ + --clear \ + --watch src \ + -- "$CHECK && $BUILD" diff --git a/bsconfig.json b/bsconfig.json deleted file mode 100644 index 95e864d..0000000 --- a/bsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "map", - "version": "0.1.0", - "sources": { - "dir": "src", - "subdirs": true - }, - "package-specs": { - "module": "es6", - "in-source": true - }, - "suffix": ".bs.js", - "bs-dependencies": [], - "warnings": { - "number": "+A-42-40-4", - "error": "+A-40-4" - }, - "bsc-flags": [ - "-bs-super-errors" - ], - "refmt": 3 -} diff --git a/deploy b/deploy deleted file mode 100755 index 6abf9b0..0000000 --- a/deploy +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Build -git branch -D pages || true -git checkout -b pages -make clean build -git add --force public/main.js -git commit -m "Deploy pages" -git push --force origin pages -git checkout master -git branch -D pages diff --git a/dev b/dev deleted file mode 100755 index 178fe6f..0000000 --- a/dev +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -cd "$(dirname $0)" -CMD="$1" -PROJECT="map" - -if [ "$CMD" = "start" ]; then - - nix-shell --run "make node_modules && tmuxinator local" - -elif [ "$CMD" = "stop" ]; then - - nix-shell --run "tmux kill-session -t $PROJECT" - -elif [ "$CMD" = "watch-ocaml" ]; then - - bsb -make-world -w - -elif [ "$CMD" = "watch-js" ]; then - - node_modules/.bin/rollup --watch --config rollup.config.js - -else - - echo "Usage: $0 start|stop|watch-ocaml|watch-js" - exit 1 - -fi diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..bc595cc --- /dev/null +++ b/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1656928814, + "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1659715246, + "narHash": "sha256-nWZOGZR4Cl4MdetOdnn8GOLxXw5L/V+unWV0KF5SqD0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ab403f07d359394c175218f66eac826e91b56565", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..325ed60 --- /dev/null +++ b/flake.nix @@ -0,0 +1,21 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let pkgs = nixpkgs.legacyPackages.${system}; + in { devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + nodePackages.typescript + python3 + psmisc # fuser + esbuild + watchexec + ]; + }; } + ); +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 6aa4899..0000000 --- a/package-lock.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "name": "map", - "version": "0.1.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@rollup/plugin-node-resolve": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", - "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deep-freeze": "^0.0.1", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.17.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - } - }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "@types/node": { - "version": "14.0.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.22.tgz", - "integrity": "sha512-emeGcJvdiZ4Z3ohbmw93E/64jRzUHAItSHt8nF7M4TGgQTiWqFVGB8KNpLGFmUHmHLvjvBgFwVlqNcq+VuGv9g==", - "dev": true - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "builtin-modules": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "deep-freeze": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", - "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "leaflet": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", - "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "rollup": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.21.0.tgz", - "integrity": "sha512-BEGgy+wSzux7Ycq58pRiWEOBZaXRXTuvzl1gsm7gqmsAHxkWf9nyA5V2LN9fGSHhhDQd0/C13iRzSh4bbIpWZQ==", - "dev": true, - "requires": { - "fsevents": "~2.1.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 2a80a56..0000000 --- a/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "map", - "version": "0.1.0", - "keywords": [], - "author": "Joris Guyonvarch", - "license": "MIT", - "devDependencies": { - "@rollup/plugin-node-resolve": "^8.1.0", - "rollup": "^2.21.0", - "terser": "^4.8.0", - "leaflet": "^1.6.0" - } -} diff --git a/public/font-awesome-4.7.0/css/font-awesome.min.css b/public/font-awesome-4.7.0/css/font-awesome.min.css deleted file mode 100644 index 540440c..0000000 --- a/public/font-awesome-4.7.0/css/font-awesome.min.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/public/font-awesome-4.7.0/fonts/FontAwesome.otf b/public/font-awesome-4.7.0/fonts/FontAwesome.otf deleted file mode 100644 index 401ec0f..0000000 Binary files a/public/font-awesome-4.7.0/fonts/FontAwesome.otf and /dev/null differ diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.eot b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca..0000000 Binary files a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.svg b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845..0000000 --- a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2..0000000 Binary files a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a..0000000 Binary files a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6..0000000 Binary files a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/public/index.html b/public/index.html index 143f477..2f215cb 100644 --- a/public/index.html +++ b/public/index.html @@ -1,23 +1,15 @@ - + + + +Map + + - - - - Map - - + + + - - + - - - - - - - - - - + diff --git a/public/leaflet/images/layers-2x.png b/public/leaflet/images/layers-2x.png deleted file mode 100644 index 200c333..0000000 Binary files a/public/leaflet/images/layers-2x.png and /dev/null differ diff --git a/public/leaflet/images/layers.png b/public/leaflet/images/layers.png deleted file mode 100644 index 1a72e57..0000000 Binary files a/public/leaflet/images/layers.png and /dev/null differ diff --git a/public/leaflet/images/marker-icon-2x.png b/public/leaflet/images/marker-icon-2x.png deleted file mode 100644 index 88f9e50..0000000 Binary files a/public/leaflet/images/marker-icon-2x.png and /dev/null differ diff --git a/public/leaflet/images/marker-icon.png b/public/leaflet/images/marker-icon.png deleted file mode 100644 index 950edf2..0000000 Binary files a/public/leaflet/images/marker-icon.png and /dev/null differ diff --git a/public/leaflet/images/marker-shadow.png b/public/leaflet/images/marker-shadow.png deleted file mode 100644 index 9fd2979..0000000 Binary files a/public/leaflet/images/marker-shadow.png and /dev/null differ diff --git a/public/leaflet/leaflet.css b/public/leaflet/leaflet.css index 983d605..981874b 100644 --- a/public/leaflet/leaflet.css +++ b/public/leaflet/leaflet.css @@ -45,7 +45,10 @@ } /* .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, +.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, @@ -53,6 +56,8 @@ .leaflet-container .leaflet-tile { max-width: none !important; max-height: none !important; + width: auto; + padding: 0; } .leaflet-container.leaflet-touch-zoom { @@ -166,9 +171,6 @@ /* zoom and fade animations */ -.leaflet-fade-anim .leaflet-tile { - will-change: opacity; - } .leaflet-fade-anim .leaflet-popup { opacity: 0; -webkit-transition: opacity 0.2s linear; @@ -183,9 +185,10 @@ -ms-transform-origin: 0 0; transform-origin: 0 0; } -.leaflet-zoom-anim .leaflet-zoom-animated { +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); @@ -251,14 +254,11 @@ svg.leaflet-image-layer.leaflet-interactive path { .leaflet-container { background: #ddd; - outline: 0; + outline-offset: 1px; } .leaflet-container a { color: #0078A8; } -.leaflet-container a.leaflet-active { - outline: 2px solid orange; - } .leaflet-zoom-box { border: 2px dotted #38f; background: rgba(255,255,255,0.5); @@ -267,7 +267,10 @@ svg.leaflet-image-layer.leaflet-interactive path { /* general typography */ .leaflet-container { - font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; + font-size: 12px; + font-size: 0.75rem; + line-height: 1.5; } @@ -277,8 +280,7 @@ svg.leaflet-image-layer.leaflet-interactive path { box-shadow: 0 1px 5px rgba(0,0,0,0.65); border-radius: 4px; } -.leaflet-bar a, -.leaflet-bar a:hover { +.leaflet-bar a { background-color: #fff; border-bottom: 1px solid #ccc; width: 26px; @@ -295,7 +297,8 @@ svg.leaflet-image-layer.leaflet-interactive path { background-repeat: no-repeat; display: block; } -.leaflet-bar a:hover { +.leaflet-bar a:hover, +.leaflet-bar a:focus { background-color: #f4f4f4; } .leaflet-bar a:first-child { @@ -385,6 +388,8 @@ svg.leaflet-image-layer.leaflet-interactive path { } .leaflet-control-layers label { display: block; + font-size: 13px; + font-size: 1.08333em; } .leaflet-control-layers-separator { height: 0; @@ -393,7 +398,7 @@ svg.leaflet-image-layer.leaflet-interactive path { } /* Default icon URLs */ -.leaflet-default-icon-path { +.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */ background-image: url(images/marker-icon.png); } @@ -402,23 +407,27 @@ svg.leaflet-image-layer.leaflet-interactive path { .leaflet-container .leaflet-control-attribution { background: #fff; - background: rgba(255, 255, 255, 0.7); + 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:hover, +.leaflet-control-attribution a:focus { text-decoration: underline; } -.leaflet-container .leaflet-control-attribution, -.leaflet-container .leaflet-control-scale { - font-size: 11px; +.leaflet-attribution-flag { + display: inline !important; + vertical-align: baseline !important; + width: 1em; + height: 0.6669em; } .leaflet-left .leaflet-control-scale { margin-left: 5px; @@ -431,14 +440,11 @@ svg.leaflet-image-layer.leaflet-interactive path { border-top: none; line-height: 1.1; padding: 2px 5px 1px; - font-size: 11px; white-space: nowrap; - overflow: hidden; -moz-box-sizing: border-box; box-sizing: border-box; - - background: #fff; - background: rgba(255, 255, 255, 0.5); + background: rgba(255, 255, 255, 0.8); + text-shadow: 1px 1px #fff; } .leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; @@ -474,17 +480,22 @@ svg.leaflet-image-layer.leaflet-interactive path { border-radius: 12px; } .leaflet-popup-content { - margin: 13px 19px; - line-height: 1.4; + margin: 13px 24px 13px 20px; + line-height: 1.3; + font-size: 13px; + font-size: 1.08333em; + min-height: 1px; } .leaflet-popup-content p { - margin: 18px 0; + 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; @@ -495,6 +506,7 @@ svg.leaflet-image-layer.leaflet-interactive path { padding: 1px; margin: -10px auto 0; + pointer-events: auto; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); @@ -511,28 +523,25 @@ svg.leaflet-image-layer.leaflet-interactive path { position: absolute; top: 0; right: 0; - padding: 4px 4px 0 0; border: none; text-align: center; - width: 18px; - height: 14px; - font: 16px/14px Tahoma, Verdana, sans-serif; - color: #c3c3c3; + width: 24px; + height: 24px; + font: 16px/24px Tahoma, Verdana, sans-serif; + color: #757575; text-decoration: none; - font-weight: bold; background: transparent; } -.leaflet-container a.leaflet-popup-close-button:hover { - color: #999; +.leaflet-container a.leaflet-popup-close-button:hover, +.leaflet-container a.leaflet-popup-close-button:focus { + color: #585858; } .leaflet-popup-scrolled { overflow: auto; - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; } .leaflet-oldie .leaflet-popup-content-wrapper { - zoom: 1; + -ms-zoom: 1; } .leaflet-oldie .leaflet-popup-tip { width: 24px; @@ -541,9 +550,6 @@ svg.leaflet-image-layer.leaflet-interactive path { -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-popup-tip-container { - margin-top: -1px; - } .leaflet-oldie .leaflet-control-zoom, .leaflet-oldie .leaflet-control-layers, @@ -578,7 +584,7 @@ svg.leaflet-image-layer.leaflet-interactive path { pointer-events: none; box-shadow: 0 1px 3px rgba(0,0,0,0.4); } -.leaflet-tooltip.leaflet-clickable { +.leaflet-tooltip.leaflet-interactive { cursor: pointer; pointer-events: auto; } @@ -638,3 +644,13 @@ svg.leaflet-image-layer.leaflet-interactive path { 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; + } + } diff --git a/public/leaflet/leaflet.js b/public/leaflet/leaflet.js index bc9ef0f..047bfe7 100644 --- a/public/leaflet/leaflet.js +++ b/public/leaflet/leaflet.js @@ -1,5 +1,6 @@ /* @preserve - * Leaflet 1.6.0+Detached: 0c81bdf904d864fd12a286e3d1979f47aba17991.0c81bdf, a JS library for interactive maps. http://leafletjs.com - * (c) 2010-2019 Vladimir Agafonkin, (c) 2010-2011 CloudMade + * Leaflet 1.9.3, a JS library for interactive maps. https://leafletjs.com + * (c) 2010-2022 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ -!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";var i=Object.freeze;function h(t){var i,e,n,o;for(e=1,n=arguments.length;e=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=R(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=R(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.xi.y&&n.y=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=D(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=D(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lati.lng&&n.lng';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}();function Bt(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var At=(Object.freeze||Object)({ie:it,ielt9:et,edge:nt,webkit:ot,android:st,android23:rt,androidStock:ht,opera:ut,chrome:lt,gecko:ct,safari:_t,phantom:dt,opera12:pt,win:mt,ie3d:ft,webkit3d:gt,gecko3d:vt,any3d:yt,mobile:xt,mobileWebkit:wt,mobileWebkit3d:Pt,msPointer:Lt,pointer:bt,touch:Tt,mobileOpera:zt,mobileGecko:Mt,retina:Ct,passiveEvents:Et,canvas:St,svg:Zt,vml:kt}),It=Lt?"MSPointerDown":"pointerdown",Ot=Lt?"MSPointerMove":"pointermove",Rt=Lt?"MSPointerUp":"pointerup",Nt=Lt?"MSPointerCancel":"pointercancel",Dt=["INPUT","SELECT","OPTION"],jt={},Wt=!1,Ht=0;function Ft(t,i,e,n){return"touchstart"===i?function(t,i,e){var n=a(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(Dt.indexOf(t.target.tagName)<0))return;ji(t)}Gt(t,i)});t["_leaflet_touchstart"+e]=n,t.addEventListener(It,n,!1),Wt||(document.documentElement.addEventListener(It,Ut,!0),document.documentElement.addEventListener(Ot,Vt,!0),document.documentElement.addEventListener(Rt,qt,!0),document.documentElement.addEventListener(Nt,qt,!0),Wt=!0)}(t,e,n):"touchmove"===i?function(t,i,e){function n(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&Gt(t,i)}t["_leaflet_touchmove"+e]=n,t.addEventListener(Ot,n,!1)}(t,e,n):"touchend"===i&&function(t,i,e){function n(t){Gt(t,i)}t["_leaflet_touchend"+e]=n,t.addEventListener(Rt,n,!1),t.addEventListener(Nt,n,!1)}(t,e,n),this}function Ut(t){jt[t.pointerId]=t,Ht++}function Vt(t){jt[t.pointerId]&&(jt[t.pointerId]=t)}function qt(t){delete jt[t.pointerId],Ht--}function Gt(t,i){for(var e in t.touches=[],jt)t.touches.push(jt[e]);t.changedTouches=[t],i(t)}var Kt=Lt?"MSPointerDown":bt?"pointerdown":"touchstart",Yt=Lt?"MSPointerUp":bt?"pointerup":"touchend",Xt="_leaflet_";function Jt(t,o,i){var s,r,a=!1;function e(t){var i;if(bt){if(!nt||"mouse"===t.pointerType)return;i=Ht}else i=t.touches.length;if(!(1this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,D(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},panInside:function(t,i){var e=I((i=i||{}).paddingTopLeft||i.padding||[0,0]),n=I(i.paddingBottomRight||i.padding||[0,0]),o=this.getCenter(),s=this.project(o),r=this.project(t),a=this.getPixelBounds(),h=a.getSize().divideBy(2),u=R([a.min.add(e),a.max.subtract(n)]);if(!u.contains(r)){this._enforcingBounds=!0;var l=s.subtract(r),c=I(r.x+l.x,r.y+l.y);(r.xu.max.x)&&(c.x=s.x-l.x,0u.max.y)&&(c.y=s.y-l.y,0=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,n=[],o="mouseout"===i||"mouseover"===i,s=t.target||t.srcElement,r=!1;s;){if((e=this._targets[u(s)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){r=!0;break}if(e&&e.listens(i,!0)){if(o&&!Yi(s,t))break;if(n.push(e),o)break}if(s===this._container)break;s=s.parentNode}return n.length||r||o||!Yi(s,t)||(n=[this]),n},_handleDOMEvent:function(t){if(this._loaded&&!Ki(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i&&"keyup"!==i&&"keydown"!==i||Mi(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,i,e){if("click"===t.type){var n=h({},t);n.type="preclick",this._fireDOMEvent(n,n.type,e)}if(!t._stopped&&(e=(e||[]).concat(this._findEventTargets(t,i))).length){var o=e[0];"contextmenu"===i&&o.listens(i,!0)&&ji(t);var s={originalEvent:t};if("keypress"!==t.type&&"keydown"!==t.type&&"keyup"!==t.type){var r=o.getLatLng&&(!o._radius||o._radius<=10);s.containerPoint=r?this.latLngToContainerPoint(o.getLatLng()):this.mouseEventToContainerPoint(t),s.layerPoint=this.containerPointToLayerPoint(s.containerPoint),s.latlng=r?o.getLatLng():this.layerPointToLatLng(s.layerPoint)}for(var a=0;athis.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(M(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,e,n){this._mapPane&&(e&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,mi(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:n}),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&fi(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),M(function(){this._moveEnd(!0)},this))}});function Qi(t){return new te(t)}var te=S.extend({options:{position:"topright"},initialize:function(t){p(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return mi(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(li(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),n=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=n):i=this._createRadioElement("leaflet-base-layers_"+u(this),n),this._layerControlInputs.push(i),i.layerId=u(t.layer),ki(i,"click",this._onInputClick,this);var o=document.createElement("span");o.innerHTML=" "+t.name;var s=document.createElement("div");return e.appendChild(s),s.appendChild(i),s.appendChild(o),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;0<=s;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;si.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),ee=te.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"−",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=ui("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=ui("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),Di(s),ki(s,"click",Wi),ki(s,"click",o,this),ki(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";fi(this._zoomInButton,i),fi(this._zoomOutButton,i),!this._disabled&&t._zoom!==t.getMinZoom()||mi(this._zoomOutButton,i),!this._disabled&&t._zoom!==t.getMaxZoom()||mi(this._zoomInButton,i)}});$i.mergeOptions({zoomControl:!0}),$i.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new ee,this.addControl(this.zoomControl))});var ne=te.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i="leaflet-control-scale",e=ui("div",i),n=this.options;return this._addScales(n,i+"-line",e),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),e},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=ui("div",i,e)),t.imperial&&(this._iScale=ui("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;5280Leaflet'},initialize:function(t){p(this,t),this._attributions={}},onAdd:function(t){for(var i in(t.attributionControl=this)._container=ui("div","leaflet-control-attribution"),Di(this._container),t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});$i.mergeOptions({attributionControl:!0}),$i.addInitHook(function(){this.options.attributionControl&&(new oe).addTo(this)});te.Layers=ie,te.Zoom=ee,te.Scale=ne,te.Attribution=oe,Qi.layers=function(t,i,e){return new ie(t,i,e)},Qi.zoom=function(t){return new ee(t)},Qi.scale=function(t){return new ne(t)},Qi.attribution=function(t){return new oe(t)};var se=S.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}});se.addTo=function(t,i){return t.addHandler(i,this),this};var re,ae={Events:Z},he=Tt?"touchstart mousedown":"mousedown",ue={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},le={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},ce=k.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){p(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(ki(this._dragStartTarget,he,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(ce._dragging===this&&this.finishDrag(),Ai(this._dragStartTarget,he,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!pi(this._element,"leaflet-zoom-anim")&&!(ce._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((ce._dragging=this)._preventOutline&&Mi(this._element),Ti(),Qt(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t,e=Ei(this._element);this._startPoint=new B(i.clientX,i.clientY),this._parentScale=Si(e),ki(document,le[t.type],this._onMove,this),ki(document,ue[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&1i.max.x&&(e|=2),t.yi.max.y&&(e|=8),e}function ge(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||je.prototype._containsPoint.call(this,t,!0)}});var He=ke.extend({initialize:function(t,i){p(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=v(t)?t:t.features;if(o){for(i=0,e=o.length;iu.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Wi(t)},_getAnchor:function(){return I(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});$i.mergeOptions({closePopupOnClick:!0}),$i.include({openPopup:function(t,i,e){return t instanceof sn||(t=new sn(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),Se.include({bindPopup:function(t,i){return t instanceof sn?(p(t,i),(this._popup=t)._source=this):(this._popup&&!i||(this._popup=new sn(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){return this._popup&&this._map&&(i=this._popup._prepareOpen(this,t,i),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(Wi(t),i instanceof Re?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var rn=on.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){on.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){on.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=on.prototype.getEvents.call(this);return Tt&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ui("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=I(this.options.offset),u=this._getAnchor();t="top"===s?t.add(I(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t.subtract(I(r/2-h.x,-h.y,!0)):"center"===s?t.subtract(I(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.xthis.options.maxZoom||ethis.options.maxZoom||void 0!==this.options.minZoom&&oe.max.x)||!i.wrapLat&&(t.ye.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return D(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new N(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new B(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(li(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){mi(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=l,t.onmousemove=l,et&&this.options.opacity<1&&yi(t,this.options.opacity),st&&!rt&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var e=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&M(a(this._tileReady,this,t,null,o)),Pi(o,e),this._tiles[n]={el:o,coords:t,current:!0},i.appendChild(o),this.fire("tileloadstart",{tile:o,coords:t})},_tileReady:function(t,i,e){i&&this.fire("tileerror",{error:i,tile:e,coords:t});var n=this._tileCoordsToKey(t);(e=this._tiles[n])&&(e.loaded=+new Date,this._map._fadeAnimated?(yi(e.el,0),C(this._fadeFrame),this._fadeFrame=M(this._updateOpacity,this)):(e.active=!0,this._pruneTiles()),i||(mi(e.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:e.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),et||!this._map._fadeAnimated?M(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new B(this._wrapX?r(t.x,this._wrapX):t.x,this._wrapY?r(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new O(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var un=hn.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=p(this,i)).detectRetina&&Ct&&0')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),fn={_initContainer:function(){this._container=ui("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(_n.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=mn("shape");mi(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=mn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[u(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;li(i),t.removeInteractiveTarget(i),delete this._layers[u(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=mn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=v(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=mn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){_i(t._container)},_bringToBack:function(t){di(t._container)}},gn=kt?mn:$,vn=_n.extend({getEvents:function(){var t=_n.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=gn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=gn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){li(this._container),Ai(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){_n.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),Pi(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=gn("path");t.options.className&&mi(i,t.options.className),t.options.interactive&&mi(i,"leaflet-interactive"),this._updateStyle(t),this._layers[u(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){li(t._path),t.removeInteractiveTarget(t._path),delete this._layers[u(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,Q(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){_i(t._path)},_bringToBack:function(t){di(t._path)}});function yn(t){return Zt||kt?new vn(t):null}kt&&vn.include(fn),$i.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this._createRenderer()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=this._createRenderer({pane:t}),this._paneRenderers[t]=i),i},_createRenderer:function(t){return this.options.preferCanvas&&pn(t)||yn(t)}});var xn=We.extend({initialize:function(t,i){We.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=D(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});vn.create=gn,vn.pointsToPath=Q,He.geometryToLayer=Fe,He.coordsToLatLng=Ve,He.coordsToLatLngs=qe,He.latLngToCoords=Ge,He.latLngsToCoords=Ke,He.getFeature=Ye,He.asFeature=Xe,$i.mergeOptions({boxZoom:!0});var wn=se.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){ki(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){Ai(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){li(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),Qt(),Ti(),this._startPoint=this._map.mouseEventToContainerPoint(t),ki(document,{contextmenu:Wi,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ui("div","leaflet-zoom-box",this._container),mi(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new O(this._point,this._startPoint),e=i.getSize();Pi(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(li(this._box),fi(this._container,"leaflet-crosshair")),ti(),zi(),Ai(document,{contextmenu:Wi,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(a(this._resetState,this),0);var i=new N(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});$i.addInitHook("addHandler","boxZoom",wn),$i.mergeOptions({doubleClickZoom:!0});var Pn=se.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});$i.addInitHook("addHandler","doubleClickZoom",Pn),$i.mergeOptions({dragging:!0,inertia:!rt,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var Ln=se.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new ce(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}mi(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){fi(this._map._container,"leaflet-grab"),fi(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=D(this._map.options.maxBounds);this._offsetLimit=R(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1i.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)i.getMaxZoom()&&1=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>=e.x&&n.x<=i.x,t=t.y>=e.y&&n.y<=i.y;return o&&t},overlaps:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>e.x&&n.xe.y&&n.y=n.lat&&i.lat<=o.lat&&e.lng>=n.lng&&i.lng<=o.lng},intersects:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>=e.lat&&n.lat<=i.lat,t=t.lng>=e.lng&&n.lng<=i.lng;return o&&t},overlaps:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>e.lat&&n.late.lng&&n.lng","http://www.w3.org/2000/svg"===(Ft.firstChild&&Ft.firstChild.namespaceURI));function y(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var b={ie:pt,ielt9:mt,edge:n,webkit:ft,android:gt,android23:vt,androidStock:yt,opera:xt,chrome:wt,gecko:bt,safari:Pt,phantom:Lt,opera12:o,win:Tt,ie3d:Mt,webkit3d:zt,gecko3d:_t,any3d:Ct,mobile:Zt,mobileWebkit:St,mobileWebkit3d:Et,msPointer:kt,pointer:Ot,touch:Bt,touchNative:At,mobileOpera:It,mobileGecko:Rt,retina:Nt,passiveEvents:Dt,canvas:jt,svg:Ht,vml:!Ht&&function(){try{var t=document.createElement("div"),e=(t.innerHTML='',t.firstChild);return e.style.behavior="url(#default#VML)",e&&"object"==typeof e.adj}catch(t){return!1}}(),inlineSvg:Ft,mac:0===navigator.platform.indexOf("Mac"),linux:0===navigator.platform.indexOf("Linux")},Wt=b.msPointer?"MSPointerDown":"pointerdown",Ut=b.msPointer?"MSPointerMove":"pointermove",Vt=b.msPointer?"MSPointerUp":"pointerup",qt=b.msPointer?"MSPointerCancel":"pointercancel",Gt={touchstart:Wt,touchmove:Ut,touchend:Vt,touchcancel:qt},Kt={touchstart:function(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&O(e);ee(t,e)},touchmove:ee,touchend:ee,touchcancel:ee},Yt={},Xt=!1;function Jt(t,e,i){return"touchstart"!==e||Xt||(document.addEventListener(Wt,$t,!0),document.addEventListener(Ut,Qt,!0),document.addEventListener(Vt,te,!0),document.addEventListener(qt,te,!0),Xt=!0),Kt[e]?(i=Kt[e].bind(this,i),t.addEventListener(Gt[e],i,!1),i):(console.warn("wrong event specified:",e),u)}function $t(t){Yt[t.pointerId]=t}function Qt(t){Yt[t.pointerId]&&(Yt[t.pointerId]=t)}function te(t){delete Yt[t.pointerId]}function ee(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||"mouse")){for(var i in e.touches=[],Yt)e.touches.push(Yt[i]);e.changedTouches=[e],t(e)}}var ie=200;function ne(t,i){t.addEventListener("dblclick",i);var n,o=0;function e(t){var e;1!==t.detail?n=t.detail:"mouse"===t.pointerType||t.sourceCapabilities&&!t.sourceCapabilities.firesTouchEvents||((e=Ne(t)).some(function(t){return t instanceof HTMLLabelElement&&t.attributes.for})&&!e.some(function(t){return t instanceof HTMLInputElement||t instanceof HTMLSelectElement})||((e=Date.now())-o<=ie?2===++n&&i(function(t){var e,i,n={};for(i in t)e=t[i],n[i]=e&&e.bind?e.bind(t):e;return(t=n).type="dblclick",n.detail=2,n.isTrusted=!1,n._simulated=!0,n}(t)):n=1,o=e))}return t.addEventListener("click",e),{dblclick:i,simDblclick:e}}var oe,se,re,ae,he,le,ue=we(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),ce=we(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),de="webkitTransition"===ce||"OTransition"===ce?ce+"End":"transitionend";function _e(t){return"string"==typeof t?document.getElementById(t):t}function pe(t,e){var i=t.style[e]||t.currentStyle&&t.currentStyle[e];return"auto"===(i=i&&"auto"!==i||!document.defaultView?i:(t=document.defaultView.getComputedStyle(t,null))?t[e]:null)?null:i}function P(t,e,i){t=document.createElement(t);return t.className=e||"",i&&i.appendChild(t),t}function T(t){var e=t.parentNode;e&&e.removeChild(t)}function me(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function fe(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function ge(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function ve(t,e){return void 0!==t.classList?t.classList.contains(e):0<(t=xe(t)).length&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(t)}function M(t,e){var i;if(void 0!==t.classList)for(var n=W(e),o=0,s=n.length;othis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var i=this.getCenter(),t=this._limitCenter(i,this._zoom,g(t));return i.equals(t)||this.panTo(t,e),this._enforcingBounds=!1,this},panInside:function(t,e){var i=m((e=e||{}).paddingTopLeft||e.padding||[0,0]),n=m(e.paddingBottomRight||e.padding||[0,0]),o=this.project(this.getCenter()),t=this.project(t),s=this.getPixelBounds(),i=_([s.min.add(i),s.max.subtract(n)]),s=i.getSize();return i.contains(t)||(this._enforcingBounds=!0,n=t.subtract(i.getCenter()),i=i.extend(t).getSize().subtract(s),o.x+=n.x<0?-i.x:i.x,o.y+=n.y<0?-i.y:i.y,this.panTo(this.unproject(o),e),this._enforcingBounds=!1),this},invalidateSize:function(t){if(!this._loaded)return this;t=l({animate:!1,pan:!0},!0===t?{animate:!0}:t);var e=this.getSize(),i=(this._sizeChanged=!0,this._lastCenter=null,this.getSize()),n=e.divideBy(2).round(),o=i.divideBy(2).round(),n=n.subtract(o);return n.x||n.y?(t.animate&&t.pan?this.panBy(n):(t.pan&&this._rawPanBy(n),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(a(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){var e,i;return t=this._locateOptions=l({timeout:1e4,watch:!1},t),"geolocation"in navigator?(e=a(this._handleGeolocationResponse,this),i=a(this._handleGeolocationError,this),t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t)):this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e;this._container._leaflet_id&&(e=t.code,t=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout"),this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+t+"."}))},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var e,i,n=new v(t.coords.latitude,t.coords.longitude),o=n.toBounds(2*t.coords.accuracy),s=this._locateOptions,r=(s.setView&&(e=this.getBoundsZoom(o),this.setView(n,s.maxZoom?Math.min(e,s.maxZoom):e)),{latlng:n,bounds:o,timestamp:t.timestamp});for(i in t.coords)"number"==typeof t.coords[i]&&(r[i]=t.coords[i]);this.fire("locationfound",r)}},addHandler:function(t,e){return e&&(e=this[t]=new e(this),this._handlers.push(e),this.options[t]&&e.enable()),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}for(var t in void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),T(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(r(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload"),this._layers)this._layers[t].remove();for(t in this._panes)T(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,e){e=P("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),e||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new s(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=g(t),i=m(i||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),t=t.getSouthEast(),i=this.getSize().subtract(i),t=_(this.project(t,n),this.project(r,n)).getSize(),r=b.any3d?this.options.zoomSnap:1,a=i.x/t.x,i=i.y/t.y,t=e?Math.max(a,i):Math.min(a,i),n=this.getScaleZoom(t,n);return r&&(n=Math.round(n/(r/100))*(r/100),n=e?Math.ceil(n/r)*r:Math.floor(n/r)*r),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new p(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){t=this._getTopLeftPoint(t,e);return new f(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,e){var i=this.options.crs;return e=void 0===e?this._zoom:e,i.scale(t)/i.scale(e)},getScaleZoom:function(t,e){var i=this.options.crs,t=(e=void 0===e?this._zoom:e,i.zoom(t*i.scale(e)));return isNaN(t)?1/0:t},project:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.latLngToPoint(w(t),e)},unproject:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.pointToLatLng(m(t),e)},layerPointToLatLng:function(t){t=m(t).add(this.getPixelOrigin());return this.unproject(t)},latLngToLayerPoint:function(t){return this.project(w(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(w(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(g(t))},distance:function(t,e){return this.options.crs.distance(w(t),w(e))},containerPointToLayerPoint:function(t){return m(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return m(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){t=this.containerPointToLayerPoint(m(t));return this.layerPointToLatLng(t)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(w(t)))},mouseEventToContainerPoint:function(t){return De(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){t=this._container=_e(t);if(!t)throw new Error("Map container not found.");if(t._leaflet_id)throw new Error("Map container is already initialized.");S(t,"scroll",this._onScroll,this),this._containerId=h(t)},_initLayout:function(){var t=this._container,e=(this._fadeAnimated=this.options.fadeAnimation&&b.any3d,M(t,"leaflet-container"+(b.touch?" leaflet-touch":"")+(b.retina?" leaflet-retina":"")+(b.ielt9?" leaflet-oldie":"")+(b.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":"")),pe(t,"position"));"absolute"!==e&&"relative"!==e&&"fixed"!==e&&"sticky"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Z(this._mapPane,new p(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(M(t.markerPane,"leaflet-zoom-hide"),M(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,e,i){Z(this._mapPane,new p(0,0));var n=!this._loaded,o=(this._loaded=!0,e=this._limitZoom(e),this.fire("viewprereset"),this._zoom!==e);this._moveStart(o,i)._move(t,e)._moveEnd(o),this.fire("viewreset"),n&&this.fire("load")},_moveStart:function(t,e){return t&&this.fire("zoomstart"),e||this.fire("movestart"),this},_move:function(t,e,i,n){void 0===e&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),n?i&&i.pinch&&this.fire("zoom",i):((o||i&&i.pinch)&&this.fire("zoom",i),this.fire("move",i)),this},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return r(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Z(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={};var e=t?k:S;e((this._targets[h(this._container)]=this)._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&e(window,"resize",this._onResize,this),b.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){r(this._resizeRequest),this._resizeRequest=x(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,e){for(var i,n=[],o="mouseout"===e||"mouseover"===e,s=t.target||t.srcElement,r=!1;s;){if((i=this._targets[h(s)])&&("click"===e||"preclick"===e)&&this._draggableMoved(i)){r=!0;break}if(i&&i.listens(e,!0)){if(o&&!Fe(s,t))break;if(n.push(i),o)break}if(s===this._container)break;s=s.parentNode}return n=n.length||r||o||!this.listens(e,!0)?n:[this]},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var e,i=t.target||t.srcElement;!this._loaded||i._leaflet_disable_events||"click"===t.type&&this._isClickDisabled(i)||("mousedown"===(e=t.type)&&Me(i),this._fireDOMEvent(t,e))},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,i){"click"===t.type&&((a=l({},t)).type="preclick",this._fireDOMEvent(a,a.type,i));var n=this._findEventTargets(t,e);if(i){for(var o=[],s=0;sthis.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),n=this._getCenterOffset(t)._divideBy(1-1/n);if(!0!==i.animate&&!this.getSize().contains(n))return!1;x(function(){this._moveStart(!0,!1)._animateZoom(t,e,!0)},this)}return!0},_animateZoom:function(t,e,i,n){this._mapPane&&(i&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,M(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:e,noUpdate:n}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&z(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}});function Ue(t){return new B(t)}var Ve,B=et.extend({options:{position:"topright"},initialize:function(t){c(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),t=t._controlCorners[i];return M(e,"leaflet-control"),-1!==i.indexOf("bottom")?t.insertBefore(e,t.firstChild):t.appendChild(e),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(T(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0",e=document.createElement("div");return e.innerHTML=t,e.firstChild},_addItem:function(t){var e,i=document.createElement("label"),n=this._map.hasLayer(t.layer),n=(t.overlay?((e=document.createElement("input")).type="checkbox",e.className="leaflet-control-layers-selector",e.defaultChecked=n):e=this._createRadioElement("leaflet-base-layers_"+h(this),n),this._layerControlInputs.push(e),e.layerId=h(t.layer),S(e,"click",this._onInputClick,this),document.createElement("span")),o=(n.innerHTML=" "+t.name,document.createElement("span"));return i.appendChild(o),o.appendChild(e),o.appendChild(n),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(i),this._checkDisabledLayers(),i},_onInputClick:function(){var t,e,i=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=i.length-1;0<=s;s--)t=i[s],e=this._getLayer(t.layerId).layer,t.checked?n.push(e):t.checked||o.push(e);for(s=0;se.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section;S(t,"click",O),this.expand(),setTimeout(function(){k(t,"click",O)})}})),Ge=B.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=P("div",e+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,e+"-in",i,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,e+"-out",i,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,e,i,n,o){i=P("a",i,n);return i.innerHTML=t,i.href="#",i.title=e,i.setAttribute("role","button"),i.setAttribute("aria-label",e),Ie(i),S(i,"click",Re),S(i,"click",o,this),S(i,"click",this._refocusOnMap,this),i},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";z(this._zoomInButton,e),z(this._zoomOutButton,e),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),!this._disabled&&t._zoom!==t.getMinZoom()||(M(this._zoomOutButton,e),this._zoomOutButton.setAttribute("aria-disabled","true")),!this._disabled&&t._zoom!==t.getMaxZoom()||(M(this._zoomInButton,e),this._zoomInButton.setAttribute("aria-disabled","true"))}}),Ke=(A.mergeOptions({zoomControl:!0}),A.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new Ge,this.addControl(this.zoomControl))}),B.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e="leaflet-control-scale",i=P("div",e),n=this.options;return this._addScales(n,e+"-line",i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=P("div",e,i)),t.imperial&&(this._iScale=P("div",e,i))},_update:function(){var t=this._map,e=t.getSize().y/2,t=t.distance(t.containerPointToLatLng([0,e]),t.containerPointToLatLng([this.options.maxWidth,e]));this._updateScales(t)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t);this._updateScale(this._mScale,e<1e3?e+" m":e/1e3+" km",e/t)},_updateImperial:function(t){var e,i,t=3.2808399*t;5280'+(b.inlineSvg?' ':"")+"Leaflet"},initialize:function(t){c(this,t),this._attributions={}},onAdd:function(t){for(var e in(t.attributionControl=this)._container=P("div","leaflet-control-attribution"),Ie(this._container),t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return this._update(),t.on("layeradd",this._addAttribution,this),this._container},onRemove:function(t){t.off("layeradd",this._addAttribution,this)},_addAttribution:function(t){t.layer.getAttribution&&(this.addAttribution(t.layer.getAttribution()),t.layer.once("remove",function(){this.removeAttribution(t.layer.getAttribution())},this))},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t,e=[];for(t in this._attributions)this._attributions[t]&&e.push(t);var i=[];this.options.prefix&&i.push(this.options.prefix),e.length&&i.push(e.join(", ")),this._container.innerHTML=i.join(' ')}}}),n=(A.mergeOptions({attributionControl:!0}),A.addInitHook(function(){this.options.attributionControl&&(new Ye).addTo(this)}),B.Layers=qe,B.Zoom=Ge,B.Scale=Ke,B.Attribution=Ye,Ue.layers=function(t,e,i){return new qe(t,e,i)},Ue.zoom=function(t){return new Ge(t)},Ue.scale=function(t){return new Ke(t)},Ue.attribution=function(t){return new Ye(t)},et.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}})),ft=(n.addTo=function(t,e){return t.addHandler(e,this),this},{Events:e}),Xe=b.touch?"touchstart mousedown":"mousedown",Je=it.extend({options:{clickTolerance:3},initialize:function(t,e,i,n){c(this,n),this._element=t,this._dragStartTarget=e||t,this._preventOutline=i},enable:function(){this._enabled||(S(this._dragStartTarget,Xe,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Je._dragging===this&&this.finishDrag(!0),k(this._dragStartTarget,Xe,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var e,i;this._enabled&&(this._moved=!1,ve(this._element,"leaflet-zoom-anim")||(t.touches&&1!==t.touches.length?Je._dragging===this&&this.finishDrag():Je._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((Je._dragging=this)._preventOutline&&Me(this._element),Le(),re(),this._moving||(this.fire("down"),i=t.touches?t.touches[0]:t,e=Ce(this._element),this._startPoint=new p(i.clientX,i.clientY),this._startPos=Pe(this._element),this._parentScale=Ze(e),i="mousedown"===t.type,S(document,i?"mousemove":"touchmove",this._onMove,this),S(document,i?"mouseup":"touchend touchcancel",this._onUp,this)))))},_onMove:function(t){var e;this._enabled&&(t.touches&&1e&&(i.push(t[n]),o=n);oe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i}function ni(t,e,i,n){var o=e.x,e=e.y,s=i.x-o,r=i.y-e,a=s*s+r*r;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(l=!l);return l||vi.prototype._containsPoint.call(this,t,!0)}});var xi=ui.extend({initialize:function(t,e){c(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,o=d(t)?t:t.features;if(o){for(e=0,i=o.length;es.x&&(r=i.x+a-s.x+o.x),i.x-r-n.x<(a=0)&&(r=i.x-n.x),i.y+e+o.y>s.y&&(a=i.y+e-s.y+o.y),i.y-a-n.y<0&&(a=i.y-n.y),(r||a)&&(this.options.keepInView&&(this._autopanning=!0),t.fire("autopanstart").panBy([r,a]))))},_getAnchor:function(){return m(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}})),Bi=(A.mergeOptions({closePopupOnClick:!0}),A.include({openPopup:function(t,e,i){return this._initOverlay(Ai,t,e,i).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),o.include({bindPopup:function(t,e){return this._popup=this._initOverlay(Ai,this._popup,t,e),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t){return this._popup&&(this instanceof ui||(this._popup._source=this),this._popup._prepareOpen(t||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var e;this._popup&&this._map&&(Re(t),e=t.layer||t.target,this._popup._source!==e||e instanceof mi?(this._popup._source=e,this.openPopup(t.latlng)):this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}}),Oi.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){Oi.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(t){Oi.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var t=Oi.prototype.getEvents.call(this);return this.options.permanent||(t.preclick=this.close),t},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=P("div",t),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+h(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e,i=this._map,n=this._container,o=i.latLngToContainerPoint(i.getCenter()),i=i.layerPointToContainerPoint(t),s=this.options.direction,r=n.offsetWidth,a=n.offsetHeight,h=m(this.options.offset),l=this._getAnchor(),i="top"===s?(e=r/2,a):"bottom"===s?(e=r/2,0):(e="center"===s?r/2:"right"===s?0:"left"===s?r:i.xthis.options.maxZoom||nthis.options.maxZoom||void 0!==this.options.minZoom&&oi.max.x)||!e.wrapLat&&(t.yi.max.y))return!1}return!this.options.bounds||(e=this._tileCoordsToBounds(t),g(this.options.bounds).overlaps(e))},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var e=this._map,i=this.getTileSize(),n=t.scaleBy(i),i=n.add(i);return[e.unproject(n,t.z),e.unproject(i,t.z)]},_tileCoordsToBounds:function(t){t=this._tileCoordsToNwSe(t),t=new s(t[0],t[1]);return t=this.options.noWrap?t:this._map.wrapLatLngBounds(t)},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var t=t.split(":"),e=new p(+t[0],+t[1]);return e.z=+t[2],e},_removeTile:function(t){var e=this._tiles[t];e&&(T(e.el),delete this._tiles[t],this.fire("tileunload",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){M(t,"leaflet-tile");var e=this.getTileSize();t.style.width=e.x+"px",t.style.height=e.y+"px",t.onselectstart=u,t.onmousemove=u,b.ielt9&&this.options.opacity<1&&C(t,this.options.opacity)},_addTile:function(t,e){var i=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&x(a(this._tileReady,this,t,null,o)),Z(o,i),this._tiles[n]={el:o,coords:t,current:!0},e.appendChild(o),this.fire("tileloadstart",{tile:o,coords:t})},_tileReady:function(t,e,i){e&&this.fire("tileerror",{error:e,tile:i,coords:t});var n=this._tileCoordsToKey(t);(i=this._tiles[n])&&(i.loaded=+new Date,this._map._fadeAnimated?(C(i.el,0),r(this._fadeFrame),this._fadeFrame=x(this._updateOpacity,this)):(i.active=!0,this._pruneTiles()),e||(M(i.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:i.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),b.ielt9||!this._map._fadeAnimated?x(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new p(this._wrapX?H(t.x,this._wrapX):t.x,this._wrapY?H(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new f(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var Ni=Ri.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,e){this._url=t,(e=c(this,e)).detectRetina&&b.retina&&0')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),zt={_initContainer:function(){this._container=P("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Hi.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var e=t._container=Ui("shape");M(e,"leaflet-vml-shape "+(this.options.className||"")),e.coordsize="1 1",t._path=Ui("path"),e.appendChild(t._path),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;T(e),t.removeInteractiveTarget(e),delete this._layers[h(t)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(e=e||(t._stroke=Ui("stroke")),o.appendChild(e),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=d(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="",e.endcap=n.lineCap.replace("butt","flat"),e.joinstyle=n.lineJoin):e&&(o.removeChild(e),t._stroke=null),n.fill?(i=i||(t._fill=Ui("fill")),o.appendChild(i),i.color=n.fillColor||n.color,i.opacity=n.fillOpacity):i&&(o.removeChild(i),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+i+","+n+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){fe(t._container)},_bringToBack:function(t){ge(t._container)}},Vi=b.vml?Ui:ct,qi=Hi.extend({_initContainer:function(){this._container=Vi("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=Vi("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){T(this._container),k(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){var t,e,i;this._map._animatingZoom&&this._bounds||(Hi.prototype._update.call(this),e=(t=this._bounds).getSize(),i=this._container,this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,i.setAttribute("width",e.x),i.setAttribute("height",e.y)),Z(i,t.min),i.setAttribute("viewBox",[t.min.x,t.min.y,e.x,e.y].join(" ")),this.fire("update"))},_initPath:function(t){var e=t._path=Vi("path");t.options.className&&M(e,t.options.className),t.options.interactive&&M(e,"leaflet-interactive"),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){T(t._path),t.removeInteractiveTarget(t._path),delete this._layers[h(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,t=t.options;e&&(t.stroke?(e.setAttribute("stroke",t.color),e.setAttribute("stroke-opacity",t.opacity),e.setAttribute("stroke-width",t.weight),e.setAttribute("stroke-linecap",t.lineCap),e.setAttribute("stroke-linejoin",t.lineJoin),t.dashArray?e.setAttribute("stroke-dasharray",t.dashArray):e.removeAttribute("stroke-dasharray"),t.dashOffset?e.setAttribute("stroke-dashoffset",t.dashOffset):e.removeAttribute("stroke-dashoffset")):e.setAttribute("stroke","none"),t.fill?(e.setAttribute("fill",t.fillColor||t.color),e.setAttribute("fill-opacity",t.fillOpacity),e.setAttribute("fill-rule",t.fillRule||"evenodd")):e.setAttribute("fill","none"))},_updatePoly:function(t,e){this._setPath(t,dt(t._parts,e))},_updateCircle:function(t){var e=t._point,i=Math.max(Math.round(t._radius),1),n="a"+i+","+(Math.max(Math.round(t._radiusY),1)||i)+" 0 1,0 ",e=t._empty()?"M0 0":"M"+(e.x-i)+","+e.y+n+2*i+",0 "+n+2*-i+",0 ";this._setPath(t,e)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){fe(t._path)},_bringToBack:function(t){ge(t._path)}});function Gi(t){return b.svg||b.vml?new qi(t):null}b.vml&&qi.include(zt),A.include({getRenderer:function(t){t=(t=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer)||(this._renderer=this._createRenderer());return this.hasLayer(t)||this.addLayer(t),t},_getPaneRenderer:function(t){var e;return"overlayPane"!==t&&void 0!==t&&(void 0===(e=this._paneRenderers[t])&&(e=this._createRenderer({pane:t}),this._paneRenderers[t]=e),e)},_createRenderer:function(t){return this.options.preferCanvas&&Wi(t)||Gi(t)}});var Ki=yi.extend({initialize:function(t,e){yi.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=g(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});qi.create=Vi,qi.pointsToPath=dt,xi.geometryToLayer=wi,xi.coordsToLatLng=Pi,xi.coordsToLatLngs=Li,xi.latLngToCoords=Ti,xi.latLngsToCoords=Mi,xi.getFeature=zi,xi.asFeature=Ci,A.mergeOptions({boxZoom:!0});var _t=n.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){S(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){k(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){T(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),re(),Le(),this._startPoint=this._map.mouseEventToContainerPoint(t),S(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=P("div","leaflet-zoom-box",this._container),M(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var t=new f(this._point,this._startPoint),e=t.getSize();Z(this._box,t.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(T(this._box),z(this._container,"leaflet-crosshair")),ae(),Te(),k(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){1!==t.which&&1!==t.button||(this._finish(),this._moved&&(this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(a(this._resetState,this),0),t=new s(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point)),this._map.fitBounds(t).fire("boxzoomend",{boxZoomBounds:t})))},_onKeyDown:function(t){27===t.keyCode&&(this._finish(),this._clearDeferredResetState(),this._resetState())}}),Ct=(A.addInitHook("addHandler","boxZoom",_t),A.mergeOptions({doubleClickZoom:!0}),n.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom(),n=e.options.zoomDelta,i=t.originalEvent.shiftKey?i-n:i+n;"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}})),Zt=(A.addInitHook("addHandler","doubleClickZoom",Ct),A.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0}),n.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new Je(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))),M(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){z(this._map._container,"leaflet-grab"),z(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t,e=this._map;e._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=g(this._map.options.maxBounds),this._offsetLimit=_(this._map.latLngToContainerPoint(t.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(t.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))):this._offsetLimit=null,e.fire("movestart").fire("dragstart"),e.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var e,i;this._map.options.inertia&&(e=this._lastTime=+new Date,i=this._lastPos=this._draggable._absPos||this._draggable._newPos,this._positions.push(i),this._times.push(e),this._prunePositions(e)),this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1e.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t))},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,n=(n+e+i)%t-e-i,t=Math.abs(o+i)e.getMaxZoom()&&1 *:not(:last-child) { - margin-right: 1.5rem; -} - -/* Header */ - -#g-Header__ImportInput { - display: none; + gap: 1.5rem; } /* Modal */ @@ -92,7 +80,6 @@ body { background-color: white; border-radius: var(--modal-border-radius); padding: 2rem 4rem; - width: 50%; } .g-Modal__Close { @@ -101,6 +88,7 @@ body { right: 0px; font-size: 2rem !important; border-top-right-radius: var(--modal-border-radius); + padding: 5px 10px 10px; } .g-Modal__Close:hover { @@ -143,49 +131,40 @@ body { /* Form */ .g-Form__Field { + display: flex; + flex-direction: column; + row-gap: 0.5rem; margin-bottom: 1rem; } -.g-Form__Label { - margin-bottom: 0.5rem; -} - -.g-Form__Textarea { - width: 100%; - height: 5rem; -} - -.g-Form__DefaultColor { +.g-Form__Color { border: 1px solid #333333 !important; width: 20px; height: 20px; border-radius: 50%; } -/* Autocomplete */ - -:root { - --autocomplete-width: 500px; -} +/* AutoComplete */ -.g-Autocomplete { +.g-AutoComplete { position: relative; + width: 100%; } -.g-Autocomplete__Input { - width: var(--autocomplete-width); +.g-AutoComplete__Input { + width: 100%; } -.g-Autocomplete__Completion { +.g-AutoComplete__Completion { position: absolute; - width: var(--autocomplete-width); + width: 100%; background-color: white; max-height: 10rem; overflow-y: auto; border: 1px solid black; } -.g-Autocomplete__Entry { +.g-AutoComplete__Entry { display: block; width: 100%; text-align: left; @@ -194,15 +173,15 @@ body { cursor: pointer; } -.g-Autocomplete__Entry:hover { +.g-AutoComplete__Entry:hover { background-color: #DDDDDD; } -.g-Autocomplete__Clear { +.g-AutoComplete__Clear { position: absolute; right: 0.5rem; top: 50%; - transform: translateY(-50%); + transform: translateY(-53%); } /* Button */ @@ -262,16 +241,16 @@ body { #g-Map__Content { width: 100%; height: 100%; - cursor: default; + cursor: grab; } /* Marker form */ -.g-MarkerForm__AutocompleteAndIcon { +.g-MarkerForm__AutoCompleteAndIcon { position: relative; } -.g-MarkerForm__Autocomplete { +.g-MarkerForm__AutoComplete { padding-left: 1.5rem; } @@ -283,76 +262,46 @@ body { } .g-MarkerForm__IconEntry { - margin-right: 0.5rem; + display: flex; + gap: 0.5rem; +} + +.g-MarkerForm__IconElem { width: 1rem; - text-align: center; } /* Marker icon */ -:root { - --marker-box-size: 12px; - --marker-width: 25px; - --marker-peak-height: calc(var(--marker-width) * 1); - --marker-border-width: 2px; - --marker-border-color: #333333; - --marker-icon-size: 14px; -} - .g-Marker { + position: relative; + width: 100%; + height: 100%; cursor: move; } -.g-Marker__Round { +.g-Marker__Base { position: absolute; - bottom: calc(var(--marker-box-size) / 2 + var(--marker-peak-height) - var(--marker-width) * 15 / 40); - left: calc((var(--marker-width) - var(--marker-box-size)) / -2); - - width: var(--marker-width); - height: var(--marker-width); - border-radius: 50%; - border: var(--marker-border-width) solid var(--marker-border-color); - cursor: move; + width: 25px; + bottom: calc(50%); + left: 50%; + transform: translateX(-50%); } .g-Marker__Icon { position: absolute; - bottom: calc(var(--marker-box-size) / 2 + var(--marker-peak-height) - var(--marker-width) * 15 / 40); - left: calc((var(--marker-width) - var(--marker-box-size)) / -2); - font-size: var(--marker-icon-size); - + bottom: 25px; display: flex; - align-items: center; - justify-content: center; - width: var(--marker-width); - height: var(--marker-width); + width: var(--marker-icon-size); + height: var(--marker-icon-size); } -.g-Marker__PeakInner { +.g-Marker__Title { position: absolute; - bottom: calc(var(--marker-box-size) / 2 + var(--marker-border-width)); - left: calc((var(--marker-width) - var(--marker-box-size)) / -2 + var(--marker-border-width)); - - width: 0; - height: 0; - border-left: calc(var(--marker-width) / 2 - var(--marker-border-width)) solid transparent; - border-right: calc(var(--marker-width) / 2 - var(--marker-border-width)) solid transparent; - - border-top-width: calc(var(--marker-peak-height) - var(--marker-border-width)); - border-top-style: solid; -} - -.g-Marker__PeakBorder { - position: absolute; - bottom: calc(var(--marker-box-size) / 2); - left: calc((var(--marker-width) - var(--marker-box-size)) / -2); - - width: 0; - height: 0; - border-left: calc(var(--marker-width) / 2) solid transparent; - border-right: calc(var(--marker-width) / 2) solid transparent; - - border-top-width: var(--marker-peak-height); - border-top-style: solid; - border-top-color: var(--marker-border-color); + bottom: 43px; + left: 50%; + transform: translateX(-50%); + color: white; + font-weight: bold; + text-align: center; + width: 100px; } diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 527f8d9..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,13 +0,0 @@ -import resolve from '@rollup/plugin-node-resolve'; - -export default { - input: 'src/Main.bs.js', - output: { - name: 'map', - file: 'public/main.js', - format: 'iife' - }, - plugins: [ - resolve() - ] -}; diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 30b9d1b..0000000 --- a/shell.nix +++ /dev/null @@ -1,19 +0,0 @@ -with (import (builtins.fetchGit { - name = "nixpkgs-20.03"; - url = "git@github.com:nixos/nixpkgs.git"; - rev = "5272327b81ed355bbed5659b8d303cf2979b6953"; - ref = "refs/tags/20.03"; -}){}); - -mkShell { - - buildInputs = [ - bs-platform - ocamlformat - nodejs - python3 - tmux - tmuxinator - ]; - -} diff --git a/src/Color.ml b/src/Color.ml deleted file mode 100644 index d2f74c4..0000000 --- a/src/Color.ml +++ /dev/null @@ -1,38 +0,0 @@ -let from_sRGB sRGB = - if sRGB <= 0.03928 then - sRGB /. 12.92 - else - ((sRGB +. 0.055) /. 1.055) ** 2.4 - -type rgb = - { r: float - ; g: float - ; b: float - } - -(* https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef *) -let relativeLuminance (c: rgb) = - 0.2126 *. from_sRGB (c.r /. 255.) +. 0.7152 *. from_sRGB (c.g /. 255.) +. 0.0722 *. from_sRGB (c.b /. 255.) - -(* https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrastratio *) -let contrast_ratio (c1: rgb) (c2: rgb) = - let rl1 = relativeLuminance c1 in - let rl2 = relativeLuminance c2 in - - if (rl1 > rl2) then - (rl1 +. 0.05) /. (rl2 +. 0.05) - else - (rl2 +. 0.05) /. (rl1 +. 0.05) - -let from_raw color = - let get_opt = function | Some x -> x | None -> raise (Invalid_argument "Option.get") in - let div = H.div [| HA.style ("color: " ^ color) |] [| |] in - let body = Document.query_selector_unsafe "body" in - let () = Element.append_child body div in - let rgb = [%raw {| window.getComputedStyle(div).color |}] in - let () = Element.remove_child body div in - let xs = Js.String.split ", " (get_opt (Js.String.splitByRe [%re "/[()]/"] rgb).(1)) in - { r = Js.Float.fromString xs.(0) - ; g = Js.Float.fromString xs.(1) - ; b = Js.Float.fromString xs.(2) - } diff --git a/src/Lib/CSV.ml b/src/Lib/CSV.ml deleted file mode 100644 index f0366f7..0000000 --- a/src/Lib/CSV.ml +++ /dev/null @@ -1,76 +0,0 @@ -let to_string lines = - let - cell_to_string cell = - if Js.String.includes "\"" cell then - "\"" ^ (Js.String.replaceByRe [%re "/\"/g"] "\"\"" cell) ^ "\"" - else - cell - in let - line_to_string line = - line - |> Js.Array.map cell_to_string - |> Js.Array.joinWith "," - in lines - |> Js.Array.map line_to_string - |> Js.Array.joinWith "\n" - -let parse str = - let lines = [| |] in - let current_line = ref [| |] in - let current_cell = ref "" in - let in_quote = ref false in - let i = ref 0 in - let l = Js.String.length str in - let () = while !i < l do - let cc = Js.String.get str !i in - let nc = Js.String.get str (!i + 1) in - let () = - if !in_quote && cc == "\"" && nc == "\"" then - let () = current_cell := !current_cell ^ cc in - i := !i + 1 - else if cc == "\"" then - in_quote := not !in_quote - else if not !in_quote && cc == "," then - let _ = Js.Array.push !current_cell !current_line in - current_cell := "" - else if not !in_quote && ((cc == "\r" && nc == "\n") || cc == "\n" || cc == "\r") then - let _ = Js.Array.push !current_cell !current_line in - let _ = Js.Array.push !current_line lines in - let _ = current_line := [| |] in - current_cell := "" - else - current_cell := !current_cell ^ cc - in - i := !i + 1 - done - in - let _ = - if Js.String.length !current_cell > 0 then - let _ = Js.Array.push !current_cell !current_line in () - else - () - in - let _ = - if Js.Array.length !current_line > 0 then - let _ = Js.Array.push !current_line lines in () - else - () - in - lines - -let to_dicts lines = - let res = [| |] in - let () = - if Js.Array.length lines > 0 then - let header = Js.Array.unsafe_get lines 0 in - for i = 1 to Js.Array.length lines - 1 do - let line = Js.Array.unsafe_get lines i in - let dict = Js.Dict.empty() in - let () = - Js.Array.forEachi - (fun key j -> Js.Dict.set dict key (Js.Array.unsafe_get line j)) - header - in - ignore (Js.Array.push dict res) - done - in res diff --git a/src/Lib/ContextMenu.ml b/src/Lib/ContextMenu.ml deleted file mode 100644 index b9ed7d4..0000000 --- a/src/Lib/ContextMenu.ml +++ /dev/null @@ -1,40 +0,0 @@ -let px f = - Js.Float.toString f ^ "px" - -type entry = - { label: string - ; action: unit -> unit - } - -let show mouse_event actions = - let menu = - H.div - [| HA.id "g-ContextMenu" - ; HA.style ("left: " ^ (px (Event.page_x mouse_event)) ^ "; top: " ^ (px (Event.page_y mouse_event))) - |] - (Js.Array.map - (fun entry -> - H.div - [| HA.class_ "g-ContextMenu__Entry" - ; HE.on_click (fun _ -> entry.action ()) - |] - [| H.text entry.label |]) - actions) - in - let () = Element.append_child Document.body menu in - - (* Remove on click or context menu *) - let _ = - Js.Global.setTimeout - (fun _ -> - let rec f = (fun _ -> - let () = Element.remove_child Document.body menu in - let () = Element.remove_event_listener Document.body "click" f in - Element.remove_event_listener Document.body "contextmenu" f) - in - let () = Element.add_event_listener Document.body "click" f in - Element.add_event_listener Document.body "contextmenu" f - ) - 0 - in - () diff --git a/src/Lib/Dom/Document.ml b/src/Lib/Dom/Document.ml deleted file mode 100644 index 46f983a..0000000 --- a/src/Lib/Dom/Document.ml +++ /dev/null @@ -1,20 +0,0 @@ -external body : Dom.element = "body" - [@@bs.val] [@@bs.scope "document"] - -external create_element : string -> Dom.element = "createElement" - [@@bs.val] [@@bs.scope "document"] - -external create_element_ns : string -> string -> Dom.element = "createElementNS" - [@@bs.val] [@@bs.scope "document"] - -external query_selector : string -> Dom.element Js.Nullable.t = "querySelector" - [@@bs.val] [@@bs.scope "document"] - -let query_selector_unsafe id = - query_selector id |> Js.Nullable.toOption |> Js.Option.getExn - -external create_text_node : string -> Dom.element = "createTextNode" - [@@bs.val] [@@bs.scope "document"] - -external location : Location.location = "location" - [@@bs.val] [@@bs.scope "document"] diff --git a/src/Lib/Dom/Element.ml b/src/Lib/Dom/Element.ml deleted file mode 100644 index feb6003..0000000 --- a/src/Lib/Dom/Element.ml +++ /dev/null @@ -1,51 +0,0 @@ -external set_value : Dom.element -> string -> unit = "value" - [@@bs.set] - -external value : Dom.element -> string = "value" - [@@bs.get] - -external set_attribute : Dom.element -> string -> string -> unit = "setAttribute" - [@@bs.send] - -external set_class_name : Dom.element -> string -> unit = "className" - [@@bs.set] - -external add_event_listener : Dom.element -> string -> (Dom.event -> unit) -> unit - = "addEventListener" - [@@bs.send] - -external remove_event_listener : Dom.element -> string -> (Dom.event -> unit) -> unit - = "removeEventListener" - [@@bs.send] - -external append_child : Dom.element -> Dom.element -> unit = "appendChild" - [@@bs.send] - -external first_child : Dom.element -> Dom.element Js.Nullable.t = "firstChild" - [@@bs.get] - -external remove_child : Dom.element -> Dom.element -> unit = "removeChild" - [@@bs.send] - -external click : Dom.element -> unit = "click" - [@@bs.send] - -let remove_first_child element = - match Js.toOption (first_child element) with - | Some child -> - let () = remove_child element child in - true - | _ -> false - -let rec remove_children element = - if remove_first_child element then remove_children element else () - -let mount_on base element = - let () = remove_children base in - append_child base element - -external files : Dom.element -> string Js.Array.t = "files" - [@@bs.get] - -external focus : Dom.element -> unit = "focus" - [@@bs.send] diff --git a/src/Lib/Dom/Event.ml b/src/Lib/Dom/Event.ml deleted file mode 100644 index 5a9790f..0000000 --- a/src/Lib/Dom/Event.ml +++ /dev/null @@ -1,17 +0,0 @@ -external prevent_default : Dom.event -> unit = "preventDefault" - [@@bs.send] - -external stop_propagation : Dom.event -> unit = "stopPropagation" - [@@bs.send] - -external target : Dom.event -> Dom.element = "target" - [@@bs.get] - -external related_target : Dom.event -> Dom.element Js.Nullable.t = "relatedTarget" - [@@bs.get] - -external page_x : Dom.mouseEvent -> float = "pageX" - [@@bs.get] - -external page_y : Dom.mouseEvent -> float = "pageY" - [@@bs.get] diff --git a/src/Lib/Dom/H.ml b/src/Lib/Dom/H.ml deleted file mode 100644 index 7213daf..0000000 --- a/src/Lib/Dom/H.ml +++ /dev/null @@ -1,65 +0,0 @@ -(* Element creation *) - -type attribute = - | TextAttr of string * string - | EventAttr of string * (Dom.event -> unit) - -let h tag attributes children = - let element = - if tag == "svg" || tag == "path" then - Document.create_element_ns "http://www.w3.org/2000/svg" tag - else Document.create_element tag - in - let () = - Js.Array.forEach - (fun attr -> - match attr with - | TextAttr (name, value) -> - Element.set_attribute element name value - - | EventAttr (name, eventListener) -> - Element.add_event_listener element name eventListener) - attributes - in - let () = - Js.Array.forEach - (fun child -> Element.append_child element child) - children - in - element - -(* Node creation *) - -let text = Document.create_text_node - -let div = h "div" - -let span = h "span" - -let header = h "header" - -let button = h "button" - -let section = h "section" - -let svg = h "svg" - -let path = h "path" - -let form = h "form" - -let label = h "label" - -let input = h "input" - -let textarea = h "textarea" - -let i = h "i" - -let a = h "a" - -let h1 = h "h1" - -let h2 = h "h2" - -let h3 = h "h3" diff --git a/src/Lib/Dom/HA.ml b/src/Lib/Dom/HA.ml deleted file mode 100644 index ce02f2a..0000000 --- a/src/Lib/Dom/HA.ml +++ /dev/null @@ -1,43 +0,0 @@ -let concat xs ys = - let partition_class = - Js.Array.reduce - (fun (class_acc, rest_acc) z -> - match z with - | H.TextAttr ("class", c) -> (class_acc ^ " " ^ c, rest_acc) - | _ -> (class_acc, Js.Array.concat [| z |] rest_acc) - ) - ("", [| |]) - in - let (xs_class, xs_rest) = partition_class xs in - let (ys_class, ys_rest) = partition_class ys in - let rest = Js.Array.concat xs_rest ys_rest in - if xs_class == "" && ys_class == "" then - rest - else - Js.Array.concat [| H.TextAttr ("class", xs_class ^ " " ^ ys_class) |] rest - -(* Attribute creation *) - -let id v = H.TextAttr ("id", v) - -let class_ v = H.TextAttr ("class", v) - -let viewBox v = H.TextAttr ("viewBox", v) - -let d v = H.TextAttr ("d", v) - -let type_ v = H.TextAttr ("type", v) - -let min_ v = H.TextAttr ("min", v) - -let value v = H.TextAttr ("value", v) - -let for_ v = H.TextAttr ("for", v) - -let style v = H.TextAttr ("style", v) - -let href v = H.TextAttr ("href", v) - -let autocomplete v = H.TextAttr ("autocomplete", v) - -let download v = H.TextAttr ("download", v) diff --git a/src/Lib/Dom/HE.ml b/src/Lib/Dom/HE.ml deleted file mode 100644 index 03d2386..0000000 --- a/src/Lib/Dom/HE.ml +++ /dev/null @@ -1,13 +0,0 @@ -(* Event listeners *) - -let on_click f = H.EventAttr ("click", f) - -let on_input f = H.EventAttr ("input", f) - -let on_submit f = H.EventAttr ("submit", f) - -let on_blur f = H.EventAttr ("blur", f) - -let on_change f = H.EventAttr ("change", f) - -let on_focus f = H.EventAttr ("focus", f) diff --git a/src/Lib/Dom/History.ml b/src/Lib/Dom/History.ml deleted file mode 100644 index ce7a877..0000000 --- a/src/Lib/Dom/History.ml +++ /dev/null @@ -1,2 +0,0 @@ -external push_state : string -> string -> string -> unit -> unit = "pushState" - [@@bs.val] [@@bs.scope "history"] diff --git a/src/Lib/Dom/Location.ml b/src/Lib/Dom/Location.ml deleted file mode 100644 index 2c58705..0000000 --- a/src/Lib/Dom/Location.ml +++ /dev/null @@ -1,7 +0,0 @@ -external set : Dom.element -> string -> unit = "location" - [@@bs.set] - -type location - -external hash : location -> string = "hash" - [@@bs.get] diff --git a/src/Lib/Dom/Window.ml b/src/Lib/Dom/Window.ml deleted file mode 100644 index 3abc921..0000000 --- a/src/Lib/Dom/Window.ml +++ /dev/null @@ -1,2 +0,0 @@ -external window : Dom.element = "window" - [@@bs.val] diff --git a/src/Lib/File.ml b/src/Lib/File.ml deleted file mode 100644 index d3597e7..0000000 --- a/src/Lib/File.ml +++ /dev/null @@ -1,21 +0,0 @@ -let download filename content = - let a = - H.a - [| HA.href ("data:text/plain;charset=utf-8," ^ URI.encode content) - ; HA.download filename - ; HA.style "display:none" - |] - [| |] - in - let () = Element.append_child Document.body a in - let () = Element.click a in - Element.remove_child Document.body a - -external reader : unit -> Dom.element = "FileReader" - [@@bs.new] - -external read_as_text : Dom.element -> string -> unit = "readAsText" - [@@bs.send] - -external result : Dom.element -> string = "result" - [@@bs.get] diff --git a/src/Lib/FontAwesome.ml b/src/Lib/FontAwesome.ml deleted file mode 100644 index daaf954..0000000 --- a/src/Lib/FontAwesome.ml +++ /dev/null @@ -1,788 +0,0 @@ -let icons = - [| "500px" - ; "address-book" - ; "address-book-o" - ; "address-card" - ; "address-card-o" - ; "adjust" - ; "adn" - ; "align-center" - ; "align-justify" - ; "align-left" - ; "align-right" - ; "amazon" - ; "ambulance" - ; "american-sign-language-interpreting" - ; "anchor" - ; "android" - ; "angellist" - ; "angle-double-down" - ; "angle-double-left" - ; "angle-double-right" - ; "angle-double-up" - ; "angle-down" - ; "angle-left" - ; "angle-right" - ; "angle-up" - ; "apple" - ; "archive" - ; "area-chart" - ; "arrow-circle-down" - ; "arrow-circle-left" - ; "arrow-circle-o-down" - ; "arrow-circle-o-left" - ; "arrow-circle-o-right" - ; "arrow-circle-o-up" - ; "arrow-circle-right" - ; "arrow-circle-up" - ; "arrow-down" - ; "arrow-left" - ; "arrow-right" - ; "arrow-up" - ; "arrows" - ; "arrows-alt" - ; "arrows-h" - ; "arrows-v" - ; "asl-interpreting" - ; "assistive-listening-systems" - ; "asterisk" - ; "at" - ; "audio-description" - ; "automobile" - ; "backward" - ; "balance-scale" - ; "ban" - ; "bandcamp" - ; "bank" - ; "bar-chart" - ; "bar-chart-o" - ; "barcode" - ; "bars" - ; "bath" - ; "bathtub" - ; "battery" - ; "battery-0" - ; "battery-1" - ; "battery-2" - ; "battery-3" - ; "battery-4" - ; "battery-empty" - ; "battery-full" - ; "battery-half" - ; "battery-quarter" - ; "battery-three-quarters" - ; "bed" - ; "beer" - ; "behance" - ; "behance-square" - ; "bell" - ; "bell-o" - ; "bell-slash" - ; "bell-slash-o" - ; "bicycle" - ; "binoculars" - ; "birthday-cake" - ; "bitbucket" - ; "bitbucket-square" - ; "bitcoin" - ; "black-tie" - ; "blind" - ; "bluetooth" - ; "bluetooth-b" - ; "bold" - ; "bolt" - ; "bomb" - ; "book" - ; "bookmark" - ; "bookmark-o" - ; "braille" - ; "briefcase" - ; "btc" - ; "bug" - ; "building" - ; "building-o" - ; "bullhorn" - ; "bullseye" - ; "bus" - ; "buysellads" - ; "cab" - ; "calculator" - ; "calendar" - ; "calendar-check-o" - ; "calendar-minus-o" - ; "calendar-o" - ; "calendar-plus-o" - ; "calendar-times-o" - ; "camera" - ; "camera-retro" - ; "car" - ; "caret-down" - ; "caret-left" - ; "caret-right" - ; "caret-square-o-down" - ; "caret-square-o-left" - ; "caret-square-o-right" - ; "caret-square-o-up" - ; "caret-up" - ; "cart-arrow-down" - ; "cart-plus" - ; "cc" - ; "cc-amex" - ; "cc-diners-club" - ; "cc-discover" - ; "cc-jcb" - ; "cc-mastercard" - ; "cc-paypal" - ; "cc-stripe" - ; "cc-visa" - ; "certificate" - ; "chain" - ; "chain-broken" - ; "check" - ; "check-circle" - ; "check-circle-o" - ; "check-square" - ; "check-square-o" - ; "chevron-circle-down" - ; "chevron-circle-left" - ; "chevron-circle-right" - ; "chevron-circle-up" - ; "chevron-down" - ; "chevron-left" - ; "chevron-right" - ; "chevron-up" - ; "child" - ; "chrome" - ; "circle" - ; "circle-o" - ; "circle-o-notch" - ; "circle-thin" - ; "clipboard" - ; "clock-o" - ; "clone" - ; "close" - ; "cloud" - ; "cloud-download" - ; "cloud-upload" - ; "cny" - ; "code" - ; "code-fork" - ; "codepen" - ; "codiepie" - ; "coffee" - ; "cog" - ; "cogs" - ; "columns" - ; "comment" - ; "comment-o" - ; "commenting" - ; "commenting-o" - ; "comments" - ; "comments-o" - ; "compass" - ; "compress" - ; "connectdevelop" - ; "contao" - ; "copy" - ; "copyright" - ; "creative-commons" - ; "credit-card" - ; "credit-card-alt" - ; "crop" - ; "crosshairs" - ; "css3" - ; "cube" - ; "cubes" - ; "cut" - ; "cutlery" - ; "dashboard" - ; "dashcube" - ; "database" - ; "deaf" - ; "deafness" - ; "dedent" - ; "delicious" - ; "desktop" - ; "deviantart" - ; "diamond" - ; "digg" - ; "dollar" - ; "dot-circle-o" - ; "download" - ; "dribbble" - ; "drivers-license" - ; "drivers-license-o" - ; "dropbox" - ; "drupal" - ; "edge" - ; "edit" - ; "eercast" - ; "eject" - ; "ellipsis-h" - ; "ellipsis-v" - ; "empire" - ; "envelope" - ; "envelope-o" - ; "envelope-open" - ; "envelope-open-o" - ; "envelope-square" - ; "envira" - ; "eraser" - ; "etsy" - ; "eur" - ; "euro" - ; "exchange" - ; "exclamation" - ; "exclamation-circle" - ; "exclamation-triangle" - ; "expand" - ; "expeditedssl" - ; "external-link" - ; "external-link-square" - ; "eye" - ; "eye-slash" - ; "eyedropper" - ; "fa" - ; "facebook" - ; "facebook-f" - ; "facebook-official" - ; "facebook-square" - ; "fast-backward" - ; "fast-forward" - ; "fax" - ; "feed" - ; "female" - ; "fighter-jet" - ; "file" - ; "file-archive-o" - ; "file-audio-o" - ; "file-code-o" - ; "file-excel-o" - ; "file-image-o" - ; "file-movie-o" - ; "file-o" - ; "file-pdf-o" - ; "file-photo-o" - ; "file-picture-o" - ; "file-powerpoint-o" - ; "file-sound-o" - ; "file-text" - ; "file-text-o" - ; "file-video-o" - ; "file-word-o" - ; "file-zip-o" - ; "files-o" - ; "film" - ; "filter" - ; "fire" - ; "fire-extinguisher" - ; "firefox" - ; "first-order" - ; "flag" - ; "flag-checkered" - ; "flag-o" - ; "flash" - ; "flask" - ; "flickr" - ; "floppy-o" - ; "folder" - ; "folder-o" - ; "folder-open" - ; "folder-open-o" - ; "font" - ; "font-awesome" - ; "fonticons" - ; "fort-awesome" - ; "forumbee" - ; "forward" - ; "foursquare" - ; "free-code-camp" - ; "frown-o" - ; "futbol-o" - ; "gamepad" - ; "gavel" - ; "gbp" - ; "ge" - ; "gear" - ; "gears" - ; "genderless" - ; "get-pocket" - ; "gg" - ; "gg-circle" - ; "gift" - ; "git" - ; "git-square" - ; "github" - ; "github-alt" - ; "github-square" - ; "gitlab" - ; "gittip" - ; "glass" - ; "glide" - ; "glide-g" - ; "globe" - ; "google" - ; "google-plus" - ; "google-plus-circle" - ; "google-plus-official" - ; "google-plus-square" - ; "google-wallet" - ; "graduation-cap" - ; "gratipay" - ; "grav" - ; "group" - ; "h-square" - ; "hacker-news" - ; "hand-grab-o" - ; "hand-lizard-o" - ; "hand-o-down" - ; "hand-o-left" - ; "hand-o-right" - ; "hand-o-up" - ; "hand-paper-o" - ; "hand-peace-o" - ; "hand-pointer-o" - ; "hand-rock-o" - ; "hand-scissors-o" - ; "hand-spock-o" - ; "hand-stop-o" - ; "handshake-o" - ; "hard-of-hearing" - ; "hashtag" - ; "hdd-o" - ; "header" - ; "headphones" - ; "heart" - ; "heart-o" - ; "heartbeat" - ; "history" - ; "home" - ; "hospital-o" - ; "hotel" - ; "hourglass" - ; "hourglass-1" - ; "hourglass-2" - ; "hourglass-3" - ; "hourglass-end" - ; "hourglass-half" - ; "hourglass-o" - ; "hourglass-start" - ; "houzz" - ; "html5" - ; "i-cursor" - ; "id-badge" - ; "id-card" - ; "id-card-o" - ; "ils" - ; "image" - ; "imdb" - ; "inbox" - ; "indent" - ; "industry" - ; "info" - ; "info-circle" - ; "inr" - ; "instagram" - ; "institution" - ; "internet-explorer" - ; "intersex" - ; "ioxhost" - ; "italic" - ; "joomla" - ; "jpy" - ; "jsfiddle" - ; "key" - ; "keyboard-o" - ; "krw" - ; "language" - ; "laptop" - ; "lastfm" - ; "lastfm-square" - ; "leaf" - ; "leanpub" - ; "legal" - ; "lemon-o" - ; "level-down" - ; "level-up" - ; "life-bouy" - ; "life-buoy" - ; "life-ring" - ; "life-saver" - ; "lightbulb-o" - ; "line-chart" - ; "link" - ; "linkedin" - ; "linkedin-square" - ; "linode" - ; "linux" - ; "list" - ; "list-alt" - ; "list-ol" - ; "list-ul" - ; "location-arrow" - ; "lock" - ; "long-arrow-down" - ; "long-arrow-left" - ; "long-arrow-right" - ; "long-arrow-up" - ; "low-vision" - ; "magic" - ; "magnet" - ; "mail-forward" - ; "mail-reply" - ; "mail-reply-all" - ; "male" - ; "map" - ; "map-marker" - ; "map-o" - ; "map-pin" - ; "map-signs" - ; "mars" - ; "mars-double" - ; "mars-stroke" - ; "mars-stroke-h" - ; "mars-stroke-v" - ; "maxcdn" - ; "meanpath" - ; "medium" - ; "medkit" - ; "meetup" - ; "meh-o" - ; "mercury" - ; "microchip" - ; "microphone" - ; "microphone-slash" - ; "minus" - ; "minus-circle" - ; "minus-square" - ; "minus-square-o" - ; "mixcloud" - ; "mobile" - ; "mobile-phone" - ; "modx" - ; "money" - ; "moon-o" - ; "mortar-board" - ; "motorcycle" - ; "mouse-pointer" - ; "music" - ; "navicon" - ; "neuter" - ; "newspaper-o" - ; "object-group" - ; "object-ungroup" - ; "odnoklassniki" - ; "odnoklassniki-square" - ; "opencart" - ; "openid" - ; "opera" - ; "optin-monster" - ; "outdent" - ; "pagelines" - ; "paint-brush" - ; "paper-plane" - ; "paper-plane-o" - ; "paperclip" - ; "paragraph" - ; "paste" - ; "pause" - ; "pause-circle" - ; "pause-circle-o" - ; "paw" - ; "paypal" - ; "pencil" - ; "pencil-square" - ; "pencil-square-o" - ; "percent" - ; "phone" - ; "phone-square" - ; "photo" - ; "picture-o" - ; "pie-chart" - ; "pied-piper" - ; "pied-piper-alt" - ; "pied-piper-pp" - ; "pinterest" - ; "pinterest-p" - ; "pinterest-square" - ; "plane" - ; "play" - ; "play-circle" - ; "play-circle-o" - ; "plug" - ; "plus" - ; "plus-circle" - ; "plus-square" - ; "plus-square-o" - ; "podcast" - ; "power-off" - ; "print" - ; "product-hunt" - ; "puzzle-piece" - ; "qq" - ; "qrcode" - ; "question" - ; "question-circle" - ; "question-circle-o" - ; "quora" - ; "quote-left" - ; "quote-right" - ; "ra" - ; "random" - ; "ravelry" - ; "rebel" - ; "recycle" - ; "reddit" - ; "reddit-alien" - ; "reddit-square" - ; "refresh" - ; "registered" - ; "remove" - ; "renren" - ; "reorder" - ; "repeat" - ; "reply" - ; "reply-all" - ; "resistance" - ; "retweet" - ; "rmb" - ; "road" - ; "rocket" - ; "rotate-left" - ; "rotate-right" - ; "rouble" - ; "rss" - ; "rss-square" - ; "rub" - ; "ruble" - ; "rupee" - ; "s15" - ; "safari" - ; "save" - ; "scissors" - ; "scribd" - ; "search" - ; "search-minus" - ; "search-plus" - ; "sellsy" - ; "send" - ; "send-o" - ; "server" - ; "share" - ; "share-alt" - ; "share-alt-square" - ; "share-square" - ; "share-square-o" - ; "shekel" - ; "sheqel" - ; "shield" - ; "ship" - ; "shirtsinbulk" - ; "shopping-bag" - ; "shopping-basket" - ; "shopping-cart" - ; "shower" - ; "sign-in" - ; "sign-language" - ; "sign-out" - ; "signal" - ; "signing" - ; "simplybuilt" - ; "sitemap" - ; "skyatlas" - ; "skype" - ; "slack" - ; "sliders" - ; "slideshare" - ; "smile-o" - ; "snapchat" - ; "snapchat-ghost" - ; "snapchat-square" - ; "snowflake-o" - ; "soccer-ball-o" - ; "sort" - ; "sort-alpha-asc" - ; "sort-alpha-desc" - ; "sort-amount-asc" - ; "sort-amount-desc" - ; "sort-asc" - ; "sort-desc" - ; "sort-down" - ; "sort-numeric-asc" - ; "sort-numeric-desc" - ; "sort-up" - ; "soundcloud" - ; "space-shuttle" - ; "spinner" - ; "spoon" - ; "spotify" - ; "square" - ; "square-o" - ; "stack-exchange" - ; "stack-overflow" - ; "star" - ; "star-half" - ; "star-half-empty" - ; "star-half-full" - ; "star-half-o" - ; "star-o" - ; "steam" - ; "steam-square" - ; "step-backward" - ; "step-forward" - ; "stethoscope" - ; "sticky-note" - ; "sticky-note-o" - ; "stop" - ; "stop-circle" - ; "stop-circle-o" - ; "street-view" - ; "strikethrough" - ; "stumbleupon" - ; "stumbleupon-circle" - ; "subscript" - ; "subway" - ; "suitcase" - ; "sun-o" - ; "superpowers" - ; "superscript" - ; "support" - ; "table" - ; "tablet" - ; "tachometer" - ; "tag" - ; "tags" - ; "tasks" - ; "taxi" - ; "telegram" - ; "television" - ; "tencent-weibo" - ; "terminal" - ; "text-height" - ; "text-width" - ; "th" - ; "th-large" - ; "th-list" - ; "themeisle" - ; "thermometer" - ; "thermometer-0" - ; "thermometer-1" - ; "thermometer-2" - ; "thermometer-3" - ; "thermometer-4" - ; "thermometer-empty" - ; "thermometer-full" - ; "thermometer-half" - ; "thermometer-quarter" - ; "thermometer-three-quarters" - ; "thumb-tack" - ; "thumbs-down" - ; "thumbs-o-down" - ; "thumbs-o-up" - ; "thumbs-up" - ; "ticket" - ; "times" - ; "times-circle" - ; "times-circle-o" - ; "times-rectangle" - ; "times-rectangle-o" - ; "tint" - ; "toggle-down" - ; "toggle-left" - ; "toggle-off" - ; "toggle-on" - ; "toggle-right" - ; "toggle-up" - ; "trademark" - ; "train" - ; "transgender" - ; "transgender-alt" - ; "trash" - ; "trash-o" - ; "tree" - ; "trello" - ; "tripadvisor" - ; "trophy" - ; "truck" - ; "try" - ; "tty" - ; "tumblr" - ; "tumblr-square" - ; "turkish-lira" - ; "tv" - ; "twitch" - ; "twitter" - ; "twitter-square" - ; "umbrella" - ; "underline" - ; "undo" - ; "universal-access" - ; "university" - ; "unlink" - ; "unlock" - ; "unlock-alt" - ; "unsorted" - ; "upload" - ; "usb" - ; "usd" - ; "user" - ; "user-circle" - ; "user-circle-o" - ; "user-md" - ; "user-o" - ; "user-plus" - ; "user-secret" - ; "user-times" - ; "users" - ; "vcard" - ; "vcard-o" - ; "venus" - ; "venus-double" - ; "venus-mars" - ; "viacoin" - ; "viadeo" - ; "viadeo-square" - ; "video-camera" - ; "vimeo" - ; "vimeo-square" - ; "vine" - ; "vk" - ; "volume-control-phone" - ; "volume-down" - ; "volume-off" - ; "volume-up" - ; "warning" - ; "wechat" - ; "weibo" - ; "weixin" - ; "whatsapp" - ; "wheelchair" - ; "wheelchair-alt" - ; "wifi" - ; "wikipedia-w" - ; "window-close" - ; "window-close-o" - ; "window-maximize" - ; "window-minimize" - ; "window-restore" - ; "windows" - ; "won" - ; "wordpress" - ; "wpbeginner" - ; "wpexplorer" - ; "wpforms" - ; "wrench" - ; "xing" - ; "xing-square" - ; "y-combinator" - ; "y-combinator-square" - ; "yahoo" - ; "yc" - ; "yc-square" - ; "yelp" - ; "yen" - ; "yoast" - ; "youtube" - ; "youtube-play" - ; "youtube-square" - |] diff --git a/src/Lib/Fun.ml b/src/Lib/Fun.ml deleted file mode 100644 index bf1eb38..0000000 --- a/src/Lib/Fun.ml +++ /dev/null @@ -1,2 +0,0 @@ -let flip f b a = - f a b diff --git a/src/Lib/Leaflet.ml b/src/Lib/Leaflet.ml deleted file mode 100644 index 282b5b0..0000000 --- a/src/Lib/Leaflet.ml +++ /dev/null @@ -1,89 +0,0 @@ -type layer - -type map_options = - { attributionControl : bool - } - -external map : string -> map_options -> layer = "map" - [@@bs.val] [@@bs.scope "L"] - -external setView : layer -> float array -> int -> unit = "setView" - [@@bs.send] - -type event - -external on : layer -> string -> (event -> unit) -> unit = "on" - [@@bs.send] - -type lat_lng = - { lat : float; - lng : float; - } - -external original_event : event -> Dom.mouseEvent = "originalEvent" - [@@bs.get] - -external lat_lng : event -> lat_lng = "latlng" - [@@bs.get] - -external target : event -> layer = "target" - [@@bs.get] - -external get_lat_lng : layer -> unit -> lat_lng = "getLatLng" - [@@bs.send] - -external title_layer : string -> layer = "tileLayer" - [@@bs.val] [@@bs.scope "L"] - -external add_layer : layer -> layer -> unit = "addLayer" - [@@bs.send] - -external clear_layers : layer -> unit = "clearLayers" - [@@bs.send] - -external remove : layer -> unit = "remove" - [@@bs.send] - -external get_layers : layer -> unit -> layer array = "getLayers" - [@@bs.send] - -(* Fit bounds *) - -external feature_group : layer array -> layer = "featureGroup" - [@@bs.val] [@@bs.scope "L"] - -type bounds - -external get_bounds : layer -> unit -> bounds = "getBounds" - [@@bs.send] - -type fit_bounds_options = - { padding: float array - } - -external fit_bounds : layer -> bounds -> fit_bounds_options -> unit = "fitBounds" - [@@bs.send] - -(* Icon *) - -type icon - -type div_icon_input = - { className : string - ; popupAnchor : float array - ; html : Dom.element - } - -external div_icon : div_icon_input -> icon = "divIcon" - [@@bs.val] [@@bs.scope "L"] - -(* Marker *) - -type markerInput = - { title : string - ; icon : icon - ; draggable : bool - } - -external marker : lat_lng -> markerInput -> layer = "marker" - [@@bs.val] [@@bs.scope "L"] diff --git a/src/Lib/Modal.ml b/src/Lib/Modal.ml deleted file mode 100644 index 5db88cd..0000000 --- a/src/Lib/Modal.ml +++ /dev/null @@ -1,25 +0,0 @@ -let hide () = - let modal = Document.query_selector_unsafe "#g-Modal" in - Element.remove_child Document.body modal - -let show content = - let view = - H.div - [| HA.id "g-Modal" |] - [| H.div - [| HA.class_ "g-Modal__Curtain" - ; HE.on_click (fun _ -> hide ()) - |] - [| |] - ; H.div - [| HA.class_ "g-Modal__Window" |] - [| Button.raw - [| HA.class_ "g-Modal__Close" - ; HE.on_click (fun _ -> hide ()) - |] - [| H.div [| HA.class_ "fa fa-close" |] [| |] |] - ; content - |] - |] - in - Element.append_child Document.body view diff --git a/src/Lib/Option.ml b/src/Lib/Option.ml deleted file mode 100644 index 1158b96..0000000 --- a/src/Lib/Option.ml +++ /dev/null @@ -1,9 +0,0 @@ -let withDefault default opt = - match opt with - | Some v -> v - | None -> default - -let map f opt = - match opt with - | Some v -> Some (f v) - | None -> None diff --git a/src/Lib/String.ml b/src/Lib/String.ml deleted file mode 100644 index be16d0e..0000000 --- a/src/Lib/String.ml +++ /dev/null @@ -1,35 +0,0 @@ -let format_float precision f = - let str = Js.Float.toString f in - match Js.String.split "." str with - | [| a ; b |] -> a ^ "." ^ (Js.String.substring ~from:0 ~to_:precision b) - | _ -> str - -external btoa : string -> string = "btoa" - [@@bs.val] [@@bs.scope "window"] - -external atob : string -> string = "atob" - [@@bs.val] [@@bs.scope "window"] - -external unescape : string -> string = "unescape" - [@@bs.val] - -external escape : string -> string = "escape" - [@@bs.val] - -external encodeURIComponent : string -> string = "encodeURIComponent" - [@@bs.val] - -external decodeURIComponent : string -> string = "decodeURIComponent" - [@@bs.val] - -let encode str = - str - |> encodeURIComponent - |> unescape - |> btoa - -let decode str = - str - |> atob - |> escape - |> decodeURIComponent diff --git a/src/Lib/URI.ml b/src/Lib/URI.ml deleted file mode 100644 index 705bc7b..0000000 --- a/src/Lib/URI.ml +++ /dev/null @@ -1,2 +0,0 @@ -external encode : string -> string = "encodeURIComponent" - [@@bs.val] diff --git a/src/Main.ml b/src/Main.ml deleted file mode 100644 index 9216b35..0000000 --- a/src/Main.ml +++ /dev/null @@ -1,3 +0,0 @@ -let () = - let body = Document.query_selector_unsafe "body" in - Element.append_child body (Map.render ()) diff --git a/src/State.ml b/src/State.ml deleted file mode 100644 index c1cb99d..0000000 --- a/src/State.ml +++ /dev/null @@ -1,119 +0,0 @@ -type marker_state = - { pos : Leaflet.lat_lng - ; name : string - ; color : string - ; icon : string - } - -let remove state pos = - Js.Array.filter (fun m -> m.pos != pos) state - -let update state previousPos marker = - Js.Array.concat [| marker |] (remove state previousPos) - -let last_added state = - if Js.Array.length state > 0 then - Some state.(0) - else - None - -(* URL Serialization *) - -let sep = "|" - -let marker_to_string marker = - [| String.format_float 6 marker.pos.lat - ; String.format_float 6 marker.pos.lng - ; marker.name - ; marker.color - ; marker.icon - |] - |> Js.Array.joinWith sep - -let to_url_string state = - state - |> Js.Array.map marker_to_string - |> Js.Array.joinWith sep - |> String.encode - -let from_url_string str = - let (_, _, res) = Js.Array.reduce - (fun (acc_str, acc_marker, acc_state) c -> - let length = Js.Array.length acc_marker in - if c != sep then - (acc_str ^ c, acc_marker, acc_state) - else if c == sep && length < 4 then - ("", Js.Array.concat [| acc_str |] acc_marker, acc_state) - else - let marker = - { pos = - { lat = Js.Float.fromString acc_marker.(0) - ; lng = Js.Float.fromString acc_marker.(1) - } - ; name = acc_marker.(2) - ; color = acc_marker.(3) - ; icon = acc_str - } - in ("", [| |], Js.Array.concat acc_state [| marker |]) - ) - ("", [| |], [| |]) - (Js.Array.from (Js.String.castToArrayLike ((String.decode str) ^ sep))) - in res - -(* Colors *) - -let default_color = "#3f92cf" - -let colors = - Js.Array.reduce - (fun colors marker -> - if Js.Array.indexOf marker.color colors == -1 then - Js.Array.concat [| marker.color |] colors - else - colors) - [| |] - -(* CSV Serialization *) - -let lat_key = "lat" -let lng_key = "lng" -let name_key = "name" -let color_key = "color" -let icon_key = "icon" - -let to_csv_string state = - let to_csv_line marker = - [| Js.Float.toString marker.pos.lat - ; Js.Float.toString marker.pos.lng - ; marker.name - ; marker.color - ; marker.icon - |] - in let - header = - [| lat_key; lng_key; name_key; color_key; icon_key |] - in - state - |> Js.Array.map to_csv_line - |> Fun.flip Js.Array.concat [| header |] - |> CSV.to_string - -let from_dicts dicts = - Js.Array.map - (fun dict -> - (* let get key default = Js.Dict.get dict key |> Option.withDefault default in *) - { pos = - { lat = - Js.Dict.get dict lat_key - |> Option.map Js.Float.fromString - |> Option.withDefault 0.0 - ; lng = - Js.Dict.get dict lng_key - |> Option.map Js.Float.fromString - |> Option.withDefault 0.0 - } - ; name = Js.Dict.get dict name_key |> Option.withDefault "" - ; color = Js.Dict.get dict color_key |> Option.withDefault default_color - ; icon = Js.Dict.get dict icon_key |> Option.withDefault "" - }) - dicts diff --git a/src/View/Button.ml b/src/View/Button.ml deleted file mode 100644 index b4641d2..0000000 --- a/src/View/Button.ml +++ /dev/null @@ -1,19 +0,0 @@ -let raw attrs content = - H.button - (HA.concat [| HA.class_ "g-Button__Raw" |] attrs) - content - -let text attrs content = - H.button - (HA.concat [| HA.class_ "g-Button__Text" |] attrs) - content - -let action attrs content = - H.button - (HA.concat [| HA.class_ "g-Button__Action" |] attrs) - content - -let cancel attrs content = - H.button - (HA.concat [| HA.class_ "g-Button__Cancel" |] attrs) - content diff --git a/src/View/Form.ml b/src/View/Form.ml deleted file mode 100644 index cec49d6..0000000 --- a/src/View/Form.ml +++ /dev/null @@ -1,65 +0,0 @@ -let input id label attrs = - H.div - [| HA.class_ "g-Form__Field" |] - [| H.div - [| HA.class_ "g-Form__Label" |] - [| H.label - [| HA.for_ id |] - [| H.text label |] - |] - ; H.input - (HA.concat attrs [| HA.id id |]) - [| |] - |] - -let color_input default_colors id label init_value on_input = - let - input = - H.input - [| HA.id id - ; HE.on_input (fun e -> on_input (Element.value (Event.target e))) - ; HA.value init_value - ; HA.type_ "color" - |] - [| |] - in - H.div - [| HA.class_ "g-Form__Field" |] - [| H.div - [| HA.class_ "g-Form__Label" |] - [| H.label - [| HA.for_ id |] - [| H.text label |] - |] - ; Layout.line - [| |] - (default_colors - |> Js.Array.map (fun color -> - Button.raw - [| HA.class_ "g-Form__DefaultColor" - ; HA.style ("background-color: " ^ color) - ; HE.on_click (fun _ -> - let () = Element.set_value input color in - on_input color) - ; HA.type_ "button" - |] - [| |]) - |> Fun.flip Js.Array.concat [| input |]) - |] - -let textarea id label init_value on_input = - H.div - [| HA.class_ "g-Form__Field" |] - [| H.div - [| HA.class_ "g-Form__Label" |] - [| H.label - [| HA.for_ id |] - [| H.text label |] - |] - ; H.textarea - [| HA.id id - ; HA.class_ "g-Form__Textarea" - ; HE.on_input (fun e -> on_input (Element.value (Event.target e))) - |] - [| H.text init_value |] - |] diff --git a/src/View/Form/Autocomplete.ml b/src/View/Form/Autocomplete.ml deleted file mode 100644 index 98e4b43..0000000 --- a/src/View/Form/Autocomplete.ml +++ /dev/null @@ -1,80 +0,0 @@ -let search s xs = - Js.Array.filter (Js.String.includes s) xs - -let render_completion render_entry on_select entries = - H.div - [| HA.class_ "g-Autocomplete__Completion" |] - (entries - |> Js.Array.map (fun c -> - Button.raw - [| HA.class_ "g-Autocomplete__Entry" - ; HA.type_ "button" - ; HE.on_click (fun e -> - let () = Event.stop_propagation e in - let () = Event.prevent_default e in - on_select c) - |] - (render_entry c))) - -let create attrs id values render_entry on_input = - - let completion = - H.div [| |] [| |] - in - - let update_completion target value = - let entries = search value values in - Element.mount_on completion (render_completion - render_entry - (fun selected -> - let () = Element.set_value target selected in - let () = Element.remove_children completion in - on_input selected) - entries) - in - - let hide_completion () = - Element.mount_on completion (H.text "") - in - - let - input = - H.input - (HA.concat - attrs - [| HA.id id - ; HA.class_ "g-Autocomplete__Input" - ; HA.autocomplete "off" - ; HE.on_focus (fun e -> - let target = Event.target e in - let value = Element.value target in - update_completion target value) - ; HE.on_input (fun e -> - let target = Event.target e in - let value = Element.value target in - let () = update_completion target value in - on_input value) - |]) - [| |] - in - - let () = - Element.add_event_listener input "blur" (fun e -> - if Js.isNullable (Event.related_target e) then - hide_completion ()) - in - - H.div - [| HA.class_ "g-Autocomplete" |] - [| input - ; completion - ; Button.raw - [| HA.class_ "g-Autocomplete__Clear fa fa-close" - ; HA.type_ "button" - ; HE.on_click (fun _ -> - let () = on_input "" in - let () = Element.set_value input "" in - Element.focus input) - |] - [| |] - |] diff --git a/src/View/Layout.ml b/src/View/Layout.ml deleted file mode 100644 index db1e234..0000000 --- a/src/View/Layout.ml +++ /dev/null @@ -1,9 +0,0 @@ -let section attrs content = - H.div - (HA.concat attrs [| HA.class_ "g-Layout__Section" |]) - content - -let line attrs content = - H.div - (HA.concat attrs [| HA.class_ "g-Layout__Line" |]) - content diff --git a/src/View/Map.ml b/src/View/Map.ml deleted file mode 100644 index 6e2611e..0000000 --- a/src/View/Map.ml +++ /dev/null @@ -1,131 +0,0 @@ -let state_from_hash () = - let hash = Js.String.sliceToEnd ~from:1 (Location.hash Document.location) in - State.from_url_string hash - -let rec reload_from_hash state map markers focus = - let update_state new_state = - let () = History.push_state "" "" ("#" ^ State.to_url_string new_state) () in - reload_from_hash state map markers false - in - - let on_remove pos = - update_state (State.remove !state pos) in - - let on_update previousPos pos name color icon = - update_state (State.update !state previousPos { pos = pos; name = name; color = color; icon = icon }) in - - let () = - if Js.Array.length (Leaflet.get_layers markers ()) > 0 then - Leaflet.clear_layers markers - else - () - in - let () = state := state_from_hash () in - let colors = State.colors !state in - let () = - Js.Array.forEach - (fun (m: State.marker_state) -> Leaflet.add_layer markers (Marker.create on_remove on_update colors m.pos m.name m.color m.icon)) - !state - in - if focus then - if Js.Array.length (Leaflet.get_layers markers ()) > 0 then - Leaflet.fit_bounds map (Leaflet.get_bounds markers ()) { padding = [| 50.; 50. |] } - else - Leaflet.setView map [| 51.505; -0.09 |] 2 - else - () - -let mapView state map markers = - H.div - [| HA.class_ "g-Layout__Page" |] - [| H.div - [| HA.class_ "g-Layout__Header" |] - [| H.a - [| HA.class_ "g-Layout__Home" - ; HA.href "#" - |] - [| H.text "Map" |] - ; Layout.line - [| HA.class_ "g-Layout__HeaderImportExport" |] - [| H.input - [| HA.id "g-Header__ImportInput" - ; HA.type_ "file" - ; HE.on_change (fun e -> - match !map with - | Some map -> - let reader = File.reader () in - let () = Element.add_event_listener reader "load" (fun _ -> - let str = File.result reader in - let new_state = State.from_dicts (CSV.to_dicts (CSV.parse str)) in - let () = History.push_state "" "" ("#" ^ State.to_url_string new_state) () in - reload_from_hash state map markers true) - in - File.read_as_text reader ( - Js.Array.unsafe_get (Element.files (Event.target e)) 0) - | _ -> - ()) - |] - [| |] - ; H.label - [| HA.for_ "g-Header__ImportInput" - ; HA.class_ "g-Button__Text" - |] - [| H.text "Import" |] - ; Button.text - [| HE.on_click (fun _ -> File.download "map.csv" (State.to_csv_string !state)) |] - [| H.text "Export" |] - |] - |] - ; H.div - [| HA.class_ "g-Map" |] - [| H.div - [| HA.id "g-Map__Content" |] - [||] - |] - |] - -let install_map state map_ref markers = - let map = Leaflet.map "g-Map__Content" { attributionControl = false } in - let () = map_ref := Some map in - let title_layer = Leaflet.title_layer "http://{s}.tile.osm.org/{z}/{x}/{y}.png" in - let () = Leaflet.add_layer map markers in - let () = Leaflet.add_layer map title_layer in - - (* Init markers from url *) - let () = reload_from_hash state map markers true in - - (* Reload the map if the URL changes *) - let () = Element.add_event_listener Window.window "popstate" (fun _ -> - reload_from_hash state map markers true) - in - - let add_marker pos name color icon = - let new_marker = { State.pos = pos; name = name; color = color; icon = icon } in - let new_state = State.update !state pos new_marker in - let () = History.push_state "" "" ("#" ^ State.to_url_string new_state) () in - reload_from_hash state map markers false - in - - (* Context menu *) - Leaflet.on map "contextmenu" (fun event -> - ContextMenu.show - (Leaflet.original_event event) - [| { label = "Add a marker" - ; action = (fun _ -> - let pos = Leaflet.lat_lng event in - let marker = - match State.last_added !state with - | Some m -> { m with pos = pos; name = "" } - | _ -> { pos = pos; name = ""; color = "#3f92cf"; icon = "" } - in - let colors = State.colors !state in - Modal.show (Marker.form (add_marker pos) colors marker.name marker.color marker.icon)) - } - |]) - -let render () = - let state = ref (state_from_hash ()) in - let map = ref None in - let markers = Leaflet.feature_group [| |] in - let _ = Js.Global.setTimeout (fun _ -> install_map state map markers) 0 in - mapView state map markers diff --git a/src/View/Map/Icon.ml b/src/View/Map/Icon.ml deleted file mode 100644 index 8737f43..0000000 --- a/src/View/Map/Icon.ml +++ /dev/null @@ -1,32 +0,0 @@ -let create name color = - let c = Color.from_raw color in - let crBlack = Color.contrast_ratio { r = 0.; g = 0.; b = 0. } c in - let crWhite = Color.contrast_ratio { r = 255.; g = 255.; b = 255. } c in - let textCol = if crBlack > crWhite then "black" else "white" in - Leaflet.div_icon - { className = "" - ; popupAnchor = [| 0.; -34. |] - ; html = - H.div - [| HA.class_ "g-Marker" |] - [| H.div - [| HA.class_ "g-Marker__Round" - ; HA.style ("background-color: " ^ color) - |] - [| |] - ; H.div [| HA.class_ "g-Marker__PeakBorder" |] [| |] - ; H.div - [| HA.class_ "g-Marker__PeakInner" - ; HA.style ("border-top-color: " ^ color) - |] - [| |] - ; H.div - [| HA.class_ "g-Marker__Icon" |] - [| H.i - [| HA.class_ ("fa fa-" ^ name) - ; HA.style ("color: " ^ textCol) - |] - [| |] - |] - |] - } diff --git a/src/View/Map/Marker.ml b/src/View/Map/Marker.ml deleted file mode 100644 index 1c0c0d6..0000000 --- a/src/View/Map/Marker.ml +++ /dev/null @@ -1,105 +0,0 @@ -let form on_validate colors init_name init_color init_icon = - let name = ref init_name in - let color = ref init_color in - let icon = ref init_icon in - let on_validate () = - let () = on_validate !name !color !icon in - Modal.hide () - in - H.div - [| |] - [| Layout.section - [| |] - [| H.form - [| HA.class_ "g-MarkerForm" - ; HE.on_submit (fun e -> - let () = Event.prevent_default e in - on_validate ()) - |] - [| Layout.section - [| |] - [| Form.input - "g-MarkerForm__Name" - "Name" - [| HE.on_input (fun e -> name := (Element.value (Event.target e))) - ; HA.value init_name - |] - ; Form.color_input colors "g-MarkerForm__Color" "Color" init_color (fun newColor -> color := newColor) - ; H.div - [| HA.class_ "g-Form__Field" |] - [| H.div - [| HA.class_ "g-Form__Label" |] - [| H.label - [| HA.for_ "g-MarkerForm__IconInput" |] - [| H.text "Icon" |] - |] - ; let dom_icon = H.div [| HA.class_ ("fa fa-" ^ !icon) |] [| |] in - Layout.line - [| HA.class_ "g-MarkerForm__AutocompleteAndIcon" |] - [| Autocomplete.create - [| HA.value init_icon - ; HA.class_ "g-MarkerForm__Autocomplete" - |] - "g-MarkerForm__IconInput" - FontAwesome.icons - (fun icon -> - [| H.div - [| HA.class_ ("g-MarkerForm__IconEntry fa fa-" ^ icon) |] - [| |] - ; H.text icon - |]) - (fun newIcon -> - let () = icon := newIcon in - Element.set_class_name dom_icon ("fa fa-" ^ newIcon)) - ; H.div [| HA.class_ "g-MarkerForm__Icon" |] [| dom_icon |] - |] - |] - |] - ; Layout.line - [| |] - [| Button.action - [| HE.on_click (fun _ -> on_validate ()) |] - [| H.text "Save" |] - ; Button.cancel - [| HE.on_click (fun _ -> Modal.hide ()) - ; HA.type_ "button" - |] - [| H.text "Cancel" |] - |] - |] - |] - |] - - -let create on_remove on_update colors pos init_name init_color init_icon = - let marker = - Leaflet.marker pos - { title = init_name - ; icon = Icon.create init_icon init_color - ; draggable = true - } - in - - (* Context menu *) - let () = Leaflet.on marker "contextmenu" (fun event -> - ContextMenu.show - (Leaflet.original_event event) - [| { label = "Modify" - ; action = fun _ -> - Modal.show (form (on_update pos pos) colors init_name init_color init_icon) - } - ; { label = "Remove" - ; action = fun _ -> on_remove pos - } - |]) - in - - (* Move the cursor on drag *) - let () = Leaflet.on marker "dragend" (fun e -> - let newPos = Leaflet.get_lat_lng (Leaflet.target e) () in - on_update pos newPos init_name init_color init_icon) in - - let () = Leaflet.on marker "dblclick" (fun _ -> - Modal.show (form (on_update pos pos) colors init_name init_color init_icon)) in - - marker diff --git a/src/lib/autoComplete.ts b/src/lib/autoComplete.ts new file mode 100644 index 0000000..b0a79eb --- /dev/null +++ b/src/lib/autoComplete.ts @@ -0,0 +1,115 @@ +import { h, Children, concatClassName } from 'lib/h' +import * as Button from 'lib/button' + +export function create( + attrs: object, + id: string, + keys: string[], + renderEntry: (entry: string) => Element, + onInput: (value: string) => void +): Element { + const completion = h('div', {}) + + const updateCompletion = (target: EventTarget, value: string) => { + const entries = search(value, keys) + mountOn( + completion, + renderCompletion( + renderEntry, + selected => { + (target as HTMLInputElement).value = selected + completion.remove + removeChildren(completion) + onInput(selected) + }, + entries + ) + ) + } + + const input = h('input', + concatClassName( + { ...attrs, + id, + autocomplete: 'off', + onfocus: (e: Event) => { + if (e.target !== null) { + const target = e.target as HTMLInputElement + updateCompletion(target, target.value) + } + }, + oninput: (e: Event) => { + if (e.target !== null) { + const target = e.target as HTMLInputElement + updateCompletion(target, target.value) + onInput(target.value) + } + } + }, + 'g-AutoComplete__Input' + ) + ) as HTMLInputElement + + input.addEventListener('blur', (e: MouseEvent) => { + if (e.relatedTarget === null) { + removeChildren(completion) + } + }) + + return h('div', + { className: 'g-AutoComplete' }, + input, + completion, + Button.raw( + { className: 'g-AutoComplete__Clear', + type: 'button', + onclick: () => { + onInput('') + input.value = '' + input.focus() + } + }, + 'x' + ) + ) +} + +function renderCompletion( + renderEntry: (entry: string) => Element, + onSelect: (entry: string) => void, + entries: string[] +): Element { + return h('div', + { className: 'g-AutoComplete__Completion' }, + ...entries.map(c => + Button.raw( + { className: 'g-AutoComplete__Entry', + type: 'button', + onclick: (e: Event) => { + e.stopPropagation() + e.preventDefault() + onSelect(c) + } + }, + renderEntry(c) + ) + ) + ) +} + +function search(s: string, xs: string[]): string[] { + return xs.filter(x => x.includes(s)) +} + +function mountOn(base: Element, ...children: Element[]) { + removeChildren(base) + children.forEach(child => base.appendChild(child)) +} + +function removeChildren(base: Element) { + const firstChild = base.firstChild + if (firstChild !== null) { + base.removeChild(firstChild) + removeChildren(base) + } +} diff --git a/src/lib/base.ts b/src/lib/base.ts new file mode 100644 index 0000000..59c91cc --- /dev/null +++ b/src/lib/base.ts @@ -0,0 +1,32 @@ +export const b2: string[] = + '01'.split('') + +export const b16: string[] = + '0123456789abcdef'.split('') + +export const b62: string[] = + '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('') + +export function encode(n: bigint, charset: string[]): string { + const base = BigInt(charset.length) + + if (n == BigInt(0)) { + return '0' + } else { + var xs = [] + while (n > BigInt(0)) { + xs.push(charset[Number(n % base)]) + n = n / base + } + return xs.reverse().join('') + } +} + +export function decode(xs: string, charset: string[]): bigint { + const base = BigInt(charset.length) + + return xs + .split('') + .reverse() + .reduce((acc, x, i) => acc + (BigInt(charset.indexOf(x)) * (base ** BigInt(i))), BigInt(0)) +} diff --git a/src/lib/button.ts b/src/lib/button.ts new file mode 100644 index 0000000..794df35 --- /dev/null +++ b/src/lib/button.ts @@ -0,0 +1,29 @@ +import { h, Children, concatClassName } from 'lib/h' + +export function raw(attrs: object, ...children: Children): Element { + return h('button', + concatClassName(attrs, 'g-Button__Raw'), + ...children + ) +} + +export function text(attrs: object, ...children: Children): Element { + return h('button', + concatClassName(attrs, 'g-Button__Text'), + ...children + ) +} + +export function action(attrs: object, ...children: Children): Element { + return h('button', + concatClassName(attrs, 'g-Button__Action'), + ...children + ) +} + +export function cancel(attrs: object, ...children: Children): Element { + return h('button', + concatClassName(attrs, 'g-Button__Cancel'), + ...children + ) +} diff --git a/src/lib/color.ts b/src/lib/color.ts new file mode 100644 index 0000000..59b320d --- /dev/null +++ b/src/lib/color.ts @@ -0,0 +1,36 @@ +interface Color { + red: number, + green: number, + blue: number, +} + +export function parse(str: string): Color { + return { + red: parseInt(str.slice(1,3), 16), + green: parseInt(str.slice(3,5), 16), + blue: parseInt(str.slice(5,7), 16), + } +} + +// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrastratio +export function contrastRatio(c1: Color, c2: Color): number { + const r1 = relativeLuminance(c1) + const r2 = relativeLuminance(c2) + + return r1 > r2 + ? (r1 + 0.05) / (r2 + 0.05) + : (r2 + 0.05) / (r1 + 0.05) +} + +function relativeLuminance(c: Color): number { + return ( + 0.2126 * fromSRGB(c.red / 255) + + 0.7152 * fromSRGB(c.green / 255) + + 0.0722 * fromSRGB(c.blue / 255)) +} + +function fromSRGB(sRGB: number): number { + return sRGB <= 0.03928 + ? sRGB / 12.92 + : Math.pow(((sRGB + 0.055) / 1.055), 2.4) +} diff --git a/src/lib/contextMenu.ts b/src/lib/contextMenu.ts new file mode 100644 index 0000000..6edd567 --- /dev/null +++ b/src/lib/contextMenu.ts @@ -0,0 +1,35 @@ +import { h } from 'lib/h' + +interface Action { + label: string, + action: () => void +} + +export function show(event: MouseEvent, actions: Action[]) { + const menu = h('div', + { id: 'g-ContextMenu', + style: `left: ${event.pageX.toString()}px; top: ${event.pageY.toString()}px` + }, + ...actions.map(({ label, action }) => + h('div', + { className: 'g-ContextMenu__Entry', + onclick: () => action() + }, + label + ) + ) + ) + + document.body.appendChild(menu) + + // Remove on click or context menu + setTimeout(() => { + const f = () => { + document.body.removeChild(menu) + document.body.removeEventListener('click', f) + document.body.removeEventListener('contextmenu', f) + } + document.body.addEventListener('click', f) + document.body.addEventListener('contextmenu', f) + }, 0) +} diff --git a/src/lib/dom.ts b/src/lib/dom.ts new file mode 100644 index 0000000..2ab4de5 --- /dev/null +++ b/src/lib/dom.ts @@ -0,0 +1,6 @@ +export function replaceChildren(parent: Element, ...newChildren: Element[]) { + while (parent.lastChild) { + parent.removeChild(parent.lastChild) + } + newChildren.forEach(c => parent.appendChild(c)) +} diff --git a/src/lib/form.ts b/src/lib/form.ts new file mode 100644 index 0000000..04a2654 --- /dev/null +++ b/src/lib/form.ts @@ -0,0 +1,54 @@ +import { h } from 'lib/h' +import * as Layout from 'lib/layout' +import * as Button from 'lib/button' + +interface InputParams { + label: string, + attrs: object, +} + +export function input({ label, attrs }: InputParams): Element { + return h('label', + { className: 'g-Form__Field' }, + label, + h('input', attrs), + ) +} + +interface ColorInputParams { + colors: string[], + label: string, + initValue: string, + onInput: (value: string) => void, +} + +export function colorInput({ colors, label, initValue, onInput }: ColorInputParams): Element { + const input = h('input', + { value: initValue, + type: 'color', + oninput: (e: Event) => { + if (e.target !== null) { + onInput((e.target as HTMLInputElement).value) + } + } + } + ) as HTMLInputElement + return h('label', + { className: 'g-Form__Field' }, + label, + Layout.line( + {}, + input, + ...colors.map(color => + Button.raw({ className: 'g-Form__Color', + style: `background-color: ${color}`, + type: 'button', + onclick: () => { + input.value = color + onInput(color) + } + }) + ) + ) + ) +} diff --git a/src/lib/h.ts b/src/lib/h.ts new file mode 100644 index 0000000..7e93311 --- /dev/null +++ b/src/lib/h.ts @@ -0,0 +1,31 @@ +type Child = Element | Text | string | number + +export type Children = Child[] + +export function h(tagName: string, attrs: object, ...children: Children): Element { + let elem = document.createElement(tagName) + elem = Object.assign(elem, attrs) + appendChildren(elem, ...children) + return elem +} + +export function s(tagName: string, attrs: object, ...children: Children): Element { + let elem = document.createElementNS('http://www.w3.org/2000/svg', tagName) + Object.entries(attrs).forEach(([key, value]) => elem.setAttribute(key, value)) + appendChildren(elem, ...children) + return elem +} + +function appendChildren(elem: Element, ...children: Children) { + for (const child of children) { + if (typeof child === 'number') + elem.append(child.toString()) + else + elem.append(child) + } +} + +export function concatClassName(attrs: any, className: string): object { + const existingClassName = 'className' in attrs ? attrs['className'] : undefined + return { ...attrs, className: `${className} ${existingClassName}` } +} diff --git a/src/lib/icons.ts b/src/lib/icons.ts new file mode 100644 index 0000000..8db4e17 --- /dev/null +++ b/src/lib/icons.ts @@ -0,0 +1,66 @@ +import { h, s } from 'lib/h' + +export function get(key: string, attrs: object = {}): Element { + const elem = fromKey(key) + if (elem !== undefined) { + Object.entries(attrs).forEach(([key, value]) => { + elem.setAttribute(key, value) + }) + return elem + } else { + return h('span', {}) + } +} + +// https://yqnn.github.io/svg-path-editor/ +function fromKey(key: string): Element | undefined { + if (key == 'house') { + return s('svg', + { viewBox: '0 0 10 10' }, + s('g', { 'stroke': 'none' }, + s('path', { d: 'M0 4V5H1.5V10H4V7C4.4 6.5 5.6 6.5 6 7V10H8.5V5H10V4L5 0Z' }) + ) + ) + } else if (key == 'music') { + return s('svg', + { viewBox: '0 0 10 10' }, + s('g', { 'stroke': 'none' }, + s('ellipse', { cx: '2', cy: '8.5', rx: '2', ry: '1.5' }), + s('ellipse', { cx: '8', cy: '7', rx: '2', ry: '1.5' }), + s('path', { d: 'M2.5 8.5 H4 V4.5 L8.5 3 V7 H10 V0 L2.5 2.5 Z' }), + ) + ) + } else if (key == 'shopping-cart') { + return s('svg', + { viewBox: '0 0 10 10' }, + s('circle', { cx: '3.3', cy: '8.5', r: '0.8' }), + s('circle', { cx: '7.3', cy: '8.5', r: '0.8' }), + s('path', { d: 'M.5.6C1.3.6 1.8.7 2.1 1L2.3 6H8.5', fill: 'transparent' }), + s('path', { d: 'M2.3 1.9H9.4L8.6 4H2.4' }), + ) + } else if (key == 'medical') { + return s('svg', + { viewBox: '0 0 10 10' }, + s('path', { d: 'M5 1V9M1 5H9', style: 'stroke-width: 3' }), + ) + } else if (key == 'envelope') { + return s('svg', + { viewBox: '0 0 10 10' }, + s('path', { d: 'M.5 2.5H9.5V7.5H.5ZM.5 3.4 3.5 5Q5 5.8 6.6 5L9.5 3.4', style: 'fill: transparent' }), + ) + } +} + +// Good to add: +// - loisir / cinéma / piscine +// - école +// - gare +// - bus +export function keys(): string[] { + return [ 'house', + 'music', + 'shopping-cart', + 'medical', + 'envelope', + ] +} diff --git a/src/lib/layout.ts b/src/lib/layout.ts new file mode 100644 index 0000000..1e38bfd --- /dev/null +++ b/src/lib/layout.ts @@ -0,0 +1,15 @@ +import { h, Children, concatClassName } from 'lib/h' + +export function section(attrs: object, ...children: Children): Element { + return h('div', + concatClassName(attrs, 'g-Layout__Section'), + ...children + ) +} + +export function line(attrs: object, ...children: Children): Element { + return h('div', + concatClassName(attrs, 'g-Layout__Line'), + ...children + ) +} diff --git a/src/lib/modal.ts b/src/lib/modal.ts new file mode 100644 index 0000000..8454e1c --- /dev/null +++ b/src/lib/modal.ts @@ -0,0 +1,28 @@ +import { h } from 'lib/h' +import * as Button from 'lib/button' + +export function show(content: Element) { + document.body.appendChild(h('div', + { id: 'g-Modal' }, + h('div', + { className: 'g-Modal__Curtain', + onclick: () => hide() + } + ), + h('div', + { className: 'g-Modal__Window' }, + Button.raw( + { className: 'g-Modal__Close', + onclick: () => hide() + }, + 'x' + ), + content + ) + )) +} + +export function hide() { + const modal = document.querySelector('#g-Modal') + modal && document.body.removeChild(modal) +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..36b1143 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,3 @@ +import * as Map from 'map' + +document.body.appendChild(Map.view()) diff --git a/src/map.ts b/src/map.ts new file mode 100644 index 0000000..04a1351 --- /dev/null +++ b/src/map.ts @@ -0,0 +1,131 @@ +import { h } from 'lib/h' +import * as Button from 'lib/button' +import * as ContextMenu from 'lib/contextMenu' +import * as Layout from 'lib/layout' +import * as Modal from 'lib/modal' +import * as Marker from 'marker' +import * as MarkerForm from 'markerForm' +import * as State from 'state' +import * as Serialization from 'serialization' +const L = window.L + +export function view() { + // Wait for elements to be on page before installing map + window.setTimeout(installMap, 0) + + return layout() +} + +const mapId: string = 'g-Map__Content' + +function layout(): Element { + return h('div', + { className: 'g-Layout__Page' }, + h('div', + { className: 'g-Layout__Header' }, + h('a', + { className: 'g-Layout__Home', + href: '#' + }, + 'Map' + ) + ) + , h('div', + { className: 'g-Map' }, + h('div', { id: mapId}) + ) + ) +} + +function installMap(): object { + + const map = L.map(mapId, { + center: [51.505, -0.09], + zoom: 2, + attributionControl: false + }) + + map.addLayer(L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png')) + + const mapMarkers = L.featureGroup() + map.addLayer(mapMarkers) + + map.addEventListener('contextmenu', e => { + ContextMenu.show( + e.originalEvent, + [ { label: 'Add a marker', + action: () => { + const pos = e.latlng + const lastMarker = State.last() + Modal.show(MarkerForm.view({ + onValidate: (color: string, icon: string, name: string, radius: number) => { + const id = State.add({ pos, color, icon, name, radius }) + Marker.add({ + id, + pos, + color, + icon, + name, + radius, + addToMap: marker => mapMarkers.addLayer(marker), + removeFromMap: marker => mapMarkers.removeLayer(marker) + }) + Modal.hide() + }, + onCancel: () => Modal.hide(), + color: lastMarker ? lastMarker.color : '#3F92CF', + icon: lastMarker ? lastMarker.icon : '', + name: '', + radius: 0, + })) + } + } + ] + ) + }) + + // Init from hash + const hash = window.location.hash.substr(1) + const state = Serialization.decode(hash) + State.reset(state) + addMarkers({ map, mapMarkers, state, isInit: true }) + + // Reload from hash + window.addEventListener('popstate', _ => reloadFromHash(map, mapMarkers)) + + return map +} + +export function reloadFromHash(map: L.Map, mapMarkers: L.FeatureGroup) { + const state = Serialization.decode(window.location.hash.substr(1)) + mapMarkers.clearLayers() + addMarkers({ map, mapMarkers, state, isInit: false }) +} + +interface AddMarkersOptions { + map: L.Map, + mapMarkers: L.FeatureGroup, + state: State.State, + isInit: boolean, +} + +function addMarkers({ map, mapMarkers, state, isInit }: AddMarkersOptions) { + state.forEach((marker, id) => { + const { pos, color, icon, name, radius } = marker + Marker.add({ + id, + pos, + color, + icon, + name, + radius, + addToMap: marker => mapMarkers.addLayer(marker), + removeFromMap: marker => mapMarkers.removeLayer(marker) + }) + }) + + // Focus + if (state.length > 0 && (isInit || !map.getBounds().contains(mapMarkers.getBounds()))) { + map.fitBounds(mapMarkers.getBounds(), { padding: [ 50, 50 ] }) + } +} diff --git a/src/marker.ts b/src/marker.ts new file mode 100644 index 0000000..9f59497 --- /dev/null +++ b/src/marker.ts @@ -0,0 +1,171 @@ +import { h, s } from 'lib/h' +import * as Color from 'lib/color' +import * as ContextMenu from 'lib/contextMenu' +import * as MarkerForm from 'markerForm' +import * as Icons from 'lib/icons' +import * as Modal from 'lib/modal' +import * as State from 'state' +const L = window.L + +interface CreateParams { + id: State.Index, + pos: L.Pos, + color: string, + icon: string, + name: string, + radius: number, + addToMap: (layer: L.Layer | L.FeatureGroup) => void, + removeFromMap: (layer: L.Layer | L.FeatureGroup) => void, +} + +export function add({ id, pos, color, icon, name, radius, addToMap, removeFromMap }: CreateParams) { + const marker = L.marker(pos, { + draggable: true, + autoPan: true, + icon: divIcon({ icon, color, name }), + }) + + const circle = + radius !== 0 + ? L.circle(pos, { radius, color, fillColor: color }) + : undefined + + const layer = + circle !== undefined + ? L.featureGroup([ marker, circle ]) + : L.featureGroup([ marker ]) + + const onUpdate = () => + Modal.show(MarkerForm.view({ + onValidate: (color: string, icon: string, name: string, radius: number) => { + removeFromMap(layer) + add({ id, pos, color, icon, name, radius, addToMap, removeFromMap }) + State.update(id, { pos, color, icon, name, radius }) + Modal.hide() + }, + onCancel: () => Modal.hide(), + color, + icon, + name, + radius, + })) + + marker.addEventListener('contextmenu', e => { + ContextMenu.show( + e.originalEvent, + [ { label: 'Modify', + action: onUpdate, + } + , { label: 'Remove', + action: () => { + removeFromMap(layer) + State.remove(id) + } + } + ] + ) + }) + + marker.addEventListener('drag', e => { + circle && circle.setLatLng(marker.getLatLng()) + }) + + marker.addEventListener('dragend', e => { + const pos = marker.getLatLng() + removeFromMap(layer) + add({ id, pos, color, icon, name, radius, addToMap, removeFromMap }) + State.update(id, { pos, color, icon, name, radius }) + }) + + marker.addEventListener('dblclick', onUpdate) + + addToMap(layer) +} + +interface CreateIconParams { + icon: string, + color: string, + name: string, +} + +function divIcon({ icon, color, name }: CreateIconParams): L.Icon { + const c = Color.parse(color) + const crBlack = Color.contrastRatio({ red: 0, green: 0, blue: 0 }, c) + const crWhite = Color.contrastRatio({ red: 255, green: 255, blue: 255 }, c) + const textCol = crBlack > crWhite ? 'black' : 'white' + const width = 10 + const height = 15 + const stroke = 'black' + const strokeWidth = 0.6 + // Triangle + const t = [ + { x: width * 0.15, y: 7.46 }, + { x: width / 2, y: height }, + { x: width * 0.85, y: 7.46 } + ] + return L.divIcon( + { className: '' + , popupAnchor: [ 0, -34 ] + , html: + h('div', + { className: 'g-Marker' }, + s('svg', + { viewBox: `0 0 ${width} ${height}`, + class: 'g-Marker__Base' + }, + s('circle', + { cx: width / 2, + cy: width / 2, + r: (width - 2 * strokeWidth) / 2, + stroke, + 'stroke-width': strokeWidth, + fill: color + } + ), + s('polygon', + { points: `${t[0].x},${t[0].y} ${t[1].x},${t[1].y} ${t[2].x},${t[2].y}`, + fill: color + } + ), + s('line', + { x1: t[0].x, + y1: t[0].y, + x2: t[1].x, + y2: t[1].y, + stroke, + 'stroke-width': strokeWidth + } + ), + s('line', + { x1: t[1].x, + y1: t[1].y, + x2: t[2].x, + y2: t[2].y, + stroke, + 'stroke-width': strokeWidth + } + ), + ), + Icons.get( + icon, + { class: 'g-Marker__Icon' + , style: `fill: ${textCol}; stroke: ${textCol}` + } + ), + h('div', + { className: 'g-Marker__Title', + style: `color: black; text-shadow: ${textShadow('white', 1, 1)}` + }, + name + ) + ) + } + ) +} + +function textShadow(color: string, w: number, blurr: number): string { + return [[-w, -w], [-w, 0], [-w, w], [0, -w], [0, w], [w, -w], [w, 0], [w, w]] + .map(xs => `${color} ${xs[0]}px ${xs[1]}px ${blurr}px`) + .join(', ') +} + diff --git a/src/markerForm.ts b/src/markerForm.ts new file mode 100644 index 0000000..54670ae --- /dev/null +++ b/src/markerForm.ts @@ -0,0 +1,116 @@ +import { h } from 'lib/h' +import * as AutoComplete from 'lib/autoComplete' +import * as Button from 'lib/button' +import * as Dom from 'lib/dom' +import * as Form from 'lib/form' +import * as Icons from 'lib/icons' +import * as Layout from 'lib/layout' +import * as State from 'state' + +interface FormParams { + onValidate: (color: string, icon: string, name: string, radius: number) => void, + onCancel: () => void, + color: string, + icon: string, + name: string, + radius: number, +} + +export function view({ onValidate, onCancel, color, icon, name, radius }: FormParams): Element { + var radiusStr = radius.toString() + const onSubmit = () => onValidate(color, icon, name, parseInt(radiusStr) || 0) + const domIcon = h('div', + { className: 'g-MarkerForm__Icon' }, + Icons.get(icon, { fill: 'black', stroke: 'black' }) + ) + return h('div', + {}, + Layout.section( + {}, + h('form', + { className: 'g-MarkerForm', + onsubmit: (e: Event) => { + e.preventDefault() + onSubmit() + } + }, + Layout.section( + {}, + Form.input({ + label: 'Name', + attrs: { + oninput: (e: Event) => { + if (e.target !== null) { + name = (e.target as HTMLInputElement).value + } + }, + onblur: (e: Event) => { + if (e.target !== null) { + name = (e.target as HTMLInputElement).value.trim() + } + }, + value: name + } + }), + Form.colorInput({ + colors: State.colors(), + label: 'Color', + initValue: color, + onInput: newColor => color = newColor + }), + h('div', + { className: 'g-Form__Field' }, + h('div', + { className: 'g-Form__Label' }, + h('label', { for: 'g-MarkerForm__IconInput' }, 'Icon') + ), + Layout.line( + { className: 'g-MarkerForm__AutoCompleteAndIcon' }, + AutoComplete.create( + { value: icon, + className: 'g-MarkerForm__AutoComplete' + }, + 'g-MarkerForm__IconInput', + Icons.keys().sort(), + iconKey => h('div', + { className: 'g-MarkerForm__IconEntry' }, + h('div', { className: 'g-MarkerForm__IconElem' }, Icons.get(iconKey, { fill: 'black', stroke: 'black' }) ), + iconKey + ), + newIcon => { + icon = newIcon + Dom.replaceChildren(domIcon, Icons.get(icon, { fill: 'black', stroke: 'black' })) + }), + domIcon + ) + ), + Form.input({ + label: 'Radius (m)', + attrs: { oninput: (e: Event) => { + if (e.target !== null) { + radiusStr = (e.target as HTMLInputElement).value + } + }, + value: radiusStr, + type: 'number', + } + }) + ), + ), + Layout.line( + {}, + Button.action({ onclick: () => onSubmit() }, 'Save'), + Button.cancel( + { onclick: () => onCancel(), + type: 'button' + }, + 'Cancel' + ) + ) + ) + ) +} + +function restrictCharacters(str: string, chars: string): string { + return str.split('').filter(c => chars.indexOf(c) != -1).join('') +} diff --git a/src/serialization.ts b/src/serialization.ts new file mode 100644 index 0000000..4289b36 --- /dev/null +++ b/src/serialization.ts @@ -0,0 +1,44 @@ +import * as Base from 'lib/base' +import * as State from 'state' +import * as Utils from 'serialization/utils' +import * as V0 from 'serialization/v0' + +// Encoding + +const lastVersion = 0 // max is 62 + +export function encode(s: State.State): string { + if (s.length == 0) { + return '' + } else { + const version = Base.encode(BigInt(lastVersion), Base.b62) + const xs = V0.encode(s).map(binaryToBase62).join('-') + return `${version}${xs}` + } +} + +function binaryToBase62(str: string): string { + // Prepend 1 so that we don’t loose leading 0s + return Base.encode(Base.decode('1' + str, Base.b2), Base.b62) +} + +// Decoding + +export function decode(encoded: string): State.State { + if (encoded == '') { + return [] + } else { + const version = Number(Base.decode(encoded.slice(0, 1), Base.b62)) + if (version == 0) return V0.decode(encoded.slice(1).split('-').map(base62ToBinary)) + else { + console.error(`Unknown decoder version ${version} in order to decode state.`) + return [] + } + } +} + +function base62ToBinary(str: string): string { + // Remove prepended 1 + return Base.encode(Base.decode(str, Base.b62), Base.b2).slice(1) +} + diff --git a/src/serialization/utils.ts b/src/serialization/utils.ts new file mode 100644 index 0000000..c94f199 --- /dev/null +++ b/src/serialization/utils.ts @@ -0,0 +1,9 @@ +import * as Base from 'lib/base' + +export function encodeNumber(n: bigint, length: number): string { + return Base.encode(n, Base.b2).padStart(length, '0') +} + +export function mod(a: number, b: number): number { + return ((a % b) + b) % b +} diff --git a/src/serialization/v0.ts b/src/serialization/v0.ts new file mode 100644 index 0000000..f90eb66 --- /dev/null +++ b/src/serialization/v0.ts @@ -0,0 +1,122 @@ +import * as Base from 'lib/base' +import * as Icons from 'lib/icons' +import * as State from 'state' +import * as Utils from 'serialization/utils' + +const posPrecision: number = 5 +const latLength: number = Base.encode(BigInt(`180${'0'.repeat(posPrecision)}`), Base.b2).length +const lngLength: number = Base.encode(BigInt(`360${'0'.repeat(posPrecision)}`), Base.b2).length +const colorLength: number = Base.encode(Base.decode('ffffff', Base.b16), Base.b2).length +const iconLength: number = 8 // At most 255 icons +const radiusLength: number = 5 + +// Encoding + +export function encode(s: State.State): string[] { + return s.map(encodeMarker) +} + +function encodeMarker({ pos, name, color, icon, radius }: State.Marker): string { + const lat = encodeLatOrLng(pos.lat, latLength, 180) // [-90; 90] + const lng = encodeLatOrLng(pos.lng, lngLength, 360) // [-180; 180] + return lat + lng + encodeColor(color) + encodeIcon(icon) + encodeRadius(radius) + encodeName(name) +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent +export const uriComponentBase: string[] = + '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.!~*\'()%'.split('') + +function encodeLatOrLng(n: number, length: number, range: number): string { + const [a, b] = Utils.mod(n + range / 2, range).toFixed(posPrecision).split('.') + return Utils.encodeNumber(BigInt(a + b), length) +} + +function encodeColor(color: string): string { + return Utils.encodeNumber(Base.decode(color.slice(1).toLowerCase(), Base.b16), colorLength) +} + +function encodeIcon(icon: string): string { + return icon == '' + ? '0' + : `1${Utils.encodeNumber(BigInt(Icons.keys().indexOf(icon) + 1), iconLength)}` +} + +function encodeRadius(radius: number): string { + if (radius == 0) { + return '0' + } else { + const binary = Base.encode(BigInt(radius), Base.b2) + const binaryLength = Utils.encodeNumber(BigInt(binary.length), radiusLength) + return `1${binaryLength}${binary}` + } +} + +function encodeName(str: string): string { + return str == '' + ? '' + : Base.encode(Base.decode(encodeURIComponent(str), uriComponentBase), Base.b2) +} + +// Decoding + +export function decode(encoded: string[]): State.State { + return encoded.map(binary => { + const [ lat, i1 ] = decodeLatOrLng(binary, 0, latLength, 180) + const [ lng, i2 ] = decodeLatOrLng(binary, i1, lngLength, 360) + const [ color, i3 ] = decodeColor(binary, i2) + const [ icon, i4 ] = decodeIcon(binary, i3) + const [ radius, i5 ] = decodeRadius(binary, i4) + const name = decodeName(binary, i5) + + return { + pos: { lat, lng }, + name, + color, + icon, + radius, + } + }) +} + +function decodeLatOrLng(encoded: string, i: number, length: number, range: number): [number, number] { + const slice = encoded.slice(i, i + length) + const digits = Base.decode(slice, Base.b2).toString() + const latOrLng = parseFloat(`${digits.slice(0, -posPrecision)}.${digits.slice(-posPrecision)}`) - range / 2 + return [ latOrLng, i + length ] +} + +function decodeColor(encoded: string, i: number): [ string, number ] { + const slice = encoded.slice(i, i + colorLength) + const color = `#${Base.encode(Base.decode(slice, Base.b2), Base.b16)}` + return [ color, i + colorLength ] +} + +function decodeIcon(encoded: string, i: number): [ string, number ] { + if (encoded.slice(i, i + 1) == '0') { + return [ '', i + 1 ] + } else { + const slice = encoded.slice(i + 1, i + 1 + iconLength) + const iconIndex = Number(Base.decode(slice, Base.b2)) - 1 + const icon = iconIndex < 0 ? '' : Icons.keys()[iconIndex] + return [ icon, i + 1 + iconLength ] + } +} + +function decodeRadius(encoded: string, i: number): [ number, number ] { + if (encoded.slice(i, i + 1) == '0') { + return [ 0, i + 1 ] + } else { + const binaryLength = encoded.slice(i + 1, i + 1 + radiusLength) + const length = Number(Base.decode(binaryLength, Base.b2)) + const binary = encoded.slice(i + 1 + radiusLength, i + 1 + radiusLength + length) + const radius = Number(Base.decode(binary, Base.b2)) + return [ radius, i + 1 + radiusLength + length ] + } +} + +function decodeName(encoded: string, i: number): string { + const slice = encoded.slice(i) + return slice == '' + ? '' + : decodeURIComponent(Base.encode(Base.decode(slice, Base.b2), uriComponentBase)) +} diff --git a/src/state.ts b/src/state.ts new file mode 100644 index 0000000..634319a --- /dev/null +++ b/src/state.ts @@ -0,0 +1,65 @@ +import * as Serialization from 'serialization' + +const L = window.L + +// State + +var nextIndex: Index = 0 + +export type State = Marker[] +export type Index = number + +var state: State = [] + +export interface Marker { + pos: L.Pos, + name: string, + color: string, + icon: string, + radius: number, +} + +export function reset(s: State) { + state = s + nextIndex = s.length +} + +// CRUD + +export function add(marker: Marker): Index { + const index = nextIndex + state[index] = marker + nextIndex += 1 + pushState() + return index +} + +export function update(index: Index, marker: Marker) { + state[index] = marker + pushState() +} + +export function remove(index: Index) { + delete state[index] + pushState() +} + +// History + +function pushState() { + const encoded = Serialization.encode(Object.values(state)) + history.pushState('', '', `#${encoded}`) +} + +// Inspection + +export function colors() { + return [...new Set(Object.values(state).map(({ color }) => color))] +} + +export function last(): Marker | undefined { + const nonempty = Object.values(state) + return nonempty.length > 0 + ? nonempty.slice(-1)[0] + : undefined +} diff --git a/src/types/leaflet.d.ts b/src/types/leaflet.d.ts new file mode 100644 index 0000000..c1eef16 --- /dev/null +++ b/src/types/leaflet.d.ts @@ -0,0 +1,95 @@ +export as namespace L + +// Map + +export function map(element: string, options?: MapOptions): Map + +export interface MapOptions { + center: number[], + zoom: number, + attributionControl: boolean, +} + +export interface Map { + addLayer: (layer: Layer | FeatureGroup) => void, + removeLayer: (layer: Layer | FeatureGroup) => void, + addEventListener: (name: string, fn: (e: MapEvent) => void) => void, + getBounds: () => LatLngBounds, + fitBounds: (bounds: LatLngBounds, options: { padding: [number, number] } | undefined) => void, +} + +// LatLngBounds + +export interface LatLngBounds { + contains: (otherBounds: LatLngBounds) => boolean, +} + +// Feature group + +export interface FeatureGroup { + clearLayers: () => void, + addLayer: (layer: Layer | FeatureGroup) => void, + removeLayer: (layer: Layer | FeatureGroup) => void, + getBounds: () => LatLngBounds, +} + +export function featureGroup(xs?: Layer[]): L.FeatureGroup + +// Layer + +export interface Layer { + addEventListener: (name: string, fn: (e: MapEvent) => void) => void, + getLatLng: () => Pos, + setLatLng: (pos: Pos) => void, +} + +export function tileLayer(url: string): Layer + +// Marker + +export function marker( + pos: Pos, + options: { + draggable: boolean, + autoPan: boolean, + icon: Icon, + } +): Layer + +// Circle + +export function circle( + pos: Pos, + options: { + radius: number, + color: string, + fillColor: string, + }, +): Layer + +// Icon + +export interface Icon {} + +export function divIcon( + params: { + className: string, + popupAnchor: number[], + html: Element, + } +): Icon + +// Pos + +export interface Pos { + lat: number, + lng: number, +} + +// MapEvent + +interface MapEvent { + originalEvent: MouseEvent, + latlng: {lat: number, lng: number}, +} + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1100ced --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "amd", + "target": "ES2020", + "baseUrl": "src", + "noImplicitAny": true, + "strictNullChecks": true, + "removeComments": true, + "preserveConstEnums": true, + "outFile": "public/main.js" + }, + "include": ["src/**/*"] +} -- cgit v1.2.3