From accc3d9c67e004aa814d1592d97342d3a7766bb4 Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 5 Jul 2022 21:55:41 +0200 Subject: WIP Rewrite in TS --- .gitignore | 7 +- .gitlab-ci.yml | 10 - .tmuxinator.yml | 11 - Makefile | 21 -- README.md | 30 +- bin/watch.sh | 8 + bsconfig.json | 22 -- deploy | 12 - dev | 28 -- package-lock.json | 167 --------- package.json | 13 - public/index.html | 47 ++- public/leaflet/leaflet.css | 2 +- public/leaflet/leaflet.js | 5 +- public/leaflet/leaflet.js.map | 1 - public/main.css | 24 +- rollup.config.js | 13 - shell.nix | 13 +- 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 | 114 ++++++ src/lib/button.ts | 29 ++ src/lib/contextMenu.ts | 35 ++ src/lib/fontAwesome.ts | 788 ++++++++++++++++++++++++++++++++++++++++++ src/lib/form.ts | 80 +++++ src/lib/h.ts | 41 +++ src/lib/layout.ts | 15 + src/lib/modal.ts | 28 ++ src/main.ts | 3 + src/map.ts | 126 +++++++ src/marker.ts | 125 +++++++ src/types/leaflet.d.ts | 28 ++ tsconfig.json | 13 + 60 files changed, 1489 insertions(+), 2278 deletions(-) delete mode 100644 .gitlab-ci.yml delete mode 100644 .tmuxinator.yml delete mode 100644 Makefile create mode 100755 bin/watch.sh delete mode 100644 bsconfig.json delete mode 100755 deploy delete mode 100755 dev delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 public/leaflet/leaflet.js.map delete mode 100644 rollup.config.js 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/button.ts create mode 100644 src/lib/contextMenu.ts create mode 100644 src/lib/fontAwesome.ts create mode 100644 src/lib/form.ts create mode 100644 src/lib/h.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/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/Makefile b/Makefile deleted file mode 100644 index c40999d..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -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 - -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..9f5b91f 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,9 @@ -# Map +# Getting started -Available at [https://guyonvarch.gitlab.io/map](https://guyonvarch.gitlab.io/map). +Run: -## Gettings started - -Start the environment with: - -```bash -./dev start -``` - -Later, stop the environment with: - -```bash -./dev stop +```sh +nix-shell --run bin/watch.sh ``` -## 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) +Then, open your browser at `http://localhost:8000`. diff --git a/bin/watch.sh b/bin/watch.sh new file mode 100755 index 0000000..064414d --- /dev/null +++ b/bin/watch.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +python -m http.server --directory public 8000 & + +trap "fuser -k 8000/tcp" EXIT + +tsc --build tsconfig.json --watch 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/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/index.html b/public/index.html index 143f477..36007b3 100644 --- a/public/index.html +++ b/public/index.html @@ -1,23 +1,34 @@ - + + + +Map + + - - - - Map - - + + - - + + + - - - - + + - - - - - + + diff --git a/public/leaflet/leaflet.css b/public/leaflet/leaflet.css index 983d605..601476f 100644 --- a/public/leaflet/leaflet.css +++ b/public/leaflet/leaflet.css @@ -532,7 +532,7 @@ svg.leaflet-image-layer.leaflet-interactive path { } .leaflet-oldie .leaflet-popup-content-wrapper { - zoom: 1; + -ms-zoom: 1; } .leaflet-oldie .leaflet-popup-tip { width: 24px; diff --git a/public/leaflet/leaflet.js b/public/leaflet/leaflet.js index bc9ef0f..21f499c 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 + * Leaflet 1.7.1, a JS library for interactive maps. http://leafletjs.com * (c) 2010-2019 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&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=O(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=O(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=N(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=N(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 kt(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var Bt={ie:tt,ielt9:it,edge:et,webkit:nt,android:ot,android23:st,androidStock:at,opera:ht,chrome:ut,gecko:lt,safari:ct,phantom:_t,opera12:dt,win:pt,ie3d:mt,webkit3d:ft,gecko3d:gt,any3d:vt,mobile:yt,mobileWebkit:xt,mobileWebkit3d:wt,msPointer:Pt,pointer:Lt,touch:bt,mobileOpera:Tt,mobileGecko:Mt,retina:zt,passiveEvents:Ct,canvas:St,svg:Zt,vml:Et},At=Pt?"MSPointerDown":"pointerdown",It=Pt?"MSPointerMove":"pointermove",Ot=Pt?"MSPointerUp":"pointerup",Rt=Pt?"MSPointerCancel":"pointercancel",Nt={},Dt=!1;function jt(t,i,e,n){function o(t){Ut(t,r)}var s,r,a,h,u,l,c,_;function d(t){t.pointerType===(t.MSPOINTER_TYPE_MOUSE||"mouse")&&0===t.buttons||Ut(t,h)}return"touchstart"===i?(u=t,l=e,c=n,_=p(function(t){t.MSPOINTER_TYPE_TOUCH&&t.pointerType===t.MSPOINTER_TYPE_TOUCH&&Ri(t),Ut(t,l)}),u["_leaflet_touchstart"+c]=_,u.addEventListener(At,_,!1),Dt||(document.addEventListener(At,Wt,!0),document.addEventListener(It,Ht,!0),document.addEventListener(Ot,Ft,!0),document.addEventListener(Rt,Ft,!0),Dt=!0)):"touchmove"===i?(h=e,(a=t)["_leaflet_touchmove"+n]=d,a.addEventListener(It,d,!1)):"touchend"===i&&(r=e,(s=t)["_leaflet_touchend"+n]=o,s.addEventListener(Ot,o,!1),s.addEventListener(Rt,o,!1)),this}function Wt(t){Nt[t.pointerId]=t}function Ht(t){Nt[t.pointerId]&&(Nt[t.pointerId]=t)}function Ft(t){delete Nt[t.pointerId]}function Ut(t,i){for(var e in t.touches=[],Nt)t.touches.push(Nt[e]);t.changedTouches=[t],i(t)}var Vt=Pt?"MSPointerDown":Lt?"pointerdown":"touchstart",qt=Pt?"MSPointerUp":Lt?"pointerup":"touchend",Gt="_leaflet_";var Kt,Yt,Xt,Jt,$t,Qt,ti=fi(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),ii=fi(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),ei="webkitTransition"===ii||"OTransition"===ii?ii+"End":"transitionend";function ni(t){return"string"==typeof t?document.getElementById(t):t}function oi(t,i){var e,n=t.style[i]||t.currentStyle&&t.currentStyle[i];return n&&"auto"!==n||!document.defaultView||(n=(e=document.defaultView.getComputedStyle(t,null))?e[i]:null),"auto"===n?null:n}function si(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function ri(t){var i=t.parentNode;i&&i.removeChild(t)}function ai(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function hi(t){var i=t.parentNode;i&&i.lastChild!==t&&i.appendChild(t)}function ui(t){var i=t.parentNode;i&&i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function li(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=pi(t);return 0this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,N(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},panInside:function(t,i){var e,n,o=A((i=i||{}).paddingTopLeft||i.padding||[0,0]),s=A(i.paddingBottomRight||i.padding||[0,0]),r=this.getCenter(),a=this.project(r),h=this.project(t),u=this.getPixelBounds(),l=u.getSize().divideBy(2),c=O([u.min.add(o),u.max.subtract(s)]);return c.contains(h)||(this._enforcingBounds=!0,e=a.subtract(h),n=A(h.x+e.x,h.y+e.y),(h.xc.max.x)&&(n.x=a.x-e.x,0c.max.y)&&(n.y=a.y-e.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[m(s)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){r=!0;break}if(e&&e.listens(i,!0)){if(o&&!Vi(s,t))break;if(n.push(e),o)break}if(s===this._container)break;s=s.parentNode}return n.length||r||o||!Vi(s,t)||(n=[this]),n},_handleDOMEvent:function(t){var i;this._loaded&&!Ui(t)&&("mousedown"!==(i=t.type)&&"keypress"!==i&&"keyup"!==i&&"keydown"!==i||Pi(t.target||t.srcElement),this._fireDOMEvent(t,i))},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,i,e){var n;if("click"===t.type&&((n=h({},t)).type="preclick",this._fireDOMEvent(n,n.type,e)),!t._stopped&&(e=(e||[]).concat(this._findEventTargets(t,i))).length){var o=e[0];"contextmenu"===i&&o.listens(i,!0)&&Ri(t);var s,r={originalEvent:t};"keypress"!==t.type&&"keydown"!==t.type&&"keyup"!==t.type&&(s=o.getLatLng&&(!o._radius||o._radius<=10),r.containerPoint=s?this.latLngToContainerPoint(o.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=s?o.getLatLng():this.layerPointToLatLng(r.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,ci(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:n}),setTimeout(p(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&_i(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),M(function(){this._moveEnd(!0)},this))}});function Yi(t){return new Xi(t)}var Xi=S.extend({options:{position:"topright"},initialize:function(t){c(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 ci(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&&(ri(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_"+m(this),n),this._layerControlInputs.push(i),i.layerId=m(t.layer),zi(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()}}),$i=Xi.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"−",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=si("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=si("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),Oi(s),zi(s,"click",Ni),zi(s,"click",o,this),zi(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";_i(this._zoomInButton,i),_i(this._zoomOutButton,i),!this._disabled&&t._zoom!==t.getMinZoom()||ci(this._zoomOutButton,i),!this._disabled&&t._zoom!==t.getMaxZoom()||ci(this._zoomInButton,i)}});Ki.mergeOptions({zoomControl:!0}),Ki.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new $i,this.addControl(this.zoomControl))});var Qi=Xi.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i="leaflet-control-scale",e=si("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=si("div",i,e)),t.imperial&&(this._iScale=si("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){c(this,t),this._attributions={}},onAdd:function(t){for(var i in(t.attributionControl=this)._container=si("div","leaflet-control-attribution"),Oi(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(" | ")}}});Ki.mergeOptions({attributionControl:!0}),Ki.addInitHook(function(){this.options.attributionControl&&(new te).addTo(this)});Xi.Layers=Ji,Xi.Zoom=$i,Xi.Scale=Qi,Xi.Attribution=te,Yi.layers=function(t,i,e){return new Ji(t,i,e)},Yi.zoom=function(t){return new $i(t)},Yi.scale=function(t){return new Qi(t)},Yi.attribution=function(t){return new te(t)};var ie=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}});ie.addTo=function(t,i){return t.addHandler(i,this),this};var ee,ne={Events:Z},oe=bt?"touchstart mousedown":"mousedown",se={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},re={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},ae=E.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){c(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(zi(this._dragStartTarget,oe,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(ae._dragging===this&&this.finishDrag(),Si(this._dragStartTarget,oe,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var i,e;!t._simulated&&this._enabled&&(this._moved=!1,li(this._element,"leaflet-zoom-anim")||ae._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((ae._dragging=this)._preventOutline&&Pi(this._element),xi(),Xt(),this._moving||(this.fire("down"),i=t.touches?t.touches[0]:t,e=bi(this._element),this._startPoint=new k(i.clientX,i.clientY),this._parentScale=Ti(e),zi(document,re[t.type],this._onMove,this),zi(document,se[t.type],this._onUp,this))))},_onMove:function(t){var i,e;!t._simulated&&this._enabled&&(t.touches&&1i&&(e.push(t[n]),o=n);oi.max.x&&(e|=2),t.yi.max.y&&(e|=8),e}function de(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||Oe.prototype._containsPoint.call(this,t,!0)}});var Ne=Ce.extend({initialize:function(t,i){c(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=g(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(),Ni(t)},_getAnchor:function(){return A(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});Ki.mergeOptions({closePopupOnClick:!0}),Ki.include({openPopup:function(t,i,e){return t instanceof tn||(t=new tn(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}}),Me.include({bindPopup:function(t,i){return t instanceof tn?(c(t,i),(this._popup=t)._source=this):(this._popup&&!i||(this._popup=new tn(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&&(Ni(t),i instanceof Be?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 en=Qe.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){Qe.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){Qe.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=Qe.prototype.getEvents.call(this);return bt&&!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=si("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i,e=this._map,n=this._container,o=e.latLngToContainerPoint(e.getCenter()),s=e.layerPointToContainerPoint(t),r=this.options.direction,a=n.offsetWidth,h=n.offsetHeight,u=A(this.options.offset),l=this._getAnchor(),c="top"===r?(i=a/2,h):"bottom"===r?(i=a/2,0):(i="center"===r?a/2:"right"===r?0:"left"===r?a:s.xthis.options.maxZoom||nthis.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 N(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 R(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 k(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(ri(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){ci(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=a,t.onmousemove=a,it&&this.options.opacity<1&&mi(t,this.options.opacity),ot&&!st&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var e=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),p(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&M(p(this._tileReady,this,t,null,o)),vi(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?(mi(e.el,0),z(this._fadeFrame),this._fadeFrame=M(this._updateOpacity,this)):(e.active=!0,this._pruneTiles()),i||(ci(e.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:e.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),it||!this._map._fadeAnimated?M(this._pruneTiles,this):setTimeout(p(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new k(this._wrapX?o(t.x,this._wrapX):t.x,this._wrapY?o(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new I(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 sn=on.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=c(this,i)).detectRetina&&zt&&0')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_n={_initContainer:function(){this._container=si("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(hn.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=cn("shape");ci(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=cn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[m(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;ri(i),t.removeInteractiveTarget(i),delete this._layers[m(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=cn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=g(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=cn("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){hi(t._container)},_bringToBack:function(t){ui(t._container)}},dn=Et?cn:J,pn=hn.extend({getEvents:function(){var t=hn.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=dn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=dn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){ri(this._container),Si(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){var t,i,e;this._map._animatingZoom&&this._bounds||(hn.prototype._update.call(this),i=(t=this._bounds).getSize(),e=this._container,this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),vi(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=dn("path");t.options.className&&ci(i,t.options.className),t.options.interactive&&ci(i,"leaflet-interactive"),this._updateStyle(t),this._layers[m(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){ri(t._path),t.removeInteractiveTarget(t._path),delete this._layers[m(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,$(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){hi(t._path)},_bringToBack:function(t){ui(t._path)}});function mn(t){return Zt||Et?new pn(t):null}Et&&pn.include(_n),Ki.include({getRenderer:function(t){var i=(i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer)||(this._renderer=this._createRenderer());return 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&&ln(t)||mn(t)}});var fn=Re.extend({initialize:function(t,i){Re.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=N(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});pn.create=dn,pn.pointsToPath=$,Ne.geometryToLayer=De,Ne.coordsToLatLng=We,Ne.coordsToLatLngs=He,Ne.latLngToCoords=Fe,Ne.latLngsToCoords=Ue,Ne.getFeature=Ve,Ne.asFeature=qe,Ki.mergeOptions({boxZoom:!0});var gn=ie.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(){zi(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){Si(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){ri(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(),Xt(),xi(),this._startPoint=this._map.mouseEventToContainerPoint(t),zi(document,{contextmenu:Ni,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=si("div","leaflet-zoom-box",this._container),ci(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new I(this._point,this._startPoint),e=i.getSize();vi(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(ri(this._box),_i(this._container,"leaflet-crosshair")),Jt(),wi(),Si(document,{contextmenu:Ni,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){var i;1!==t.which&&1!==t.button||(this._finish(),this._moved&&(this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(p(this._resetState,this),0),i=new R(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()}});Ki.addInitHook("addHandler","boxZoom",gn),Ki.mergeOptions({doubleClickZoom:!0});var vn=ie.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)}});Ki.addInitHook("addHandler","doubleClickZoom",vn),Ki.mergeOptions({dragging:!0,inertia:!st,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var yn=ie.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new ae(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))),ci(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){_i(this._map._container,"leaflet-grab"),_i(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,i=this._map;i._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=N(this._map.options.maxBounds),this._offsetLimit=O(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,i.fire("movestart").fire("dragstart"),i.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var i,e;this._map.options.inertia&&(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 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..769f617 --- /dev/null +++ b/src/lib/autoComplete.ts @@ -0,0 +1,114 @@ +import { h, Children, concatClassName } from 'lib/h' +import * as Button from 'lib/button' + +export function create( + attrs: object, + id: string, + values: string[], + renderEntry: (entry: string) => Element, + onInput: (value: string) => void +): Element { + const completion = h('div', {}) + + const updateCompletion = (target: EventTarget, value: string) => { + const entries = search(value, values) + 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 fa fa-close', + type: 'button', + onclick: () => { + onInput('') + input.value = '' + input.focus() + } + } + ) + ) +} + +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/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/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/fontAwesome.ts b/src/lib/fontAwesome.ts new file mode 100644 index 0000000..896fa52 --- /dev/null +++ b/src/lib/fontAwesome.ts @@ -0,0 +1,788 @@ +export const icons: string [] = [ + "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/form.ts b/src/lib/form.ts new file mode 100644 index 0000000..a1f8cfd --- /dev/null +++ b/src/lib/form.ts @@ -0,0 +1,80 @@ +import { h } from 'lib/h' +import * as Layout from 'lib/layout' +import * as Button from 'lib/button' + +export function input(id: string, label: string, attrs: object): Element { + return h('div', + { className: 'g-Form__Field' }, + h('div', + { className: 'g-Form__Label' }, + h('label', { for: id }, label) + ), + h('input', { id: id, ...attrs }) + ) +} + +export function colorInput( + defaultColors: string[], + id: string, + label: string, + initValue: string, + onInput: (value: string) => void +): Element { + const input = h('input', + { id, + value: initValue, + type: 'color', + oninput: (e: Event) => { + if (e.target !== null) { + onInput((e.target as HTMLInputElement).value) + } + } + } + ) as HTMLInputElement + return h('div', + { className: 'g-Form__Field' }, + h('div', + { className: 'g-Form__Label' }, + h('label', { for: id }, label) + ), + Layout.line( + {}, + ...(defaultColors.map(color => + Button.raw({ className: 'g-Form__DefaultColor', + style: `background-color: ${color}`, + type: 'button', + onclick: () => { + input.value = color + onInput(color) + } + }) + ).concat(input)) + ) + ) +} + +export function textarea( + id: string, + label: string, + initValue: string, + onInput: (value: string) => void +): Element { + return h('div', + { className: 'g-Form__Field' }, + h('div', + { className: 'g-Form__Label' }, + h('label', { for: id }, label) + ), + h('textarea', + { id, + className: 'g-Form__Textarea', + oninput: (e: Event) => { + if (e.target !== null) { + onInput((e.target as HTMLTextAreaElement).value) + } + } + }, + initValue + ) + ) +} diff --git a/src/lib/h.ts b/src/lib/h.ts new file mode 100644 index 0000000..1e49f2f --- /dev/null +++ b/src/lib/h.ts @@ -0,0 +1,41 @@ +type Child = Element | Text | string | number + +export type Children = Child[] + +export function h( + tagName: string, + attrs: object, + ...children: Children +): Element { + const isSvg = tagName === 'svg' || tagName === 'path' + + let elem = isSvg + ? document.createElementNS('http://www.w3.org/2000/svg', tagName) + : document.createElement(tagName) + + if (isSvg) { + Object.entries(attrs).forEach(([key, value]) => { + elem.setAttribute(key, value) + }) + } else { + elem = Object.assign(elem, attrs) + } + + for (const child of children) { + if (typeof child === 'number') + elem.append(child.toString()) + else + elem.append(child) + // if (Array.isArray(child)) + // elem.append(...child) + // else + // elem.append(child) + } + + return elem +} + +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/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..4f8c675 --- /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() + }, + h('div', { className: 'fa fa-close' }) + ), + 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..cc1df17 --- /dev/null +++ b/src/map.ts @@ -0,0 +1,126 @@ +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' +const L = window.L + +let map + +export function view() { + // let state = ref (state_from_hash ()) in + // let map = ref None in + // let markers = Leaflet.feature_group [| |] in + window.setTimeout(() => map = getMap(), 0) + return element() +} + +function element(): Element { + return h('div', + { className: 'g-Layout__Page' }, + h('div', + { className: 'g-Layout__Header' }, + h('a', + { className: 'g-Layout__Home', + href: '#' + }, + 'Map' + ), + Layout.line( + { className: 'g-Layout__HeaderImportExport' }, + h('input', + { id: 'g-Header__ImportInput', + type: 'file', + onchange: () => { + // 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', + { for: 'g-Header__ImportInput', + className: 'g-Button__Text' + }, + 'Import' + ), + Button.text({}, 'Export') + // { onclick: () => File.download 'map.csv' (State.to_csv_string !state)) |] + ) + ) + , h('div', + { className: 'g-Map' }, + h('div', { id: 'g-Map__Content' }) + ) + ) +} + +function getMap(): object { + + const map = L.map('g-Map__Content', { + center: [51.505, -0.09], + zoom: 13, + attributionControl: false + }) + + // map.addLayer(markers) + map.addLayer(L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png')) + + // (* 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 + + // Context menu + map.addEventListener('contextmenu', e => { + ContextMenu.show( + e.originalEvent, + [ { label: 'Add a marker', + action: () => { + const pos = e.latlng + const marker = { pos, name: '', color: '#3F92CF', icon: '' } + const colors: string[] = [] + + const add_marker = (name: string, color: string, icon: string) => { + console.log('adding marker…') + // 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 + } + + // 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, + colors, + marker.name, + marker.color, + marker.icon + )) + } + } + ] + ) + }) + + return map +} diff --git a/src/marker.ts b/src/marker.ts new file mode 100644 index 0000000..67b9649 --- /dev/null +++ b/src/marker.ts @@ -0,0 +1,125 @@ +import { h } from 'lib/h' +import * as Button from 'lib/button' +import * as Form from 'lib/form' +import * as Layout from 'lib/layout' +import * as Modal from 'lib/modal' +import * as FontAwesome from 'lib/fontAwesome' +import * as AutoComplete from 'lib/autoComplete' + +export function form( + onValidate: (name: string, color: string, icon: string) => void, + colors: string[], + name: string, + color: string, + icon: string +): Element { + const onSubmit = () => { + onValidate(name, color, icon) + Modal.hide() + } + const domIcon = h('div', { className: `fa fa-${icon}` }) + return h('div', + {}, + Layout.section( + {}, + h('form', + { className: 'g-MarkerForm', + onsubmit: (e: Event) => { + e.preventDefault() + onSubmit() + } + }, + Layout.section( + {}, + Form.input( + 'g-MarkerForm__Name', + 'Name', + { oninput: (e: Event) => { + if (e.target !== null) { + name = (e.target as HTMLInputElement).value + } + }, + value: name + } + ), + Form.colorInput( + colors, + 'g-MarkerForm__Color', + 'Color', + color, + 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', + FontAwesome.icons, + icon => h('div', + {}, + h('div', { className: `g-MarkerForm__IconEntry fa fa-${icon}` }), + icon + ), + newIcon => { + icon = newIcon + domIcon.className = `fa fa-${icon}` + }), + h('div', { className: 'g-MarkerForm__Icon' }, domIcon) + ) + ) + ) + ), + Layout.line( + {}, + Button.action({ onclick: () => onSubmit() }, 'Save'), + Button.cancel( + { onclick: () => Modal.hide(), + type: 'button' + }, + '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/types/leaflet.d.ts b/src/types/leaflet.d.ts new file mode 100644 index 0000000..39ddf5a --- /dev/null +++ b/src/types/leaflet.d.ts @@ -0,0 +1,28 @@ +export as namespace L + +export function map(element: string, options?: MapOptions): Map + +export interface MapOptions { + center: number[], + zoom: number, + attributionControl: boolean, +} + +export interface Map { + addLayer: (layer: Layer) => void, + addEventListener: (name: string, fn: (e: MapEvent) => void) => void, +} + +interface MapEvent { + originalEvent: MouseEvent, + latlng: {lat: number, lng: number}, +} + +export interface Pos { + lat: number, + lng: number, +} + +export function tileLayer(url: string): Layer + +export interface Layer {} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5cbbec5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "amd", + "target": "ES2017", + "baseUrl": "src", + "noImplicitAny": true, + "strictNullChecks": true, + "removeComments": true, + "preserveConstEnums": true, + "outFile": "public/main.js" + }, + "include": ["src/**/*"] +} -- cgit v1.2.3