diff --git a/.gitattributes b/.gitattributes index 516857104..ea2a81515 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,3 +8,6 @@ yarn.lock linguist-generated # Mark any vendored files as having been vendored. vendor/* linguist-vendored + +Gemfile merge=ours +Gemfile.lock merge=ours \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8ac3419f0..3bea7feff 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,16 @@ .tool-versions # Ignore ENV variable file -.env \ No newline at end of file +.env + +# Ignore Gemfiles +Gemfile +Gemfile.lock + +# ignore emacs backup files +*~ +\#*\# +.idea + +config/master.key + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..82cf78194 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.exclude": { + "**/.git": false + } +} \ No newline at end of file diff --git a/Gemfile b/Gemfile index 5efe339e6..2843f3eb7 100644 --- a/Gemfile +++ b/Gemfile @@ -34,10 +34,10 @@ gem "jbuilder" gem "redis", "~> 4.0" # Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] -# gem "kredis" +gem "kredis" # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] -# gem "bcrypt", "~> 3.1.7" +gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] @@ -45,8 +45,11 @@ gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] # Reduces boot times through caching; required in config/boot.rb gem "bootsnap", require: false +# Boostrap +gem 'bootstrap', '~> 4.1.1' + # Use Sass to process CSS -# gem "sassc-rails" +gem "sass-rails", '~> 5.0' # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # gem "image_processing", "~> 1.2" @@ -83,3 +86,13 @@ gem 'dotenv-rails' # gem 'paperclip # gem 'uniquify' # gem 'will_paginate' + +gem "nio4r", "~> 2.5" + +gem 'geocoder', '~> 1.3', '>= 1.3.7' + +gem 'stripe' + +# for pfp +gem 'carrierwave', '~> 3.0' +gem 'rmagick' diff --git a/Gemfile.lock b/Gemfile.lock index c3e104938..a718e5ce2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,244 +1,321 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.0.3.1) - actionpack (= 7.0.3.1) - activesupport (= 7.0.3.1) + actioncable (7.0.8.6) + actionpack (= 7.0.8.6) + activesupport (= 7.0.8.6) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.3.1) - actionpack (= 7.0.3.1) - activejob (= 7.0.3.1) - activerecord (= 7.0.3.1) - activestorage (= 7.0.3.1) - activesupport (= 7.0.3.1) + actionmailbox (7.0.8.6) + actionpack (= 7.0.8.6) + activejob (= 7.0.8.6) + activerecord (= 7.0.8.6) + activestorage (= 7.0.8.6) + activesupport (= 7.0.8.6) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.3.1) - actionpack (= 7.0.3.1) - actionview (= 7.0.3.1) - activejob (= 7.0.3.1) - activesupport (= 7.0.3.1) + actionmailer (7.0.8.6) + actionpack (= 7.0.8.6) + actionview (= 7.0.8.6) + activejob (= 7.0.8.6) + activesupport (= 7.0.8.6) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.3.1) - actionview (= 7.0.3.1) - activesupport (= 7.0.3.1) - rack (~> 2.0, >= 2.2.0) + actionpack (7.0.8.6) + actionview (= 7.0.8.6) + activesupport (= 7.0.8.6) + rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.3.1) - actionpack (= 7.0.3.1) - activerecord (= 7.0.3.1) - activestorage (= 7.0.3.1) - activesupport (= 7.0.3.1) + actiontext (7.0.8.6) + actionpack (= 7.0.8.6) + activerecord (= 7.0.8.6) + activestorage (= 7.0.8.6) + activesupport (= 7.0.8.6) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.3.1) - activesupport (= 7.0.3.1) + actionview (7.0.8.6) + activesupport (= 7.0.8.6) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.3.1) - activesupport (= 7.0.3.1) + activejob (7.0.8.6) + activesupport (= 7.0.8.6) globalid (>= 0.3.6) - activemodel (7.0.3.1) - activesupport (= 7.0.3.1) - activerecord (7.0.3.1) - activemodel (= 7.0.3.1) - activesupport (= 7.0.3.1) - activestorage (7.0.3.1) - actionpack (= 7.0.3.1) - activejob (= 7.0.3.1) - activerecord (= 7.0.3.1) - activesupport (= 7.0.3.1) + activemodel (7.0.8.6) + activesupport (= 7.0.8.6) + activerecord (7.0.8.6) + activemodel (= 7.0.8.6) + activesupport (= 7.0.8.6) + activestorage (7.0.8.6) + actionpack (= 7.0.8.6) + activejob (= 7.0.8.6) + activerecord (= 7.0.8.6) + activesupport (= 7.0.8.6) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.3.1) + activesupport (7.0.8.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + autoprefixer-rails (10.4.19.0) + execjs (~> 2) + base64 (0.2.0) + bcrypt (3.1.20) bindex (0.8.1) - bootsnap (1.13.0) + bootsnap (1.18.4) msgpack (~> 1.2) - builder (3.2.4) - capybara (3.37.1) + bootstrap (4.1.3) + autoprefixer-rails (>= 6.0.3) + popper_js (>= 1.12.9, < 2) + sass (>= 3.5.2) + builder (3.3.0) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (4.1.0) - concurrent-ruby (1.1.10) + carrierwave (3.0.7) + activemodel (>= 6.0.0) + activesupport (>= 6.0.0) + addressable (~> 2.6) + image_processing (~> 1.1) + marcel (~> 1.0.0) + ssrf_filter (~> 1.0) + concurrent-ruby (1.3.4) crass (1.0.6) - debug (1.6.2) - irb (>= 1.3.6) - reline (>= 0.3.1) - digest (3.1.0) - dotenv (2.8.1) - dotenv-rails (2.8.1) - dotenv (= 2.8.1) - railties (>= 3.2) - erubi (1.11.0) - globalid (1.0.0) - activesupport (>= 5.0) - i18n (1.12.0) + csv (3.3.0) + date (3.4.0) + debug (1.9.2) + irb (~> 1.10) + reline (>= 0.3.8) + dotenv (3.1.4) + dotenv-rails (3.1.4) + dotenv (= 3.1.4) + railties (>= 6.1) + erubi (1.13.0) + execjs (2.10.0) + ffi (1.17.0) + ffi (1.17.0-arm64-darwin) + ffi (1.17.0-x86_64-darwin) + geocoder (1.8.3) + base64 (>= 0.1.0) + csv (>= 3.0.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.6) concurrent-ruby (~> 1.0) - importmap-rails (1.1.5) + image_processing (1.13.0) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) + importmap-rails (2.0.3) actionpack (>= 6.0.0) + activesupport (>= 6.0.0) railties (>= 6.0.0) - io-console (0.5.11) - irb (1.4.1) - reline (>= 0.3.0) - jbuilder (2.11.5) + io-console (0.7.2) + irb (1.14.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) - loofah (2.18.0) + kredis (1.7.0) + activemodel (>= 6.0.0) + activesupport (>= 6.0.0) + redis (>= 4.2, < 6) + logger (1.6.1) + loofah (2.23.1) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) - marcel (1.0.2) + net-imap + net-pop + net-smtp + marcel (1.0.4) matrix (0.4.2) - method_source (1.0.0) - mini_mime (1.1.2) - minitest (5.16.3) - msgpack (1.5.6) - mysql2 (0.5.4) - net-imap (0.2.3) - digest + method_source (1.1.0) + mini_magick (4.13.2) + mini_mime (1.1.5) + minitest (5.25.1) + msgpack (1.7.3) + mysql2 (0.5.6) + net-imap (0.5.0) + date net-protocol - strscan - net-pop (0.1.1) - digest + net-pop (0.1.2) net-protocol + net-protocol (0.2.2) timeout - net-protocol (0.1.3) - timeout - net-smtp (0.3.1) - digest + net-smtp (0.5.0) net-protocol - timeout - nio4r (2.5.8) - nokogiri (1.13.8-arm64-darwin) + nio4r (2.7.4) + nokogiri (1.16.7-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-darwin) racc (~> 1.4) - nokogiri (1.13.8-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) - public_suffix (5.0.0) - puma (5.6.5) + observer (0.1.2) + pkg-config (1.5.8) + popper_js (1.16.1) + psych (5.1.2) + stringio + public_suffix (6.0.1) + puma (5.6.9) nio4r (~> 2.0) - racc (1.6.0) - rack (2.2.4) - rack-test (2.0.2) + racc (1.8.1) + rack (2.2.10) + rack-test (2.1.0) rack (>= 1.3) - rails (7.0.3.1) - actioncable (= 7.0.3.1) - actionmailbox (= 7.0.3.1) - actionmailer (= 7.0.3.1) - actionpack (= 7.0.3.1) - actiontext (= 7.0.3.1) - actionview (= 7.0.3.1) - activejob (= 7.0.3.1) - activemodel (= 7.0.3.1) - activerecord (= 7.0.3.1) - activestorage (= 7.0.3.1) - activesupport (= 7.0.3.1) + rails (7.0.8.6) + actioncable (= 7.0.8.6) + actionmailbox (= 7.0.8.6) + actionmailer (= 7.0.8.6) + actionpack (= 7.0.8.6) + actiontext (= 7.0.8.6) + actionview (= 7.0.8.6) + activejob (= 7.0.8.6) + activemodel (= 7.0.8.6) + activerecord (= 7.0.8.6) + activestorage (= 7.0.8.6) + activesupport (= 7.0.8.6) bundler (>= 1.15.0) - railties (= 7.0.3.1) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + railties (= 7.0.8.6) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.3) - loofah (~> 2.3) - railties (7.0.3.1) - actionpack (= 7.0.3.1) - activesupport (= 7.0.3.1) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.0.8.6) + actionpack (= 7.0.8.6) + activesupport (= 7.0.8.6) method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) - rake (13.0.6) - redis (4.8.0) - regexp_parser (2.5.0) - reline (0.3.1) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rdoc (6.7.0) + psych (>= 4.0.0) + redis (4.8.1) + regexp_parser (2.9.2) + reline (0.5.10) io-console (~> 0.5) - rexml (3.2.5) + rexml (3.3.9) + rmagick (6.0.1) + observer (~> 0.1) + pkg-config (~> 1.4) + ruby-vips (2.2.2) + ffi (~> 1.12) + logger rubyzip (2.3.2) - selenium-webdriver (4.4.0) - childprocess (>= 0.5, < 5.0) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.1.0) + railties (>= 5.2.0) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + selenium-webdriver (4.10.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - sprockets (4.1.1) + sprockets (3.7.5) + base64 concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - stimulus-rails (1.1.0) - railties (>= 6.0.0) - strscan (3.0.4) - tailwindcss-rails (2.0.12-arm64-darwin) - railties (>= 6.0.0) - tailwindcss-rails (2.0.12-x86_64-linux) + ssrf_filter (1.2.0) + stimulus-rails (1.3.4) railties (>= 6.0.0) - thor (1.2.1) - timeout (0.3.0) - turbo-rails (1.1.1) + stringio (3.1.1) + stripe (13.2.0) + tailwindcss-rails (3.0.0) + railties (>= 7.0.0) + tailwindcss-ruby + tailwindcss-ruby (3.4.14-arm64-darwin) + tailwindcss-ruby (3.4.14-x86_64-darwin) + tailwindcss-ruby (3.4.14-x86_64-linux) + thor (1.3.2) + tilt (2.4.0) + timeout (0.4.1) + turbo-rails (2.0.11) actionpack (>= 6.0.0) - activejob (>= 6.0.0) railties (>= 6.0.0) - tzinfo (2.0.5) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - web-console (4.2.0) + web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webdrivers (5.0.0) + webdrivers (5.3.1) nokogiri (~> 1.6) rubyzip (>= 1.3.0) - selenium-webdriver (~> 4.0) - websocket (1.2.9) - websocket-driver (0.7.5) + selenium-webdriver (~> 4.0, < 4.11) + websocket (1.2.11) + websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.0) + zeitwerk (2.6.18) PLATFORMS - arm64-darwin-20 + arm64-darwin + arm64-darwin-23 + x86_64-darwin x86_64-linux DEPENDENCIES + bcrypt (~> 3.1.7) bootsnap + bootstrap (~> 4.1.1) capybara + carrierwave (~> 3.0) debug dotenv-rails + geocoder (~> 1.3, >= 1.3.7) importmap-rails jbuilder + kredis mysql2 (~> 0.5) + nio4r (~> 2.5) puma (~> 5.0) rails (~> 7.0.3, >= 7.0.3.1) redis (~> 4.0) + rmagick + sass-rails (~> 5.0) selenium-webdriver sprockets-rails stimulus-rails + stripe tailwindcss-rails turbo-rails tzinfo-data @@ -249,4 +326,4 @@ RUBY VERSION ruby 3.1.2p20 BUNDLED WITH - 2.3.18 + 2.5.18 diff --git a/README.md b/README.md index 85f6c86ae..8ac3209fb 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ Smith College CSC223: Software Engineering\ Starter App for ValetBike project +#katie was here :3 + ## Environment Configuration As you configure your environment you should **keep a log** where you **write down all the steps you take** including **each command you type**. You will inevitably run into errors setting up your development environment and maintaining a meticulous log will allow others to help you troubleshoot. Ignore this recommendation at your own peril, but don't say you haven't been warned :] diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index b06fc42ac..a2d906a5b 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,5 +1,6 @@ //= link_tree ../images -//= link_directory ../stylesheets .css +//= link application.css +//= link_directory ../stylesheets .scss //= link_tree ../../javascript .js //= link_tree ../../../vendor/javascript .js //= link_tree ../builds diff --git a/app/assets/images/MHBike.png b/app/assets/images/MHBike.png new file mode 100644 index 000000000..27e6e0eca Binary files /dev/null and b/app/assets/images/MHBike.png differ diff --git a/app/assets/images/MHEndTrip.png b/app/assets/images/MHEndTrip.png new file mode 100644 index 000000000..619a52b75 Binary files /dev/null and b/app/assets/images/MHEndTrip.png differ diff --git a/app/assets/images/MHPaymentSuccess.png b/app/assets/images/MHPaymentSuccess.png new file mode 100644 index 000000000..24f4ccc3f Binary files /dev/null and b/app/assets/images/MHPaymentSuccess.png differ diff --git a/app/assets/images/MHmoney.jpg b/app/assets/images/MHmoney.jpg new file mode 100644 index 000000000..1fc457e69 Binary files /dev/null and b/app/assets/images/MHmoney.jpg differ diff --git a/app/assets/images/MHvehicle.webp b/app/assets/images/MHvehicle.webp new file mode 100644 index 000000000..bf632d45a Binary files /dev/null and b/app/assets/images/MHvehicle.webp differ diff --git a/app/assets/images/bike.svg b/app/assets/images/bike.svg new file mode 100644 index 000000000..6a3d09430 --- /dev/null +++ b/app/assets/images/bike.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/assets/images/color-theme.svg b/app/assets/images/color-theme.svg new file mode 100644 index 000000000..caeb069d4 --- /dev/null +++ b/app/assets/images/color-theme.svg @@ -0,0 +1,12 @@ + + + + ic_fluent_dark_theme_24_regular + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/app/assets/images/cycling.svg b/app/assets/images/cycling.svg new file mode 100644 index 000000000..9c2234cc1 --- /dev/null +++ b/app/assets/images/cycling.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/edit.svg b/app/assets/images/edit.svg new file mode 100644 index 000000000..54bc42433 --- /dev/null +++ b/app/assets/images/edit.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/assets/images/fallback/default.png b/app/assets/images/fallback/default.png new file mode 100644 index 000000000..a83bbd969 Binary files /dev/null and b/app/assets/images/fallback/default.png differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100644 index 000000000..ffb2aed28 Binary files /dev/null and b/app/assets/images/favicon.ico differ diff --git a/app/assets/images/ghoulia.jpg b/app/assets/images/ghoulia.jpg new file mode 100644 index 000000000..f165cad2c Binary files /dev/null and b/app/assets/images/ghoulia.jpg differ diff --git a/app/assets/images/locations.svg b/app/assets/images/locations.svg new file mode 100644 index 000000000..ac5ad7615 --- /dev/null +++ b/app/assets/images/locations.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png new file mode 100644 index 000000000..7184b3aaa Binary files /dev/null and b/app/assets/images/logo.png differ diff --git a/app/assets/images/map-marker.png b/app/assets/images/map-marker.png new file mode 100644 index 000000000..9f85490c0 Binary files /dev/null and b/app/assets/images/map-marker.png differ diff --git a/app/assets/images/pin.svg b/app/assets/images/pin.svg new file mode 100644 index 000000000..993e648bd --- /dev/null +++ b/app/assets/images/pin.svg @@ -0,0 +1,19 @@ + + + + Pin-location-1 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/stylesheets/account.css b/app/assets/stylesheets/account.css new file mode 100644 index 000000000..0b255e07a --- /dev/null +++ b/app/assets/stylesheets/account.css @@ -0,0 +1,16 @@ +.avatar-wrapper { + margin: 0 auto; + width: 150px; + height: 150px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + border: 3px solid var(--theme-color); +} + +.avatar-wrapper img { + width: 100%; + height: 100%; + border-radius: 50%; +} \ No newline at end of file diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 288b9ab71..7c35026b8 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -12,4 +12,5 @@ * *= require_tree . *= require_self - */ + + */ \ No newline at end of file diff --git a/app/assets/stylesheets/footer.css b/app/assets/stylesheets/footer.css new file mode 100644 index 000000000..2bfd0828f --- /dev/null +++ b/app/assets/stylesheets/footer.css @@ -0,0 +1,11 @@ +footer { + background-color: var(--theme-color); + width: 100%; + padding: 3rem; + margin-top: 5rem; + min-height: 300px; +} + +footer .page-section { + margin: 5rem auto; +} \ No newline at end of file diff --git a/app/assets/stylesheets/form.css b/app/assets/stylesheets/form.css new file mode 100644 index 000000000..1ca78a081 --- /dev/null +++ b/app/assets/stylesheets/form.css @@ -0,0 +1,63 @@ +.form-container { + background-color: var(--white-color); + margin-top: 5rem; + padding: 2rem; + border-radius: var(--widget-radius); +} + +.form-wrapper { + margin-top: 2rem; +} + +.form-wrapper input[type=submit], .form-wrapper button { + display: block; + margin: auto; + width: 100%; +} + +.form-title { + font-family: var(--title-font); + font-weight: 500; + font-size: 1.5em; + text-align: center; +} + +.form-field label { + font-size: 0.8em; +} + +.form-field .form-control { + border-radius: var(--btn-radius); + border: var(--btn-thickness) solid var(--gray-color); +} + +.error-message { + font-size: 0.8em; + margin-bottom: 0; +} + +.error-message li { + list-style-type: circle; +} + +.form-links { + margin-top: 1em; + text-align: center; +} + +.form-links p a { + color: var(--theme-color); +} + +.logo-header { + height: 6em; + width: auto; + margin: 0 auto; +} + +/** Account page **/ + +.sm-icon { + width: 15px; + height: 15px; +} \ No newline at end of file diff --git a/app/assets/stylesheets/global.css b/app/assets/stylesheets/global.css index ff48f1abe..2848f7042 100644 --- a/app/assets/stylesheets/global.css +++ b/app/assets/stylesheets/global.css @@ -5,7 +5,6 @@ * { margin: 0; padding: 0; - z-index: 1; } html, body { @@ -22,6 +21,30 @@ body { -ms-text-size-adjust:100%; } +caption{ + font-size:15pt; + -webkit-text-size-adjust:100%; + -moz-text-size-adjust:100%; + -ms-text-size-adjust:100%; + text-align: center; +} + +sub-caption{ + font-size:12pt; + -webkit-text-size-adjust:100%; + -moz-text-size-adjust:100%; + -ms-text-size-adjust:100%; + text-align: center; +} + +a.link-regular { + color: var(--theme-color); +} + +a.link-black { + color: var(--black-color); +} + #site-container{ position: relative; width: 100%; @@ -29,7 +52,146 @@ body { height: auto !important; min-height: 100%; font-family: var(--body-font); - font-weight: 400; + font-weight: 100; color: var(--black-color); background: var(--white-color); } + +input[type=submit]:disabled { + background-color: var(--gray-color); + color: var(--white-color); + border-color: var(--gray-color); +} + +.bg-fill { + background-color: var(--theme-color); + position: absolute; + width: 100%; + height: auto; + min-height: 100%; + padding-bottom: 3rem; +} + +.section-heading { + font-size: 1.3em; + font-family: var(--title-font); + margin-bottom: 0.8rem; +} + +.section-heading-h2 { + font-size: 1em; + font-family: var(--title-font); + margin-bottom: 0.8rem; +} + +.custom-btn { + padding: 0.5rem 1.5rem; + border-radius: var(--btn-radius); + transition: 0.1s ease; +} + +.button-container { + display: flex; + align-items: center; + gap: 10px; /* Optional: adds some space between the button and the link */ +} + +.btn-fill { + background: var(--theme-color) !important; + color: var(--white-color) !important; + border: var(--btn-thickness) solid var(--theme-color) !important; +} + +.btn-fill:hover { + background: var(--white-color) !important; + color: var(--black-color) !important; +} + +.btn-outline { + background: var(--white-color) !important; + border: var(--btn-thickness) solid var(--theme-color) !important; +} + +.togglestuff .btn-toggle { + font-family: var(--body-font) !important; + font-weight: 200; + padding: 0.5rem 1.5rem; + border-radius: var(--btn-radius); + transition: 0.1s ease; +} + +.togglestuff .btn-toggle-outline { + background: var(--white-color) !important; + border: var(--btn-thickness) solid var(--theme-color) !important; +} + +.togglestuff .btn-toggle-outline:hover { + background: var(--theme-color) !important; + color: var(--white-color) !important; + border: var(--btn-thickness) solid var(--theme-color) !important; +} + +input[type="radio"]:checked + label { + background: var(--theme-color) !important; + color: var(--white-color) !important; + border: var(--btn-thickness) solid var(--theme-color) !important; +} + +.btn-outline:hover { + background: var(--theme-color) !important; + color: var(--white-color) !important; +} + +.btn-fill-danger { + background: var(--danger-color); + color: var(--white-color); + border: var(--btn-thickness) solid var(--danger-color); +} + +.btn-fill-danger:hover { + background-color: var(--white-color); + color: var(--danger-color); +} + +.btn-invalid { + background: var(--invalid_fill-color); + color: var(--invalid_text-color); + border: var(--btn-thickness) solid var(--invalid_fill-color); +} + +.btn-underline { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + transition: 0.2s ease-in-out; +} + +.btn-underline::before { + content: ''; + position: absolute; + left: 0; + bottom: -2px; + height: 1.5px; + width: 100%; +} + +.btn-underline.regular::before { + background-color: var(--black-color); +} + +.btn-underline.danger::before { + background-color: var(--danger-color); +} + +.btn-underline:hover { + opacity: 0.7; +} + +.highlighter { + color: var(--theme-color); +} + +.danger { + color: var(--danger-color); +} \ No newline at end of file diff --git a/app/assets/stylesheets/header.css b/app/assets/stylesheets/header.css new file mode 100644 index 000000000..0d723548b --- /dev/null +++ b/app/assets/stylesheets/header.css @@ -0,0 +1,63 @@ +.navbar { + margin: 3em auto; + width: 70%; + background: rgba(255, 255, 255, 0.2); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(5px); + border-radius: var(--widget-radius); +} + +ul.header { + list-style: none; + padding: 1rem 2rem; + margin: 0; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +ul.header li { + display: block; +} + +.navbar li a { + text-decoration: none; + font-family: var(--title-font); + font-size: 1rem; + color: var(--black-color); + font-weight: 300; + transition: 0.2s ease-in-out; +} + +.navbar li a:hover { + color: var(--theme-color); +} + +.navbar.navbar-mobile { + display: none; + background: rgba(255, 255, 255, 0.2); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(5px); + width: 100%; + margin: 0; + padding: 1em; +} + +/***********************************/ +/* Responsiveness */ +/***********************************/ + +@media (max-width:868px) { + .navbar.navbar-desktop { + display: none; + } + + .navbar.navbar-mobile { + display: block; + } + + .navbar.navbar-mobile .show.collapse { + visibility: visible; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/pages.css b/app/assets/stylesheets/pages.css index 16bd64b55..0b2caf208 100644 --- a/app/assets/stylesheets/pages.css +++ b/app/assets/stylesheets/pages.css @@ -5,7 +5,7 @@ .page-section { max-width: 80em; width: 100%; - margin-top: 4em; + margin-top: 10em; } .page-section .section-inner { @@ -15,33 +15,114 @@ .page-section .section-title { font-family: var(--title-font); - font-weight: 500; + font-weight: 300; font-size: 2.5em; margin-bottom: 1em; } -.record-row { - padding: 0.5em 1.25em 0.5em 1.25em; - font-size: 0.85em; - line-height: 1.75em; +.page-section .section-caption { + font-family: var(--body-font); + font-weight: 200; + font-size: 1.2em; + overflow-wrap: break-word; + margin-bottom: 1em; +} + +.page-section .section-banner { + max-height: 300px; + margin: auto; +} + +.step-block { + margin: 1rem; + padding: 1rem; + display: block; + position: relative; + border: 1px solid var(--light-black-color); + border-radius: var(--widget-radius); + background-color: var(--white-color); +} + +.step-block.small { + height: 180px; +} + +.step-block.medium { + height: 300px; + padding: 2rem; +} + +.step-block::before { + content: ''; + opacity: 0; + display: block; + position: absolute; + left: 5px; + top: 5px; + width: 100%; + height: 100%; + background-color: var(--theme-color); + border: 1px solid var(--theme-color); + border-radius: var(--widget-radius); + z-index: -1; +} + +.step-block:hover { + transition: all 0.2s ease-in; + background-color: var(--theme-color); } -.record-row.even { - background: var(--medium-white-color); +.step-block h4 { + font-family: var(--title-font); + text-align: center; + margin-bottom: 2rem; } -.record-row.odd { - background: var(--dark-white-color); +.step-block p { + text-align: center; } +.step-block .svg-icon { + width: auto; + height: 3em; + margin: 1em auto; +} + +.leaflet-popup-content { + font-family: var(--body-font) !important; + font-weight: 200 !important; +} + +.leaflet-popup-content .title { + font-size: 1rem !important; + color: var(--theme-color) !important; +} + +.leaflet-popup-content .body { + font-weight: 100 !important; + font-size: 1em !important; +} + +.record-row { + padding: 2em 1.25em; + font-size: 0.85em; + border-bottom: 1px solid var(--gray-color); +} + +.record-row .record-name { + font-family: var(--title-font); + font-size: 1rem; + margin-bottom: 0.8rem; + color: var(--theme-color); +} /***********************************/ /* Responsiveness */ /***********************************/ -@media (max-width:48em) { +@media (max-width:868px) { .page-section { - margin-top: 3em; + margin-top: 3em; } .page-section .section-title { diff --git a/app/assets/stylesheets/stations.css b/app/assets/stylesheets/stations.css new file mode 100644 index 000000000..d3fcb949e --- /dev/null +++ b/app/assets/stylesheets/stations.css @@ -0,0 +1,5 @@ +.breadcrumb-container { + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/app/assets/stylesheets/variables.css b/app/assets/stylesheets/variables.css index 915c14dd3..c96fe840d 100644 --- a/app/assets/stylesheets/variables.css +++ b/app/assets/stylesheets/variables.css @@ -1,6 +1,8 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap'); + :root { - --title-font: 'Rubik', 'Arial'; - --body-font: 'Rubik', 'Arial'; + --title-font: "Climate Crisis", sans-serif; + --body-font: "Inter", sans-serif; --black-color: #160919; --light-black-color: #2E2230; @@ -9,5 +11,12 @@ --dark-white-color: #F3F2F3; --medium-white-color: #FDFCFD; --white-color: #FFFFFF; + --invalid_text-color: #818181; + --invalid_fill-color: #f2f2f2; + --theme-color: #7C88F4; + --btn-radius: 0.3rem; + --widget-radius: 0.5rem; + --btn-thickness: 2px; + --danger-color: #8B0000; } \ No newline at end of file diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb new file mode 100644 index 000000000..71cc9f352 --- /dev/null +++ b/app/controllers/access_controller.rb @@ -0,0 +1,20 @@ +class AccessController < ApplicationController + before_action :disable_navigation + + def create + # Process log in information + @user = User.find_by(email: params[:email]) + if !!@user && @user&.authenticate(params[:password]) + session[:user_id] = @user[:id] + redirect_to account_index_path + else + flash[:notice] = "Something went wrong. Please check your email and password." + redirect_to log_in_path + end + end + + def destroy + session[:user_id] = nil + redirect_to root_path + end +end diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb new file mode 100644 index 000000000..b642f771d --- /dev/null +++ b/app/controllers/account_controller.rb @@ -0,0 +1,109 @@ +require 'date' + +class AccountController < ApplicationController + before_action :get_user_info_from_session + before_action :redirect_edit, only: [:edit, :update] + before_action :remove_pfp_upload, only: [:edit_profile_img, :upload_profile_img] + def index + @trips = Trip.all + if !logged_in? || !@user + redirect_to log_in_path + else + set_up_edit_mode() + + if !!@user.subscription_id + # Search for the user's available subscription + @subscription = Stripe::Subscription.retrieve(@user.subscription_id) + + @subscription.items.data.each do |item| + @meter_id = item.plan.meter + @start_date = Time.at(item.created).to_datetime.strftime('%F %H:%M') + @status = item.plan.active ? "Active" : "Inactive" + end + + # Get the product by meter_id + @product = Product.find_by(meter_id: @meter_id) + + @upcoming_invoice = Stripe::Invoice.upcoming({customer: @user.stripe_customer_id}) + + @invoice_total = @upcoming_invoice.amount_due + @created_time = Time.at(@upcoming_invoice.created).to_datetime.strftime('%F %H:%M') + @attempted = Time.now < Time.at(@upcoming_invoice.created) ? 'Payment not started' : @upcoming_invoice.attempted ? 'Paid' : 'Unpaid' + + end + end + end + + def cancel_subscription + if !!@user && !!@user.subscription_id + # Search for the user's available subscription + subscription_id = @user.subscription_id + Stripe::Subscription.cancel(subscription_id) + @user.update(subscription_id: nil) + redirect_to account_index_path + end + end + + def destroy + if !!@user + session[:user_id] = nil + @user.destroy + redirect_to root_path + end + end + + def edit + @enable_edit = !@enable_edit + render :index + end + + def update + if !!@user && @user.update(user_params) + flash[:notice] = "Your details were updated successfully!" + redirect_to account_index_path + else + render :index, status: 422 + end + end + + def edit_profile_img + @enable_profile_upload = !@enable_profile_upload + render :index + end + + def upload_profile_img + if !!@user && @user.update_attribute(:avatar, params[:avatar]) + flash[:notice] = "Avatar uploaded successfully!" + redirect_to account_index_path + else + render :index, status: 422, error: "Something went wrong. Please try again." + end + end + + private + + def user_params + params.require(:user).permit(:name, :phone_number) + end + + def set_up_edit_mode + @enable_edit = false + @enable_profile_upload = false + end + + def redirect_edit + # Cancel the edits + redirect_to account_index_path if params[:cancel_edit] + end + + def remove_pfp_upload + if !!@user && params[:cancel_edit_pfp] + @user.remove_avatar! + @user.remove_avatar = true + @user.save + @user.reload + redirect_to account_index_path + end + end + +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d12a..c52dd4b75 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,20 @@ class ApplicationController < ActionController::Base + + helper_method :get_user_info_from_session + helper_method :logged_in? + helper_method :disable_navigation + helper_method :station + def get_user_info_from_session + @user = User.find_by(id: session[:user_id]) + end + def logged_in? + # Check user's log in status + session[:user_id].present? + end + def disable_navigation + # For pages that don't need header/footer for navigation + # For example: sign in/up forms + @disable_header = true + @disable_footer = true + end end diff --git a/app/controllers/bikes_controller.rb b/app/controllers/bikes_controller.rb new file mode 100644 index 000000000..036a4f038 --- /dev/null +++ b/app/controllers/bikes_controller.rb @@ -0,0 +1,12 @@ +class BikesController < ApplicationController + def _row + @bikes = Bike.all.order(identifier: :asc) + end + + def index + end + def show + @bike = Bike.find(params[:id]) + end + +end diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb new file mode 100644 index 000000000..513b8c4f8 --- /dev/null +++ b/app/controllers/main_controller.rb @@ -0,0 +1,7 @@ +class MainController < ApplicationController + def index + end + + def contact + end +end diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb new file mode 100644 index 000000000..998b29e18 --- /dev/null +++ b/app/controllers/password_resets_controller.rb @@ -0,0 +1,52 @@ +class PasswordResetsController < ApplicationController + before_action :disable_navigation + + def create + # Process email and send reset link + @user = User.find_by(email: params[:email]) + + if !!@user + # Send email in the background + PasswordMailer.with(user: @user).reset.deliver_later + flash[:notice] = "An email will be sent with a link to reset your password." + else + flash[:notice] = "An error occured. Please try again." + end + + redirect_to password_reset_url + end + + def edit + # Find the user with the given token + if params[:token] + @user = User.find_signed(params[:token], purpose: "password_reset") + else + redirect_to log_in_path + end + rescue ActiveSupport::MessageVerifier::InvalidSignature + redirect_to password_reset_url, notice: "Your token has expired. Please try again" + end + + def update + # Actually updates the password + @user = User.find_signed(params[:token], purpose: "password_reset") + + if @user.update(password_params) + flash[:notice] = "Your password was reset successfully. Please sign in." + redirect_to log_in_path + else + render :edit, status: 422 # have to set status. otherwise, the errors won't render + end + + rescue ActiveSupport::MessageVerifier::InvalidSignature + redirect_to password_reset_url, notice: "Your token has expired. Please try again" + end + + private + + def password_params + params.require(:user).permit(:password, :password_confirmation) + end + + +end diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb new file mode 100644 index 000000000..8d2b5cef7 --- /dev/null +++ b/app/controllers/payments_controller.rb @@ -0,0 +1,50 @@ +class PaymentsController < ApplicationController + before_action :set_product, only: %i[ create ] + before_action :get_user_info_from_session + + # GET /products + def index + @products = Product.all + end + + def create + if !logged_in? || !@user + redirect_to log_in_path + else + @session = Stripe::Checkout::Session.create({ + customer: @user.stripe_customer_id, + line_items: [{ + # Provide the exact Price ID (e.g. pr_1234) of the product you want to sell + price: @product.stripe_price_id + }], + mode: 'subscription', + success_url: success_url + "?session_id={CHECKOUT_SESSION_ID}&customer_email={CUSTOMER_EMAIL}", + cancel_url: payments_url, + }) + + redirect_to @session.url, status: 303, allow_other_host: true + end + end + + def success + if params[:session_id] + # Display message on successfull subscription + session_expanded = Stripe::Checkout::Session.retrieve({ + id: params[:session_id], + expand: ["line_items", "subscription"]}) + + @user.update(subscription_id: session_expanded.subscription.id) + + session_expanded.line_items.data.each do |item| + @product = Product.find_by(stripe_product_id: item.price.product) + end + else + redirect_to root_url + end + end + + private + def set_product + @product = Product.find(params[:id]) + end +end diff --git a/app/controllers/stations_controller.rb b/app/controllers/stations_controller.rb index 88090af16..f12a39aff 100644 --- a/app/controllers/stations_controller.rb +++ b/app/controllers/stations_controller.rb @@ -1,7 +1,58 @@ class StationsController < ApplicationController def index + @user = get_user_info_from_session + @uCoords = request.location.coordinates + unless @user&.current_trip_id.nil? + @trip = Trip.find(@user.current_trip_id) + end + Station.find_by(name: "State St/Mass Central Rail Trail").update(address: "298 State St Northampton MA") + @stations = Station.all.order(identifier: :asc) + + @lats = Array.new # array of latitudes + @longs = Array.new # array of longitudes + @names = Array.new # station names + @identifiers = Array.new # station identifierss + @ids = Array.new # station ids + @addresses = Array.new # REYFUCKS UP + @bikes = Array.new # REYFUCKS UP + + @userid = @user&.id.present? || false #does the user id exist? + @tripid = @user&.current_trip_id.present? || false #does trip id exist? + @iduser = @user&.id #user id + @idtrip = @user&.current_trip_id #trip id + # sorry for the terrible naming + + @stations.each do |s| + if s.latitude && s.longitude then # block when geocode fails + @lats.append(s.latitude) + @longs.append(s.longitude) + @names.append(s.name) + @identifiers.append(s.identifier) + @ids.append(s.id) + @addresses.append(s.address) #REYFUCKS UP + @bikes.append(Bike.where(current_station_id: s.id).length) #REYFUCKS UP + end + end + end + + def show + @user = get_user_info_from_session + @station = Station.find(params[:id]) + if @user + unless @user.current_trip_id.nil? + @trip = Trip.find(@user.current_trip_id) + end + end + end + + def _row + @user = get_user_info_from_session + if @user + unless @user.current_trip_id.nil? + @trip = Trip.find(@user.current_trip_id) + end + end + end end - -end diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb new file mode 100644 index 000000000..10416cfe6 --- /dev/null +++ b/app/controllers/trips_controller.rb @@ -0,0 +1,90 @@ +class TripsController < ApplicationController + def new + @trip = Trip.new + @station = Station.find(params[:station_id]) + @user = User.find(params[:user_id]) + @bike = Bike.where(current_station_id: params[:station_id]).first + end + + def index + @user = get_user_info_from_session + @trips = Trip.all.order(end_time: :desc) + @past_trips = @trips.where.not(end_time: nil) + @curr_trip = @trips.find_by(end_time: nil) + if @curr_trip + @curr_address = Station.find(@curr_trip.start_station_is).address + end + end + + def create + @trip = Trip.new(trip_params) + if @trip.save + @bike = Bike.find(@trip.bike_id) + @station = Station.find(@trip.start_station_is) + @user = User.find(@trip.user_id) + @user.update(current_trip_id: @trip.id) + @bike.update(is_available: false, current_station_id: nil) + redirect_to confirm_path(trip_id: @trip.id) + else + render :new + end + end + def show + @trip = Trip.find(params[:trip_id]) + end + + def confirmation + @trip = Trip.find(params[:trip_id]) + end + + def end_confirmation + @user = User.find(params[:user_id]) + if !!@user.subscription_id + @trip = Trip.find(params[:id]) + user_stripe_id = @user.stripe_customer_id + # Search for the user's available subscription + subscription = Stripe::Subscription.retrieve(@user.subscription_id) + + # Get the meter event associated with the subscription + subscription.items.data.each do |item| + @meter_id = item.plan.meter + end + + # Get the product by meter_id + product = Product.find_by(meter_id: @meter_id) + + # Create the meter + @meter = Stripe::Billing::MeterEvent.create({ + event_name: product.meter_event, + payload: { + value: @trip.time_minutes.to_s, # this should be number of minutes + stripe_customer_id: user_stripe_id + } + }) + @bike = Bike.find(@trip.bike_id) + @user.update(current_trip_id: nil) + @bike.update(is_available: true, current_station_id: params[:end_station_id]) + @trip.update(end_time: DateTime.now, end_station_id: params[:end_station_id]) + else + # No subscription + # Make the trip status *Payment pending* + redirect_to payments_url + end + end + + def update + end + + def edit + end + + def trip_params + params.require(:trip).permit(:id, :bike_id, :user_id, :start_station_is, :start_time) + end + + private + def trip_time(trip) + trip + end + +end diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb new file mode 100644 index 000000000..2bb9baaab --- /dev/null +++ b/app/controllers/user_controller.rb @@ -0,0 +1,30 @@ +class UserController < ApplicationController + before_action :disable_navigation + + def new + # Display the sign up form + @user = User.new + end + + def create + # Process sign up information + @user = User.new(user_params) + if @user.save + # redirect_to log_in_path # make them log in + + session[:user_id] = @user[:id] # don't make them log in :) + redirect_to account_index_path + else + render :new, status: 422 + end + end + + private + + def user_params + # Return params that include only the given fields + # and set the permitted attribute for the object to true. + # (useful for limiting which attributes should be allowed for updating) + params.require(:user).permit(:email, :password, :password_confirmation) + end +end diff --git a/app/helpers/access_helper.rb b/app/helpers/access_helper.rb new file mode 100644 index 000000000..342c6ff9f --- /dev/null +++ b/app/helpers/access_helper.rb @@ -0,0 +1,2 @@ +module AccessHelper +end diff --git a/app/helpers/account_helper.rb b/app/helpers/account_helper.rb new file mode 100644 index 000000000..6d3694a49 --- /dev/null +++ b/app/helpers/account_helper.rb @@ -0,0 +1,2 @@ +module AccountHelper +end diff --git a/app/helpers/bikes_helper.rb b/app/helpers/bikes_helper.rb new file mode 100644 index 000000000..a2645e4e1 --- /dev/null +++ b/app/helpers/bikes_helper.rb @@ -0,0 +1,2 @@ +module BikesHelper +end diff --git a/app/helpers/main_helper.rb b/app/helpers/main_helper.rb new file mode 100644 index 000000000..826effed9 --- /dev/null +++ b/app/helpers/main_helper.rb @@ -0,0 +1,2 @@ +module MainHelper +end diff --git a/app/helpers/password_resets_helper.rb b/app/helpers/password_resets_helper.rb new file mode 100644 index 000000000..0c9d96ecf --- /dev/null +++ b/app/helpers/password_resets_helper.rb @@ -0,0 +1,2 @@ +module PasswordResetsHelper +end diff --git a/app/helpers/payments_helper.rb b/app/helpers/payments_helper.rb new file mode 100644 index 000000000..c1b884f80 --- /dev/null +++ b/app/helpers/payments_helper.rb @@ -0,0 +1,2 @@ +module PaymentsHelper +end diff --git a/app/helpers/trips_helper.rb b/app/helpers/trips_helper.rb new file mode 100644 index 000000000..04f333d46 --- /dev/null +++ b/app/helpers/trips_helper.rb @@ -0,0 +1,2 @@ +module TripsHelper +end diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb new file mode 100644 index 000000000..0147c3fe6 --- /dev/null +++ b/app/helpers/user_helper.rb @@ -0,0 +1,2 @@ +module UserHelper +end diff --git a/app/javascript/application.js b/app/javascript/application.js index 0d7b49404..05e26fa16 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,3 +1,10 @@ +//= require bootstrap-sprockets // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails import "@hotwired/turbo-rails" import "controllers" + +import "bootstrap" + +import "leaflet" +import "leaflet-providers" +import "geocoder" \ No newline at end of file diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js new file mode 100644 index 000000000..30826086c --- /dev/null +++ b/app/javascript/controllers/map_controller.js @@ -0,0 +1,161 @@ +import { Controller } from "@hotwired/stimulus" +import L from 'leaflet' + +export default class extends Controller { + static targets = [ "container" ]; + static values = { + lat: Array, + long: Array, + name: Array, + identifier: Array, + id: Array, + address: Array, + bike: Array, + userid: Boolean, + tripid: Boolean, + iduser: String, + idtrip: String, + userCoords: {type: Array, default: ['42.3172951', '-72.6386734'] }, + pfp: {type: String, default: '/images/fallback/default.png'} + }; + + + connect(){ + console.log("Controller Connected!!"); + + this.containerTarget.style.height = "400px"; // pulled this out here ig? should likely be in a css script + + this.createMap(); + + let purpleIcon = '/images/map-marker.png'; + + // Add the markers for each station + for (let i = 0; i < this.idValue.length; i++) { + console.log("loading item " + i); + let lat = this.latValue[i]; + let long = this.longValue[i]; + let name = this.nameValue[i]; + let identifier = this.identifierValue[i]; + let id = this.idValue[i]; + let address = this.addressValue[i]; + let bike = this.bikeValue[i]; + if (lat !== null && long !== null && name !== null) { + this.addMarker(lat, long, purpleIcon, name, identifier, id, address, bike); + } + } + } + + /* initializes the map for future use */ + createMap() { + // Create Leaflet map on map element. + this.map = L.map(this.containerTarget); + L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(this.map); + + // Add a you-are-here + let uCoords = this.userCoordsValue; // user lat long + let pfpath = this.pfpValue; // ProFile pic PATH + + this.addMarker(uCoords[0], uCoords[1], pfpath, null, null, null, null, null); + + console.log("Entering from [" + uCoords[0] + ", " + uCoords[1] + "]"); + console.log("User profile from `" + pfpath + "`"); + + // Set map's center. + const center = L.latLng(uCoords[0], uCoords[1]); + + /*const northamptonLat = '42.3255'; // what it says on the tag + const northamptonLng = '-72.646'; // rough center of northampton + const center = L.latLng(northamptonLat, northamptonLng); + */ + this.map.setView(center, 13.5); // consider changing zoom + console.log("Map Loaded!!"); + } + + /* adds a marker to the leaflet */ + addMarker(lat, long, iconPath, name, identifier, id, address, bike) { + + let size = id === null ? [30, 30] : [30, 40]; // size of the icon + + let icon = L.icon({ + iconUrl: iconPath, + iconSize: size + }); + + // Place a marker on the location with custom icon + let marker = L.marker([lat, long], {icon: icon}).addTo(this.map); + if (id !== null) { // here markers + marker.bindPopup(this.popupContent(name, identifier, id, address, bike)); + } + } + +/* helper method to get the button_to link to place in the popup */ + +/* popupContent(name, id, address, bike) { + return ` +
+
Station ${id}: ${name}

+
${address}
+
${bike} available bikes
+
+ +
+
+ `; + }*/ + popupContent(name, identifier, id, address, bike) { + const availableBikes = bike; + const user = this.useridValue; + const trip = this.tripidValue; + const user_id = this.iduserValue; + let buttonContent = ''; + + if (!user) { + buttonContent = ` + + `; + + } else if (availableBikes == 0 && !trip) { + buttonContent = ` + + `; + } else if (!trip) { + buttonContent = ` + + + + Reserve Bike + + + `; + } else if (trip) { + buttonContent = ` + + Return Bike Here + + `; + } + return ` +
+
Station ${identifier}: ${name}

+
${address}
+
${availableBikes} available bikes
+ ${buttonContent} +
+ + `; + } + +} \ No newline at end of file diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 3c34c8148..0626a3401 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,4 @@ class ApplicationMailer < ActionMailer::Base - default from: "from@example.com" + default from: "#{Rails.application.credentials.dig(:smtp, :user_name)}@gmail.com" layout "mailer" end diff --git a/app/mailers/password_mailer.rb b/app/mailers/password_mailer.rb new file mode 100644 index 000000000..f3dd6296e --- /dev/null +++ b/app/mailers/password_mailer.rb @@ -0,0 +1,15 @@ +class PasswordMailer < ApplicationMailer + default from: "#{Rails.application.credentials.dig(:smtp, :user_name)}@gmail.com" + # Subject can be set in your I18n file at config/locales/en.yml + # with the following lookup: + # + # en.password_mailer.reset.subject + # + def reset + @user = params[:user] + + @token = @user.signed_id(expires_in: 15.minutes, purpose: 'password_reset') + + mail to: @user.email + end +end diff --git a/app/models/bike.rb b/app/models/bike.rb index f3648e343..eeb6d33a7 100644 --- a/app/models/bike.rb +++ b/app/models/bike.rb @@ -4,4 +4,13 @@ class Bike < ApplicationRecord belongs_to :current_station, class_name: :Station, foreign_key: :current_station_id, optional: true + + def available? + is_available + end + + def get_station + Station.find(current_station_id) + end + end diff --git a/app/models/product.rb b/app/models/product.rb new file mode 100644 index 000000000..35a85acab --- /dev/null +++ b/app/models/product.rb @@ -0,0 +1,2 @@ +class Product < ApplicationRecord +end diff --git a/app/models/station.rb b/app/models/station.rb index 64b9eaada..1a9430776 100644 --- a/app/models/station.rb +++ b/app/models/station.rb @@ -1,9 +1,14 @@ -class Station < ApplicationRecord +class Station < ApplicationRecord validates_presence_of :identifier, :name, :address validates_uniqueness_of :identifier has_many :docked_bikes, class_name: :Bike, foreign_key: :current_station_id - + + attribute :latitude, type: Float + attribute :longitude, type: Float + + geocoded_by :address + after_validation :geocode end diff --git a/app/models/trip.rb b/app/models/trip.rb new file mode 100644 index 000000000..2d8f8733a --- /dev/null +++ b/app/models/trip.rb @@ -0,0 +1,26 @@ +class Trip < ApplicationRecord + belongs_to :user + + def time + hour = 3600 + minute = 60 + if end_time.present? && start_time.present? + diff = end_time - start_time + hours = (diff/3600).floor + minutes = ((diff - hours*hour) / minute).floor + seconds = (diff - hours*hour - minute*minutes).round + vars = [hours, minutes, seconds] + else + 0 + end + end + + def time_minutes + hr_min_sec = self.time + hr = hr_min_sec[0] + min = hr_min_sec[1] + sec = hr_min_sec[2] + record = 60*hr + min + (sec/60).round + end +end + diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 000000000..97bfced3e --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,39 @@ +class User < ApplicationRecord + has_secure_password + mount_uploader :avatar, AvatarUploader + + EMAIL_REGEX = /\A[\w\-\.]+@([\w\-]+\.)+[\w\-]{2,4}\z/ + PHONE_REGEX = /\A(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}\z/ + validates :email, presence: true, + uniqueness: {case_sensitive: false}, + format: {with: EMAIL_REGEX} + + validates :name, presence: false, + length: {minimum: 1}, + allow_nil: true, + allow_blank: true + + validates :phone_number, presence: false, + format: {with: PHONE_REGEX}, + allow_nil: true, + allow_blank: true + + has_many :trips, dependent: :destroy + + def has_trip? + unless current_trip_id.nil? + current_trip_id + end + end + + after_create do + customer = Stripe::Customer.create(email: email) + update(stripe_customer_id: customer.id) + end + + def has_name? + unless name.nil? + name + end + end +end diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb new file mode 100644 index 000000000..d0384251e --- /dev/null +++ b/app/uploaders/avatar_uploader.rb @@ -0,0 +1,46 @@ +class AvatarUploader < CarrierWave::Uploader::Base + # Include RMagick or MiniMagick support: + include CarrierWave::RMagick + # include CarrierWave::MiniMagick + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + def default_url(*args) + # For Rails 3.1+ asset pipeline compatibility: + ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_')) + "/images/fallback/" + [version_name, "default.png"].compact.join('_') + end + + # Process files as they are uploaded: + # process scale: [200, 300] + # + def scale(width, height) + process scale: [120, 120] + end + + # Create different versions of your uploaded files: + version :thumb do + process resize_to_fill: [20, 20] + end + + # Add an allowlist of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_allowlist + %w(jpg jpeg gif png) + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" + # end +end diff --git a/app/views/access/login.html.erb b/app/views/access/login.html.erb new file mode 100644 index 000000000..44603d8ec --- /dev/null +++ b/app/views/access/login.html.erb @@ -0,0 +1,37 @@ +
+
+
+
+ +
+ Log into your account +
+
+ <% if flash[:notice].present? %> +
+

<%= flash[:notice] %>

+
+ <% end %> + <%= form_with model: @user, url: log_in_path do |f| %> +
+ <%= f.label :email, "Email" %> + <%= f.text_field :email, placeholder: "Enter your email", class: "form-control" %> +
+
+ <%= f.label :password, "Password" %> + <%= f.password_field :password, placeholder: "Enter your password", class: "form-control" %> +
+ <%= f.submit "Log in", class: "mt-3 custom-btn btn-fill" %> + <% end %> + <%= button_to('Cancel', root_path, class: "mt-3 custom-btn btn-outline", method: :get) %> +
+ +
+
+
+
\ No newline at end of file diff --git a/app/views/account/index.html.erb b/app/views/account/index.html.erb new file mode 100644 index 000000000..54d6d927b --- /dev/null +++ b/app/views/account/index.html.erb @@ -0,0 +1,231 @@ +
+
+
+ My account +
+ + <% if @user %> +
+ <% if flash[:notice] %> +
+

<%= flash[:notice] %>

+
+ <% end %> +
+
+
+
+
+ <%= button_to 'Edit', + profile_picture_edit_path(@user[:id]), + class: "btn-underline regular", + method: :get %> +
+
+ <%= image_tag(@user.avatar? ? @user.avatar_url : 'fallback/default.png') %> +
+ <% if !!@enable_profile_upload %> + <%= form_with url: {action: :upload_profile_img} do |f| %> + <%= f.file_field :avatar, class: "form-control mt-2 mb-2" %> + <%= f.hidden_field :avatar_cache %> + <%= f.submit "Upload", class: "mt-3 custom-btn btn-fill"%> + <%= button_tag 'Remove profile picture', + type: 'submit', + name: 'cancel_edit_pfp', + class: "ms-3 custom-btn btn-fill-danger" %> + <% end %> + <% end %> +
+
+
+
+
+
+
+
+ Contact Information +
+
+
+ <%= button_to 'Edit', + edit_account_path(@user[:id]), + class: "btn-underline regular float-right", + method: :get %> +
+
+ <%= form_with model: @user, url: account_path(@user[:id]) do |f| %> + <% if f.object && f.object.errors.any? %> +
+
    + <% f.object.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%= f.label :name, "Full name" %> + <%= f.text_field :name, placeholder: @user[:name]? @user[:name] : '-' , class: "form-control", :readonly => !@enable_edit %> +
+
+ <%= f.label :email, "Email" %> + <%= f.text_field :email, placeholder: @user[:email], class: "form-control", :disabled => true %> +
+
+ <%= f.label :phone_number, "Phone number" %> + <%= f.text_field :phone_number, placeholder: @user[:phone_number]? @user[:phone_number] : '-', class: "form-control", :readonly => !@enable_edit %> +
+ <% if !!@enable_edit %> + <%= f.submit "Reset", class: "mt-3 custom-btn btn-fill" %> + <%= button_tag t('buttons.cancel'), + type: 'submit', + name: 'cancel_edit', + disabled: !@enable_edit, + class: "ms-3 custom-btn btn-outline" %> + <% end %> + <% end %>
+
+
+ Account +
+
+
+ + + + + + +
+
+
+
+ Subscription information +
+ <% if @product %> + <% if @upcoming_invoice %> +

Upcoming Invoice

+
+ + + + + + + + + + + + + + + +
Total amount$<%= @invoice_total.to_f/100 %>
Due on<%= @created_time %>
Payment status<%= @attempted %>
+
+ <% end %> +

Plan details

+
+ + + + + + + + + + + + + + + + + + + +
Plan<%= @product.name %> plan
Description<%= @product.desc %>
Start date<%= @start_date %>
Status<%= @status %>
+
+ + + <% else %> +

You have no subscription available.

+

Please go to <%= link_to('Pricing', {controller: 'payments' , action: 'index'}) %> to select a plan.

+ <% end %> +
+
+ <% else %> +
+ No account found. +
+ <% end %> + +
+
diff --git a/app/views/bikes/_row.html.erb b/app/views/bikes/_row.html.erb new file mode 100644 index 000000000..430939cb8 --- /dev/null +++ b/app/views/bikes/_row.html.erb @@ -0,0 +1,32 @@ +
+
+
+
+
+
+
+ Bike <%= bike.identifier %> +
+
+
+ <% user = get_user_info_from_session %> + <% station = bike.get_station %> + <% if !logged_in? || !user %> + <%= button_to('Reserve Bike', {}, class: "custom-btn btn-invalid", method: :get) %> + Please log in to reserve bike + <% elsif user.has_trip?%> + <%= button_to('Reserve Bike', {}, class: "custom-btn btn-invalid", method: :get) %> + Please return your current bike to reserve a new one + <% elsif !bike.available? %> + <%= button_to('Reserve Bike', {}, class: "custom-btn btn-invalid", method: :get) %> + Bike is currently Unavailable + <% else %> + <%= link_to 'Reserve Bike', new_trip_path(bike_id: bike.id, station_id: station.id, user_id: user.id), + class: 'custom-btn btn-outline', method: :get, :style=>' color: unset; text-decoration: unset;'%> + <% end %> +
+
+
+
+
+
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 7ace1393e..b10294836 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -7,16 +7,29 @@ <%= csp_meta_tag %> <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + <%= stylesheet_link_tag "https://unpkg.com/leaflet@1.7.1/dist/leaflet.css", + integrity: "sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==", + crossorigin: ""%> + <%= stylesheet_link_tag "https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.css" %> <%= javascript_importmap_tags %> - - + + + + <%= favicon_link_tag asset_path('favicon.ico') %> + <%= yield :head %> ValetBike
- <%= yield %> + <%= render "shared/header" unless @disable_header %> + <%= content_for(:content) ? yield(:content) : yield %> + <%= render "shared/footer" unless @disable_footer %>
+ + diff --git a/app/views/main/contact.html.erb b/app/views/main/contact.html.erb new file mode 100644 index 000000000..86fcbbbaa --- /dev/null +++ b/app/views/main/contact.html.erb @@ -0,0 +1,18 @@ +
+
+
+ Contact us +
+
+
+
+
+ We are ValetBike! We are NOT a group of gay kids taking CSC223 and making a website at all nooooo... + You can reachout to our contact: divadevs@valetbike.com with any questions or concerns. +
+ <%= image_tag('ghoulia.jpg') %> +
+
+
+
+
\ No newline at end of file diff --git a/app/views/main/index.html.erb b/app/views/main/index.html.erb new file mode 100644 index 000000000..fe65f925e --- /dev/null +++ b/app/views/main/index.html.erb @@ -0,0 +1,70 @@ +<% content_for :head do %> +<% end %> + +<% content_for :content do %> +
+
+
+ Welcome to
ValetBike
+
+
+
+
+
+
+
+ Discover your city on two wheels! Whether you're commuting, exploring, or just enjoying the ride, + we’ve got the perfect bike for you. Easy booking, flexible rental options, and a commitment + to sustainability—all at your fingertips. + +
+ +
+
+
+ +
+
+
+ <%= image_tag('locations.svg', alt: 'Find a nearby station', class: 'svg-icon') %> +

Find a nearby station

+
+
+
+
+ <%= image_tag('bike.svg', alt: 'Select an available bike', class: 'svg-icon') %> +

Select an available bike

+
+
+
+
+ <%= image_tag('cycling.svg', alt: 'Book a trip and enjoy the ride', class: 'svg-icon') %> +

Book a trip and enjoy the ride

+
+
+
+
+ <%= image_tag('pin.svg', alt: 'Park at a service area', class: 'svg-icon') %> +

Park at a service area

+
+
+
+
+
+<% end %> \ No newline at end of file diff --git a/app/views/password_mailer/reset.html.erb b/app/views/password_mailer/reset.html.erb new file mode 100644 index 000000000..523bb6aa2 --- /dev/null +++ b/app/views/password_mailer/reset.html.erb @@ -0,0 +1,11 @@ +
+ <%= image_tag('logo.png', alt: 'valetbike-logo') %> + +
+

Hi <%= @user.email %>,

+ +

If you requested a password reset, please click on the link below to reset your password. The link will expire in 15 minutes.

+ + <%= link_to "Reset Password", password_reset_edit_url(token: @token) %> +
+
\ No newline at end of file diff --git a/app/views/password_mailer/reset.text.erb b/app/views/password_mailer/reset.text.erb new file mode 100644 index 000000000..bc082f009 --- /dev/null +++ b/app/views/password_mailer/reset.text.erb @@ -0,0 +1,5 @@ +Hi <%= @user.email %>, + +If you requested a password reset, please click on the link below to reset your password. The link will expire in 15 minutes. + +<%= password_reset_edit_url(token: @token) %> \ No newline at end of file diff --git a/app/views/password_resets/edit.html.erb b/app/views/password_resets/edit.html.erb new file mode 100644 index 000000000..0a93da900 --- /dev/null +++ b/app/views/password_resets/edit.html.erb @@ -0,0 +1,37 @@ +
+
+
+
+ +
+ Reset your password +
+
+ <% if flash[:notice].present? %> +
+

<%= flash[:notice] %>

+
+ <% end %> + <%= form_with model: @user, url: password_reset_edit_path(token: params[:token]) do |f| %> +
+ <%= f.label :password, "Password" %> + <%= f.password_field :password, placeholder: "Enter your password", class: "form-control" %> +
+
+ <%= f.label :password_confirmation, "Confirm your password" %> + <%= f.password_field :password_confirmation, placeholder: "Enter your password again", class: "form-control" %> +
+ <%= f.submit "Reset", class: "mt-3 custom-btn btn-fill" %> + <% end %> + <%= button_to('Cancel', root_path, class: "mt-3 custom-btn btn-outline", method: :get) %> +
+ +
+
+
+
\ No newline at end of file diff --git a/app/views/password_resets/new.html.erb b/app/views/password_resets/new.html.erb new file mode 100644 index 000000000..4d01b0827 --- /dev/null +++ b/app/views/password_resets/new.html.erb @@ -0,0 +1,34 @@ +
+
+
+
+ +
+ Reset password +
+
+ <% if flash[:notice].present? %> +
+

<%= flash[:notice] %>

+
+ <% end %> + + <%= form_with url: password_reset_path do |f| %> +
+ <%= f.label :email, "Email" %> + <%= f.text_field :email, placeholder: "Enter your email", class: "form-control" %> +
+ <%= f.submit "Reset password", class: "mt-3 custom-btn btn-fill" %> + <% end %> + <%= button_to('Cancel', root_path, class: "mt-3 custom-btn btn-outline", method: :get) %> +
+ +
+
+
+
\ No newline at end of file diff --git a/app/views/payments/_product.html.erb b/app/views/payments/_product.html.erb new file mode 100644 index 000000000..cf717c868 --- /dev/null +++ b/app/views/payments/_product.html.erb @@ -0,0 +1,13 @@ +
+
+

<%= product.name %>

+
+ <%= button_to("Choose plan", {controller: 'payments', action: 'create'}, + params: {id: product.id}, + remote: true, + data: { turbo: false }, + class: "custom-btn btn-fill") %> +
+

<%= product.desc %>

+
+
\ No newline at end of file diff --git a/app/views/payments/index.html.erb b/app/views/payments/index.html.erb new file mode 100644 index 000000000..c0da6c8d4 --- /dev/null +++ b/app/views/payments/index.html.erb @@ -0,0 +1,22 @@ +
+
+
+ Pricing +
+
+ We at ValetBike offer many different options that range various levels. +
+
+ <% @products.each do |product| %> + <%= render "payments/product", product: product %> + <% end %> +
+
+
+
+ <%= image_tag('MHmoney.jpg', class: "section-banner") %> +
+
+
+
+
\ No newline at end of file diff --git a/app/views/payments/success.html.erb b/app/views/payments/success.html.erb new file mode 100644 index 000000000..b26af82fc --- /dev/null +++ b/app/views/payments/success.html.erb @@ -0,0 +1,40 @@ +
+
+
+ Thank you for subscribing +
+ <% if @product %> +
+ We received your payment for the subscription to <%= @product.name %> plan. + We hope you will enjoy our service. +
+
+
+ + + + + + + + + + + + + + + +
Plan<%= @product.name %> plan
Monthly price$<%= @product.price.to_f/100 %>
Description<%= @product.desc %>
+
+
+
+
+
+ <%= image_tag('MHPaymentSuccess.png', class: "section-banner") %> +
+
+
+ <% end %> +
+
\ No newline at end of file diff --git a/app/views/shared/_footer.html.erb b/app/views/shared/_footer.html.erb new file mode 100644 index 000000000..3639abdf8 --- /dev/null +++ b/app/views/shared/_footer.html.erb @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/app/views/shared/_header.html.erb b/app/views/shared/_header.html.erb new file mode 100644 index 000000000..0cc8f2842 --- /dev/null +++ b/app/views/shared/_header.html.erb @@ -0,0 +1,59 @@ + + + + + \ No newline at end of file diff --git a/app/views/stations/_leaflet.html.erb b/app/views/stations/_leaflet.html.erb new file mode 100644 index 000000000..52c325545 --- /dev/null +++ b/app/views/stations/_leaflet.html.erb @@ -0,0 +1,23 @@ +
+ data-map-pfp-value="<%= user.avatar_url %>" + <% end %> + <% if uCoords.length == 2 then %> + data-map-userCoords-value="<%= uCoords %>" + <% end %> +> + +
+
\ No newline at end of file diff --git a/app/views/stations/_list.html.erb b/app/views/stations/_list.html.erb new file mode 100644 index 000000000..94a8c12f8 --- /dev/null +++ b/app/views/stations/_list.html.erb @@ -0,0 +1,7 @@ + <% if stations.present? %> + <%= render(partial: "stations/row", collection: stations, as: :station) %> + <% else %> +
+ No stations found. +
+ <% end %> diff --git a/app/views/stations/_row.html.erb b/app/views/stations/_row.html.erb index 68a74d983..0760750a0 100644 --- a/app/views/stations/_row.html.erb +++ b/app/views/stations/_row.html.erb @@ -1,3 +1,63 @@ -
flexbox vertical stretch"> - <%= station.identifier %>: <%= station.name %> (<%= station.address %>) -
\ No newline at end of file +
+
+
+
+ Station <%= station.identifier %>: <%= station.name %> +
+ <%= station.address %>
+ <%= Bike.where(current_station_id: station.id).length %> available bike(s) +
+
+ <% bikes = Bike.where(current_station_id: station.id) %> + + <% if @user&.id.nil?%> + Reserve Bike + <% elsif bikes.count == 0 && station&.id && !@user.current_trip_id%> + Reserve Bike + + + <% elsif !@user&.id.nil? && !station&.id.nil? && !@user.current_trip_id.present? && bikes%> + <%= link_to 'Reserve Bike',new_trip_path(station_id: station.id, user_id: @user.id), + class: "mt-3 custom-btn btn-fill", method: :get, :style=>' color: unset; text-decoration: unset;'%> + + <% elsif station&.id && + @trip&.id && @user.current_trip_id%> + + + <% end %> +
+
+ + +
+ + diff --git a/app/views/stations/index.html.erb b/app/views/stations/index.html.erb index d211dda42..d2d0f57c2 100644 --- a/app/views/stations/index.html.erb +++ b/app/views/stations/index.html.erb @@ -1,16 +1,90 @@ -
-
-
- Welcome to ValetBike! -
-
- <% if @stations.present? %> - <%= render(partial: "stations/row", collection: @stations, as: :station) %> - <% else %> -
- No stations found. + +
+
+
+ Find a Station
- <% end %> +
+ + + + + + + +
+ +
+ + + +
+
+ <%= @stations.count %> stations found +
+
+ <%= render partial: "stations/leaflet", locals: { + lats: @lats, + longs: @longs, + names: @names, + identifiers: @identifiers, + ids: @ids, + addresses: @addresses, + bikes: @bikes, + userid: @userid, + tripid: @tripid, + iduser: @iduser, + idtrip: @idtrip, + user: @user, + uCoords: @uCoords + } %> +
+ + + +
-
-
+
+ + + + + + diff --git a/app/views/stations/show.html.erb b/app/views/stations/show.html.erb new file mode 100644 index 000000000..a4d19c45a --- /dev/null +++ b/app/views/stations/show.html.erb @@ -0,0 +1,55 @@ +
+
+
+ +
+
+ <%= @station.identifier %>: <%= @station.name %> +
+
+ <%= @station.address %> +
+
+ <% unless @user&.current_trip_id.nil? && @trip&.id.nil? %> + + + + <% end %> +

+ <%= Bike.where(current_station_id: @station.id).length %> available bike(s) +

+ <%= render(partial: "bikes/row", collection: Bike.where(current_station_id: @station.id), as: :bike )%> +
+
+
\ No newline at end of file diff --git a/app/views/trips/_form.html.erb b/app/views/trips/_form.html.erb new file mode 100644 index 000000000..c8c3b9f83 --- /dev/null +++ b/app/views/trips/_form.html.erb @@ -0,0 +1,23 @@ + +

New Trip

+ + + + + + + + + + + + + + + + + + + + +
Bike ID: <%= @bike.identifier %>
User ID:<%= @user.id %>
Station:<%= @station.identifier %>
Pickup Time<%= f.time_field(:start_time) %>
diff --git a/app/views/trips/_row.html.erb b/app/views/trips/_row.html.erb new file mode 100644 index 000000000..5319d413b --- /dev/null +++ b/app/views/trips/_row.html.erb @@ -0,0 +1,18 @@ +
+
+<% if trip&.end_station_id%> + Trip <%= trip.id %> +
+
+ Status: Ended
+ Trip Length: <%=trip.time[0]%>h <%=trip.time[1]%>m <%=trip.time[2]%>s
+ Start Location: <%= Station.find(trip.start_station_is).address %>
+ Start Time: <%= trip.start_time.in_time_zone('America/New_York'). + strftime("%m/%d/%Y %I:%M %p") %> +
+ End Location: <%=Station.find(trip.end_station_id).address %>
+ End Time: <%= trip.end_time.in_time_zone('America/New_York'). + strftime("%m/%d/%Y %I:%M %p") %> +
+
+ <% end %> diff --git a/app/views/trips/confirmation.html.erb b/app/views/trips/confirmation.html.erb new file mode 100644 index 000000000..44c26797a --- /dev/null +++ b/app/views/trips/confirmation.html.erb @@ -0,0 +1,18 @@ +
+
+
+ Thank you for booking +
+
+
+
+
+ Your trip (ID: <%= @trip.id %>) is confirmed. Navigate to <%= link_to 'your trips dashboard', trips_path %> to manage your trip. + We hope you will enjoy the ride. +
+ <%= image_tag('MHEndTrip.png', class: "section-banner") %> +
+
+
+
+
\ No newline at end of file diff --git a/app/views/trips/edit.html.erb b/app/views/trips/edit.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/trips/end_confirmation.html.erb b/app/views/trips/end_confirmation.html.erb new file mode 100644 index 000000000..0f96b2ba5 --- /dev/null +++ b/app/views/trips/end_confirmation.html.erb @@ -0,0 +1,58 @@ +
+
+
+ Your trip has ended successfully +
+
+
+
+
+ Details on your trip +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID<%= @trip.id %>
Duration<%= @trip.time[0]%>h <%= @trip.time[1]%>m <%= @trip.time[2]%>s
Start Location<%= Station.find(@trip.start_station_is).address %>
Start Time<%= @trip.start_time.in_time_zone('America/New_York'). + strftime("%m/%d/%Y %I:%M %p") %>
End Location<%=Station.find(@trip.end_station_id).address %>
End Time<%= @trip.end_time.in_time_zone('America/New_York'). + strftime("%m/%d/%Y %I:%M %p") %>
+
+
+ <%= link_to("Go to trip dashboard", trips_path, class: "custom-btn btn-fill", style: "color: unset; text-decoration: unset;") %> +
+
+
+
+
+
+ diff --git a/app/views/trips/index.html.erb b/app/views/trips/index.html.erb new file mode 100644 index 000000000..8324e3e1e --- /dev/null +++ b/app/views/trips/index.html.erb @@ -0,0 +1,46 @@ +
+
+
+ Manage your trips +
+
+ <% if @trips.present? && @user %> + <% if @curr_trip.present?%> +
+
+ Trip <%= @curr_trip.id %> +
+
+ Status: Ongoing +
+
+ Start Location: <%= @curr_address%>
+ Start Time: <%=@curr_trip. + start_time.in_time_zone('America/New_York'). + strftime("%m/%d/%Y %I:%M %p") %> +
+
+ <% end %> + <%if @past_trips.count > 0 %> + <%= render(partial: "trips/row", collection: @past_trips, as: :trip) %> + <%end%> + <% else %> +
+ <% if !@user %> + Please log in to see trips! + <% else %> + No trips found. Start your journey with us so you can be like her. + <% end %> +
+
+
+
+ <%= image_tag('MHBike.png', class: "section-banner") %> +
+
+
+ <% end %> +
+
+
+ diff --git a/app/views/trips/new.html.erb b/app/views/trips/new.html.erb new file mode 100644 index 000000000..97460d1c2 --- /dev/null +++ b/app/views/trips/new.html.erb @@ -0,0 +1,49 @@ +
+
+
+ +
+
+ Start a new trip +
+ +
+
+
+ <%= form_for(@trip) do |f| %> +
+ + + + + + + + + + + + + + + <%= f.hidden_field :bike_id, value: @bike.id %> + <%= f.hidden_field :user_id, value: @user.id %> + <%= f.hidden_field :start_station_is, value: @station.id %> + <%= f.hidden_field :start_time, value: DateTime.now %> + +
Bike ID<%= @bike.identifier %>
User<%= @user.email %>
Station<%= @station.identifier %>
+
+
+ <%= f.submit("Create Trip", class: "custom-btn btn-fill") %> +
+ <% end %> +
+
+
+
+
diff --git a/app/views/trips/show.html.erb b/app/views/trips/show.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/user/new.html.erb b/app/views/user/new.html.erb new file mode 100644 index 000000000..58e6c06ee --- /dev/null +++ b/app/views/user/new.html.erb @@ -0,0 +1,45 @@ +
+
+
+
+ +
+ Sign up an account +
+
+ <% if @user.errors.any? %> +
+
    + <% @user.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + + <%= form_with model: @user, url: sign_up_path do |f| %> +
+ <%= f.label :email, "Email" %> + <%= f.text_field :email, placeholder: "Enter your email", class: "form-control" %> +
+
+ <%= f.label :password, "Password" %> + <%= f.password_field :password, placeholder: "Enter your password", class: "form-control" %> +
+
+ <%= f.label :password_confirmation, "Confirm your password" %> + <%= f.password_field :password_confirmation, placeholder: "Enter your password again", class: "form-control" %> +
+ <%= f.submit "Create an account", class: "mt-3 custom-btn btn-fill" %> + <% end %> + <%= button_to('Cancel', root_path, class: "mt-3 custom-btn btn-outline", method: :get) %> +
+ +
+
+
+
\ No newline at end of file diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 615512281..9bac03c49 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -VB7sGK56yLf+jGFOVRuElgcgJgJFZsDwgMsbypn5ZOdcfKS8UubZu0TYIeYlOQgoWoFqBJQOrWRi7jJR1PlIo7SOLpXsll9HUNEOozEQ+uTAHE42YJfHOKKmYHlC3hKiHIUAsKrxBz9UgIWZTKGil0MEy2CDl1J0BOdEODmxWNsj5F/tYmwyjtq2LqWVmifJYgUEiKHvtbdcnerUXscJ3zH7/Nnnh8Y0MkWarRhIMBivxVFwsHFOj0KlApq3Y8e/Levy0wSPku6USLszoiaYkF6nj8EzIj8Nu0ns6nGaRvKZSpp5xiWTdXxgDwO5ho8B5ArQ5SYFX0LxpUT7bz7iAAmURqs7ZqI1Cj/TlAsWJlCtiNUoTJfVRsOdjB+7a3MNqrAXOAAsEBTlehBvAc/YrniTLgUJpzSUIpdN--93ih5nUF/iEYPW5R--XSFZIFrRWrriu0Ay9mgs1A== \ No newline at end of file +cosw7X0gETQGEnx+VSZUAfi5j4Q2Jg015Kf26/UeqvTd4h8l9JbXkLjN0WDgHN1s3Fl+ri+TrfOFZft3IcfCCrzPT8XMruuEWlQNjeiwOl/rpCQiOsJQ6GSupZG7pwsMdUsI9pENwxcTm2DCcg3x9OMzFaeliQpjk/DGmdmoFUM88aDKGK706yCNyCF5DsTBKJBD3QPWj6cNRDHDHOFgOuP5T7TF//ABfG/xmwe80rF4LapLpSxOIa7Y7C1UmEjsxedm77XerHny4xDeCKL3zUPgYebVR/+I5Fd9qyFSaTFINHcjtQIS+JuIRlLKV7/Wkx2vYH7LJU21kpOKofDwOpPL3ZiV14eafZ0dAK1G3qQx4agbemWXrqOIavUx4mYcCkaT7YAiEj30I96433oIuYcbXcgDtYJaXiICOarWf+mlt05UJpqMvq9DJUSgKfawZ+dVzqpMraBcRgxGn6Ba/hpheIYXomVEPRC2CWUHHeJt4VGM8+t4ZvzXgUJUcdOLBLxgvkfv11qcvB/5JQwB5TwzAKfiDlRT9zhDOj4mnw+PUqugvpQ1blHC3wPRFSawhjnnTlRHmgnjyO2QVAwibylxnWugSYuHkjHBFCejr/eDXny4xIu5rxXcr/BwPqnudcaBTELfU3n2PL+g8j1jtB4/I8eTw7Q2p+TtLxoNdBMaUWUyRMyTmC+5odJQ1rvNu7OEDzzOk6ywpzjnCo4Ebz00F99kgcilgXWsGd801w4njurZmoNcB+WS3Qc8hU8Qcyg231jbOYH6EfQJKDdhQys/2memKqHOHbx/GD/J0SXOykuVIgecYuzl3A==--oHssfh/6pEeyyl90--CxH9e/UjBLo9STtwAzfW2w== \ No newline at end of file diff --git a/config/environments/development.rb b/config/environments/development.rb index 8500f459a..b242f5c70 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -67,4 +67,6 @@ # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true + + config.action_mailer.default_url_options = {host: 'localhost:3000'} end diff --git a/config/environments/production.rb b/config/environments/production.rb index c816bd150..52e907150 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -90,4 +90,17 @@ # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false + + config.action_mailer.default_url_options = {host: 'https://valetbike-by-divas-2c66adac9bf4.herokuapp.com'} + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { + address: "smtp.gmail.com", + port: 587, + domain: "gmail.com", + user_name: Rails.application.credentials.dig(:smtp, :user_name), + password: Rails.application.credentials.dig(:smtp, :password), + authentication: "plain", + enable_starttls: true, + open_timeout: 5, + read_timeout: 5 } end diff --git a/config/importmap.rb b/config/importmap.rb index 8dce42d40..e3501bf6b 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -4,4 +4,8 @@ pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true +pin "bootstrap", to: 'bootstrap.min.js', preload: true pin_all_from "app/javascript/controllers", under: "controllers" +pin "leaflet" # @1.9.4 +pin "geocoder" # @0.2.3 +pin "leaflet-providers" # @2.0.0 diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 2eeef966f..6cd4397d0 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -9,4 +9,14 @@ # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) +Rails.application.config.assets.precompile += %w( form.css ) + +# uhh so rails told me to add all these inexplicably but it works now so... +Rails.application.config.assets.precompile += %w( tailwind.css ) +Rails.application.config.assets.precompile += %w( bootstrap.min.js ) +Rails.application.config.assets.precompile += %w( controllers/hello_controller.js ) +Rails.application.config.assets.precompile += %w( controllers/index.js ) +Rails.application.config.assets.precompile += %w( leaflet.js ) +Rails.application.config.assets.precompile += %w( leaflet-providers.js ) +Rails.application.config.assets.precompile += %w( geocoder.js ) +Rails.application.config.assets.precompile += %w( controllers/map_controller.js ) diff --git a/config/initializers/carrier_wave.rb b/config/initializers/carrier_wave.rb new file mode 100644 index 000000000..57807870d --- /dev/null +++ b/config/initializers/carrier_wave.rb @@ -0,0 +1 @@ +require 'carrierwave/orm/activerecord' \ No newline at end of file diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb new file mode 100644 index 000000000..7d269d703 --- /dev/null +++ b/config/initializers/geocoder.rb @@ -0,0 +1,27 @@ +Geocoder.configure( + # Geocoding options + # timeout: 3, # geocoding service timeout (secs) + lookup: :nominatim, # name of geocoding service (symbol) + ip_lookup: :ipinfo_io, # name of IP address geocoding service (symbol) + language: :en, # ISO-639 language code + # use_https: false, # use HTTPS for lookup requests? (if supported) + # http_proxy: nil, # HTTP proxy server (user:pass@host:port) + # https_proxy: nil, # HTTPS proxy server (user:pass@host:port) + # api_key: nil, # API key for geocoding service + # cache: nil, # cache object (must respond to #[], #[]=, and #del) + + # Exceptions that should not be rescued by default + # (if you want to implement custom error handling); + # supports SocketError and Timeout::Error + # always_raise: [], + + # Calculation options + units: :mi, # :km for kilometers or :mi for miles + distances: :linear # :spherical or :linear + + # Cache configuration + # cache_options: { + # expiration: 2.days, + # prefix: 'geocoder:' + # } +) diff --git a/config/initializers/stripe.rb b/config/initializers/stripe.rb new file mode 100644 index 000000000..1ba8ff0b8 --- /dev/null +++ b/config/initializers/stripe.rb @@ -0,0 +1 @@ +Stripe.api_key = Rails.application.credentials[:stripe][:secret] \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index f1eb0a0d8..51739ea93 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,40 @@ Rails.application.routes.draw do - root to: "stations#index" + get 'trips/index' + get 'stations', to: 'stations#index' + get 'stations/show' + get 'bikes/index' + get 'bikes/_row' + get 'main/contact' + + get "confirm", to: "trips#confirmation" + get "end_confirm", to: "trips#end_confirmation" + + # resources :bikes + # resources :stations + resources :stations, only: [:index, :show] do + resources :bikes, only: [:index, :show] + end + + resources :trips, except: [:destroy] + resources :payments, only: [:index, :create] + get "success", to: "payments#success" + + get "sign-up", to: "user#new" + post "sign-up", to: "user#create" + + get "log-in", to: "access#login" + post "log-in", to: "access#create" + post "log-out", to: "access#destroy" + + get "password/reset", to: "password_resets#new" + post "password/reset", to: "password_resets#create" + get "password/reset/edit", to: "password_resets#edit" + patch "password/reset/edit", to: "password_resets#update" + + resources :account, only: [:index, :destroy, :edit, :update] + get "profile-picture/edit", to: "account#edit_profile_img" + post "profile-picture/upload", to: "account#upload_profile_img" + post "subscription/cancel", to: "account#cancel_subscription" + + root to: "main#index" end \ No newline at end of file diff --git a/db/migrate/20241031151655_create_users.rb b/db/migrate/20241031151655_create_users.rb new file mode 100644 index 000000000..ca0284b32 --- /dev/null +++ b/db/migrate/20241031151655_create_users.rb @@ -0,0 +1,11 @@ +class CreateUsers < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + t.string :name + t.string :email, null: false + # t.string :password_digest + + t.timestamps + end + end +end diff --git a/db/migrate/20241031210449_add_password_digest_to_users.rb b/db/migrate/20241031210449_add_password_digest_to_users.rb new file mode 100644 index 000000000..5b478015b --- /dev/null +++ b/db/migrate/20241031210449_add_password_digest_to_users.rb @@ -0,0 +1,6 @@ +class AddPasswordDigestToUsers < ActiveRecord::Migration[7.0] + def change + add_column :users, :password_digest, :string + add_column :users, :string, :string + end +end diff --git a/db/migrate/20241101172048_add_phone_number_to_users.rb b/db/migrate/20241101172048_add_phone_number_to_users.rb new file mode 100644 index 000000000..85fbd3aa2 --- /dev/null +++ b/db/migrate/20241101172048_add_phone_number_to_users.rb @@ -0,0 +1,5 @@ +class AddPhoneNumberToUsers < ActiveRecord::Migration[7.0] + def change + add_column :users, :phone_number, :string + end +end diff --git a/db/migrate/20241110203104_add_availablility.rb b/db/migrate/20241110203104_add_availablility.rb new file mode 100644 index 000000000..3999bd2ea --- /dev/null +++ b/db/migrate/20241110203104_add_availablility.rb @@ -0,0 +1,5 @@ +class AddAvailablility < ActiveRecord::Migration[7.0] + def change + add_column :bikes, :is_available, :boolean, :default => true + end +end diff --git a/db/migrate/20241110210524_create_trips.rb b/db/migrate/20241110210524_create_trips.rb new file mode 100644 index 000000000..c63317ac2 --- /dev/null +++ b/db/migrate/20241110210524_create_trips.rb @@ -0,0 +1,13 @@ +class CreateTrips < ActiveRecord::Migration[7.0] + def change + create_table :trips do |t| + t.integer :bike_id # bike IDENTIFIER + t.integer :user_id + t.datetime :start_time + t.datetime :end_time + t.integer :end_station_id # station IDENTIFIER + + t.timestamps + end + end +end diff --git a/db/migrate/20241112212031_geocode_stations.rb b/db/migrate/20241112212031_geocode_stations.rb new file mode 100644 index 000000000..f5e8262ae --- /dev/null +++ b/db/migrate/20241112212031_geocode_stations.rb @@ -0,0 +1,6 @@ +class GeocodeStations < ActiveRecord::Migration[7.0] + def change + add_column :stations, :latitude, :double + add_column :stations, :longitude, :double + end +end diff --git a/db/migrate/20241114204842_add_start_station_to_trip.rb b/db/migrate/20241114204842_add_start_station_to_trip.rb new file mode 100644 index 000000000..931c4c15b --- /dev/null +++ b/db/migrate/20241114204842_add_start_station_to_trip.rb @@ -0,0 +1,5 @@ +class AddStartStationToTrip < ActiveRecord::Migration[7.0] + def change + add_column :trips, :start_station_is, :integer + end +end diff --git a/db/migrate/20241120010325_add_trip_to_users.rb b/db/migrate/20241120010325_add_trip_to_users.rb new file mode 100644 index 000000000..b940d9efc --- /dev/null +++ b/db/migrate/20241120010325_add_trip_to_users.rb @@ -0,0 +1,5 @@ +class AddTripToUsers < ActiveRecord::Migration[7.0] + def change + add_column :users, :current_trip_id, :integer + end +end diff --git a/db/migrate/20241124213250_add_avatar_to_users.rb b/db/migrate/20241124213250_add_avatar_to_users.rb new file mode 100644 index 000000000..a7dab8a2a --- /dev/null +++ b/db/migrate/20241124213250_add_avatar_to_users.rb @@ -0,0 +1,5 @@ +class AddAvatarToUsers < ActiveRecord::Migration[7.0] + def change + add_column :users, :avatar, :string + end +end diff --git a/db/migrate/20241205163556_create_products.rb b/db/migrate/20241205163556_create_products.rb new file mode 100644 index 000000000..937774e23 --- /dev/null +++ b/db/migrate/20241205163556_create_products.rb @@ -0,0 +1,13 @@ +class CreateProducts < ActiveRecord::Migration[7.0] + def change + create_table :products do |t| + t.string :name + t.integer :price + t.string :desc + t.string :stripe_price_id + t.string :stripe_product_id + + t.timestamps + end + end +end diff --git a/db/migrate/20241205213457_add_stripe_customer_id_to_users.rb b/db/migrate/20241205213457_add_stripe_customer_id_to_users.rb new file mode 100644 index 000000000..02211efb9 --- /dev/null +++ b/db/migrate/20241205213457_add_stripe_customer_id_to_users.rb @@ -0,0 +1,5 @@ +class AddStripeCustomerIdToUsers < ActiveRecord::Migration[7.0] + def change + add_column :users, :stripe_customer_id, :string + end +end diff --git a/db/migrate/20241207024838_add_meter_event_to_products.rb b/db/migrate/20241207024838_add_meter_event_to_products.rb new file mode 100644 index 000000000..1780c01df --- /dev/null +++ b/db/migrate/20241207024838_add_meter_event_to_products.rb @@ -0,0 +1,5 @@ +class AddMeterEventToProducts < ActiveRecord::Migration[7.0] + def change + add_column :products, :meter_event, :string + end +end diff --git a/db/migrate/20241207032336_add_subscription_id_to_users.rb b/db/migrate/20241207032336_add_subscription_id_to_users.rb new file mode 100644 index 000000000..bf8eae8d1 --- /dev/null +++ b/db/migrate/20241207032336_add_subscription_id_to_users.rb @@ -0,0 +1,5 @@ +class AddSubscriptionIdToUsers < ActiveRecord::Migration[7.0] + def change + add_column :users, :subscription_id, :string + end +end diff --git a/db/migrate/20241207035648_add_meter_id_to_products.rb b/db/migrate/20241207035648_add_meter_id_to_products.rb new file mode 100644 index 000000000..3d2897caf --- /dev/null +++ b/db/migrate/20241207035648_add_meter_id_to_products.rb @@ -0,0 +1,5 @@ +class AddMeterIdToProducts < ActiveRecord::Migration[7.0] + def change + add_column :products, :meter_id, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index b98268f0e..cecbf83cf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,12 +10,25 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_08_31_090719) do +ActiveRecord::Schema[7.0].define(version: 2024_12_07_035648) do create_table "bikes", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "identifier" t.integer "current_station_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "is_available", default: true + end + + create_table "products", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "name" + t.integer "price" + t.string "desc" + t.string "stripe_price_id" + t.string "stripe_product_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "meter_event" + t.string "meter_id" end create_table "stations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| @@ -24,6 +37,33 @@ t.string "address" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.float "latitude", limit: 53 + t.float "longitude", limit: 53 + end + + create_table "trips", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.integer "bike_id" + t.integer "user_id" + t.datetime "start_time" + t.datetime "end_time" + t.integer "end_station_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "start_station_is" + end + + create_table "users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "name" + t.string "email", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "password_digest" + t.string "string" + t.string "phone_number" + t.integer "current_trip_id" + t.string "avatar" + t.string "stripe_customer_id" + t.string "subscription_id" end end diff --git a/lib/tasks/load_data.rake b/lib/tasks/load_data.rake new file mode 100644 index 000000000..dd0e476f7 --- /dev/null +++ b/lib/tasks/load_data.rake @@ -0,0 +1,37 @@ +namespace :load_data do + require 'csv' + + desc "Reads the station data in from the CSV." + task :station => :environment do + CSV.foreach('notes/station-data.csv', :headers => true) do |row| #stations + station = Station.create(identifier: row[0], + name: row[1], + address: row[6]) + end + end + + desc "Reads the bike data in from the CSV." + task :bike => :environment do + + CSV.foreach('notes/bike-data.csv', :headers => true) do |row| #bikes + station = Station.find_by(identifier: row[1]) + bike = Bike.create(identifier: row[0], current_station: station) + bike.current_station_id = station != nil ? station.id : nil + bike.save + + end + end + + desc "Read the plan pricing data from the CSV." + task :product => :environment do + CSV.foreach('notes/plan-data.csv', :headers => true) do |row| #product + product = Product.create(name: row[0], price: row[1], desc: row[2], + stripe_price_id: row[3], + stripe_product_id: row[4], + meter_event: row[5], + meter_id: row[6]) + end + + end + +end diff --git a/lib/tasks/purge_data.rake b/lib/tasks/purge_data.rake new file mode 100644 index 000000000..db014fd05 --- /dev/null +++ b/lib/tasks/purge_data.rake @@ -0,0 +1,18 @@ +namespace :purge_data do + require 'csv' + + desc "Purges the bike and station data for reloading while preserving other db tables" + task :station_bike => :environment do + Station.all.each do |station| + station.destroy + end + end + + desc "Purges the product data for reload." + task :product => :environment do + Product.all.each do |station| + station.destroy + end + end + +end diff --git a/notes/plan-data.csv b/notes/plan-data.csv new file mode 100644 index 000000000..e519c0a85 --- /dev/null +++ b/notes/plan-data.csv @@ -0,0 +1,4 @@ +name,price,desc,stripe_price_id,stripe_product_id,meter_event,meter_id +On-the-go,25,Pay on-the-go with a flat rate of $0.25/minute.,price_1QT9JwK3fFRnZeLjMtnaWRiA,prod_RLr1mmHL8zkfdq,trip_meter,mtr_test_61RcXH9lpQ2XCOBK541K3fFRnZeLj3CS +Student,599,Free 30 minutes of ride time a day at the charge of $5.99 a month. Must have vaild student ID from a University or College you are currently attending.,price_1QT8y8K3fFRnZeLjc8hc65gB,prod_RLqfqTE0oRxuoE,student_meter,mtr_test_61RcXBb48BdoQPf2G41K3fFRnZeLjC24 +Regular,1599,Free 30 minutes of ride time a day at the charge of $15.99 a month.,price_1QT9EgK3fFRnZeLjrnXOQsFp,prod_RLqwcQUIna3quM,regular_meter,mtr_test_61RcXBtL3arm1QrYM41K3fFRnZeLj3Bw \ No newline at end of file diff --git a/notes/station-data.csv b/notes/station-data.csv index 6f147a8a8..c6004bc4c 100644 --- a/notes/station-data.csv +++ b/notes/station-data.csv @@ -1,15 +1,15 @@ identifier,name,has_kiosk,needs_maintenance,dock_count,docked_bike_count,address -21,Florence Bank Station,0,0,7,4,19 Meadow Street Florence MA @ Lilly Library +21,Florence Bank Station,0,0,7,4,Lilly Library 19 Meadow Street Florence MA 24,Florence Center,0,0,10,2,31 Main Street Florence MA 30,Jackson Street,0,0,16,8,7A-7D Jackson Street Northampton MA 25,Cooley Dickinson Health Care,0,1,14,6,51 Locust Street Northampton MA -29,State St/Mass Central Rail Trail,0,0,10,4,State Street/Mass Central Rail Trail +29,State St/Mass Central Rail Trail,0,0,10,4,42.328255 -72.635812 33,Northampton High School,0,0,15,8,380 Elm Street Northampton MA -20,Village Hill/State Hospital,0,0,14,6,Village Hill/State Hospital -26,John M Greene Hall/Smith College,0,0,16,5,1 Chapin Way Northampton MA @ John M. Greene Hall +20,Village Hill/State Hospital,0,0,14,6,Northampton State Hospital Memorial Park +26,John M Greene Hall/Smith College,0,0,16,5,60 Elm Street Northampton MA 23,Forbes Library,0,0,13,6,20 West Street Northampton MA -27,Pulaski Park/Downtown,1,0,11,3,274 Main Street Northampton MA at Pulaski Park +27,Pulaski Park/Downtown,1,0,11,3,Pulaski Park Northampton MA 31,Main Street/Court House,0,1,10,3,99 Main Street Northampton MA -28,Northampton Train Station,1,0,13,5,Northampton Train Station +28,Northampton Train Station,1,0,13,5,170 Pleasant St Northampton MA 32,Main Street/Bridge Street,0,0,15,8,1 Bridge Street Northampton MA -22,YMCA/Childs Park,0,0,17,8,YMCA/Childs Park \ No newline at end of file +22,YMCA/Childs Park,0,0,17,8,YMCA 286 Prospect Street Northampton MA \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico index e69de29bb..ffb2aed28 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/images/map-marker.png b/public/images/map-marker.png new file mode 100644 index 000000000..9f85490c0 Binary files /dev/null and b/public/images/map-marker.png differ diff --git a/public/uploads/user/avatar/1/Untitled_Artwork_6_copy_3.png b/public/uploads/user/avatar/1/Untitled_Artwork_6_copy_3.png new file mode 100644 index 000000000..b571c9443 Binary files /dev/null and b/public/uploads/user/avatar/1/Untitled_Artwork_6_copy_3.png differ diff --git a/public/uploads/user/avatar/1/thumb_Untitled_Artwork_6_copy_3.png b/public/uploads/user/avatar/1/thumb_Untitled_Artwork_6_copy_3.png new file mode 100644 index 000000000..fc94a61db Binary files /dev/null and b/public/uploads/user/avatar/1/thumb_Untitled_Artwork_6_copy_3.png differ diff --git a/public/uploads/user/avatar/1/thumb_walk.gif b/public/uploads/user/avatar/1/thumb_walk.gif new file mode 100644 index 000000000..409da6132 Binary files /dev/null and b/public/uploads/user/avatar/1/thumb_walk.gif differ diff --git "a/public/uploads/user/avatar/1/thumb_\343\202\263\343\203\263\343\202\277\343\202\257\343\203\210\343\203\254\343\203\263\343\202\272___\345\261\261\346\236\235___AWRD.jpeg" "b/public/uploads/user/avatar/1/thumb_\343\202\263\343\203\263\343\202\277\343\202\257\343\203\210\343\203\254\343\203\263\343\202\272___\345\261\261\346\236\235___AWRD.jpeg" new file mode 100644 index 000000000..6494e2c72 Binary files /dev/null and "b/public/uploads/user/avatar/1/thumb_\343\202\263\343\203\263\343\202\277\343\202\257\343\203\210\343\203\254\343\203\263\343\202\272___\345\261\261\346\236\235___AWRD.jpeg" differ diff --git a/public/uploads/user/avatar/1/walk.gif b/public/uploads/user/avatar/1/walk.gif new file mode 100644 index 000000000..38f466b4e Binary files /dev/null and b/public/uploads/user/avatar/1/walk.gif differ diff --git "a/public/uploads/user/avatar/1/\343\202\263\343\203\263\343\202\277\343\202\257\343\203\210\343\203\254\343\203\263\343\202\272___\345\261\261\346\236\235___AWRD.jpeg" "b/public/uploads/user/avatar/1/\343\202\263\343\203\263\343\202\277\343\202\257\343\203\210\343\203\254\343\203\263\343\202\272___\345\261\261\346\236\235___AWRD.jpeg" new file mode 100644 index 000000000..245fa427d Binary files /dev/null and "b/public/uploads/user/avatar/1/\343\202\263\343\203\263\343\202\277\343\202\257\343\203\210\343\203\254\343\203\263\343\202\272___\345\261\261\346\236\235___AWRD.jpeg" differ diff --git a/public/uploads/user/avatar/3/2024-11-05_10.04.03-1.jpg b/public/uploads/user/avatar/3/2024-11-05_10.04.03-1.jpg new file mode 100644 index 000000000..958f2bf89 Binary files /dev/null and b/public/uploads/user/avatar/3/2024-11-05_10.04.03-1.jpg differ diff --git a/public/uploads/user/avatar/3/thumb_2024-11-05_10.04.03-1.jpg b/public/uploads/user/avatar/3/thumb_2024-11-05_10.04.03-1.jpg new file mode 100644 index 000000000..0ecee9c20 Binary files /dev/null and b/public/uploads/user/avatar/3/thumb_2024-11-05_10.04.03-1.jpg differ diff --git a/test/controllers/access_controller_test.rb b/test/controllers/access_controller_test.rb new file mode 100644 index 000000000..13ef117b0 --- /dev/null +++ b/test/controllers/access_controller_test.rb @@ -0,0 +1,8 @@ +require "test_helper" + +class AccessControllerTest < ActionDispatch::IntegrationTest + test "should get create" do + get access_create_url + assert_response :success + end +end diff --git a/test/controllers/account_controller_test.rb b/test/controllers/account_controller_test.rb new file mode 100644 index 000000000..032b870bd --- /dev/null +++ b/test/controllers/account_controller_test.rb @@ -0,0 +1,8 @@ +require "test_helper" + +class AccountControllerTest < ActionDispatch::IntegrationTest + test "should get index" do + get account_index_url + assert_response :success + end +end diff --git a/test/controllers/bikes_controller_test.rb b/test/controllers/bikes_controller_test.rb new file mode 100644 index 000000000..4f9b2d3f3 --- /dev/null +++ b/test/controllers/bikes_controller_test.rb @@ -0,0 +1,13 @@ +require "test_helper" + +class BikesControllerTest < ActionDispatch::IntegrationTest + test "should get index" do + get bikes_index_url + assert_response :success + end + + test "should get _row" do + get bikes__row_url + assert_response :success + end +end diff --git a/test/controllers/main_controller_test.rb b/test/controllers/main_controller_test.rb new file mode 100644 index 000000000..759df146c --- /dev/null +++ b/test/controllers/main_controller_test.rb @@ -0,0 +1,8 @@ +require "test_helper" + +class MainControllerTest < ActionDispatch::IntegrationTest + test "should get index" do + get main_index_url + assert_response :success + end +end diff --git a/test/controllers/password_resets_controller_test.rb b/test/controllers/password_resets_controller_test.rb new file mode 100644 index 000000000..abcca3e88 --- /dev/null +++ b/test/controllers/password_resets_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PasswordResetsControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/payments_controller_test.rb b/test/controllers/payments_controller_test.rb new file mode 100644 index 000000000..c5e86fa35 --- /dev/null +++ b/test/controllers/payments_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PaymentsControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/trips_controller_test.rb b/test/controllers/trips_controller_test.rb new file mode 100644 index 000000000..c0efe9f75 --- /dev/null +++ b/test/controllers/trips_controller_test.rb @@ -0,0 +1,18 @@ +require "test_helper" + +class TripsControllerTest < ActionDispatch::IntegrationTest + test "should get index" do + get trips_index_url + assert_response :success + end + + test "should get new" do + get trips_new_url + assert_response :success + end + + test "should get edit" do + get trips_edit_url + assert_response :success + end +end diff --git a/test/controllers/user_controller_test.rb b/test/controllers/user_controller_test.rb new file mode 100644 index 000000000..f067a70e1 --- /dev/null +++ b/test/controllers/user_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class UserControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/fixtures/trips.yml b/test/fixtures/trips.yml new file mode 100644 index 000000000..45222f191 --- /dev/null +++ b/test/fixtures/trips.yml @@ -0,0 +1,15 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + bike_id: 1 + user_id: 1 + start_time: 2024-11-10 16:05:24 + end_time: 2024-11-10 16:05:24 + end_station_id: 1 + +two: + bike_id: 1 + user_id: 1 + start_time: 2024-11-10 16:05:24 + end_time: 2024-11-10 16:05:24 + end_station_id: 1 diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml new file mode 100644 index 000000000..cc9a815cd --- /dev/null +++ b/test/fixtures/users.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + email: MyString + password: MyString + +two: + name: MyString + email: MyString + password: MyString diff --git a/test/mailers/password_mailer_test.rb b/test/mailers/password_mailer_test.rb new file mode 100644 index 000000000..6f5cc7daf --- /dev/null +++ b/test/mailers/password_mailer_test.rb @@ -0,0 +1,12 @@ +require "test_helper" + +class PasswordMailerTest < ActionMailer::TestCase + test "reset" do + mail = PasswordMailer.reset + assert_equal "Reset", mail.subject + assert_equal ["to@example.org"], mail.to + assert_equal ["from@example.com"], mail.from + assert_match "Hi", mail.body.encoded + end + +end diff --git a/test/mailers/previews/password_mailer_preview.rb b/test/mailers/previews/password_mailer_preview.rb new file mode 100644 index 000000000..684b74290 --- /dev/null +++ b/test/mailers/previews/password_mailer_preview.rb @@ -0,0 +1,9 @@ +# Preview all emails at http://localhost:3000/rails/mailers/password_mailer +class PasswordMailerPreview < ActionMailer::Preview + + # Preview this email at http://localhost:3000/rails/mailers/password_mailer/reset + def reset + PasswordMailer.reset + end + +end diff --git a/test/models/trip_test.rb b/test/models/trip_test.rb new file mode 100644 index 000000000..c5ff46fcb --- /dev/null +++ b/test/models/trip_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class TripTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 000000000..5c07f4900 --- /dev/null +++ b/test/models/user_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/vendor/javascript/geocoder.js b/vendor/javascript/geocoder.js new file mode 100644 index 000000000..99f53d4bd --- /dev/null +++ b/vendor/javascript/geocoder.js @@ -0,0 +1,4 @@ +// geocoder@0.2.3 downloaded from https://ga.jspm.io/npm:geocoder@0.2.3/index.js + +var e="undefined"!==typeof globalThis?globalThis:"undefined"!==typeof self?self:global;var r={};var o="0.2.3";function Geocoder(){this.selectProvider("google")}Geocoder.prototype={selectProvider:function(r,o){if(!r)return cbk(new Error("Geocoder.selectProvider requires a name."));(this||e).provider=r;(this||e).providerOpts=o||{};(this||e).providerObj=require("./providers/"+r)},geocode:function(r,o,i){return r?(this||e).providerObj.geocode((this||e).providerOpts,r,o,i):o(new Error("Geocoder.geocode requires a location."))},reverseGeocode:function(r,o,i,d){return r&&o?(this||e).providerObj.reverseGeocode((this||e).providerOpts,r,o,i,d):i(new Error("Geocoder.reverseGeocode requires a latitude and longitude."))},version:o};r=new Geocoder;var i=r;export default i; + diff --git a/vendor/javascript/leaflet-providers.js b/vendor/javascript/leaflet-providers.js new file mode 100644 index 000000000..bb1329316 --- /dev/null +++ b/vendor/javascript/leaflet-providers.js @@ -0,0 +1,4 @@ +// leaflet-providers@2.0.0 downloaded from https://ga.jspm.io/npm:leaflet-providers@2.0.0/leaflet-providers.js + +import*as a from"leaflet";var t="default"in a?a.default:a;var e="undefined"!==typeof globalThis?globalThis:"undefined"!==typeof self?self:global;var r={};(function(a,e){"object"===typeof modules&&r?r=e(t):e(L)})(0,(function(a){a.TileLayer.Provider=a.TileLayer.extend({initialize:function(t,r){var o=a.TileLayer.Provider.providers;var i=t.split(".");var n=i[0];var s=i[1];if(!o[n])throw"No such provider ("+n+")";var p={url:o[n].url,options:o[n].options};if(s&&"variants"in o[n]){if(!(s in o[n].variants))throw"No such variant of "+n+" ("+s+")";var m=o[n].variants[s];var l;l="string"===typeof m?{variant:m}:m.options;p={url:m.url||p.url,options:a.Util.extend({},p.options,l)}}var attributionReplacer=function(a){return-1===a.indexOf("{attribution.")?a:a.replace(/\{attribution.(\w*)\}/g,(function(a,t){return attributionReplacer(o[t].options.attribution)}))};p.options.attribution=attributionReplacer(p.options.attribution);var y=a.Util.extend({},p.options,r);a.TileLayer.prototype.initialize.call(this||e,p.url,y)}});a.TileLayer.Provider.providers={OpenStreetMap:{url:"https://tile.openstreetmap.org/{z}/{x}/{y}.png",options:{maxZoom:19,attribution:'© OpenStreetMap contributors'},variants:{Mapnik:{},DE:{url:"https://tile.openstreetmap.de/{z}/{x}/{y}.png",options:{maxZoom:18}},CH:{url:"https://tile.osm.ch/switzerland/{z}/{x}/{y}.png",options:{maxZoom:18,bounds:[[45,5],[48,11]]}},France:{url:"https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png",options:{maxZoom:20,attribution:"© OpenStreetMap France | {attribution.OpenStreetMap}"}},HOT:{url:"https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",options:{attribution:'{attribution.OpenStreetMap}, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France'}},BZH:{url:"https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png",options:{attribution:'{attribution.OpenStreetMap}, Tiles courtesy of Breton OpenStreetMap Team',bounds:[[46.2,-5.5],[50,.7]]}}}},MapTilesAPI:{url:"https://maptiles.p.rapidapi.com/{variant}/{z}/{x}/{y}.png?rapidapi-key={apikey}",options:{attribution:'© MapTiles API, {attribution.OpenStreetMap}',variant:"en/map/v1",apikey:"",maxZoom:19},variants:{OSMEnglish:{options:{variant:"en/map/v1"}},OSMFrancais:{options:{variant:"fr/map/v1"}},OSMEspagnol:{options:{variant:"es/map/v1"}}}},OpenSeaMap:{url:"https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png",options:{attribution:'Map data: © OpenSeaMap contributors'}},OPNVKarte:{url:"https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png",options:{maxZoom:18,attribution:'Map memomaps.de CC-BY-SA, map data {attribution.OpenStreetMap}'}},OpenTopoMap:{url:"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",options:{maxZoom:17,attribution:'Map data: {attribution.OpenStreetMap}, SRTM | Map style: © OpenTopoMap (CC-BY-SA)'}},OpenRailwayMap:{url:"https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png",options:{maxZoom:19,attribution:'Map data: {attribution.OpenStreetMap} | Map style: © OpenRailwayMap (CC-BY-SA)'}},OpenFireMap:{url:"http://openfiremap.org/hytiles/{z}/{x}/{y}.png",options:{maxZoom:19,attribution:'Map data: {attribution.OpenStreetMap} | Map style: © OpenFireMap (CC-BY-SA)'}},SafeCast:{url:"https://s3.amazonaws.com/te512.safecast.org/{z}/{x}/{y}.png",options:{maxZoom:16,attribution:'Map data: {attribution.OpenStreetMap} | Map style: © SafeCast (CC-BY-SA)'}},Stadia:{url:"https://tiles.stadiamaps.com/tiles/{variant}/{z}/{x}/{y}{r}.{ext}",options:{minZoom:0,maxZoom:20,attribution:'© Stadia Maps © OpenMapTiles {attribution.OpenStreetMap}',variant:"alidade_smooth",ext:"png"},variants:{AlidadeSmooth:"alidade_smooth",AlidadeSmoothDark:"alidade_smooth_dark",OSMBright:"osm_bright",Outdoors:"outdoors",StamenToner:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_toner"}},StamenTonerBackground:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_toner_background"}},StamenTonerLines:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_toner_lines"}},StamenTonerLabels:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_toner_labels"}},StamenTonerLite:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_toner_lite"}},StamenWatercolor:{url:"https://tiles.stadiamaps.com/tiles/{variant}/{z}/{x}/{y}.{ext}",options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_watercolor",ext:"jpg",minZoom:1,maxZoom:16}},StamenTerrain:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_terrain",minZoom:0,maxZoom:18}},StamenTerrainBackground:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_terrain_background",minZoom:0,maxZoom:18}},StamenTerrainLabels:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_terrain_labels",minZoom:0,maxZoom:18}},StamenTerrainLines:{options:{attribution:'© Stadia Maps © Stamen Design © OpenMapTiles {attribution.OpenStreetMap}',variant:"stamen_terrain_lines",minZoom:0,maxZoom:18}}}},Thunderforest:{url:"https://{s}.tile.thunderforest.com/{variant}/{z}/{x}/{y}.png?apikey={apikey}",options:{attribution:'© Thunderforest, {attribution.OpenStreetMap}',variant:"cycle",apikey:"",maxZoom:22},variants:{OpenCycleMap:"cycle",Transport:{options:{variant:"transport"}},TransportDark:{options:{variant:"transport-dark"}},SpinalMap:{options:{variant:"spinal-map"}},Landscape:"landscape",Outdoors:"outdoors",Pioneer:"pioneer",MobileAtlas:"mobile-atlas",Neighbourhood:"neighbourhood"}},CyclOSM:{url:"https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",options:{maxZoom:20,attribution:'CyclOSM | Map data: {attribution.OpenStreetMap}'}},Jawg:{url:"https://{s}.tile.jawg.io/{variant}/{z}/{x}/{y}{r}.png?access-token={accessToken}",options:{attribution:'© JawgMaps {attribution.OpenStreetMap}',minZoom:0,maxZoom:22,subdomains:"abcd",variant:"jawg-terrain",accessToken:""},variants:{Streets:"jawg-streets",Terrain:"jawg-terrain",Sunny:"jawg-sunny",Dark:"jawg-dark",Light:"jawg-light",Matrix:"jawg-matrix"}},MapBox:{url:"https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}{r}?access_token={accessToken}",options:{attribution:'© Mapbox {attribution.OpenStreetMap} Improve this map',tileSize:512,maxZoom:18,zoomOffset:-1,id:"mapbox/streets-v11",accessToken:""}},MapTiler:{url:"https://api.maptiler.com/maps/{variant}/{z}/{x}/{y}{r}.{ext}?key={key}",options:{attribution:'© MapTiler © OpenStreetMap contributors',variant:"streets",ext:"png",key:"",tileSize:512,zoomOffset:-1,minZoom:0,maxZoom:21},variants:{Streets:"streets",Basic:"basic",Bright:"bright",Pastel:"pastel",Positron:"positron",Hybrid:{options:{variant:"hybrid",ext:"jpg"}},Toner:"toner",Topo:"topo",Voyager:"voyager"}},TomTom:{url:"https://{s}.api.tomtom.com/map/1/tile/{variant}/{style}/{z}/{x}/{y}.{ext}?key={apikey}",options:{variant:"basic",maxZoom:22,attribution:'© 1992 - '+(new Date).getFullYear()+" TomTom. ",subdomains:"abcd",style:"main",ext:"png",apikey:""},variants:{Basic:"basic",Hybrid:"hybrid",Labels:"labels"}},Esri:{url:"https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}",options:{variant:"World_Street_Map",attribution:"Tiles © Esri"},variants:{WorldStreetMap:{options:{attribution:"{attribution.Esri} — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012"}},DeLorme:{options:{variant:"Specialty/DeLorme_World_Base_Map",minZoom:1,maxZoom:11,attribution:"{attribution.Esri} — Copyright: ©2012 DeLorme"}},WorldTopoMap:{options:{variant:"World_Topo_Map",attribution:"{attribution.Esri} — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community"}},WorldImagery:{options:{variant:"World_Imagery",attribution:"{attribution.Esri} — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"}},WorldTerrain:{options:{variant:"World_Terrain_Base",maxZoom:13,attribution:"{attribution.Esri} — Source: USGS, Esri, TANA, DeLorme, and NPS"}},WorldShadedRelief:{options:{variant:"World_Shaded_Relief",maxZoom:13,attribution:"{attribution.Esri} — Source: Esri"}},WorldPhysical:{options:{variant:"World_Physical_Map",maxZoom:8,attribution:"{attribution.Esri} — Source: US National Park Service"}},OceanBasemap:{options:{variant:"Ocean/World_Ocean_Base",maxZoom:13,attribution:"{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri"}},NatGeoWorldMap:{options:{variant:"NatGeo_World_Map",maxZoom:16,attribution:"{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC"}},WorldGrayCanvas:{options:{variant:"Canvas/World_Light_Gray_Base",maxZoom:16,attribution:"{attribution.Esri} — Esri, DeLorme, NAVTEQ"}}}},OpenWeatherMap:{url:"http://{s}.tile.openweathermap.org/map/{variant}/{z}/{x}/{y}.png?appid={apiKey}",options:{maxZoom:19,attribution:'Map data © OpenWeatherMap',apiKey:"",opacity:.5},variants:{Clouds:"clouds",CloudsClassic:"clouds_cls",Precipitation:"precipitation",PrecipitationClassic:"precipitation_cls",Rain:"rain",RainClassic:"rain_cls",Pressure:"pressure",PressureContour:"pressure_cntr",Wind:"wind",Temperature:"temp",Snow:"snow"}},HERE:{url:"https://{s}.{base}.maps.api.here.com/maptile/2.1/{type}/{mapID}/{variant}/{z}/{x}/{y}/{size}/{format}?app_id={app_id}&app_code={app_code}&lg={language}",options:{attribution:"Map © 1987-"+(new Date).getFullYear()+' HERE',subdomains:"1234",mapID:"newest",app_id:"",app_code:"",base:"base",variant:"normal.day",maxZoom:20,type:"maptile",language:"eng",format:"png8",size:"256"},variants:{normalDay:"normal.day",normalDayCustom:"normal.day.custom",normalDayGrey:"normal.day.grey",normalDayMobile:"normal.day.mobile",normalDayGreyMobile:"normal.day.grey.mobile",normalDayTransit:"normal.day.transit",normalDayTransitMobile:"normal.day.transit.mobile",normalDayTraffic:{options:{variant:"normal.traffic.day",base:"traffic",type:"traffictile"}},normalNight:"normal.night",normalNightMobile:"normal.night.mobile",normalNightGrey:"normal.night.grey",normalNightGreyMobile:"normal.night.grey.mobile",normalNightTransit:"normal.night.transit",normalNightTransitMobile:"normal.night.transit.mobile",reducedDay:"reduced.day",reducedNight:"reduced.night",basicMap:{options:{type:"basetile"}},mapLabels:{options:{type:"labeltile",format:"png"}},trafficFlow:{options:{base:"traffic",type:"flowtile"}},carnavDayGrey:"carnav.day.grey",hybridDay:{options:{base:"aerial",variant:"hybrid.day"}},hybridDayMobile:{options:{base:"aerial",variant:"hybrid.day.mobile"}},hybridDayTransit:{options:{base:"aerial",variant:"hybrid.day.transit"}},hybridDayGrey:{options:{base:"aerial",variant:"hybrid.grey.day"}},hybridDayTraffic:{options:{variant:"hybrid.traffic.day",base:"traffic",type:"traffictile"}},pedestrianDay:"pedestrian.day",pedestrianNight:"pedestrian.night",satelliteDay:{options:{base:"aerial",variant:"satellite.day"}},terrainDay:{options:{base:"aerial",variant:"terrain.day"}},terrainDayMobile:{options:{base:"aerial",variant:"terrain.day.mobile"}}}},HEREv3:{url:"https://{s}.{base}.maps.ls.hereapi.com/maptile/2.1/{type}/{mapID}/{variant}/{z}/{x}/{y}/{size}/{format}?apiKey={apiKey}&lg={language}",options:{attribution:"Map © 1987-"+(new Date).getFullYear()+' HERE',subdomains:"1234",mapID:"newest",apiKey:"",base:"base",variant:"normal.day",maxZoom:20,type:"maptile",language:"eng",format:"png8",size:"256"},variants:{normalDay:"normal.day",normalDayCustom:"normal.day.custom",normalDayGrey:"normal.day.grey",normalDayMobile:"normal.day.mobile",normalDayGreyMobile:"normal.day.grey.mobile",normalDayTransit:"normal.day.transit",normalDayTransitMobile:"normal.day.transit.mobile",normalNight:"normal.night",normalNightMobile:"normal.night.mobile",normalNightGrey:"normal.night.grey",normalNightGreyMobile:"normal.night.grey.mobile",normalNightTransit:"normal.night.transit",normalNightTransitMobile:"normal.night.transit.mobile",reducedDay:"reduced.day",reducedNight:"reduced.night",basicMap:{options:{type:"basetile"}},mapLabels:{options:{type:"labeltile",format:"png"}},trafficFlow:{options:{base:"traffic",type:"flowtile"}},carnavDayGrey:"carnav.day.grey",hybridDay:{options:{base:"aerial",variant:"hybrid.day"}},hybridDayMobile:{options:{base:"aerial",variant:"hybrid.day.mobile"}},hybridDayTransit:{options:{base:"aerial",variant:"hybrid.day.transit"}},hybridDayGrey:{options:{base:"aerial",variant:"hybrid.grey.day"}},pedestrianDay:"pedestrian.day",pedestrianNight:"pedestrian.night",satelliteDay:{options:{base:"aerial",variant:"satellite.day"}},terrainDay:{options:{base:"aerial",variant:"terrain.day"}},terrainDayMobile:{options:{base:"aerial",variant:"terrain.day.mobile"}}}},FreeMapSK:{url:"https://{s}.freemap.sk/T/{z}/{x}/{y}.jpeg",options:{minZoom:8,maxZoom:16,subdomains:"abcd",bounds:[[47.204642,15.996093],[49.830896,22.576904]],attribution:'{attribution.OpenStreetMap}, visualization CC-By-SA 2.0 Freemap.sk'}},MtbMap:{url:"http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png",options:{attribution:"{attribution.OpenStreetMap} & USGS"}},CartoDB:{url:"https://{s}.basemaps.cartocdn.com/{variant}/{z}/{x}/{y}{r}.png",options:{attribution:'{attribution.OpenStreetMap} © CARTO',subdomains:"abcd",maxZoom:20,variant:"light_all"},variants:{Positron:"light_all",PositronNoLabels:"light_nolabels",PositronOnlyLabels:"light_only_labels",DarkMatter:"dark_all",DarkMatterNoLabels:"dark_nolabels",DarkMatterOnlyLabels:"dark_only_labels",Voyager:"rastertiles/voyager",VoyagerNoLabels:"rastertiles/voyager_nolabels",VoyagerOnlyLabels:"rastertiles/voyager_only_labels",VoyagerLabelsUnder:"rastertiles/voyager_labels_under"}},HikeBike:{url:"https://tiles.wmflabs.org/{variant}/{z}/{x}/{y}.png",options:{maxZoom:19,attribution:"{attribution.OpenStreetMap}",variant:"hikebike"},variants:{HikeBike:{},HillShading:{options:{maxZoom:15,variant:"hillshading"}}}},BasemapAT:{url:"https://mapsneu.wien.gv.at/basemap/{variant}/{type}/google3857/{z}/{y}/{x}.{format}",options:{maxZoom:19,attribution:'Datenquelle: basemap.at',type:"normal",format:"png",bounds:[[46.35877,8.782379],[49.037872,17.189532]],variant:"geolandbasemap"},variants:{basemap:{options:{maxZoom:20,variant:"geolandbasemap"}},grau:"bmapgrau",overlay:"bmapoverlay",terrain:{options:{variant:"bmapgelaende",type:"grau",format:"jpeg"}},surface:{options:{variant:"bmapoberflaeche",type:"grau",format:"jpeg"}},highdpi:{options:{variant:"bmaphidpi",format:"jpeg"}},orthofoto:{options:{maxZoom:20,variant:"bmaporthofoto30cm",format:"jpeg"}}}},nlmaps:{url:"https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0/{variant}/EPSG:3857/{z}/{x}/{y}.png",options:{minZoom:6,maxZoom:19,bounds:[[50.5,3.25],[54,7.6]],attribution:'Kaartgegevens © Kadaster'},variants:{standaard:"standaard",pastel:"pastel",grijs:"grijs",water:"water",luchtfoto:{url:"https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0/Actueel_ortho25/EPSG:3857/{z}/{x}/{y}.jpeg"}}},NASAGIBS:{url:"https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{maxZoom}/{z}/{y}/{x}.{format}",options:{attribution:'Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.',bounds:[[-85.0511287776,-179.999999975],[85.0511287776,179.999999975]],minZoom:1,maxZoom:9,format:"jpg",time:"",tilematrixset:"GoogleMapsCompatible_Level"},variants:{ModisTerraTrueColorCR:"MODIS_Terra_CorrectedReflectance_TrueColor",ModisTerraBands367CR:"MODIS_Terra_CorrectedReflectance_Bands367",ViirsEarthAtNight2012:{options:{variant:"VIIRS_CityLights_2012",maxZoom:8}},ModisTerraLSTDay:{options:{variant:"MODIS_Terra_Land_Surface_Temp_Day",format:"png",maxZoom:7,opacity:.75}},ModisTerraSnowCover:{options:{variant:"MODIS_Terra_NDSI_Snow_Cover",format:"png",maxZoom:8,opacity:.75}},ModisTerraAOD:{options:{variant:"MODIS_Terra_Aerosol",format:"png",maxZoom:6,opacity:.75}},ModisTerraChlorophyll:{options:{variant:"MODIS_Terra_Chlorophyll_A",format:"png",maxZoom:7,opacity:.75}}}},NLS:{url:"https://nls-{s}.tileserver.com/nls/{z}/{x}/{y}.jpg",options:{attribution:'National Library of Scotland Historic Maps',bounds:[[49.6,-12],[61.7,3]],minZoom:1,maxZoom:18,subdomains:"0123"}},JusticeMap:{url:"https://www.justicemap.org/tile/{size}/{variant}/{z}/{x}/{y}.png",options:{attribution:'Justice Map',size:"county",bounds:[[14,-180],[72,-56]]},variants:{income:"income",americanIndian:"indian",asian:"asian",black:"black",hispanic:"hispanic",multi:"multi",nonWhite:"nonwhite",white:"white",plurality:"plural"}},GeoportailFrance:{url:"https://wxs.ign.fr/{apikey}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET=PM&FORMAT={format}&LAYER={variant}&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}",options:{attribution:'Geoportail France',bounds:[[-75,-180],[81,180]],minZoom:2,maxZoom:18,apikey:"choisirgeoportail",format:"image/png",style:"normal",variant:"GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2"},variants:{plan:"GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2",parcels:{options:{variant:"CADASTRALPARCELS.PARCELLAIRE_EXPRESS",style:"PCI vecteur",maxZoom:20}},orthos:{options:{maxZoom:19,format:"image/jpeg",variant:"ORTHOIMAGERY.ORTHOPHOTOS"}}}},OneMapSG:{url:"https://maps-{s}.onemap.sg/v3/{variant}/{z}/{x}/{y}.png",options:{variant:"Default",minZoom:11,maxZoom:18,bounds:[[1.56073,104.11475],[1.16,103.502]],attribution:' New OneMap | Map data © contributors, Singapore Land Authority'},variants:{Default:"Default",Night:"Night",Original:"Original",Grey:"Grey",LandLot:"LandLot"}},USGS:{url:"https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}",options:{maxZoom:20,attribution:'Tiles courtesy of the U.S. Geological Survey'},variants:{USTopo:{},USImagery:{url:"https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}"},USImageryTopo:{url:"https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}"}}},WaymarkedTrails:{url:"https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png",options:{maxZoom:18,attribution:'Map data: {attribution.OpenStreetMap} | Map style: © waymarkedtrails.org (CC-BY-SA)'},variants:{hiking:"hiking",cycling:"cycling",mtb:"mtb",slopes:"slopes",riding:"riding",skating:"skating"}},OpenAIP:{url:"https://{s}.tile.maps.openaip.net/geowebcache/service/tms/1.0.0/openaip_basemap@EPSG%3A900913@png/{z}/{x}/{y}.{ext}",options:{attribution:'openAIP Data (CC-BY-NC-SA)',ext:"png",minZoom:4,maxZoom:14,tms:true,detectRetina:true,subdomains:"12"}},OpenSnowMap:{url:"https://tiles.opensnowmap.org/{variant}/{z}/{x}/{y}.png",options:{minZoom:9,maxZoom:18,attribution:'Map data: {attribution.OpenStreetMap} & ODbL, © www.opensnowmap.org CC-BY-SA'},variants:{pistes:"pistes"}},AzureMaps:{url:"https://atlas.microsoft.com/map/tile?api-version={apiVersion}&tilesetId={variant}&x={x}&y={y}&zoom={z}&language={language}&subscription-key={subscriptionKey}",options:{attribution:"See https://docs.microsoft.com/en-us/rest/api/maps/render-v2/get-map-tile for details.",apiVersion:"2.0",variant:"microsoft.imagery",subscriptionKey:"",language:"en-US"},variants:{MicrosoftImagery:"microsoft.imagery",MicrosoftBaseDarkGrey:"microsoft.base.darkgrey",MicrosoftBaseRoad:"microsoft.base.road",MicrosoftBaseHybridRoad:"microsoft.base.hybrid.road",MicrosoftTerraMain:"microsoft.terra.main",MicrosoftWeatherInfraredMain:{url:"https://atlas.microsoft.com/map/tile?api-version={apiVersion}&tilesetId={variant}&x={x}&y={y}&zoom={z}&timeStamp={timeStamp}&language={language}&subscription-key={subscriptionKey}",options:{timeStamp:"2021-05-08T09:03:00Z",attribution:"See https://docs.microsoft.com/en-us/rest/api/maps/render-v2/get-map-tile#uri-parameters for details.",variant:"microsoft.weather.infrared.main"}},MicrosoftWeatherRadarMain:{url:"https://atlas.microsoft.com/map/tile?api-version={apiVersion}&tilesetId={variant}&x={x}&y={y}&zoom={z}&timeStamp={timeStamp}&language={language}&subscription-key={subscriptionKey}",options:{timeStamp:"2021-05-08T09:03:00Z",attribution:"See https://docs.microsoft.com/en-us/rest/api/maps/render-v2/get-map-tile#uri-parameters for details.",variant:"microsoft.weather.radar.main"}}}},SwissFederalGeoportal:{url:"https://wmts.geo.admin.ch/1.0.0/{variant}/default/current/3857/{z}/{x}/{y}.jpeg",options:{attribution:'© swisstopo',minZoom:2,maxZoom:18,bounds:[[45.398181,5.140242],[48.230651,11.47757]]},variants:{NationalMapColor:"ch.swisstopo.pixelkarte-farbe",NationalMapGrey:"ch.swisstopo.pixelkarte-grau",SWISSIMAGE:{options:{variant:"ch.swisstopo.swissimage",maxZoom:19}}}}};a.tileLayer.provider=function(t,e){return new a.TileLayer.Provider(t,e)};return a}));var o=r;export{o as default}; + diff --git a/vendor/javascript/leaflet.js b/vendor/javascript/leaflet.js new file mode 100644 index 000000000..788f7537d --- /dev/null +++ b/vendor/javascript/leaflet.js @@ -0,0 +1,8 @@ +// leaflet@1.9.4 downloaded from https://ga.jspm.io/npm:leaflet@1.9.4/dist/leaflet-src.js + +var t="undefined"!==typeof globalThis?globalThis:"undefined"!==typeof self?self:global;var e={}; +/* @preserve + * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com + * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */(function(t,i){i(e)})(0,(function(e){var i="1.9.4";function extend(t){var e,i,n,o;for(i=1,n=arguments.length;i0?Math.floor(t):Math.ceil(t)};Point.prototype={clone:function(){return new Point((this||t).x,(this||t).y)},add:function(t){return this.clone()._add(toPoint(t))},_add:function(e){(this||t).x+=e.x;(this||t).y+=e.y;return this||t},subtract:function(t){return this.clone()._subtract(toPoint(t))},_subtract:function(e){(this||t).x-=e.x;(this||t).y-=e.y;return this||t},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(e){(this||t).x/=e;(this||t).y/=e;return this||t},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(e){(this||t).x*=e;(this||t).y*=e;return this||t},scaleBy:function(e){return new Point((this||t).x*e.x,(this||t).y*e.y)},unscaleBy:function(e){return new Point((this||t).x/e.x,(this||t).y/e.y)},round:function(){return this.clone()._round()},_round:function(){(this||t).x=Math.round((this||t).x);(this||t).y=Math.round((this||t).y);return this||t},floor:function(){return this.clone()._floor()},_floor:function(){(this||t).x=Math.floor((this||t).x);(this||t).y=Math.floor((this||t).y);return this||t},ceil:function(){return this.clone()._ceil()},_ceil:function(){(this||t).x=Math.ceil((this||t).x);(this||t).y=Math.ceil((this||t).y);return this||t},trunc:function(){return this.clone()._trunc()},_trunc:function(){(this||t).x=m((this||t).x);(this||t).y=m((this||t).y);return this||t},distanceTo:function(e){e=toPoint(e);var i=e.x-(this||t).x,n=e.y-(this||t).y;return Math.sqrt(i*i+n*n)},equals:function(e){e=toPoint(e);return e.x===(this||t).x&&e.y===(this||t).y},contains:function(e){e=toPoint(e);return Math.abs(e.x)<=Math.abs((this||t).x)&&Math.abs(e.y)<=Math.abs((this||t).y)},toString:function(){return"Point("+formatNum((this||t).x)+", "+formatNum((this||t).y)+")"}};function toPoint(t,e,i){return t instanceof Point?t:a(t)?new Point(t[0],t[1]):void 0===t||null===t?t:"object"===typeof t&&"x"in t&&"y"in t?new Point(t.x,t.y):new Point(t,e,i)}function Bounds(t,e){if(t){var i=e?[t,e]:t;for(var n=0,o=i.length;n=(this||t).min.x&&n.x<=(this||t).max.x&&i.y>=(this||t).min.y&&n.y<=(this||t).max.y},intersects:function(e){e=toBounds(e);var i=(this||t).min,n=(this||t).max,o=e.min,s=e.max,a=s.x>=i.x&&o.x<=n.x,h=s.y>=i.y&&o.y<=n.y;return a&&h},overlaps:function(e){e=toBounds(e);var i=(this||t).min,n=(this||t).max,o=e.min,s=e.max,a=s.x>i.x&&o.xi.y&&o.y=o.lat&&n.lat<=s.lat&&i.lng>=o.lng&&n.lng<=s.lng},intersects:function(e){e=toLatLngBounds(e);var i=(this||t)._southWest,n=(this||t)._northEast,o=e.getSouthWest(),s=e.getNorthEast(),a=s.lat>=i.lat&&o.lat<=n.lat,h=s.lng>=i.lng&&o.lng<=n.lng;return a&&h},overlaps:function(e){e=toLatLngBounds(e);var i=(this||t)._southWest,n=(this||t)._northEast,o=e.getSouthWest(),s=e.getNorthEast(),a=s.lat>i.lat&&o.lati.lng&&o.lng1;var it=function(){var t=false;try{var e=Object.defineProperty({},"passive",{get:function(){t=true}});window.addEventListener("testPassiveEventSupport",falseFn,e);window.removeEventListener("testPassiveEventSupport",falseFn,e)}catch(t){}return t}();var nt=function(){return!!document.createElement("canvas").getContext}();var ot=!!(document.createElementNS&&svgCreate("svg").createSVGRect);var st=!!ot&&function(){var t=document.createElement("div");t.innerHTML="";return"http://www.w3.org/2000/svg"===(t.firstChild&&t.firstChild.namespaceURI)}();var at=!ot&&function(){try{var t=document.createElement("div");t.innerHTML='';var e=t.firstChild;e.style.behavior="url(#default#VML)";return e&&"object"===typeof e.adj}catch(t){return false}}();var rt=0===navigator.platform.indexOf("Mac");var ht=0===navigator.platform.indexOf("Linux");function userAgentContains(t){return navigator.userAgent.toLowerCase().indexOf(t)>=0}var lt={ie:C,ielt9:M,edge:z,webkit:S,android:B,android23:O,androidStock:Z,opera:E,chrome:A,gecko:I,safari:D,phantom:N,opera12:R,win:j,ie3d:W,webkit3d:H,gecko3d:q,any3d:U,mobile:V,mobileWebkit:G,mobileWebkit3d:$,msPointer:K,pointer:Y,touch:X,touchNative:J,mobileOpera:Q,mobileGecko:tt,retina:et,passiveEvents:it,canvas:nt,svg:ot,vml:at,inlineSvg:st,mac:rt,linux:ht};var ut=lt.msPointer?"MSPointerDown":"pointerdown";var ct=lt.msPointer?"MSPointerMove":"pointermove";var dt=lt.msPointer?"MSPointerUp":"pointerup";var _t=lt.msPointer?"MSPointerCancel":"pointercancel";var pt={touchstart:ut,touchmove:ct,touchend:dt,touchcancel:_t};var ft={touchstart:_onPointerStart,touchmove:_handlePointer,touchend:_handlePointer,touchcancel:_handlePointer};var mt={};var gt=false;function addPointerListener(e,i,n){"touchstart"===i&&_addPointerDocListener();if(!ft[i]){console.warn("wrong event specified:",i);return falseFn}n=ft[i].bind(this||t,n);e.addEventListener(pt[i],n,false);return n}function removePointerListener(t,e,i){pt[e]?t.removeEventListener(pt[e],i,false):console.warn("wrong event specified:",e)}function _globalPointerDown(t){mt[t.pointerId]=t}function _globalPointerMove(t){mt[t.pointerId]&&(mt[t.pointerId]=t)}function _globalPointerUp(t){delete mt[t.pointerId]}function _addPointerDocListener(){if(!gt){document.addEventListener(ut,_globalPointerDown,true);document.addEventListener(ct,_globalPointerMove,true);document.addEventListener(dt,_globalPointerUp,true);document.addEventListener(_t,_globalPointerUp,true);gt=true}}function _handlePointer(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||"mouse")){e.touches=[];for(var i in mt)e.touches.push(mt[i]);e.changedTouches=[e];t(e)}}function _onPointerStart(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&preventDefault(e);_handlePointer(t,e)}function makeDblclick(t){var e,i,n={};for(i in t){e=t[i];n[i]=e&&e.bind?e.bind(t):e}t=n;n.type="dblclick";n.detail=2;n.isTrusted=false;n._simulated=true;return n}var vt=200;function addDoubleTapListener(t,e){t.addEventListener("dblclick",e);var i,n=0;function simDblclick(t){if(1===t.detail){if("mouse"!==t.pointerType&&(!t.sourceCapabilities||t.sourceCapabilities.firesTouchEvents)){var o=getPropagationPath(t);if(!o.some((function(t){return t instanceof HTMLLabelElement&&t.attributes.for}))||o.some((function(t){return t instanceof HTMLInputElement||t instanceof HTMLSelectElement}))){var s=Date.now();if(s-n<=vt){i++;2===i&&e(makeDblclick(t))}else i=1;n=s}}}else i=t.detail}t.addEventListener("click",simDblclick);return{dblclick:e,simDblclick:simDblclick}}function removeDoubleTapListener(t,e){t.removeEventListener("dblclick",e.dblclick);t.removeEventListener("click",e.simDblclick)}var yt=testProp(["transform","webkitTransform","OTransform","MozTransform","msTransform"]);var Lt=testProp(["webkitTransition","transition","OTransition","MozTransition","msTransition"]);var Pt="webkitTransition"===Lt||"OTransition"===Lt?Lt+"End":"transitionend";function get(t){return"string"===typeof t?document.getElementById(t):t}function getStyle(t,e){var i=t.style[e]||t.currentStyle&&t.currentStyle[e];if((!i||"auto"===i)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);i=n?n[e]:null}return"auto"===i?null:i}function create$1(t,e,i){var n=document.createElement(t);n.className=e||"";i&&i.appendChild(n);return n}function remove(t){var e=t.parentNode;e&&e.removeChild(t)}function empty(t){while(t.firstChild)t.removeChild(t.firstChild)}function toFront(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function toBack(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function hasClass(t,e){if(void 0!==t.classList)return t.classList.contains(e);var i=getClass(t);return i.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(i)}function addClass(t,e){if(void 0!==t.classList){var i=splitWords(e);for(var n=0,o=i.length;n0?2*window.devicePixelRatio:1;function getWheelDelta(t){return lt.edge?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/Ot:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function isExternalTarget(t,e){var i=e.relatedTarget;if(!i)return true;try{while(i&&i!==t)i=i.parentNode}catch(t){return false}return i!==t}var kt={__proto__:null,on:on,off:off,stopPropagation:stopPropagation,disableScrollPropagation:disableScrollPropagation,disableClickPropagation:disableClickPropagation,preventDefault:preventDefault,stop:stop,getPropagationPath:getPropagationPath,getMousePosition:getMousePosition,getWheelDelta:getWheelDelta,isExternalTarget:isExternalTarget,addListener:on,removeListener:off};var Zt=f.extend({run:function(e,i,n,o){this.stop();(this||t)._el=e;(this||t)._inProgress=true;(this||t)._duration=n||.25;(this||t)._easeOutPower=1/Math.max(o||.5,.2);(this||t)._startPos=getPosition(e);(this||t)._offset=i.subtract((this||t)._startPos);(this||t)._startTime=+new Date;this.fire("start");this._animate()},stop:function(){if((this||t)._inProgress){this._step(true);this._complete()}},_animate:function(){(this||t)._animId=requestAnimFrame((this||t)._animate,this||t);this._step()},_step:function(e){var i=+new Date-(this||t)._startTime,n=1e3*(this||t)._duration;if(i(this||t).options.maxZoom)return this.setZoom(e)}return this||t},panInsideBounds:function(e,i){(this||t)._enforcingBounds=true;var n=this.getCenter(),o=this._limitCenter(n,(this||t)._zoom,toLatLngBounds(e));n.equals(o)||this.panTo(o,i);(this||t)._enforcingBounds=false;return this||t},panInside:function(e,i){i=i||{};var n=toPoint(i.paddingTopLeft||i.padding||[0,0]),o=toPoint(i.paddingBottomRight||i.padding||[0,0]),s=this.project(this.getCenter()),a=this.project(e),h=this.getPixelBounds(),l=toBounds([h.min.add(n),h.max.subtract(o)]),c=l.getSize();if(!l.contains(a)){(this||t)._enforcingBounds=true;var d=a.subtract(l.getCenter());var _=l.extend(a).getSize().subtract(c);s.x+=d.x<0?-_.x:_.x;s.y+=d.y<0?-_.y:_.y;this.panTo(this.unproject(s),i);(this||t)._enforcingBounds=false}return this||t},invalidateSize:function(e){if(!(this||t)._loaded)return this||t;e=extend({animate:false,pan:true},true===e?{animate:true}:e);var i=this.getSize();(this||t)._sizeChanged=true;(this||t)._lastCenter=null;var n=this.getSize(),o=i.divideBy(2).round(),s=n.divideBy(2).round(),a=o.subtract(s);if(!a.x&&!a.y)return this||t;if(e.animate&&e.pan)this.panBy(a);else{e.pan&&this._rawPanBy(a);this.fire("move");if(e.debounceMoveend){clearTimeout((this||t)._sizeTimer);(this||t)._sizeTimer=setTimeout(bind((this||t).fire,this||t,"moveend"),200)}else this.fire("moveend")}return this.fire("resize",{oldSize:i,newSize:n})},stop:function(){this.setZoom(this._limitZoom((this||t)._zoom));(this||t).options.zoomSnap||this.fire("viewreset");return this._stop()},locate:function(e){e=(this||t)._locateOptions=extend({timeout:1e4,watch:false},e);if(!("geolocation"in navigator)){this._handleGeolocationError({code:0,message:"Geolocation not supported."});return this||t}var i=bind((this||t)._handleGeolocationResponse,this||t),n=bind((this||t)._handleGeolocationError,this||t);e.watch?(this||t)._locationWatchId=navigator.geolocation.watchPosition(i,n,e):navigator.geolocation.getCurrentPosition(i,n,e);return this||t},stopLocate:function(){navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch((this||t)._locationWatchId);(this||t)._locateOptions&&((this||t)._locateOptions.setView=false);return this||t},_handleGeolocationError:function(e){if((this||t)._container._leaflet_id){var i=e.code,n=e.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");(this||t)._locateOptions.setView&&!(this||t)._loaded&&this.fitWorld();this.fire("locationerror",{code:i,message:"Geolocation error: "+n+"."})}},_handleGeolocationResponse:function(e){if((this||t)._container._leaflet_id){var i=e.coords.latitude,n=e.coords.longitude,o=new LatLng(i,n),s=o.toBounds(2*e.coords.accuracy),a=(this||t)._locateOptions;if(a.setView){var h=this.getBoundsZoom(s);this.setView(o,a.maxZoom?Math.min(h,a.maxZoom):h)}var l={latlng:o,bounds:s,timestamp:e.timestamp};for(var c in e.coords)"number"===typeof e.coords[c]&&(l[c]=e.coords[c]);this.fire("locationfound",l)}},addHandler:function(e,i){if(!i)return this||t;var n=(this||t)[e]=new i(this||t);(this||t)._handlers.push(n);(this||t).options[e]&&n.enable();return this||t},remove:function(){this._initEvents(true);(this||t).options.maxBounds&&this.off("moveend",(this||t)._panInsideMaxBounds);if((this||t)._containerId!==(this||t)._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete(this||t)._container._leaflet_id;delete(this||t)._containerId}catch(e){(this||t)._container._leaflet_id=void 0;(this||t)._containerId=void 0}void 0!==(this||t)._locationWatchId&&this.stopLocate();this._stop();remove((this||t)._mapPane);(this||t)._clearControlPos&&this._clearControlPos();if((this||t)._resizeRequest){cancelAnimFrame((this||t)._resizeRequest);(this||t)._resizeRequest=null}this._clearHandlers();(this||t)._loaded&&this.fire("unload");var e;for(e in(this||t)._layers)(this||t)._layers[e].remove();for(e in(this||t)._panes)remove((this||t)._panes[e]);(this||t)._layers=[];(this||t)._panes=[];delete(this||t)._mapPane;delete(this||t)._renderer;return this||t},createPane:function(e,i){var n="leaflet-pane"+(e?" leaflet-"+e.replace("Pane","")+"-pane":""),o=create$1("div",n,i||(this||t)._mapPane);e&&((this||t)._panes[e]=o);return o},getCenter:function(){this._checkIfLoaded();return(this||t)._lastCenter&&!this._moved()?(this||t)._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return(this||t)._zoom},getBounds:function(){var t=this.getPixelBounds(),e=this.unproject(t.getBottomLeft()),i=this.unproject(t.getTopRight());return new LatLngBounds(e,i)},getMinZoom:function(){return void 0===(this||t).options.minZoom?(this||t)._layersMinZoom||0:(this||t).options.minZoom},getMaxZoom:function(){return void 0===(this||t).options.maxZoom?void 0===(this||t)._layersMaxZoom?Infinity:(this||t)._layersMaxZoom:(this||t).options.maxZoom},getBoundsZoom:function(e,i,n){e=toLatLngBounds(e);n=toPoint(n||[0,0]);var o=this.getZoom()||0,s=this.getMinZoom(),a=this.getMaxZoom(),h=e.getNorthWest(),l=e.getSouthEast(),c=this.getSize().subtract(n),d=toBounds(this.project(l,o),this.project(h,o)).getSize(),_=lt.any3d?(this||t).options.zoomSnap:1,p=c.x/d.x,f=c.y/d.y,m=i?Math.max(p,f):Math.min(p,f);o=this.getScaleZoom(m,o);if(_){o=Math.round(o/(_/100))*(_/100);o=i?Math.ceil(o/_)*_:Math.floor(o/_)*_}return Math.max(s,Math.min(a,o))},getSize:function(){if(!(this||t)._size||(this||t)._sizeChanged){(this||t)._size=new Point((this||t)._container.clientWidth||0,(this||t)._container.clientHeight||0);(this||t)._sizeChanged=false}return(this||t)._size.clone()},getPixelBounds:function(t,e){var i=this._getTopLeftPoint(t,e);return new Bounds(i,i.add(this.getSize()))},getPixelOrigin:function(){this._checkIfLoaded();return(this||t)._pixelOrigin},getPixelWorldBounds:function(e){return(this||t).options.crs.getProjectedBounds(void 0===e?this.getZoom():e)},getPane:function(e){return"string"===typeof e?(this||t)._panes[e]:e},getPanes:function(){return(this||t)._panes},getContainer:function(){return(this||t)._container},getZoomScale:function(e,i){var n=(this||t).options.crs;i=void 0===i?(this||t)._zoom:i;return n.scale(e)/n.scale(i)},getScaleZoom:function(e,i){var n=(this||t).options.crs;i=void 0===i?(this||t)._zoom:i;var o=n.zoom(e*n.scale(i));return isNaN(o)?Infinity:o},project:function(e,i){i=void 0===i?(this||t)._zoom:i;return(this||t).options.crs.latLngToPoint(toLatLng(e),i)},unproject:function(e,i){i=void 0===i?(this||t)._zoom:i;return(this||t).options.crs.pointToLatLng(toPoint(e),i)},layerPointToLatLng:function(t){var e=toPoint(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(toLatLng(t))._round();return e._subtract(this.getPixelOrigin())},wrapLatLng:function(e){return(this||t).options.crs.wrapLatLng(toLatLng(e))},wrapLatLngBounds:function(e){return(this||t).options.crs.wrapLatLngBounds(toLatLngBounds(e))},distance:function(e,i){return(this||t).options.crs.distance(toLatLng(e),toLatLng(i))},containerPointToLayerPoint:function(t){return toPoint(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return toPoint(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(toPoint(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(t)))},mouseEventToContainerPoint:function(e){return getMousePosition(e,(this||t)._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(e){var i=(this||t)._container=get(e);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");on(i,"scroll",(this||t)._onScroll,this||t);(this||t)._containerId=stamp(i)},_initLayout:function(){var e=(this||t)._container;(this||t)._fadeAnimated=(this||t).options.fadeAnimation&<.any3d;addClass(e,"leaflet-container"+(lt.touch?" leaflet-touch":"")+(lt.retina?" leaflet-retina":"")+(lt.ielt9?" leaflet-oldie":"")+(lt.safari?" leaflet-safari":"")+((this||t)._fadeAnimated?" leaflet-fade-anim":""));var i=getStyle(e,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&"sticky"!==i&&(e.style.position="relative");this._initPanes();(this||t)._initControlPos&&this._initControlPos()},_initPanes:function(){var e=(this||t)._panes={};(this||t)._paneRenderers={};(this||t)._mapPane=this.createPane("mapPane",(this||t)._container);setPosition((this||t)._mapPane,new Point(0,0));this.createPane("tilePane");this.createPane("overlayPane");this.createPane("shadowPane");this.createPane("markerPane");this.createPane("tooltipPane");this.createPane("popupPane");if(!(this||t).options.markerZoomAnimation){addClass(e.markerPane,"leaflet-zoom-hide");addClass(e.shadowPane,"leaflet-zoom-hide")}},_resetView:function(e,i,n){setPosition((this||t)._mapPane,new Point(0,0));var o=!(this||t)._loaded;(this||t)._loaded=true;i=this._limitZoom(i);this.fire("viewprereset");var s=(this||t)._zoom!==i;this._moveStart(s,n)._move(e,i)._moveEnd(s);this.fire("viewreset");o&&this.fire("load")},_moveStart:function(e,i){e&&this.fire("zoomstart");i||this.fire("movestart");return this||t},_move:function(e,i,n,o){void 0===i&&(i=(this||t)._zoom);var s=(this||t)._zoom!==i;(this||t)._zoom=i;(this||t)._lastCenter=e;(this||t)._pixelOrigin=this._getNewPixelOrigin(e);if(o)n&&n.pinch&&this.fire("zoom",n);else{(s||n&&n.pinch)&&this.fire("zoom",n);this.fire("move",n)}return this||t},_moveEnd:function(t){t&&this.fire("zoomend");return this.fire("moveend")},_stop:function(){cancelAnimFrame((this||t)._flyToFrame);(this||t)._panAnim&&(this||t)._panAnim.stop();return this||t},_rawPanBy:function(e){setPosition((this||t)._mapPane,this._getMapPanePos().subtract(e))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){(this||t)._enforcingBounds||this.panInsideBounds((this||t).options.maxBounds)},_checkIfLoaded:function(){if(!(this||t)._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(e){(this||t)._targets={};(this||t)._targets[stamp((this||t)._container)]=this||t;var i=e?off:on;i((this||t)._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",(this||t)._handleDOMEvent,this||t);(this||t).options.trackResize&&i(window,"resize",(this||t)._onResize,this||t);lt.any3d&&(this||t).options.transform3DLimit&&(e?(this||t).off:(this||t).on).call(this||t,"moveend",(this||t)._onMoveEnd)},_onResize:function(){cancelAnimFrame((this||t)._resizeRequest);(this||t)._resizeRequest=requestAnimFrame((function(){this.invalidateSize({debounceMoveend:true})}),this||t)},_onScroll:function(){(this||t)._container.scrollTop=0;(this||t)._container.scrollLeft=0},_onMoveEnd:function(){var e=this._getMapPanePos();Math.max(Math.abs(e.x),Math.abs(e.y))>=(this||t).options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(e,i){var n,o=[],s="mouseout"===i||"mouseover"===i,a=e.target||e.srcElement,h=false;while(a){n=(this||t)._targets[stamp(a)];if(n&&("click"===i||"preclick"===i)&&this._draggableMoved(n)){h=true;break}if(n&&n.listens(i,true)){if(s&&!isExternalTarget(a,e))break;o.push(n);if(s)break}if(a===(this||t)._container)break;a=a.parentNode}o.length||h||s||!this.listens(i,true)||(o=[this||t]);return o},_isClickDisabled:function(e){while(e&&e!==(this||t)._container){if(e._leaflet_disable_click)return true;e=e.parentNode}},_handleDOMEvent:function(e){var i=e.target||e.srcElement;if(!(!(this||t)._loaded||i._leaflet_disable_events||"click"===e.type&&this._isClickDisabled(i))){var n=e.type;"mousedown"===n&&preventOutline(i);this._fireDOMEvent(e,n)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(e,i,n){if("click"===e.type){var o=extend({},e);o.type="preclick";this._fireDOMEvent(o,o.type,n)}var s=this._findEventTargets(e,i);if(n){var a=[];for(var h=0;h0?Math.round(t-e)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(e))},_limitZoom:function(e){var i=this.getMinZoom(),n=this.getMaxZoom(),o=lt.any3d?(this||t).options.zoomSnap:1;o&&(e=Math.round(e/o)*o);return Math.max(i,Math.min(n,e))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){removeClass((this||t)._mapPane,"leaflet-pan-anim");this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._trunc();if(true!==(e&&e.animate)&&!this.getSize().contains(i))return false;this.panBy(i,e);return true},_createAnimProxy:function(){var e=(this||t)._proxy=create$1("div","leaflet-proxy leaflet-zoom-animated");(this||t)._panes.mapPane.appendChild(e);this.on("zoomanim",(function(e){var i=yt,n=(this||t)._proxy.style[i];setTransform((this||t)._proxy,this.project(e.center,e.zoom),this.getZoomScale(e.zoom,1));n===(this||t)._proxy.style[i]&&(this||t)._animatingZoom&&this._onZoomTransitionEnd()}),this||t);this.on("load moveend",(this||t)._animMoveEnd,this||t);this._on("unload",(this||t)._destroyAnimProxy,this||t)},_destroyAnimProxy:function(){remove((this||t)._proxy);this.off("load moveend",(this||t)._animMoveEnd,this||t);delete(this||t)._proxy},_animMoveEnd:function(){var e=this.getCenter(),i=this.getZoom();setTransform((this||t)._proxy,this.project(e,i),this.getZoomScale(i,1))},_catchTransitionEnd:function(e){(this||t)._animatingZoom&&e.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!(this||t)._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(e,i,n){if((this||t)._animatingZoom)return true;n=n||{};if(!(this||t)._zoomAnimated||false===n.animate||this._nothingToAnimate()||Math.abs(i-(this||t)._zoom)>(this||t).options.zoomAnimationThreshold)return false;var o=this.getZoomScale(i),s=this._getCenterOffset(e)._divideBy(1-1/o);if(true!==n.animate&&!this.getSize().contains(s))return false;requestAnimFrame((function(){this._moveStart(true,n.noMoveStart||false)._animateZoom(e,i,true)}),this||t);return true},_animateZoom:function(e,i,n,o){if((this||t)._mapPane){if(n){(this||t)._animatingZoom=true;(this||t)._animateToCenter=e;(this||t)._animateToZoom=i;addClass((this||t)._mapPane,"leaflet-zoom-anim")}this.fire("zoomanim",{center:e,zoom:i,noUpdate:o});(this||t)._tempFireZoomEvent||((this||t)._tempFireZoomEvent=(this||t)._zoom!==(this||t)._animateToZoom);this._move((this||t)._animateToCenter,(this||t)._animateToZoom,void 0,true);setTimeout(bind((this||t)._onZoomTransitionEnd,this||t),250)}},_onZoomTransitionEnd:function(){if((this||t)._animatingZoom){(this||t)._mapPane&&removeClass((this||t)._mapPane,"leaflet-zoom-anim");(this||t)._animatingZoom=false;this._move((this||t)._animateToCenter,(this||t)._animateToZoom,void 0,true);(this||t)._tempFireZoomEvent&&this.fire("zoom");delete(this||t)._tempFireZoomEvent;this.fire("move");this._moveEnd(true)}}});function createMap(t,e){return new Et(t,e)}var At=Class.extend({options:{position:"topright"},initialize:function(e){setOptions(this||t,e)},getPosition:function(){return(this||t).options.position},setPosition:function(e){var i=(this||t)._map;i&&i.removeControl(this||t);(this||t).options.position=e;i&&i.addControl(this||t);return this||t},getContainer:function(){return(this||t)._container},addTo:function(e){this.remove();(this||t)._map=e;var i=(this||t)._container=this.onAdd(e),n=this.getPosition(),o=e._controlCorners[n];addClass(i,"leaflet-control");-1!==n.indexOf("bottom")?o.insertBefore(i,o.firstChild):o.appendChild(i);(this||t)._map.on("unload",(this||t).remove,this||t);return this||t},remove:function(){if(!(this||t)._map)return this||t;remove((this||t)._container);(this||t).onRemove&&this.onRemove((this||t)._map);(this||t)._map.off("unload",(this||t).remove,this||t);(this||t)._map=null;return this||t},_refocusOnMap:function(e){(this||t)._map&&e&&e.screenX>0&&e.screenY>0&&(this||t)._map.getContainer().focus()}});var control=function(t){return new At(t)};Et.include({addControl:function(e){e.addTo(this||t);return this||t},removeControl:function(e){e.remove();return this||t},_initControlPos:function(){var e=(this||t)._controlCorners={},i="leaflet-",n=(this||t)._controlContainer=create$1("div",i+"control-container",(this||t)._container);function createCorner(t,o){var s=i+t+" "+i+o;e[t+o]=create$1("div",s,n)}createCorner("top","left");createCorner("top","right");createCorner("bottom","left");createCorner("bottom","right")},_clearControlPos:function(){for(var e in(this||t)._controlCorners)remove((this||t)._controlCorners[e]);remove((this||t)._controlContainer);delete(this||t)._controlCorners;delete(this||t)._controlContainer}});var It=At.extend({options:{collapsed:true,position:"topright",autoZIndex:true,hideSingleBase:false,sortLayers:false,sortFunction:function(t,e,i,n){return i1;(this||t)._baseLayersList.style.display=e?"":"none"}(this||t)._separator.style.display=i&&e?"":"none";return this||t},_onLayerChange:function(e){(this||t)._handlingClick||this._update();var i=this._getLayer(stamp(e.target));var n=i.overlay?"add"===e.type?"overlayadd":"overlayremove":"add"===e.type?"baselayerchange":null;n&&(this||t)._map.fire(n,i)},_createRadioElement:function(t,e){var i='";var n=document.createElement("div");n.innerHTML=i;return n.firstChild},_addItem:function(e){var i,n=document.createElement("label"),o=(this||t)._map.hasLayer(e.layer);if(e.overlay){i=document.createElement("input");i.type="checkbox";i.className="leaflet-control-layers-selector";i.defaultChecked=o}else i=this._createRadioElement("leaflet-base-layers_"+stamp(this||t),o);(this||t)._layerControlInputs.push(i);i.layerId=stamp(e.layer);on(i,"click",(this||t)._onInputClick,this||t);var s=document.createElement("span");s.innerHTML=" "+e.name;var a=document.createElement("span");n.appendChild(a);a.appendChild(i);a.appendChild(s);var h=e.overlay?(this||t)._overlaysList:(this||t)._baseLayersList;h.appendChild(n);this._checkDisabledLayers();return n},_onInputClick:function(){if(!(this||t)._preventClick){var e,i,n=(this||t)._layerControlInputs;var o=[],s=[];(this||t)._handlingClick=true;for(var a=n.length-1;a>=0;a--){e=n[a];i=this._getLayer(e.layerId).layer;e.checked?o.push(i):e.checked||s.push(i)}for(a=0;a=0;s--){e=n[s];i=this._getLayer(e.layerId).layer;e.disabled=void 0!==i.options.minZoom&&oi.options.maxZoom}},_expandIfNotCollapsed:function(){(this||t)._map&&!(this||t).options.collapsed&&this.expand();return this||t},_expandSafely:function(){var e=(this||t)._section;(this||t)._preventClick=true;on(e,"click",preventDefault);this.expand();var i=this||t;setTimeout((function(){off(e,"click",preventDefault);i._preventClick=false}))}});var layers=function(t,e,i){return new It(t,e,i)};var Dt=At.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(e){var i="leaflet-control-zoom",n=create$1("div",i+" leaflet-bar"),o=(this||t).options;(this||t)._zoomInButton=this._createButton(o.zoomInText,o.zoomInTitle,i+"-in",n,(this||t)._zoomIn);(this||t)._zoomOutButton=this._createButton(o.zoomOutText,o.zoomOutTitle,i+"-out",n,(this||t)._zoomOut);this._updateDisabled();e.on("zoomend zoomlevelschange",(this||t)._updateDisabled,this||t);return n},onRemove:function(e){e.off("zoomend zoomlevelschange",(this||t)._updateDisabled,this||t)},disable:function(){(this||t)._disabled=true;this._updateDisabled();return this||t},enable:function(){(this||t)._disabled=false;this._updateDisabled();return this||t},_zoomIn:function(e){!(this||t)._disabled&&(this||t)._map._zoom<(this||t)._map.getMaxZoom()&&(this||t)._map.zoomIn((this||t)._map.options.zoomDelta*(e.shiftKey?3:1))},_zoomOut:function(e){!(this||t)._disabled&&(this||t)._map._zoom>(this||t)._map.getMinZoom()&&(this||t)._map.zoomOut((this||t)._map.options.zoomDelta*(e.shiftKey?3:1))},_createButton:function(e,i,n,o,s){var a=create$1("a",n,o);a.innerHTML=e;a.href="#";a.title=i;a.setAttribute("role","button");a.setAttribute("aria-label",i);disableClickPropagation(a);on(a,"click",stop);on(a,"click",s,this||t);on(a,"click",(this||t)._refocusOnMap,this||t);return a},_updateDisabled:function(){var e=(this||t)._map,i="leaflet-disabled";removeClass((this||t)._zoomInButton,i);removeClass((this||t)._zoomOutButton,i);(this||t)._zoomInButton.setAttribute("aria-disabled","false");(this||t)._zoomOutButton.setAttribute("aria-disabled","false");if((this||t)._disabled||e._zoom===e.getMinZoom()){addClass((this||t)._zoomOutButton,i);(this||t)._zoomOutButton.setAttribute("aria-disabled","true")}if((this||t)._disabled||e._zoom===e.getMaxZoom()){addClass((this||t)._zoomInButton,i);(this||t)._zoomInButton.setAttribute("aria-disabled","true")}}});Et.mergeOptions({zoomControl:true});Et.addInitHook((function(){if((this||t).options.zoomControl){(this||t).zoomControl=new Dt;this.addControl((this||t).zoomControl)}}));var zoom=function(t){return new Dt(t)};var Nt=At.extend({options:{position:"bottomleft",maxWidth:100,metric:true,imperial:true},onAdd:function(e){var i="leaflet-control-scale",n=create$1("div",i),o=(this||t).options;this._addScales(o,i+"-line",n);e.on(o.updateWhenIdle?"moveend":"move",(this||t)._update,this||t);e.whenReady((this||t)._update,this||t);return n},onRemove:function(e){e.off((this||t).options.updateWhenIdle?"moveend":"move",(this||t)._update,this||t)},_addScales:function(e,i,n){e.metric&&((this||t)._mScale=create$1("div",i,n));e.imperial&&((this||t)._iScale=create$1("div",i,n))},_update:function(){var e=(this||t)._map,i=e.getSize().y/2;var n=e.distance(e.containerPointToLatLng([0,i]),e.containerPointToLatLng([(this||t).options.maxWidth,i]));this._updateScales(n)},_updateScales:function(e){(this||t).options.metric&&e&&this._updateMetric(e);(this||t).options.imperial&&e&&this._updateImperial(e)},_updateMetric:function(e){var i=this._getRoundNum(e),n=i<1e3?i+" m":i/1e3+" km";this._updateScale((this||t)._mScale,n,i/e)},_updateImperial:function(e){var i,n,o,s=3.2808399*e;if(s>5280){i=s/5280;n=this._getRoundNum(i);this._updateScale((this||t)._iScale,n+" mi",n/i)}else{o=this._getRoundNum(s);this._updateScale((this||t)._iScale,o+" ft",o/s)}},_updateScale:function(e,i,n){e.style.width=Math.round((this||t).options.maxWidth*n)+"px";e.innerHTML=i},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1;return e*i}});var scale=function(t){return new Nt(t)};var Ft='';var Rt=At.extend({options:{position:"bottomright",prefix:''+(lt.inlineSvg?Ft+" ":"")+"Leaflet"},initialize:function(e){setOptions(this||t,e);(this||t)._attributions={}},onAdd:function(e){e.attributionControl=this||t;(this||t)._container=create$1("div","leaflet-control-attribution");disableClickPropagation((this||t)._container);for(var i in e._layers)e._layers[i].getAttribution&&this.addAttribution(e._layers[i].getAttribution());this._update();e.on("layeradd",(this||t)._addAttribution,this||t);return(this||t)._container},onRemove:function(e){e.off("layeradd",(this||t)._addAttribution,this||t)},_addAttribution:function(e){if(e.layer.getAttribution){this.addAttribution(e.layer.getAttribution());e.layer.once("remove",(function(){this.removeAttribution(e.layer.getAttribution())}),this||t)}},setPrefix:function(e){(this||t).options.prefix=e;this._update();return this||t},addAttribution:function(e){if(!e)return this||t;(this||t)._attributions[e]||((this||t)._attributions[e]=0);(this||t)._attributions[e]++;this._update();return this||t},removeAttribution:function(e){if(!e)return this||t;if((this||t)._attributions[e]){(this||t)._attributions[e]--;this._update()}return this||t},_update:function(){if((this||t)._map){var e=[];for(var i in(this||t)._attributions)(this||t)._attributions[i]&&e.push(i);var n=[];(this||t).options.prefix&&n.push((this||t).options.prefix);e.length&&n.push(e.join(", "));(this||t)._container.innerHTML=n.join(' ')}}});Et.mergeOptions({attributionControl:true});Et.addInitHook((function(){(this||t).options.attributionControl&&(new Rt).addTo(this||t)}));var attribution=function(t){return new Rt(t)};At.Layers=It;At.Zoom=Dt;At.Scale=Nt;At.Attribution=Rt;control.layers=layers;control.zoom=zoom;control.scale=scale;control.attribution=attribution;var jt=Class.extend({initialize:function(e){(this||t)._map=e},enable:function(){if((this||t)._enabled)return this||t;(this||t)._enabled=true;this.addHooks();return this||t},disable:function(){if(!(this||t)._enabled)return this||t;(this||t)._enabled=false;this.removeHooks();return this||t},enabled:function(){return!!(this||t)._enabled}});jt.addTo=function(e,i){e.addHandler(i,this||t);return this||t};var Wt={Events:p};var Ht=lt.touch?"touchstart mousedown":"mousedown";var qt=f.extend({options:{clickTolerance:3},initialize:function(e,i,n,o){setOptions(this||t,o);(this||t)._element=e;(this||t)._dragStartTarget=i||e;(this||t)._preventOutline=n},enable:function(){if(!(this||t)._enabled){on((this||t)._dragStartTarget,Ht,(this||t)._onDown,this||t);(this||t)._enabled=true}},disable:function(){if((this||t)._enabled){qt._dragging===(this||t)&&this.finishDrag(true);off((this||t)._dragStartTarget,Ht,(this||t)._onDown,this||t);(this||t)._enabled=false;(this||t)._moved=false}},_onDown:function(e){if((this||t)._enabled){(this||t)._moved=false;if(!hasClass((this||t)._element,"leaflet-zoom-anim"))if(e.touches&&1!==e.touches.length)qt._dragging===(this||t)&&this.finishDrag();else if(!(qt._dragging||e.shiftKey||1!==e.which&&1!==e.button&&!e.touches)){qt._dragging=this||t;(this||t)._preventOutline&&preventOutline((this||t)._element);disableImageDrag();xt();if(!(this||t)._moving){this.fire("down");var i=e.touches?e.touches[0]:e,n=getSizedParentNode((this||t)._element);(this||t)._startPoint=new Point(i.clientX,i.clientY);(this||t)._startPos=getPosition((this||t)._element);(this||t)._parentScale=getScale(n);var o="mousedown"===e.type;on(document,o?"mousemove":"touchmove",(this||t)._onMove,this||t);on(document,o?"mouseup":"touchend touchcancel",(this||t)._onUp,this||t)}}}},_onMove:function(e){if((this||t)._enabled)if(e.touches&&e.touches.length>1)(this||t)._moved=true;else{var i=e.touches&&1===e.touches.length?e.touches[0]:e,n=new Point(i.clientX,i.clientY)._subtract((this||t)._startPoint);if((n.x||n.y)&&!(Math.abs(n.x)+Math.abs(n.y)<(this||t).options.clickTolerance)){n.x/=(this||t)._parentScale.x;n.y/=(this||t)._parentScale.y;preventDefault(e);if(!(this||t)._moved){this.fire("dragstart");(this||t)._moved=true;addClass(document.body,"leaflet-dragging");(this||t)._lastTarget=e.target||e.srcElement;window.SVGElementInstance&&(this||t)._lastTarget instanceof window.SVGElementInstance&&((this||t)._lastTarget=(this||t)._lastTarget.correspondingUseElement);addClass((this||t)._lastTarget,"leaflet-drag-target")}(this||t)._newPos=(this||t)._startPos.add(n);(this||t)._moving=true;(this||t)._lastEvent=e;this._updatePosition()}}},_updatePosition:function(){var e={originalEvent:(this||t)._lastEvent};this.fire("predrag",e);setPosition((this||t)._element,(this||t)._newPos);this.fire("drag",e)},_onUp:function(){(this||t)._enabled&&this.finishDrag()},finishDrag:function(e){removeClass(document.body,"leaflet-dragging");if((this||t)._lastTarget){removeClass((this||t)._lastTarget,"leaflet-drag-target");(this||t)._lastTarget=null}off(document,"mousemove touchmove",(this||t)._onMove,this||t);off(document,"mouseup touchend touchcancel",(this||t)._onUp,this||t);enableImageDrag();wt();var i=(this||t)._moved&&(this||t)._moving;(this||t)._moving=false;qt._dragging=false;i&&this.fire("dragend",{noInertia:e,distance:(this||t)._newPos.distanceTo((this||t)._startPos)})}});function clipPolygon(t,e,i){var n,o,s,a,h,l,c,d,_,p=[1,4,2,8];for(o=0,c=t.length;ol){s=a;l=h}}if(l>i){e[s]=1;_simplifyDPStep(t,e,i,n,s);_simplifyDPStep(t,e,i,s,o)}}function _reducePoints(t,e){var i=[t[0]];for(var n=1,o=0,s=t.length;ne){i.push(t[n]);o=n}oe.max.x&&(i|=2);t.ye.max.y&&(i|=8);return i}function _sqDist(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n}function _sqClosestPointOnSegment(t,e,i,n){var o,s=e.x,a=e.y,h=i.x-s,l=i.y-a,c=h*h+l*l;if(c>0){o=((t.x-s)*h+(t.y-a)*l)/c;if(o>1){s=i.x;a=i.y}else if(o>0){s+=h*o;a+=l*o}}h=t.x-s;l=t.y-a;return n?h*h+l*l:new Point(s,a)}function isFlat(t){return!a(t[0])||"object"!==typeof t[0][0]&&"undefined"!==typeof t[0][0]}function _flat(t){console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead.");return isFlat(t)}function polylineCenter(t,e){var i,n,o,s,a,h,l,c;if(!t||0===t.length)throw new Error("latlngs not passed");if(!isFlat(t)){console.warn("latlngs are not flat! Only the first ring will be used");t=t[0]}var d=toLatLng([0,0]);var _=toLatLngBounds(t);var p=_.getNorthWest().distanceTo(_.getSouthWest())*_.getNorthEast().distanceTo(_.getNorthWest());p<1700&&(d=centroid(t));var f=t.length;var m=[];for(i=0;in){l=(s-n)/o;c=[h.x-l*(h.x-a.x),h.y-l*(h.y-a.y)];break}}var v=e.unproject(toPoint(c));return toLatLng([v.lat+d.lat,v.lng+d.lng])}var Gt={__proto__:null,simplify:simplify,pointToSegmentDistance:pointToSegmentDistance,closestPointOnSegment:closestPointOnSegment,clipSegment:clipSegment,_getEdgeIntersection:_getEdgeIntersection,_getBitCode:_getBitCode,_sqClosestPointOnSegment:_sqClosestPointOnSegment,isFlat:isFlat,_flat:_flat,polylineCenter:polylineCenter};var $t={project:function(t){return new Point(t.lng,t.lat)},unproject:function(t){return new LatLng(t.y,t.x)},bounds:new Bounds([-180,-90],[180,90])};var Kt={R:6378137,R_MINOR:6356752.314245179,bounds:new Bounds([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(e){var i=Math.PI/180,n=(this||t).R,o=e.lat*i,s=(this||t).R_MINOR/n,a=Math.sqrt(1-s*s),h=a*Math.sin(o);var l=Math.tan(Math.PI/4-o/2)/Math.pow((1-h)/(1+h),a/2);o=-n*Math.log(Math.max(l,1e-10));return new Point(e.lng*i*n,o)},unproject:function(e){var i=180/Math.PI,n=(this||t).R,o=(this||t).R_MINOR/n,s=Math.sqrt(1-o*o),a=Math.exp(-e.y/n),h=Math.PI/2-2*Math.atan(a);for(var l,c=0,d=.1;c<15&&Math.abs(d)>1e-7;c++){l=s*Math.sin(h);l=Math.pow((1-l)/(1+l),s/2);d=Math.PI/2-2*Math.atan(a*l)-h;h+=d}return new LatLng(h*i,e.x*i/n)}};var Yt={__proto__:null,LonLat:$t,Mercator:Kt,SphericalMercator:P};var Jt=extend({},v,{code:"EPSG:3395",projection:Kt,transformation:function(){var t=.5/(Math.PI*Kt.R);return toTransformation(t,.5,-t,.5)}()});var Xt=extend({},v,{code:"EPSG:4326",projection:$t,transformation:toTransformation(1/180,1,-1/180,.5)});var Qt=extend({},g,{projection:$t,transformation:toTransformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,e){var i=e.lng-t.lng,n=e.lat-t.lat;return Math.sqrt(i*i+n*n)},infinite:true});g.Earth=v;g.EPSG3395=Jt;g.EPSG3857=x;g.EPSG900913=b;g.EPSG4326=Xt;g.Simple=Qt;var te=f.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:true},addTo:function(e){e.addLayer(this||t);return this||t},remove:function(){return this.removeFrom((this||t)._map||(this||t)._mapToAdd)},removeFrom:function(e){e&&e.removeLayer(this||t);return this||t},getPane:function(e){return(this||t)._map.getPane(e?(this||t).options[e]||e:(this||t).options.pane)},addInteractiveTarget:function(e){(this||t)._map._targets[stamp(e)]=this||t;return this||t},removeInteractiveTarget:function(e){delete(this||t)._map._targets[stamp(e)];return this||t},getAttribution:function(){return(this||t).options.attribution},_layerAdd:function(e){var i=e.target;if(i.hasLayer(this||t)){(this||t)._map=i;(this||t)._zoomAnimated=i._zoomAnimated;if((this||t).getEvents){var n=this.getEvents();i.on(n,this||t);this.once("remove",(function(){i.off(n,this||t)}),this||t)}this.onAdd(i);this.fire("add");i.fire("layeradd",{layer:this||t})}}});Et.include({addLayer:function(e){if(!e._layerAdd)throw new Error("The provided object is not a Layer.");var i=stamp(e);if((this||t)._layers[i])return this||t;(this||t)._layers[i]=e;e._mapToAdd=this||t;e.beforeAdd&&e.beforeAdd(this||t);this.whenReady(e._layerAdd,e);return this||t},removeLayer:function(e){var i=stamp(e);if(!(this||t)._layers[i])return this||t;(this||t)._loaded&&e.onRemove(this||t);delete(this||t)._layers[i];if((this||t)._loaded){this.fire("layerremove",{layer:e});e.fire("remove")}e._map=e._mapToAdd=null;return this||t},hasLayer:function(e){return stamp(e)in(this||t)._layers},eachLayer:function(e,i){for(var n in(this||t)._layers)e.call(i,(this||t)._layers[n]);return this||t},_addLayers:function(t){t=t?a(t)?t:[t]:[];for(var e=0,i=t.length;e(this||t)._layersMaxZoom&&this.setZoom((this||t)._layersMaxZoom);void 0===(this||t).options.minZoom&&(this||t)._layersMinZoom&&this.getZoom()<(this||t)._layersMinZoom&&this.setZoom((this||t)._layersMinZoom)}});var ee=te.extend({initialize:function(e,i){setOptions(this||t,i);(this||t)._layers={};var n,o;if(e)for(n=0,o=e.length;n=2&&i[0]instanceof LatLng&&i[0].equals(i[n-1])&&i.pop();return i},_setLatLngs:function(e){ue.prototype._setLatLngs.call(this||t,e);isFlat((this||t)._latlngs)&&((this||t)._latlngs=[(this||t)._latlngs])},_defaultShape:function(){return isFlat((this||t)._latlngs[0])?(this||t)._latlngs[0]:(this||t)._latlngs[0][0]},_clipPoints:function(){var e=(this||t)._renderer._bounds,i=(this||t).options.weight,n=new Point(i,i);e=new Bounds(e.min.subtract(n),e.max.add(n));(this||t)._parts=[];if((this||t)._pxBounds&&(this||t)._pxBounds.intersects(e))if((this||t).options.noClip)(this||t)._parts=(this||t)._rings;else for(var o,s=0,a=(this||t)._rings.length;se.y!==o.y>e.y&&e.x<(o.x-n.x)*(e.y-n.y)/(o.y-n.y)+n.x&&(d=!d)}}return d||ue.prototype._containsPoint.call(this||t,e,true)}});function polygon(t,e){return new ce(t,e)}var de=ie.extend({initialize:function(e,i){setOptions(this||t,i);(this||t)._layers={};e&&this.addData(e)},addData:function(e){var i,n,o,s=a(e)?e:e.features;if(s){for(i=0,n=s.length;i0&&o.push(o[0].slice());return o}function getFeature(t,e){return t.feature?extend({},t.feature,{geometry:e}):asFeature(e)}function asFeature(t){return"Feature"===t.type||"FeatureCollection"===t.type?t:{type:"Feature",properties:{},geometry:t}}var _e={toGeoJSON:function(e){return getFeature(this||t,{type:"Point",coordinates:latLngToCoords(this.getLatLng(),e)})}};ae.include(_e);le.include(_e);he.include(_e);ue.include({toGeoJSON:function(e){var i=!isFlat((this||t)._latlngs);var n=latLngsToCoords((this||t)._latlngs,i?1:0,false,e);return getFeature(this||t,{type:(i?"Multi":"")+"LineString",coordinates:n})}});ce.include({toGeoJSON:function(e){var i=!isFlat((this||t)._latlngs),n=i&&!isFlat((this||t)._latlngs[0]);var o=latLngsToCoords((this||t)._latlngs,n?2:i?1:0,true,e);i||(o=[o]);return getFeature(this||t,{type:(n?"Multi":"")+"Polygon",coordinates:o})}});ee.include({toMultiPoint:function(e){var i=[];this.eachLayer((function(t){i.push(t.toGeoJSON(e).geometry.coordinates)}));return getFeature(this||t,{type:"MultiPoint",coordinates:i})},toGeoJSON:function(e){var i=(this||t).feature&&(this||t).feature.geometry&&(this||t).feature.geometry.type;if("MultiPoint"===i)return this.toMultiPoint(e);var n="GeometryCollection"===i,o=[];this.eachLayer((function(t){if(t.toGeoJSON){var i=t.toGeoJSON(e);if(n)o.push(i.geometry);else{var s=asFeature(i);"FeatureCollection"===s.type?o.push.apply(o,s.features):o.push(s)}}}));return n?getFeature(this||t,{geometries:o,type:"GeometryCollection"}):{type:"FeatureCollection",features:o}}});function geoJSON(t,e){return new de(t,e)}var pe=geoJSON;var fe=te.extend({options:{opacity:1,alt:"",interactive:false,crossOrigin:false,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(e,i,n){(this||t)._url=e;(this||t)._bounds=toLatLngBounds(i);setOptions(this||t,n)},onAdd:function(){if(!(this||t)._image){this._initImage();(this||t).options.opacity<1&&this._updateOpacity()}if((this||t).options.interactive){addClass((this||t)._image,"leaflet-interactive");this.addInteractiveTarget((this||t)._image)}this.getPane().appendChild((this||t)._image);this._reset()},onRemove:function(){remove((this||t)._image);(this||t).options.interactive&&this.removeInteractiveTarget((this||t)._image)},setOpacity:function(e){(this||t).options.opacity=e;(this||t)._image&&this._updateOpacity();return this||t},setStyle:function(e){e.opacity&&this.setOpacity(e.opacity);return this||t},bringToFront:function(){(this||t)._map&&toFront((this||t)._image);return this||t},bringToBack:function(){(this||t)._map&&toBack((this||t)._image);return this||t},setUrl:function(e){(this||t)._url=e;(this||t)._image&&((this||t)._image.src=e);return this||t},setBounds:function(e){(this||t)._bounds=toLatLngBounds(e);(this||t)._map&&this._reset();return this||t},getEvents:function(){var e={zoom:(this||t)._reset,viewreset:(this||t)._reset};(this||t)._zoomAnimated&&(e.zoomanim=(this||t)._animateZoom);return e},setZIndex:function(e){(this||t).options.zIndex=e;this._updateZIndex();return this||t},getBounds:function(){return(this||t)._bounds},getElement:function(){return(this||t)._image},_initImage:function(){var e="IMG"===(this||t)._url.tagName;var i=(this||t)._image=e?(this||t)._url:create$1("img");addClass(i,"leaflet-image-layer");(this||t)._zoomAnimated&&addClass(i,"leaflet-zoom-animated");(this||t).options.className&&addClass(i,(this||t).options.className);i.onselectstart=falseFn;i.onmousemove=falseFn;i.onload=bind((this||t).fire,this||t,"load");i.onerror=bind((this||t)._overlayOnError,this||t,"error");((this||t).options.crossOrigin||""===(this||t).options.crossOrigin)&&(i.crossOrigin=true===(this||t).options.crossOrigin?"":(this||t).options.crossOrigin);(this||t).options.zIndex&&this._updateZIndex();if(e)(this||t)._url=i.src;else{i.src=(this||t)._url;i.alt=(this||t).options.alt}},_animateZoom:function(e){var i=(this||t)._map.getZoomScale(e.zoom),n=(this||t)._map._latLngBoundsToNewLayerBounds((this||t)._bounds,e.zoom,e.center).min;setTransform((this||t)._image,n,i)},_reset:function(){var e=(this||t)._image,i=new Bounds((this||t)._map.latLngToLayerPoint((this||t)._bounds.getNorthWest()),(this||t)._map.latLngToLayerPoint((this||t)._bounds.getSouthEast())),n=i.getSize();setPosition(e,i.min);e.style.width=n.x+"px";e.style.height=n.y+"px"},_updateOpacity:function(){setOpacity((this||t)._image,(this||t).options.opacity)},_updateZIndex:function(){(this||t)._image&&void 0!==(this||t).options.zIndex&&null!==(this||t).options.zIndex&&((this||t)._image.style.zIndex=(this||t).options.zIndex)},_overlayOnError:function(){this.fire("error");var e=(this||t).options.errorOverlayUrl;if(e&&(this||t)._url!==e){(this||t)._url=e;(this||t)._image.src=e}},getCenter:function(){return(this||t)._bounds.getCenter()}});var imageOverlay=function(t,e,i){return new fe(t,e,i)};var me=fe.extend({options:{autoplay:true,loop:true,keepAspectRatio:true,muted:false,playsInline:true},_initImage:function(){var e="VIDEO"===(this||t)._url.tagName;var i=(this||t)._image=e?(this||t)._url:create$1("video");addClass(i,"leaflet-image-layer");(this||t)._zoomAnimated&&addClass(i,"leaflet-zoom-animated");(this||t).options.className&&addClass(i,(this||t).options.className);i.onselectstart=falseFn;i.onmousemove=falseFn;i.onloadeddata=bind((this||t).fire,this||t,"load");if(e){var n=i.getElementsByTagName("source");var o=[];for(var s=0;s0?o:[i.src]}else{a((this||t)._url)||((this||t)._url=[(this||t)._url]);!(this||t).options.keepAspectRatio&&Object.prototype.hasOwnProperty.call(i.style,"objectFit")&&(i.style.objectFit="fill");i.autoplay=!!(this||t).options.autoplay;i.loop=!!(this||t).options.loop;i.muted=!!(this||t).options.muted;i.playsInline=!!(this||t).options.playsInline;for(var h=0;h<(this||t)._url.length;h++){var l=create$1("source");l.src=(this||t)._url[h];i.appendChild(l)}}}});function videoOverlay(t,e,i){return new me(t,e,i)}var ge=fe.extend({_initImage:function(){var e=(this||t)._image=(this||t)._url;addClass(e,"leaflet-image-layer");(this||t)._zoomAnimated&&addClass(e,"leaflet-zoom-animated");(this||t).options.className&&addClass(e,(this||t).options.className);e.onselectstart=falseFn;e.onmousemove=falseFn}});function svgOverlay(t,e,i){return new ge(t,e,i)}var ve=te.extend({options:{interactive:false,offset:[0,0],className:"",pane:void 0,content:""},initialize:function(e,i){if(e&&(e instanceof LatLng||a(e))){(this||t)._latlng=toLatLng(e);setOptions(this||t,i)}else{setOptions(this||t,e);(this||t)._source=i}(this||t).options.content&&((this||t)._content=(this||t).options.content)},openOn:function(e){e=arguments.length?e:(this||t)._source._map;e.hasLayer(this||t)||e.addLayer(this||t);return this||t},close:function(){(this||t)._map&&(this||t)._map.removeLayer(this||t);return this||t},toggle:function(e){if((this||t)._map)this.close();else{arguments.length?(this||t)._source=e:e=(this||t)._source;this._prepareOpen();this.openOn(e._map)}return this||t},onAdd:function(e){(this||t)._zoomAnimated=e._zoomAnimated;(this||t)._container||this._initLayout();e._fadeAnimated&&setOpacity((this||t)._container,0);clearTimeout((this||t)._removeTimeout);this.getPane().appendChild((this||t)._container);this.update();e._fadeAnimated&&setOpacity((this||t)._container,1);this.bringToFront();if((this||t).options.interactive){addClass((this||t)._container,"leaflet-interactive");this.addInteractiveTarget((this||t)._container)}},onRemove:function(e){if(e._fadeAnimated){setOpacity((this||t)._container,0);(this||t)._removeTimeout=setTimeout(bind(remove,void 0,(this||t)._container),200)}else remove((this||t)._container);if((this||t).options.interactive){removeClass((this||t)._container,"leaflet-interactive");this.removeInteractiveTarget((this||t)._container)}},getLatLng:function(){return(this||t)._latlng},setLatLng:function(e){(this||t)._latlng=toLatLng(e);if((this||t)._map){this._updatePosition();this._adjustPan()}return this||t},getContent:function(){return(this||t)._content},setContent:function(e){(this||t)._content=e;this.update();return this||t},getElement:function(){return(this||t)._container},update:function(){if((this||t)._map){(this||t)._container.style.visibility="hidden";this._updateContent();this._updateLayout();this._updatePosition();(this||t)._container.style.visibility="";this._adjustPan()}},getEvents:function(){var e={zoom:(this||t)._updatePosition,viewreset:(this||t)._updatePosition};(this||t)._zoomAnimated&&(e.zoomanim=(this||t)._animateZoom);return e},isOpen:function(){return!!(this||t)._map&&(this||t)._map.hasLayer(this||t)},bringToFront:function(){(this||t)._map&&toFront((this||t)._container);return this||t},bringToBack:function(){(this||t)._map&&toBack((this||t)._container);return this||t},_prepareOpen:function(e){var i=(this||t)._source;if(!i._map)return false;if(i instanceof ie){i=null;var n=(this||t)._source._layers;for(var o in n)if(n[o]._map){i=n[o];break}if(!i)return false;(this||t)._source=i}if(!e)if(i.getCenter)e=i.getCenter();else if(i.getLatLng)e=i.getLatLng();else{if(!i.getBounds)throw new Error("Unable to get source layer LatLng.");e=i.getBounds().getCenter()}this.setLatLng(e);(this||t)._map&&this.update();return true},_updateContent:function(){if((this||t)._content){var e=(this||t)._contentNode;var i="function"===typeof(this||t)._content?this._content((this||t)._source||this||t):(this||t)._content;if("string"===typeof i)e.innerHTML=i;else{while(e.hasChildNodes())e.removeChild(e.firstChild);e.appendChild(i)}this.fire("contentupdate")}},_updatePosition:function(){if((this||t)._map){var e=(this||t)._map.latLngToLayerPoint((this||t)._latlng),i=toPoint((this||t).options.offset),n=this._getAnchor();(this||t)._zoomAnimated?setPosition((this||t)._container,e.add(n)):i=i.add(e).add(n);var o=(this||t)._containerBottom=-i.y,s=(this||t)._containerLeft=-Math.round((this||t)._containerWidth/2)+i.x;(this||t)._container.style.bottom=o+"px";(this||t)._container.style.left=s+"px"}},_getAnchor:function(){return[0,0]}});Et.include({_initOverlay:function(t,e,i,n){var o=e;o instanceof t||(o=new t(n).setContent(e));i&&o.setLatLng(i);return o}});te.include({_initOverlay:function(e,i,n,o){var s=n;if(s instanceof e){setOptions(s,o);s._source=this||t}else{s=i&&!o?i:new e(o,this||t);s.setContent(n)}return s}});var ye=ve.extend({options:{pane:"popupPane",offset:[0,7],maxWidth:300,minWidth:50,maxHeight:null,autoPan:true,autoPanPaddingTopLeft:null,autoPanPaddingBottomRight:null,autoPanPadding:[5,5],keepInView:false,closeButton:true,autoClose:true,closeOnEscapeKey:true,className:""},openOn:function(e){e=arguments.length?e:(this||t)._source._map;!e.hasLayer(this||t)&&e._popup&&e._popup.options.autoClose&&e.removeLayer(e._popup);e._popup=this||t;return ve.prototype.openOn.call(this||t,e)},onAdd:function(e){ve.prototype.onAdd.call(this||t,e);e.fire("popupopen",{popup:this||t});if((this||t)._source){(this||t)._source.fire("popupopen",{popup:this||t},true);(this||t)._source instanceof re||(this||t)._source.on("preclick",stopPropagation)}},onRemove:function(e){ve.prototype.onRemove.call(this||t,e);e.fire("popupclose",{popup:this||t});if((this||t)._source){(this||t)._source.fire("popupclose",{popup:this||t},true);(this||t)._source instanceof re||(this||t)._source.off("preclick",stopPropagation)}},getEvents:function(){var e=ve.prototype.getEvents.call(this||t);(void 0!==(this||t).options.closeOnClick?(this||t).options.closeOnClick:(this||t)._map.options.closePopupOnClick)&&(e.preclick=(this||t).close);(this||t).options.keepInView&&(e.moveend=(this||t)._adjustPan);return e},_initLayout:function(){var e="leaflet-popup",i=(this||t)._container=create$1("div",e+" "+((this||t).options.className||"")+" leaflet-zoom-animated");var n=(this||t)._wrapper=create$1("div",e+"-content-wrapper",i);(this||t)._contentNode=create$1("div",e+"-content",n);disableClickPropagation(i);disableScrollPropagation((this||t)._contentNode);on(i,"contextmenu",stopPropagation);(this||t)._tipContainer=create$1("div",e+"-tip-container",i);(this||t)._tip=create$1("div",e+"-tip",(this||t)._tipContainer);if((this||t).options.closeButton){var o=(this||t)._closeButton=create$1("a",e+"-close-button",i);o.setAttribute("role","button");o.setAttribute("aria-label","Close popup");o.href="#close";o.innerHTML='';on(o,"click",(function(t){preventDefault(t);this.close()}),this||t)}},_updateLayout:function(){var e=(this||t)._contentNode,i=e.style;i.width="";i.whiteSpace="nowrap";var n=e.offsetWidth;n=Math.min(n,(this||t).options.maxWidth);n=Math.max(n,(this||t).options.minWidth);i.width=n+1+"px";i.whiteSpace="";i.height="";var o=e.offsetHeight,s=(this||t).options.maxHeight,a="leaflet-popup-scrolled";if(s&&o>s){i.height=s+"px";addClass(e,a)}else removeClass(e,a);(this||t)._containerWidth=(this||t)._container.offsetWidth},_animateZoom:function(e){var i=(this||t)._map._latLngToNewLayerPoint((this||t)._latlng,e.zoom,e.center),n=this._getAnchor();setPosition((this||t)._container,i.add(n))},_adjustPan:function(){if((this||t).options.autoPan){(this||t)._map._panAnim&&(this||t)._map._panAnim.stop();if((this||t)._autopanning)(this||t)._autopanning=false;else{var e=(this||t)._map,i=parseInt(getStyle((this||t)._container,"marginBottom"),10)||0,n=(this||t)._container.offsetHeight+i,o=(this||t)._containerWidth,s=new Point((this||t)._containerLeft,-n-(this||t)._containerBottom);s._add(getPosition((this||t)._container));var a=e.layerPointToContainerPoint(s),h=toPoint((this||t).options.autoPanPadding),l=toPoint((this||t).options.autoPanPaddingTopLeft||h),c=toPoint((this||t).options.autoPanPaddingBottomRight||h),d=e.getSize(),_=0,p=0;a.x+o+c.x>d.x&&(_=a.x+o-d.x+c.x);a.x-_-l.x<0&&(_=a.x-l.x);a.y+n+c.y>d.y&&(p=a.y+n-d.y+c.y);a.y-p-l.y<0&&(p=a.y-l.y);if(_||p){(this||t).options.keepInView&&((this||t)._autopanning=true);e.fire("autopanstart").panBy([_,p])}}}},_getAnchor:function(){return toPoint((this||t)._source&&(this||t)._source._getPopupAnchor?(this||t)._source._getPopupAnchor():[0,0])}});var popup=function(t,e){return new ye(t,e)};Et.mergeOptions({closePopupOnClick:true});Et.include({openPopup:function(e,i,n){this._initOverlay(ye,e,i,n).openOn(this||t);return this||t},closePopup:function(e){e=arguments.length?e:(this||t)._popup;e&&e.close();return this||t}});te.include({bindPopup:function(e,i){(this||t)._popup=this._initOverlay(ye,(this||t)._popup,e,i);if(!(this||t)._popupHandlersAdded){this.on({click:(this||t)._openPopup,keypress:(this||t)._onKeyPress,remove:(this||t).closePopup,move:(this||t)._movePopup});(this||t)._popupHandlersAdded=true}return this||t},unbindPopup:function(){if((this||t)._popup){this.off({click:(this||t)._openPopup,keypress:(this||t)._onKeyPress,remove:(this||t).closePopup,move:(this||t)._movePopup});(this||t)._popupHandlersAdded=false;(this||t)._popup=null}return this||t},openPopup:function(e){if((this||t)._popup){(this||t)instanceof ie||((this||t)._popup._source=this||t);(this||t)._popup._prepareOpen(e||(this||t)._latlng)&&(this||t)._popup.openOn((this||t)._map)}return this||t},closePopup:function(){(this||t)._popup&&(this||t)._popup.close();return this||t},togglePopup:function(){(this||t)._popup&&(this||t)._popup.toggle(this||t);return this||t},isPopupOpen:function(){return!!(this||t)._popup&&(this||t)._popup.isOpen()},setPopupContent:function(e){(this||t)._popup&&(this||t)._popup.setContent(e);return this||t},getPopup:function(){return(this||t)._popup},_openPopup:function(e){if((this||t)._popup&&(this||t)._map){stop(e);var i=e.layer||e.target;if((this||t)._popup._source!==i||i instanceof re){(this||t)._popup._source=i;this.openPopup(e.latlng)}else(this||t)._map.hasLayer((this||t)._popup)?this.closePopup():this.openPopup(e.latlng)}},_movePopup:function(e){(this||t)._popup.setLatLng(e.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var Le=ve.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:false,sticky:false,opacity:.9},onAdd:function(e){ve.prototype.onAdd.call(this||t,e);this.setOpacity((this||t).options.opacity);e.fire("tooltipopen",{tooltip:this||t});if((this||t)._source){this.addEventParent((this||t)._source);(this||t)._source.fire("tooltipopen",{tooltip:this||t},true)}},onRemove:function(e){ve.prototype.onRemove.call(this||t,e);e.fire("tooltipclose",{tooltip:this||t});if((this||t)._source){this.removeEventParent((this||t)._source);(this||t)._source.fire("tooltipclose",{tooltip:this||t},true)}},getEvents:function(){var e=ve.prototype.getEvents.call(this||t);(this||t).options.permanent||(e.preclick=(this||t).close);return e},_initLayout:function(){var e="leaflet-tooltip",i=e+" "+((this||t).options.className||"")+" leaflet-zoom-"+((this||t)._zoomAnimated?"animated":"hide");(this||t)._contentNode=(this||t)._container=create$1("div",i);(this||t)._container.setAttribute("role","tooltip");(this||t)._container.setAttribute("id","leaflet-tooltip-"+stamp(this||t))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(e){var i,n,o=(this||t)._map,s=(this||t)._container,a=o.latLngToContainerPoint(o.getCenter()),h=o.layerPointToContainerPoint(e),l=(this||t).options.direction,c=s.offsetWidth,d=s.offsetHeight,_=toPoint((this||t).options.offset),p=this._getAnchor();if("top"===l){i=c/2;n=d}else if("bottom"===l){i=c/2;n=0}else if("center"===l){i=c/2;n=d/2}else if("right"===l){i=0;n=d/2}else if("left"===l){i=c;n=d/2}else if(h.x(this||t).options.maxZoom||n<(this||t).options.minZoom)this._removeAllTiles();else{for(e in(this||t)._tiles){i=(this||t)._tiles[e];i.retain=i.current}for(e in(this||t)._tiles){i=(this||t)._tiles[e];if(i.current&&!i.active){var o=i.coords;this._retainParent(o.x,o.y,o.z,o.z-5)||this._retainChildren(o.x,o.y,o.z,o.z+2)}}for(e in(this||t)._tiles)(this||t)._tiles[e].retain||this._removeTile(e)}}},_removeTilesAtZoom:function(e){for(var i in(this||t)._tiles)(this||t)._tiles[i].coords.z===e&&this._removeTile(i)},_removeAllTiles:function(){for(var e in(this||t)._tiles)this._removeTile(e)},_invalidateAll:function(){for(var e in(this||t)._levels){remove((this||t)._levels[e].el);this._onRemoveLevel(Number(e));delete(this||t)._levels[e]}this._removeAllTiles();(this||t)._tileZoom=void 0},_retainParent:function(e,i,n,o){var s=Math.floor(e/2),a=Math.floor(i/2),h=n-1,l=new Point(+s,+a);l.z=+h;var c=this._tileCoordsToKey(l),d=(this||t)._tiles[c];if(d&&d.active){d.retain=true;return true}d&&d.loaded&&(d.retain=true);return h>o&&this._retainParent(s,a,h,o)},_retainChildren:function(e,i,n,o){for(var s=2*e;s<2*e+2;s++)for(var a=2*i;a<2*i+2;a++){var h=new Point(s,a);h.z=n+1;var l=this._tileCoordsToKey(h),c=(this||t)._tiles[l];if(c&&c.active)c.retain=true;else{c&&c.loaded&&(c.retain=true);n+1(this||t).options.maxZoom||void 0!==(this||t).options.minZoom&&s<(this||t).options.minZoom?void 0:this._clampZoom(s);var a=(this||t).options.updateWhenZooming&&s!==(this||t)._tileZoom;if(!o||a){(this||t)._tileZoom=s;(this||t)._abortLoading&&this._abortLoading();this._updateLevels();this._resetGrid();void 0!==s&&this._update(e);n||this._pruneTiles();(this||t)._noPrune=!!n}this._setZoomTransforms(e,i)},_setZoomTransforms:function(e,i){for(var n in(this||t)._levels)this._setZoomTransform((this||t)._levels[n],e,i)},_setZoomTransform:function(e,i,n){var o=(this||t)._map.getZoomScale(n,e.zoom),s=e.origin.multiplyBy(o).subtract((this||t)._map._getNewPixelOrigin(i,n)).round();lt.any3d?setTransform(e.el,s,o):setPosition(e.el,s)},_resetGrid:function(){var e=(this||t)._map,i=e.options.crs,n=(this||t)._tileSize=this.getTileSize(),o=(this||t)._tileZoom;var s=(this||t)._map.getPixelWorldBounds((this||t)._tileZoom);s&&((this||t)._globalTileRange=this._pxBoundsToTileRange(s));(this||t)._wrapX=i.wrapLng&&!(this||t).options.noWrap&&[Math.floor(e.project([0,i.wrapLng[0]],o).x/n.x),Math.ceil(e.project([0,i.wrapLng[1]],o).x/n.y)];(this||t)._wrapY=i.wrapLat&&!(this||t).options.noWrap&&[Math.floor(e.project([i.wrapLat[0],0],o).y/n.x),Math.ceil(e.project([i.wrapLat[1],0],o).y/n.y)]},_onMoveEnd:function(){(this||t)._map&&!(this||t)._map._animatingZoom&&this._update()},_getTiledPixelBounds:function(e){var i=(this||t)._map,n=i._animatingZoom?Math.max(i._animateToZoom,i.getZoom()):i.getZoom(),o=i.getZoomScale(n,(this||t)._tileZoom),s=i.project(e,(this||t)._tileZoom).floor(),a=i.getSize().divideBy(2*o);return new Bounds(s.subtract(a),s.add(a))},_update:function(e){var i=(this||t)._map;if(i){var n=this._clampZoom(i.getZoom());void 0===e&&(e=i.getCenter());if(void 0!==(this||t)._tileZoom){var o=this._getTiledPixelBounds(e),s=this._pxBoundsToTileRange(o),a=s.getCenter(),h=[],l=(this||t).options.keepBuffer,c=new Bounds(s.getBottomLeft().subtract([l,-l]),s.getTopRight().add([l,-l]));if(!(isFinite(s.min.x)&&isFinite(s.min.y)&&isFinite(s.max.x)&&isFinite(s.max.y)))throw new Error("Attempted to load an infinite number of tiles");for(var d in(this||t)._tiles){var _=(this||t)._tiles[d].coords;_.z===(this||t)._tileZoom&&c.contains(new Point(_.x,_.y))||((this||t)._tiles[d].current=false)}if(Math.abs(n-(this||t)._tileZoom)>1)this._setView(e,n);else{for(var p=s.min.y;p<=s.max.y;p++)for(var f=s.min.x;f<=s.max.x;f++){var m=new Point(f,p);m.z=(this||t)._tileZoom;if(this._isValidTile(m)){var g=(this||t)._tiles[this._tileCoordsToKey(m)];g?g.current=true:h.push(m)}}h.sort((function(t,e){return t.distanceTo(a)-e.distanceTo(a)}));if(0!==h.length){if(!(this||t)._loading){(this||t)._loading=true;this.fire("loading")}var v=document.createDocumentFragment();for(f=0;fn.max.x)||!i.wrapLat&&(e.yn.max.y))return false}if(!(this||t).options.bounds)return true;var o=this._tileCoordsToBounds(e);return toLatLngBounds((this||t).options.bounds).overlaps(o)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(e){var i=(this||t)._map,n=this.getTileSize(),o=e.scaleBy(n),s=o.add(n),a=i.unproject(o,e.z),h=i.unproject(s,e.z);return[a,h]},_tileCoordsToBounds:function(e){var i=this._tileCoordsToNwSe(e),n=new LatLngBounds(i[0],i[1]);(this||t).options.noWrap||(n=(this||t)._map.wrapLatLngBounds(n));return n},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var e=t.split(":"),i=new Point(+e[0],+e[1]);i.z=+e[2];return i},_removeTile:function(e){var i=(this||t)._tiles[e];if(i){remove(i.el);delete(this||t)._tiles[e];this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(e)})}},_initTile:function(e){addClass(e,"leaflet-tile");var i=this.getTileSize();e.style.width=i.x+"px";e.style.height=i.y+"px";e.onselectstart=falseFn;e.onmousemove=falseFn;lt.ielt9&&(this||t).options.opacity<1&&setOpacity(e,(this||t).options.opacity)},_addTile:function(e,i){var n=this._getTilePos(e),o=this._tileCoordsToKey(e);var s=this.createTile(this._wrapCoords(e),bind((this||t)._tileReady,this||t,e));this._initTile(s);(this||t).createTile.length<2&&requestAnimFrame(bind((this||t)._tileReady,this||t,e,null,s));setPosition(s,n);(this||t)._tiles[o]={el:s,coords:e,current:true};i.appendChild(s);this.fire("tileloadstart",{tile:s,coords:e})},_tileReady:function(e,i,n){i&&this.fire("tileerror",{error:i,tile:n,coords:e});var o=this._tileCoordsToKey(e);n=(this||t)._tiles[o];if(n){n.loaded=+new Date;if((this||t)._map._fadeAnimated){setOpacity(n.el,0);cancelAnimFrame((this||t)._fadeFrame);(this||t)._fadeFrame=requestAnimFrame((this||t)._updateOpacity,this||t)}else{n.active=true;this._pruneTiles()}if(!i){addClass(n.el,"leaflet-tile-loaded");this.fire("tileload",{tile:n.el,coords:e})}if(this._noTilesToLoad()){(this||t)._loading=false;this.fire("load");lt.ielt9||!(this||t)._map._fadeAnimated?requestAnimFrame((this||t)._pruneTiles,this||t):setTimeout(bind((this||t)._pruneTiles,this||t),250)}}},_getTilePos:function(e){return e.scaleBy(this.getTileSize()).subtract((this||t)._level.origin)},_wrapCoords:function(e){var i=new Point((this||t)._wrapX?wrapNum(e.x,(this||t)._wrapX):e.x,(this||t)._wrapY?wrapNum(e.y,(this||t)._wrapY):e.y);i.z=e.z;return i},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new Bounds(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var e in(this||t)._tiles)if(!(this||t)._tiles[e].loaded)return false;return true}});function gridLayer(t){return new xe(t)}var we=xe.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:false,zoomReverse:false,detectRetina:false,crossOrigin:false,referrerPolicy:false},initialize:function(e,i){(this||t)._url=e;i=setOptions(this||t,i);if(i.detectRetina&<.retina&&i.maxZoom>0){i.tileSize=Math.floor(i.tileSize/2);if(i.zoomReverse){i.zoomOffset--;i.minZoom=Math.min(i.maxZoom,i.minZoom+1)}else{i.zoomOffset++;i.maxZoom=Math.max(i.minZoom,i.maxZoom-1)}i.minZoom=Math.max(0,i.minZoom)}else i.zoomReverse?i.minZoom=Math.min(i.maxZoom,i.minZoom):i.maxZoom=Math.max(i.minZoom,i.maxZoom);"string"===typeof i.subdomains&&(i.subdomains=i.subdomains.split(""));this.on("tileunload",(this||t)._onTileRemove)},setUrl:function(e,i){(this||t)._url===e&&void 0===i&&(i=true);(this||t)._url=e;i||this.redraw();return this||t},createTile:function(e,i){var n=document.createElement("img");on(n,"load",bind((this||t)._tileOnLoad,this||t,i,n));on(n,"error",bind((this||t)._tileOnError,this||t,i,n));((this||t).options.crossOrigin||""===(this||t).options.crossOrigin)&&(n.crossOrigin=true===(this||t).options.crossOrigin?"":(this||t).options.crossOrigin);"string"===typeof(this||t).options.referrerPolicy&&(n.referrerPolicy=(this||t).options.referrerPolicy);n.alt="";n.src=this.getTileUrl(e);return n},getTileUrl:function(e){var i={r:lt.retina?"@2x":"",s:this._getSubdomain(e),x:e.x,y:e.y,z:this._getZoomForUrl()};if((this||t)._map&&!(this||t)._map.options.crs.infinite){var n=(this||t)._globalTileRange.max.y-e.y;(this||t).options.tms&&(i.y=n);i["-y"]=n}return template((this||t)._url,extend(i,(this||t).options))},_tileOnLoad:function(e,i){lt.ielt9?setTimeout(bind(e,this||t,null,i),0):e(null,i)},_tileOnError:function(e,i,n){var o=(this||t).options.errorTileUrl;o&&i.getAttribute("src")!==o&&(i.src=o);e(n,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var e=(this||t)._tileZoom,i=(this||t).options.maxZoom,n=(this||t).options.zoomReverse,o=(this||t).options.zoomOffset;n&&(e=i-e);return e+o},_getSubdomain:function(e){var i=Math.abs(e.x+e.y)%(this||t).options.subdomains.length;return(this||t).options.subdomains[i]},_abortLoading:function(){var e,i;for(e in(this||t)._tiles)if((this||t)._tiles[e].coords.z!==(this||t)._tileZoom){i=(this||t)._tiles[e].el;i.onload=falseFn;i.onerror=falseFn;if(!i.complete){i.src=h;var n=(this||t)._tiles[e].coords;remove(i);delete(this||t)._tiles[e];this.fire("tileabort",{tile:i,coords:n})}}},_removeTile:function(e){var i=(this||t)._tiles[e];if(i){i.el.setAttribute("src",h);return xe.prototype._removeTile.call(this||t,e)}},_tileReady:function(e,i,n){if((this||t)._map&&(!n||n.getAttribute("src")!==h))return xe.prototype._tileReady.call(this||t,e,i,n)}});function tileLayer(t,e){return new we(t,e)}var be=we.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:false,version:"1.1.1"},options:{crs:null,uppercase:false},initialize:function(e,i){(this||t)._url=e;var n=extend({},(this||t).defaultWmsParams);for(var o in i)o in(this||t).options||(n[o]=i[o]);i=setOptions(this||t,i);var s=i.detectRetina&<.retina?2:1;var a=this.getTileSize();n.width=a.x*s;n.height=a.y*s;(this||t).wmsParams=n},onAdd:function(e){(this||t)._crs=(this||t).options.crs||e.options.crs;(this||t)._wmsVersion=parseFloat((this||t).wmsParams.version);var i=(this||t)._wmsVersion>=1.3?"crs":"srs";(this||t).wmsParams[i]=(this||t)._crs.code;we.prototype.onAdd.call(this||t,e)},getTileUrl:function(e){var i=this._tileCoordsToNwSe(e),n=(this||t)._crs,o=toBounds(n.project(i[0]),n.project(i[1])),s=o.min,a=o.max,h=((this||t)._wmsVersion>=1.3&&(this||t)._crs===Xt?[s.y,s.x,a.y,a.x]:[s.x,s.y,a.x,a.y]).join(","),l=we.prototype.getTileUrl.call(this||t,e);return l+getParamString((this||t).wmsParams,l,(this||t).options.uppercase)+((this||t).options.uppercase?"&BBOX=":"&bbox=")+h},setParams:function(e,i){extend((this||t).wmsParams,e);i||this.redraw();return this||t}});function tileLayerWMS(t,e){return new be(t,e)}we.WMS=be;tileLayer.wms=tileLayerWMS;var Te=te.extend({options:{padding:.1},initialize:function(e){setOptions(this||t,e);stamp(this||t);(this||t)._layers=(this||t)._layers||{}},onAdd:function(){if(!(this||t)._container){this._initContainer();addClass((this||t)._container,"leaflet-zoom-animated")}this.getPane().appendChild((this||t)._container);this._update();this.on("update",(this||t)._updatePaths,this||t)},onRemove:function(){this.off("update",(this||t)._updatePaths,this||t);this._destroyContainer()},getEvents:function(){var e={viewreset:(this||t)._reset,zoom:(this||t)._onZoom,moveend:(this||t)._update,zoomend:(this||t)._onZoomEnd};(this||t)._zoomAnimated&&(e.zoomanim=(this||t)._onAnimZoom);return e},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform((this||t)._map.getCenter(),(this||t)._map.getZoom())},_updateTransform:function(e,i){var n=(this||t)._map.getZoomScale(i,(this||t)._zoom),o=(this||t)._map.getSize().multiplyBy(.5+(this||t).options.padding),s=(this||t)._map.project((this||t)._center,i),a=o.multiplyBy(-n).add(s).subtract((this||t)._map._getNewPixelOrigin(e,i));lt.any3d?setTransform((this||t)._container,a,n):setPosition((this||t)._container,a)},_reset:function(){this._update();this._updateTransform((this||t)._center,(this||t)._zoom);for(var e in(this||t)._layers)(this||t)._layers[e]._reset()},_onZoomEnd:function(){for(var e in(this||t)._layers)(this||t)._layers[e]._project()},_updatePaths:function(){for(var e in(this||t)._layers)(this||t)._layers[e]._update()},_update:function(){var e=(this||t).options.padding,i=(this||t)._map.getSize(),n=(this||t)._map.containerPointToLayerPoint(i.multiplyBy(-e)).round();(this||t)._bounds=new Bounds(n,n.add(i.multiplyBy(1+2*e)).round());(this||t)._center=(this||t)._map.getCenter();(this||t)._zoom=(this||t)._map.getZoom()}});var Ce=Te.extend({options:{tolerance:0},getEvents:function(){var e=Te.prototype.getEvents.call(this||t);e.viewprereset=(this||t)._onViewPreReset;return e},_onViewPreReset:function(){(this||t)._postponeUpdatePaths=true},onAdd:function(){Te.prototype.onAdd.call(this||t);this._draw()},_initContainer:function(){var e=(this||t)._container=document.createElement("canvas");on(e,"mousemove",(this||t)._onMouseMove,this||t);on(e,"click dblclick mousedown mouseup contextmenu",(this||t)._onClick,this||t);on(e,"mouseout",(this||t)._handleMouseOut,this||t);e._leaflet_disable_events=true;(this||t)._ctx=e.getContext("2d")},_destroyContainer:function(){cancelAnimFrame((this||t)._redrawRequest);delete(this||t)._ctx;remove((this||t)._container);off((this||t)._container);delete(this||t)._container},_updatePaths:function(){if(!(this||t)._postponeUpdatePaths){var e;(this||t)._redrawBounds=null;for(var i in(this||t)._layers){e=(this||t)._layers[i];e._update()}this._redraw()}},_update:function(){if(!(this||t)._map._animatingZoom||!(this||t)._bounds){Te.prototype._update.call(this||t);var e=(this||t)._bounds,i=(this||t)._container,n=e.getSize(),o=lt.retina?2:1;setPosition(i,e.min);i.width=o*n.x;i.height=o*n.y;i.style.width=n.x+"px";i.style.height=n.y+"px";lt.retina&&(this||t)._ctx.scale(2,2);(this||t)._ctx.translate(-e.min.x,-e.min.y);this.fire("update")}},_reset:function(){Te.prototype._reset.call(this||t);if((this||t)._postponeUpdatePaths){(this||t)._postponeUpdatePaths=false;this._updatePaths()}},_initPath:function(e){this._updateDashArray(e);(this||t)._layers[stamp(e)]=e;var i=e._order={layer:e,prev:(this||t)._drawLast,next:null};(this||t)._drawLast&&((this||t)._drawLast.next=i);(this||t)._drawLast=i;(this||t)._drawFirst=(this||t)._drawFirst||(this||t)._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(e){var i=e._order;var n=i.next;var o=i.prev;n?n.prev=o:(this||t)._drawLast=o;o?o.next=n:(this||t)._drawFirst=n;delete e._order;delete(this||t)._layers[stamp(e)];this._requestRedraw(e)},_updatePath:function(t){this._extendRedrawBounds(t);t._project();t._update();this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t);this._requestRedraw(t)},_updateDashArray:function(t){if("string"===typeof t.options.dashArray){var e,i,n=t.options.dashArray.split(/[, ]+/),o=[];for(i=0;i')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}();var ze={_initContainer:function(){(this||t)._container=create$1("div","leaflet-vml-container")},_update:function(){if(!(this||t)._map._animatingZoom){Te.prototype._update.call(this||t);this.fire("update")}},_initPath:function(e){var i=e._container=Me("shape");addClass(i,"leaflet-vml-shape "+((this||t).options.className||""));i.coordsize="1 1";e._path=Me("path");i.appendChild(e._path);this._updateStyle(e);(this||t)._layers[stamp(e)]=e},_addPath:function(e){var i=e._container;(this||t)._container.appendChild(i);e.options.interactive&&e.addInteractiveTarget(i)},_removePath:function(e){var i=e._container;remove(i);e.removeInteractiveTarget(i);delete(this||t)._layers[stamp(e)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke;o.filled=!!n.fill;if(n.stroke){e||(e=t._stroke=Me("stroke"));o.appendChild(e);e.weight=n.weight+"px";e.color=n.color;e.opacity=n.opacity;n.dashArray?e.dashStyle=a(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="";e.endcap=n.lineCap.replace("butt","flat");e.joinstyle=n.lineJoin}else if(e){o.removeChild(e);t._stroke=null}if(n.fill){i||(i=t._fill=Me("fill"));o.appendChild(i);i.color=n.fillColor||n.color;i.opacity=n.fillOpacity}else if(i){o.removeChild(i);t._fill=null}},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+i+","+n+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){toFront(t._container)},_bringToBack:function(t){toBack(t._container)}};var Se=lt.vml?Me:svgCreate;var Be=Te.extend({_initContainer:function(){(this||t)._container=Se("svg");(this||t)._container.setAttribute("pointer-events","none");(this||t)._rootGroup=Se("g");(this||t)._container.appendChild((this||t)._rootGroup)},_destroyContainer:function(){remove((this||t)._container);off((this||t)._container);delete(this||t)._container;delete(this||t)._rootGroup;delete(this||t)._svgSize},_update:function(){if(!(this||t)._map._animatingZoom||!(this||t)._bounds){Te.prototype._update.call(this||t);var e=(this||t)._bounds,i=e.getSize(),n=(this||t)._container;if(!(this||t)._svgSize||!(this||t)._svgSize.equals(i)){(this||t)._svgSize=i;n.setAttribute("width",i.x);n.setAttribute("height",i.y)}setPosition(n,e.min);n.setAttribute("viewBox",[e.min.x,e.min.y,i.x,i.y].join(" "));this.fire("update")}},_initPath:function(e){var i=e._path=Se("path");e.options.className&&addClass(i,e.options.className);e.options.interactive&&addClass(i,"leaflet-interactive");this._updateStyle(e);(this||t)._layers[stamp(e)]=e},_addPath:function(e){(this||t)._rootGroup||this._initContainer();(this||t)._rootGroup.appendChild(e._path);e.addInteractiveTarget(e._path)},_removePath:function(e){remove(e._path);e.removeInteractiveTarget(e._path);delete(this||t)._layers[stamp(e)]},_updatePath:function(t){t._project();t._update()},_updateStyle:function(t){var e=t._path,i=t.options;if(e){if(i.stroke){e.setAttribute("stroke",i.color);e.setAttribute("stroke-opacity",i.opacity);e.setAttribute("stroke-width",i.weight);e.setAttribute("stroke-linecap",i.lineCap);e.setAttribute("stroke-linejoin",i.lineJoin);i.dashArray?e.setAttribute("stroke-dasharray",i.dashArray):e.removeAttribute("stroke-dasharray");i.dashOffset?e.setAttribute("stroke-dashoffset",i.dashOffset):e.removeAttribute("stroke-dashoffset")}else e.setAttribute("stroke","none");if(i.fill){e.setAttribute("fill",i.fillColor||i.color);e.setAttribute("fill-opacity",i.fillOpacity);e.setAttribute("fill-rule",i.fillRule||"evenodd")}else e.setAttribute("fill","none")}},_updatePoly:function(t,e){this._setPath(t,pointsToPath(t._parts,e))},_updateCircle:function(t){var e=t._point,i=Math.max(Math.round(t._radius),1),n=Math.max(Math.round(t._radiusY),1)||i,o="a"+i+","+n+" 0 1,0 ";var s=t._empty()?"M0 0":"M"+(e.x-i)+","+e.y+o+2*i+",0 "+o+2*-i+",0 ";this._setPath(t,s)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){toFront(t._path)},_bringToBack:function(t){toBack(t._path)}});lt.vml&&Be.include(ze);function svg(t){return lt.svg||lt.vml?new Be(t):null}Et.include({getRenderer:function(e){var i=e.options.renderer||this._getPaneRenderer(e.options.pane)||(this||t).options.renderer||(this||t)._renderer;i||(i=(this||t)._renderer=this._createRenderer());this.hasLayer(i)||this.addLayer(i);return i},_getPaneRenderer:function(e){if("overlayPane"===e||void 0===e)return false;var i=(this||t)._paneRenderers[e];if(void 0===i){i=this._createRenderer({pane:e});(this||t)._paneRenderers[e]=i}return i},_createRenderer:function(e){return(this||t).options.preferCanvas&&canvas(e)||svg(e)}});var Oe=ce.extend({initialize:function(e,i){ce.prototype.initialize.call(this||t,this._boundsToLatLngs(e),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){t=toLatLngBounds(t);return[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});function rectangle(t,e){return new Oe(t,e)}Be.create=Se;Be.pointsToPath=pointsToPath;de.geometryToLayer=geometryToLayer;de.coordsToLatLng=coordsToLatLng;de.coordsToLatLngs=coordsToLatLngs;de.latLngToCoords=latLngToCoords;de.latLngsToCoords=latLngsToCoords;de.getFeature=getFeature;de.asFeature=asFeature;Et.mergeOptions({boxZoom:true});var ke=jt.extend({initialize:function(e){(this||t)._map=e;(this||t)._container=e._container;(this||t)._pane=e._panes.overlayPane;(this||t)._resetStateTimeout=0;e.on("unload",(this||t)._destroy,this||t)},addHooks:function(){on((this||t)._container,"mousedown",(this||t)._onMouseDown,this||t)},removeHooks:function(){off((this||t)._container,"mousedown",(this||t)._onMouseDown,this||t)},moved:function(){return(this||t)._moved},_destroy:function(){remove((this||t)._pane);delete(this||t)._pane},_resetState:function(){(this||t)._resetStateTimeout=0;(this||t)._moved=false},_clearDeferredResetState:function(){if(0!==(this||t)._resetStateTimeout){clearTimeout((this||t)._resetStateTimeout);(this||t)._resetStateTimeout=0}},_onMouseDown:function(e){if(!e.shiftKey||1!==e.which&&1!==e.button)return false;this._clearDeferredResetState();this._resetState();xt();disableImageDrag();(this||t)._startPoint=(this||t)._map.mouseEventToContainerPoint(e);on(document,{contextmenu:stop,mousemove:(this||t)._onMouseMove,mouseup:(this||t)._onMouseUp,keydown:(this||t)._onKeyDown},this||t)},_onMouseMove:function(e){if(!(this||t)._moved){(this||t)._moved=true;(this||t)._box=create$1("div","leaflet-zoom-box",(this||t)._container);addClass((this||t)._container,"leaflet-crosshair");(this||t)._map.fire("boxzoomstart")}(this||t)._point=(this||t)._map.mouseEventToContainerPoint(e);var i=new Bounds((this||t)._point,(this||t)._startPoint),n=i.getSize();setPosition((this||t)._box,i.min);(this||t)._box.style.width=n.x+"px";(this||t)._box.style.height=n.y+"px"},_finish:function(){if((this||t)._moved){remove((this||t)._box);removeClass((this||t)._container,"leaflet-crosshair")}wt();enableImageDrag();off(document,{contextmenu:stop,mousemove:(this||t)._onMouseMove,mouseup:(this||t)._onMouseUp,keydown:(this||t)._onKeyDown},this||t)},_onMouseUp:function(e){if(1===e.which||1===e.button){this._finish();if((this||t)._moved){this._clearDeferredResetState();(this||t)._resetStateTimeout=setTimeout(bind((this||t)._resetState,this||t),0);var i=new LatLngBounds((this||t)._map.containerPointToLatLng((this||t)._startPoint),(this||t)._map.containerPointToLatLng((this||t)._point));(this||t)._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}}},_onKeyDown:function(t){if(27===t.keyCode){this._finish();this._clearDeferredResetState();this._resetState()}}});Et.addInitHook("addHandler","boxZoom",ke);Et.mergeOptions({doubleClickZoom:true});var Ze=jt.extend({addHooks:function(){(this||t)._map.on("dblclick",(this||t)._onDoubleClick,this||t)},removeHooks:function(){(this||t)._map.off("dblclick",(this||t)._onDoubleClick,this||t)},_onDoubleClick:function(e){var i=(this||t)._map,n=i.getZoom(),o=i.options.zoomDelta,s=e.originalEvent.shiftKey?n-o:n+o;"center"===i.options.doubleClickZoom?i.setZoom(s):i.setZoomAround(e.containerPoint,s)}});Et.addInitHook("addHandler","doubleClickZoom",Ze);Et.mergeOptions({dragging:true,inertia:true,inertiaDeceleration:3400,inertiaMaxSpeed:Infinity,easeLinearity:.2,worldCopyJump:false,maxBoundsViscosity:0});var Ee=jt.extend({addHooks:function(){if(!(this||t)._draggable){var e=(this||t)._map;(this||t)._draggable=new qt(e._mapPane,e._container);(this||t)._draggable.on({dragstart:(this||t)._onDragStart,drag:(this||t)._onDrag,dragend:(this||t)._onDragEnd},this||t);(this||t)._draggable.on("predrag",(this||t)._onPreDragLimit,this||t);if(e.options.worldCopyJump){(this||t)._draggable.on("predrag",(this||t)._onPreDragWrap,this||t);e.on("zoomend",(this||t)._onZoomEnd,this||t);e.whenReady((this||t)._onZoomEnd,this||t)}}addClass((this||t)._map._container,"leaflet-grab leaflet-touch-drag");(this||t)._draggable.enable();(this||t)._positions=[];(this||t)._times=[]},removeHooks:function(){removeClass((this||t)._map._container,"leaflet-grab");removeClass((this||t)._map._container,"leaflet-touch-drag");(this||t)._draggable.disable()},moved:function(){return(this||t)._draggable&&(this||t)._draggable._moved},moving:function(){return(this||t)._draggable&&(this||t)._draggable._moving},_onDragStart:function(){var e=(this||t)._map;e._stop();if((this||t)._map.options.maxBounds&&(this||t)._map.options.maxBoundsViscosity){var i=toLatLngBounds((this||t)._map.options.maxBounds);(this||t)._offsetLimit=toBounds((this||t)._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),(this||t)._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add((this||t)._map.getSize()));(this||t)._viscosity=Math.min(1,Math.max(0,(this||t)._map.options.maxBoundsViscosity))}else(this||t)._offsetLimit=null;e.fire("movestart").fire("dragstart");if(e.options.inertia){(this||t)._positions=[];(this||t)._times=[]}},_onDrag:function(e){if((this||t)._map.options.inertia){var i=(this||t)._lastTime=+new Date,n=(this||t)._lastPos=(this||t)._draggable._absPos||(this||t)._draggable._newPos;(this||t)._positions.push(n);(this||t)._times.push(i);this._prunePositions(i)}(this||t)._map.fire("move",e).fire("drag",e)},_prunePositions:function(e){while((this||t)._positions.length>1&&e-(this||t)._times[0]>50){(this||t)._positions.shift();(this||t)._times.shift()}},_onZoomEnd:function(){var e=(this||t)._map.getSize().divideBy(2),i=(this||t)._map.latLngToLayerPoint([0,0]);(this||t)._initialWorldOffset=i.subtract(e).x;(this||t)._worldWidth=(this||t)._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(e,i){return e-(e-i)*(this||t)._viscosity},_onPreDragLimit:function(){if((this||t)._viscosity&&(this||t)._offsetLimit){var e=(this||t)._draggable._newPos.subtract((this||t)._draggable._startPos);var i=(this||t)._offsetLimit;e.xi.max.x&&(e.x=this._viscousLimit(e.x,i.max.x));e.y>i.max.y&&(e.y=this._viscousLimit(e.y,i.max.y));(this||t)._draggable._newPos=(this||t)._draggable._startPos.add(e)}},_onPreDragWrap:function(){var e=(this||t)._worldWidth,i=Math.round(e/2),n=(this||t)._initialWorldOffset,o=(this||t)._draggable._newPos.x,s=(o-i+n)%e+i-n,a=(o+i+n)%e-i-n,h=Math.abs(s+n)0?a:-a))-i;(this||t)._delta=0;(this||t)._startTime=null;h&&("center"===e.options.scrollWheelZoom?e.setZoom(i+h):e.setZoomAround((this||t)._lastMousePos,i+h))}});Et.addInitHook("addHandler","scrollWheelZoom",Ie);var De=600;Et.mergeOptions({tapHold:lt.touchNative&<.safari&<.mobile,tapTolerance:15});var Ne=jt.extend({addHooks:function(){on((this||t)._map._container,"touchstart",(this||t)._onDown,this||t)},removeHooks:function(){off((this||t)._map._container,"touchstart",(this||t)._onDown,this||t)},_onDown:function(e){clearTimeout((this||t)._holdTimeout);if(1===e.touches.length){var i=e.touches[0];(this||t)._startPos=(this||t)._newPos=new Point(i.clientX,i.clientY);(this||t)._holdTimeout=setTimeout(bind((function(){this._cancel();if(this._isTapValid()){on(document,"touchend",preventDefault);on(document,"touchend touchcancel",(this||t)._cancelClickPrevent);this._simulateEvent("contextmenu",i)}}),this||t),De);on(document,"touchend touchcancel contextmenu",(this||t)._cancel,this||t);on(document,"touchmove",(this||t)._onMove,this||t)}},_cancelClickPrevent:function cancelClickPrevent(){off(document,"touchend",preventDefault);off(document,"touchend touchcancel",cancelClickPrevent)},_cancel:function(){clearTimeout((this||t)._holdTimeout);off(document,"touchend touchcancel contextmenu",(this||t)._cancel,this||t);off(document,"touchmove",(this||t)._onMove,this||t)},_onMove:function(e){var i=e.touches[0];(this||t)._newPos=new Point(i.clientX,i.clientY)},_isTapValid:function(){return(this||t)._newPos.distanceTo((this||t)._startPos)<=(this||t)._map.options.tapTolerance},_simulateEvent:function(t,e){var i=new MouseEvent(t,{bubbles:true,cancelable:true,view:window,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY});i._simulated=true;e.target.dispatchEvent(i)}});Et.addInitHook("addHandler","tapHold",Ne);Et.mergeOptions({touchZoom:lt.touch,bounceAtZoomLimits:true});var Fe=jt.extend({addHooks:function(){addClass((this||t)._map._container,"leaflet-touch-zoom");on((this||t)._map._container,"touchstart",(this||t)._onTouchStart,this||t)},removeHooks:function(){removeClass((this||t)._map._container,"leaflet-touch-zoom");off((this||t)._map._container,"touchstart",(this||t)._onTouchStart,this||t)},_onTouchStart:function(e){var i=(this||t)._map;if(e.touches&&2===e.touches.length&&!i._animatingZoom&&!(this||t)._zooming){var n=i.mouseEventToContainerPoint(e.touches[0]),o=i.mouseEventToContainerPoint(e.touches[1]);(this||t)._centerPoint=i.getSize()._divideBy(2);(this||t)._startLatLng=i.containerPointToLatLng((this||t)._centerPoint);"center"!==i.options.touchZoom&&((this||t)._pinchStartLatLng=i.containerPointToLatLng(n.add(o)._divideBy(2)));(this||t)._startDist=n.distanceTo(o);(this||t)._startZoom=i.getZoom();(this||t)._moved=false;(this||t)._zooming=true;i._stop();on(document,"touchmove",(this||t)._onTouchMove,this||t);on(document,"touchend touchcancel",(this||t)._onTouchEnd,this||t);preventDefault(e)}},_onTouchMove:function(e){if(e.touches&&2===e.touches.length&&(this||t)._zooming){var i=(this||t)._map,n=i.mouseEventToContainerPoint(e.touches[0]),o=i.mouseEventToContainerPoint(e.touches[1]),s=n.distanceTo(o)/(this||t)._startDist;(this||t)._zoom=i.getScaleZoom(s,(this||t)._startZoom);!i.options.bounceAtZoomLimits&&((this||t)._zoomi.getMaxZoom()&&s>1)&&((this||t)._zoom=i._limitZoom((this||t)._zoom));if("center"===i.options.touchZoom){(this||t)._center=(this||t)._startLatLng;if(1===s)return}else{var a=n._add(o)._divideBy(2)._subtract((this||t)._centerPoint);if(1===s&&0===a.x&&0===a.y)return;(this||t)._center=i.unproject(i.project((this||t)._pinchStartLatLng,(this||t)._zoom).subtract(a),(this||t)._zoom)}if(!(this||t)._moved){i._moveStart(true,false);(this||t)._moved=true}cancelAnimFrame((this||t)._animRequest);var h=bind(i._move,i,(this||t)._center,(this||t)._zoom,{pinch:true,round:false},void 0);(this||t)._animRequest=requestAnimFrame(h,this||t,true);preventDefault(e)}},_onTouchEnd:function(){if((this||t)._moved&&(this||t)._zooming){(this||t)._zooming=false;cancelAnimFrame((this||t)._animRequest);off(document,"touchmove",(this||t)._onTouchMove,this||t);off(document,"touchend touchcancel",(this||t)._onTouchEnd,this||t);(this||t)._map.options.zoomAnimation?(this||t)._map._animateZoom((this||t)._center,(this||t)._map._limitZoom((this||t)._zoom),true,(this||t)._map.options.zoomSnap):(this||t)._map._resetView((this||t)._center,(this||t)._map._limitZoom((this||t)._zoom))}else(this||t)._zooming=false}});Et.addInitHook("addHandler","touchZoom",Fe);Et.BoxZoom=ke;Et.DoubleClickZoom=Ze;Et.Drag=Ee;Et.Keyboard=Ae;Et.ScrollWheelZoom=Ie;Et.TapHold=Ne;Et.TouchZoom=Fe;e.Bounds=Bounds;e.Browser=lt;e.CRS=g;e.Canvas=Ce;e.Circle=le;e.CircleMarker=he;e.Class=Class;e.Control=At;e.DivIcon=Pe;e.DivOverlay=ve;e.DomEvent=kt;e.DomUtil=zt;e.Draggable=qt;e.Evented=f;e.FeatureGroup=ie;e.GeoJSON=de;e.GridLayer=xe;e.Handler=jt;e.Icon=ne;e.ImageOverlay=fe;e.LatLng=LatLng;e.LatLngBounds=LatLngBounds;e.Layer=te;e.LayerGroup=ee;e.LineUtil=Gt;e.Map=Et;e.Marker=ae;e.Mixin=Wt;e.Path=re;e.Point=Point;e.PolyUtil=Ut;e.Polygon=ce;e.Polyline=ue;e.Popup=ye;e.PosAnimation=Zt;e.Projection=Yt;e.Rectangle=Oe;e.Renderer=Te;e.SVG=Be;e.SVGOverlay=ge;e.TileLayer=we;e.Tooltip=Le;e.Transformation=Transformation;e.Util=_;e.VideoOverlay=me;e.bind=bind;e.bounds=toBounds;e.canvas=canvas;e.circle=circle;e.circleMarker=circleMarker;e.control=control;e.divIcon=divIcon;e.extend=extend;e.featureGroup=featureGroup;e.geoJSON=geoJSON;e.geoJson=pe;e.gridLayer=gridLayer;e.icon=icon;e.imageOverlay=imageOverlay;e.latLng=toLatLng;e.latLngBounds=toLatLngBounds;e.layerGroup=layerGroup;e.map=createMap;e.marker=marker;e.point=toPoint;e.polygon=polygon;e.polyline=polyline;e.popup=popup;e.rectangle=rectangle;e.setOptions=setOptions;e.stamp=stamp;e.svg=svg;e.svgOverlay=svgOverlay;e.tileLayer=tileLayer;e.tooltip=tooltip;e.transformation=toTransformation;e.version=i;e.videoOverlay=videoOverlay;var Re=window.L;e.noConflict=function(){window.L=Re;return this||t};window.L=e}));const i=e.Bounds,n=e.Browser,o=e.CRS,s=e.Canvas,a=e.Circle,h=e.CircleMarker,l=e.Class,c=e.Control,d=e.DivIcon,_=e.DivOverlay,p=e.DomEvent,f=e.DomUtil,m=e.Draggable,g=e.Evented,v=e.FeatureGroup,y=e.GeoJSON,P=e.GridLayer,x=e.Handler,b=e.Icon,T=e.ImageOverlay,C=e.LatLng,M=e.LatLngBounds,z=e.Layer,S=e.LayerGroup,B=e.LineUtil,O=e.Marker,k=e.Mixin,Z=e.Path,E=e.Point,A=e.PolyUtil,I=e.Polygon,D=e.Polyline,N=e.Popup,R=e.PosAnimation,j=e.Projection,W=e.Rectangle,H=e.Renderer,q=e.SVG,U=e.SVGOverlay,V=e.TileLayer,G=e.Tooltip,$=e.Transformation,K=e.Util,Y=e.VideoOverlay,J=e.bind,X=e.bounds,Q=e.canvas,tt=e.circle,et=e.circleMarker,it=e.control,nt=e.divIcon,ot=e.extend,st=e.featureGroup,at=e.geoJSON,rt=e.geoJson,ht=e.gridLayer,lt=e.icon,ut=e.imageOverlay,ct=e.latLng,dt=e.latLngBounds,_t=e.layerGroup,pt=e.map,ft=e.marker,mt=e.point,gt=e.polygon,vt=e.polyline,yt=e.popup,Lt=e.rectangle,Pt=e.setOptions,xt=e.stamp,wt=e.svg,bt=e.svgOverlay,Tt=e.tileLayer,Ct=e.tooltip,Mt=e.transformation,zt=e.version,St=e.videoOverlay,Bt=e.noConflict;const Ot=e.Map;export{i as Bounds,n as Browser,o as CRS,s as Canvas,a as Circle,h as CircleMarker,l as Class,c as Control,d as DivIcon,_ as DivOverlay,p as DomEvent,f as DomUtil,m as Draggable,g as Evented,v as FeatureGroup,y as GeoJSON,P as GridLayer,x as Handler,b as Icon,T as ImageOverlay,C as LatLng,M as LatLngBounds,z as Layer,S as LayerGroup,B as LineUtil,Ot as Map,O as Marker,k as Mixin,Z as Path,E as Point,A as PolyUtil,I as Polygon,D as Polyline,N as Popup,R as PosAnimation,j as Projection,W as Rectangle,H as Renderer,q as SVG,U as SVGOverlay,V as TileLayer,G as Tooltip,$ as Transformation,K as Util,Y as VideoOverlay,J as bind,X as bounds,Q as canvas,tt as circle,et as circleMarker,it as control,e as default,nt as divIcon,ot as extend,st as featureGroup,at as geoJSON,rt as geoJson,ht as gridLayer,lt as icon,ut as imageOverlay,ct as latLng,dt as latLngBounds,_t as layerGroup,pt as map,ft as marker,Bt as noConflict,mt as point,gt as polygon,vt as polyline,yt as popup,Lt as rectangle,Pt as setOptions,xt as stamp,wt as svg,bt as svgOverlay,Tt as tileLayer,Ct as tooltip,Mt as transformation,zt as version,St as videoOverlay}; +