diff --git a/.formatter.exs b/.formatter.exs index 8a6391c6a6ba..1e3d79fdf398 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,5 +1,5 @@ [ import_deps: [:ecto, :phoenix], - inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"], + inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"], subdirectories: ["priv/*/migrations"] ] diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 1684edca3141..000000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,175 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - - -## Unreleased - -### Added -- Integration with [Matomo's referrer spam list](https://github.com/matomo-org/referrer-spam-list/blob/master/spammers.txt) to block known spammers -- API route `PUT /api/v1/sites/goals` with form params `site_id`, `event_name` and/or `page_path`, and `goal_type` with supported types `event` and `page` -- API route `DELETE /api/v1/sites/goals/:goal_id` with form params `site_id` -- The public breakdown endpoint can be queried with the "events" metric -- Data exported via the download button will contain CSV data for all visible graps in a zip file. -- Region and city-level geolocation plausible/analytics#1449 -- The `u` option can now be used in the `manual` extension to specify a URL when triggering events. -- Delete a site and all related data through the Sites API -- Subscribed users can see their Paddle invoices from the last 12 months under the user settings -- Allow custom styles to be passed to embedded iframe plausible/analytics#1522 -- New UTM Tags `utm_content` and `utm_term` plausible/analytics#515 -- If a session was started without a screen_size it is updated if an event with screen_size occurs -- Added `LISTEN_IP` configuration parameter plausible/analytics#1189 -- The breakdown endpoint with the property query `property=event:goal` returns custom goal properties (within `props`) -- Added IPv6 Ecto support (via the environment-variable `ECTO_IPV6`) - -### Fixed -- UI fix where multi-line text in pills would not be underlined properly on small screens. -- UI fix to align footer columns -- Guests can now use the favicon to toggle additional info about the site bing viewed (such as in public embeds). -- Fix SecurityError in tracking script when user has blocked all local storage - -### Changed -- Cache the tracking script for 24 hours - -## v1.4.1 - -### Fixed -- Fixes database error when pathname contains a question mark - -## v1.4.0 - -### Added -- New parameter `metrics` for the `/api/v1/stats/timeseries` endpoint plausible/analytics#952 -- CSV export now includes pageviews, bounce rate and visit duration in addition to visitors plausible/analytics#952 -- Send stats to multiple dashboards by configuring a comma-separated list of domains plausible/analytics#968 -- To authenticate against a local postgresql via socket authentication, the environment-variables - `DATABASE_SOCKET_DIR` & `DATABASE_NAME` were added. -- Time on Page metric available in detailed Top Pages report plausible/analytics#1007 -- Wildcard based page, entry page and exit page filters plausible/analytics#1067 -- Exclusion filters for page, entry page and exit page filters plausible/analytics#1067 -- Menu (with auto-complete) to add new and edit existing filters directly plausible/analytics#1089 -- Added `CLICKHOUSE_FLUSH_INTERVAL_MS` and `CLICKHOUSE_MAX_BUFFER_SIZE` configuration parameters plausible/analytics#1073 -- Ability to invite users to sites with different roles plausible/analytics#1122 -- Option to configure a custom name for the script file -- Add Conversion Rate to Top Sources, Top Pages Devices, Countries when filtered by a goal plausible/analytics#1299 -- Add list view for countries report in dashboard plausible/analytics#1381 -- Add ability to view more than 100 custom goal properties plausible/analytics#1353 - -### Fixed -- Fix weekly report time range plausible/analytics#951 -- Make sure embedded dashboards can run when user has blocked third-party cookies plausible/analytics#971 -- Sites listing page will paginate if the user has a lot of sites plausible/analytics#994 -- Crash when changing theme on a loaded dashboard plausible/analytics#1123 -- UI fix for details button overlapping content on mobile plausible/analytics#1114 -- UI fix for the main graph on mobile overlapping its tick items on both axis -- UI fixes for text not showing properly in bars across multiple lines. This hides the totals on <768px and only shows the uniques and % to accommodate the goals text too. Larger screens still truncate as usual. -- Turn off autocomplete for name and password inputs in the _New shared link_ form. -- Details modals are now responsive and take up less horizontal space on smaller screens to make it easier to scroll. -- Fix reading config from file -- Fix some links not opening correctly in new tab -- UI fix for more than one row of custom event properties plausible/analytics#1383 -- UI fix for user menu and time picker overlapping plausible/analytics#1352 -- Respect the `path` component of BASE_URL to allow subfolder installatons - -### Removed -- Removes AppSignal monitoring package - -### Changes -- Disable email verification by default. Added a configuration option `ENABLE_EMAIL_VERIFICATION=true` if you want to keep the old behaviour - -## [1.3] - 2021-04-14 - -### Added -- Stats API [currently in beta] plausible/analytics#679 -- Ability to view and filter by entry and exit pages, in addition to regular page hits plausible/analytics#712 -- 30 day and 6 month keybindings (`T` and `S`, respectively) plausible/analytics#709 -- Site switching keybinds (1-9 for respective sites) plausible/analytics#735 -- Glob (wildcard) based pageview goals plausible/analytics#750 -- Support for embedding shared links in an iframe plausible/analytics#812 -- Include a basic IP-To-Country database by default plausible/analytics#906 -- Add name/label to shared links plausible/analytics#910 - -### Fixed -- Capitalized date/time selection keybinds not working plausible/analytics#709 -- Invisible text on Google Search Console settings page in dark mode plausible/analytics#759 -- Disable analytics tracking when running Cypress tests -- CSV reports can be downloaded via shared links plausible/analytics#884 -- Fixes weekly/monthly email report delivery over SMTP plausible/analytics#889 -- Disable self-tracking with self hosting plausible/analytics#907 -- Fix current visitors request when using shared links - -## [1.2] - 2021-01-26 - -### Added -- Ability to add event metadata plausible/analytics#381 -- Add tracker module to automatically track outbound links plausible/analytics#389 -- Display weekday on the visitor graph plausible/analytics#175 -- Collect and display browser & OS versions plausible/analytics#397 -- Simple notifications around traffic spikes plausible/analytics#453 -- Dark theme option/system setting follow plausible/analytics#467 -- "Load More" capability to pages modal plausible/analytics#480 -- Unique Visitors (last 30 min) as a top stat in realtime view plausible/analytics#500 -- Pinned filter and date selector rows while scrolling plausible/analytics#472 -- Escape keyboard shortcut to clear all filters plausible/analytics#625 -- Tracking exclusions, see our documentation [here](https://docs.plausible.io/excluding) and [here](https://docs.plausible.io/excluding-pages) for details plausible/analytics#489 -- Keybindings for selecting dates/ranges plausible/analytics#630 - -### Changed -- Use alpine as base image to decrease Docker image size plausible/analytics#353 -- Ignore automated browsers (Phantom, Selenium, Headless Chrome, etc) -- Display domain's favicon on the home page -- Ignore consecutive pageviews on same pathname plausible/analytics#417 -- Validate domain format on site creation plausible/analytics#427 -- Improve settings UX and design plausible/analytics#412 -- Improve site listing UX and design plausible/analytics#438 -- Improve onboarding UX and design plausible/analytics#441 -- Allows outbound link tracking script to use new tab redirection plausible/analytics#494 -- "This Month" view is now Month-to-date for the current month plausible/analytics#491 -- My sites now show settings cog at all times on smaller screens plausible/analytics#497 -- Background jobs are enabled by default for self-hosted installations plausible/analytics#603 -- All new users on self-hosted installations have a never-ending trial plausible/analytics#603 -- Changed caret/chevron color in datepicker and filters dropdown - -### Fixed -- Do not error when activating an already activated account plausible/analytics#370 -- Ignore arrow keys when modifier keys are pressed plausible/analytics#363 -- Show correct stats when goal filter is combined with source plausible/analytics#374 -- Going back in history now correctly resets the period filter plausible/analytics#408 -- Fix URL decoding in query parameters plausible/analytics#416 -- Fix overly-sticky date in query parameters plausible/analytics/#439 -- Prevent picking dates before site insertion plausible/analtics#446 -- Fix overly-sticky from and to in query parameters plausible/analytics#495 -- Adds support for single-day date selection plausible/analytics#495 -- Goal conversion rate in realtime view is now accurate plausible/analytics#500 -- Various UI/UX issues plausible/analytics#503 - -### Security -- Do not run the plausible Docker container as root plausible/analytics#362 - -## [1.1.1] - 2020-10-14 - -### Fixed -- Revert Dockerfile change that introduced a regression - -## [1.1.0] - 2020-10-14 - -### Added -- Linkify top pages [plausible/analytics#91](https://github.com/plausible/analytics/issues/91) -- Filter by country, screen size, browser and operating system [plausible/analytics#303](https://github.com/plausible/analytics/issues/303) - -### Fixed -- Fix issue with creating a PostgreSQL database when `?ssl=true` [plausible/analytics#347](https://github.com/plausible/analytics/issues/347) -- Do no disclose current URL to DuckDuckGo's favicon service [plausible/analytics#343](https://github.com/plausible/analytics/issues/343) -- Updated UAInspector database to detect newer devices [plausible/analytics#309](https://github.com/plausible/analytics/issues/309) - -## [1.0.0] - 2020-10-06 - -### Added -- Collect and present link tags (`utm_medium`, `utm_source`, `utm_campaign`) in the dashboard - -### Changed -- Replace configuration parameters `CLICKHOUSE_DATABASE_{HOST,NAME,USER,PASSWORD}` with a single `CLICKHOUSE_DATABASE_URL` [plausible/analytics#317](https://github.com/plausible/analytics/pull/317) -- Disable subscriptions by default -- Remove `CLICKHOUSE_DATABASE_POOLSIZE`, `DATABASE_POOLSIZE` and `DATABASE_TLS_ENABLED` parameters. Use query parameters in `CLICKHOUSE_DATABASE_URL` and `DATABASE_URL` instead. -- Remove `HOST` and `SCHEME` parameters in favor of a single `BASE_URL` parameter. -- Make `Bamboo.SMTPAdapter` the default as opposed to `Bamboo.PostmarkAdapter` -- Disable subscription flow by default diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 9d585fda3e45..000000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,40 +0,0 @@ -# Contributing - -## Development setup - -The easiest way to get up and running is to [install](https://docs.docker.com/get-docker/) and use Docker for running both Postgres and Clickhouse. - -Make sure Docker, Elixir, Erlang and Node.js are all installed on your development machine. - -### Start the environment: - -1. Run both `make postgres` and `make clickhouse`. -2. You can set up everything with `make install`, alternatively run each command seperately: - 1. Run `mix deps.get`. This will download the required Elixir dependencies. - 2. Run `mix ecto.create`. This will create the required databases in both Postgres and Clickhouse. - 3. Run `mix ecto.migrate` to build the database schema. - 4. Run `npm ci --prefix assets` to install the required node dependencies. -3. Run `make server` or `mix phx.server` to start the Phoenix server. -4. The system is now available on `localhost:8000`. - -### Creating an account - -1. Navigate to `http://localhost:8000/register` and fill in the form. -2. An e-mail won't actually be sent, but you can find the activation in the Phoenix logs in your terminal. Search for `[info] VERIFICATION CODE:` and enter the verification code. -3. Fill in the rest of the forms and for the domain use `dummy.site` -4. Skip the JS snippet and click start collecting data. -5. Run `make dummy_event` from the terminal to generate a fake pageview event for the dummy site. -6. You should now be all set! - -### Stopping Docker containers - -1. Stop and remove the Postgres container with `make postgres-stop`. -2. Stop and remove the Clickhouse container with `make clickhouse-stop`. - -Volumes are preserved. You'll find that the Postgres and Clickhouse state are retained when you bring them up again the next time: no need to re-register and so on. - -Note: Since we are deleting the containers, be careful when deleting volumes with `docker volume prune`. You might accidentally delete the database and would have to go through re-registration process. - -### Pre-commit hooks - -`pre-commit` requires Python to be available locally and covers JavaScript and CSS. Set up with `pip install --user pre-commit` followed by `pre-commit install`. Conversely, if the prompts are far too bothersome, remove with `pre-commit uninstall`. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index fd30fe74c49e..000000000000 --- a/SECURITY.md +++ /dev/null @@ -1,13 +0,0 @@ -# Security Policy - -## Supported Versions - -We only add security updates to the latest MAJOR.MINOR version of the project. No security updates are backported to previous versions. If you -want be up to date on security patches, make sure your Plausible image is up to date with `plausible/analytics:latest` - -## Reporting a Vulnerability - -If you've found a security vulnerability with the Plausible codebase, you can disclose it responsibly by sending a summary to security@plausible.io. -We will review the potential threat and fix it as fast as we can. - -While we do not have a bounty program in place yet, we are incredibly thankful for people who take the time to share their findings with us. Whether it's a tiny bug that you've found or a security vulnerability, all reports help us to continuously improve Plausible for everyone. Thank you! diff --git a/assets/js/dashboard/router.js b/assets/js/dashboard/router.js index b6d769435226..459daa66dceb 100644 --- a/assets/js/dashboard/router.js +++ b/assets/js/dashboard/router.js @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import {BrowserRouter, Switch, Route, useLocation} from "react-router-dom"; +import { BrowserRouter, Switch, Route, useLocation } from "react-router-dom"; import Dash from './index' import SourcesModal from './stats/modals/sources' @@ -23,41 +23,41 @@ function ScrollToTop() { return null; } -export default function Router({site, loggedIn, currentUserRole}) { +export default function Router({ site, loggedIn, currentUserRole }) { return ( - + - + - + - + - + - + - + - - + + - - + + - - + + - + diff --git a/lib/plausible/auth/password.ex b/lib/plausible/auth/password.ex deleted file mode 100644 index 193d6e97a0a8..000000000000 --- a/lib/plausible/auth/password.ex +++ /dev/null @@ -1,13 +0,0 @@ -defmodule Plausible.Auth.Password do - def hash(password) do - Bcrypt.hash_pwd_salt(password) - end - - def match?(password, hash) do - Bcrypt.verify_pass(password, hash) - end - - def dummy_calculation do - Bcrypt.no_user_verify() - end -end diff --git a/lib/plausible/auth/user.ex b/lib/plausible/auth/user.ex deleted file mode 100644 index d3518dafe056..000000000000 --- a/lib/plausible/auth/user.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule Plausible.Auth.User do - use Ecto.Schema - - schema "users" do - field :email, :string - field :password_hash - field :password, :string, virtual: true - field :password_confirmation, :string, virtual: true - field :name, :string - field :last_seen, :naive_datetime - field :trial_expiry_date, :date - field :theme, :string - field :email_verified, :boolean - - has_many :site_memberships, Plausible.Site.Membership - has_many :sites, through: [:site_memberships, :site] - - timestamps() - end -end diff --git a/lib/plausible/clickhouse_repo.ex b/lib/plausible/clickhouse_repo.ex index b77ef717f7d9..6304c49501f4 100644 --- a/lib/plausible/clickhouse_repo.ex +++ b/lib/plausible/clickhouse_repo.ex @@ -17,22 +17,4 @@ defmodule Plausible.ClickhouseRepo do Ecto.Adapters.SQL.query!(__MODULE__, events_sql, [domain]) Ecto.Adapters.SQL.query!(__MODULE__, sessions_sql, [domain]) end - - def clear_imported_stats_for(site_id) do - [ - "imported_visitors", - "imported_sources", - "imported_pages", - "imported_entry_pages", - "imported_exit_pages", - "imported_locations", - "imported_devices", - "imported_browsers", - "imported_operating_systems" - ] - |> Enum.map(fn table -> - sql = "ALTER TABLE #{table} DELETE WHERE site_id = ?" - Ecto.Adapters.SQL.query!(__MODULE__, sql, [site_id]) - end) - end end diff --git a/lib/plausible/site/membership.ex b/lib/plausible/site/membership.ex deleted file mode 100644 index 52e7c94e830c..000000000000 --- a/lib/plausible/site/membership.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Plausible.Site.Membership do - use Ecto.Schema - import Ecto.Changeset - - schema "site_memberships" do - field :role, Ecto.Enum, values: [:owner, :admin, :viewer] - belongs_to :site, Plausible.Site - belongs_to :user, Plausible.Auth.User - - timestamps() - end - - def changeset(schema, attrs) do - schema - |> cast(attrs, [:user_id, :site_id, :role]) - |> validate_required([:user_id, :site_id]) - end -end diff --git a/lib/plausible/site/site.ex b/lib/plausible/site/site.ex deleted file mode 100644 index 99ae26ef46b1..000000000000 --- a/lib/plausible/site/site.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Plausible.Site do - use Ecto.Schema - alias Plausible.Auth.User - - @derive {Jason.Encoder, only: [:domain, :timezone]} - schema "sites" do - field :domain, :string - field :timezone, :string, default: "Etc/UTC" - field :public, :boolean - field :locked, :boolean - field :has_stats, :boolean - - many_to_many :members, User, join_through: Plausible.Site.Membership - has_many :memberships, Plausible.Site.Membership - - timestamps() - end -end diff --git a/lib/plausible/sites.ex b/lib/plausible/sites.ex deleted file mode 100644 index 1ddea35893c3..000000000000 --- a/lib/plausible/sites.ex +++ /dev/null @@ -1,69 +0,0 @@ -defmodule Plausible.Sites do - use Plausible.Repo - - def has_stats?(_site), do: true - - def get_for_user!(user_id, domain, roles \\ [:owner, :admin, :viewer]), - do: Repo.one!(get_for_user_q(user_id, domain, roles)) - - def get_for_user(user_id, domain, roles \\ [:owner, :admin, :viewer]), - do: Repo.one(get_for_user_q(user_id, domain, roles)) - - defp get_for_user_q(user_id, domain, roles) do - from(s in Plausible.Site, - join: sm in Plausible.Site.Membership, - on: sm.site_id == s.id, - where: sm.user_id == ^user_id, - where: sm.role in ^roles, - where: s.domain == ^domain, - select: s - ) - end - - def is_member?(user_id, site) do - role(user_id, site) !== nil - end - - def has_admin_access?(user_id, site) do - role(user_id, site) in [:admin, :owner] - end - - def role(user_id, site) do - Repo.one( - from sm in Plausible.Site.Membership, - where: sm.user_id == ^user_id and sm.site_id == ^site.id, - select: sm.role - ) - end - - def owned_by(user) do - Repo.all( - from s in Plausible.Site, - join: sm in Plausible.Site.Membership, - on: sm.site_id == s.id, - where: sm.role == :owner, - where: sm.user_id == ^user.id - ) - end - - def count_owned_by(user) do - Repo.one( - from s in Plausible.Site, - join: sm in Plausible.Site.Membership, - on: sm.site_id == s.id, - where: sm.role == :owner, - where: sm.user_id == ^user.id, - select: count(sm) - ) - end - - def owner_for(site) do - Repo.one( - from u in Plausible.Auth.User, - join: sm in Plausible.Site.Membership, - on: sm.user_id == u.id, - where: sm.site_id == ^site.id, - where: sm.role == :owner - ) - end -end diff --git a/lib/plausible/stats/aggregate.ex b/lib/plausible/stats/aggregate.ex index b79fc521c601..fadf6c1d4773 100644 --- a/lib/plausible/stats/aggregate.ex +++ b/lib/plausible/stats/aggregate.ex @@ -6,36 +6,14 @@ defmodule Plausible.Stats.Aggregate do @event_metrics [:visitors, :pageviews, :events, :sample_percent] @session_metrics [:visits, :bounce_rate, :visit_duration, :sample_percent] - def aggregate(%{domain: "all"} = site, query, metrics) do - event_metrics = Enum.filter(metrics, &(&1 in @event_metrics)) - event_task = Task.async(fn -> aggregate_events(site, query, event_metrics) end) - session_metrics = Enum.filter(metrics, &(&1 in @session_metrics)) - session_task = Task.async(fn -> aggregate_sessions(site, query, session_metrics) end) - - Task.await(session_task, 10_000) - |> Map.merge(Task.await(event_task, 10_000)) - |> Enum.map(fn {metric, value} -> - {metric, %{value: round(value || 0)}} - end) - |> Enum.into(%{}) - end - def aggregate(site, query, metrics) do event_metrics = Enum.filter(metrics, &(&1 in @event_metrics)) event_task = Task.async(fn -> aggregate_events(site, query, event_metrics) end) session_metrics = Enum.filter(metrics, &(&1 in @session_metrics)) session_task = Task.async(fn -> aggregate_sessions(site, query, session_metrics) end) - time_on_page_task = - if :time_on_page in metrics do - Task.async(fn -> aggregate_time_on_page(site, query) end) - else - Task.async(fn -> %{} end) - end - Task.await(session_task, 10_000) |> Map.merge(Task.await(event_task, 10_000)) - |> Map.merge(Task.await(time_on_page_task, 10_000)) |> Enum.map(fn {metric, value} -> {metric, %{value: round(value || 0)}} end) @@ -44,12 +22,6 @@ defmodule Plausible.Stats.Aggregate do defp aggregate_events(_, _, []), do: %{} - defp aggregate_events(%{domain: "all"} = site, query, metrics) do - from(e in base_event_query(site, query), select: %{}) - |> select_event_metrics(metrics) - |> ClickhouseRepo.one() - end - defp aggregate_events(site, query, metrics) do from(e in base_event_query(site, query), select: %{}) |> select_event_metrics(metrics) @@ -58,82 +30,11 @@ defmodule Plausible.Stats.Aggregate do defp aggregate_sessions(_, _, []), do: %{} - defp aggregate_sessions(%{domain: "all"} = site, query, metrics) do - query = Query.treat_page_filter_as_entry_page(query) - - from(e in query_sessions(site, query), select: %{}) - |> select_session_metrics(metrics) - |> ClickhouseRepo.one() - end - defp aggregate_sessions(site, query, metrics) do query = Query.treat_page_filter_as_entry_page(query) from(e in query_sessions(site, query), select: %{}) - |> filter_converted_sessions(site, query) |> select_session_metrics(metrics) |> ClickhouseRepo.one() end - - defp aggregate_time_on_page(site, query) do - q = - from( - e in base_event_query(site, %Query{ - query - | filters: Map.delete(query.filters, "event:page") - }), - select: { - fragment("? as p", e.pathname), - fragment("? as t", e.timestamp), - fragment("? as s", e.session_id) - }, - order_by: [e.session_id, e.timestamp] - ) - - {base_query_raw, base_query_raw_params} = ClickhouseRepo.to_sql(:all, q) - - {where_clause, where_arg} = - case query.filters["event:page"] do - {:is, page} -> - {"p = ?", page} - - {:is_not, page} -> - {"p != ?", page} - - {:matches, expr} -> - regex = page_regex(expr) - {"match(p, ?)", regex} - - {:does_not_match, expr} -> - regex = page_regex(expr) - {"not(match(p, ?))", regex} - end - - time_query = " - SELECT - avg(ifNotFinite(avgTime, null)) - FROM - (SELECT - p, - sum(td)/count(case when p2 != p then 1 end) as avgTime - FROM - (SELECT - p, - p2, - sum(t2-t) as td - FROM - (SELECT - *, - neighbor(t, 1) as t2, - neighbor(p, 1) as p2, - neighbor(s, 1) as s2 - FROM (#{base_query_raw})) - WHERE s=s2 AND #{where_clause} - GROUP BY p,p2,s) - GROUP BY p)" - - {:ok, res} = ClickhouseRepo.query(time_query, base_query_raw_params ++ [where_arg]) - [[time_on_page]] = res.rows - %{time_on_page: time_on_page} - end end diff --git a/lib/plausible/stats/base.ex b/lib/plausible/stats/base.ex index bcf54142f3c2..eefe65178008 100644 --- a/lib/plausible/stats/base.ex +++ b/lib/plausible/stats/base.ex @@ -4,6 +4,7 @@ defmodule Plausible.Stats.Base do import Ecto.Query @no_ref "Direct / None" + @timezone "Europe/Warsaw" def base_event_query(site, query) do events_q = query_events(site, query) @@ -25,8 +26,8 @@ defmodule Plausible.Stats.Base do end end - def query_events(%{domain: "all"} = site, query) do - {first_datetime, last_datetime} = utc_boundaries(query, site.timezone) + def query_events(_site, query) do + {first_datetime, last_datetime} = utc_boundaries(query, @timezone) q = from( @@ -110,93 +111,6 @@ defmodule Plausible.Stats.Base do end) end - def query_events(site, query) do - {first_datetime, last_datetime} = utc_boundaries(query, site.timezone) - - q = - from( - e in "events", - where: e.domain == ^site.domain, - where: e.timestamp >= ^first_datetime and e.timestamp < ^last_datetime - ) - |> add_sample_hint(query) - - q = - case query.filters["event:page"] do - {:is, page} -> - from(e in q, where: e.pathname == ^page) - - {:is_not, page} -> - from(e in q, where: e.pathname != ^page) - - {:matches, glob_expr} -> - regex = page_regex(glob_expr) - from(e in q, where: fragment("match(?, ?)", e.pathname, ^regex)) - - {:does_not_match, glob_expr} -> - regex = page_regex(glob_expr) - from(e in q, where: fragment("not(match(?, ?))", e.pathname, ^regex)) - - {:member, list} -> - from(e in q, where: e.pathname in ^list) - - nil -> - q - end - - q = - case query.filters["event:name"] do - {:is, name} -> - from(e in q, where: e.name == ^name) - - {:member, list} -> - from(e in q, where: e.name in ^list) - - nil -> - q - end - - q = - case query.filters["event:goal"] do - {:is, :page, path} -> - from(e in q, where: e.pathname == ^path) - - {:matches, :page, expr} -> - regex = page_regex(expr) - from(e in q, where: fragment("match(?, ?)", e.pathname, ^regex)) - - {:is, :event, event} -> - from(e in q, where: e.name == ^event) - - nil -> - q - end - - Enum.reduce(query.filters, q, fn {filter_key, filter_value}, query -> - case filter_key do - "event:props:" <> prop_name -> - filter_value = elem(filter_value, 1) - - if filter_value == "(none)" do - from( - e in query, - where: fragment("not has(?, ?)", field(e, :"meta.key"), ^prop_name) - ) - else - from( - e in query, - inner_lateral_join: meta in "meta", - as: :meta, - where: meta.key == ^prop_name and meta.value == ^filter_value - ) - end - - _ -> - query - end - end) - end - @api_prop_name_to_db %{ "source" => "referrer_source", "device" => "screen_size", @@ -208,86 +122,14 @@ defmodule Plausible.Stats.Base do "city" => "city_geoname_id" } - def query_sessions(%{domain: "all"} = site, query) do - {first_datetime, last_datetime} = utc_boundaries(query, site.timezone) - - sessions_q = - from( - s in "sessions", - where: s.start >= ^first_datetime and s.start < ^last_datetime - ) - - sessions_q = - case {query.filters["visit:goal"], query.filters["visit:page"]} do - {nil, nil} -> - sessions_q - - {goal_filter, page_filter} -> - events_query = - Query.put_filter(query, "event:goal", goal_filter) - |> Query.put_filter("event:name", nil) - |> Query.put_filter("event:page", page_filter) - - events_q = - from( - s in query_events(site, events_query), - select: %{session_id: fragment("DISTINCT ?", s.session_id)} - ) - - from( - s in sessions_q, - join: sq in subquery(events_q), - on: s.session_id == sq.session_id - ) - end - - Enum.reduce(Filters.visit_props(), sessions_q, fn prop_name, sessions_q -> - filter = query.filters["visit:" <> prop_name] - - prop_name = - Map.get(@api_prop_name_to_db, prop_name, prop_name) - |> String.to_existing_atom() - - case filter do - {:is, value} -> - value = db_prop_val(prop_name, value) - from(s in sessions_q, where: fragment("? = ?", field(s, ^prop_name), ^value)) - - {:is_not, value} -> - value = db_prop_val(prop_name, value) - from(s in sessions_q, where: fragment("? != ?", field(s, ^prop_name), ^value)) - - {:member, values} -> - list = Enum.map(values, &db_prop_val(prop_name, &1)) - from(s in sessions_q, where: fragment("? in tuple(?)", field(s, ^prop_name), ^list)) - - {:matches, expr} -> - regex = page_regex(expr) - from(s in sessions_q, where: fragment("match(?, ?)", field(s, ^prop_name), ^regex)) - - {:does_not_match, expr} -> - regex = page_regex(expr) - from(s in sessions_q, where: fragment("not(match(?, ?))", field(s, ^prop_name), ^regex)) - - nil -> - sessions_q - - _ -> - raise "Unknown filter type" - end - end) - end - def query_sessions(site, query) do - {first_datetime, last_datetime} = utc_boundaries(query, site.timezone) + {first_datetime, last_datetime} = utc_boundaries(query, @timezone) sessions_q = from( s in "sessions", - where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime ) - |> add_sample_hint(query) sessions_q = case {query.filters["visit:goal"], query.filters["visit:page"]} do @@ -522,14 +364,4 @@ defmodule Plausible.Stats.Base do |> String.replace(~r/\*\*/, ".*") |> String.replace(~r/(? - db_q - - threshold -> - from(e in db_q, hints: [sample: threshold]) - end - end end diff --git a/lib/plausible/stats/breakdown.ex b/lib/plausible/stats/breakdown.ex index cdf85595a194..dba438bf8cbf 100644 --- a/lib/plausible/stats/breakdown.ex +++ b/lib/plausible/stats/breakdown.ex @@ -8,103 +8,12 @@ defmodule Plausible.Stats.Breakdown do @session_metrics [:visits, :bounce_rate, :visit_duration] @event_props ["event:page", "event:page_match", "event:name"] - def breakdown(site, query, "event:goal", metrics, pagination) do - {event_goals, pageview_goals} = {[], []} - - events = Enum.map(event_goals, & &1.event_name) - event_query = %Query{query | filters: Map.put(query.filters, "event:name", {:member, events})} - - event_results = - if Enum.any?(event_goals) do - breakdown(site, event_query, "event:name", metrics, pagination) - |> transform_keys(%{name: :goal}) - else - [] - end - - {limit, page} = pagination - offset = (page - 1) * limit - - page_results = - if Enum.any?(pageview_goals) do - page_exprs = Enum.map(pageview_goals, & &1.page_path) - page_regexes = Enum.map(page_exprs, &page_regex/1) - - from(e in base_event_query(site, query), - order_by: [desc: fragment("uniq(?)", e.user_id)], - limit: ^limit, - offset: ^offset, - where: - fragment( - "notEmpty(multiMatchAllIndices(?, array(?)) as indices)", - e.pathname, - ^page_regexes - ), - group_by: fragment("index"), - select: %{ - index: fragment("arrayJoin(indices) as index"), - goal: fragment("concat('Visit ', array(?)[index])", ^page_exprs) - } - ) - |> select_event_metrics(metrics) - |> ClickhouseRepo.all() - |> Enum.map(fn row -> Map.delete(row, :index) end) - else - [] - end - - zip_results(event_results, page_results, :goal, metrics) - end - - def breakdown(site, query, "event:props:" <> custom_prop, metrics, pagination) do - {limit, _} = pagination - - none_result = - if !Enum.any?(query.filters, fn {key, _} -> String.starts_with?(key, "event:props") end) do - none_filters = Map.put(query.filters, "event:props:" <> custom_prop, {:is, "(none)"}) - none_query = %Query{query | filters: none_filters} - - from(e in base_event_query(site, none_query), - order_by: [desc: fragment("uniq(?)", e.user_id)], - select: %{}, - select_merge: %{^custom_prop => "(none)"}, - having: fragment("uniq(?)", e.user_id) > 0 - ) - |> select_event_metrics(metrics) - |> ClickhouseRepo.all() - else - [] - end - - results = breakdown_events(site, query, "event:props:" <> custom_prop, metrics, pagination) - - zipped = zip_results(none_result, results, custom_prop, metrics) - - if Enum.find_index(zipped, fn value -> value[custom_prop] == "(none)" end) == limit do - Enum.slice(zipped, 0..(limit - 1)) - else - zipped - end - end - def breakdown(site, query, "event:page", metrics, pagination) do event_metrics = Enum.filter(metrics, &(&1 in @event_metrics)) session_metrics = Enum.filter(metrics, &(&1 in @session_metrics)) event_result = breakdown_events(site, query, "event:page", event_metrics, pagination) - event_result = - if :time_on_page in metrics do - pages = Enum.map(event_result, & &1[:page]) - time_on_page_result = breakdown_time_on_page(site, query, pages) - - Enum.map(event_result, fn row -> - Map.put(row, :time_on_page, time_on_page_result[row[:page]]) - end) - else - event_result - end - new_query = case event_result do [] -> @@ -219,54 +128,6 @@ defmodule Plausible.Stats.Breakdown do |> transform_keys(%{operating_system: :os}) end - defp breakdown_time_on_page(_site, _query, []) do - [] - end - - defp breakdown_time_on_page(site, query, pages) do - q = - from( - e in base_event_query(site, %Query{ - query - | filters: Map.delete(query.filters, "event:page") - }), - select: { - fragment("? as p", e.pathname), - fragment("? as t", e.timestamp), - fragment("? as s", e.session_id) - }, - order_by: [e.session_id, e.timestamp] - ) - - {base_query_raw, base_query_raw_params} = ClickhouseRepo.to_sql(:all, q) - - select = "round(sum(td)/count(case when p2 != p then 1 end))" - - time_query = " - SELECT - p, - #{select} - FROM - (SELECT - p, - p2, - sum(t2-t) as td - FROM - (SELECT - *, - neighbor(t, 1) as t2, - neighbor(p, 1) as p2, - neighbor(s, 1) as s2 - FROM (#{base_query_raw})) - WHERE s=s2 AND p IN tuple(?) - GROUP BY p,p2,s) - GROUP BY p" - - {:ok, res} = ClickhouseRepo.query(time_query, base_query_raw_params ++ [pages]) - - res.rows |> Enum.map(fn [page, time] -> {page, time} end) |> Enum.into(%{}) - end - defp do_group_by( %Ecto.Query{ from: %Ecto.Query.FromExpr{source: {"events", _}}, diff --git a/lib/plausible/stats/clickhouse.ex b/lib/plausible/stats/clickhouse.ex deleted file mode 100644 index 76dd9eb700e3..000000000000 --- a/lib/plausible/stats/clickhouse.ex +++ /dev/null @@ -1,571 +0,0 @@ -defmodule Plausible.Stats.Clickhouse do - use Plausible.Repo - use Plausible.ClickhouseRepo - alias Plausible.Stats.Query - use Plausible.Stats.Fragments - @no_ref "Direct / None" - - def usage_breakdown(domains) do - range = - Date.range( - Timex.shift(Timex.today(), days: -30), - Timex.today() - ) - - usage_breakdown(domains, range) - end - - def usage_breakdown(domains, date_range) do - Enum.chunk_every(domains, 300) - |> Enum.reduce({0, 0}, fn domains, {pageviews_total, custom_events_total} -> - {chunk_pageviews, chunk_custom_events} = - ClickhouseRepo.one( - from e in "events", - where: e.domain in ^domains, - where: fragment("toDate(?)", e.timestamp) >= ^date_range.first, - where: fragment("toDate(?)", e.timestamp) <= ^date_range.last, - select: { - fragment("countIf(? = 'pageview')", e.name), - fragment("countIf(? != 'pageview')", e.name) - } - ) - - {pageviews_total + chunk_pageviews, custom_events_total + chunk_custom_events} - end) - end - - def top_sources(site, query, limit, page, show_noref \\ false, include_details) do - offset = (page - 1) * limit - - referrers = - from(s in base_session_query(site, query), - group_by: s.referrer_source, - order_by: [desc: uniq(s.user_id), asc: fragment("min(start)")], - limit: ^limit, - offset: ^offset - ) - |> filter_converted_sessions(site, query) - - referrers = - if show_noref do - referrers - else - from(s in referrers, where: s.referrer_source != "") - end - - referrers = apply_page_as_entry_page(referrers, site, query) - - referrers = - if include_details do - from( - s in referrers, - select: %{ - name: - fragment( - "if(empty(?), ?, ?) as name", - s.referrer_source, - @no_ref, - s.referrer_source - ), - url: fragment("any(?)", s.referrer), - count: uniq(s.user_id), - bounce_rate: bounce_rate(), - visit_duration: visit_duration() - } - ) - else - from( - s in referrers, - select: %{ - name: - fragment( - "if(empty(?), ?, ?) as name", - s.referrer_source, - @no_ref, - s.referrer_source - ), - url: fragment("any(?)", s.referrer), - count: uniq(s.user_id) - } - ) - end - - ClickhouseRepo.all(referrers) - |> Enum.map(fn ref -> - Map.update(ref, :url, nil, fn url -> url && URI.parse("http://" <> url).host end) - end) - end - - defp filter_converted_sessions(db_query, site, query) do - goal = query.filters["goal"] - page = query.filters[:page] - - if is_binary(goal) || is_binary(page) do - converted_sessions = - from(e in base_query(site, query), - select: %{session_id: fragment("DISTINCT ?", e.session_id)} - ) - - from(s in db_query, - join: cs in subquery(converted_sessions), - on: s.session_id == cs.session_id - ) - else - db_query - end - end - - defp apply_page_as_entry_page(db_query, _site, query) do - include_path_filter_entry(db_query, query.filters[:page]) - end - - def current_visitors(site, query) do - Plausible.ClickhouseRepo.one( - from e in base_query(site, query), - select: uniq(e.user_id) - ) - end - - def has_pageviews?([]), do: false - - def has_pageviews?(domains) when is_list(domains) do - ClickhouseRepo.exists?( - from e in "events", - select: e.timestamp, - where: fragment("? IN tuple(?)", e.domain, ^domains) - ) - end - - def has_pageviews?(site) do - ClickhouseRepo.exists?( - from e in "events", where: e.domain == ^site.domain and e.name == "pageview" - ) - end - - def last_24h_visitors([]), do: %{} - - def last_24h_visitors(sites) do - domains = Enum.map(sites, & &1.domain) - - ClickhouseRepo.all( - from e in "events", - group_by: e.domain, - where: fragment("? IN tuple(?)", e.domain, ^domains), - where: e.timestamp > fragment("now() - INTERVAL 24 HOUR"), - select: {e.domain, fragment("uniq(user_id)")} - ) - |> Enum.into(%{}) - end - - defp base_session_query(site, query) do - {first_datetime, last_datetime} = utc_boundaries(query, site.timezone) - - q = - from(s in "sessions", - hints: ["SAMPLE 10000000"], - where: s.domain == ^site.domain, - where: s.timestamp >= ^first_datetime and s.start < ^last_datetime - ) - - q = - if query.filters["source"] do - source = query.filters["source"] - source = if source == @no_ref, do: "", else: source - from(s in q, where: s.referrer_source == ^source) - else - q - end - - q = - if query.filters["screen"] do - size = query.filters["screen"] - from(s in q, where: s.screen_size == ^size) - else - q - end - - q = - if query.filters["browser"] do - browser = query.filters["browser"] - from(s in q, where: s.browser == ^browser) - else - q - end - - q = - if query.filters["browser_version"] do - version = query.filters["browser_version"] - from(s in q, where: s.browser_version == ^version) - else - q - end - - q = - if query.filters["os"] do - os = query.filters["os"] - from(s in q, where: s.operating_system == ^os) - else - q - end - - q = - if query.filters["os_version"] do - version = query.filters["os_version"] - from(s in q, where: s.operating_system_version == ^version) - else - q - end - - q = - if query.filters["country"] do - country = query.filters["country"] - from(s in q, where: s.country_code == ^country) - else - q - end - - q = - if query.filters["utm_medium"] do - utm_medium = query.filters["utm_medium"] - from(s in q, where: s.utm_medium == ^utm_medium) - else - q - end - - q = - if query.filters["utm_source"] do - utm_source = query.filters["utm_source"] - from(s in q, where: s.utm_source == ^utm_source) - else - q - end - - q = - if query.filters["utm_campaign"] do - utm_campaign = query.filters["utm_campaign"] - from(s in q, where: s.utm_campaign == ^utm_campaign) - else - q - end - - q = - if query.filters["utm_content"] do - utm_content = query.filters["utm_content"] - from(s in q, where: s.utm_content == ^utm_content) - else - q - end - - q = - if query.filters["utm_term"] do - utm_term = query.filters["utm_term"] - from(s in q, where: s.utm_term == ^utm_term) - else - q - end - - q = include_path_filter_entry(q, query.filters["entry_page"]) - - q = include_path_filter_exit(q, query.filters["exit_page"]) - - if query.filters["referrer"] do - ref = query.filters["referrer"] - from(s in q, where: s.referrer == ^ref) - else - q - end - end - - defp base_query_bare(site, query) do - {first_datetime, last_datetime} = utc_boundaries(query, site.timezone) - - q = - from(e in "events", - hints: ["SAMPLE 10000000"], - where: e.domain == ^site.domain, - where: e.timestamp >= ^first_datetime and e.timestamp < ^last_datetime - ) - - q = - if query.filters["screen"] do - size = query.filters["screen"] - from(e in q, where: e.screen_size == ^size) - else - q - end - - q = - if query.filters["browser"] do - browser = query.filters["browser"] - from(s in q, where: s.browser == ^browser) - else - q - end - - q = - if query.filters["browser_version"] do - version = query.filters["browser_version"] - from(s in q, where: s.browser_version == ^version) - else - q - end - - q = - if query.filters["os"] do - os = query.filters["os"] - from(s in q, where: s.operating_system == ^os) - else - q - end - - q = - if query.filters["os_version"] do - version = query.filters["os_version"] - from(s in q, where: s.operating_system_version == ^version) - else - q - end - - q = - if query.filters["country"] do - country = query.filters["country"] - from(s in q, where: s.country_code == ^country) - else - q - end - - q = - if query.filters["utm_medium"] do - utm_medium = query.filters["utm_medium"] - from(e in q, where: e.utm_medium == ^utm_medium) - else - q - end - - q = - if query.filters["utm_source"] do - utm_source = query.filters["utm_source"] - from(e in q, where: e.utm_source == ^utm_source) - else - q - end - - q = - if query.filters["utm_campaign"] do - utm_campaign = query.filters["utm_campaign"] - from(e in q, where: e.utm_campaign == ^utm_campaign) - else - q - end - - q = - if query.filters["utm_content"] do - utm_content = query.filters["utm_content"] - from(e in q, where: e.utm_content == ^utm_content) - else - q - end - - q = - if query.filters["utm_term"] do - utm_term = query.filters["utm_term"] - from(e in q, where: e.utm_term == ^utm_term) - else - q - end - - q = - if query.filters["referrer"] do - ref = query.filters["referrer"] - from(e in q, where: e.referrer == ^ref) - else - q - end - - q = include_path_filter(q, query.filters[:page]) - - if query.filters["props"] do - [{key, val}] = query.filters["props"] |> Enum.into([]) - - if val == "(none)" do - from( - e in q, - where: fragment("not has(meta.key, ?)", ^key) - ) - else - from( - e in q, - inner_lateral_join: meta in fragment("meta as m"), - where: meta.key == ^key and meta.value == ^val - ) - end - else - q - end - end - - defp base_query(site, query) do - base_query_bare(site, query) |> include_goal_conversions(query) - end - - defp utc_boundaries(%Query{period: "30m"}, _timezone) do - last_datetime = NaiveDateTime.utc_now() - - first_datetime = last_datetime |> Timex.shift(minutes: -30) - {first_datetime, last_datetime} - end - - defp utc_boundaries(%Query{period: "realtime"}, _timezone) do - last_datetime = NaiveDateTime.utc_now() - - first_datetime = last_datetime |> Timex.shift(minutes: -5) - {first_datetime, last_datetime} - end - - defp utc_boundaries(%Query{date_range: date_range}, timezone) do - {:ok, first} = NaiveDateTime.new(date_range.first, ~T[00:00:00]) - - first_datetime = - Timex.to_datetime(first, timezone) - |> Timex.Timezone.convert("UTC") - - {:ok, last} = NaiveDateTime.new(date_range.last |> Timex.shift(days: 1), ~T[00:00:00]) - - last_datetime = - Timex.to_datetime(last, timezone) - |> Timex.Timezone.convert("UTC") - - {first_datetime, last_datetime} - end - - defp event_name_for_goal(query) do - case query.filters["goal"] do - "Visit " <> page -> - {"pageview", page} - - goal when is_binary(goal) -> - {goal, nil} - - _ -> - {nil, nil} - end - end - - defp include_goal_conversions(db_query, query) do - {goal_event, path} = event_name_for_goal(query) - - q = - if goal_event do - from(e in db_query, where: e.name == ^goal_event) - else - from(e in db_query, where: e.name == "pageview") - end - - if path do - {contains_regex, path_regex} = convert_path_regex(path) - - if contains_regex do - from(e in q, where: fragment("match(?, ?)", e.pathname, ^path_regex)) - else - from(e in q, where: e.pathname == ^path) - end - else - q - end - end - - defp check_negated_filter(filter) do - negated = String.at(filter, 0) == "!" - updated_filter = if negated, do: String.slice(filter, 1..-1), else: filter - - {negated, updated_filter} - end - - defp convert_path_regex(path) do - contains_regex = String.match?(path, ~r/\*/) - - regex = - "^#{path}\/?$" - |> String.replace(~r/\*\*/, ".*") - |> String.replace(~r/(? Timex.shift(minutes: -5) @@ -15,17 +15,4 @@ defmodule Plausible.Stats.CurrentVisitors do select: uniq(e.user_id) ) end - - def current_visitors(site) do - first_datetime = - NaiveDateTime.utc_now() - |> Timex.shift(minutes: -5) - - ClickhouseRepo.one( - from e in "events", - where: e.domain == ^site.domain, - where: e.timestamp >= ^first_datetime, - select: uniq(e.user_id) - ) - end end diff --git a/lib/plausible/stats/query.ex b/lib/plausible/stats/query.ex index a29269ad560e..3614d996d693 100644 --- a/lib/plausible/stats/query.ex +++ b/lib/plausible/stats/query.ex @@ -5,21 +5,22 @@ defmodule Plausible.Stats.Query do filters: %{}, sample_threshold: 20_000_000 + @timezone "Europe/Warsaw" @default_sample_threshold 20_000_000 - def shift_back(%__MODULE__{period: "month"} = query, site) do + def shift_back(%__MODULE__{period: "month"} = query, _site) do # Querying current month to date {new_first, new_last} = - if Timex.compare(Timex.now(site.timezone), query.date_range.first, :month) == 0 do + if Timex.compare(Timex.now(@timezone), query.date_range.first, :month) == 0 do diff = Timex.diff( - Timex.beginning_of_month(Timex.now(site.timezone)), - Timex.now(site.timezone), + Timex.beginning_of_month(Timex.now(@timezone)), + Timex.now(@timezone), :days ) - 1 {query.date_range.first |> Timex.shift(days: diff), - Timex.now(site.timezone) |> Timex.to_date() |> Timex.shift(days: diff)} + Timex.now(@timezone) |> Timex.to_date() |> Timex.shift(days: diff)} else diff = Timex.diff(query.date_range.first, query.date_range.last, :days) - 1 @@ -37,8 +38,8 @@ defmodule Plausible.Stats.Query do Map.put(query, :date_range, Date.range(new_first, new_last)) end - def from(site, %{"period" => "realtime"} = params) do - date = today(site.timezone) + def from(_site, %{"period" => "realtime"} = params) do + date = today(@timezone) %__MODULE__{ period: "realtime", @@ -49,8 +50,8 @@ defmodule Plausible.Stats.Query do } end - def from(%{domain: "all"} = site, %{"period" => "day"} = params) do - date = parse_single_date(site.timezone, params) + def from(_site, %{"period" => "day"} = params) do + date = parse_single_date(@timezone, params) %__MODULE__{ period: "day", @@ -61,20 +62,8 @@ defmodule Plausible.Stats.Query do } end - def from(site, %{"period" => "day"} = params) do - date = parse_single_date(site.timezone, params) - - %__MODULE__{ - period: "day", - date_range: Date.range(date, date), - interval: "hour", - filters: parse_filters(params), - sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold) - } - end - - def from(%{domain: "all"} = site, %{"period" => "7d"} = params) do - end_date = parse_single_date(site.timezone, params) + def from(_site, %{"period" => "7d"} = params) do + end_date = parse_single_date(@timezone, params) start_date = end_date |> Timex.shift(days: -6) %__MODULE__{ @@ -86,21 +75,8 @@ defmodule Plausible.Stats.Query do } end - def from(site, %{"period" => "7d"} = params) do - end_date = parse_single_date(site.timezone, params) - start_date = end_date |> Timex.shift(days: -6) - - %__MODULE__{ - period: "7d", - date_range: Date.range(start_date, end_date), - interval: "date", - filters: parse_filters(params), - sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold) - } - end - - def from(%{domain: "all"} = site, %{"period" => "30d"} = params) do - end_date = parse_single_date(site.timezone, params) + def from(_site, %{"period" => "30d"} = params) do + end_date = parse_single_date(@timezone, params) start_date = end_date |> Timex.shift(days: -30) %__MODULE__{ @@ -112,36 +88,8 @@ defmodule Plausible.Stats.Query do } end - def from(site, %{"period" => "30d"} = params) do - end_date = parse_single_date(site.timezone, params) - start_date = end_date |> Timex.shift(days: -30) - - %__MODULE__{ - period: "30d", - date_range: Date.range(start_date, end_date), - interval: "date", - filters: parse_filters(params), - sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold) - } - end - - def from(%{domain: "all"} = site, %{"period" => "month"} = params) do - date = parse_single_date(site.timezone, params) - - start_date = Timex.beginning_of_month(date) - end_date = Timex.end_of_month(date) - - %__MODULE__{ - period: "month", - date_range: Date.range(start_date, end_date), - interval: "date", - filters: parse_filters(params), - sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold) - } - end - - def from(site, %{"period" => "month"} = params) do - date = parse_single_date(site.timezone, params) + def from(_site, %{"period" => "month"} = params) do + date = parse_single_date(@timezone, params) start_date = Timex.beginning_of_month(date) end_date = Timex.end_of_month(date) @@ -155,9 +103,9 @@ defmodule Plausible.Stats.Query do } end - def from(%{domain: "all"} = site, %{"period" => "6mo"} = params) do + def from(_site, %{"period" => "6mo"} = params) do end_date = - parse_single_date(site.timezone, params) + parse_single_date(@timezone, params) |> Timex.end_of_month() start_date = @@ -173,45 +121,9 @@ defmodule Plausible.Stats.Query do } end - def from(site, %{"period" => "6mo"} = params) do + def from(_site, %{"period" => "12mo"} = params) do end_date = - parse_single_date(site.timezone, params) - |> Timex.end_of_month() - - start_date = - Timex.shift(end_date, months: -5) - |> Timex.beginning_of_month() - - %__MODULE__{ - period: "6mo", - date_range: Date.range(start_date, end_date), - interval: Map.get(params, "interval", "month"), - filters: parse_filters(params), - sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold) - } - end - - def from(%{domain: "all"} = site, %{"period" => "12mo"} = params) do - end_date = - parse_single_date(site.timezone, params) - |> Timex.end_of_month() - - start_date = - Timex.shift(end_date, months: -11) - |> Timex.beginning_of_month() - - %__MODULE__{ - period: "12mo", - date_range: Date.range(start_date, end_date), - interval: Map.get(params, "interval", "month"), - filters: parse_filters(params), - sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold) - } - end - - def from(site, %{"period" => "12mo"} = params) do - end_date = - parse_single_date(site.timezone, params) + parse_single_date(@timezone, params) |> Timex.end_of_month() start_date = @@ -237,20 +149,6 @@ defmodule Plausible.Stats.Query do from(site, new_params) end - def from(%{domain: "all"}, %{"period" => "custom", "date" => date} = params) do - [from, to] = String.split(date, ",") - from_date = Date.from_iso8601!(String.trim(from)) - to_date = Date.from_iso8601!(String.trim(to)) - - %__MODULE__{ - period: "custom", - date_range: Date.range(from_date, to_date), - interval: Map.get(params, "interval", "date"), - filters: parse_filters(params), - sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold) - } - end - def from(_site, %{"period" => "custom", "date" => date} = params) do [from, to] = String.split(date, ",") from_date = Date.from_iso8601!(String.trim(from)) diff --git a/lib/plausible/stats/timeseries.ex b/lib/plausible/stats/timeseries.ex index a8f0bf837639..ace3056669ef 100644 --- a/lib/plausible/stats/timeseries.ex +++ b/lib/plausible/stats/timeseries.ex @@ -6,6 +6,8 @@ defmodule Plausible.Stats.Timeseries do @event_metrics [:visitors, :pageviews] @session_metrics [:visits, :bounce_rate, :visit_duration] + @timezone "Europe/Warsaw" + def timeseries(site, query, metrics) do steps = buckets(query) @@ -74,35 +76,35 @@ defmodule Plausible.Stats.Timeseries do Enum.into(-30..-1, []) end - def select_bucket(q, site, %Query{interval: "month"}) do + def select_bucket(q, _site, %Query{interval: "month"}) do from( e in q, - group_by: fragment("toStartOfMonth(toTimeZone(?, ?))", e.timestamp, ^site.timezone), - order_by: fragment("toStartOfMonth(toTimeZone(?, ?))", e.timestamp, ^site.timezone), + group_by: fragment("toStartOfMonth(toTimeZone(?, ?))", e.timestamp, @timezone), + order_by: fragment("toStartOfMonth(toTimeZone(?, ?))", e.timestamp, @timezone), select_merge: %{ - date: fragment("toStartOfMonth(toTimeZone(?, ?))", e.timestamp, ^site.timezone) + date: fragment("toStartOfMonth(toTimeZone(?, ?))", e.timestamp, @timezone) } ) end - def select_bucket(q, site, %Query{interval: "date"}) do + def select_bucket(q, _site, %Query{interval: "date"}) do from( e in q, - group_by: fragment("toDate(toTimeZone(?, ?))", e.timestamp, ^site.timezone), - order_by: fragment("toDate(toTimeZone(?, ?))", e.timestamp, ^site.timezone), + group_by: fragment("toDate(toTimeZone(?, ?))", e.timestamp, @timezone), + order_by: fragment("toDate(toTimeZone(?, ?))", e.timestamp, @timezone), select_merge: %{ - date: fragment("toDate(toTimeZone(?, ?))", e.timestamp, ^site.timezone) + date: fragment("toDate(toTimeZone(?, ?))", e.timestamp, @timezone) } ) end - def select_bucket(q, site, %Query{interval: "hour"}) do + def select_bucket(q, _site, %Query{interval: "hour"}) do from( e in q, - group_by: fragment("toStartOfHour(toTimeZone(?, ?))", e.timestamp, ^site.timezone), - order_by: fragment("toStartOfHour(toTimeZone(?, ?))", e.timestamp, ^site.timezone), + group_by: fragment("toStartOfHour(toTimeZone(?, ?))", e.timestamp, @timezone), + order_by: fragment("toStartOfHour(toTimeZone(?, ?))", e.timestamp, @timezone), select_merge: %{ - date: fragment("toStartOfHour(toTimeZone(?, ?))", e.timestamp, ^site.timezone) + date: fragment("toStartOfHour(toTimeZone(?, ?))", e.timestamp, @timezone) } ) end diff --git a/lib/plausible/themes.ex b/lib/plausible/themes.ex deleted file mode 100644 index 5772bfaa5dcc..000000000000 --- a/lib/plausible/themes.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Plausible.Themes do - @options [ - [key: "Follow System Theme", value: "system"], - [key: "Light", value: "light"], - [key: "Dark", value: "dark"] - ] - - def options() do - @options - end -end diff --git a/lib/plausible/timezones.ex b/lib/plausible/timezones.ex deleted file mode 100644 index 816e31b96a41..000000000000 --- a/lib/plausible/timezones.ex +++ /dev/null @@ -1,123 +0,0 @@ -defmodule Plausible.Timezones do - @moduledoc "https://stackoverflow.com/a/52265733" - - @options [ - [key: "(GMT-12:00) International Date Line West", value: "Etc/GMT+12", offset: "720"], - [key: "(GMT-11:00) Midway Island, Samoa", value: "Pacific/Midway", offset: "660"], - [key: "(GMT-10:00) Hawaii", value: "Pacific/Honolulu", offset: "600"], - [key: "(GMT-09:00) Alaska", value: "US/Alaska", offset: "540"], - [key: "(GMT-08:00) Pacific Time (US & Canada)", value: "America/Los_Angeles", offset: "480"], - [key: "(GMT-08:00) Tijuana, Baja California", value: "America/Tijuana"], - [key: "(GMT-07:00) Arizona", value: "US/Arizona"], - [key: "(GMT-07:00) Chihuahua, La Paz, Mazatlan", value: "America/Chihuahua"], - [key: "(GMT-07:00) Mountain Time (US & Canada)", value: "US/Mountain", offset: "420"], - [key: "(GMT-06:00) Central America", value: "America/Managua"], - [key: "(GMT-06:00) Central Time (US & Canada)", value: "US/Central", offset: "360"], - [key: "(GMT-06:00) Guadalajara, Mexico City, Monterrey", value: "America/Mexico_City"], - [key: "(GMT-06:00) Saskatchewan", value: "Canada/Saskatchewan"], - [key: "(GMT-05:00) Bogota, Lima, Quito, Rio Branco", value: "America/Bogota"], - [key: "(GMT-05:00) Eastern Time (US & Canada)", value: "US/Eastern", offset: "300"], - [key: "(GMT-05:00) Indiana (East)", value: "US/East-Indiana"], - [key: "(GMT-04:00) Atlantic Time (Canada)", value: "Canada/Atlantic", offset: "240"], - [key: "(GMT-04:00) Caracas, La Paz", value: "America/Caracas"], - [key: "(GMT-04:00) Manaus", value: "America/Manaus"], - [key: "(GMT-04:00) Santiago", value: "America/Santiago"], - [key: "(GMT-03:30) Newfoundland", value: "Canada/Newfoundland"], - [key: "(GMT-03:00) Brasilia", value: "America/Sao_Paulo", offset: "180"], - [key: "(GMT-03:00) Buenos Aires, Georgetown", value: "America/Argentina/Buenos_Aires"], - [key: "(GMT-03:00) Greenland", value: "America/Godthab"], - [key: "(GMT-03:00) Montevideo", value: "America/Montevideo"], - [key: "(GMT-02:00) Mid-Atlantic", value: "America/Noronha", offset: "120"], - [key: "(GMT-01:00) Cape Verde Is.", value: "Atlantic/Cape_Verde", offset: "60"], - [key: "(GMT-01:00) Azores", value: "Atlantic/Azores"], - [key: "(GMT+00:00) Casablanca, Monrovia, Reykjavik", value: "Africa/Casablanca"], - [ - key: "(GMT+00:00) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London", - value: "Etc/Greenwich", - offset: "0" - ], - [ - key: "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", - value: "Europe/Amsterdam", - offset: "-60" - ], - [ - key: "(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", - value: "Europe/Belgrade" - ], - [key: "(GMT+01:00) Brussels, Copenhagen, Madrid, Paris", value: "Europe/Brussels"], - [key: "(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb", value: "Europe/Sarajevo"], - [key: "(GMT+01:00) West Central Africa", value: "Africa/Lagos"], - [key: "(GMT+02:00) Amman", value: "Asia/Amman"], - [key: "(GMT+02:00) Athens, Bucharest, Istanbul", value: "Europe/Athens"], - [key: "(GMT+02:00) Beirut", value: "Asia/Beirut"], - [key: "(GMT+02:00) Cairo", value: "Africa/Cairo"], - [key: "(GMT+02:00) Harare, Pretoria", value: "Africa/Harare"], - [ - key: "(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", - value: "Europe/Helsinki", - offset: "-120" - ], - [key: "(GMT+02:00) Jerusalem", value: "Asia/Jerusalem"], - [key: "(GMT+02:00) Minsk", value: "Europe/Minsk"], - [key: "(GMT+02:00) Windhoek", value: "Africa/Windhoek"], - [key: "(GMT+03:00) Kuwait, Riyadh, Baghdad", value: "Asia/Kuwait"], - [ - key: "(GMT+03:00) Moscow, St. Petersburg, Volgograd", - value: "Europe/Moscow", - offset: "-180" - ], - [key: "(GMT+03:00) Nairobi", value: "Africa/Nairobi"], - [key: "(GMT+03:00) Tbilisi", value: "Asia/Tbilisi"], - [key: "(GMT+03:30) Tehran", value: "Asia/Tehran", offset: "-210"], - [key: "(GMT+04:00) Abu Dhabi, Muscat", value: "Asia/Muscat", offset: "-240"], - [key: "(GMT+04:00) Baku", value: "Asia/Baku"], - [key: "(GMT+04:00) Yerevan", value: "Asia/Yerevan"], - [key: "(GMT+04:30) Kabul", value: "Asia/Kabul", offset: "-270"], - [key: "(GMT+05:00) Yekaterinburg", value: "Asia/Yekaterinburg"], - [key: "(GMT+05:00) Islamabad, Karachi, Tashkent", value: "Asia/Karachi", offset: "-300"], - [ - key: "(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi", - value: "Asia/Calcutta", - offset: "-330" - ], - [key: "(GMT+05:30) Sri Jayawardenapura", value: "Asia/Calcutta"], - [key: "(GMT+05:45) Kathmandu", value: "Asia/Katmandu", offset: "-345"], - [key: "(GMT+06:00) Almaty, Novosibirsk", value: "Asia/Almaty", offset: "-360"], - [key: "(GMT+06:00) Astana, Dhaka", value: "Asia/Dhaka"], - [key: "(GMT+06:30) Yangon (Rangoon)", value: "Asia/Rangoon", offset: "-390"], - [key: "(GMT+07:00) Bangkok, Hanoi, Jakarta", value: "Asia/Bangkok", offset: "-420"], - [key: "(GMT+07:00) Krasnoyarsk", value: "Asia/Krasnoyarsk"], - [ - key: "(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi", - value: "Asia/Hong_Kong", - offset: "-480" - ], - [key: "(GMT+08:00) Kuala Lumpur, Singapore", value: "Asia/Kuala_Lumpur"], - [key: "(GMT+08:00) Irkutsk, Ulaan Bataar", value: "Asia/Irkutsk"], - [key: "(GMT+08:00) Perth", value: "Australia/Perth"], - [key: "(GMT+08:00) Taipei", value: "Asia/Taipei"], - [key: "(GMT+09:00) Osaka, Sapporo, Tokyo", value: "Asia/Tokyo", offset: "-540"], - [key: "(GMT+09:00) Seoul", value: "Asia/Seoul"], - [key: "(GMT+09:00) Yakutsk", value: "Asia/Yakutsk"], - [key: "(GMT+09:30) Adelaide", value: "Australia/Adelaide", offset: "-570"], - [key: "(GMT+09:30) Darwin", value: "Australia/Darwin"], - [key: "(GMT+10:00) Brisbane", value: "Australia/Brisbane", offset: "-600"], - [key: "(GMT+10:00) Canberra, Melbourne, Sydney", value: "Australia/Canberra"], - [key: "(GMT+10:00) Hobart", value: "Australia/Hobart"], - [key: "(GMT+10:00) Guam, Port Moresby", value: "Pacific/Guam"], - [key: "(GMT+10:00) Vladivostok", value: "Asia/Vladivostok"], - [ - key: "(GMT+11:00) Magadan, Solomon Is., New Caledonia", - value: "Asia/Magadan", - offset: "-660" - ], - [key: "(GMT+12:00) Auckland, Wellington", value: "Pacific/Auckland", offset: "-720"], - [key: "(GMT+12:00) Fiji, Kamchatka, Marshall Is.", value: "Pacific/Fiji"], - [key: "(GMT+13:00) Nuku'alofa", value: "Pacific/Tongatapu", offset: "-780"] - ] - - def options() do - @options - end -end diff --git a/lib/plausible_web.ex b/lib/plausible_web.ex index 4288cecb9a35..696a1f163de2 100644 --- a/lib/plausible_web.ex +++ b/lib/plausible_web.ex @@ -4,7 +4,6 @@ defmodule PlausibleWeb do use Phoenix.Controller, namespace: PlausibleWeb import Plug.Conn - import PlausibleWeb.ControllerHelpers alias PlausibleWeb.Router.Helpers, as: Routes end end diff --git a/lib/plausible_web/controllers/api/helpers.ex b/lib/plausible_web/controllers/api/helpers.ex deleted file mode 100644 index 6229ed870742..000000000000 --- a/lib/plausible_web/controllers/api/helpers.ex +++ /dev/null @@ -1,31 +0,0 @@ -defmodule PlausibleWeb.Api.Helpers do - import Plug.Conn - - def unauthorized(conn, msg) do - conn - |> put_status(401) - |> Phoenix.Controller.json(%{error: msg}) - |> halt() - end - - def bad_request(conn, msg) do - conn - |> put_status(400) - |> Phoenix.Controller.json(%{error: msg}) - |> halt() - end - - def not_found(conn, msg) do - conn - |> put_status(404) - |> Phoenix.Controller.json(%{error: msg}) - |> halt() - end - - def too_many_requests(conn, msg) do - conn - |> put_status(429) - |> Phoenix.Controller.json(%{error: msg}) - |> halt() - end -end diff --git a/lib/plausible_web/controllers/api/stats_controller.ex b/lib/plausible_web/controllers/api/stats_controller.ex index 94fa9705bca0..dfa87f03324a 100644 --- a/lib/plausible_web/controllers/api/stats_controller.ex +++ b/lib/plausible_web/controllers/api/stats_controller.ex @@ -5,6 +5,8 @@ defmodule PlausibleWeb.Api.StatsController do alias Plausible.Stats alias Plausible.Stats.{Query, Filters} + @timezone "Europe/Warsaw" + def main_graph(conn, params) do site = conn.assigns[:site] query = Query.from(site, params) |> Filters.add_prefix() @@ -34,25 +36,25 @@ defmodule PlausibleWeb.Api.StatsController do }) end - defp present_index_for(site, query, dates) do + defp present_index_for(_site, query, dates) do case query.interval do "hour" -> current_date = - Timex.now(site.timezone) + Timex.now(@timezone) |> Timex.format!("{YYYY}-{0M}-{0D} {h24}:00:00") Enum.find_index(dates, &(&1 == current_date)) "date" -> current_date = - Timex.now(site.timezone) + Timex.now(@timezone) |> Timex.to_date() Enum.find_index(dates, &(&1 == current_date)) "month" -> current_date = - Timex.now(site.timezone) + Timex.now(@timezone) |> Timex.to_date() |> Timex.beginning_of_month() @@ -89,58 +91,6 @@ defmodule PlausibleWeb.Api.StatsController do {stats, 100} end - defp fetch_top_stats(site, %Query{filters: %{"event:goal" => _goal}} = query) do - total_q = Query.remove_goal(query) - prev_query = Query.shift_back(query, site) - prev_total_query = Query.shift_back(total_q, site) - - %{ - visitors: %{value: unique_visitors} - } = Stats.aggregate(site, total_q, [:visitors]) - - %{ - visitors: %{value: prev_unique_visitors} - } = Stats.aggregate(site, prev_total_query, [:visitors]) - - %{ - visitors: %{value: converted_visitors}, - events: %{value: completions} - } = Stats.aggregate(site, query, [:visitors, :events]) - - %{ - visitors: %{value: prev_converted_visitors}, - events: %{value: prev_completions} - } = Stats.aggregate(site, prev_query, [:visitors, :events]) - - conversion_rate = calculate_cr(unique_visitors, converted_visitors) - prev_conversion_rate = calculate_cr(prev_unique_visitors, prev_converted_visitors) - - stats = [ - %{ - name: "Unique visitors", - value: unique_visitors, - change: percent_change(prev_unique_visitors, unique_visitors) - }, - %{ - name: "Unique conversions", - value: converted_visitors, - change: percent_change(prev_converted_visitors, converted_visitors) - }, - %{ - name: "Total conversions", - value: completions, - change: percent_change(prev_completions, completions) - }, - %{ - name: "Conversion rate", - value: conversion_rate, - change: percent_change(prev_conversion_rate, conversion_rate) - } - ] - - {stats, 100} - end - defp fetch_top_stats(site, query) do prev_query = Query.shift_back(query, site) @@ -213,20 +163,9 @@ defmodule PlausibleWeb.Api.StatsController do res = Stats.breakdown(site, query, "visit:source", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :source, "visit:source") |> transform_keys(%{source: :name}) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - res - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - res |> to_csv([:name, :visitors, :bounce_rate, :visit_duration]) - end - else - json(conn, res) - end + json(conn, res) end def utm_mediums(conn, params) do @@ -243,20 +182,9 @@ defmodule PlausibleWeb.Api.StatsController do res = Stats.breakdown(site, query, "visit:utm_medium", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :utm_medium, "visit:utm_medium") |> transform_keys(%{utm_medium: :name}) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - res - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - res |> to_csv([:name, :visitors, :bounce_rate, :visit_duration]) - end - else - json(conn, res) - end + json(conn, res) end def utm_campaigns(conn, params) do @@ -273,20 +201,9 @@ defmodule PlausibleWeb.Api.StatsController do res = Stats.breakdown(site, query, "visit:utm_campaign", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :utm_campaign, "visit:utm_campaign") |> transform_keys(%{utm_campaign: :name}) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - res - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - res |> to_csv([:name, :visitors, :bounce_rate, :visit_duration]) - end - else - json(conn, res) - end + json(conn, res) end def utm_contents(conn, params) do @@ -302,20 +219,9 @@ defmodule PlausibleWeb.Api.StatsController do res = Stats.breakdown(site, query, "visit:utm_content", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :utm_content, "visit:utm_content") |> transform_keys(%{utm_content: :name}) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - res - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - res |> to_csv([:name, :visitors, :bounce_rate, :visit_duration]) - end - else - json(conn, res) - end + json(conn, res) end def utm_terms(conn, params) do @@ -331,20 +237,9 @@ defmodule PlausibleWeb.Api.StatsController do res = Stats.breakdown(site, query, "visit:utm_term", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :utm_term, "visit:utm_term") |> transform_keys(%{utm_term: :name}) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - res - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - res |> to_csv([:name, :visitors, :bounce_rate, :visit_duration]) - end - else - json(conn, res) - end + json(conn, res) end def utm_sources(conn, params) do @@ -361,50 +256,9 @@ defmodule PlausibleWeb.Api.StatsController do res = Stats.breakdown(site, query, "visit:utm_source", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :utm_source, "visit:utm_source") |> transform_keys(%{utm_source: :name}) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - res - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - res |> to_csv([:name, :visitors, :bounce_rate, :visit_duration]) - end - else - json(conn, res) - end - end - - def referrer_drilldown(conn, %{"referrer" => "Google"} = params) do - site = conn.assigns[:site] |> Repo.preload(:google_auth) - - query = - Query.from(site, params) - |> Query.put_filter("source", "Google") - |> Filters.add_prefix() - - search_terms = - if site.google_auth && site.google_auth.property && !query.filters["goal"] do - google_api().fetch_stats(site, query, params["limit"] || 9) - end - - %{:visitors => %{value: total_visitors}} = Stats.aggregate(site, query, [:visitors]) - - case search_terms do - nil -> - user_id = get_session(conn, :current_user_id) - is_admin = user_id && Plausible.Sites.has_admin_access?(user_id, site) - json(conn, %{not_configured: true, is_admin: is_admin, total_visitors: total_visitors}) - - {:ok, terms} -> - json(conn, %{search_terms: terms, total_visitors: total_visitors}) - - {:error, e} -> - put_status(conn, 500) - |> json(%{error: e}) - end + json(conn, res) end def referrer_drilldown(conn, %{"referrer" => referrer} = params) do @@ -422,7 +276,6 @@ defmodule PlausibleWeb.Api.StatsController do referrers = Stats.breakdown(site, query, "visit:referrer", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :referrer, "visit:referrer") |> transform_keys(%{referrer: :name}) |> Enum.map(&Map.drop(&1, [:visits])) @@ -443,20 +296,9 @@ defmodule PlausibleWeb.Api.StatsController do pages = Stats.breakdown(site, query, "event:page", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :page, "event:page") |> transform_keys(%{page: :name}) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - pages - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - pages |> to_csv([:name, :visitors, :bounce_rate, :time_on_page]) - end - else - json(conn, pages) - end + json(conn, pages) end def entry_pages(conn, params) do @@ -467,24 +309,13 @@ defmodule PlausibleWeb.Api.StatsController do entry_pages = Stats.breakdown(site, query, "visit:entry_page", metrics, pagination) - |> maybe_add_cr(site, query, pagination, :entry_page, "visit:entry_page") |> transform_keys(%{ entry_page: :name, visitors: :unique_entrances, visits: :total_entrances }) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - entry_pages - |> transform_keys(%{unique_entrances: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - entry_pages |> to_csv([:name, :unique_entrances, :total_entrances, :visit_duration]) - end - else - json(conn, entry_pages) - end + json(conn, entry_pages) end def exit_pages(conn, params) do @@ -495,7 +326,6 @@ defmodule PlausibleWeb.Api.StatsController do exit_pages = Stats.breakdown(site, query, "visit:exit_page", metrics, {limit, page}) - |> maybe_add_cr(site, query, {limit, page}, :exit_page, "visit:exit_page") |> transform_keys(%{ exit_page: :name, visitors: :unique_exits, @@ -528,17 +358,7 @@ defmodule PlausibleWeb.Api.StatsController do Map.put(exit_page, :exit_rate, exit_rate) end) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - exit_pages - |> transform_keys(%{unique_exits: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - exit_pages |> to_csv([:name, :unique_exits, :total_exits, :exit_rate]) - end - else - json(conn, exit_pages) - end + json(conn, exit_pages) end def countries(conn, params) do @@ -553,49 +373,31 @@ defmodule PlausibleWeb.Api.StatsController do countries = Stats.breakdown(site, query, "visit:country", [:visitors], pagination) - |> maybe_add_cr(site, query, {300, 1}, :country, "visit:country") |> transform_keys(%{country: :code}) |> maybe_add_percentages(query) - if params["csv"] do - countries = - countries - |> Enum.map(fn country -> - country_info = get_country(country[:code]) - Map.put(country, :name, country_info.name) - end) - - if Map.has_key?(query.filters, "event:goal") do - countries - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - countries |> to_csv([:name, :visitors]) - end - else - countries = - Enum.map(countries, fn row -> - country = get_country(row[:code]) - - if country do - Map.merge(row, %{ - name: country.name, - flag: country.flag, - alpha_3: country.alpha_3, - code: country.alpha_2 - }) - else - Map.merge(row, %{ - name: row[:code], - flag: "", - alpha_3: "", - code: "" - }) - end - end) + countries = + Enum.map(countries, fn row -> + country = get_country(row[:code]) + + if country do + Map.merge(row, %{ + name: country.name, + flag: country.flag, + alpha_3: country.alpha_3, + code: country.alpha_2 + }) + else + Map.merge(row, %{ + name: row[:code], + flag: "", + alpha_3: "", + code: "" + }) + end + end) - json(conn, countries) - end + json(conn, countries) end def regions(conn, params) do @@ -623,17 +425,7 @@ defmodule PlausibleWeb.Api.StatsController do end end) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - regions - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - regions |> to_csv([:name, :visitors]) - end - else - json(conn, regions) - end + json(conn, regions) end def cities(conn, params) do @@ -666,17 +458,7 @@ defmodule PlausibleWeb.Api.StatsController do end end) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - cities - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - cities |> to_csv([:name, :visitors]) - end - else - json(conn, cities) - end + json(conn, cities) end def browsers(conn, params) do @@ -686,21 +468,10 @@ defmodule PlausibleWeb.Api.StatsController do browsers = Stats.breakdown(site, query, "visit:browser", [:visitors], pagination) - |> maybe_add_cr(site, query, pagination, :browser, "visit:browser") |> transform_keys(%{browser: :name}) |> maybe_add_percentages(query) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - browsers - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - browsers |> to_csv([:name, :visitors]) - end - else - json(conn, browsers) - end + json(conn, browsers) end def browser_versions(conn, params) do @@ -710,7 +481,6 @@ defmodule PlausibleWeb.Api.StatsController do versions = Stats.breakdown(site, query, "visit:browser_version", [:visitors], pagination) - |> maybe_add_cr(site, query, pagination, :browser_version, "visit:browser_version") |> transform_keys(%{browser_version: :name}) |> maybe_add_percentages(query) @@ -724,21 +494,10 @@ defmodule PlausibleWeb.Api.StatsController do systems = Stats.breakdown(site, query, "visit:os", [:visitors], pagination) - |> maybe_add_cr(site, query, pagination, :os, "visit:os") |> transform_keys(%{os: :name}) |> maybe_add_percentages(query) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - systems - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - systems |> to_csv([:name, :visitors]) - end - else - json(conn, systems) - end + json(conn, systems) end def operating_system_versions(conn, params) do @@ -748,7 +507,6 @@ defmodule PlausibleWeb.Api.StatsController do versions = Stats.breakdown(site, query, "visit:os_version", [:visitors], pagination) - |> maybe_add_cr(site, query, pagination, :os_version, "visit:os_version") |> transform_keys(%{os_version: :name}) |> maybe_add_percentages(query) @@ -762,129 +520,10 @@ defmodule PlausibleWeb.Api.StatsController do sizes = Stats.breakdown(site, query, "visit:device", [:visitors], pagination) - |> maybe_add_cr(site, query, pagination, :device, "visit:device") |> transform_keys(%{device: :name}) |> maybe_add_percentages(query) - if params["csv"] do - if Map.has_key?(query.filters, "event:goal") do - sizes - |> transform_keys(%{visitors: :conversions}) - |> to_csv([:name, :conversions, :conversion_rate]) - else - sizes |> to_csv([:name, :visitors]) - end - else - json(conn, sizes) - end - end - - defp calculate_cr(nil, _converted_visitors), do: 100.0 - - defp calculate_cr(unique_visitors, converted_visitors) do - if unique_visitors > 0, - do: Float.round(converted_visitors / unique_visitors * 100, 1), - else: 0.0 - end - - def conversions(conn, params) do - site = conn.assigns[:site] - query = Query.from(site, params) |> Filters.add_prefix() - - query = - if query.period == "realtime" do - %Query{query | period: "30m"} - else - query - end - - total_q = Query.remove_goal(query) - - %{visitors: %{value: total_visitors}} = Stats.aggregate(site, total_q, [:visitors]) - - prop_names = - if query.filters["event:goal"] do - Stats.props(site, query) - else - %{} - end - - conversions = - Stats.breakdown(site, query, "event:goal", [:visitors, :events], {100, 1}) - |> transform_keys(%{ - goal: :name, - visitors: :unique_conversions, - events: :total_conversions - }) - |> Enum.map(fn goal -> - goal - |> Map.put(:prop_names, prop_names[goal[:name]]) - |> Map.put(:conversion_rate, calculate_cr(total_visitors, goal[:unique_conversions])) - end) - - if params["csv"] do - conversions |> to_csv([:name, :unique_conversions, :total_conversions]) - else - json(conn, conversions) - end - end - - def prop_breakdown(conn, params) do - site = conn.assigns[:site] - query = Query.from(site, params) |> Filters.add_prefix() - pagination = parse_pagination(params) - - total_q = Query.remove_goal(query) - - %{:visitors => %{value: unique_visitors}} = Stats.aggregate(site, total_q, [:visitors]) - - prop_name = "event:props:" <> params["prop_name"] - - props = - Stats.breakdown(site, query, prop_name, [:visitors, :events], pagination) - |> transform_keys(%{ - params["prop_name"] => :name, - :events => :total_conversions, - :visitors => :unique_conversions - }) - |> Enum.map(fn prop -> - Map.put( - prop, - :conversion_rate, - calculate_cr(unique_visitors, prop[:unique_conversions]) - ) - end) - - if params["csv"] do - props - else - json(conn, props) - end - end - - def all_props_breakdown(conn, params) do - site = conn.assigns[:site] - query = Query.from(site, params) |> Filters.add_prefix() - - prop_names = - if query.filters["event:goal"] do - {_, _, goal} = query.filters["event:goal"] - - Stats.props(site, query) - |> Map.get(goal, []) - else - [] - end - - values = - prop_names - |> Enum.map(fn prop -> - prop_breakdown(conn, Map.put(params, "prop_name", prop)) - |> Enum.map(&Map.put(&1, :prop, prop)) - end) - |> Enum.concat() - - to_csv(values, [:prop, :name, :unique_conversions, :total_conversions]) + json(conn, sizes) end def current_visitors(conn, _) do @@ -892,8 +531,6 @@ defmodule PlausibleWeb.Api.StatsController do json(conn, Stats.current_visitors(site)) end - defp google_api(), do: Application.fetch_env!(:plausible, :google_api) - def handle_errors(conn, %{kind: kind, reason: reason}) do json(conn, %{error: Exception.format_banner(kind, reason)}) end @@ -943,45 +580,6 @@ defmodule PlausibleWeb.Api.StatsController do end end - defp add_cr(list, list_without_goals, key_name) do - Enum.map(list, fn item -> - without_goal = Enum.find(list_without_goals, fn s -> s[key_name] === item[key_name] end) - - item - |> Map.put(:total_visitors, without_goal[:visitors]) - |> Map.put(:conversion_rate, calculate_cr(without_goal[:visitors], item[:visitors])) - end) - end - - defp maybe_add_cr([], _site, _query, _pagination, _key_name, _filter_name), do: [] - - defp maybe_add_cr(list, site, query, pagination, key_name, filter_name) do - if Map.has_key?(query.filters, "event:goal") do - items = Enum.map(list, fn item -> item[key_name] end) - - query_without_goal = - query - |> Query.put_filter(filter_name, {:member, items}) - |> Query.remove_goal() - - res_without_goal = - Stats.breakdown(site, query_without_goal, filter_name, [:visitors], pagination) - - list - |> add_cr(res_without_goal, key_name) - else - list - end - end - - defp to_csv(list, headers) do - list - |> Enum.map(fn row -> Enum.map(headers, &row[&1]) end) - |> (fn res -> [headers | res] end).() - |> CSV.encode() - |> Enum.join() - end - defp get_country(code) do case Location.get_country(code) do nil -> diff --git a/lib/plausible_web/controllers/helpers.ex b/lib/plausible_web/controllers/helpers.ex deleted file mode 100644 index d8db7f88e822..000000000000 --- a/lib/plausible_web/controllers/helpers.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule PlausibleWeb.ControllerHelpers do - import Plug.Conn - import Phoenix.Controller - - def render_error(conn, status, message) do - conn - |> put_status(status) - |> put_view(PlausibleWeb.ErrorView) - |> render("#{status}.html", layout: false, message: message) - end - - def render_error(conn, status) do - conn - |> put_status(status) - |> put_view(PlausibleWeb.ErrorView) - |> render("#{status}.html", layout: false) - end -end diff --git a/lib/plausible_web/controllers/page_controller.ex b/lib/plausible_web/controllers/page_controller.ex deleted file mode 100644 index f94eec7dd75a..000000000000 --- a/lib/plausible_web/controllers/page_controller.ex +++ /dev/null @@ -1,9 +0,0 @@ -defmodule PlausibleWeb.PageController do - use PlausibleWeb, :controller - - def index(conn, _params) do - conn - |> redirect(to: "/all") - |> halt() - end -end diff --git a/lib/plausible_web/controllers/stats_controller.ex b/lib/plausible_web/controllers/stats_controller.ex index 27fffc22f15c..cd9b5db9497d 100644 --- a/lib/plausible_web/controllers/stats_controller.ex +++ b/lib/plausible_web/controllers/stats_controller.ex @@ -1,48 +1,9 @@ defmodule PlausibleWeb.StatsController do use PlausibleWeb, :controller - plug PlausibleWeb.AuthorizeSiteAccess when action == :stats - - def stats(%{assigns: %{site: %{domain: "all"} = site}} = conn, _params) do + def index(conn, _params) do conn |> put_resp_header("x-robots-tag", "noindex") - |> render("stats.html", - site: site, - has_goals: false, - title: "Careers Analytics", - offer_email_report: false, - demo: false - ) - end - - def stats(%{assigns: %{site: site}} = conn, _params) do - has_stats = Plausible.Sites.has_stats?(site) - can_see_stats = !site.locked || conn.assigns[:current_user_role] == :super_admin - - cond do - has_stats && can_see_stats -> - demo = site.domain == PlausibleWeb.Endpoint.host() - offer_email_report = get_session(conn, site.domain <> "_offer_email_report") - - conn - |> put_resp_header("x-robots-tag", "noindex") - |> render("stats.html", - site: site, - has_goals: false, - title: "Plausible · " <> site.domain, - offer_email_report: offer_email_report, - demo: demo - ) - - !has_stats && can_see_stats -> - conn - |> render("waiting_first_pageview.html", site: site) - - site.locked -> - owner = Plausible.Sites.owner_for(site) - - conn - |> render("site_locked.html", owner: owner, site: site) - end + |> render("index.html", site: %{}, title: "Careers Analytics") end end diff --git a/lib/plausible_web/plugs/auth_plug.ex b/lib/plausible_web/plugs/auth_plug.ex deleted file mode 100644 index 64cd35b7a489..000000000000 --- a/lib/plausible_web/plugs/auth_plug.ex +++ /dev/null @@ -1,16 +0,0 @@ -defmodule PlausibleWeb.AuthPlug do - import Plug.Conn - import Ecto.Query - - alias Plausible.Repo - - def init(options) do - options - end - - def call(conn, _opts) do - user = Plausible.Auth.User |> limit(1) |> Repo.one() - - assign(conn, :current_user, user) - end -end diff --git a/lib/plausible_web/plugs/authorize_site_access.ex b/lib/plausible_web/plugs/authorize_site_access.ex deleted file mode 100644 index 80cca81b7fa5..000000000000 --- a/lib/plausible_web/plugs/authorize_site_access.ex +++ /dev/null @@ -1,14 +0,0 @@ -defmodule PlausibleWeb.AuthorizeSiteAccess do - import Plug.Conn - import Ecto.Query - - alias Plausible.Repo - - def init(opts), do: opts - - def call(conn, _opts) do - site = Plausible.Site |> limit(1) |> Repo.one() - - merge_assigns(conn, site: site, current_user_role: :owner) - end -end diff --git a/lib/plausible_web/router.ex b/lib/plausible_web/router.ex index 884f6b5abbd4..9fce86612f38 100644 --- a/lib/plausible_web/router.ex +++ b/lib/plausible_web/router.ex @@ -7,7 +7,6 @@ defmodule PlausibleWeb.Router do plug :fetch_flash plug :put_secure_browser_headers plug :protect_from_forgery - plug PlausibleWeb.AuthPlug end pipeline :api do @@ -18,7 +17,6 @@ defmodule PlausibleWeb.Router do pipeline :internal_stats_api do plug :accepts, ["json"] plug :fetch_session - plug PlausibleWeb.AuthorizeSiteAccess end scope "/api/stats", PlausibleWeb.Api do @@ -44,8 +42,6 @@ defmodule PlausibleWeb.Router do get "/:domain/operating-systems", StatsController, :operating_systems get "/:domain/operating-system-versions", StatsController, :operating_system_versions get "/:domain/screen-sizes", StatsController, :screen_sizes - get "/:domain/conversions", StatsController, :conversions - get "/:domain/property/:prop_name", StatsController, :prop_breakdown get "/:domain/suggestions/:filter_name", StatsController, :filter_suggestions end @@ -58,7 +54,6 @@ defmodule PlausibleWeb.Router do scope "/", PlausibleWeb do pipe_through :browser - get "/", PageController, :index - get "/:domain/*path", StatsController, :stats + get "/", StatsController, :index end end diff --git a/lib/plausible_web/templates/error/error.html.eex b/lib/plausible_web/templates/error/error.html.eex index abab1ac4ff7c..9208d061b881 100644 --- a/lib/plausible_web/templates/error/error.html.eex +++ b/lib/plausible_web/templates/error/error.html.eex @@ -8,7 +8,7 @@ "> <%= assigns[:title] || "Plausible · Web analytics" %> "/> - + diff --git a/lib/plausible_web/templates/layout/app.html.eex b/lib/plausible_web/templates/layout/app.html.eex index cb43c9d23ece..068295610c23 100644 --- a/lib/plausible_web/templates/layout/app.html.eex +++ b/lib/plausible_web/templates/layout/app.html.eex @@ -9,9 +9,9 @@ <%= assigns[:title] || "Plausible · Simple, privacy-friendly alternative to Google Analytics" %> "/> - + - <%= if @conn.assigns[:background], do: "background-color: #{@conn.assigns[:background]}" %>"> + <%= render("_header.html", assigns) %>
diff --git a/lib/plausible_web/templates/stats/index.html.eex b/lib/plausible_web/templates/stats/index.html.eex new file mode 100644 index 000000000000..37410e179a1c --- /dev/null +++ b/lib/plausible_web/templates/stats/index.html.eex @@ -0,0 +1,7 @@ +
+
+
+ + + +
diff --git a/lib/plausible_web/templates/stats/stats.html.eex b/lib/plausible_web/templates/stats/stats.html.eex deleted file mode 100644 index 4ea2675f8743..000000000000 --- a/lib/plausible_web/templates/stats/stats.html.eex +++ /dev/null @@ -1,7 +0,0 @@ -
" data-site-domain="<%= @site.domain %>"> -
-
- - - -
diff --git a/lib/plausible_web/templates/stats/waiting_first_pageview.html.eex b/lib/plausible_web/templates/stats/waiting_first_pageview.html.eex deleted file mode 100644 index c09b9ed6b9e7..000000000000 --- a/lib/plausible_web/templates/stats/waiting_first_pageview.html.eex +++ /dev/null @@ -1,33 +0,0 @@ - - - -
- <%= if @site.locked do %> - - <% end %> -
-

Waiting for first pageview

-

on <%= @site.domain %>

-
-
-

- Need to see the snippet again? <%= link("Click here", to: "/#{URI.encode_www_form(@site.domain)}/snippet", class: "text-indigo-600 dark:text-indigo-500 text-underline")%>
- Not working? <%= link("Troubleshoot the integration", to: "https://plausible.io/docs/troubleshoot-integration#keep-seeing-a-blinking-green-dot", class: "text-indigo-600 dark:text-indigo-500 text-underline", rel: "noreferrer") %> with our guide -

-
-
-
diff --git a/lib/plausible_web/views/error_view.ex b/lib/plausible_web/views/error_view.ex index b75a884be71e..441a1129dcea 100644 --- a/lib/plausible_web/views/error_view.ex +++ b/lib/plausible_web/views/error_view.ex @@ -23,41 +23,17 @@ defmodule PlausibleWeb.ErrorView do end def render("500.html", assigns) do - case Sentry.get_last_event_id_and_source() do - {event_id, :plug} when is_binary(event_id) -> - current_user = assigns[:current_user] - - opts = - %{ - eventId: event_id, - user: %{ - name: current_user && current_user.name, - email: current_user && current_user.email - } - } - |> Jason.encode!() - - ~E""" - - - """ - - _ -> - render( - "error.html", - Map.merge( - %{ - layout: false, - status: 500, - message: "Oops! Looks like we're having server issues" - }, - assigns - ) - ) - end + render( + "error.html", + Map.merge( + %{ + layout: false, + status: 500, + message: "Oops! Looks like we're having server issues" + }, + assigns + ) + ) end def template_not_found(template, assigns) do diff --git a/lib/plausible_web/views/layout_view.ex b/lib/plausible_web/views/layout_view.ex index 1b4c39e51a28..7d9d2b5c1dfd 100644 --- a/lib/plausible_web/views/layout_view.ex +++ b/lib/plausible_web/views/layout_view.ex @@ -1,50 +1,8 @@ defmodule PlausibleWeb.LayoutView do use PlausibleWeb, :view - def admin_email do - Application.get_env(:plausible, :admin_email) - end - - def base_domain do - PlausibleWeb.Endpoint.host() - end - - def plausible_url do - PlausibleWeb.Endpoint.url() - end - - def trial_notificaton(_user), do: "Trial ends tomorrow" - - def on_grace_period?(nil), do: false - - def on_grace_period?(user) do - user.grace_period && - Timex.diff(user.grace_period.end_date, Timex.today(), :days) >= 0 - end - - def grace_period_over?(nil), do: false - - def grace_period_over?(user) do - user.grace_period && - Timex.diff(user.grace_period.end_date, Timex.today(), :days) < 0 - end - - def grace_period_end(user) do - end_date = user.grace_period.end_date - - case Timex.diff(end_date, Timex.today(), :days) do - 0 -> "today" - 1 -> "tomorrow" - n -> "within #{n} days" - end - end - @doc "http://blog.plataformatec.com.br/2018/05/nested-layouts-with-phoenix/" def render_layout(layout, assigns, do: content) do render(layout, Map.put(assigns, :inner_layout, content)) end - - def is_current_tab(conn, tab) do - List.last(conn.path_info) == tab - end end diff --git a/lib/plausible_web/views/stats_view.ex b/lib/plausible_web/views/stats_view.ex index 0f33dd723cf3..3b6e699a61f5 100644 --- a/lib/plausible_web/views/stats_view.ex +++ b/lib/plausible_web/views/stats_view.ex @@ -1,66 +1,3 @@ defmodule PlausibleWeb.StatsView do use PlausibleWeb, :view - - def admin_email do - Application.get_env(:plausible, :admin_email) - end - - def base_domain do - PlausibleWeb.Endpoint.host() - end - - def plausible_url do - PlausibleWeb.Endpoint.url() - end - - def large_number_format(n) do - cond do - n >= 1_000 && n < 1_000_000 -> - thousands = trunc(n / 100) / 10 - - if thousands == trunc(thousands) || n >= 100_000 do - "#{trunc(thousands)}k" - else - "#{thousands}k" - end - - n >= 1_000_000 && n < 1_000_000_000 -> - millions = trunc(n / 100_000) / 10 - - if millions == trunc(millions) || n > 100_000_000 do - "#{trunc(millions)}M" - else - "#{millions}M" - end - - n >= 1_000_000_000 && n < 1_000_000_000_000 -> - billions = trunc(n / 100_000_000) / 10 - - if billions == trunc(billions) || n > 100_000_000_000 do - "#{trunc(billions)}B" - else - "#{billions}B" - end - - true -> - Integer.to_string(n) - end - end - - def bar(count, all, color \\ :blue) do - ~E""" -
- """ - end - - defp bar_width(count, all) do - max = - Enum.max_by(all, fn - {_, count} -> count - {_, count, _} -> count - end) - |> elem(1) - - count / max * 100 - end end diff --git a/mix.exs b/mix.exs index 4fb37d39a559..5c4ba764e442 100644 --- a/mix.exs +++ b/mix.exs @@ -104,7 +104,7 @@ defmodule Plausible.MixProject do defp aliases do [ - "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], + "ecto.setup": ["ecto.create", "ecto.migrate"], "ecto.reset": ["ecto.drop", "ecto.setup"], test: ["ecto.create --quiet", "ecto.migrate", "test", "clean_clickhouse"], sentry_recompile: ["compile", "deps.compile sentry --force"] diff --git a/priv/repo/migrations/20190127213938_add_tz_to_sites.exs b/priv/repo/migrations/20190127213938_add_tz_to_sites.exs index 3649dfb09e4e..9e2edb0a2d11 100644 --- a/priv/repo/migrations/20190127213938_add_tz_to_sites.exs +++ b/priv/repo/migrations/20190127213938_add_tz_to_sites.exs @@ -7,10 +7,6 @@ defmodule Plausible.Repo.Migrations.AddTzToSites do add :timezone, :string end - flush() - - Repo.update_all(Plausible.Site, set: [timezone: "UTC"]) - alter table(:sites) do modify :timezone, :string, null: false end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs deleted file mode 100644 index b4931d33bbf1..000000000000 --- a/priv/repo/seeds.exs +++ /dev/null @@ -1,11 +0,0 @@ -# Script for populating the database. You can run it as: -# -# mix run priv/repo/seeds.exs -# -# Inside the script, you can read and write to any of your -# repositories directly: -# -# Plausible.Repo.insert!(%Plausible.SomeSchema{}) -# -# We recommend using the bang functions (`insert!`, `update!` -# and so on) as they will fail if something goes wrong. diff --git a/priv/static/js/dashboard.js b/priv/static/js/dashboard.js index 36ab184901f1..e8ad8e8d2d18 100644 --- a/priv/static/js/dashboard.js +++ b/priv/static/js/dashboard.js @@ -1,2 +1,2 @@ /*! For license information please see dashboard.js.LICENSE.txt */ -(()=>{var e={254:(e,t,n)=>{"use strict";n.r(t),n.d(t,{ResizeObserver:()=>V,ResizeObserverEntry:()=>k,ResizeObserverSize:()=>l});var r,o=[],i="ResizeObserver loop completed with undelivered notifications.";!function(e){e.BORDER_BOX="border-box",e.CONTENT_BOX="content-box",e.DEVICE_PIXEL_CONTENT_BOX="device-pixel-content-box"}(r||(r={}));var a,s=function(e){return Object.freeze(e)},l=function(e,t){this.inlineSize=e,this.blockSize=t,s(this)},c=function(){function e(e,t,n,r){return this.x=e,this.y=t,this.width=n,this.height=r,this.top=this.y,this.left=this.x,this.bottom=this.top+this.height,this.right=this.left+this.width,s(this)}return e.prototype.toJSON=function(){var e=this;return{x:e.x,y:e.y,top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.width,height:e.height}},e.fromRect=function(t){return new e(t.x,t.y,t.width,t.height)},e}(),u=function(e){return e instanceof SVGElement&&"getBBox"in e},f=function(e){if(u(e)){var t=e.getBBox(),n=t.width,r=t.height;return!n&&!r}var o=e,i=o.offsetWidth,a=o.offsetHeight;return!(i||a||e.getClientRects().length)},d=function(e){var t,n;if(e instanceof Element)return!0;var r=null===(n=null===(t=e)||void 0===t?void 0:t.ownerDocument)||void 0===n?void 0:n.defaultView;return!!(r&&e instanceof r.Element)},p="undefined"!=typeof window?window:{},h=new WeakMap,m=/auto|scroll/,g=/^tb|vertical/,y=/msie|trident/i.test(p.navigator&&p.navigator.userAgent),v=function(e){return parseFloat(e||"0")},b=function(e,t,n){return void 0===e&&(e=0),void 0===t&&(t=0),void 0===n&&(n=!1),new l((n?t:e)||0,(n?e:t)||0)},x=s({devicePixelContentBoxSize:b(),borderBoxSize:b(),contentBoxSize:b(),contentRect:new c(0,0,0,0)}),w=function(e,t){if(void 0===t&&(t=!1),h.has(e)&&!t)return h.get(e);if(f(e))return h.set(e,x),x;var n=getComputedStyle(e),r=u(e)&&e.ownerSVGElement&&e.getBBox(),o=!y&&"border-box"===n.boxSizing,i=g.test(n.writingMode||""),a=!r&&m.test(n.overflowY||""),l=!r&&m.test(n.overflowX||""),d=r?0:v(n.paddingTop),p=r?0:v(n.paddingRight),w=r?0:v(n.paddingBottom),_=r?0:v(n.paddingLeft),k=r?0:v(n.borderTopWidth),E=r?0:v(n.borderRightWidth),S=r?0:v(n.borderBottomWidth),M=_+p,C=d+w,T=(r?0:v(n.borderLeftWidth))+E,P=k+S,O=l?e.offsetHeight-P-e.clientHeight:0,N=a?e.offsetWidth-T-e.clientWidth:0,D=o?M+T:0,R=o?C+P:0,A=r?r.width:v(n.width)-D-N,I=r?r.height:v(n.height)-R-O,L=A+M+N+T,j=I+C+O+P,F=s({devicePixelContentBoxSize:b(Math.round(A*devicePixelRatio),Math.round(I*devicePixelRatio),i),borderBoxSize:b(L,j,i),contentBoxSize:b(A,I,i),contentRect:new c(_,d,A,I)});return h.set(e,F),F},_=function(e,t,n){var o=w(e,n),i=o.borderBoxSize,a=o.contentBoxSize,s=o.devicePixelContentBoxSize;switch(t){case r.DEVICE_PIXEL_CONTENT_BOX:return s;case r.BORDER_BOX:return i;default:return a}},k=function(e){var t=w(e);this.target=e,this.contentRect=t.contentRect,this.borderBoxSize=s([t.borderBoxSize]),this.contentBoxSize=s([t.contentBoxSize]),this.devicePixelContentBoxSize=s([t.devicePixelContentBoxSize])},E=function(e){if(f(e))return 1/0;for(var t=0,n=e.parentNode;n;)t+=1,n=n.parentNode;return t},S=function(){var e=1/0,t=[];o.forEach((function(n){if(0!==n.activeTargets.length){var r=[];n.activeTargets.forEach((function(t){var n=new k(t.target),o=E(t.target);r.push(n),t.lastReportedSize=_(t.target,t.observedBox),oe?t.activeTargets.push(n):t.skippedTargets.push(n))}))}))},C=function(){var e,t=0;for(M(t);o.some((function(e){return e.activeTargets.length>0}));)t=S(),M(t);return o.some((function(e){return e.skippedTargets.length>0}))&&("function"==typeof ErrorEvent?e=new ErrorEvent("error",{message:i}):((e=document.createEvent("Event")).initEvent("error",!1,!1),e.message=i),window.dispatchEvent(e)),t>0},T=[],P=function(e){if(!a){var t=0,n=document.createTextNode("");new MutationObserver((function(){return T.splice(0).forEach((function(e){return e()}))})).observe(n,{characterData:!0}),a=function(){n.textContent=""+(t?t--:t++)}}T.push(e),a()},O=0,N={attributes:!0,characterData:!0,childList:!0,subtree:!0},D=["resize","load","transitionend","animationend","animationstart","animationiteration","keyup","keydown","mouseup","mousedown","mouseover","mouseout","blur","focus"],R=function(e){return void 0===e&&(e=0),Date.now()+e},A=!1,I=new(function(){function e(){var e=this;this.stopped=!0,this.listener=function(){return e.schedule()}}return e.prototype.run=function(e){var t=this;if(void 0===e&&(e=250),!A){A=!0;var n,r=R(e);n=function(){var n=!1;try{n=C()}finally{if(A=!1,e=r-R(),!O)return;n?t.run(1e3):e>0?t.run(e):t.start()}},P((function(){requestAnimationFrame(n)}))}},e.prototype.schedule=function(){this.stop(),this.run()},e.prototype.observe=function(){var e=this,t=function(){return e.observer&&e.observer.observe(document.body,N)};document.body?t():p.addEventListener("DOMContentLoaded",t)},e.prototype.start=function(){var e=this;this.stopped&&(this.stopped=!1,this.observer=new MutationObserver(this.listener),this.observe(),D.forEach((function(t){return p.addEventListener(t,e.listener,!0)})))},e.prototype.stop=function(){var e=this;this.stopped||(this.observer&&this.observer.disconnect(),D.forEach((function(t){return p.removeEventListener(t,e.listener,!0)})),this.stopped=!0)},e}()),L=function(e){!O&&e>0&&I.start(),!(O+=e)&&I.stop()},j=function(){function e(e,t){this.target=e,this.observedBox=t||r.CONTENT_BOX,this.lastReportedSize={inlineSize:0,blockSize:0}}return e.prototype.isActive=function(){var e,t=_(this.target,this.observedBox,!0);return e=this.target,u(e)||function(e){switch(e.tagName){case"INPUT":if("image"!==e.type)break;case"VIDEO":case"AUDIO":case"EMBED":case"OBJECT":case"CANVAS":case"IFRAME":case"IMG":return!0}return!1}(e)||"inline"!==getComputedStyle(e).display||(this.lastReportedSize=t),this.lastReportedSize.inlineSize!==t.inlineSize||this.lastReportedSize.blockSize!==t.blockSize},e}(),F=function(e,t){this.activeTargets=[],this.skippedTargets=[],this.observationTargets=[],this.observer=e,this.callback=t},z=new WeakMap,B=function(e,t){for(var n=0;n=0&&(i&&o.splice(o.indexOf(n),1),n.observationTargets.splice(r,1),L(-1))},e.disconnect=function(e){var t=this,n=z.get(e);n.observationTargets.slice().forEach((function(n){return t.unobserve(e,n.target)})),n.activeTargets.splice(0,n.activeTargets.length)},e}(),V=function(){function e(e){if(0===arguments.length)throw new TypeError("Failed to construct 'ResizeObserver': 1 argument required, but only 0 present.");if("function"!=typeof e)throw new TypeError("Failed to construct 'ResizeObserver': The callback provided as parameter 1 is not a function.");U.connect(this,e)}return e.prototype.observe=function(e,t){if(0===arguments.length)throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': 1 argument required, but only 0 present.");if(!d(e))throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element");U.observe(this,e,t)},e.prototype.unobserve=function(e){if(0===arguments.length)throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': 1 argument required, but only 0 present.");if(!d(e))throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element");U.unobserve(this,e)},e.prototype.disconnect=function(){U.disconnect(this)},e.toString=function(){return"function ResizeObserver () { [polyfill code] }"},e}()},184:(e,t)=>{var n;!function(){"use strict";var r={}.hasOwnProperty;function o(){for(var e=[],t=0;t{var r,o;!function(){var i={version:"3.5.17"},a=[].slice,s=function(e){return a.call(e)},l=this.document;function c(e){return e&&(e.ownerDocument||e.document||e).documentElement}function u(e){return e&&(e.ownerDocument&&e.ownerDocument.defaultView||e.document&&e||e.defaultView)}if(l)try{s(l.documentElement.childNodes)[0].nodeType}catch(e){s=function(e){for(var t=e.length,n=new Array(t);t--;)n[t]=e[t];return n}}if(Date.now||(Date.now=function(){return+new Date}),l)try{l.createElement("DIV").style.setProperty("opacity",0,"")}catch(e){var f=this.Element.prototype,d=f.setAttribute,p=f.setAttributeNS,h=this.CSSStyleDeclaration.prototype,m=h.setProperty;f.setAttribute=function(e,t){d.call(this,e,t+"")},f.setAttributeNS=function(e,t,n){p.call(this,e,t,n+"")},h.setProperty=function(e,t,n){m.call(this,e,t+"",n)}}function g(e,t){return et?1:e>=t?0:NaN}function y(e){return null===e?NaN:+e}function v(e){return!isNaN(e)}function b(e){return{left:function(t,n,r,o){for(arguments.length<3&&(r=0),arguments.length<4&&(o=t.length);r>>1;e(t[i],n)<0?r=i+1:o=i}return r},right:function(t,n,r,o){for(arguments.length<3&&(r=0),arguments.length<4&&(o=t.length);r>>1;e(t[i],n)>0?o=i:r=i+1}return r}}}i.ascending=g,i.descending=function(e,t){return te?1:t>=e?0:NaN},i.min=function(e,t){var n,r,o=-1,i=e.length;if(1===arguments.length){for(;++o=r){n=r;break}for(;++or&&(n=r)}else{for(;++o=r){n=r;break}for(;++or&&(n=r)}return n},i.max=function(e,t){var n,r,o=-1,i=e.length;if(1===arguments.length){for(;++o=r){n=r;break}for(;++on&&(n=r)}else{for(;++o=r){n=r;break}for(;++on&&(n=r)}return n},i.extent=function(e,t){var n,r,o,i=-1,a=e.length;if(1===arguments.length){for(;++i=r){n=o=r;break}for(;++ir&&(n=r),o=r){n=o=r;break}for(;++ir&&(n=r),o1)return a/(l-1)},i.deviation=function(){var e=i.variance.apply(this,arguments);return e?Math.sqrt(e):e};var x=b(g);function w(e){return e.length}i.bisectLeft=x.left,i.bisect=i.bisectRight=x.right,i.bisector=function(e){return b(1===e.length?function(t,n){return g(e(t),n)}:e)},i.shuffle=function(e,t,n){(i=arguments.length)<3&&(n=e.length,i<2&&(t=0));for(var r,o,i=n-t;i;)o=Math.random()*i--|0,r=e[i+t],e[i+t]=e[o+t],e[o+t]=r;return e},i.permute=function(e,t){for(var n=t.length,r=new Array(n);n--;)r[n]=e[t[n]];return r},i.pairs=function(e){for(var t=0,n=e.length-1,r=e[0],o=new Array(n<0?0:n);t=0;)for(t=(r=e[o]).length;--t>=0;)n[--a]=r[t];return n};var _=Math.abs;function k(e){for(var t=1;e*t%1;)t*=10;return t}function E(e,t){for(var n in t)Object.defineProperty(e.prototype,n,{value:t[n],enumerable:!1})}function S(){this._=Object.create(null)}i.range=function(e,t,n){if(arguments.length<3&&(n=1,arguments.length<2&&(t=e,e=0)),(t-e)/n==1/0)throw new Error("infinite range");var r,o=[],i=k(_(n)),a=-1;if(e*=i,t*=i,(n*=i)<0)for(;(r=e+n*++a)>t;)o.push(r/i);else for(;(r=e+n*++a)=r.length)return t?t.call(n,i):e?i.sort(e):i;for(var l,c,u,f,d=-1,p=i.length,h=r[s++],m=new S;++d=r.length)return e;var n=[],i=o[t++];return e.forEach((function(e,r){n.push({key:e,values:s(r,t)})})),i?n.sort((function(e,t){return i(e.key,t.key)})):n}return n.map=function(e,t){return a(t,e,0)},n.entries=function(e){return s(a(i.map,e,0),0)},n.key=function(e){return r.push(e),n},n.sortKeys=function(e){return o[r.length-1]=e,n},n.sortValues=function(t){return e=t,n},n.rollup=function(e){return t=e,n},n},i.set=function(e){var t=new R;if(e)for(var n=0,r=e.length;n=0&&(r=e.slice(n+1),e=e.slice(0,n)),e)return arguments.length<2?this[e].on(r):this[e].on(r,t);if(2===arguments.length){if(null==t)for(e in this)this.hasOwnProperty(e)&&this[e].on(r,null);return this}},i.event=null,i.requote=function(e){return e.replace(q,"\\$&")};var q=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,W={}.__proto__?function(e,t){e.__proto__=t}:function(e,t){for(var n in t)e[n]=t[n]};function Y(e){return W(e,X),e}var K=function(e,t){return t.querySelector(e)},$=function(e,t){return t.querySelectorAll(e)},G=function(e,t){var n=e.matches||e[L(e,"matchesSelector")];return(G=function(e,t){return n.call(e,t)})(e,t)};"function"==typeof Sizzle&&(K=function(e,t){return Sizzle(e,t)[0]||null},$=Sizzle,G=Sizzle.matchesSelector),i.selection=function(){return i.select(l.documentElement)};var X=i.selection.prototype=[];function Q(e){return"function"==typeof e?e:function(){return K(e,this)}}function J(e){return"function"==typeof e?e:function(){return $(e,this)}}X.select=function(e){var t,n,r,o,i=[];e=Q(e);for(var a=-1,s=this.length;++a=0&&"xmlns"!==(n=e.slice(0,t))&&(e=e.slice(t+1)),ee.hasOwnProperty(n)?{space:ee[n],local:e}:e}},X.attr=function(e,t){if(arguments.length<2){if("string"==typeof e){var n=this.node();return(e=i.ns.qualify(e)).local?n.getAttributeNS(e.space,e.local):n.getAttribute(e)}for(t in e)this.each(te(t,e[t]));return this}return this.each(te(e,t))},X.classed=function(e,t){if(arguments.length<2){if("string"==typeof e){var n=this.node(),r=(e=oe(e)).length,o=-1;if(t=n.classList){for(;++o=0;)(n=r[o])&&(i&&i!==n.nextSibling&&i.parentNode.insertBefore(n,i),i=n);return this},X.sort=function(e){e=pe.apply(this,arguments);for(var t=-1,n=this.length;++t=t&&(t=o+1);!(a=s[t])&&++t0&&(e=e.slice(0,o));var l=be.get(e);function c(){var t=this[r];t&&(this.removeEventListener(e,t,t.$),delete this[r])}return l&&(e=l,a=we),o?t?function(){var o=a(t,s(arguments));c.call(this),this.addEventListener(e,this[r]=o,o.$=n),o._=t}:c:t?F:function(){var t,n=new RegExp("^__on([^.]+)"+i.requote(e)+"$");for(var r in this)if(t=r.match(n)){var o=this[r];this.removeEventListener(t[1],o,o.$),delete this[r]}}}i.selection.enter=me,i.selection.enter.prototype=ge,ge.append=X.append,ge.empty=X.empty,ge.node=X.node,ge.call=X.call,ge.size=X.size,ge.select=function(e){for(var t,n,r,o,i,a=[],s=-1,l=this.length;++s0?1:e<0?-1:0}function je(e,t,n){return(t[0]-e[0])*(n[1]-e[1])-(t[1]-e[1])*(n[0]-e[0])}function Fe(e){return e>1?0:e<-1?Oe:Math.acos(e)}function ze(e){return e>1?Re:e<-1?-Re:Math.asin(e)}function Be(e){return((e=Math.exp(e))+1/e)/2}function Ue(e){return(e=Math.sin(e/2))*e}var Ve=Math.SQRT2;i.interpolateZoom=function(e,t){var n,r,o=e[0],i=e[1],a=e[2],s=t[0],l=t[1],c=t[2],u=s-o,f=l-i,d=u*u+f*f;if(d0&&(e=e.transition().duration(m)),e.call(_.event)}function C(){s&&s.domain(a.range().map((function(e){return(e-d.x)/d.k})).map(a.invert)),f&&f.domain(c.range().map((function(e){return(e-d.y)/d.k})).map(c.invert))}function T(e){g++||e({type:"zoomstart"})}function P(e){C(),e({type:"zoom",scale:d.k,translate:[d.x,d.y]})}function O(e){--g||(e({type:"zoomend"}),t=null)}function N(){var e=this,t=w.of(e,arguments),n=0,r=i.select(u(e)).on(v,s).on(b,l),o=k(i.mouse(e)),a=Ee(e);function s(){n=1,S(i.mouse(e),o),P(t)}function l(){r.on(v,null).on(b,null),a(n),O(t)}Ls.call(e),T(t)}function D(){var e,t=this,n=w.of(t,arguments),r={},a=0,s=".zoom-"+i.event.changedTouches[0].identifier,l="touchmove"+s,c="touchend"+s,u=[],f=i.select(t),p=Ee(t);function h(){var n=i.touches(t);return e=d.k,n.forEach((function(e){e.identifier in r&&(r[e.identifier]=k(e))})),n}function m(){var e=i.event.target;i.select(e).on(l,g).on(c,v),u.push(e);for(var n=i.event.changedTouches,s=0,f=n.length;s1){y=p[0];var b=p[1],x=y[0]-b[0],w=y[1]-b[1];a=x*x+w*w}}function g(){var s,l,c,u,f=i.touches(t);Ls.call(t);for(var d=0,p=f.length;d360?e-=360:e<0&&(e+=360),e<60?r+(o-r)*e/60:e<180?o:e<240?r+(o-r)*(240-e)/60:r}(e))}return e=isNaN(e)?0:(e%=360)<0?e+360:e,t=isNaN(t)||t<0?0:t>1?1:t,r=2*(n=n<0?0:n>1?1:n)-(o=n<=.5?n*(1+t):n+t-n*t),new ct(i(e+120),i(e),i(e-120))}function Xe(e,t,n){return this instanceof Xe?(this.h=+e,this.c=+t,void(this.l=+n)):arguments.length<2?e instanceof Xe?new Xe(e.h,e.c,e.l):it(e instanceof Ze?e.l:(e=gt((e=i.rgb(e)).r,e.g,e.b)).l,e.a,e.b):new Xe(e,t,n)}$e.brighter=function(e){return e=Math.pow(.7,arguments.length?e:1),new Ke(this.h,this.s,this.l/e)},$e.darker=function(e){return e=Math.pow(.7,arguments.length?e:1),new Ke(this.h,this.s,e*this.l)},$e.rgb=function(){return Ge(this.h,this.s,this.l)},i.hcl=Xe;var Qe=Xe.prototype=new Ye;function Je(e,t,n){return isNaN(e)&&(e=0),isNaN(t)&&(t=0),new Ze(n,Math.cos(e*=Ae)*t,Math.sin(e)*t)}function Ze(e,t,n){return this instanceof Ze?(this.l=+e,this.a=+t,void(this.b=+n)):arguments.length<2?e instanceof Ze?new Ze(e.l,e.a,e.b):e instanceof Xe?Je(e.h,e.c,e.l):gt((e=ct(e)).r,e.g,e.b):new Ze(e,t,n)}Qe.brighter=function(e){return new Xe(this.h,this.c,Math.min(100,this.l+et*(arguments.length?e:1)))},Qe.darker=function(e){return new Xe(this.h,this.c,Math.max(0,this.l-et*(arguments.length?e:1)))},Qe.rgb=function(){return Je(this.h,this.c,this.l).rgb()},i.lab=Ze;var et=18,tt=.95047,nt=1.08883,rt=Ze.prototype=new Ye;function ot(e,t,n){var r=(e+16)/116,o=r+t/500,i=r-n/200;return new ct(lt(3.2404542*(o=at(o)*tt)-1.5371385*(r=1*at(r))-.4985314*(i=at(i)*nt)),lt(-.969266*o+1.8760108*r+.041556*i),lt(.0556434*o-.2040259*r+1.0572252*i))}function it(e,t,n){return e>0?new Xe(Math.atan2(n,t)*Ie,Math.sqrt(t*t+n*n),e):new Xe(NaN,NaN,e)}function at(e){return e>.206893034?e*e*e:(e-4/29)/7.787037}function st(e){return e>.008856?Math.pow(e,1/3):7.787037*e+4/29}function lt(e){return Math.round(255*(e<=.00304?12.92*e:1.055*Math.pow(e,1/2.4)-.055))}function ct(e,t,n){return this instanceof ct?(this.r=~~e,this.g=~~t,void(this.b=~~n)):arguments.length<2?e instanceof ct?new ct(e.r,e.g,e.b):ht(""+e,ct,Ge):new ct(e,t,n)}function ut(e){return new ct(e>>16,e>>8&255,255&e)}function ft(e){return ut(e)+""}rt.brighter=function(e){return new Ze(Math.min(100,this.l+et*(arguments.length?e:1)),this.a,this.b)},rt.darker=function(e){return new Ze(Math.max(0,this.l-et*(arguments.length?e:1)),this.a,this.b)},rt.rgb=function(){return ot(this.l,this.a,this.b)},i.rgb=ct;var dt=ct.prototype=new Ye;function pt(e){return e<16?"0"+Math.max(0,e).toString(16):Math.min(255,e).toString(16)}function ht(e,t,n){var r,o,i,a=0,s=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(e=e.toLowerCase()))switch(o=r[2].split(","),r[1]){case"hsl":return n(parseFloat(o[0]),parseFloat(o[1])/100,parseFloat(o[2])/100);case"rgb":return t(vt(o[0]),vt(o[1]),vt(o[2]))}return(i=bt.get(e))?t(i.r,i.g,i.b):(null==e||"#"!==e.charAt(0)||isNaN(i=parseInt(e.slice(1),16))||(4===e.length?(a=(3840&i)>>4,a|=a>>4,s=240&i,s|=s>>4,l=15&i,l|=l<<4):7===e.length&&(a=(16711680&i)>>16,s=(65280&i)>>8,l=255&i)),t(a,s,l))}function mt(e,t,n){var r,o,i=Math.min(e/=255,t/=255,n/=255),a=Math.max(e,t,n),s=a-i,l=(a+i)/2;return s?(o=l<.5?s/(a+i):s/(2-a-i),r=e==a?(t-n)/s+(t0&&l<1?0:r),new Ke(r,o,l)}function gt(e,t,n){var r=st((.4124564*(e=yt(e))+.3575761*(t=yt(t))+.1804375*(n=yt(n)))/tt),o=st((.2126729*e+.7151522*t+.072175*n)/1);return Ze(116*o-16,500*(r-o),200*(o-st((.0193339*e+.119192*t+.9503041*n)/nt)))}function yt(e){return(e/=255)<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}function vt(e){var t=parseFloat(e);return"%"===e.charAt(e.length-1)?Math.round(2.55*t):t}dt.brighter=function(e){e=Math.pow(.7,arguments.length?e:1);var t=this.r,n=this.g,r=this.b,o=30;return t||n||r?(t&&t=200&&t<300||304===t){try{e=n.call(o,c)}catch(e){return void a.error.call(o,e)}a.load.call(o,e)}else a.error.call(o,c)}return this.XDomainRequest&&!("withCredentials"in c)&&/^(http(s)?:)?\/\//.test(e)&&(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=f:c.onreadystatechange=function(){c.readyState>3&&f()},c.onprogress=function(e){var t=i.event;i.event=e;try{a.progress.call(o,c)}finally{i.event=t}},o.header=function(e,t){return e=(e+"").toLowerCase(),arguments.length<2?l[e]:(null==t?delete l[e]:l[e]=t+"",o)},o.mimeType=function(e){return arguments.length?(t=null==e?null:e+"",o):t},o.responseType=function(e){return arguments.length?(u=e,o):u},o.response=function(e){return n=e,o},["get","post"].forEach((function(e){o[e]=function(){return o.send.apply(o,[e].concat(s(arguments)))}})),o.send=function(n,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),c.open(n,e,!0),null==t||"accept"in l||(l.accept=t+",*/*"),c.setRequestHeader)for(var s in l)c.setRequestHeader(s,l[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=u&&(c.responseType=u),null!=i&&o.on("error",i).on("load",(function(e){i(null,e)})),a.beforesend.call(o,c),c.send(null==r?null:r),o},o.abort=function(){return c.abort(),o},i.rebind(o,a,"on"),null==r?o:o.get(function(e){return 1===e.length?function(t,n){e(null==t?n:null)}:e}(r))}bt.forEach((function(e,t){bt.set(e,ut(t))})),i.functor=xt,i.xhr=wt(A),i.dsv=function(e,t){var n=new RegExp('["'+e+"\n]"),r=e.charCodeAt(0);function o(e,n,r){arguments.length<3&&(r=n,n=null);var o=_t(e,t,null==n?i:a(n),r);return o.row=function(e){return arguments.length?o.response(null==(n=e)?i:a(e)):n},o}function i(e){return o.parse(e.responseText)}function a(e){return function(t){return o.parse(t.responseText,e)}}function s(t){return t.map(l).join(e)}function l(e){return n.test(e)?'"'+e.replace(/\"/g,'""')+'"':e}return o.parse=function(e,t){var n;return o.parseRows(e,(function(e,r){if(n)return n(e,r-1);var o=new Function("d","return {"+e.map((function(e,t){return JSON.stringify(e)+": d["+t+"]"})).join(",")+"}");n=t?function(e,n){return t(o(e),n)}:o}))},o.parseRows=function(e,t){var n,o,i={},a={},s=[],l=e.length,c=0,u=0;function f(){if(c>=l)return a;if(o)return o=!1,i;var t=c;if(34===e.charCodeAt(t)){for(var n=t;n++24?(isFinite(t)&&(clearTimeout(Mt),Mt=setTimeout(Pt,t)),St=0):(St=1,Ct(Pt))}function Ot(){for(var e=Date.now(),t=kt;t;)e>=t.t&&t.c(e-t.t)&&(t.c=null),t=t.n;return e}function Nt(){for(var e,t=kt,n=1/0;t;)t.c?(t.t8?function(e){return e/n}:function(e){return e*n},symbol:e}}));function At(e){var t=e.decimal,n=e.thousands,r=e.grouping,o=e.currency,a=r&&n?function(e,t){for(var o=e.length,i=[],a=0,s=r[0],l=0;o>0&&s>0&&(l+s+1>t&&(s=Math.max(1,t-l)),i.push(e.substring(o-=s,o+s)),!((l+=s+1)>t));)s=r[a=(a+1)%r.length];return i.reverse().join(n)}:A;return function(e){var n=It.exec(e),r=n[1]||" ",s=n[2]||">",l=n[3]||"-",c=n[4]||"",u=n[5],f=+n[6],d=n[7],p=n[8],h=n[9],m=1,g="",y="",v=!1,b=!0;switch(p&&(p=+p.substring(1)),(u||"0"===r&&"="===s)&&(u=r="0",s="="),h){case"n":d=!0,h="g";break;case"%":m=100,y="%",h="f";break;case"p":m=100,y="%",h="r";break;case"b":case"o":case"x":case"X":"#"===c&&(g="0"+h.toLowerCase());case"c":b=!1;case"d":v=!0,p=0;break;case"s":m=-1,h="r"}"$"===c&&(g=o[0],y=o[1]),"r"!=h||p||(h="g"),null!=p&&("g"==h?p=Math.max(1,Math.min(21,p)):"e"!=h&&"f"!=h||(p=Math.max(0,Math.min(20,p)))),h=Lt.get(h)||jt;var x=u&&d;return function(e){var n=y;if(v&&e%1)return"";var o=e<0||0===e&&1/e<0?(e=-e,"-"):"-"===l?"":l;if(m<0){var c=i.formatPrefix(e,p);e=c.scale(e),n=c.symbol+y}else e*=m;var w,_,k=(e=h(e,p)).lastIndexOf(".");if(k<0){var E=b?e.lastIndexOf("e"):-1;E<0?(w=e,_=""):(w=e.substring(0,E),_=e.substring(E))}else w=e.substring(0,k),_=t+e.substring(k+1);!u&&d&&(w=a(w,1/0));var S=g.length+w.length+_.length+(x?0:o.length),M=S"===s?M+o+e:"^"===s?M.substring(0,S>>=1)+o+e+M.substring(S):o+(x?e:M+e))+n}}}i.formatPrefix=function(e,t){var n=0;return(e=+e)&&(e<0&&(e*=-1),t&&(e=i.round(e,Dt(e,t))),n=1+Math.floor(1e-12+Math.log(e)/Math.LN10),n=Math.max(-24,Math.min(24,3*Math.floor((n-1)/3)))),Rt[8+n/3]};var It=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,Lt=i.map({b:function(e){return e.toString(2)},c:function(e){return String.fromCharCode(e)},o:function(e){return e.toString(8)},x:function(e){return e.toString(16)},X:function(e){return e.toString(16).toUpperCase()},g:function(e,t){return e.toPrecision(t)},e:function(e,t){return e.toExponential(t)},f:function(e,t){return e.toFixed(t)},r:function(e,t){return(e=i.round(e,Dt(e,t))).toFixed(Math.max(0,Math.min(20,Dt(e*(1+1e-15),t))))}});function jt(e){return e+""}var Ft=i.time={},zt=Date;function Bt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}Bt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){Ut.setUTCDate.apply(this._,arguments)},setDay:function(){Ut.setUTCDay.apply(this._,arguments)},setFullYear:function(){Ut.setUTCFullYear.apply(this._,arguments)},setHours:function(){Ut.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){Ut.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){Ut.setUTCMinutes.apply(this._,arguments)},setMonth:function(){Ut.setUTCMonth.apply(this._,arguments)},setSeconds:function(){Ut.setUTCSeconds.apply(this._,arguments)},setTime:function(){Ut.setTime.apply(this._,arguments)}};var Ut=Date.prototype;function Vt(e,t,n){function r(t){var n=e(t),r=i(n,1);return t-n1)for(;a=c)return-1;if(37===(o=t.charCodeAt(s++))){if(a=t.charAt(s++),!(i=_[a in Wt?t.charAt(s++):a])||(r=i(e,n,r))<0)return-1}else if(o!=n.charCodeAt(r++))return-1}return r}u.utc=function(e){var t=u(e);function n(e){try{var n=new(zt=Bt);return n._=e,t(n)}finally{zt=Date}}return n.parse=function(e){try{zt=Bt;var n=t.parse(e);return n&&n._}finally{zt=Date}},n.toString=t.toString,n},u.multi=u.utc.multi=pn;var d=i.map(),p=Gt(a),h=Xt(a),m=Gt(s),g=Xt(s),y=Gt(l),v=Xt(l),b=Gt(c),x=Xt(c);o.forEach((function(e,t){d.set(e.toLowerCase(),t)}));var w={a:function(e){return s[e.getDay()]},A:function(e){return a[e.getDay()]},b:function(e){return c[e.getMonth()]},B:function(e){return l[e.getMonth()]},c:u(t),d:function(e,t){return $t(e.getDate(),t,2)},e:function(e,t){return $t(e.getDate(),t,2)},H:function(e,t){return $t(e.getHours(),t,2)},I:function(e,t){return $t(e.getHours()%12||12,t,2)},j:function(e,t){return $t(1+Ft.dayOfYear(e),t,3)},L:function(e,t){return $t(e.getMilliseconds(),t,3)},m:function(e,t){return $t(e.getMonth()+1,t,2)},M:function(e,t){return $t(e.getMinutes(),t,2)},p:function(e){return o[+(e.getHours()>=12)]},S:function(e,t){return $t(e.getSeconds(),t,2)},U:function(e,t){return $t(Ft.sundayOfYear(e),t,2)},w:function(e){return e.getDay()},W:function(e,t){return $t(Ft.mondayOfYear(e),t,2)},x:u(n),X:u(r),y:function(e,t){return $t(e.getFullYear()%100,t,2)},Y:function(e,t){return $t(e.getFullYear()%1e4,t,4)},Z:fn,"%":function(){return"%"}},_={a:function(e,t,n){m.lastIndex=0;var r=m.exec(t.slice(n));return r?(e.w=g.get(r[0].toLowerCase()),n+r[0].length):-1},A:function(e,t,n){p.lastIndex=0;var r=p.exec(t.slice(n));return r?(e.w=h.get(r[0].toLowerCase()),n+r[0].length):-1},b:function(e,t,n){b.lastIndex=0;var r=b.exec(t.slice(n));return r?(e.m=x.get(r[0].toLowerCase()),n+r[0].length):-1},B:function(e,t,n){y.lastIndex=0;var r=y.exec(t.slice(n));return r?(e.m=v.get(r[0].toLowerCase()),n+r[0].length):-1},c:function(e,t,n){return f(e,w.c.toString(),t,n)},d:on,e:on,H:sn,I:sn,j:an,L:un,m:rn,M:ln,p:function(e,t,n){var r=d.get(t.slice(n,n+=2).toLowerCase());return null==r?-1:(e.p=r,n)},S:cn,U:Jt,w:Qt,W:Zt,x:function(e,t,n){return f(e,w.x.toString(),t,n)},X:function(e,t,n){return f(e,w.X.toString(),t,n)},y:tn,Y:en,Z:nn,"%":dn};return u}Ft.year=Vt((function(e){return(e=Ft.day(e)).setMonth(0,1),e}),(function(e,t){e.setFullYear(e.getFullYear()+t)}),(function(e){return e.getFullYear()})),Ft.years=Ft.year.range,Ft.years.utc=Ft.year.utc.range,Ft.day=Vt((function(e){var t=new zt(2e3,0);return t.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),t}),(function(e,t){e.setDate(e.getDate()+t)}),(function(e){return e.getDate()-1})),Ft.days=Ft.day.range,Ft.days.utc=Ft.day.utc.range,Ft.dayOfYear=function(e){var t=Ft.year(e);return Math.floor((e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach((function(e,t){t=7-t;var n=Ft[e]=Vt((function(e){return(e=Ft.day(e)).setDate(e.getDate()-(e.getDay()+t)%7),e}),(function(e,t){e.setDate(e.getDate()+7*Math.floor(t))}),(function(e){var n=Ft.year(e).getDay();return Math.floor((Ft.dayOfYear(e)+(n+t)%7)/7)-(n!==t)}));Ft[e+"s"]=n.range,Ft[e+"s"].utc=n.utc.range,Ft[e+"OfYear"]=function(e){var n=Ft.year(e).getDay();return Math.floor((Ft.dayOfYear(e)+(n+t)%7)/7)}})),Ft.week=Ft.sunday,Ft.weeks=Ft.sunday.range,Ft.weeks.utc=Ft.sunday.utc.range,Ft.weekOfYear=Ft.sundayOfYear;var Wt={"-":"",_:" ",0:"0"},Yt=/^\s*\d+/,Kt=/^%/;function $t(e,t,n){var r=e<0?"-":"",o=(r?-e:e)+"",i=o.length;return r+(i68?1900:2e3),n+o[0].length):-1}function nn(e,t,n){return/^[+-]\d{4}$/.test(t=t.slice(n,n+5))?(e.Z=-t,n+5):-1}function rn(e,t,n){Yt.lastIndex=0;var r=Yt.exec(t.slice(n,n+2));return r?(e.m=r[0]-1,n+r[0].length):-1}function on(e,t,n){Yt.lastIndex=0;var r=Yt.exec(t.slice(n,n+2));return r?(e.d=+r[0],n+r[0].length):-1}function an(e,t,n){Yt.lastIndex=0;var r=Yt.exec(t.slice(n,n+3));return r?(e.j=+r[0],n+r[0].length):-1}function sn(e,t,n){Yt.lastIndex=0;var r=Yt.exec(t.slice(n,n+2));return r?(e.H=+r[0],n+r[0].length):-1}function ln(e,t,n){Yt.lastIndex=0;var r=Yt.exec(t.slice(n,n+2));return r?(e.M=+r[0],n+r[0].length):-1}function cn(e,t,n){Yt.lastIndex=0;var r=Yt.exec(t.slice(n,n+2));return r?(e.S=+r[0],n+r[0].length):-1}function un(e,t,n){Yt.lastIndex=0;var r=Yt.exec(t.slice(n,n+3));return r?(e.L=+r[0],n+r[0].length):-1}function fn(e){var t=e.getTimezoneOffset(),n=t>0?"-":"+",r=_(t)/60|0,o=_(t)%60;return n+$t(r,"0",2)+$t(o,"0",2)}function dn(e,t,n){Kt.lastIndex=0;var r=Kt.exec(t.slice(n,n+1));return r?n+r[0].length:-1}function pn(e){for(var t=e.length,n=-1;++n=0?1:-1,s=a*i,l=Math.cos(t),c=Math.sin(t),u=o*c,f=r*l+u*Math.cos(s),d=u*a*Math.sin(s);In.add(Math.atan2(d,f)),n=e,r=l,o=c}Ln.point=function(a,s){Ln.point=i,n=(e=a)*Ae,r=Math.cos(s=(t=s)*Ae/2+Oe/4),o=Math.sin(s)},Ln.lineEnd=function(){i(e,t)}}function Fn(e){var t=e[0],n=e[1],r=Math.cos(n);return[r*Math.cos(t),r*Math.sin(t),Math.sin(n)]}function zn(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]}function Bn(e,t){return[e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]]}function Un(e,t){e[0]+=t[0],e[1]+=t[1],e[2]+=t[2]}function Vn(e,t){return[e[0]*t,e[1]*t,e[2]*t]}function Hn(e){var t=Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]);e[0]/=t,e[1]/=t,e[2]/=t}function qn(e){return[Math.atan2(e[1],e[0]),ze(e[2])]}function Wn(e,t){return _(e[0]-t[0])Te?r=90:c<-Te&&(t=-90),f[0]=e,f[1]=n}};function p(o,i){u.push(f=[e=o,n=o]),ir&&(r=i)}function h(i,a){var s=Fn([i*Ae,a*Ae]);if(l){var c=Bn(l,s),u=Bn([c[1],-c[0],0],c);Hn(u),u=qn(u);var f=i-o,d=f>0?1:-1,h=u[0]*Ie*d,m=_(f)>180;if(m^(d*or&&(r=g);else if(m^(d*o<(h=(h+360)%360-180)&&hr&&(r=a);m?ix(e,n)&&(n=i):x(i,n)>x(e,n)&&(e=i):n>=e?(in&&(n=i)):i>o?x(e,i)>x(e,n)&&(n=i):x(i,n)>x(e,n)&&(e=i)}else p(i,a);l=s,o=i}function m(){d.point=h}function g(){f[0]=e,f[1]=n,d.point=p,l=null}function y(e,t){if(l){var n=e-o;c+=_(n)>180?n+(n>0?360:-360):n}else a=e,s=t;Ln.point(e,t),h(e,t)}function v(){Ln.lineStart()}function b(){y(a,s),Ln.lineEnd(),_(c)>Te&&(e=-(n=180)),f[0]=e,f[1]=n,l=null}function x(e,t){return(t-=e)<0?t+360:t}function w(e,t){return e[0]-t[0]}function k(e,t){return t[0]<=t[1]?t[0]<=e&&e<=t[1]:ex(m[0],m[1])&&(m[1]=p[1]),x(p[0],m[1])>x(m[0],m[1])&&(m[0]=p[0])):s.push(m=p);for(var l,c,p,h=-1/0,m=(a=0,s[c=s.length-1]);a<=c;m=p,++a)p=s[a],(l=x(m[1],p[0]))>h&&(h=l,e=p[0],n=m[1])}return u=f=null,e===1/0||t===1/0?[[NaN,NaN],[NaN,NaN]]:[[e,t],[n,r]]}}(),i.geo.centroid=function(e){En=Sn=Mn=Cn=Tn=Pn=On=Nn=Dn=Rn=An=0,i.geo.stream(e,Yn);var t=Dn,n=Rn,r=An,o=t*t+n*n+r*r;return o=0;--s)o.point((f=u[s])[0],f[1]);else r(p.x,p.p.x,-1,o);p=p.p}u=(p=p.o).z,h=!h}while(!p.v);o.lineEnd()}}}function tr(e){if(t=e.length){for(var t,n,r=0,o=e[0];++r=0?1:-1,k=_*w,E=k>Oe,S=h*b;if(In.add(Math.atan2(S*_*Math.sin(k),m*x+S*Math.cos(k))),i+=E?w+_*Ne:w,E^d>=n^y>=n){var M=Bn(Fn(f),Fn(e));Hn(M);var C=Bn(o,M);Hn(C);var T=(E^w>=0?-1:1)*ze(C[2]);(r>T||r===T&&(M[0]||M[1]))&&(a+=E^w>=0?1:-1)}if(!g++)break;d=y,h=b,m=x,f=e}}return(i<-Te||i0){for(b||(a.polygonStart(),b=!0),a.lineStart();++i1&&2&t&&n.push(n.pop().concat(n.shift())),s.push(n.filter(or))}return u}}function or(e){return e.length>1}function ir(){var e,t=[];return{lineStart:function(){t.push(e=[])},point:function(t,n){e.push([t,n])},lineEnd:F,buffer:function(){var n=t;return t=[],e=null,n},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function ar(e,t){return((e=e.x)[0]<0?e[1]-Re-Te:Re-e[1])-((t=t.x)[0]<0?t[1]-Re-Te:Re-t[1])}var sr=rr(Zn,(function(e){var t,n=NaN,r=NaN,o=NaN;return{lineStart:function(){e.lineStart(),t=1},point:function(i,a){var s=i>0?Oe:-Oe,l=_(i-n);_(l-Oe)0?Re:-Re),e.point(o,r),e.lineEnd(),e.lineStart(),e.point(s,r),e.point(i,r),t=0):o!==s&&l>=Oe&&(_(n-o)Te?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(n)-Math.sin(r)*(o=Math.cos(t))*Math.sin(e))/(o*i*a)):(t+r)/2}(n,r,i,a),e.point(o,r),e.lineEnd(),e.lineStart(),e.point(s,r),t=0),e.point(n=i,r=a),o=s},lineEnd:function(){e.lineEnd(),n=r=NaN},clean:function(){return 2-t}}}),(function(e,t,n,r){var o;if(null==e)o=n*Re,r.point(-Oe,o),r.point(0,o),r.point(Oe,o),r.point(Oe,0),r.point(Oe,-o),r.point(0,-o),r.point(-Oe,-o),r.point(-Oe,0),r.point(-Oe,o);else if(_(e[0]-t[0])>Te){var i=e[0]0,r=_(t)>Te;return rr(o,(function(e){var t,s,l,c,u;return{lineStart:function(){c=l=!1,u=1},point:function(f,d){var p,h=[f,d],m=o(f,d),g=n?m?0:a(f,d):m?a(f+(f<0?Oe:-Oe),d):0;if(!t&&(c=l=m)&&e.lineStart(),m!==l&&(p=i(t,h),(Wn(t,p)||Wn(h,p))&&(h[0]+=Te,h[1]+=Te,m=o(h[0],h[1]))),m!==l)u=0,m?(e.lineStart(),p=i(h,t),e.point(p[0],p[1])):(p=i(t,h),e.point(p[0],p[1]),e.lineEnd()),t=p;else if(r&&t&&n^m){var y;g&s||!(y=i(h,t,!0))||(u=0,n?(e.lineStart(),e.point(y[0][0],y[0][1]),e.point(y[1][0],y[1][1]),e.lineEnd()):(e.point(y[1][0],y[1][1]),e.lineEnd(),e.lineStart(),e.point(y[0][0],y[0][1])))}!m||t&&Wn(t,h)||e.point(h[0],h[1]),t=h,l=m,s=g},lineEnd:function(){l&&e.lineEnd(),t=null},clean:function(){return u|(c&&l)<<1}}}),Wr(e,6*Ae),n?[0,-e]:[-Oe,e-Oe]);function o(e,n){return Math.cos(e)*Math.cos(n)>t}function i(e,n,r){var o=[1,0,0],i=Bn(Fn(e),Fn(n)),a=zn(i,i),s=i[0],l=a-s*s;if(!l)return!r&&e;var c=t*a/l,u=-t*s/l,f=Bn(o,i),d=Vn(o,c);Un(d,Vn(i,u));var p=f,h=zn(d,p),m=zn(p,p),g=h*h-m*(zn(d,d)-1);if(!(g<0)){var y=Math.sqrt(g),v=Vn(p,(-h-y)/m);if(Un(v,d),v=qn(v),!r)return v;var b,x=e[0],w=n[0],k=e[1],E=n[1];w0^v[1]<(_(v[0]-x)Oe^(x<=v[0]&&v[0]<=w)){var C=Vn(p,(-h+y)/m);return Un(C,d),[v,qn(C)]}}}function a(t,r){var o=n?e:Oe-e,i=0;return t<-o?i|=1:t>o&&(i|=2),r<-o?i|=4:r>o&&(i|=8),i}}function cr(e,t,n,r){return function(o){var i,a=o.a,s=o.b,l=a.x,c=a.y,u=0,f=1,d=s.x-l,p=s.y-c;if(i=e-l,d||!(i>0)){if(i/=d,d<0){if(i0){if(i>f)return;i>u&&(u=i)}if(i=n-l,d||!(i<0)){if(i/=d,d<0){if(i>f)return;i>u&&(u=i)}else if(d>0){if(i0)){if(i/=p,p<0){if(i0){if(i>f)return;i>u&&(u=i)}if(i=r-c,p||!(i<0)){if(i/=p,p<0){if(i>f)return;i>u&&(u=i)}else if(p>0){if(i0&&(o.a={x:l+u*d,y:c+u*p}),f<1&&(o.b={x:l+f*d,y:c+f*p}),o}}}}}}var ur=1e9;function fr(e,t,n,r){return function(l){var c,u,f,d,p,h,m,g,y,v,b,x=l,w=ir(),_=cr(e,t,n,r),k={point:M,lineStart:function(){k.point=C,u&&u.push(f=[]);v=!0,y=!1,m=g=NaN},lineEnd:function(){c&&(C(d,p),h&&y&&w.rejoin(),c.push(w.buffer()));k.point=M,y&&l.lineEnd()},polygonStart:function(){l=w,c=[],u=[],b=!0},polygonEnd:function(){l=x,c=i.merge(c);var t=function(e){for(var t=0,n=u.length,r=e[1],o=0;or&&je(c,i,e)>0&&++t:i[1]<=r&&je(c,i,e)<0&&--t,c=i;return 0!==t}([e,r]),n=b&&t,o=c.length;(n||o)&&(l.polygonStart(),n&&(l.lineStart(),E(null,null,1,l),l.lineEnd()),o&&er(c,a,t,E,l),l.polygonEnd()),c=u=f=null}};function E(i,a,l,c){var u=0,f=0;if(null==i||(u=o(i,l))!==(f=o(a,l))||s(i,a)<0^l>0)do{c.point(0===u||3===u?e:n,u>1?r:t)}while((u=(u+l+4)%4)!==f);else c.point(a[0],a[1])}function S(o,i){return e<=o&&o<=n&&t<=i&&i<=r}function M(e,t){S(e,t)&&l.point(e,t)}function C(e,t){var n=S(e=Math.max(-1e9,Math.min(ur,e)),t=Math.max(-1e9,Math.min(ur,t)));if(u&&f.push([e,t]),v)d=e,p=t,h=n,v=!1,n&&(l.lineStart(),l.point(e,t));else if(n&&y)l.point(e,t);else{var r={a:{x:m,y:g},b:{x:e,y:t}};_(r)?(y||(l.lineStart(),l.point(r.a.x,r.a.y)),l.point(r.b.x,r.b.y),n||l.lineEnd(),b=!1):n&&(l.lineStart(),l.point(e,t),b=!1)}m=e,g=t,y=n}return k};function o(r,o){return _(r[0]-e)0?0:3:_(r[0]-n)0?2:1:_(r[1]-t)0?1:0:o>0?3:2}function a(e,t){return s(e.x,t.x)}function s(e,t){var n=o(e,1),r=o(t,1);return n!==r?n-r:0===n?t[1]-e[1]:1===n?e[0]-t[0]:2===n?e[1]-t[1]:t[0]-e[0]}}function dr(e){var t=0,n=Oe/3,r=jr(e),o=r(t,n);return o.parallels=function(e){return arguments.length?r(t=e[0]*Oe/180,n=e[1]*Oe/180):[t/Oe*180,n/Oe*180]},o}function pr(e,t){var n=Math.sin(e),r=(n+Math.sin(t))/2,o=1+n*(2*r-n),i=Math.sqrt(o)/r;function a(e,t){var n=Math.sqrt(o-2*r*Math.sin(t))/r;return[n*Math.sin(e*=r),i-n*Math.cos(e)]}return a.invert=function(e,t){var n=i-t;return[Math.atan2(e,n)/r,ze((o-(e*e+n*n)*r*r)/(2*r))]},a}i.geo.clipExtent=function(){var e,t,n,r,o,i,a={stream:function(e){return o&&(o.valid=!1),(o=i(e)).valid=!0,o},extent:function(s){return arguments.length?(i=fr(e=+s[0][0],t=+s[0][1],n=+s[1][0],r=+s[1][1]),o&&(o.valid=!1,o=null),a):[[e,t],[n,r]]}};return a.extent([[0,0],[960,500]])},(i.geo.conicEqualArea=function(){return dr(pr)}).raw=pr,i.geo.albers=function(){return i.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},i.geo.albersUsa=function(){var e,t,n,r,o=i.geo.albers(),a=i.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),s=i.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(t,n){e=[t,n]}};function c(o){var i=o[0],a=o[1];return e=null,t(i,a),e||(n(i,a),e)||r(i,a),e}return c.invert=function(e){var t=o.scale(),n=o.translate(),r=(e[0]-n[0])/t,i=(e[1]-n[1])/t;return(i>=.12&&i<.234&&r>=-.425&&r<-.214?a:i>=.166&&i<.234&&r>=-.214&&r<-.115?s:o).invert(e)},c.stream=function(e){var t=o.stream(e),n=a.stream(e),r=s.stream(e);return{point:function(e,o){t.point(e,o),n.point(e,o),r.point(e,o)},sphere:function(){t.sphere(),n.sphere(),r.sphere()},lineStart:function(){t.lineStart(),n.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),n.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),n.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),n.polygonEnd(),r.polygonEnd()}}},c.precision=function(e){return arguments.length?(o.precision(e),a.precision(e),s.precision(e),c):o.precision()},c.scale=function(e){return arguments.length?(o.scale(e),a.scale(.35*e),s.scale(e),c.translate(o.translate())):o.scale()},c.translate=function(e){if(!arguments.length)return o.translate();var i=o.scale(),u=+e[0],f=+e[1];return t=o.translate(e).clipExtent([[u-.455*i,f-.238*i],[u+.455*i,f+.238*i]]).stream(l).point,n=a.translate([u-.307*i,f+.201*i]).clipExtent([[u-.425*i+Te,f+.12*i+Te],[u-.214*i-Te,f+.234*i-Te]]).stream(l).point,r=s.translate([u-.205*i,f+.212*i]).clipExtent([[u-.214*i+Te,f+.166*i+Te],[u-.115*i-Te,f+.234*i-Te]]).stream(l).point,c},c.scale(1070)};var hr,mr,gr,yr,vr,br,xr={point:F,lineStart:F,lineEnd:F,polygonStart:function(){mr=0,xr.lineStart=wr},polygonEnd:function(){xr.lineStart=xr.lineEnd=xr.point=F,hr+=_(mr/2)}};function wr(){var e,t,n,r;function o(e,t){mr+=r*e-n*t,n=e,r=t}xr.point=function(i,a){xr.point=o,e=n=i,t=r=a},xr.lineEnd=function(){o(e,t)}}var _r={point:function(e,t){evr&&(vr=e);tbr&&(br=t)},lineStart:F,lineEnd:F,polygonStart:F,polygonEnd:F};function kr(){var e=Er(4.5),t=[],n={point:r,lineStart:function(){n.point=o},lineEnd:a,polygonStart:function(){n.lineEnd=s},polygonEnd:function(){n.lineEnd=a,n.point=r},pointRadius:function(t){return e=Er(t),n},result:function(){if(t.length){var e=t.join("");return t=[],e}}};function r(n,r){t.push("M",n,",",r,e)}function o(e,r){t.push("M",e,",",r),n.point=i}function i(e,n){t.push("L",e,",",n)}function a(){n.point=r}function s(){t.push("Z")}return n}function Er(e){return"m0,"+e+"a"+e+","+e+" 0 1,1 0,"+-2*e+"a"+e+","+e+" 0 1,1 0,"+2*e+"z"}var Sr,Mr={point:Cr,lineStart:Tr,lineEnd:Pr,polygonStart:function(){Mr.lineStart=Or},polygonEnd:function(){Mr.point=Cr,Mr.lineStart=Tr,Mr.lineEnd=Pr}};function Cr(e,t){Mn+=e,Cn+=t,++Tn}function Tr(){var e,t;function n(n,r){var o=n-e,i=r-t,a=Math.sqrt(o*o+i*i);Pn+=a*(e+n)/2,On+=a*(t+r)/2,Nn+=a,Cr(e=n,t=r)}Mr.point=function(r,o){Mr.point=n,Cr(e=r,t=o)}}function Pr(){Mr.point=Cr}function Or(){var e,t,n,r;function o(e,t){var o=e-n,i=t-r,a=Math.sqrt(o*o+i*i);Pn+=a*(n+e)/2,On+=a*(r+t)/2,Nn+=a,Dn+=(a=r*e-n*t)*(n+e),Rn+=a*(r+t),An+=3*a,Cr(n=e,r=t)}Mr.point=function(i,a){Mr.point=o,Cr(e=n=i,t=r=a)},Mr.lineEnd=function(){o(e,t)}}function Nr(e){var t=4.5,n={point:r,lineStart:function(){n.point=o},lineEnd:a,polygonStart:function(){n.lineEnd=s},polygonEnd:function(){n.lineEnd=a,n.point=r},pointRadius:function(e){return t=e,n},result:F};function r(n,r){e.moveTo(n+t,r),e.arc(n,r,t,0,Ne)}function o(t,r){e.moveTo(t,r),n.point=i}function i(t,n){e.lineTo(t,n)}function a(){n.point=r}function s(){e.closePath()}return n}function Dr(e){var t=.5,n=Math.cos(30*Ae),r=16;function o(e){return(r?a:i)(e)}function i(t){return Ir(t,(function(n,r){n=e(n,r),t.point(n[0],n[1])}))}function a(t){var n,o,i,a,l,c,u,f,d,p,h,m,g={point:y,lineStart:v,lineEnd:x,polygonStart:function(){t.polygonStart(),g.lineStart=w},polygonEnd:function(){t.polygonEnd(),g.lineStart=v}};function y(n,r){n=e(n,r),t.point(n[0],n[1])}function v(){f=NaN,g.point=b,t.lineStart()}function b(n,o){var i=Fn([n,o]),a=e(n,o);s(f,d,u,p,h,m,f=a[0],d=a[1],u=n,p=i[0],h=i[1],m=i[2],r,t),t.point(f,d)}function x(){g.point=y,t.lineEnd()}function w(){v(),g.point=_,g.lineEnd=k}function _(e,t){b(n=e,t),o=f,i=d,a=p,l=h,c=m,g.point=b}function k(){s(f,d,u,p,h,m,o,i,n,a,l,c,r,t),g.lineEnd=x,x()}return g}function s(r,o,i,a,l,c,u,f,d,p,h,m,g,y){var v=u-r,b=f-o,x=v*v+b*b;if(x>4*t&&g--){var w=a+p,k=l+h,E=c+m,S=Math.sqrt(w*w+k*k+E*E),M=Math.asin(E/=S),C=_(_(E)-1)t||_((v*N+b*D)/x-.5)>.3||a*p+l*h+c*m0&&16,o):Math.sqrt(t)},o}function Rr(e){var t=Dr((function(t,n){return e([t*Ie,n*Ie])}));return function(e){return Fr(t(e))}}function Ar(e){this.stream=e}function Ir(e,t){return{point:t,sphere:function(){e.sphere()},lineStart:function(){e.lineStart()},lineEnd:function(){e.lineEnd()},polygonStart:function(){e.polygonStart()},polygonEnd:function(){e.polygonEnd()}}}function Lr(e){return jr((function(){return e}))()}function jr(e){var t,n,r,o,a,s,l=Dr((function(e,n){return[(e=t(e,n))[0]*c+o,a-e[1]*c]})),c=150,u=480,f=250,d=0,p=0,h=0,m=0,g=0,y=sr,v=A,b=null,x=null;function w(e){return[(e=r(e[0]*Ae,e[1]*Ae))[0]*c+o,a-e[1]*c]}function _(e){return(e=r.invert((e[0]-o)/c,(a-e[1])/c))&&[e[0]*Ie,e[1]*Ie]}function k(){r=Jn(n=Ur(h,m,g),t);var e=t(d,p);return o=u-e[0]*c,a=f+e[1]*c,E()}function E(){return s&&(s.valid=!1,s=null),w}return w.stream=function(e){return s&&(s.valid=!1),(s=Fr(y(n,l(v(e))))).valid=!0,s},w.clipAngle=function(e){return arguments.length?(y=null==e?(b=e,sr):lr((b=+e)*Ae),E()):b},w.clipExtent=function(e){return arguments.length?(x=e,v=e?fr(e[0][0],e[0][1],e[1][0],e[1][1]):A,E()):x},w.scale=function(e){return arguments.length?(c=+e,k()):c},w.translate=function(e){return arguments.length?(u=+e[0],f=+e[1],k()):[u,f]},w.center=function(e){return arguments.length?(d=e[0]%360*Ae,p=e[1]%360*Ae,k()):[d*Ie,p*Ie]},w.rotate=function(e){return arguments.length?(h=e[0]%360*Ae,m=e[1]%360*Ae,g=e.length>2?e[2]%360*Ae:0,k()):[h*Ie,m*Ie,g*Ie]},i.rebind(w,l,"precision"),function(){return t=e.apply(this,arguments),w.invert=t.invert&&_,k()}}function Fr(e){return Ir(e,(function(t,n){e.point(t*Ae,n*Ae)}))}function zr(e,t){return[e,t]}function Br(e,t){return[e>Oe?e-Ne:e<-Oe?e+Ne:e,t]}function Ur(e,t,n){return e?t||n?Jn(Hr(e),qr(t,n)):Hr(e):t||n?qr(t,n):Br}function Vr(e){return function(t,n){return[(t+=e)>Oe?t-Ne:t<-Oe?t+Ne:t,n]}}function Hr(e){var t=Vr(e);return t.invert=Vr(-e),t}function qr(e,t){var n=Math.cos(e),r=Math.sin(e),o=Math.cos(t),i=Math.sin(t);function a(e,t){var a=Math.cos(t),s=Math.cos(e)*a,l=Math.sin(e)*a,c=Math.sin(t),u=c*n+s*r;return[Math.atan2(l*o-u*i,s*n-c*r),ze(u*o+l*i)]}return a.invert=function(e,t){var a=Math.cos(t),s=Math.cos(e)*a,l=Math.sin(e)*a,c=Math.sin(t),u=c*o-l*i;return[Math.atan2(l*o+c*i,s*n+u*r),ze(u*n-s*r)]},a}function Wr(e,t){var n=Math.cos(e),r=Math.sin(e);return function(o,i,a,s){var l=a*t;null!=o?(o=Yr(n,o),i=Yr(n,i),(a>0?oi)&&(o+=a*Ne)):(o=e+a*Ne,i=e-.5*l);for(var c,u=o;a>0?u>i:u2?e[2]*Ae:0),t.invert=function(t){return(t=e.invert(t[0]*Ae,t[1]*Ae))[0]*=Ie,t[1]*=Ie,t},t},Br.invert=zr,i.geo.circle=function(){var e,t,n=[0,0],r=6;function o(){var e="function"==typeof n?n.apply(this,arguments):n,r=Ur(-e[0]*Ae,-e[1]*Ae,0).invert,o=[];return t(null,null,1,{point:function(e,t){o.push(e=r(e,t)),e[0]*=Ie,e[1]*=Ie}}),{type:"Polygon",coordinates:[o]}}return o.origin=function(e){return arguments.length?(n=e,o):n},o.angle=function(n){return arguments.length?(t=Wr((e=+n)*Ae,r*Ae),o):e},o.precision=function(n){return arguments.length?(t=Wr(e*Ae,(r=+n)*Ae),o):r},o.angle(90)},i.geo.distance=function(e,t){var n,r=(t[0]-e[0])*Ae,o=e[1]*Ae,i=t[1]*Ae,a=Math.sin(r),s=Math.cos(r),l=Math.sin(o),c=Math.cos(o),u=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((n=f*a)*n+(n=c*u-l*f*s)*n),l*u+c*f*s)},i.geo.graticule=function(){var e,t,n,r,o,a,s,l,c,u,f,d,p=10,h=p,m=90,g=360,y=2.5;function v(){return{type:"MultiLineString",coordinates:b()}}function b(){return i.range(Math.ceil(r/m)*m,n,m).map(f).concat(i.range(Math.ceil(l/g)*g,s,g).map(d)).concat(i.range(Math.ceil(t/p)*p,e,p).filter((function(e){return _(e%m)>Te})).map(c)).concat(i.range(Math.ceil(a/h)*h,o,h).filter((function(e){return _(e%g)>Te})).map(u))}return v.lines=function(){return b().map((function(e){return{type:"LineString",coordinates:e}}))},v.outline=function(){return{type:"Polygon",coordinates:[f(r).concat(d(s).slice(1),f(n).reverse().slice(1),d(l).reverse().slice(1))]}},v.extent=function(e){return arguments.length?v.majorExtent(e).minorExtent(e):v.minorExtent()},v.majorExtent=function(e){return arguments.length?(r=+e[0][0],n=+e[1][0],l=+e[0][1],s=+e[1][1],r>n&&(e=r,r=n,n=e),l>s&&(e=l,l=s,s=e),v.precision(y)):[[r,l],[n,s]]},v.minorExtent=function(n){return arguments.length?(t=+n[0][0],e=+n[1][0],a=+n[0][1],o=+n[1][1],t>e&&(n=t,t=e,e=n),a>o&&(n=a,a=o,o=n),v.precision(y)):[[t,a],[e,o]]},v.step=function(e){return arguments.length?v.majorStep(e).minorStep(e):v.minorStep()},v.majorStep=function(e){return arguments.length?(m=+e[0],g=+e[1],v):[m,g]},v.minorStep=function(e){return arguments.length?(p=+e[0],h=+e[1],v):[p,h]},v.precision=function(i){return arguments.length?(y=+i,c=Kr(a,o,90),u=$r(t,e,y),f=Kr(l,s,90),d=$r(r,n,y),v):y},v.majorExtent([[-180,-90+Te],[180,90-Te]]).minorExtent([[-180,-80-Te],[180,80+Te]])},i.geo.greatArc=function(){var e,t,n=Gr,r=Xr;function o(){return{type:"LineString",coordinates:[e||n.apply(this,arguments),t||r.apply(this,arguments)]}}return o.distance=function(){return i.geo.distance(e||n.apply(this,arguments),t||r.apply(this,arguments))},o.source=function(t){return arguments.length?(n=t,e="function"==typeof t?null:t,o):n},o.target=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,o):r},o.precision=function(){return arguments.length?o:0},o},i.geo.interpolate=function(e,t){return n=e[0]*Ae,r=e[1]*Ae,o=t[0]*Ae,i=t[1]*Ae,a=Math.cos(r),s=Math.sin(r),l=Math.cos(i),c=Math.sin(i),u=a*Math.cos(n),f=a*Math.sin(n),d=l*Math.cos(o),p=l*Math.sin(o),h=2*Math.asin(Math.sqrt(Ue(i-r)+a*l*Ue(o-n))),m=1/Math.sin(h),(g=h?function(e){var t=Math.sin(e*=h)*m,n=Math.sin(h-e)*m,r=n*u+t*d,o=n*f+t*p,i=n*s+t*c;return[Math.atan2(o,r)*Ie,Math.atan2(i,Math.sqrt(r*r+o*o))*Ie]}:function(){return[n*Ie,r*Ie]}).distance=h,g;var n,r,o,i,a,s,l,c,u,f,d,p,h,m,g},i.geo.length=function(e){return Sr=0,i.geo.stream(e,Qr),Sr};var Qr={sphere:F,point:F,lineStart:function(){var e,t,n;function r(r,o){var i=Math.sin(o*=Ae),a=Math.cos(o),s=_((r*=Ae)-e),l=Math.cos(s);Sr+=Math.atan2(Math.sqrt((s=a*Math.sin(s))*s+(s=n*i-t*a*l)*s),t*i+n*a*l),e=r,t=i,n=a}Qr.point=function(o,i){e=o*Ae,t=Math.sin(i*=Ae),n=Math.cos(i),Qr.point=r},Qr.lineEnd=function(){Qr.point=Qr.lineEnd=F}},lineEnd:F,polygonStart:F,polygonEnd:F};function Jr(e,t){function n(t,n){var r=Math.cos(t),o=Math.cos(n),i=e(r*o);return[i*o*Math.sin(t),i*Math.sin(n)]}return n.invert=function(e,n){var r=Math.sqrt(e*e+n*n),o=t(r),i=Math.sin(o),a=Math.cos(o);return[Math.atan2(e*i,r*a),Math.asin(r&&n*i/r)]},n}var Zr=Jr((function(e){return Math.sqrt(2/(1+e))}),(function(e){return 2*Math.asin(e/2)}));(i.geo.azimuthalEqualArea=function(){return Lr(Zr)}).raw=Zr;var eo=Jr((function(e){var t=Math.acos(e);return t&&t/Math.sin(t)}),A);function to(e,t){var n=Math.cos(e),r=function(e){return Math.tan(Oe/4+e/2)},o=e===t?Math.sin(e):Math.log(n/Math.cos(t))/Math.log(r(t)/r(e)),i=n*Math.pow(r(e),o)/o;if(!o)return oo;function a(e,t){i>0?t<-Re+Te&&(t=-Re+Te):t>Re-Te&&(t=Re-Te);var n=i/Math.pow(r(t),o);return[n*Math.sin(o*e),i-n*Math.cos(o*e)]}return a.invert=function(e,t){var n=i-t,r=Le(o)*Math.sqrt(e*e+n*n);return[Math.atan2(e,n)/o,2*Math.atan(Math.pow(i/r,1/o))-Re]},a}function no(e,t){var n=Math.cos(e),r=e===t?Math.sin(e):(n-Math.cos(t))/(t-e),o=n/r+e;if(_(r)1&&je(e[n[r-2]],e[n[r-1]],e[o])<=0;)--r;n[r++]=o}return n.slice(0,r)}function po(e,t){return e[0]-t[0]||e[1]-t[1]}(i.geo.stereographic=function(){return Lr(so)}).raw=so,lo.invert=function(e,t){return[-t,2*Math.atan(Math.exp(e))-Re]},(i.geo.transverseMercator=function(){var e=io(lo),t=e.center,n=e.rotate;return e.center=function(e){return e?t([-e[1],e[0]]):[(e=t())[1],-e[0]]},e.rotate=function(e){return e?n([e[0],e[1],e.length>2?e[2]+90:90]):[(e=n())[0],e[1],e[2]-90]},n([0,0,90])}).raw=lo,i.geom={},i.geom.hull=function(e){var t=co,n=uo;if(arguments.length)return r(e);function r(e){if(e.length<3)return[];var r,o=xt(t),i=xt(n),a=e.length,s=[],l=[];for(r=0;r=0;--r)p.push(e[s[c[r]][2]]);for(r=+f;rTe)s=s.L;else{if(!((o=i-No(s,a))>Te)){r>-Te?(t=s.P,n=s):o>-Te?(t=s,n=s.N):t=n=s;break}if(!s.R){t=s;break}s=s.R}var l=Mo(e);if(xo.insert(t,l),t||n){if(t===n)return Lo(t),n=Mo(t.site),xo.insert(l,n),l.edge=n.edge=zo(t.site,l.site),Io(t),void Io(n);if(n){Lo(t),Lo(n);var c=t.site,u=c.x,f=c.y,d=e.x-u,p=e.y-f,h=n.site,m=h.x-u,g=h.y-f,y=2*(d*g-p*m),v=d*d+p*p,b=m*m+g*g,x={x:(g*v-p*b)/y+u,y:(d*b-m*v)/y+f};Uo(n.edge,c,h,x),l.edge=zo(c,e,null,x),n.edge=zo(e,h,null,x),Io(t),Io(n)}else l.edge=zo(t.site,l.site)}}function Oo(e,t){var n=e.site,r=n.x,o=n.y,i=o-t;if(!i)return r;var a=e.P;if(!a)return-1/0;var s=(n=a.site).x,l=n.y,c=l-t;if(!c)return s;var u=s-r,f=1/i-1/c,d=u/c;return f?(-d+Math.sqrt(d*d-2*f*(u*u/(-2*c)-l+c/2+o-i/2)))/f+r:(r+s)/2}function No(e,t){var n=e.N;if(n)return Oo(n,t);var r=e.site;return r.y===t?r.x:1/0}function Do(e){this.site=e,this.edges=[]}function Ro(e,t){return t.angle-e.angle}function Ao(){qo(this),this.x=this.y=this.arc=this.site=this.cy=null}function Io(e){var t=e.P,n=e.N;if(t&&n){var r=t.site,o=e.site,i=n.site;if(r!==i){var a=o.x,s=o.y,l=r.x-a,c=r.y-s,u=i.x-a,f=2*(l*(g=i.y-s)-c*u);if(!(f>=-Pe)){var d=l*l+c*c,p=u*u+g*g,h=(g*d-c*p)/f,m=(l*p-u*d)/f,g=m+s,y=Eo.pop()||new Ao;y.arc=e,y.site=o,y.x=h+a,y.y=g+Math.sqrt(h*h+m*m),y.cy=g,e.circle=y;for(var v=null,b=_o._;b;)if(y.y=s)return;if(d>h){if(i){if(i.y>=c)return}else i={x:g,y:l};n={x:g,y:c}}else{if(i){if(i.y1)if(d>h){if(i){if(i.y>=c)return}else i={x:(l-o)/r,y:l};n={x:(c-o)/r,y:c}}else{if(i){if(i.y=s)return}else i={x:a,y:r*a+o};n={x:s,y:r*s+o}}else{if(i){if(i.xTe||_(o-n)>Te)&&(s.splice(a,0,new Vo(Bo(i.site,u,_(r-f)Te?{x:f,y:_(t-f)Te?{x:_(n-h)Te?{x:d,y:_(t-d)Te?{x:_(n-p)=n&&c.x<=o&&c.y>=r&&c.y<=a?[[n,a],[o,a],[o,r],[n,r]]:[]).point=e[s]})),t}function s(e){return e.map((function(e,t){return{x:Math.round(r(e,t)/Te)*Te,y:Math.round(o(e,t)/Te)*Te,i:t}}))}return a.links=function(e){return $o(s(e)).edges.filter((function(e){return e.l&&e.r})).map((function(t){return{source:e[t.l.i],target:e[t.r.i]}}))},a.triangles=function(e){var t=[];return $o(s(e)).cells.forEach((function(n,r){for(var o,i,a,s,l=n.site,c=n.edges.sort(Ro),u=-1,f=c.length,d=c[f-1].edge,p=d.l===l?d.r:d.l;++ui||f>a||d=w)<<1|t>=x,k=_+4;_i&&(o=t.slice(i,o),s[a]?s[a]+=o:s[++a]=o),(n=n[0])===(r=r[0])?s[a]?s[a]+=r:s[++a]=r:(s[++a]=null,l.push({i:a,x:ri(n,r)})),i=ai.lastIndex;return im&&(m=l.x),l.y>g&&(g=l.y),c.push(l.x),u.push(l.y);else for(f=0;fm&&(m=b),x>g&&(g=x),c.push(b),u.push(x)}var w=m-p,k=g-h;function E(e,t,n,r,o,i,a,s){if(!isNaN(n)&&!isNaN(r))if(e.leaf){var l=e.x,c=e.y;if(null!=l)if(_(l-n)+_(c-r)<.01)S(e,t,n,r,o,i,a,s);else{var u=e.point;e.x=e.y=e.point=null,S(e,u,l,c,o,i,a,s),S(e,t,n,r,o,i,a,s)}else e.x=n,e.y=r,e.point=t}else S(e,t,n,r,o,i,a,s)}function S(e,t,n,r,o,i,a,s){var l=.5*(o+a),c=.5*(i+s),u=n>=l,f=r>=c,d=f<<1|u;e.leaf=!1,u?o=l:a=l,f?i=c:s=c,E(e=e.nodes[d]||(e.nodes[d]={leaf:!0,nodes:[],point:null,x:null,y:null}),t,n,r,o,i,a,s)}w>k?g=h+w:m=p+k;var M={leaf:!0,nodes:[],point:null,x:null,y:null,add:function(e){E(M,e,+y(e,++f),+v(e,f),p,h,m,g)},visit:function(e){Zo(e,M,p,h,m,g)},find:function(e){return ei(M,e[0],e[1],p,h,m,g)}};if(f=-1,null==t){for(;++f=0&&!(n=i.interpolators[r](e,t)););return n}function li(e,t){var n,r=[],o=[],i=e.length,a=t.length,s=Math.min(e.length,t.length);for(n=0;n=1?1:e(t)}}function pi(e){return function(t){return 1-e(1-t)}}function hi(e){return function(t){return.5*(t<.5?e(2*t):2-e(2-2*t))}}function mi(e){return e*e}function gi(e){return e*e*e}function yi(e){if(e<=0)return 0;if(e>=1)return 1;var t=e*e,n=t*e;return 4*(e<.5?n:3*(e-t)+n-.75)}function vi(e){return 1-Math.cos(e*Re)}function bi(e){return Math.pow(2,10*(e-1))}function xi(e){return 1-Math.sqrt(1-e*e)}function wi(e){return e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375}function _i(e,t){return t-=e,function(n){return Math.round(e+t*n)}}function ki(e){var t,n,r,o=[e.a,e.b],i=[e.c,e.d],a=Si(o),s=Ei(o,i),l=Si(((t=i)[0]+=(r=-s)*(n=o)[0],t[1]+=r*n[1],t))||0;o[0]*i[1]=0?e.slice(0,t):e,r=t>=0?e.slice(t+1):"in";return n=ui.get(n)||ci,di((r=fi.get(r)||A)(n.apply(null,a.call(arguments,1))))},i.interpolateHcl=function(e,t){e=i.hcl(e),t=i.hcl(t);var n=e.h,r=e.c,o=e.l,a=t.h-n,s=t.c-r,l=t.l-o;isNaN(s)&&(s=0,r=isNaN(r)?t.c:r);isNaN(a)?(a=0,n=isNaN(n)?t.h:n):a>180?a-=360:a<-180&&(a+=360);return function(e){return Je(n+a*e,r+s*e,o+l*e)+""}},i.interpolateHsl=function(e,t){e=i.hsl(e),t=i.hsl(t);var n=e.h,r=e.s,o=e.l,a=t.h-n,s=t.s-r,l=t.l-o;isNaN(s)&&(s=0,r=isNaN(r)?t.s:r);isNaN(a)?(a=0,n=isNaN(n)?t.h:n):a>180?a-=360:a<-180&&(a+=360);return function(e){return Ge(n+a*e,r+s*e,o+l*e)+""}},i.interpolateLab=function(e,t){e=i.lab(e),t=i.lab(t);var n=e.l,r=e.a,o=e.b,a=t.l-n,s=t.a-r,l=t.b-o;return function(e){return ot(n+a*e,r+s*e,o+l*e)+""}},i.interpolateRound=_i,i.transform=function(e){var t=l.createElementNS(i.ns.prefix.svg,"g");return(i.transform=function(e){if(null!=e){t.setAttribute("transform",e);var n=t.transform.baseVal.consolidate()}return new ki(n?n.matrix:Mi)})(e)},ki.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var Mi={a:1,b:0,c:0,d:1,e:0,f:0};function Ci(e){return e.length?e.pop()+",":""}function Ti(e,t){var n=[],r=[];return e=i.transform(e),t=i.transform(t),function(e,t,n,r){if(e[0]!==t[0]||e[1]!==t[1]){var o=n.push("translate(",null,",",null,")");r.push({i:o-4,x:ri(e[0],t[0])},{i:o-2,x:ri(e[1],t[1])})}else(t[0]||t[1])&&n.push("translate("+t+")")}(e.translate,t.translate,n,r),function(e,t,n,r){e!==t?(e-t>180?t+=360:t-e>180&&(e+=360),r.push({i:n.push(Ci(n)+"rotate(",null,")")-2,x:ri(e,t)})):t&&n.push(Ci(n)+"rotate("+t+")")}(e.rotate,t.rotate,n,r),function(e,t,n,r){e!==t?r.push({i:n.push(Ci(n)+"skewX(",null,")")-2,x:ri(e,t)}):t&&n.push(Ci(n)+"skewX("+t+")")}(e.skew,t.skew,n,r),function(e,t,n,r){if(e[0]!==t[0]||e[1]!==t[1]){var o=n.push(Ci(n)+"scale(",null,",",null,")");r.push({i:o-4,x:ri(e[0],t[0])},{i:o-2,x:ri(e[1],t[1])})}else 1===t[0]&&1===t[1]||n.push(Ci(n)+"scale("+t+")")}(e.scale,t.scale,n,r),e=t=null,function(e){for(var t,o=-1,i=r.length;++o0?n=t:(e.c=null,e.t=NaN,e=null,l.end({type:"end",alpha:n=0})):t>0&&(l.start({type:"start",alpha:n=t}),e=Tt(s.tick)),s):n},s.start=function(){var e,t,n,i=y.length,l=v.length,u=c[0],h=c[1];for(e=0;e=0;)n.push(o[r])}function Hi(e,t){for(var n=[e],r=[];null!=(e=n.pop());)if(r.push(e),(i=e.children)&&(o=i.length))for(var o,i,a=-1;++a=0;)a.push(u=c[l]),u.parent=i,u.depth=i.depth+1;n&&(i.value=0),i.children=c}else n&&(i.value=+n.call(r,i,i.depth)||0),delete i.children;return Hi(o,(function(t){var r,o;e&&(r=t.children)&&r.sort(e),n&&(o=t.parent)&&(o.value+=t.value)})),s}return r.sort=function(t){return arguments.length?(e=t,r):e},r.children=function(e){return arguments.length?(t=e,r):t},r.value=function(e){return arguments.length?(n=e,r):n},r.revalue=function(e){return n&&(Vi(e,(function(e){e.children&&(e.value=0)})),Hi(e,(function(e){var t;e.children||(e.value=+n.call(r,e,e.depth)||0),(t=e.parent)&&(t.value+=e.value)}))),e},r},i.layout.partition=function(){var e=i.layout.hierarchy(),t=[1,1];function n(e,t,r,o){var i=e.children;if(e.x=t,e.y=e.depth*o,e.dx=r,e.dy=o,i&&(a=i.length)){var a,s,l,c=-1;for(r=e.value?r/e.value:0;++cs&&(s=r),a.push(r)}for(n=0;no&&(r=n,o=t);return r}function ra(e){return e.reduce(oa,0)}function oa(e,t){return e+t[1]}function ia(e,t){return aa(e,Math.ceil(Math.log(t.length)/Math.LN2+1))}function aa(e,t){for(var n=-1,r=+e[0],o=(e[1]-r)/t,i=[];++n<=t;)i[n]=o*n+r;return i}function sa(e){return[i.min(e),i.max(e)]}function la(e,t){return e.value-t.value}function ca(e,t){var n=e._pack_next;e._pack_next=t,t._pack_prev=e,t._pack_next=n,n._pack_prev=t}function ua(e,t){e._pack_next=t,t._pack_prev=e}function fa(e,t){var n=t.x-e.x,r=t.y-e.y,o=e.r+t.r;return.999*o*o>n*n+r*r}function da(e){if((t=e.children)&&(l=t.length)){var t,n,r,o,i,a,s,l,c=1/0,u=-1/0,f=1/0,d=-1/0;if(t.forEach(pa),(n=t[0]).x=-n.r,n.y=0,b(n),l>1&&((r=t[1]).x=r.r,r.y=0,b(r),l>2))for(ga(n,r,o=t[2]),b(o),ca(n,o),n._pack_prev=o,ca(o,r),r=n._pack_next,i=3;i0)for(a=-1;++a=f[0]&&l<=f[1]&&((s=c[i.bisect(d,l,1,h)-1]).y+=m,s.push(o[a]));return c}return o.value=function(e){return arguments.length?(t=e,o):t},o.range=function(e){return arguments.length?(n=xt(e),o):n},o.bins=function(e){return arguments.length?(r="number"==typeof e?function(t){return aa(t,e)}:xt(e),o):r},o.frequency=function(t){return arguments.length?(e=!!t,o):e},o},i.layout.pack=function(){var e,t=i.layout.hierarchy().sort(la),n=0,r=[1,1];function o(o,i){var a=t.call(this,o,i),s=a[0],l=r[0],c=r[1],u=null==e?Math.sqrt:"function"==typeof e?e:function(){return e};if(s.x=s.y=0,Hi(s,(function(e){e.r=+u(e.value)})),Hi(s,da),n){var f=n*(e?1:Math.max(2*s.r/l,2*s.r/c))/2;Hi(s,(function(e){e.r+=f})),Hi(s,da),Hi(s,(function(e){e.r-=f}))}return ma(s,l/2,c/2,e?1:1/Math.max(2*s.r/l,2*s.r/c)),a}return o.size=function(e){return arguments.length?(r=e,o):r},o.radius=function(t){return arguments.length?(e=null==t||"function"==typeof t?t:+t,o):e},o.padding=function(e){return arguments.length?(n=+e,o):n},Ui(o,t)},i.layout.tree=function(){var e=i.layout.hierarchy().sort(null).value(null),t=ya,n=[1,1],r=null;function o(o,i){var c=e.call(this,o,i),u=c[0],f=function(e){var t,n={A:null,children:[e]},r=[n];for(;null!=(t=r.pop());)for(var o,i=t.children,a=0,s=i.length;ap.x&&(p=e),e.depth>h.depth&&(h=e)}));var m=t(d,p)/2-d.x,g=n[0]/(p.x+t(p,d)/2+m),y=n[1]/(h.depth||1);Vi(u,(function(e){e.x=(e.x+m)*g,e.y=e.depth*y}))}return c}function a(e){var n=e.children,r=e.parent.children,o=e.i?r[e.i-1]:null;if(n.length){!function(e){var t,n=0,r=0,o=e.children,i=o.length;for(;--i>=0;)(t=o[i]).z+=n,t.m+=n,n+=t.s+(r+=t.c)}(e);var i=(n[0].z+n[n.length-1].z)/2;o?(e.z=o.z+t(e._,o._),e.m=e.z-i):e.z=i}else o&&(e.z=o.z+t(e._,o._));e.parent.A=function(e,n,r){if(n){for(var o,i=e,a=e,s=n,l=i.parent.children[0],c=i.m,u=a.m,f=s.m,d=l.m;s=ba(s),i=va(i),s&&i;)l=va(l),(a=ba(a)).a=e,(o=s.z+f-i.z-c+t(s._,i._))>0&&(xa(wa(s,e,r),e,o),c+=o,u+=o),f+=s.m,c+=i.m,d+=l.m,u+=a.m;s&&!ba(a)&&(a.t=s,a.m+=f-u),i&&!va(l)&&(l.t=i,l.m+=c-d,r=e)}return r}(e,o,e.parent.A||r[0])}function s(e){e._.x=e.z+e.parent.m,e.m+=e.parent.m}function l(e){e.x*=n[0],e.y=e.depth*n[1]}return o.separation=function(e){return arguments.length?(t=e,o):t},o.size=function(e){return arguments.length?(r=null==(n=e)?l:null,o):r?null:n},o.nodeSize=function(e){return arguments.length?(r=null==(n=e)?null:l,o):r?n:null},Ui(o,e)},i.layout.cluster=function(){var e=i.layout.hierarchy().sort(null).value(null),t=ya,n=[1,1],r=!1;function o(o,a){var s,l=e.call(this,o,a),c=l[0],u=0;Hi(c,(function(e){var n=e.children;n&&n.length?(e.x=function(e){return e.reduce((function(e,t){return e+t.x}),0)/e.length}(n),e.y=function(e){return 1+i.max(e,(function(e){return e.y}))}(n)):(e.x=s?u+=t(e,s):0,e.y=0,s=e)}));var f=_a(c),d=ka(c),p=f.x-t(f,d)/2,h=d.x+t(d,f)/2;return Hi(c,r?function(e){e.x=(e.x-c.x)*n[0],e.y=(c.y-e.y)*n[1]}:function(e){e.x=(e.x-p)/(h-p)*n[0],e.y=(1-(c.y?e.y/c.y:1))*n[1]}),l}return o.separation=function(e){return arguments.length?(t=e,o):t},o.size=function(e){return arguments.length?(r=null==(n=e),o):r?null:n},o.nodeSize=function(e){return arguments.length?(r=null!=(n=e),o):r?n:null},Ui(o,e)},i.layout.treemap=function(){var e,t=i.layout.hierarchy(),n=Math.round,r=[1,1],o=null,a=Ea,s=!1,l="squarify",c=.5*(1+Math.sqrt(5));function u(e,t){for(var n,r,o=-1,i=e.length;++o0;)s.push(n=c[o-1]),s.area+=n.area,"squarify"!==l||(r=p(s,m))<=d?(c.pop(),d=r):(s.area-=s.pop().area,h(s,m,i,!1),m=Math.min(i.dx,i.dy),s.length=s.area=0,d=1/0);s.length&&(h(s,m,i,!0),s.length=s.area=0),t.forEach(f)}}function d(e){var t=e.children;if(t&&t.length){var n,r=a(e),o=t.slice(),i=[];for(u(o,r.dx*r.dy/e.value),i.area=0;n=o.pop();)i.push(n),i.area+=n.area,null!=n.z&&(h(i,n.z?r.dx:r.dy,r,!o.length),i.length=i.area=0);t.forEach(d)}}function p(e,t){for(var n,r=e.area,o=0,i=1/0,a=-1,s=e.length;++ao&&(o=n));return t*=t,(r*=r)?Math.max(t*o*c/r,r/(t*i*c)):1/0}function h(e,t,r,o){var i,a=-1,s=e.length,l=r.x,c=r.y,u=t?n(e.area/t):0;if(t==r.dx){for((o||u>r.dy)&&(u=r.dy);++ar.dx)&&(u=r.dx);++a1);return e+t*n*Math.sqrt(-2*Math.log(o)/o)}},logNormal:function(){var e=i.random.normal.apply(i,arguments);return function(){return Math.exp(e())}},bates:function(e){var t=i.random.irwinHall(e);return function(){return t()/e}},irwinHall:function(e){return function(){for(var t=0,n=0;n2?Da:Ta,l=r?Oi:Pi;return o=a(e,t,l,n),i=a(t,e,l,si),s}function s(e){return o(e)}return s.invert=function(e){return i(e)},s.domain=function(t){return arguments.length?(e=t.map(Number),a()):e},s.range=function(e){return arguments.length?(t=e,a()):t},s.rangeRound=function(e){return s.range(e).interpolate(_i)},s.clamp=function(e){return arguments.length?(r=e,a()):r},s.interpolate=function(e){return arguments.length?(n=e,a()):n},s.ticks=function(t){return ja(e,t)},s.tickFormat=function(t,n){return Fa(e,t,n)},s.nice=function(t){return Ia(e,t),a()},s.copy=function(){return Ra(e,t,n,r)},a()}function Aa(e,t){return i.rebind(e,t,"range","rangeRound","interpolate","clamp")}function Ia(e,t){return Pa(e,Oa(La(e,t)[2])),Pa(e,Oa(La(e,t)[2])),e}function La(e,t){null==t&&(t=10);var n=Ma(e),r=n[1]-n[0],o=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*o;return i<=.15?o*=10:i<=.35?o*=5:i<=.75&&(o*=2),n[0]=Math.ceil(n[0]/o)*o,n[1]=Math.floor(n[1]/o)*o+.5*o,n[2]=o,n}function ja(e,t){return i.range.apply(i,La(e,t))}function Fa(e,t,n){var r=La(e,t);if(n){var o=It.exec(n);if(o.shift(),"s"===o[8]){var a=i.formatPrefix(Math.max(_(r[0]),_(r[1])));return o[7]||(o[7]="."+Ba(a.scale(r[2]))),o[8]="f",n=i.format(o.join("")),function(e){return n(a.scale(e))+a.symbol}}o[7]||(o[7]="."+function(e,t){var n=Ba(t[2]);return e in za?Math.abs(n-Ba(Math.max(_(t[0]),_(t[1]))))+ +("e"!==e):n-2*("%"===e)}(o[8],r)),n=o.join("")}else n=",."+Ba(r[2])+"f";return i.format(n)}i.scale.linear=function(){return Ra([0,1],[0,1],si,!1)};var za={s:1,g:1,p:1,r:1,e:1};function Ba(e){return-Math.floor(Math.log(e)/Math.LN10+.01)}function Ua(e,t,n,r){function o(e){return(n?Math.log(e<0?0:e):-Math.log(e>0?0:-e))/Math.log(t)}function a(e){return n?Math.pow(t,e):-Math.pow(t,-e)}function s(t){return e(o(t))}return s.invert=function(t){return a(e.invert(t))},s.domain=function(t){return arguments.length?(n=t[0]>=0,e.domain((r=t.map(Number)).map(o)),s):r},s.base=function(n){return arguments.length?(t=+n,e.domain(r.map(o)),s):t},s.nice=function(){var t=Pa(r.map(o),n?Math:Ha);return e.domain(t),r=t.map(a),s},s.ticks=function(){var e=Ma(r),i=[],s=e[0],l=e[1],c=Math.floor(o(s)),u=Math.ceil(o(l)),f=t%1?2:t;if(isFinite(u-c)){if(n){for(;c0;d--)i.push(a(c)*d);for(c=0;i[c]l;u--);i=i.slice(c,u)}return i},s.tickFormat=function(e,n){if(!arguments.length)return Va;arguments.length<2?n=Va:"function"!=typeof n&&(n=i.format(n));var r=Math.max(1,t*e/s.ticks().length);return function(e){var i=e/a(Math.round(o(e)));return i*t0?n[r-1]:e[0],rf?0:1;if(c=De)return l(c,p)+(s?l(s,1-p):"")+"Z";var h,m,g,y,v,b,x,w,_,k,E,S,M=0,C=0,T=[];if((y=(+a.apply(this,arguments)||0)/2)&&(g=r===ns?Math.sqrt(s*s+c*c):+r.apply(this,arguments),p||(C*=-1),c&&(C=ze(g/c*Math.sin(y))),s&&(M=ze(g/s*Math.sin(y)))),c){v=c*Math.cos(u+C),b=c*Math.sin(u+C),x=c*Math.cos(f-C),w=c*Math.sin(f-C);var P=Math.abs(f-u-2*C)<=Oe?0:1;if(C&&ls(v,b,x,w)===p^P){var O=(u+f)/2;v=c*Math.cos(O),b=c*Math.sin(O),x=w=null}}else v=b=0;if(s){_=s*Math.cos(f-M),k=s*Math.sin(f-M),E=s*Math.cos(u+M),S=s*Math.sin(u+M);var N=Math.abs(u-f+2*M)<=Oe?0:1;if(M&&ls(_,k,E,S)===1-p^N){var D=(u+f)/2;_=s*Math.cos(D),k=s*Math.sin(D),E=S=null}}else _=k=0;if(d>Te&&(h=Math.min(Math.abs(c-s)/2,+n.apply(this,arguments)))>.001){m=s0?0:1}function cs(e,t,n,r,o){var i=e[0]-t[0],a=e[1]-t[1],s=(o?r:-r)/Math.sqrt(i*i+a*a),l=s*a,c=-s*i,u=e[0]+l,f=e[1]+c,d=t[0]+l,p=t[1]+c,h=(u+d)/2,m=(f+p)/2,g=d-u,y=p-f,v=g*g+y*y,b=n-r,x=u*p-d*f,w=(y<0?-1:1)*Math.sqrt(Math.max(0,b*b*v-x*x)),_=(x*y-g*w)/v,k=(-x*g-y*w)/v,E=(x*y+g*w)/v,S=(-x*g+y*w)/v,M=_-h,C=k-m,T=E-h,P=S-m;return M*M+C*C>T*T+P*P&&(_=E,k=S),[[_-l,k-c],[_*n/b,k*n/b]]}function us(e){var t=co,n=uo,r=Zn,o=ds,i=o.key,a=.7;function s(i){var s,l=[],c=[],u=-1,f=i.length,d=xt(t),p=xt(n);function h(){l.push("M",o(e(c),a))}for(;++u1&&o.push("H",r[0]);return o.join("")},"step-before":hs,"step-after":ms,basis:vs,"basis-open":function(e){if(e.length<4)return ds(e);var t,n=[],r=-1,o=e.length,i=[0],a=[0];for(;++r<3;)t=e[r],i.push(t[0]),a.push(t[1]);n.push(bs(_s,i)+","+bs(_s,a)),--r;for(;++r9&&(o=3*t/Math.sqrt(o),a[s]=o*n,a[s+1]=o*r);s=-1;for(;++s<=l;)o=(e[Math.min(l,s+1)][0]-e[Math.max(0,s-1)][0])/(6*(1+a[s]*a[s])),i.push([o||0,a[s]*o||0]);return i}(e))}});function ds(e){return e.length>1?e.join("L"):e+"Z"}function ps(e){return e.join("L")+"Z"}function hs(e){for(var t=0,n=e.length,r=e[0],o=[r[0],",",r[1]];++t1){s=t[1],i=e[l],l++,r+="C"+(o[0]+a[0])+","+(o[1]+a[1])+","+(i[0]-s[0])+","+(i[1]-s[1])+","+i[0]+","+i[1];for(var c=2;cOe)+",1 "+t}function l(e,t,n,r){return"Q 0,0 "+r}return i.radius=function(e){return arguments.length?(n=xt(e),i):n},i.source=function(t){return arguments.length?(e=xt(t),i):e},i.target=function(e){return arguments.length?(t=xt(e),i):t},i.startAngle=function(e){return arguments.length?(r=xt(e),i):r},i.endAngle=function(e){return arguments.length?(o=xt(e),i):o},i},i.svg.diagonal=function(){var e=Gr,t=Xr,n=Ts;function r(r,o){var i=e.call(this,r,o),a=t.call(this,r,o),s=(i.y+a.y)/2,l=[i,{x:i.x,y:s},{x:a.x,y:s},a];return"M"+(l=l.map(n))[0]+"C"+l[1]+" "+l[2]+" "+l[3]}return r.source=function(t){return arguments.length?(e=xt(t),r):e},r.target=function(e){return arguments.length?(t=xt(e),r):t},r.projection=function(e){return arguments.length?(n=e,r):n},r},i.svg.diagonal.radial=function(){var e=i.svg.diagonal(),t=Ts,n=e.projection;return e.projection=function(e){return arguments.length?n(Ps(t=e)):t},e},i.svg.symbol=function(){var e=Ns,t=Os;function n(n,r){return(Rs.get(e.call(this,n,r))||Ds)(t.call(this,n,r))}return n.type=function(t){return arguments.length?(e=xt(t),n):e},n.size=function(e){return arguments.length?(t=xt(e),n):t},n};var Rs=i.map({circle:Ds,cross:function(e){var t=Math.sqrt(e/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(e){var t=Math.sqrt(e/(2*Is)),n=t*Is;return"M0,"+-t+"L"+n+",0 0,"+t+" "+-n+",0Z"},square:function(e){var t=Math.sqrt(e)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(e){var t=Math.sqrt(e/As),n=t*As/2;return"M0,"+n+"L"+t+","+-n+" "+-t+","+-n+"Z"},"triangle-up":function(e){var t=Math.sqrt(e/As),n=t*As/2;return"M0,"+-n+"L"+t+","+n+" "+-t+","+n+"Z"}});i.svg.symbolTypes=Rs.keys();var As=Math.sqrt(3),Is=Math.tan(30*Ae);X.transition=function(e){for(var t,n,r=zs||++Vs,o=Ws(e),i=[],a=Bs||{time:Date.now(),ease:yi,delay:0,duration:250},s=-1,l=this.length;++s0;)c[--d].call(e,a);if(i>=1)return f.event&&f.event.end.call(e,e.__data__,t),--u.count?delete u[r]:delete e[n],1}f||(i=o.time,a=Tt((function(e){var t=f.delay;if(a.t=t+i,t<=e)return d(e-t);a.c=d}),0,i),f=u[r]={tween:new S,time:i,timer:a,delay:o.delay,duration:o.duration,ease:o.ease,index:t},o=null,++u.count)}Us.call=X.call,Us.empty=X.empty,Us.node=X.node,Us.size=X.size,i.transition=function(e,t){return e&&e.transition?zs?e.transition(t):e:i.selection().transition(e)},i.transition.prototype=Us,Us.select=function(e){var t,n,r,o=this.id,i=this.namespace,a=[];e=Q(e);for(var s=-1,l=this.length;++srect,.s>rect").attr("width",a[1]-a[0])}function m(e){e.select(".extent").attr("y",s[0]),e.selectAll(".extent,.e>rect,.w>rect").attr("height",s[1]-s[0])}function g(){var f,g,y=this,v=i.select(i.event.target),b=n.of(y,arguments),x=i.select(y),w=v.datum(),_=!/^(n|s)$/.test(w)&&r,k=!/^(e|w)$/.test(w)&&o,E=v.classed("extent"),S=Ee(y),M=i.mouse(y),C=i.select(u(y)).on("keydown.brush",O).on("keyup.brush",N);if(i.event.changedTouches?C.on("touchmove.brush",D).on("touchend.brush",A):C.on("mousemove.brush",D).on("mouseup.brush",A),x.interrupt().selectAll("*").interrupt(),E)M[0]=a[0]-M[0],M[1]=s[0]-M[1];else if(w){var T=+/w$/.test(w),P=+/^n/.test(w);g=[a[1-T]-M[0],s[1-P]-M[1]],M[0]=a[T],M[1]=s[P]}else i.event.altKey&&(f=M.slice());function O(){32==i.event.keyCode&&(E||(f=null,M[0]-=a[1],M[1]-=s[1],E=2),U())}function N(){32==i.event.keyCode&&2==E&&(M[0]+=a[1],M[1]+=s[1],E=0,U())}function D(){var e=i.mouse(y),t=!1;g&&(e[0]+=g[0],e[1]+=g[1]),E||(i.event.altKey?(f||(f=[(a[0]+a[1])/2,(s[0]+s[1])/2]),M[0]=a[+(e[0]1?{floor:function(t){for(;s(t=e.floor(t));)t=ol(t-1);return t},ceil:function(t){for(;s(t=e.ceil(t));)t=ol(+t+1);return t}}:e))},r.ticks=function(e,t){var n=Ma(r.domain()),i=null==e?o(n,10):"number"==typeof e?o(n,e):!e.range&&[{range:e},t];return i&&(e=i[0],t=i[1]),e.range(n[0],ol(+n[1]+1),t<1?1:t)},r.tickFormat=function(){return n},r.copy=function(){return rl(e.copy(),t,n)},Aa(r,e)}function ol(e){return new Date(e)}Zs.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?nl:tl,nl.parse=function(e){var t=new Date(e);return isNaN(t)?null:t},nl.toString=tl.toString,Ft.second=Vt((function(e){return new zt(1e3*Math.floor(e/1e3))}),(function(e,t){e.setTime(e.getTime()+1e3*Math.floor(t))}),(function(e){return e.getSeconds()})),Ft.seconds=Ft.second.range,Ft.seconds.utc=Ft.second.utc.range,Ft.minute=Vt((function(e){return new zt(6e4*Math.floor(e/6e4))}),(function(e,t){e.setTime(e.getTime()+6e4*Math.floor(t))}),(function(e){return e.getMinutes()})),Ft.minutes=Ft.minute.range,Ft.minutes.utc=Ft.minute.utc.range,Ft.hour=Vt((function(e){var t=e.getTimezoneOffset()/60;return new zt(36e5*(Math.floor(e/36e5-t)+t))}),(function(e,t){e.setTime(e.getTime()+36e5*Math.floor(t))}),(function(e){return e.getHours()})),Ft.hours=Ft.hour.range,Ft.hours.utc=Ft.hour.utc.range,Ft.month=Vt((function(e){return(e=Ft.day(e)).setDate(1),e}),(function(e,t){e.setMonth(e.getMonth()+t)}),(function(e){return e.getMonth()})),Ft.months=Ft.month.range,Ft.months.utc=Ft.month.utc.range;var il=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],al=[[Ft.second,1],[Ft.second,5],[Ft.second,15],[Ft.second,30],[Ft.minute,1],[Ft.minute,5],[Ft.minute,15],[Ft.minute,30],[Ft.hour,1],[Ft.hour,3],[Ft.hour,6],[Ft.hour,12],[Ft.day,1],[Ft.day,2],[Ft.week,1],[Ft.month,1],[Ft.month,3],[Ft.year,1]],sl=Zs.multi([[".%L",function(e){return e.getMilliseconds()}],[":%S",function(e){return e.getSeconds()}],["%I:%M",function(e){return e.getMinutes()}],["%I %p",function(e){return e.getHours()}],["%a %d",function(e){return e.getDay()&&1!=e.getDate()}],["%b %d",function(e){return 1!=e.getDate()}],["%B",function(e){return e.getMonth()}],["%Y",Zn]]),ll={range:function(e,t,n){return i.range(Math.ceil(e/n)*n,+t,n).map(ol)},floor:A,ceil:A};al.year=Ft.year,Ft.scale=function(){return rl(i.scale.linear(),al,sl)};var cl=al.map((function(e){return[e[0].utc,e[1]]})),ul=el.multi([[".%L",function(e){return e.getUTCMilliseconds()}],[":%S",function(e){return e.getUTCSeconds()}],["%I:%M",function(e){return e.getUTCMinutes()}],["%I %p",function(e){return e.getUTCHours()}],["%a %d",function(e){return e.getUTCDay()&&1!=e.getUTCDate()}],["%b %d",function(e){return 1!=e.getUTCDate()}],["%B",function(e){return e.getUTCMonth()}],["%Y",Zn]]);function fl(e){return JSON.parse(e.responseText)}function dl(e){var t=l.createRange();return t.selectNode(l.body),t.createContextualFragment(e.responseText)}cl.year=Ft.year.utc,Ft.scale.utc=function(){return rl(i.scale.linear(),cl,ul)},i.text=wt((function(e){return e.responseText})),i.json=function(e,t){return _t(e,"application/json",fl,t)},i.html=function(e,t){return _t(e,"text/html",dl,t)},i.xml=wt((function(e){return e.responseXML})),this.d3=i,void 0===(o="function"==typeof(r=i)?r.call(t,n,t,e):r)||(e.exports=o)}()},152:(e,t,n)=>{!function(){var t=window.d3,r=window.topojson,o={scope:"world",responsive:!1,aspectRatio:.5625,setProjection:function(e,n){var r,o,i=n.width||e.offsetWidth,a=n.height||e.offsetHeight,s=this.svg;n&&void 0===n.scope&&(n.scope="world");"usa"===n.scope?r=t.geo.albersUsa().scale(i).translate([i/2,a/2]):"world"===n.scope&&(r=t.geo[n.projection]().scale((i+1)/2/Math.PI).translate([i/2,a/("mercator"===n.projection?1.45:1.8)]));"orthographic"===n.projection&&(s.append("defs").append("path").datum({type:"Sphere"}).attr("id","sphere").attr("d",o),s.append("use").attr("class","stroke").attr("xlink:href","#sphere"),s.append("use").attr("class","fill").attr("xlink:href","#sphere"),r.scale(250).clipAngle(90).rotate(n.projectionConfig.rotation));return{path:o=t.geo.path().projection(r),projection:r}},projection:"equirectangular",dataType:"json",data:{},done:function(){},fills:{defaultFill:"#ABDDA4"},filters:{},geographyConfig:{dataUrl:null,hideAntarctica:!0,hideHawaiiAndAlaska:!1,borderWidth:1,borderOpacity:1,borderColor:"#FDFDFD",popupTemplate:function(e,t){return'
'+e.properties.name+"
"},popupOnHover:!0,highlightOnHover:!0,highlightFillColor:"#FC8D59",highlightBorderColor:"rgba(250, 15, 160, 0.2)",highlightBorderWidth:2,highlightBorderOpacity:1},projectionConfig:{rotation:[97,0]},bubblesConfig:{borderWidth:2,borderOpacity:1,borderColor:"#FFFFFF",popupOnHover:!0,radius:null,popupTemplate:function(e,t){return'
'+t.name+"
"},fillOpacity:.75,animate:!0,highlightOnHover:!0,highlightFillColor:"#FC8D59",highlightBorderColor:"rgba(250, 15, 160, 0.2)",highlightBorderWidth:2,highlightBorderOpacity:1,highlightFillOpacity:.85,exitDelay:100,key:JSON.stringify},arcConfig:{strokeColor:"#DD1C77",strokeWidth:1,arcSharpness:1,animationSpeed:600,popupOnHover:!1,popupTemplate:function(e,t){return t.origin&&t.destination&&t.origin.latitude&&t.origin.longitude&&t.destination.latitude&&t.destination.longitude?'
Arc
Origin: '+JSON.stringify(t.origin)+"
Destination: "+JSON.stringify(t.destination)+"
":t.origin&&t.destination?'
Arc
'+t.origin+" -> "+t.destination+"
":""}}};function i(e,t,n){void 0===n&&(n=t,optionsValues=void 0);var r=void 0!==e?e:t;if(void 0===r)return null;if("function"==typeof r){var o=[n];return n.geography&&(o=[n.geography,n.data]),r.apply(null,o)}return r}function a(e,n,r){return this.svg=t.select(e).append("svg").attr("width",r||e.offsetWidth).attr("data-width",r||e.offsetWidth).attr("class","datamap").attr("height",n||e.offsetHeight).style("overflow","hidden"),this.options.responsive&&(t.select(this.options.element).style({position:"relative","padding-bottom":100*this.options.aspectRatio+"%"}),t.select(this.options.element).select("svg").style({position:"absolute",width:"100%",height:"100%"}),t.select(this.options.element).select("svg").select("g").selectAll("path").style("vector-effect","non-scaling-stroke")),this.svg}function s(e){var t=this.options.fills,n=this.options.data||{},o=this.options.geographyConfig,a=this.svg.select("g.datamaps-subunits");a.empty()&&(a=this.addLayer("datamaps-subunits",null,!0));var s=r.feature(e,e.objects[this.options.scope]).features;o.hideAntarctica&&(s=s.filter((function(e){return"ATA"!==e.id}))),o.hideHawaiiAndAlaska&&(s=s.filter((function(e){return"HI"!==e.id&&"AK"!==e.id}))),a.selectAll("path.datamaps-subunit").data(s).enter().append("path").attr("d",this.path).attr("class",(function(e){return"datamaps-subunit "+e.id})).attr("data-info",(function(e){return JSON.stringify(n[e.id])})).style("fill",(function(e){var r,o=n[e.id];return o&&o.fillKey&&(r=t[i(o.fillKey,{data:n[e.id],geography:e})]),void 0===r&&(r=i(o&&o.fillColor,t.defaultFill,{data:n[e.id],geography:e})),r})).style("stroke-width",o.borderWidth).style("stroke-opacity",o.borderOpacity).style("stroke",o.borderColor)}function l(){var e=this.svg,n=this,r=this.options.geographyConfig;function o(){this.parentNode.appendChild(this)}(r.highlightOnHover||r.popupOnHover)&&e.selectAll(".datamaps-subunit").on("mouseover",(function(a){var s=t.select(this),l=n.options.data[a.id]||{};if(r.highlightOnHover){var c={fill:s.style("fill"),stroke:s.style("stroke"),"stroke-width":s.style("stroke-width"),"fill-opacity":s.style("fill-opacity")};s.style("fill",i(l.highlightFillColor,r.highlightFillColor,l)).style("stroke",i(l.highlightBorderColor,r.highlightBorderColor,l)).style("stroke-width",i(l.highlightBorderWidth,r.highlightBorderWidth,l)).style("stroke-opacity",i(l.highlightBorderOpacity,r.highlightBorderOpacity,l)).style("fill-opacity",i(l.highlightFillOpacity,r.highlightFillOpacity,l)).attr("data-previousAttributes",JSON.stringify(c)),/((MSIE)|(Trident))/.test(navigator.userAgent)||o.call(this)}r.popupOnHover&&n.updatePopup(s,a,r,e)})).on("mouseout",(function(){var e=t.select(this);if(r.highlightOnHover){var n=JSON.parse(e.attr("data-previousAttributes"));for(var o in n)e.style(o,n[o])}e.on("mousemove",null),t.selectAll(".datamaps-hoverover").style("display","none")}))}function c(e,n,r){if(n=n||{},this.options.fills){var o="
",i="";for(var a in n.legendTitle&&(o="

"+n.legendTitle+"

"+o),this.options.fills){if("defaultFill"===a){if(!n.defaultFillName)continue;i=n.defaultFillName}else i=n.labels&&n.labels[a]?n.labels[a]:a+": ";o+="
"+i+"
",o+='
 
'}o+="
";t.select(this.options.element).append("div").attr("class","datamaps-legend").html(o)}}function u(e,n){var r=t.geo.graticule();this.svg.insert("path",".datamaps-subunits").datum(r).attr("class","datamaps-graticule").attr("d",this.path)}function f(e,n,r){var a=this,s=this.svg;if(!n||n&&!n.slice)throw"Datamaps Error - arcs must be an array";for(var l=0;l-1&&(l=-2.5),"NY"===o.id&&(l=-1),"MI"===o.id&&(c=18),"LA"===o.id&&(l=13),i=s[0]-l,a=s[1]+c;var u=["VT","NH","MA","RI","CT","NJ","DE","MD","DC"].indexOf(o.id);if(u>-1){var f=r[1];i=r[0],a=f+u*(2+(t.fontSize||12)),e.append("line").attr("x1",i-3).attr("y1",a-5).attr("x2",s[0]).attr("y2",s[1]).style("stroke",t.labelColor||"#000").style("stroke-width",t.lineWidth||1)}return e.append("text").attr("x",i).attr("y",a).style("font-size",(t.fontSize||10)+"px").style("font-family",t.fontFamily||"Verdana").style("fill",t.labelColor||"#000").text((function(){return t.customLabelText&&t.customLabelText[o.id]?t.customLabelText[o.id]:o.id})),"bar"}))}function p(e,n,r){var o=this,a=this.options.fills,s=this.options.filters,l=this.svg;if(!n||n&&!n.slice)throw"Datamaps Error - bubbles must be an array";var c=e.selectAll("circle.datamaps-bubble").data(n,r.key);function u(e){return void 0!==e&&void 0!==e.latitude&&void 0!==e.longitude}c.enter().append("svg:circle").attr("class","datamaps-bubble").attr("cx",(function(e){var t;if(u(e)?t=o.latLngToXY(e.latitude,e.longitude):e.centered&&(t="USA"===e.centered?o.projection([-98.58333,39.83333]):o.path.centroid(l.select("path."+e.centered).data()[0])),t)return t[0]})).attr("cy",(function(e){var t;if(u(e)?t=o.latLngToXY(e.latitude,e.longitude):e.centered&&(t="USA"===e.centered?o.projection([-98.58333,39.83333]):o.path.centroid(l.select("path."+e.centered).data()[0])),t)return t[1]})).attr("r",(function(e){return r.animate?0:i(e.radius,r.radius,e)})).attr("data-info",(function(e){return JSON.stringify(e)})).attr("filter",(function(e){var t=s[i(e.filterKey,r.filterKey,e)];if(t)return t})).style("stroke",(function(e){return i(e.borderColor,r.borderColor,e)})).style("stroke-width",(function(e){return i(e.borderWidth,r.borderWidth,e)})).style("stroke-opacity",(function(e){return i(e.borderOpacity,r.borderOpacity,e)})).style("fill-opacity",(function(e){return i(e.fillOpacity,r.fillOpacity,e)})).style("fill",(function(e){return a[i(e.fillKey,r.fillKey,e)]||a.defaultFill})).on("mouseover",(function(e){var n=t.select(this);if(r.highlightOnHover){var a={fill:n.style("fill"),stroke:n.style("stroke"),"stroke-width":n.style("stroke-width"),"fill-opacity":n.style("fill-opacity")};n.style("fill",i(e.highlightFillColor,r.highlightFillColor,e)).style("stroke",i(e.highlightBorderColor,r.highlightBorderColor,e)).style("stroke-width",i(e.highlightBorderWidth,r.highlightBorderWidth,e)).style("stroke-opacity",i(e.highlightBorderOpacity,r.highlightBorderOpacity,e)).style("fill-opacity",i(e.highlightFillOpacity,r.highlightFillOpacity,e)).attr("data-previousAttributes",JSON.stringify(a))}r.popupOnHover&&o.updatePopup(n,e,r,l)})).on("mouseout",(function(e){var n=t.select(this);if(r.highlightOnHover){var o=JSON.parse(n.attr("data-previousAttributes"));for(var i in o)n.style(i,o[i])}t.selectAll(".datamaps-hoverover").style("display","none")})),c.transition().duration(400).attr("r",(function(e){return i(e.radius,r.radius,e)})).transition().duration(0).attr("data-info",(function(e){return JSON.stringify(e)})),c.exit().transition().delay(r.exitDelay).attr("r",0).remove()}function h(e){return Array.prototype.slice.call(arguments,1).forEach((function(t){if(t)for(var n in t)null==e[n]&&("function"==typeof t[n]?e[n]=t[n]:e[n]=JSON.parse(JSON.stringify(t[n])))})),e}function m(e){if(void 0===t||void 0===r)throw new Error("Include d3.js (v3.0.3 or greater) and topojson on this page before creating a new map");return this.options=h(e,o),this.options.geographyConfig=h(e.geographyConfig,o.geographyConfig),this.options.projectionConfig=h(e.projectionConfig,o.projectionConfig),this.options.bubblesConfig=h(e.bubblesConfig,o.bubblesConfig),this.options.arcConfig=h(e.arcConfig,o.arcConfig),t.select(this.options.element).select("svg").length>0&&a.call(this,this.options.element,this.options.height,this.options.width),this.addPlugin("bubbles",p),this.addPlugin("legend",c),this.addPlugin("arc",f),this.addPlugin("labels",d),this.addPlugin("graticule",u),this.options.disableDefaultStyles||t.select(".datamaps-style-block").empty()&&t.select("head").append("style").attr("class","datamaps-style-block").html('.datamap path.datamaps-graticule { fill: none; stroke: #777; stroke-width: 0.5px; stroke-opacity: .5; pointer-events: none; } .datamap .labels {pointer-events: none;} .datamap path:not(.datamaps-arc), .datamap circle, .datamap line {stroke: #FFFFFF; vector-effect: non-scaling-stroke; stroke-width: 1px;} .datamaps-legend dt, .datamaps-legend dd { float: left; margin: 0 3px 0 0;} .datamaps-legend dd {width: 20px; margin-right: 6px; border-radius: 3px;} .datamaps-legend {padding-bottom: 20px; z-index: 1001; position: absolute; left: 4px; font-size: 12px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;} .datamaps-hoverover {display: none; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } .hoverinfo {padding: 4px; border-radius: 1px; background-color: #FFF; box-shadow: 1px 1px 5px #CCC; font-size: 12px; border: 1px solid #CCC; } .hoverinfo hr {border:1px dotted #CCC; }'),this.draw()}m.prototype.resize=function(){var e=this.options;if(e.responsive){var n=e.element.clientWidth,r=t.select(e.element).select("svg").attr("data-width");t.select(e.element).select("svg").selectAll("g").attr("transform","scale("+n/r+")")}},m.prototype.draw=function(){var e=this,n=e.options,r=n.setProjection.apply(this,[n.element,n]);return this.path=r.path,this.projection=r.projection,n.geographyConfig.dataUrl?t.json(n.geographyConfig.dataUrl,(function(t,n){if(t)throw new Error(t);e.customTopo=n,o(n)})):o(this[n.scope+"Topo"]||n.geographyConfig.dataJson),this;function o(n){e.options.dataUrl&&t[e.options.dataType](e.options.dataUrl,(function(t){if("csv"===e.options.dataType&&t&&t.slice){for(var n={},r=0;r{"use strict";function t(e){return"function"==typeof e?e():e}function n(){var e={};return e.promise=new Promise((function(t,n){e.resolve=t,e.reject=n})),e}e.exports=function(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=void 0,a=void 0,s=void 0,l=[];return function(){var u=t(r),f=(new Date).getTime(),d=!i||f-i>u;i=f;for(var p=arguments.length,h=Array(p),m=0;m{"use strict";var n=60103,r=60106,o=60107,i=60108,a=60114,s=60109,l=60110,c=60112,u=60113,f=60120,d=60115,p=60116,h=60121,m=60122,g=60117,y=60129,v=60131;if("function"==typeof Symbol&&Symbol.for){var b=Symbol.for;n=b("react.element"),r=b("react.portal"),o=b("react.fragment"),i=b("react.strict_mode"),a=b("react.profiler"),s=b("react.provider"),l=b("react.context"),c=b("react.forward_ref"),u=b("react.suspense"),f=b("react.suspense_list"),d=b("react.memo"),p=b("react.lazy"),h=b("react.block"),m=b("react.server.block"),g=b("react.fundamental"),y=b("react.debug_trace_mode"),v=b("react.legacy_hidden")}function x(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case n:switch(e=e.type){case o:case a:case i:case u:case f:return e;default:switch(e=e&&e.$$typeof){case l:case c:case p:case d:case s:return e;default:return t}}case r:return t}}}},726:(e,t,n)=>{"use strict";n(781)},144:function(e){e.exports=function(){"use strict";var e=function(){return(e=Object.assign||function(e){for(var t,n=1,r=arguments.length;n",noCalendar:!1,now:new Date,onChange:[],onClose:[],onDayCreate:[],onDestroy:[],onKeyDown:[],onMonthChange:[],onOpen:[],onParseConfig:[],onReady:[],onValueUpdate:[],onYearChange:[],onPreCalendarPosition:[],plugins:[],position:"auto",positionElement:void 0,prevArrow:"",shorthandCurrentMonth:!1,showMonths:1,static:!1,time_24hr:!1,weekNumbers:!1,wrap:!1},o={weekdays:{shorthand:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],longhand:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},months:{shorthand:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],longhand:["January","February","March","April","May","June","July","August","September","October","November","December"]},daysInMonth:[31,28,31,30,31,30,31,31,30,31,30,31],firstDayOfWeek:0,ordinal:function(e){var t=e%100;if(t>3&&t<21)return"th";switch(t%10){case 1:return"st";case 2:return"nd";case 3:return"rd";default:return"th"}},rangeSeparator:" to ",weekAbbreviation:"Wk",scrollTitle:"Scroll to increment",toggleTitle:"Click to toggle",amPM:["AM","PM"],yearAriaLabel:"Year",monthAriaLabel:"Month",hourAriaLabel:"Hour",minuteAriaLabel:"Minute",time_24hr:!1},i=function(e,t){return void 0===t&&(t=2),("000"+e).slice(-1*t)},a=function(e){return!0===e?1:0};function s(e,t,n){var r;return void 0===n&&(n=!1),function(){var o=this,i=arguments;null!==r&&clearTimeout(r),r=window.setTimeout((function(){r=null,n||e.apply(o,i)}),t),n&&!r&&e.apply(o,i)}}var l=function(e){return e instanceof Array?e:[e]};function c(e,t,n){if(!0===n)return e.classList.add(t);e.classList.remove(t)}function u(e,t,n){var r=window.document.createElement(e);return t=t||"",n=n||"",r.className=t,void 0!==n&&(r.textContent=n),r}function f(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function d(e,t){return t(e)?e:e.parentNode?d(e.parentNode,t):void 0}function p(e,t){var n=u("div","numInputWrapper"),r=u("input","numInput "+e),o=u("span","arrowUp"),i=u("span","arrowDown");if(-1===navigator.userAgent.indexOf("MSIE 9.0")?r.type="number":(r.type="text",r.pattern="\\d*"),void 0!==t)for(var a in t)r.setAttribute(a,t[a]);return n.appendChild(r),n.appendChild(o),n.appendChild(i),n}function h(e){try{return"function"==typeof e.composedPath?e.composedPath()[0]:e.target}catch(t){return e.target}}var m=function(){},g=function(e,t,n){return n.months[t?"shorthand":"longhand"][e]},y={D:m,F:function(e,t,n){e.setMonth(n.months.longhand.indexOf(t))},G:function(e,t){e.setHours(parseFloat(t))},H:function(e,t){e.setHours(parseFloat(t))},J:function(e,t){e.setDate(parseFloat(t))},K:function(e,t,n){e.setHours(e.getHours()%12+12*a(new RegExp(n.amPM[1],"i").test(t)))},M:function(e,t,n){e.setMonth(n.months.shorthand.indexOf(t))},S:function(e,t){e.setSeconds(parseFloat(t))},U:function(e,t){return new Date(1e3*parseFloat(t))},W:function(e,t,n){var r=parseInt(t),o=new Date(e.getFullYear(),0,2+7*(r-1),0,0,0,0);return o.setDate(o.getDate()-o.getDay()+n.firstDayOfWeek),o},Y:function(e,t){e.setFullYear(parseFloat(t))},Z:function(e,t){return new Date(t)},d:function(e,t){e.setDate(parseFloat(t))},h:function(e,t){e.setHours(parseFloat(t))},i:function(e,t){e.setMinutes(parseFloat(t))},j:function(e,t){e.setDate(parseFloat(t))},l:m,m:function(e,t){e.setMonth(parseFloat(t)-1)},n:function(e,t){e.setMonth(parseFloat(t)-1)},s:function(e,t){e.setSeconds(parseFloat(t))},u:function(e,t){return new Date(parseFloat(t))},w:m,y:function(e,t){e.setFullYear(2e3+parseFloat(t))}},v={D:"(\\w+)",F:"(\\w+)",G:"(\\d\\d|\\d)",H:"(\\d\\d|\\d)",J:"(\\d\\d|\\d)\\w+",K:"",M:"(\\w+)",S:"(\\d\\d|\\d)",U:"(.+)",W:"(\\d\\d|\\d)",Y:"(\\d{4})",Z:"(.+)",d:"(\\d\\d|\\d)",h:"(\\d\\d|\\d)",i:"(\\d\\d|\\d)",j:"(\\d\\d|\\d)",l:"(\\w+)",m:"(\\d\\d|\\d)",n:"(\\d\\d|\\d)",s:"(\\d\\d|\\d)",u:"(.+)",w:"(\\d\\d|\\d)",y:"(\\d{2})"},b={Z:function(e){return e.toISOString()},D:function(e,t,n){return t.weekdays.shorthand[b.w(e,t,n)]},F:function(e,t,n){return g(b.n(e,t,n)-1,!1,t)},G:function(e,t,n){return i(b.h(e,t,n))},H:function(e){return i(e.getHours())},J:function(e,t){return void 0!==t.ordinal?e.getDate()+t.ordinal(e.getDate()):e.getDate()},K:function(e,t){return t.amPM[a(e.getHours()>11)]},M:function(e,t){return g(e.getMonth(),!0,t)},S:function(e){return i(e.getSeconds())},U:function(e){return e.getTime()/1e3},W:function(e,t,n){return n.getWeek(e)},Y:function(e){return i(e.getFullYear(),4)},d:function(e){return i(e.getDate())},h:function(e){return e.getHours()%12?e.getHours()%12:12},i:function(e){return i(e.getMinutes())},j:function(e){return e.getDate()},l:function(e,t){return t.weekdays.longhand[e.getDay()]},m:function(e){return i(e.getMonth()+1)},n:function(e){return e.getMonth()+1},s:function(e){return e.getSeconds()},u:function(e){return e.getTime()},w:function(e){return e.getDay()},y:function(e){return String(e.getFullYear()).substring(2)}},x=function(e){var t=e.config,n=void 0===t?r:t,i=e.l10n,a=void 0===i?o:i,s=e.isMobile,l=void 0!==s&&s;return function(e,t,r){var o=r||a;return void 0===n.formatDate||l?t.split("").map((function(t,r,i){return b[t]&&"\\"!==i[r-1]?b[t](e,o,n):"\\"!==t?t:""})).join(""):n.formatDate(e,t,o)}},w=function(e){var t=e.config,n=void 0===t?r:t,i=e.l10n,a=void 0===i?o:i;return function(e,t,o,i){if(0===e||e){var s,l=i||a,c=e;if(e instanceof Date)s=new Date(e.getTime());else if("string"!=typeof e&&void 0!==e.toFixed)s=new Date(e);else if("string"==typeof e){var u=t||(n||r).dateFormat,f=String(e).trim();if("today"===f)s=new Date,o=!0;else if(/Z$/.test(f)||/GMT$/.test(f))s=new Date(e);else if(n&&n.parseDate)s=n.parseDate(e,u);else{s=n&&n.noCalendar?new Date((new Date).setHours(0,0,0,0)):new Date((new Date).getFullYear(),0,1,0,0,0,0);for(var d=void 0,p=[],h=0,m=0,g="";hMath.min(t,n)&&e=12)]),void 0!==b.secondElement&&(b.secondElement.value=i(n)))}function F(e){var t=h(e),n=parseInt(t.value)+(e.delta||0);(n/1e3>1||"Enter"===e.key&&!/[^\d]/.test(n.toString()))&&pe(n)}function z(e,t,n,r){return t instanceof Array?t.forEach((function(t){return z(e,t,n,r)})):e instanceof Array?e.forEach((function(e){return z(e,t,n,r)})):(e.addEventListener(t,n,r),void b._handlers.push({element:e,event:t,handler:n,options:r}))}function B(){Be("onChange")}function U(){if(b.config.wrap&&["open","close","toggle","clear"].forEach((function(e){Array.prototype.forEach.call(b.element.querySelectorAll("[data-"+e+"]"),(function(t){return z(t,"click",b[e])}))})),b.isMobile)Fe();else{var e=s(be,50);if(b._debouncedChange=s(B,S),b.daysContainer&&!/iPhone|iPad|iPod/i.test(navigator.userAgent)&&z(b.daysContainer,"mouseover",(function(e){"range"===b.config.mode&&ve(h(e))})),z(window.document.body,"keydown",ye),b.config.inline||b.config.static||z(window,"resize",e),void 0!==window.ontouchstart?z(window.document,"touchstart",de):z(window.document,"click",de),z(window.document,"focus",de,{capture:!0}),!0===b.config.clickOpens&&(z(b._input,"focus",b.open),z(b._input,"click",b.open)),void 0!==b.daysContainer&&(z(b.monthNav,"click",Ke),z(b.monthNav,["keyup","increment"],F),z(b.daysContainer,"click",Oe)),void 0!==b.timeContainer&&void 0!==b.minuteElement&&void 0!==b.hourElement){var t=function(e){return h(e).select()};z(b.timeContainer,["increment"],N),z(b.timeContainer,"blur",N,{capture:!0}),z(b.timeContainer,"click",H),z([b.hourElement,b.minuteElement],["focus","click"],t),void 0!==b.secondElement&&z(b.secondElement,"focus",(function(){return b.secondElement&&b.secondElement.select()})),void 0!==b.amPM&&z(b.amPM,"click",(function(e){N(e),B()}))}b.config.allowInput&&z(b._input,"blur",ge)}}function V(e,t){var n=void 0!==e?b.parseDate(e):b.latestSelectedDateObj||(b.config.minDate&&b.config.minDate>b.now?b.config.minDate:b.config.maxDate&&b.config.maxDate1),b.calendarContainer.appendChild(e);var o=void 0!==b.config.appendTo&&void 0!==b.config.appendTo.nodeType;if((b.config.inline||b.config.static)&&(b.calendarContainer.classList.add(b.config.inline?"inline":"static"),b.config.inline&&(!o&&b.element.parentNode?b.element.parentNode.insertBefore(b.calendarContainer,b._input.nextSibling):void 0!==b.config.appendTo&&b.config.appendTo.appendChild(b.calendarContainer)),b.config.static)){var i=u("div","flatpickr-wrapper");b.element.parentNode&&b.element.parentNode.insertBefore(i,b.element),i.appendChild(b.element),b.altInput&&i.appendChild(b.altInput),i.appendChild(b.calendarContainer)}b.config.static||b.config.inline||(void 0!==b.config.appendTo?b.config.appendTo:window.document.body).appendChild(b.calendarContainer)}function Y(e,t,n,r){var o=he(t,!0),i=u("span","flatpickr-day "+e,t.getDate().toString());return i.dateObj=t,i.$i=r,i.setAttribute("aria-label",b.formatDate(t,b.config.ariaDateFormat)),-1===e.indexOf("hidden")&&0===_(t,b.now)&&(b.todayDateElem=i,i.classList.add("today"),i.setAttribute("aria-current","date")),o?(i.tabIndex=-1,Ve(t)&&(i.classList.add("selected"),b.selectedDateElem=i,"range"===b.config.mode&&(c(i,"startRange",b.selectedDates[0]&&0===_(t,b.selectedDates[0],!0)),c(i,"endRange",b.selectedDates[1]&&0===_(t,b.selectedDates[1],!0)),"nextMonthDay"===e&&i.classList.add("inRange")))):i.classList.add("flatpickr-disabled"),"range"===b.config.mode&&He(t)&&!Ve(t)&&i.classList.add("inRange"),b.weekNumbers&&1===b.config.showMonths&&"prevMonthDay"!==e&&n%7==1&&b.weekNumbers.insertAdjacentHTML("beforeend",""+b.config.getWeek(t)+""),Be("onDayCreate",i),i}function K(e){e.focus(),"range"===b.config.mode&&ve(e)}function $(e){for(var t=e>0?0:b.config.showMonths-1,n=e>0?b.config.showMonths:-1,r=t;r!=n;r+=e)for(var o=b.daysContainer.children[r],i=e>0?0:o.children.length-1,a=e>0?o.children.length:-1,s=i;s!=a;s+=e){var l=o.children[s];if(-1===l.className.indexOf("hidden")&&he(l.dateObj))return l}}function G(e,t){for(var n=-1===e.className.indexOf("Month")?e.dateObj.getMonth():b.currentMonth,r=t>0?b.config.showMonths:-1,o=t>0?1:-1,i=n-b.currentMonth;i!=r;i+=o)for(var a=b.daysContainer.children[i],s=n-b.currentMonth===i?e.$i+t:t<0?a.children.length-1:0,l=a.children.length,c=s;c>=0&&c0?l:-1);c+=o){var u=a.children[c];if(-1===u.className.indexOf("hidden")&&he(u.dateObj)&&Math.abs(e.$i-c)>=Math.abs(t))return K(u)}b.changeMonth(o),X($(o),0)}function X(e,t){var n=me(document.activeElement||document.body),r=void 0!==e?e:n?document.activeElement:void 0!==b.selectedDateElem&&me(b.selectedDateElem)?b.selectedDateElem:void 0!==b.todayDateElem&&me(b.todayDateElem)?b.todayDateElem:$(t>0?1:-1);void 0===r?b._input.focus():n?G(r,t):K(r)}function Q(e,t){for(var n=(new Date(e,t,1).getDay()-b.l10n.firstDayOfWeek+7)%7,r=b.utils.getDaysInMonth((t-1+12)%12,e),o=b.utils.getDaysInMonth(t,e),i=window.document.createDocumentFragment(),a=b.config.showMonths>1,s=a?"prevMonthDay hidden":"prevMonthDay",l=a?"nextMonthDay hidden":"nextMonthDay",c=r+1-n,f=0;c<=r;c++,f++)i.appendChild(Y(s,new Date(e,t-1,c),c,f));for(c=1;c<=o;c++,f++)i.appendChild(Y("",new Date(e,t,c),c,f));for(var d=o+1;d<=42-n&&(1===b.config.showMonths||f%7!=0);d++,f++)i.appendChild(Y(l,new Date(e,t+1,d%o),d,f));var p=u("div","dayContainer");return p.appendChild(i),p}function J(){if(void 0!==b.daysContainer){f(b.daysContainer),b.weekNumbers&&f(b.weekNumbers);for(var e=document.createDocumentFragment(),t=0;t1||"dropdown"!==b.config.monthSelectorType)){var e=function(e){return!(void 0!==b.config.minDate&&b.currentYear===b.config.minDate.getFullYear()&&eb.config.maxDate.getMonth())};b.monthsDropdownContainer.tabIndex=-1,b.monthsDropdownContainer.innerHTML="";for(var t=0;t<12;t++)if(e(t)){var n=u("option","flatpickr-monthDropdown-month");n.value=new Date(b.currentYear,t).getMonth().toString(),n.textContent=g(t,b.config.shorthandCurrentMonth,b.l10n),n.tabIndex=-1,b.currentMonth===t&&(n.selected=!0),b.monthsDropdownContainer.appendChild(n)}}}function ee(){var e,t=u("div","flatpickr-month"),n=window.document.createDocumentFragment();b.config.showMonths>1||"static"===b.config.monthSelectorType?e=u("span","cur-month"):(b.monthsDropdownContainer=u("select","flatpickr-monthDropdown-months"),b.monthsDropdownContainer.setAttribute("aria-label",b.l10n.monthAriaLabel),z(b.monthsDropdownContainer,"change",(function(e){var t=h(e),n=parseInt(t.value,10);b.changeMonth(n-b.currentMonth),Be("onMonthChange")})),Z(),e=b.monthsDropdownContainer);var r=p("cur-year",{tabindex:"-1"}),o=r.getElementsByTagName("input")[0];o.setAttribute("aria-label",b.l10n.yearAriaLabel),b.config.minDate&&o.setAttribute("min",b.config.minDate.getFullYear().toString()),b.config.maxDate&&(o.setAttribute("max",b.config.maxDate.getFullYear().toString()),o.disabled=!!b.config.minDate&&b.config.minDate.getFullYear()===b.config.maxDate.getFullYear());var i=u("div","flatpickr-current-month");return i.appendChild(e),i.appendChild(r),n.appendChild(i),t.appendChild(n),{container:t,yearElement:o,monthElement:e}}function te(){f(b.monthNav),b.monthNav.appendChild(b.prevMonthNav),b.config.showMonths&&(b.yearElements=[],b.monthElements=[]);for(var e=b.config.showMonths;e--;){var t=ee();b.yearElements.push(t.yearElement),b.monthElements.push(t.monthElement),b.monthNav.appendChild(t.container)}b.monthNav.appendChild(b.nextMonthNav)}function ne(){return b.monthNav=u("div","flatpickr-months"),b.yearElements=[],b.monthElements=[],b.prevMonthNav=u("span","flatpickr-prev-month"),b.prevMonthNav.innerHTML=b.config.prevArrow,b.nextMonthNav=u("span","flatpickr-next-month"),b.nextMonthNav.innerHTML=b.config.nextArrow,te(),Object.defineProperty(b,"_hidePrevMonthArrow",{get:function(){return b.__hidePrevMonthArrow},set:function(e){b.__hidePrevMonthArrow!==e&&(c(b.prevMonthNav,"flatpickr-disabled",e),b.__hidePrevMonthArrow=e)}}),Object.defineProperty(b,"_hideNextMonthArrow",{get:function(){return b.__hideNextMonthArrow},set:function(e){b.__hideNextMonthArrow!==e&&(c(b.nextMonthNav,"flatpickr-disabled",e),b.__hideNextMonthArrow=e)}}),b.currentYearElement=b.yearElements[0],qe(),b.monthNav}function re(){b.calendarContainer.classList.add("hasTime"),b.config.noCalendar&&b.calendarContainer.classList.add("noCalendar"),b.timeContainer=u("div","flatpickr-time"),b.timeContainer.tabIndex=-1;var e=u("span","flatpickr-time-separator",":"),t=p("flatpickr-hour",{"aria-label":b.l10n.hourAriaLabel});b.hourElement=t.getElementsByTagName("input")[0];var n=p("flatpickr-minute",{"aria-label":b.l10n.minuteAriaLabel});if(b.minuteElement=n.getElementsByTagName("input")[0],b.hourElement.tabIndex=b.minuteElement.tabIndex=-1,b.hourElement.value=i(b.latestSelectedDateObj?b.latestSelectedDateObj.getHours():b.config.time_24hr?b.config.defaultHour:R(b.config.defaultHour)),b.minuteElement.value=i(b.latestSelectedDateObj?b.latestSelectedDateObj.getMinutes():b.config.defaultMinute),b.hourElement.setAttribute("step",b.config.hourIncrement.toString()),b.minuteElement.setAttribute("step",b.config.minuteIncrement.toString()),b.hourElement.setAttribute("min",b.config.time_24hr?"0":"1"),b.hourElement.setAttribute("max",b.config.time_24hr?"23":"12"),b.minuteElement.setAttribute("min","0"),b.minuteElement.setAttribute("max","59"),b.timeContainer.appendChild(t),b.timeContainer.appendChild(e),b.timeContainer.appendChild(n),b.config.time_24hr&&b.timeContainer.classList.add("time24hr"),b.config.enableSeconds){b.timeContainer.classList.add("hasSeconds");var r=p("flatpickr-second");b.secondElement=r.getElementsByTagName("input")[0],b.secondElement.value=i(b.latestSelectedDateObj?b.latestSelectedDateObj.getSeconds():b.config.defaultSeconds),b.secondElement.setAttribute("step",b.minuteElement.getAttribute("step")),b.secondElement.setAttribute("min","0"),b.secondElement.setAttribute("max","59"),b.timeContainer.appendChild(u("span","flatpickr-time-separator",":")),b.timeContainer.appendChild(r)}return b.config.time_24hr||(b.amPM=u("span","flatpickr-am-pm",b.l10n.amPM[a((b.latestSelectedDateObj?b.hourElement.value:b.config.defaultHour)>11)]),b.amPM.title=b.l10n.toggleTitle,b.amPM.tabIndex=-1,b.timeContainer.appendChild(b.amPM)),b.timeContainer}function oe(){b.weekdayContainer?f(b.weekdayContainer):b.weekdayContainer=u("div","flatpickr-weekdays");for(var e=b.config.showMonths;e--;){var t=u("div","flatpickr-weekdaycontainer");b.weekdayContainer.appendChild(t)}return ie(),b.weekdayContainer}function ie(){if(b.weekdayContainer){var e=b.l10n.firstDayOfWeek,n=t(b.l10n.weekdays.shorthand);e>0&&e\n "+n.join("")+"\n \n "}}function ae(){b.calendarContainer.classList.add("hasWeeks");var e=u("div","flatpickr-weekwrapper");e.appendChild(u("span","flatpickr-weekday",b.l10n.weekAbbreviation));var t=u("div","flatpickr-weeks");return e.appendChild(t),{weekWrapper:e,weekNumbers:t}}function se(e,t){void 0===t&&(t=!0);var n=t?e:e-b.currentMonth;n<0&&!0===b._hidePrevMonthArrow||n>0&&!0===b._hideNextMonthArrow||(b.currentMonth+=n,(b.currentMonth<0||b.currentMonth>11)&&(b.currentYear+=b.currentMonth>11?1:-1,b.currentMonth=(b.currentMonth+12)%12,Be("onYearChange"),Z()),J(),Be("onMonthChange"),qe())}function le(e,t){if(void 0===e&&(e=!0),void 0===t&&(t=!0),b.input.value="",void 0!==b.altInput&&(b.altInput.value=""),void 0!==b.mobileInput&&(b.mobileInput.value=""),b.selectedDates=[],b.latestSelectedDateObj=void 0,!0===t&&(b.currentYear=b._initialDate.getFullYear(),b.currentMonth=b._initialDate.getMonth()),!0===b.config.enableTime){var n=L();j(n.hours,n.minutes,n.seconds)}b.redraw(),e&&Be("onChange")}function ce(){b.isOpen=!1,b.isMobile||(void 0!==b.calendarContainer&&b.calendarContainer.classList.remove("open"),void 0!==b._input&&b._input.classList.remove("active")),Be("onClose")}function ue(){void 0!==b.config&&Be("onDestroy");for(var e=b._handlers.length;e--;){var t=b._handlers[e];t.element.removeEventListener(t.event,t.handler,t.options)}if(b._handlers=[],b.mobileInput)b.mobileInput.parentNode&&b.mobileInput.parentNode.removeChild(b.mobileInput),b.mobileInput=void 0;else if(b.calendarContainer&&b.calendarContainer.parentNode)if(b.config.static&&b.calendarContainer.parentNode){var n=b.calendarContainer.parentNode;if(n.lastChild&&n.removeChild(n.lastChild),n.parentNode){for(;n.firstChild;)n.parentNode.insertBefore(n.firstChild,n);n.parentNode.removeChild(n)}}else b.calendarContainer.parentNode.removeChild(b.calendarContainer);b.altInput&&(b.input.type="text",b.altInput.parentNode&&b.altInput.parentNode.removeChild(b.altInput),delete b.altInput),b.input&&(b.input.type=b.input._type,b.input.classList.remove("flatpickr-input"),b.input.removeAttribute("readonly")),["_showTimeInput","latestSelectedDateObj","_hideNextMonthArrow","_hidePrevMonthArrow","__hideNextMonthArrow","__hidePrevMonthArrow","isMobile","isOpen","selectedDateElem","minDateHasTime","maxDateHasTime","days","daysContainer","_input","_positionElement","innerContainer","rContainer","monthNav","todayDateElem","calendarContainer","weekdayContainer","prevMonthNav","nextMonthNav","monthsDropdownContainer","currentMonthElement","currentYearElement","navigationCurrentMonth","selectedDateElem","config"].forEach((function(e){try{delete b[e]}catch(e){}}))}function fe(e){return!(!b.config.appendTo||!b.config.appendTo.contains(e))||b.calendarContainer.contains(e)}function de(e){if(b.isOpen&&!b.config.inline){var t=h(e),n=fe(t),r=t===b.input||t===b.altInput||b.element.contains(t)||e.path&&e.path.indexOf&&(~e.path.indexOf(b.input)||~e.path.indexOf(b.altInput)),o="blur"===e.type?r&&e.relatedTarget&&!fe(e.relatedTarget):!r&&!n&&!fe(e.relatedTarget),i=!b.config.ignoredFocusElements.some((function(e){return e.contains(t)}));o&&i&&(void 0!==b.timeContainer&&void 0!==b.minuteElement&&void 0!==b.hourElement&&""!==b.input.value&&void 0!==b.input.value&&N(),b.close(),b.config&&"range"===b.config.mode&&1===b.selectedDates.length&&(b.clear(!1),b.redraw()))}}function pe(e){if(!(!e||b.config.minDate&&eb.config.maxDate.getFullYear())){var t=e,n=b.currentYear!==t;b.currentYear=t||b.currentYear,b.config.maxDate&&b.currentYear===b.config.maxDate.getFullYear()?b.currentMonth=Math.min(b.config.maxDate.getMonth(),b.currentMonth):b.config.minDate&&b.currentYear===b.config.minDate.getFullYear()&&(b.currentMonth=Math.max(b.config.minDate.getMonth(),b.currentMonth)),n&&(b.redraw(),Be("onYearChange"),Z())}}function he(e,t){void 0===t&&(t=!0);var n=b.parseDate(e,void 0,t);if(b.config.minDate&&n&&_(n,b.config.minDate,void 0!==t?t:!b.minDateHasTime)<0||b.config.maxDate&&n&&_(n,b.config.maxDate,void 0!==t?t:!b.maxDateHasTime)>0)return!1;if(0===b.config.enable.length&&0===b.config.disable.length)return!0;if(void 0===n)return!1;for(var r=b.config.enable.length>0,o=r?b.config.enable:b.config.disable,i=0,a=void 0;i=a.from.getTime()&&n.getTime()<=a.to.getTime())return r}return!r}function me(e){return void 0!==b.daysContainer&&-1===e.className.indexOf("hidden")&&-1===e.className.indexOf("flatpickr-disabled")&&b.daysContainer.contains(e)}function ge(e){e.target!==b._input||e.relatedTarget&&fe(e.relatedTarget)||b.setDate(b._input.value,!0,e.target===b.altInput?b.config.altFormat:b.config.dateFormat)}function ye(e){var t=h(e),n=b.config.wrap?m.contains(t):t===b._input,r=b.config.allowInput,o=b.isOpen&&(!r||!n),i=b.config.inline&&n&&!r;if(13===e.keyCode&&n){if(r)return b.setDate(b._input.value,!0,t===b.altInput?b.config.altFormat:b.config.dateFormat),t.blur();b.open()}else if(fe(t)||o||i){var a=!!b.timeContainer&&b.timeContainer.contains(t);switch(e.keyCode){case 13:a?(e.preventDefault(),N(),Pe()):Oe(e);break;case 27:e.preventDefault(),Pe();break;case 8:case 46:n&&!b.config.allowInput&&(e.preventDefault(),b.clear());break;case 37:case 39:if(a||n)b.hourElement&&b.hourElement.focus();else if(e.preventDefault(),void 0!==b.daysContainer&&(!1===r||document.activeElement&&me(document.activeElement))){var s=39===e.keyCode?1:-1;e.ctrlKey?(e.stopPropagation(),se(s),X($(1),0)):X(void 0,s)}break;case 38:case 40:e.preventDefault();var l=40===e.keyCode?1:-1;b.daysContainer&&void 0!==t.$i||t===b.input||t===b.altInput?e.ctrlKey?(e.stopPropagation(),pe(b.currentYear-l),X($(1),0)):a||X(void 0,7*l):t===b.currentYearElement?pe(b.currentYear-l):b.config.enableTime&&(!a&&b.hourElement&&b.hourElement.focus(),N(e),b._debouncedChange());break;case 9:if(a){var c=[b.hourElement,b.minuteElement,b.secondElement,b.amPM].concat(b.pluginElements).filter((function(e){return e})),u=c.indexOf(t);if(-1!==u){var f=c[u+(e.shiftKey?-1:1)];e.preventDefault(),(f||b._input).focus()}}else!b.config.noCalendar&&b.daysContainer&&b.daysContainer.contains(t)&&e.shiftKey&&(e.preventDefault(),b._input.focus())}}if(void 0!==b.amPM&&t===b.amPM)switch(e.key){case b.l10n.amPM[0].charAt(0):case b.l10n.amPM[0].charAt(0).toLowerCase():b.amPM.textContent=b.l10n.amPM[0],A(),Ye();break;case b.l10n.amPM[1].charAt(0):case b.l10n.amPM[1].charAt(0).toLowerCase():b.amPM.textContent=b.l10n.amPM[1],A(),Ye()}(n||fe(t))&&Be("onKeyDown",e)}function ve(e){if(1===b.selectedDates.length&&(!e||e.classList.contains("flatpickr-day")&&!e.classList.contains("flatpickr-disabled"))){for(var t=e?e.dateObj.getTime():b.days.firstElementChild.dateObj.getTime(),n=b.parseDate(b.selectedDates[0],void 0,!0).getTime(),r=Math.min(t,b.selectedDates[0].getTime()),o=Math.max(t,b.selectedDates[0].getTime()),i=!1,a=0,s=0,l=r;lr&&la)?a=l:l>n&&(!s||l0&&c0&&c>s;return f?(l.classList.add("notAllowed"),["inRange","startRange","endRange"].forEach((function(e){l.classList.remove(e)})),"continue"):i&&!f?"continue":(["startRange","inRange","endRange","notAllowed"].forEach((function(e){l.classList.remove(e)})),void(void 0!==e&&(e.classList.add(t<=b.selectedDates[0].getTime()?"startRange":"endRange"),nt&&c===n&&l.classList.add("endRange"),c>=a&&(0===s||c<=s)&&k(c,n,t)&&l.classList.add("inRange"))))},d=0,p=u.children.length;d0||n.getMinutes()>0||n.getSeconds()>0),b.selectedDates&&(b.selectedDates=b.selectedDates.filter((function(e){return he(e)})),b.selectedDates.length||"min"!==e||I(n),Ye()),b.daysContainer&&(Te(),void 0!==n?b.currentYearElement[e]=n.getFullYear().toString():b.currentYearElement.removeAttribute(e),b.currentYearElement.disabled=!!r&&void 0!==n&&r.getFullYear()===n.getFullYear())}}function _e(){var t=["wrap","weekNumbers","allowInput","allowInvalidPreload","clickOpens","time_24hr","enableTime","noCalendar","altInput","shorthandCurrentMonth","inline","static","enableSeconds","disableMobile"],o=e(e({},JSON.parse(JSON.stringify(m.dataset||{}))),y),i={};b.config.parseDate=o.parseDate,b.config.formatDate=o.formatDate,Object.defineProperty(b.config,"enable",{get:function(){return b.config._enable},set:function(e){b.config._enable=Ie(e)}}),Object.defineProperty(b.config,"disable",{get:function(){return b.config._disable},set:function(e){b.config._disable=Ie(e)}});var a="time"===o.mode;if(!o.dateFormat&&(o.enableTime||a)){var s=T.defaultConfig.dateFormat||r.dateFormat;i.dateFormat=o.noCalendar||a?"H:i"+(o.enableSeconds?":S":""):s+" H:i"+(o.enableSeconds?":S":"")}if(o.altInput&&(o.enableTime||a)&&!o.altFormat){var c=T.defaultConfig.altFormat||r.altFormat;i.altFormat=o.noCalendar||a?"h:i"+(o.enableSeconds?":S K":" K"):c+" h:i"+(o.enableSeconds?":S":"")+" K"}Object.defineProperty(b.config,"minDate",{get:function(){return b.config._minDate},set:we("min")}),Object.defineProperty(b.config,"maxDate",{get:function(){return b.config._maxDate},set:we("max")});var u=function(e){return function(t){b.config["min"===e?"_minTime":"_maxTime"]=b.parseDate(t,"H:i:S")}};Object.defineProperty(b.config,"minTime",{get:function(){return b.config._minTime},set:u("min")}),Object.defineProperty(b.config,"maxTime",{get:function(){return b.config._maxTime},set:u("max")}),"time"===o.mode&&(b.config.noCalendar=!0,b.config.enableTime=!0),Object.assign(b.config,i,o);for(var f=0;f-1?b.config[p]=l(d[p]).map(P).concat(b.config[p]):void 0===o[p]&&(b.config[p]=d[p])}o.altInputClass||(b.config.altInputClass=ke().className+" "+b.config.altInputClass),Be("onParseConfig")}function ke(){return b.config.wrap?m.querySelector("[data-input]"):m}function Ee(){"object"!=typeof b.config.locale&&void 0===T.l10ns[b.config.locale]&&b.config.errorHandler(new Error("flatpickr: invalid locale "+b.config.locale)),b.l10n=e(e({},T.l10ns.default),"object"==typeof b.config.locale?b.config.locale:"default"!==b.config.locale?T.l10ns[b.config.locale]:void 0),v.K="("+b.l10n.amPM[0]+"|"+b.l10n.amPM[1]+"|"+b.l10n.amPM[0].toLowerCase()+"|"+b.l10n.amPM[1].toLowerCase()+")",void 0===e(e({},y),JSON.parse(JSON.stringify(m.dataset||{}))).time_24hr&&void 0===T.defaultConfig.time_24hr&&(b.config.time_24hr=b.l10n.time_24hr),b.formatDate=x(b),b.parseDate=w({config:b.config,l10n:b.l10n})}function Se(e){if(void 0!==b.calendarContainer){Be("onPreCalendarPosition");var t=e||b._positionElement,n=Array.prototype.reduce.call(b.calendarContainer.children,(function(e,t){return e+t.offsetHeight}),0),r=b.calendarContainer.offsetWidth,o=b.config.position.split(" "),i=o[0],a=o.length>1?o[1]:null,s=t.getBoundingClientRect(),l=window.innerHeight-s.bottom,u="above"===i||"below"!==i&&ln,f=window.pageYOffset+s.top+(u?-n-2:t.offsetHeight+2);if(c(b.calendarContainer,"arrowTop",!u),c(b.calendarContainer,"arrowBottom",u),!b.config.inline){var d=window.pageXOffset+s.left,p=!1,h=!1;"center"===a?(d-=(r-s.width)/2,p=!0):"right"===a&&(d-=r-s.width,h=!0),c(b.calendarContainer,"arrowLeft",!p&&!h),c(b.calendarContainer,"arrowCenter",p),c(b.calendarContainer,"arrowRight",h);var m=window.document.body.offsetWidth-(window.pageXOffset+s.right),g=d+r>window.document.body.offsetWidth,y=m+r>window.document.body.offsetWidth;if(c(b.calendarContainer,"rightMost",g),!b.config.static)if(b.calendarContainer.style.top=f+"px",g)if(y){var v=Me();if(void 0===v)return;var x=window.document.body.offsetWidth,w=Math.max(0,x/2-r/2),_=".flatpickr-calendar.centerMost:before",k=".flatpickr-calendar.centerMost:after",E=v.cssRules.length,S="{left:"+s.left+"px;right:auto;}";c(b.calendarContainer,"rightMost",!1),c(b.calendarContainer,"centerMost",!0),v.insertRule(_+","+k+S,E),b.calendarContainer.style.left=w+"px",b.calendarContainer.style.right="auto"}else b.calendarContainer.style.left="auto",b.calendarContainer.style.right=m+"px";else b.calendarContainer.style.left=d+"px",b.calendarContainer.style.right="auto"}}}function Me(){for(var e=null,t=0;tb.currentMonth+b.config.showMonths-1)&&"range"!==b.config.mode;if(b.selectedDateElem=r,"single"===b.config.mode)b.selectedDates=[o];else if("multiple"===b.config.mode){var a=Ve(o);a?b.selectedDates.splice(parseInt(a),1):b.selectedDates.push(o)}else"range"===b.config.mode&&(2===b.selectedDates.length&&b.clear(!1,!1),b.latestSelectedDateObj=o,b.selectedDates.push(o),0!==_(o,b.selectedDates[0],!0)&&b.selectedDates.sort((function(e,t){return e.getTime()-t.getTime()})));if(A(),i){var s=b.currentYear!==o.getFullYear();b.currentYear=o.getFullYear(),b.currentMonth=o.getMonth(),s&&(Be("onYearChange"),Z()),Be("onMonthChange")}if(qe(),J(),Ye(),i||"range"===b.config.mode||1!==b.config.showMonths?void 0!==b.selectedDateElem&&void 0===b.hourElement&&b.selectedDateElem&&b.selectedDateElem.focus():K(r),void 0!==b.hourElement&&void 0!==b.hourElement&&b.hourElement.focus(),b.config.closeOnSelect){var l="single"===b.config.mode&&!b.config.enableTime,c="range"===b.config.mode&&2===b.selectedDates.length&&!b.config.enableTime;(l||c)&&Pe()}B()}}b.parseDate=w({config:b.config,l10n:b.l10n}),b._handlers=[],b.pluginElements=[],b.loadedPlugins=[],b._bind=z,b._setHoursFromDate=I,b._positionCalendar=Se,b.changeMonth=se,b.changeYear=pe,b.clear=le,b.close=ce,b._createElement=u,b.destroy=ue,b.isEnabled=he,b.jumpToDate=V,b.open=xe,b.redraw=Te,b.set=De,b.setDate=Ae,b.toggle=ze;var Ne={locale:[Ee,ie],showMonths:[te,O,oe],minDate:[V],maxDate:[V]};function De(e,t){if(null!==e&&"object"==typeof e)for(var r in Object.assign(b.config,e),e)void 0!==Ne[r]&&Ne[r].forEach((function(e){return e()}));else b.config[e]=t,void 0!==Ne[e]?Ne[e].forEach((function(e){return e()})):n.indexOf(e)>-1&&(b.config[e]=l(t));b.redraw(),Ye(!0)}function Re(e,t){var n=[];if(e instanceof Array)n=e.map((function(e){return b.parseDate(e,t)}));else if(e instanceof Date||"number"==typeof e)n=[b.parseDate(e,t)];else if("string"==typeof e)switch(b.config.mode){case"single":case"time":n=[b.parseDate(e,t)];break;case"multiple":n=e.split(b.config.conjunction).map((function(e){return b.parseDate(e,t)}));break;case"range":n=e.split(b.l10n.rangeSeparator).map((function(e){return b.parseDate(e,t)}))}else b.config.errorHandler(new Error("Invalid date supplied: "+JSON.stringify(e)));b.selectedDates=b.config.allowInvalidPreload?n:n.filter((function(e){return e instanceof Date&&he(e,!1)})),"range"===b.config.mode&&b.selectedDates.sort((function(e,t){return e.getTime()-t.getTime()}))}function Ae(e,t,n){if(void 0===t&&(t=!1),void 0===n&&(n=b.config.dateFormat),0!==e&&!e||e instanceof Array&&0===e.length)return b.clear(t);Re(e,n),b.latestSelectedDateObj=b.selectedDates[b.selectedDates.length-1],b.redraw(),V(void 0,t),I(),0===b.selectedDates.length&&b.clear(!1),Ye(t),t&&Be("onChange")}function Ie(e){return e.slice().map((function(e){return"string"==typeof e||"number"==typeof e||e instanceof Date?b.parseDate(e,void 0,!0):e&&"object"==typeof e&&e.from&&e.to?{from:b.parseDate(e.from,void 0),to:b.parseDate(e.to,void 0)}:e})).filter((function(e){return e}))}function Le(){b.selectedDates=[],b.now=b.parseDate(b.config.now)||new Date;var e=b.config.defaultDate||("INPUT"!==b.input.nodeName&&"TEXTAREA"!==b.input.nodeName||!b.input.placeholder||b.input.value!==b.input.placeholder?b.input.value:null);e&&Re(e,b.config.dateFormat),b._initialDate=b.selectedDates.length>0?b.selectedDates[0]:b.config.minDate&&b.config.minDate.getTime()>b.now.getTime()?b.config.minDate:b.config.maxDate&&b.config.maxDate.getTime()0&&(b.latestSelectedDateObj=b.selectedDates[0]),void 0!==b.config.minTime&&(b.config.minTime=b.parseDate(b.config.minTime,"H:i")),void 0!==b.config.maxTime&&(b.config.maxTime=b.parseDate(b.config.maxTime,"H:i")),b.minDateHasTime=!!b.config.minDate&&(b.config.minDate.getHours()>0||b.config.minDate.getMinutes()>0||b.config.minDate.getSeconds()>0),b.maxDateHasTime=!!b.config.maxDate&&(b.config.maxDate.getHours()>0||b.config.maxDate.getMinutes()>0||b.config.maxDate.getSeconds()>0)}function je(){b.input=ke(),b.input?(b.input._type=b.input.type,b.input.type="text",b.input.classList.add("flatpickr-input"),b._input=b.input,b.config.altInput&&(b.altInput=u(b.input.nodeName,b.config.altInputClass),b._input=b.altInput,b.altInput.placeholder=b.input.placeholder,b.altInput.disabled=b.input.disabled,b.altInput.required=b.input.required,b.altInput.tabIndex=b.input.tabIndex,b.altInput.type="text",b.input.setAttribute("type","hidden"),!b.config.static&&b.input.parentNode&&b.input.parentNode.insertBefore(b.altInput,b.input.nextSibling)),b.config.allowInput||b._input.setAttribute("readonly","readonly"),b._positionElement=b.config.positionElement||b._input):b.config.errorHandler(new Error("Invalid input element specified"))}function Fe(){var e=b.config.enableTime?b.config.noCalendar?"time":"datetime-local":"date";b.mobileInput=u("input",b.input.className+" flatpickr-mobile"),b.mobileInput.tabIndex=1,b.mobileInput.type=e,b.mobileInput.disabled=b.input.disabled,b.mobileInput.required=b.input.required,b.mobileInput.placeholder=b.input.placeholder,b.mobileFormatStr="datetime-local"===e?"Y-m-d\\TH:i:S":"date"===e?"Y-m-d":"H:i:S",b.selectedDates.length>0&&(b.mobileInput.defaultValue=b.mobileInput.value=b.formatDate(b.selectedDates[0],b.mobileFormatStr)),b.config.minDate&&(b.mobileInput.min=b.formatDate(b.config.minDate,"Y-m-d")),b.config.maxDate&&(b.mobileInput.max=b.formatDate(b.config.maxDate,"Y-m-d")),b.input.getAttribute("step")&&(b.mobileInput.step=String(b.input.getAttribute("step"))),b.input.type="hidden",void 0!==b.altInput&&(b.altInput.type="hidden");try{b.input.parentNode&&b.input.parentNode.insertBefore(b.mobileInput,b.input.nextSibling)}catch(e){}z(b.mobileInput,"change",(function(e){b.setDate(h(e).value,!1,b.mobileFormatStr),Be("onChange"),Be("onClose")}))}function ze(e){if(!0===b.isOpen)return b.close();b.open(e)}function Be(e,t){if(void 0!==b.config){var n=b.config[e];if(void 0!==n&&n.length>0)for(var r=0;n[r]&&r=0&&_(e,b.selectedDates[1])<=0}function qe(){b.config.noCalendar||b.isMobile||!b.monthNav||(b.yearElements.forEach((function(e,t){var n=new Date(b.currentYear,b.currentMonth,1);n.setMonth(b.currentMonth+t),b.config.showMonths>1||"static"===b.config.monthSelectorType?b.monthElements[t].textContent=g(n.getMonth(),b.config.shorthandCurrentMonth,b.l10n)+" ":b.monthsDropdownContainer.value=n.getMonth().toString(),e.value=n.getFullYear().toString()})),b._hidePrevMonthArrow=void 0!==b.config.minDate&&(b.currentYear===b.config.minDate.getFullYear()?b.currentMonth<=b.config.minDate.getMonth():b.currentYearb.config.maxDate.getMonth():b.currentYear>b.config.maxDate.getFullYear()))}function We(e){return b.selectedDates.map((function(t){return b.formatDate(t,e)})).filter((function(e,t,n){return"range"!==b.config.mode||b.config.enableTime||n.indexOf(e)===t})).join("range"!==b.config.mode?b.config.conjunction:b.l10n.rangeSeparator)}function Ye(e){void 0===e&&(e=!0),void 0!==b.mobileInput&&b.mobileFormatStr&&(b.mobileInput.value=void 0!==b.latestSelectedDateObj?b.formatDate(b.latestSelectedDateObj,b.mobileFormatStr):""),b.input.value=We(b.config.dateFormat),void 0!==b.altInput&&(b.altInput.value=We(b.config.altFormat)),!1!==e&&Be("onValueUpdate")}function Ke(e){var t=h(e),n=b.prevMonthNav.contains(t),r=b.nextMonthNav.contains(t);n||r?se(n?-1:1):b.yearElements.indexOf(t)>=0?t.select():t.classList.contains("arrowUp")?b.changeYear(b.currentYear+1):t.classList.contains("arrowDown")&&b.changeYear(b.currentYear-1)}function $e(e){e.preventDefault();var t="keydown"===e.type,n=h(e),r=n;void 0!==b.amPM&&n===b.amPM&&(b.amPM.textContent=b.l10n.amPM[a(b.amPM.textContent===b.l10n.amPM[0])]);var o=parseFloat(r.getAttribute("min")),s=parseFloat(r.getAttribute("max")),l=parseFloat(r.getAttribute("step")),c=parseInt(r.value,10),u=c+l*(e.delta||(t?38===e.which?1:-1:0));if(void 0!==r.value&&2===r.value.length){var f=r===b.hourElement,d=r===b.minuteElement;us&&(u=r===b.hourElement?u-s-a(!b.amPM):o,d&&q(void 0,1,b.hourElement)),b.amPM&&f&&(1===l?u+c===23:Math.abs(u-c)>l)&&(b.amPM.textContent=b.l10n.amPM[a(b.amPM.textContent===b.l10n.amPM[0])]),r.value=i(u)}}return C(),b}function C(e,t){for(var n=Array.prototype.slice.call(e).filter((function(e){return e instanceof HTMLElement})),r=[],o=0;o{"use strict";var r=n(864),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},i={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},a={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},s={};function l(e){return r.isMemo(e)?a:s[e.$$typeof]||o}s[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},s[r.Memo]=a;var c=Object.defineProperty,u=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(h){var o=p(n);o&&o!==h&&e(t,o,r)}var a=u(n);f&&(a=a.concat(f(n)));for(var s=l(t),m=l(n),g=0;g{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function o(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,i){for(var a,s,l=o(e),c=1;c{var r=n(536);e.exports=p,e.exports.parse=i,e.exports.compile=function(e,t){return s(i(e,t),t)},e.exports.tokensToFunction=s,e.exports.tokensToRegExp=d;var o=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function i(e,t){for(var n,r=[],i=0,a=0,s="",u=t&&t.delimiter||"/";null!=(n=o.exec(e));){var f=n[0],d=n[1],p=n.index;if(s+=e.slice(a,p),a=p+f.length,d)s+=d[1];else{var h=e[a],m=n[2],g=n[3],y=n[4],v=n[5],b=n[6],x=n[7];s&&(r.push(s),s="");var w=null!=m&&null!=h&&h!==m,_="+"===b||"*"===b,k="?"===b||"*"===b,E=n[2]||u,S=y||v;r.push({name:g||i++,prefix:m||"",delimiter:E,optional:k,repeat:_,partial:w,asterisk:!!x,pattern:S?c(S):x?".*":"[^"+l(E)+"]+?"})}}return a{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},703:(e,t,n)=>{"use strict";var r=n(414);function o(){}function i(){}i.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,i,a){if(a!==r){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:i,resetWarningCache:o};return n.PropTypes=n,n}},697:(e,t,n)=>{e.exports=n(703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},448:(e,t,n)=>{"use strict";var r=n(294),o=n(418),i=n(840);function a(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n