From 67c3319306ebc336fd33101efe29041f32c8f457 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Wed, 13 Dec 2023 12:01:46 +0200 Subject: [PATCH 1/3] connect statistics to AMS --- CHANGELOG.md | 233 +---------- CODEOWNERS | 15 - README.md | 67 --- composer.json | 21 - config-templates/mysql_tables.sql | 52 --- config-templates/pgsql_tables.sql | 56 --- default-disable | 0 dictionaries/Proxystatistics.definition.json | 186 --------- hooks/hook_cron.php | 24 -- lib/Auth/Process/DatabaseConnector.php | 199 --------- phpcs.xml | 207 ---------- scripts/migrate_2.0.php | 66 --- src/Auth/Process/AmsCommand.php | 145 +++++++ src/Auth/Process/AmsConnector.php | 115 ++++++ {lib => src}/Auth/Process/DatabaseCommand.php | 0 {lib => src}/Auth/Process/Statistics.php | 12 +- {lib => src}/Utils.php | 2 +- www/Chart.min.css | 1 - www/Chart.min.js | 7 - www/bootstrap.config.json | 390 ------------------ www/bootstrap.min.css | 7 - www/chartjs-plugin-zoom.min.js | 11 - www/hammer.min.js | 7 - www/identityProviders.php | 22 - www/idpDetail.php | 36 -- www/index.js | 382 ----------------- www/index.php | 65 --- www/moment.cs.min.js | 2 - www/moment.min.js | 1 - www/serviceProviders.php | 22 - www/spDetail.php | 36 -- www/statisticsproxy.css | 123 ------ www/summary.php | 62 --- 33 files changed, 269 insertions(+), 2305 deletions(-) delete mode 100644 CODEOWNERS delete mode 100644 README.md delete mode 100644 composer.json delete mode 100644 config-templates/mysql_tables.sql delete mode 100644 config-templates/pgsql_tables.sql delete mode 100644 default-disable delete mode 100644 dictionaries/Proxystatistics.definition.json delete mode 100644 hooks/hook_cron.php delete mode 100644 lib/Auth/Process/DatabaseConnector.php delete mode 100644 phpcs.xml delete mode 100644 scripts/migrate_2.0.php create mode 100644 src/Auth/Process/AmsCommand.php create mode 100644 src/Auth/Process/AmsConnector.php rename {lib => src}/Auth/Process/DatabaseCommand.php (100%) rename {lib => src}/Auth/Process/Statistics.php (92%) rename {lib => src}/Utils.php (96%) delete mode 100644 www/Chart.min.css delete mode 100644 www/Chart.min.js delete mode 100644 www/bootstrap.config.json delete mode 100644 www/bootstrap.min.css delete mode 100644 www/chartjs-plugin-zoom.min.js delete mode 100644 www/hammer.min.js delete mode 100644 www/identityProviders.php delete mode 100644 www/idpDetail.php delete mode 100644 www/index.js delete mode 100644 www/index.php delete mode 100644 www/moment.cs.min.js delete mode 100644 www/moment.min.js delete mode 100644 www/serviceProviders.php delete mode 100644 www/spDetail.php delete mode 100644 www/statisticsproxy.css delete mode 100644 www/summary.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 2881236..cdc32e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,236 +2,5 @@ All notable changes to this project will be documented in this file. -## [v3.1.2-rciam] - 2023-01-16 +## [Unreleased] - 2023-xx-xx -### Fixed - -- Fix recording of user's IP address - -## [v3.1.1-rciam] - 2022-12-19 - -### Fixed - -- Fix wrong condition when `RequesterID` is present in the SAML request - -## [v3.1.0-rciam] - 2022-04-20 - -### Added - -- Support for Keycloak via `keycloakSp` config option - -## [v3.0.10-rciam] - 2022-02-22 - -### Changed - -- Remove unique constraint on IP statistics table to allow concurrent logins - -## [v3.0.9-rciam] - 2022-02-01 - -### Fixed - -- Fix login statistics for newly registered users accessing OIDC clients - -## [v3.0.8-rciam] - 2021-11-22 - -### Fixed - -- Fix default value of `userIdAttribute` -- Fix parameter in `insertLogin()` call - -## [v3.0.7-rciam] - 2021-11-12 - -### Added - -- Read `userIdAttribute` from state - -## [v3.0.6-rciam] - 2021-04-27 - -#### Fixed -- Statistics class name - -## [v3.0.5-rciam] - 2021-03-08 - -#### Fixed -- PostgreSQL syntax error in `addDaysRange()` - -## [v3.0.4-rciam] - 2021-03-04 - -#### Fixed -- Add DateTimeZone library - -## [v3.0.3-rciam] - 2021-03-01 - -#### Changed -- Record metrics in UTC timezone - -## [v3.0.2-rciam] - 2021-02-20 - -#### Fixed -- Add missing library to statistics class - -## [v3.0.1-rciam] - 2021-02-11 - -#### Added -- Support for collecting IP statistics -- Support for excluding user IDs from statistics - -#### Changed -- Improve PostgreSQL table init scripts - -## [v3.0.0-rciam] - 2020-06-03 -This version is based on upstream v3.2.1 and is compatible with [SimpleSAMLphp v1.17](https://simplesamlphp.org/docs/1.17/simplesamlphp-changelog) -#### Added -- Add support for PostgreSQL queries -- Replace entityID with the RequesterID for the OIDC Clients -- Get IdP's friendly name from metadata -- Get SP's friendly name from metadata - -## [v3.2.1] -#### Fixed -- Fixed the bug in using double '$' - -## [v3.2.0] -#### Added -- Added possibility to show statistics only after authentication - -#### Changed -- Remove unnecessary is_null() -- Use SimpleSAML\Database - -#### Fixed -- Log info message about successful authentication only after successful authentication to SP -- Correct log message in insertLogin() -- Update README.md - - describe setup for modes PROXY/SP/IDP - - change array notation from `array()` to `[]` -- Read spName from $request only if present -- Remove unused indexes -- Optimize left outer join -- Don't double queries w/o days -- Fixed the table header in detailed statistics for SP - -## [v3.1.0] -#### Added -- Added configuration file for ESLint -- Module now supports running statistics as IDP/SP -- Store detailed statistics(include some user identifier) for several days - -#### Changed -- Using of short array syntax (from array() to []) -- Specify engine and default charset in tables.sql -- Removed unused include from 'templates/spDetail-tpl.php' -- Deleted useless code -- Deleted 'head' and 'body' tag in tab templates -- Use 'filter_input' to GET and VALIDATE value send as GET/POST param -- Eliminate inline javascript - - All JS code was moved to 'index.js' -- Using 'fetch_all' instead of 'fetch_asoc' to get data from DB -- Set default values for some option in 'DatabaseConnector.php' -- Remove duplicate code from 'DatabaseConnector.php' -- Move duplicate code for timeRange to separate file -- Use import instead of unnecessary qualifier - -#### Fixed -- Fixed the syntax of CHANGELOG -- Fixed SQL injection vulnerability -- Fixed list of required packages - -## [v3.0.0] -#### Added -- Added file phpcs.xml - -#### Fixed -- Fixed the problem with generating error, when some of attributes 'eduPersonUniqueId', 'sourceIdPEppn', 'sourceIdPEntityId' is null - -#### Changed -- Changed code style to PSR-2 -- Module uses namespaces - -## [v2.1.0] -#### Added -- Every successfully log in is logged with notice level - -## [v2.0.0] -#### Added -- Added details with statistics for individually SPs and IdPs -- Added script for migrate data to new version of database structure - -## [v1.5.0] -#### Added -- Added legends to charts -- Instance name in header is taken from config file - -#### Fixed -- set default value of lastDays and tab in index.php: no error logs when user open statistics for the first time - -## [v1.4.1] -#### Fixed -- Statistics will be now full screen -- Fixed bad checks before insert translation to db - -## [v1.4.0] -#### Added -- Possibility to change the time range of displayed data - -#### Changed -- DB commands work with apostrophes in IdP/SP names -- New visual form of the site -- Draw tables without month - -#### Fixed -- Draws tables data by selected time range - -#### Removed -- Removed unused functions - -## [v1.3.0] -#### Added -- Added mapping tables for mapping identifier to name - -#### Changed -- Storing entityIds instead of SpName/IdPName. - -#### Fixed -- Used only tabs for indentations - -## [v1.2.1] -#### Fixed -- Fixed the problem with getting utf8 chars from database - -## [v1.2.0] -#### Added -- Classes SimpleSAML_Logger and SimpleSAML_Module renamed to SimpleSAML\Logger and SimpleSAML\Module -- Dictionary -- Czech translation - -#### Changed -- Database commands use prepared statements -- Saving SourceIdPName instead of EntityId - -## [v1.1.0] -#### Added -- Added average and maximal count of logins per day into summary table - -#### Changed -- Fixed overqualified element in statisticsproxy.css - -## [v1.0.0] -#### Added -- Changelog - -[Unreleased]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/master -[v3.2.1]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v3.2.1 -[v3.2.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v3.2.0 -[v3.1.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v3.1.0 -[v3.0.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v3.0.0 -[v2.1.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v2.1.0 -[v2.0.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v2.0.0 -[v1.5.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.5.0 -[v1.4.1]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.4.1 -[v1.4.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.4.0 -[v1.3.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.3.0 -[v1.2.1]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.2.1 -[v1.2.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.2.0 -[v1.1.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.1.0 -[v1.0.0]: https://github.com/CESNET/proxystatistics-simplesamlphp-module/tree/v1.0.0 diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index c9b57c7..0000000 --- a/CODEOWNERS +++ /dev/null @@ -1,15 +0,0 @@ -# Lines starting with '#' are comments. -# Each line is a file pattern followed by one or more owners. - -# These owners will be the default owners for everything in the repo. -* @pajavyskocil @dBucik - -# Order is important. The last matching pattern has the most precedence. -# So if a pull request only touches javascript files, only these owners -# will be requested to review. - -#*.php @login - -# You can also use email addresses if you prefer. - -#* login@example.com \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 40fec49..0000000 --- a/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# proxystatistics-simplesamlphp-module -[![Latest Stable Version](https://poser.pugx.org/cesnet/simplesamlphp-module-proxystatistics/v/stable)](https://packagist.org/packages/cesnet/simplesamlphp-module-proxystatistics) -[![Total Downloads](https://poser.pugx.org/cesnet/simplesamlphp-module-proxystatistics/downloads)](https://packagist.org/packages/cesnet/simplesamlphp-module-proxystatistics) -[![CodeFactor](https://www.codefactor.io/repository/github/cesnet/proxystatistics-simplesamlphp-module/badge)](https://www.codefactor.io/repository/github/cesnet/proxystatistics-simplesamlphp-module) -[![License](https://poser.pugx.org/cesnet/simplesamlphp-module-proxystatistics/license)](https://packagist.org/packages/cesnet/simplesamlphp-module-proxystatistics) - -Module for simpleSAMLphp which shows Proxy IdP/SP statistics - -## Instalation -Once you have installed SimpleSAMLphp, installing this module is very simple. First of all, you will need to download Composer if you haven't already. After installing Composer, just execute the following command in the root of your SimpleSAMLphp installation: - -`php composer.phar require cesnet/simplesamlphp-module-proxystatistics` - - -## Configuration -1. Install MySQL Database and create database for statistics and user. -2. For this database run script to create tables. Script is available in config-templates/tables.sql. -3. Copy config-templates/module_statisticsproxy.php to your folder vith config and fill it. -4. Configure, according to mode -* for mode PROXY, configure IdPAttribute filter from Perun module to get sourceIdPName from IdP metadata: -``` - XX => [ - 'class' => 'perun:IdPAttribute', - 'attrMap' => [ - 'name:en' => 'sourceIdPName', - ], - ], - // where XX is priority (for example 30, must not be used for other modules) -``` -* for mode IDP, configure `idpEntityId` and `idpName` in `module_statisticsproxy.php` -``` - /* - * EntityId of IdP - * REQUIRED FOR IDP MODE - */ - 'idpEntityId' => '', - /* - * Name of IdP - * REQUIRED FOR IDP MODE - */ - 'idpName' => '', -``` -* for mode SP, configure `spEntityId` and `spName` in `module_statisticsproxy.php` -``` - /* - * EntityId of SP - * REQUIRED FOR SP MODE - */ - 'spEntityId' => '', - /* - * Name of SP - * REQUIRED FOR SP MODE - */ - 'spName' => '', -``` -5. Configure proxystatistic filter -``` - XX => array( - 'class' => 'proxystatistics:statistics', - 'config' => [], - ), - // where XX is priority (for example 50, must not be used for other modules) -``` -6. Add to `config.php`: -``` -'instance_name' => 'Instance name', -``` diff --git a/composer.json b/composer.json deleted file mode 100644 index 8419d20..0000000 --- a/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "cesnet/simplesamlphp-module-proxystatistics", - "description": "A SimpleSAMLPHP module for statistics", - "version": "3.2.1", - "type": "simplesamlphp-module", - "keywords": ["statistics","simplesamlphp"], - "license": "BSD-2-Clause", - "authors": [ - { - "name": "Pavel Vyskocil", - "email": "vyskocilpavel@muni.cz" - } - ], - "require": { - "php": ">=5.4.0", - "cesnet/simplesamlphp-module-perun" : "~3.0", - "simplesamlphp/simplesamlphp": "~1.17", - "ext-mysqli": "*", - "ext-json": "*" - } -} diff --git a/config-templates/mysql_tables.sql b/config-templates/mysql_tables.sql deleted file mode 100644 index 5d2651d..0000000 --- a/config-templates/mysql_tables.sql +++ /dev/null @@ -1,52 +0,0 @@ ---Statistics for IdPs -CREATE TABLE statistics ( - year INT NOT NULL, - month INT NOT NULL, - day INT NOT NULL, - sourceIdp VARCHAR(255) NOT NULL, - service VARCHAR(255) NOT NULL, - count INT, - INDEX (sourceIdp), - INDEX (service), - PRIMARY KEY (year, month, day, sourceIdp, service) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE statistics_detail ( - year INT NOT NULL, - month INT NOT NULL, - day INT NOT NULL, - sourceIdp VARCHAR(255) NOT NULL, - service VARCHAR(255) NOT NULL, - user VARCHAR(255) NOT NULL, - count INT, - INDEX (sourceIdp), - INDEX (service), - PRIMARY KEY (year, month, day, sourceIdp, service, user) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE statistics_ip ( - accessed TIMESTAMP NOT NULL, - sourceIdp VARCHAR(255) NOT NULL, - service VARCHAR(255) NOT NULL, - user VARCHAR(255) NOT NULL, - ip VARBINARY(16) NOT NULL, - ipVersion VARCHAR(4) NOT NULL, - INDEX (accessed), - INDEX (sourceIdp), - INDEX (service), - INDEX (user), - INDEX (ipVersion) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - ---Tables for mapping identifier to name -CREATE TABLE identityProvidersMap( - entityId VARCHAR(255) NOT NULL, - name VARCHAR(255) NOT NULL, - PRIMARY KEY (entityId) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE serviceProvidersMap( - identifier VARCHAR(255) NOT NULL, - name VARCHAR(255) NOT NULL, - PRIMARY KEY (identifier) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/config-templates/pgsql_tables.sql b/config-templates/pgsql_tables.sql deleted file mode 100644 index b9cf783..0000000 --- a/config-templates/pgsql_tables.sql +++ /dev/null @@ -1,56 +0,0 @@ ---Statistics for IdPs -CREATE TABLE IF NOT EXISTS statistics ( - year bigint NOT NULL, - month bigint NOT NULL, - day bigint NOT NULL, - sourceidp character varying(255) NOT NULL, - service character varying(255) NOT NULL, - count bigint, - PRIMARY KEY (year, month, day, sourceIdp, service) -); - -CREATE INDEX IF NOT EXISTS statistics_i1 ON statistics (sourceIdp); -CREATE INDEX IF NOT EXISTS statistics_i2 ON statistics (service); - -CREATE TABLE IF NOT EXISTS statistics_detail ( - year bigint NOT NULL, - month bigint NOT NULL, - day bigint NOT NULL, - sourceidp character varying(255) NOT NULL, - service character varying(255) NOT NULL, - userid character varying(255) NOT NULL, - count bigint, - PRIMARY KEY (year, month, day, sourceIdp, service, userid) -); - -CREATE INDEX IF NOT EXISTS statistics_detail_i1 ON statistics_detail (sourceIdp); -CREATE INDEX IF NOT EXISTS statistics_detail_i2 ON statistics_detail (service); -CREATE INDEX IF NOT EXISTS statistics_detail_i3 ON statistics_detail (userid); - -CREATE TABLE IF NOT EXISTS statistics_ip ( - accessed timestamptz NOT NULL, - sourceidp character varying(255) NOT NULL, - service character varying(255) NOT NULL, - userid character varying(255) NOT NULL, - ip CIDR NOT NULL, - ipversion VARCHAR(4) NOT NULL -); - -CREATE INDEX IF NOT EXISTS statistics_ip_i1 ON statistics_ip (accessed); -CREATE INDEX IF NOT EXISTS statistics_ip_i2 ON statistics_ip (sourceidp); -CREATE INDEX IF NOT EXISTS statistics_ip_i3 ON statistics_ip (service); -CREATE INDEX IF NOT EXISTS statistics_ip_i4 ON statistics_ip (userid); -CREATE INDEX IF NOT EXISTS statistics_ip_i5 ON statistics_ip (ipversion); - ---Tables for mapping identifier to name -CREATE TABLE IF NOT EXISTS identityprovidersmap ( - entityid character varying(255) NOT NULL, - name character varying(255) NOT NULL, - PRIMARY KEY (entityid) -); - -CREATE TABLE IF NOT EXISTS serviceprovidersmap ( - identifier character varying(255) NOT NULL, - name character varying(255) NOT NULL, - PRIMARY KEY (identifier) -); diff --git a/default-disable b/default-disable deleted file mode 100644 index e69de29..0000000 diff --git a/dictionaries/Proxystatistics.definition.json b/dictionaries/Proxystatistics.definition.json deleted file mode 100644 index 2db8eb6..0000000 --- a/dictionaries/Proxystatistics.definition.json +++ /dev/null @@ -1,186 +0,0 @@ -{ - "summary": { - "en": "Summary", - "cs": "Shrnutí" - }, - "templates/statistics-tpl_idpsDetail": { - "en": "Identity providers detail", - "cs": "Detail poskytovatelů identity" - }, - "templates/statistics-tpl_spsDetail": { - "en": "Service providers detail", - "cs": "Detail služeb" - }, - "templates_time_range": { - "en": "Select the time range:", - "cs": "Vyberte časový interval:" - }, - "templates/statistics-tpl_all": { - "en": "All", - "cs": "Vše" - }, - "templates/statistics-tpl_week": { - "en": "Last 7 days", - "cs": "Posledních 7 dnů" - }, - "templates/statistics-tpl_month": { - "en": "Last 30 days", - "cs": "Posledních 30 dnů" - }, - "templates/statistics-tpl_year": { - "en": "Last year", - "cs": "Poslední rok" - }, - "templates/summary_overall_logins": { - "en": "Overall number of logins", - "cs": "Celkový počet přihlášení" - }, - "templates/summary_accessed_providers": { - "en": "Total number of accessed service providers", - "cs": "Celkový počet používaných služeb" - }, - "templates/summary_used_identity_providers": { - "en": "Total number of used identity providers", - "cs": "Celkový počet použitých poskytovatelů identit" - }, - "templates/summary_logins_today": { - "en": "Number of logins for today", - "cs": "Počet přihlášení během dnešního dne" - }, - "templates/summary_average_logins": { - "en": "Average number of logins per day", - "cs": "Průměrný počet přihlášení za den" - }, - "templates/summary_max_logins": { - "en": "Maximal number of logins per day", - "cs": "Maximální počet přihlášení za den" - }, - "templates/summary_name": { - "en": "Name", - "cs": "Jméno" - }, - "templates/count": { - "en": "Count", - "cs": "Počet" - }, - "templates/other": { - "en": "other", - "cs": "ostatní" - }, - "templates/graphs_logins": { - "en": "Number of logins", - "cs": "Počet přihlášení" - }, - "templates/graphs_id_providers": { - "en": "Identity providers", - "cs": "Poskytovatelé identit" - }, - "templates/graphs_service_providers": { - "en": "Service providers", - "cs": "Poskytovatelé služeb" - }, - "templates/logins_day": { - "en": "Number of logins per day", - "cs": "Počet přihlášení za den" - }, - "templates/logins_identity": { - "en": "Overall logins per identity provider", - "cs": "Počet přihlášení podle poskytovatele identity" - }, - "templates/logins_service": { - "en": "Overall logins to service providers", - "cs": "Počet přihlášení ke službám" - }, - "templates/tables_date": { - "en": "Date", - "cs": "Datum" - }, - "templates/tables_month": { - "en": "Month", - "cs": "Měsíc" - }, - "templates/tables_identity_provider": { - "en": "Identity provider", - "cs": "Poskytovatel identity" - }, - "templates/tables_service_provider": { - "en": "Service provider", - "cs": "Služba" - }, - "templates/statistics_header": { - "en": "AAI Statistics", - "cs": "AAI Statistiky" - }, - "templates/identityProviders_legend": { - "en": "The chart and the table show number of logins from each identity provider in selected time range. Click a specific identity provider to view detailed statistics for that identity provider.", - "cs": "Graf s tabulkou ukazují počet přihlášení od každého poskytovatele identity ve zvoleném časovém rozmezí. Kliknutím na konkrétního poskytovatele identit zobrazíte detailní statistiky pro daného poskytovatele identit." - }, - "templates/serviceProviders_legend": { - "en": "The chart and the table show number of logins to each service provider in selected time range. Only first access to the service is counted, following single sign-on accesses are not counted, because they are not going through the Proxy. Click a specific service to view detailed statistics for that service.", - "cs": "Graf s tabulkou ukazují přihlášení ke každé službě ve zvoleném časovém rozmezí. Počítá se pouze první přístup ke službě, protože další již nejdou přes Proxy. Kliknutím na konkrétní službu zobrazíte detailní statistiky pro danou službu." - }, - "templates/summary_logins_info": { - "en": "The chart shows overall number of logins from identity providers for each day.", - "cs": "Graf zobrazuje počet přihlášení za každý den." - }, - "templates/summary_idps_info": { - "en": "The chart shows number of logins from each identity provider in selected time range. Click a specific identity provider to view detailed statistics for that identity provider.", - "cs": "Graf zobrazuje počet přihlášení od každého poskytovatele identity ve zvoleném časovém rozmezí. Kliknutím na konkrétního poskytovatele identit zobrazíte detailní statistiky pro daného poskytovatele identit." - }, - "templates/summary_sps_info": { - "en": "The chart shows number of logins to each service provider in selected time range. Only first access to the service is counted, following single sign-on accesses are not counted, because they are not going through the Proxy. Click a specific service to view detailed statistics for that service.", - "cs": "Graf zobrazuje počet přihlášení ke každé službě ve zvoleném časovém rozmezí. Počítá se pouze první přístup ke službě, následující přístupy neprocházejí přes Proxy. Kliknutím na konkrétní službu zobrazíte detailní statistiky pro danou službu." - }, - "templates/spDetail_header_name": { - "en": "Detail statistics for Service Provider: ", - "cs": "Podrobné statistiky služby: " - }, - "templates/spDetail_header_identifier": { - "en": "Detail statistics for Service Provider with identifier: ", - "cs": "Podrobné statistiky služby s identifikátorem: " - }, - "templates/spDetail_dashboard_header": { - "en": "Number of logins", - "cs": "Počet přihlášení" - }, - "templates/spDetail_dashboard_legend": { - "en": "The chart shows number of logins to this service for each day.", - "cs": "Graf zobrazuje počet přihlášení k dané službě za každý den." - }, - "templates/spDetail_graph_header": { - "en": "Used identity providers", - "cs": "Použití poskytovatelé identit" - }, - "templates/spDetail_graph_legend": { - "en": "The chart and the table shows used identity providers to login to this service provider in selected time range. Only first access to the service is counted, following single sign-on accesses are not counted, because they are not going through the Proxy.", - "cs": "Graf s tabulkou ukazují použité poskytovatele identit pro přihlášení k této službě ve zvoleném časovém rozmezí. Počítá se pouze první přístup ke službě, protože další již nejdou přes Proxy." - }, - "templates/idpDetail_header_name": { - "en": "Detail usage of Identity Provider: ", - "cs": "Detail využití poskytovatele identit: " - }, - "templates/idpDetail_header_entityId": { - "en": "Detail usage for Service Provider with entityId: ", - "cs": "Detail využití poskytovatele identit s entityId: " - }, - "templates/idpDetail_dashboard_header": { - "en": "Number of usage of identity provider", - "cs": "Počet přístupů skrze poskytovatele identit" - }, - "templates/idpDetail_dashboard_legend": { - "en": "The chart shows number of logins using this identity provider for each day.", - "cs": "Graf zobrazuje počet přihlášení tímto poskytovatelem identit za každý den." - }, - "templates/idpDetail_graph_header": { - "en": "Accessed service providers", - "cs": "Služby, ke kterým bylo přistupováno" - }, - "templates/idpDetail_graph_legend": { - "en": "The chart and the table shows accessed service providers by this identity provider in selected time range. Only first access to the service is counted, following single sign-on accesses are not counted, because they are not going through the Proxy.", - "cs": "Graf s tabulkou ukazují služby, ke kterým bylo přistoupeno daným poskytovatelem identit ve zvoleném časovém rozmezí. Počítá se pouze první přístup ke službě, protože další již nejdou přes Proxy." - }, - "btn_label_back_to_stats": { - "en": "Back to overall statistics", - "cs": "Zpět na přehled celkových statistik" - } -} diff --git a/hooks/hook_cron.php b/hooks/hook_cron.php deleted file mode 100644 index f48acf4..0000000 --- a/hooks/hook_cron.php +++ /dev/null @@ -1,24 +0,0 @@ -deleteOldDetailedStatistics(); - } catch (\Exception $e) { - $croninfo['summary'][] = 'Error during deleting old detailed statistics: ' . $e->getMessage(); - } -} diff --git a/lib/Auth/Process/DatabaseConnector.php b/lib/Auth/Process/DatabaseConnector.php deleted file mode 100644 index 4464586..0000000 --- a/lib/Auth/Process/DatabaseConnector.php +++ /dev/null @@ -1,199 +0,0 @@ - - */ - -class DatabaseConnector -{ - private $databaseDsn; - private $statisticsTableName; - private $detailedStatisticsTableName; - private $identityProvidersMapTableName; - private $serviceProvidersMapTableName; - private $mode; - private $idpEntityId; - private $idpName; - private $spEntityId; - private $spName; - private $detailedDays; - private $userIdAttribute; - private $conn = null; - private $oidcIss; - private $keycloakSp; - - const CONFIG_FILE_NAME = 'module_statisticsproxy.php'; - /** @deprecated */ - const SERVER = 'serverName'; - /** @deprecated */ - const PORT = 'port'; - /** @deprecated */ - const USER = 'userName'; - /** @deprecated */ - const PASSWORD = 'password'; - /** @deprecated */ - const DATABASE = 'databaseName'; - const STATS_TABLE_NAME = 'statisticsTableName'; - const DETAILED_STATS_TABLE_NAME = 'detailedStatisticsTableName'; - const IP_STATS_TABLE_NAME = 'ipStatisticsTableName'; - const IDP_MAP_TABLE_NAME = 'identityProvidersMapTableName'; - const SP_MAP_TABLE_NAME = 'serviceProvidersMapTableName'; - /** @deprecated */ - const ENCRYPTION = 'encryption'; - const STORE = 'store'; - /** @deprecated */ - const SSL_CA = 'ssl_ca'; - /** @deprecated */ - const SSL_CERT = 'ssl_cert_path'; - /** @deprecated */ - const SSL_KEY = 'ssl_key_path'; - /** @deprecated */ - const SSL_CA_PATH = 'ssl_ca_path'; - const MODE = 'mode'; - const IDP_ENTITY_ID = 'idpEntityId'; - const IDP_NAME = 'idpName'; - const SP_ENTITY_ID = 'spEntityId'; - const SP_NAME = 'spName'; - const DETAILED_DAYS = 'detailedDays'; - const USER_ID_ATTRIBUTE = 'userIdAttribute'; - const OIDC_ISS = 'oidcIssuer'; - const KEYCLOAK_SP = 'keycloakSp'; - const TABLE_PREFIX = 'database.prefix'; - - public function __construct() - { - $conf = Configuration::getConfig(self::CONFIG_FILE_NAME); - $this->storeConfig = $conf->getArray(self::STORE, null); - - // TODO: remove - if (empty($this->storeConfig) && $conf->getString(self::DATABASE, false)) { - $this->storeConfig = [ - 'database.dsn' => sprintf( - 'mysql:host=%s;port=%d;dbname=%s;charset=utf8', - $conf->getString(self::SERVER, 'localhost'), - $conf->getInteger(self::PORT, 3306), - $conf->getString(self::DATABASE) - ), - 'database.username' => $conf->getString(self::USER), - 'database.password' => $conf->getString(self::PASSWORD), - ]; - if ($conf->getBoolean(self::ENCRYPTION, false)) { - Logger::debug("Getting connection with encryption."); - $this->storeConfig['database.driver_options'] = [ - PDO::MYSQL_ATTR_SSL_KEY => $conf->getString(self::SSL_KEY, ''), - PDO::MYSQL_ATTR_SSL_CERT => $conf->getString(self::SSL_CERT, ''), - PDO::MYSQL_ATTR_SSL_CA => $conf->getString(self::SSL_CA, ''), - PDO::MYSQL_ATTR_SSL_CAPATH => $conf->getString(self::SSL_CA_PATH, ''), - ]; - } - - Logger::debug("Deprecated option(s) used for proxystatistics. Please use the store option."); - } - - $this->storeConfig = Configuration::loadFromArray($this->storeConfig); - $this->databaseDsn = $this->storeConfig->getString('database.dsn'); - - $this->statisticsTableName = $conf->getString(self::STATS_TABLE_NAME); - $this->detailedStatisticsTableName = $conf->getString(self::DETAILED_STATS_TABLE_NAME, 'statistics_detail'); - $this->ipStatisticsTableName = $conf->getString(self::IP_STATS_TABLE_NAME, 'statistics_ip'); - $this->identityProvidersMapTableName = $conf->getString(self::IDP_MAP_TABLE_NAME); - $this->serviceProvidersMapTableName = $conf->getString(self::SP_MAP_TABLE_NAME); - $this->mode = $conf->getString(self::MODE, 'PROXY'); - $this->idpEntityId = $conf->getString(self::IDP_ENTITY_ID, ''); - $this->idpName = $conf->getString(self::IDP_NAME, ''); - $this->spEntityId = $conf->getString(self::SP_ENTITY_ID, ''); - $this->spName = $conf->getString(self::SP_NAME, ''); - $this->detailedDays = $conf->getInteger(self::DETAILED_DAYS, 0); - $this->userIdAttribute = $conf->getString(self::USER_ID_ATTRIBUTE, null); - $this->oidcIss = $conf->getString(self::OIDC_ISS, null); - $this->keycloakSp = $conf->getString(self::KEYCLOAK_SP, null); - } - - public function getConnection() - { - return Database::getInstance($this->storeConfig); - } - - public function getStatisticsTableName() - { - return $this->storeConfig->getString(self::TABLE_PREFIX, '') . $this->statisticsTableName; - } - - public function getDetailedStatisticsTableName() - { - return $this->storeConfig->getString(self::TABLE_PREFIX, '') . $this->detailedStatisticsTableName; - } - - public function getIpStatisticsTableName() - { - return $this->ipStatisticsTableName; - } - - public function getIdentityProvidersMapTableName() - { - return $this->storeConfig->getString(self::TABLE_PREFIX, '') . $this->identityProvidersMapTableName; - } - - public function getServiceProvidersMapTableName() - { - return $this->storeConfig->getString(self::TABLE_PREFIX, '') . $this->serviceProvidersMapTableName; - } - - public function getDbDriver() - { - preg_match('/.+?(?=:)/', $this->databaseDsn, $driver); - return $driver[0]; - } - - public function getMode() - { - return $this->mode; - } - - public function getIdpEntityId() - { - return $this->idpEntityId; - } - - public function getIdpName() - { - return $this->idpName; - } - - public function getSpEntityId() - { - return $this->spEntityId; - } - - public function getSpName() - { - return $this->spName; - } - - public function getDetailedDays() - { - return $this->detailedDays; - } - - public function getUserIdAttribute() - { - return $this->userIdAttribute; - } - - public function getOidcIssuer() - { - return $this->oidcIss; - } - - public function getKeycloakSp() - { - return $this->keycloakSp; - } -} diff --git a/phpcs.xml b/phpcs.xml deleted file mode 100644 index 7e64f46..0000000 --- a/phpcs.xml +++ /dev/null @@ -1,207 +0,0 @@ - - - The PSR-2 coding standard. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - - - - - - - - - - - - - - - - - 0 - - - 0 - - - - - - - - - - - - - 0 - - - 0 - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/migrate_2.0.php b/scripts/migrate_2.0.php deleted file mode 100644 index d31a847..0000000 --- a/scripts/migrate_2.0.php +++ /dev/null @@ -1,66 +0,0 @@ - 2.0.0 - * - * You need firstly export the tables identityProviders and serviceProviders into two separate CSV files. - * - * The result file is in text format as SQL inserts. - * - * How to run this script: - * php -f migrate_2.0.php - * - * @author: Pavel Vyskocil - */ - -// Absolute path to CSV file with data about identityProviders -$identityProvidersFileName = ''; - -// Absolute path to CSV file with data about serviceProviders -$serviceProvidersFileName = ''; - -// Absolute path where result file will be stored -$resultFileName = ''; - -if (empty($identityProvidersFileName) || empty($serviceProvidersFileName) || empty($resultFileName)) { - exit("One of required attributes is empty." . PHP_EOL); -} - -$tableName = 'statistics'; - -$result = ''; -$line = null; - -// Identity providers part -$file = fopen($identityProvidersFileName, "r"); - -while (!feof($file)) { - $line = (fgetcsv($file)); - if ($line != null) { - $lineInsert = 'INSERT INTO ' . $tableName . '(year, month, day, sourceIdp, service, count) ' . - 'VALUES(' . $line[0] . ', ' . $line[1] . ', ' . $line[2] . ', "' . $line[3] . '","" , ' . $line[4] . ');' . - PHP_EOL; - $result .= $lineInsert; - } -} - -fclose($file); - -// Service providers part -$file = fopen($serviceProvidersFileName, "r"); - -while (!feof($file)) { - $line = (fgetcsv($file)); - if ($line != null) { - $lineInsert = 'INSERT INTO ' . $tableName . '(year, month, day, sourceIdp, service, count) ' . - 'VALUES(' . $line[0] . ', ' . $line[1] . ', ' . $line[2] . ', "", "' . $line[3] . '", ' . $line[4] . ');' . - PHP_EOL; - $result .= $lineInsert; - } -} - -fclose($file); - -// save to result file -file_put_contents($resultFileName, $result); diff --git a/src/Auth/Process/AmsCommand.php b/src/Auth/Process/AmsCommand.php new file mode 100644 index 0000000..467c0f9 --- /dev/null +++ b/src/Auth/Process/AmsCommand.php @@ -0,0 +1,145 @@ + + */ +class AmsCommand +{ + private $amsConnector; + + public function __construct() + { + $this->amsConnector = new AmsConnector(); + $this->conn = $this->amsConnector->getConnection(); + assert($this->conn !== null); + } + private function writeLoginIp($sourceIdp, $service, $user, $ip, $date): void + { + $params = [ + 'ip' => $ip, + 'user' => $user, + 'sourceIdp' => $sourceIdp, + 'service' => $service, + 'accessed' => $date, + 'ipVersion' => (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 'ipv4' : 'ipv6') + ]; + + // todo: Send to ams + } + private function writeLogin($year, $month, $day, $sourceIdp, $service, $user = null) + { + $params = [ + 'year' => $year, + 'month' => $month, + 'day' => $day, + 'sourceIdp' => $sourceIdp, + 'service' => $service, + 'count' => 1, + ]; + $table = $this->statisticsTableName; + if ($user && $this->amsConnector->getDetailedDays() > 0) { + $params['user'] = $user; + } + + // todo: Send ams + } + + public function insertLogin(&$request, &$date, &$userId) + { + if (!in_array($this->amsConnector->getMode(), ['PROXY', 'IDP', 'SP'])) { + throw new Exception('Unknown mode is set. Mode has to be one of the following: PROXY, IDP, SP.'); + } + if ($this->amsConnector->getMode() !== 'IDP') { + if (!empty($request['saml:sp:IdP'])) { + $idpEntityID = $request['saml:sp:IdP']; + $idpMetadata = MetaDataStorageHandler::getMetadataHandler()->getMetaData($idpEntityID, 'saml20-idp-remote'); + } else { + $idpEntityID = $request['Source']['entityid']; + $idpMetadata = $request['Source']; + } + $idpName = self::getIdPDisplayName($idpMetadata); + } + if ($this->amsConnector->getMode() !== 'SP') { + if ( + !empty($request['saml:RelayState']) + && !empty($this->amsConnector->getKeycloakSp()) + && strpos($request['Destination']['entityid'], $this->amsConnector->getKeycloakSp()) !== false + ) { + $spEntityId = explode('.', $request['saml:RelayState'], 3)[2]; + $spName = null; + } elseif ( + !empty($request['saml:RequesterID']) + && !empty($this->amsConnector->getOidcIssuer()) + && (strpos($request['Destination']['entityid'], $this->amsConnector->getOidcIssuer()) !== false) + ) { + $spEntityId = str_replace( + $this->amsConnector->getOidcIssuer() . "/", + "", + $request['saml:RequesterID'][0] + ); + $spName = null; + } elseif ( + !empty($request['saml:RelayState']) + && !empty($this->amsConnector->getOidcIssuer()) + && strpos($request['Destination']['entityid'], $this->amsConnector->getOidcIssuer()) !== false + ) { + $spEntityId = $request['saml:RelayState']; + $spName = null; + } else { + $spEntityId = $request['Destination']['entityid']; + $spName = self::getSPDisplayName($request['Destination']); + } + } + + if ($this->amsConnector->getMode() === 'IDP') { + $idpName = $this->amsConnector->getIdpName(); + $idpEntityID = $this->amsConnector->getIdpEntityId(); + } elseif ($this->amsConnector->getMode() === 'SP') { + $spEntityId = $this->amsConnector->getSpEntityId(); + $spName = $this->amsConnector->getSpName(); + } + + $year = $date->format('Y'); + $month = $date->format('m'); + $day = $date->format('d'); + $dateTimestamp = $date->format('Y-m-d H:i:s T'); + $ip = Utils::getClientIpAddress(); + + if (empty($idpEntityID) || empty($spEntityId)) { + Logger::error( + "'idpEntityId' or 'spEntityId'" . + " is empty and login log wasn't inserted into the database." + ); + } else { + if ($this->writeLogin($year, $month, $day, $idpEntityID, $spEntityId, $userId) === false) { + Logger::error("The login log wasn't inserted into table: " . $this->statisticsTableName . "."); + } + if ($this->writeLoginIp($idpEntityID, $spEntityId, $userId, $ip, $dateTimestamp) === false) { + Logger::error("The login log for ip wasn't inserted into table: " . $this->ipStatisticsTableName . "."); + } + if (!empty($idpName)) { + $data = ['entityId' => $idpEntityID, 'idpName' => $idpName, 'idpName2' => $idpName]; + + // todo: send to ams + } + + if (!empty($spName)) { + $data = [ + 'identifier' => $spEntityId, + 'spName' => $spName, + 'spName2' => $spName + ]; + // todo send to ams + } + } + } + +} diff --git a/src/Auth/Process/AmsConnector.php b/src/Auth/Process/AmsConnector.php new file mode 100644 index 0000000..80435b1 --- /dev/null +++ b/src/Auth/Process/AmsConnector.php @@ -0,0 +1,115 @@ +storeConfig = $conf->getArray(self::STORE, null); + + $this->storeConfig = Configuration::loadFromArray($this->storeConfig); + $this->databaseDsn = $this->storeConfig->getString('database.dsn'); + + $this->mode = $conf->getString(self::MODE, 'PROXY'); + $this->idpEntityId = $conf->getString(self::IDP_ENTITY_ID, ''); + $this->idpName = $conf->getString(self::IDP_NAME, ''); + $this->spEntityId = $conf->getString(self::SP_ENTITY_ID, ''); + $this->spName = $conf->getString(self::SP_NAME, ''); + $this->detailedDays = $conf->getInteger(self::DETAILED_DAYS, 0); + $this->userIdAttribute = $conf->getString(self::USER_ID_ATTRIBUTE, null); + $this->oidcIss = $conf->getString(self::OIDC_ISS, null); + $this->keycloakSp = $conf->getString(self::KEYCLOAK_SP, null); + } + + public function getConnection() + { + return Database::getInstance($this->storeConfig); + } + + public function getMode() + { + return $this->mode; + } + + public function getIdpEntityId() + { + return $this->idpEntityId; + } + + public function getIdpName() + { + return $this->idpName; + } + + public function getSpEntityId() + { + return $this->spEntityId; + } + + public function getSpName() + { + return $this->spName; + } + + public function getDetailedDays() + { + return $this->detailedDays; + } + + public function getUserIdAttribute() + { + return $this->userIdAttribute; + } + + public function getOidcIssuer() + { + return $this->oidcIss; + } + + public function getKeycloakSp() + { + return $this->keycloakSp; + } +} \ No newline at end of file diff --git a/lib/Auth/Process/DatabaseCommand.php b/src/Auth/Process/DatabaseCommand.php similarity index 100% rename from lib/Auth/Process/DatabaseCommand.php rename to src/Auth/Process/DatabaseCommand.php diff --git a/lib/Auth/Process/Statistics.php b/src/Auth/Process/Statistics.php similarity index 92% rename from lib/Auth/Process/Statistics.php rename to src/Auth/Process/Statistics.php index aec4372..98b634c 100644 --- a/lib/Auth/Process/Statistics.php +++ b/src/Auth/Process/Statistics.php @@ -15,18 +15,18 @@ */ class Statistics extends ProcessingFilter { - private $config; - private $reserved; - private $userIdAttribute; + private array $config; + private string $reserved; + private string $userIdAttribute; - public function __construct($config, $reserved) + public function __construct(array $config, string $reserved) { parent::__construct($config, $reserved); $this->config = Configuration::getConfig(DatabaseConnector::CONFIG_FILE_NAME); $this->reserved = (array)$reserved; } - public function process(&$request) + public function process(array &$request): void { if (empty($this->config->getString('userIdAttribute', null))) { if (empty($request['rciamAttributes']['cuid'])) { @@ -54,6 +54,8 @@ public function process(&$request) } $dateTime = new DateTime('now', new DateTimeZone('UTC')); + + // TODO: This should become AMS send $dbCmd = new DatabaseCommand(); $dbCmd->insertLogin($request, $dateTime, $this->userIdAttribute[0]); $spEntityId = $request['SPMetadata']['entityid']; diff --git a/lib/Utils.php b/src/Utils.php similarity index 96% rename from lib/Utils.php rename to src/Utils.php index 766215d..c58912b 100644 --- a/lib/Utils.php +++ b/src/Utils.php @@ -14,7 +14,7 @@ class Utils /** * Retrieve original IP address of client. */ - function getClientIpAddress() + public function getClientIpAddress():string { $ip_keys = [ diff --git a/www/Chart.min.css b/www/Chart.min.css deleted file mode 100644 index 3a23924..0000000 --- a/www/Chart.min.css +++ /dev/null @@ -1 +0,0 @@ -@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0} diff --git a/www/Chart.min.js b/www/Chart.min.js deleted file mode 100644 index c74a791..0000000 --- a/www/Chart.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Chart.js v2.8.0 - * https://www.chartjs.org - * (c) 2019 Chart.js Contributors - * Released under the MIT License - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(function(){try{return require("moment")}catch(t){}}()):"function"==typeof define&&define.amd?define(["require"],function(t){return e(function(){try{return t("moment")}catch(t){}}())}):t.Chart=e(t.moment)}(this,function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e={rgb2hsl:i,rgb2hsv:n,rgb2hwb:a,rgb2cmyk:o,rgb2keyword:s,rgb2xyz:l,rgb2lab:d,rgb2lch:function(t){return x(d(t))},hsl2rgb:u,hsl2hsv:function(t){var e=t[0],i=t[1]/100,n=t[2]/100;if(0===n)return[0,0,0];return[e,100*(2*(i*=(n*=2)<=1?n:2-n)/(n+i)),100*((n+i)/2)]},hsl2hwb:function(t){return a(u(t))},hsl2cmyk:function(t){return o(u(t))},hsl2keyword:function(t){return s(u(t))},hsv2rgb:h,hsv2hsl:function(t){var e,i,n=t[0],a=t[1]/100,o=t[2]/100;return e=a*o,[n,100*(e=(e/=(i=(2-a)*o)<=1?i:2-i)||0),100*(i/=2)]},hsv2hwb:function(t){return a(h(t))},hsv2cmyk:function(t){return o(h(t))},hsv2keyword:function(t){return s(h(t))},hwb2rgb:c,hwb2hsl:function(t){return i(c(t))},hwb2hsv:function(t){return n(c(t))},hwb2cmyk:function(t){return o(c(t))},hwb2keyword:function(t){return s(c(t))},cmyk2rgb:f,cmyk2hsl:function(t){return i(f(t))},cmyk2hsv:function(t){return n(f(t))},cmyk2hwb:function(t){return a(f(t))},cmyk2keyword:function(t){return s(f(t))},keyword2rgb:w,keyword2hsl:function(t){return i(w(t))},keyword2hsv:function(t){return n(w(t))},keyword2hwb:function(t){return a(w(t))},keyword2cmyk:function(t){return o(w(t))},keyword2lab:function(t){return d(w(t))},keyword2xyz:function(t){return l(w(t))},xyz2rgb:p,xyz2lab:m,xyz2lch:function(t){return x(m(t))},lab2xyz:v,lab2rgb:y,lab2lch:x,lch2lab:k,lch2xyz:function(t){return v(k(t))},lch2rgb:function(t){return y(k(t))}};function i(t){var e,i,n=t[0]/255,a=t[1]/255,o=t[2]/255,r=Math.min(n,a,o),s=Math.max(n,a,o),l=s-r;return s==r?e=0:n==s?e=(a-o)/l:a==s?e=2+(o-n)/l:o==s&&(e=4+(n-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),i=(r+s)/2,[e,100*(s==r?0:i<=.5?l/(s+r):l/(2-s-r)),100*i]}function n(t){var e,i,n=t[0],a=t[1],o=t[2],r=Math.min(n,a,o),s=Math.max(n,a,o),l=s-r;return i=0==s?0:l/s*1e3/10,s==r?e=0:n==s?e=(a-o)/l:a==s?e=2+(o-n)/l:o==s&&(e=4+(n-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),[e,i,s/255*1e3/10]}function a(t){var e=t[0],n=t[1],a=t[2];return[i(t)[0],100*(1/255*Math.min(e,Math.min(n,a))),100*(a=1-1/255*Math.max(e,Math.max(n,a)))]}function o(t){var e,i=t[0]/255,n=t[1]/255,a=t[2]/255;return[100*((1-i-(e=Math.min(1-i,1-n,1-a)))/(1-e)||0),100*((1-n-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]}function s(t){return _[JSON.stringify(t)]}function l(t){var e=t[0]/255,i=t[1]/255,n=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)+.1805*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)),100*(.2126*e+.7152*i+.0722*n),100*(.0193*e+.1192*i+.9505*n)]}function d(t){var e=l(t),i=e[0],n=e[1],a=e[2];return n/=100,a/=108.883,i=(i/=95.047)>.008856?Math.pow(i,1/3):7.787*i+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(i-n),200*(n-(a=a>.008856?Math.pow(a,1/3):7.787*a+16/116))]}function u(t){var e,i,n,a,o,r=t[0]/360,s=t[1]/100,l=t[2]/100;if(0==s)return[o=255*l,o,o];e=2*l-(i=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var d=0;d<3;d++)(n=r+1/3*-(d-1))<0&&n++,n>1&&n--,o=6*n<1?e+6*(i-e)*n:2*n<1?i:3*n<2?e+(i-e)*(2/3-n)*6:e,a[d]=255*o;return a}function h(t){var e=t[0]/60,i=t[1]/100,n=t[2]/100,a=Math.floor(e)%6,o=e-Math.floor(e),r=255*n*(1-i),s=255*n*(1-i*o),l=255*n*(1-i*(1-o));n*=255;switch(a){case 0:return[n,l,r];case 1:return[s,n,r];case 2:return[r,n,l];case 3:return[r,s,n];case 4:return[l,r,n];case 5:return[n,r,s]}}function c(t){var e,i,n,a,o=t[0]/360,s=t[1]/100,l=t[2]/100,d=s+l;switch(d>1&&(s/=d,l/=d),n=6*o-(e=Math.floor(6*o)),0!=(1&e)&&(n=1-n),a=s+n*((i=1-l)-s),e){default:case 6:case 0:r=i,g=a,b=s;break;case 1:r=a,g=i,b=s;break;case 2:r=s,g=i,b=a;break;case 3:r=s,g=a,b=i;break;case 4:r=a,g=s,b=i;break;case 5:r=i,g=s,b=a}return[255*r,255*g,255*b]}function f(t){var e=t[0]/100,i=t[1]/100,n=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a))]}function p(t){var e,i,n,a=t[0]/100,o=t[1]/100,r=t[2]/100;return i=-.9689*a+1.8758*o+.0415*r,n=.0557*a+-.204*o+1.057*r,e=(e=3.2406*a+-1.5372*o+-.4986*r)>.0031308?1.055*Math.pow(e,1/2.4)-.055:e*=12.92,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:i*=12.92,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:n*=12.92,[255*(e=Math.min(Math.max(0,e),1)),255*(i=Math.min(Math.max(0,i),1)),255*(n=Math.min(Math.max(0,n),1))]}function m(t){var e=t[0],i=t[1],n=t[2];return i/=100,n/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(e-i),200*(i-(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116))]}function v(t){var e,i,n,a,o=t[0],r=t[1],s=t[2];return o<=8?a=(i=100*o/903.3)/100*7.787+16/116:(i=100*Math.pow((o+16)/116,3),a=Math.pow(i/100,1/3)),[e=e/95.047<=.008856?e=95.047*(r/500+a-16/116)/7.787:95.047*Math.pow(r/500+a,3),i,n=n/108.883<=.008859?n=108.883*(a-s/200-16/116)/7.787:108.883*Math.pow(a-s/200,3)]}function x(t){var e,i=t[0],n=t[1],a=t[2];return(e=360*Math.atan2(a,n)/2/Math.PI)<0&&(e+=360),[i,Math.sqrt(n*n+a*a),e]}function y(t){return p(v(t))}function k(t){var e,i=t[0],n=t[1];return e=t[2]/360*2*Math.PI,[i,n*Math.cos(e),n*Math.sin(e)]}function w(t){return M[t]}var M={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},_={};for(var C in M)_[JSON.stringify(M[C])]=C;var S=function(){return new T};for(var P in e){S[P+"Raw"]=function(t){return function(i){return"number"==typeof i&&(i=Array.prototype.slice.call(arguments)),e[t](i)}}(P);var I=/(\w+)2(\w+)/.exec(P),A=I[1],D=I[2];(S[A]=S[A]||{})[D]=S[P]=function(t){return function(i){"number"==typeof i&&(i=Array.prototype.slice.call(arguments));var n=e[t](i);if("string"==typeof n||void 0===n)return n;for(var a=0;a=0&&e<1?H(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return N(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:N,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return W(t,e);var i=Math.round(t[0]/255*100),n=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+i+"%, "+n+"%, "+a+"%)"},percentaString:W,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return V(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:V,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return j[t.slice(0,3)]}};function O(t){if(t){var e=[0,0,0],i=1,n=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(n){a=(n=n[1])[3];for(var o=0;oi?(e+.05)/(i+.05):(i+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,i=(e[0]+t)%360;return e[0]=i<0?360+i:i,this.setValues("hsl",e),this},mix:function(t,e){var i=t,n=void 0===e?.5:e,a=2*n-1,o=this.alpha()-i.alpha(),r=((a*o==-1?a:(a+o)/(1+a*o))+1)/2,s=1-r;return this.rgb(r*this.red()+s*i.red(),r*this.green()+s*i.green(),r*this.blue()+s*i.blue()).alpha(this.alpha()*n+i.alpha()*(1-n))},toJSON:function(){return this.rgb()},clone:function(){var t,e,i=new Y,n=this.values,a=i.values;for(var o in n)n.hasOwnProperty(o)&&(t=n[o],"[object Array]"===(e={}.toString.call(t))?a[o]=t.slice(0):"[object Number]"===e?a[o]=t:console.error("unexpected color value:",t));return i}},Y.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},Y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},Y.prototype.getValues=function(t){for(var e=this.values,i={},n=0;n=0;a--)e.call(i,t[a],a);else for(a=0;a=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:1===t?1:(i||(i=.3),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),-n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i))},easeOutElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:1===t?1:(i||(i=.3),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),n*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/i)+1)},easeInOutElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:2==(t/=.5)?1:(i||(i=.45),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),t<1?n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*-.5:n*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-Z.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*Z.easeInBounce(2*t):.5*Z.easeOutBounce(2*t-1)+.5}},$={effects:Z};G.easingEffects=Z;var J=Math.PI,Q=J/180,tt=2*J,et=J/2,it=J/4,nt=2*J/3,at={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,i,n,a,o){if(o){var r=Math.min(o,a/2,n/2),s=e+r,l=i+r,d=e+n-r,u=i+a-r;t.moveTo(e,l),se.left-1e-6&&t.xe.top-1e-6&&t.y0&&this.requestAnimationFrame()},advance:function(){for(var t,e,i,n,a=this.animations,o=0;o=i?(ut.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(o,1)):++o}},xt=ut.options.resolve,yt=["push","pop","shift","splice","unshift"];function kt(t,e){var i=t._chartjs;if(i){var n=i.listeners,a=n.indexOf(e);-1!==a&&n.splice(a,1),n.length>0||(yt.forEach(function(e){delete t[e]}),delete t._chartjs)}}var wt=function(t,e){this.initialize(t,e)};ut.extend(wt.prototype,{datasetElementType:null,dataElementType:null,initialize:function(t,e){this.chart=t,this.index=e,this.linkScales(),this.addElements()},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),i=t.getDataset();null!==e.xAxisID&&e.xAxisID in t.chart.scales||(e.xAxisID=i.xAxisID||t.chart.options.scales.xAxes[0].id),null!==e.yAxisID&&e.yAxisID in t.chart.scales||(e.yAxisID=i.yAxisID||t.chart.options.scales.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this.update(!0)},destroy:function(){this._data&&kt(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,i=this.getMeta(),n=this.getDataset().data||[],a=i.data;for(t=0,e=n.length;ti&&this.insertElements(i,n-i)},insertElements:function(t,e){for(var i=0;is;)a-=2*Math.PI;for(;a=r&&a<=s,d=o>=i.innerRadius&&o<=i.outerRadius;return l&&d}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,i=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,i=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},draw:function(){var t,e=this._chart.ctx,i=this._view,n=i.startAngle,a=i.endAngle,o="inner"===i.borderAlign?.33:0;e.save(),e.beginPath(),e.arc(i.x,i.y,Math.max(i.outerRadius-o,0),n,a),e.arc(i.x,i.y,i.innerRadius,a,n,!0),e.closePath(),e.fillStyle=i.backgroundColor,e.fill(),i.borderWidth&&("inner"===i.borderAlign?(e.beginPath(),t=o/i.outerRadius,e.arc(i.x,i.y,i.outerRadius,n-t,a+t),i.innerRadius>o?(t=o/i.innerRadius,e.arc(i.x,i.y,i.innerRadius-o,a+t,n-t,!0)):e.arc(i.x,i.y,o,a+Math.PI/2,n-Math.PI/2),e.closePath(),e.clip(),e.beginPath(),e.arc(i.x,i.y,i.outerRadius,n,a),e.arc(i.x,i.y,i.innerRadius,a,n,!0),e.closePath(),e.lineWidth=2*i.borderWidth,e.lineJoin="round"):(e.lineWidth=i.borderWidth,e.lineJoin="bevel"),e.strokeStyle=i.borderColor,e.stroke()),e.restore()}}),Ct=ut.valueOrDefault,St=st.global.defaultColor;st._set("global",{elements:{line:{tension:.4,backgroundColor:St,borderWidth:3,borderColor:St,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var Pt=pt.extend({draw:function(){var t,e,i,n,a=this._view,o=this._chart.ctx,r=a.spanGaps,s=this._children.slice(),l=st.global,d=l.elements.line,u=-1;for(this._loop&&s.length&&s.push(s[0]),o.save(),o.lineCap=a.borderCapStyle||d.borderCapStyle,o.setLineDash&&o.setLineDash(a.borderDash||d.borderDash),o.lineDashOffset=Ct(a.borderDashOffset,d.borderDashOffset),o.lineJoin=a.borderJoinStyle||d.borderJoinStyle,o.lineWidth=Ct(a.borderWidth,d.borderWidth),o.strokeStyle=a.borderColor||l.defaultColor,o.beginPath(),u=-1,t=0;tt.x&&(e=Ot(e,"left","right")):t.basei?i:n,r:l.right||a<0?0:a>e?e:a,b:l.bottom||o<0?0:o>i?i:o,l:l.left||r<0?0:r>e?e:r}}function Bt(t,e,i){var n=null===e,a=null===i,o=!(!t||n&&a)&&Rt(t);return o&&(n||e>=o.left&&e<=o.right)&&(a||i>=o.top&&i<=o.bottom)}st._set("global",{elements:{rectangle:{backgroundColor:Ft,borderColor:Ft,borderSkipped:"bottom",borderWidth:0}}});var Nt=pt.extend({draw:function(){var t=this._chart.ctx,e=this._view,i=function(t){var e=Rt(t),i=e.right-e.left,n=e.bottom-e.top,a=zt(t,i/2,n/2);return{outer:{x:e.left,y:e.top,w:i,h:n},inner:{x:e.left+a.l,y:e.top+a.t,w:i-a.l-a.r,h:n-a.t-a.b}}}(e),n=i.outer,a=i.inner;t.fillStyle=e.backgroundColor,t.fillRect(n.x,n.y,n.w,n.h),n.w===a.w&&n.h===a.h||(t.save(),t.beginPath(),t.rect(n.x,n.y,n.w,n.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return Bt(this._view,t,e)},inLabelRange:function(t,e){var i=this._view;return Lt(i)?Bt(i,t,null):Bt(i,null,e)},inXRange:function(t){return Bt(this._view,t,null)},inYRange:function(t){return Bt(this._view,null,t)},getCenterPoint:function(){var t,e,i=this._view;return Lt(i)?(t=i.x,e=(i.y+i.base)/2):(t=(i.x+i.base)/2,e=i.y),{x:t,y:e}},getArea:function(){var t=this._view;return Lt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),Wt={},Vt=_t,Et=Pt,Ht=Tt,jt=Nt;Wt.Arc=Vt,Wt.Line=Et,Wt.Point=Ht,Wt.Rectangle=jt;var qt=ut.options.resolve;st._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}});var Yt=Mt.extend({dataElementType:Wt.Rectangle,initialize:function(){var t;Mt.prototype.initialize.apply(this,arguments),(t=this.getMeta()).stack=this.getDataset().stack,t.bar=!0},update:function(t){var e,i,n=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,i=n.length;e0?Math.min(r,n-i):r,i=n;return r}(i,l):-1,pixels:l,start:r,end:s,stackCount:n,scale:i}},calculateBarValuePixels:function(t,e){var i,n,a,o,r,s,l=this.chart,d=this.getMeta(),u=this._getValueScale(),h=u.isHorizontal(),c=l.data.datasets,f=+u.getRightValue(c[t].data[e]),g=u.options.minBarLength,p=u.options.stacked,m=d.stack,v=0;if(p||void 0===p&&void 0!==m)for(i=0;i=0&&a>0)&&(v+=a));return o=u.getPixelForValue(v),s=(r=u.getPixelForValue(v+f))-o,void 0!==g&&Math.abs(s)=0&&!h||f<0&&h?o-g:o+g),{size:s,base:o,head:r,center:r+s/2}},calculateBarIndexPixels:function(t,e,i){var n=i.scale.options,a="flex"===n.barThickness?function(t,e,i){var n,a=e.pixels,o=a[t],r=t>0?a[t-1]:null,s=t');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o'),a[o]&&e.push(a[o]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(i,n){var a=t.getDatasetMeta(0),o=e.datasets[0],r=a.data[n],s=r&&r.custom||{},l=t.options.elements.arc;return{text:i,fillStyle:Gt([s.backgroundColor,o.backgroundColor,l.backgroundColor],void 0,n),strokeStyle:Gt([s.borderColor,o.borderColor,l.borderColor],void 0,n),lineWidth:Gt([s.borderWidth,o.borderWidth,l.borderWidth],void 0,n),hidden:isNaN(o.data[n])||a.data[n].hidden,index:n}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i=Math.PI?-1:m<-Math.PI?1:0))+g,b={x:Math.cos(m),y:Math.sin(m)},x={x:Math.cos(v),y:Math.sin(v)},y=m<=0&&v>=0||m<=2*Math.PI&&2*Math.PI<=v,k=m<=.5*Math.PI&&.5*Math.PI<=v||m<=2.5*Math.PI&&2.5*Math.PI<=v,w=m<=-Math.PI&&-Math.PI<=v||m<=Math.PI&&Math.PI<=v,M=m<=.5*-Math.PI&&.5*-Math.PI<=v||m<=1.5*Math.PI&&1.5*Math.PI<=v,_=f/100,C={x:w?-1:Math.min(b.x*(b.x<0?1:_),x.x*(x.x<0?1:_)),y:M?-1:Math.min(b.y*(b.y<0?1:_),x.y*(x.y<0?1:_))},S={x:y?1:Math.max(b.x*(b.x>0?1:_),x.x*(x.x>0?1:_)),y:k?1:Math.max(b.y*(b.y>0?1:_),x.y*(x.y>0?1:_))},P={width:.5*(S.x-C.x),height:.5*(S.y-C.y)};d=Math.min(s/P.width,l/P.height),u={x:-.5*(S.x+C.x),y:-.5*(S.y+C.y)}}for(e=0,i=c.length;e0&&!isNaN(t)?2*Math.PI*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,i,n,a,o,r,s,l,d=0,u=this.chart;if(!t)for(e=0,i=u.data.datasets.length;e(d=s>d?s:d)?l:d);return d},setHoverStyle:function(t){var e=t._model,i=t._options,n=ut.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Zt(i.hoverBackgroundColor,n(i.backgroundColor)),e.borderColor=Zt(i.hoverBorderColor,n(i.borderColor)),e.borderWidth=Zt(i.hoverBorderWidth,i.borderWidth)},_resolveElementOptions:function(t,e){var i,n,a,o=this.chart,r=this.getDataset(),s=t.custom||{},l=o.options.elements.arc,d={},u={chart:o,dataIndex:e,dataset:r,datasetIndex:this.index},h=["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"];for(i=0,n=h.length;i0&&ee(l[t-1]._model,s)&&(i.controlPointPreviousX=d(i.controlPointPreviousX,s.left,s.right),i.controlPointPreviousY=d(i.controlPointPreviousY,s.top,s.bottom)),t');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o'),a[o]&&e.push(a[o]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(i,n){var a=t.getDatasetMeta(0),o=e.datasets[0],r=a.data[n].custom||{},s=t.options.elements.arc;return{text:i,fillStyle:ae([r.backgroundColor,o.backgroundColor,s.backgroundColor],void 0,n),strokeStyle:ae([r.borderColor,o.borderColor,s.borderColor],void 0,n),lineWidth:ae([r.borderWidth,o.borderWidth,s.borderWidth],void 0,n),hidden:isNaN(o.data[n])||a.data[n].hidden,index:n}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i0&&(o=t.getDatasetMeta(o[0]._datasetIndex).data),o},"x-axis":function(t,e){return me(t,e,{intersect:!1})},point:function(t,e){return fe(t,he(e,t))},nearest:function(t,e,i){var n=he(e,t);i.axis=i.axis||"xy";var a=pe(i.axis);return ge(t,n,i.intersect,a)},x:function(t,e,i){var n=he(e,t),a=[],o=!1;return ce(t,function(t){t.inXRange(n.x)&&a.push(t),t.inRange(n.x,n.y)&&(o=!0)}),i.intersect&&!o&&(a=[]),a},y:function(t,e,i){var n=he(e,t),a=[],o=!1;return ce(t,function(t){t.inYRange(n.y)&&a.push(t),t.inRange(n.x,n.y)&&(o=!0)}),i.intersect&&!o&&(a=[]),a}}};function be(t,e){return ut.where(t,function(t){return t.position===e})}function xe(t,e){t.forEach(function(t,e){return t._tmpIndex_=e,t}),t.sort(function(t,i){var n=e?i:t,a=e?t:i;return n.weight===a.weight?n._tmpIndex_-a._tmpIndex_:n.weight-a.weight}),t.forEach(function(t){delete t._tmpIndex_})}function ye(t,e){ut.each(t,function(t){e[t.position]+=t.isHorizontal()?t.height:t.width})}st._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var ke={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,t.boxes.push(e)},removeBox:function(t,e){var i=t.boxes?t.boxes.indexOf(e):-1;-1!==i&&t.boxes.splice(i,1)},configure:function(t,e,i){for(var n,a=["fullWidth","position","weight"],o=a.length,r=0;rdiv{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&we.default||we,_e="$chartjs",Ce="chartjs-size-monitor",Se="chartjs-render-monitor",Pe="chartjs-render-animation",Ie=["animationstart","webkitAnimationStart"],Ae={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function De(t,e){var i=ut.getStyle(t,e),n=i&&i.match(/^(\d+)(\.\d+)?px$/);return n?Number(n[1]):void 0}var Te=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Fe(t,e,i){t.addEventListener(e,i,Te)}function Le(t,e,i){t.removeEventListener(e,i,Te)}function Re(t,e,i,n,a){return{type:t,chart:e,native:a||null,x:void 0!==i?i:null,y:void 0!==n?n:null}}function Oe(t){var e=document.createElement("div");return e.className=t||"",e}function ze(t,e,i){var n,a,o,r,s=t[_e]||(t[_e]={}),l=s.resizer=function(t){var e=Oe(Ce),i=Oe(Ce+"-expand"),n=Oe(Ce+"-shrink");i.appendChild(Oe()),n.appendChild(Oe()),e.appendChild(i),e.appendChild(n),e._reset=function(){i.scrollLeft=1e6,i.scrollTop=1e6,n.scrollLeft=1e6,n.scrollTop=1e6};var a=function(){e._reset(),t()};return Fe(i,"scroll",a.bind(i,"expand")),Fe(n,"scroll",a.bind(n,"shrink")),e}((n=function(){if(s.resizer){var n=i.options.maintainAspectRatio&&t.parentNode,a=n?n.clientWidth:0;e(Re("resize",i)),n&&n.clientWidth0){var o=t[0];o.label?i=o.label:o.xLabel?i=o.xLabel:a>0&&o.index-1?t.split("\n"):t}function Xe(t){var e=st.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,bodyFontColor:t.bodyFontColor,_bodyFontFamily:je(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:je(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:je(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:je(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:je(t.titleFontStyle,e.defaultFontStyle),titleFontSize:je(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:je(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:je(t.footerFontStyle,e.defaultFontStyle),footerFontSize:je(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function Ke(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function Ge(t){return Ye([],Ue(t))}var Ze=pt.extend({initialize:function(){this._model=Xe(this._options),this._lastActive=[]},getTitle:function(){var t=this._options.callbacks,e=t.beforeTitle.apply(this,arguments),i=t.title.apply(this,arguments),n=t.afterTitle.apply(this,arguments),a=[];return a=Ye(a,Ue(e)),a=Ye(a,Ue(i)),a=Ye(a,Ue(n))},getBeforeBody:function(){return Ge(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var i=this,n=i._options.callbacks,a=[];return ut.each(t,function(t){var o={before:[],lines:[],after:[]};Ye(o.before,Ue(n.beforeLabel.call(i,t,e))),Ye(o.lines,n.label.call(i,t,e)),Ye(o.after,Ue(n.afterLabel.call(i,t,e))),a.push(o)}),a},getAfterBody:function(){return Ge(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this._options.callbacks,e=t.beforeFooter.apply(this,arguments),i=t.footer.apply(this,arguments),n=t.afterFooter.apply(this,arguments),a=[];return a=Ye(a,Ue(e)),a=Ye(a,Ue(i)),a=Ye(a,Ue(n))},update:function(t){var e,i,n,a,o,r,s,l,d,u,h=this,c=h._options,f=h._model,g=h._model=Xe(c),p=h._active,m=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var k=[],w=[];y=qe[c.position].call(h,p,h._eventPosition);var M=[];for(e=0,i=p.length;en.width&&(a=n.width-e.width),a<0&&(a=0)),"top"===u?o+=h:o-="bottom"===u?e.height+h:e.height/2,"center"===u?"left"===d?a+=h:"right"===d&&(a-=h):"left"===d?a-=c:"right"===d&&(a+=c),{x:a,y:o}}(g,x,v=function(t,e){var i,n,a,o,r,s=t._model,l=t._chart,d=t._chart.chartArea,u="center",h="center";s.yl.height-e.height&&(h="bottom");var c=(d.left+d.right)/2,f=(d.top+d.bottom)/2;"center"===h?(i=function(t){return t<=c},n=function(t){return t>c}):(i=function(t){return t<=e.width/2},n=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},o=function(t){return t-e.width-s.caretSize-s.caretPadding<0},r=function(t){return t<=f?"top":"bottom"},i(s.x)?(u="left",a(s.x)&&(u="center",h=r(s.y))):n(s.x)&&(u="right",o(s.x)&&(u="center",h=r(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:u,yAlign:g.yAlign?g.yAlign:h}}(this,x),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var i=this._chart.ctx,n=this._view,a=this.getCaretPosition(t,e,n);i.lineTo(a.x1,a.y1),i.lineTo(a.x2,a.y2),i.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,i){var n,a,o,r,s,l,d=i.caretSize,u=i.cornerRadius,h=i.xAlign,c=i.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===c)s=g+m/2,"left"===h?(a=(n=f)-d,o=n,r=s+d,l=s-d):(a=(n=f+p)+d,o=n,r=s-d,l=s+d);else if("left"===h?(n=(a=f+u+d)-d,o=a+d):"right"===h?(n=(a=f+p-u-d)-d,o=a+d):(n=(a=i.caretX)-d,o=a+d),"top"===c)s=(r=g)-d,l=r;else{s=(r=g+m)+d,l=r;var v=o;o=n,n=v}return{x1:n,x2:a,x3:o,y1:r,y2:s,y3:l}},drawTitle:function(t,e,i){var n=e.title;if(n.length){t.x=Ke(e,e._titleAlign),i.textAlign=e._titleAlign,i.textBaseline="top";var a,o,r=e.titleFontSize,s=e.titleSpacing;for(i.fillStyle=e.titleFontColor,i.font=ut.fontString(r,e._titleFontStyle,e._titleFontFamily),a=0,o=n.length;a0&&i.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var i={width:e.width,height:e.height},n={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,o=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&o&&(t.save(),t.globalAlpha=a,this.drawBackground(n,e,t,i),n.y+=e.yPadding,this.drawTitle(n,e,t),this.drawBody(n,e,t),this.drawFooter(n,e,t),t.restore())}},handleEvent:function(t){var e,i=this,n=i._options;return i._lastActive=i._lastActive||[],"mouseout"===t.type?i._active=[]:i._active=i._chart.getElementsAtEventForMode(t,n.mode,n),(e=!ut.arrayEquals(i._active,i._lastActive))&&(i._lastActive=i._active,(n.enabled||n.custom)&&(i._eventPosition={x:t.x,y:t.y},i.update(!0),i.pivot())),e}}),$e=qe,Je=Ze;Je.positioners=$e;var Qe=ut.valueOrDefault;function ti(){return ut.merge({},[].slice.call(arguments),{merger:function(t,e,i,n){if("xAxes"===t||"yAxes"===t){var a,o,r,s=i[t].length;for(e[t]||(e[t]=[]),a=0;a=e[t].length&&e[t].push({}),!e[t][a].type||r.type&&r.type!==e[t][a].type?ut.merge(e[t][a],[He.getScaleDefaults(o),r]):ut.merge(e[t][a],r)}else ut._merger(t,e,i,n)}})}function ei(){return ut.merge({},[].slice.call(arguments),{merger:function(t,e,i,n){var a=e[t]||{},o=i[t];"scales"===t?e[t]=ti(a,o):"scale"===t?e[t]=ut.merge(a,[He.getScaleDefaults(o.type),o]):ut._merger(t,e,i,n)}})}function ii(t){return"top"===t||"bottom"===t}st._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var ni=function(t,e){return this.construct(t,e),this};ut.extend(ni.prototype,{construct:function(t,e){var i=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=ei(st.global,st[t.type],t.options||{}),t}(e);var n=Ve.acquireContext(t,e),a=n&&n.canvas,o=a&&a.height,r=a&&a.width;i.id=ut.uid(),i.ctx=n,i.canvas=a,i.config=e,i.width=r,i.height=o,i.aspectRatio=o?r/o:null,i.options=e.options,i._bufferedRender=!1,i.chart=i,i.controller=i,ni.instances[i.id]=i,Object.defineProperty(i,"data",{get:function(){return i.config.data},set:function(t){i.config.data=t}}),n&&a?(i.initialize(),i.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Ee.notify(t,"beforeInit"),ut.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.initToolTip(),Ee.notify(t,"afterInit"),t},clear:function(){return ut.canvas.clear(this),this},stop:function(){return bt.cancelAnimation(this),this},resize:function(t){var e=this,i=e.options,n=e.canvas,a=i.maintainAspectRatio&&e.aspectRatio||null,o=Math.max(0,Math.floor(ut.getMaximumWidth(n))),r=Math.max(0,Math.floor(a?o/a:ut.getMaximumHeight(n)));if((e.width!==o||e.height!==r)&&(n.width=e.width=o,n.height=e.height=r,n.style.width=o+"px",n.style.height=r+"px",ut.retinaScale(e,i.devicePixelRatio),!t)){var s={width:o,height:r};Ee.notify(e,"resize",[s]),i.onResize&&i.onResize(e,s),e.stop(),e.update({duration:i.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},i=t.scale;ut.each(e.xAxes,function(t,e){t.id=t.id||"x-axis-"+e}),ut.each(e.yAxes,function(t,e){t.id=t.id||"y-axis-"+e}),i&&(i.id=i.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,i=t.scales||{},n=[],a=Object.keys(i).reduce(function(t,e){return t[e]=!1,t},{});e.scales&&(n=n.concat((e.scales.xAxes||[]).map(function(t){return{options:t,dtype:"category",dposition:"bottom"}}),(e.scales.yAxes||[]).map(function(t){return{options:t,dtype:"linear",dposition:"left"}}))),e.scale&&n.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),ut.each(n,function(e){var n=e.options,o=n.id,r=Qe(n.type,e.dtype);ii(n.position)!==ii(e.dposition)&&(n.position=e.dposition),a[o]=!0;var s=null;if(o in i&&i[o].type===r)(s=i[o]).options=n,s.ctx=t.ctx,s.chart=t;else{var l=He.getScaleConstructor(r);if(!l)return;s=new l({id:o,type:r,options:n,ctx:t.ctx,chart:t}),i[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)}),ut.each(a,function(t,e){t||delete i[e]}),t.scales=i,He.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t=this,e=[];return ut.each(t.data.datasets,function(i,n){var a=t.getDatasetMeta(n),o=i.type||t.config.type;if(a.type&&a.type!==o&&(t.destroyDatasetMeta(n),a=t.getDatasetMeta(n)),a.type=o,a.controller)a.controller.updateIndex(n),a.controller.linkScales();else{var r=ue[a.type];if(void 0===r)throw new Error('"'+a.type+'" is not a chart type.');a.controller=new r(t,n),e.push(a.controller)}},t),e},resetElements:function(){var t=this;ut.each(t.data.datasets,function(e,i){t.getDatasetMeta(i).controller.reset()},t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,i,n=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),i=(e=n).options,ut.each(e.scales,function(t){ke.removeBox(e,t)}),i=ei(st.global,st[e.config.type],i),e.options=e.config.options=i,e.ensureScalesHaveIDs(),e.buildOrUpdateScales(),e.tooltip._options=i.tooltips,e.tooltip.initialize(),Ee._invalidate(n),!1!==Ee.notify(n,"beforeUpdate")){n.tooltip._data=n.data;var a=n.buildOrUpdateControllers();ut.each(n.data.datasets,function(t,e){n.getDatasetMeta(e).controller.buildOrUpdateElements()},n),n.updateLayout(),n.options.animation&&n.options.animation.duration&&ut.each(a,function(t){t.reset()}),n.updateDatasets(),n.tooltip.initialize(),n.lastActive=[],Ee.notify(n,"afterUpdate"),n._bufferedRender?n._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:n.render(t)}},updateLayout:function(){!1!==Ee.notify(this,"beforeLayout")&&(ke.update(this,this.width,this.height),Ee.notify(this,"afterScaleUpdate"),Ee.notify(this,"afterLayout"))},updateDatasets:function(){if(!1!==Ee.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t=0;--i)e.isDatasetVisible(i)&&e.drawDataset(i,t);Ee.notify(e,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var i=this.getDatasetMeta(t),n={meta:i,index:t,easingValue:e};!1!==Ee.notify(this,"beforeDatasetDraw",[n])&&(i.controller.draw(e),Ee.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,i={tooltip:e,easingValue:t};!1!==Ee.notify(this,"beforeTooltipDraw",[i])&&(e.draw(),Ee.notify(this,"afterTooltipDraw",[i]))},getElementAtEvent:function(t){return ve.modes.single(this,t)},getElementsAtEvent:function(t){return ve.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return ve.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,i){var n=ve.modes[e];return"function"==typeof n?n(this,t,i):[]},getDatasetAtEvent:function(t){return ve.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var i=e._meta[this.id];return i||(i=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null}),i},getVisibleDatasetCount:function(){for(var t=0,e=0,i=this.data.datasets.length;e3?i[2]-i[1]:i[1]-i[0];Math.abs(n)>1&&t!==Math.floor(t)&&(n=t-Math.floor(t));var a=ut.log10(Math.abs(n)),o="";if(0!==t)if(Math.max(Math.abs(i[0]),Math.abs(i[i.length-1]))<1e-4){var r=ut.log10(Math.abs(t));o=t.toExponential(Math.floor(r)-Math.floor(a))}else{var s=-1*Math.floor(a);s=Math.max(Math.min(s,20),0),o=t.toFixed(s)}else o="0";return o},logarithmic:function(t,e,i){var n=t/Math.pow(10,Math.floor(ut.log10(t)));return 0===t?"0":1===n||2===n||5===n||0===e||e===i.length-1?t.toExponential():""}}},di=ut.valueOrDefault,ui=ut.valueAtIndexOrDefault;function hi(t){var e,i,n=[];for(e=0,i=t.length;ed&&ot.maxHeight){o--;break}o++,l=r*s}t.labelRotation=o},afterCalculateTickRotation:function(){ut.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){ut.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},i=hi(t._ticks),n=t.options,a=n.ticks,o=n.scaleLabel,r=n.gridLines,s=t._isVisible(),l=n.position,d=t.isHorizontal(),u=ut.options._parseFont,h=u(a),c=n.gridLines.tickMarkLength;if(e.width=d?t.isFullWidth()?t.maxWidth-t.margins.left-t.margins.right:t.maxWidth:s&&r.drawTicks?c:0,e.height=d?s&&r.drawTicks?c:0:t.maxHeight,o.display&&s){var f=u(o),g=ut.options.toPadding(o.padding),p=f.lineHeight+g.height;d?e.height+=p:e.width+=p}if(a.display&&s){var m=ut.longestText(t.ctx,h.string,i,t.longestTextCache),v=ut.numberOfLabelLines(i),b=.5*h.size,x=t.options.ticks.padding;if(t._maxLabelLines=v,t.longestLabelWidth=m,d){var y=ut.toRadians(t.labelRotation),k=Math.cos(y),w=Math.sin(y)*m+h.lineHeight*v+b;e.height=Math.min(t.maxHeight,e.height+w+x),t.ctx.font=h.string;var M,_,C=ci(t.ctx,i[0],h.string),S=ci(t.ctx,i[i.length-1],h.string),P=t.getPixelForTick(0)-t.left,I=t.right-t.getPixelForTick(i.length-1);0!==t.labelRotation?(M="bottom"===l?k*C:k*b,_="bottom"===l?k*b:k*S):(M=C/2,_=S/2),t.paddingLeft=Math.max(M-P,0)+3,t.paddingRight=Math.max(_-I,0)+3}else a.mirror?m=0:m+=x+b,e.width=Math.min(t.maxWidth,e.width+m),t.paddingTop=h.size/2,t.paddingBottom=h.size/2}t.handleMargins(),t.width=e.width,t.height=e.height},handleMargins:function(){var t=this;t.margins&&(t.paddingLeft=Math.max(t.paddingLeft-t.margins.left,0),t.paddingTop=Math.max(t.paddingTop-t.margins.top,0),t.paddingRight=Math.max(t.paddingRight-t.margins.right,0),t.paddingBottom=Math.max(t.paddingBottom-t.margins.bottom,0))},afterFit:function(){ut.callback(this.options.afterFit,[this])},isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ut.isNullOrUndef(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},getLabelForIndex:ut.noop,getPixelForValue:ut.noop,getValueForPixel:ut.noop,getPixelForTick:function(t){var e=this,i=e.options.offset;if(e.isHorizontal()){var n=(e.width-(e.paddingLeft+e.paddingRight))/Math.max(e._ticks.length-(i?0:1),1),a=n*t+e.paddingLeft;i&&(a+=n/2);var o=e.left+a;return o+=e.isFullWidth()?e.margins.left:0}var r=e.height-(e.paddingTop+e.paddingBottom);return e.top+t*(r/(e._ticks.length-1))},getPixelForDecimal:function(t){var e=this;if(e.isHorizontal()){var i=(e.width-(e.paddingLeft+e.paddingRight))*t+e.paddingLeft,n=e.left+i;return n+=e.isFullWidth()?e.margins.left:0}return e.top+t*e.height},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,i,n=this,a=n.isHorizontal(),o=n.options.ticks.minor,r=t.length,s=!1,l=o.maxTicksLimit,d=n._tickSize()*(r-1),u=a?n.width-(n.paddingLeft+n.paddingRight):n.height-(n.paddingTop+n.PaddingBottom),h=[];for(d>u&&(s=1+Math.floor(d/u)),r>l&&(s=Math.max(s,1+Math.floor(r/l))),e=0;e1&&e%s>0&&delete i.label,h.push(i);return h},_tickSize:function(){var t=this,e=t.isHorizontal(),i=t.options.ticks.minor,n=ut.toRadians(t.labelRotation),a=Math.abs(Math.cos(n)),o=Math.abs(Math.sin(n)),r=i.autoSkipPadding||0,s=t.longestLabelWidth+r||0,l=ut.options._parseFont(i),d=t._maxLabelLines*l.lineHeight+r||0;return e?d*a>s*o?s/a:d/o:d*o0&&n>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,o=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==o&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,i=e.stepSize,n=e.maxTicksLimit;return i?t=Math.ceil(this.max/i)-Math.floor(this.min/i)+1:(t=this._computeTickLimit(),n=n||11),n&&(t=Math.min(n,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:mi,buildTicks:function(){var t=this,e=t.options.ticks,i=t.getTickLimit(),n={maxTicks:i=Math.max(2,i),min:e.min,max:e.max,precision:e.precision,stepSize:ut.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var i,n,a,o,r=[],s=t.stepSize,l=s||1,d=t.maxTicks-1,u=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,p=ut.niceNum((g-f)/d/l)*l;if(p<1e-14&&vi(u)&&vi(h))return[f,g];(o=Math.ceil(g/p)-Math.floor(f/p))>d&&(p=ut.niceNum(o*p/d/l)*l),s||vi(c)?i=Math.pow(10,ut._decimalPlaces(p)):(i=Math.pow(10,c),p=Math.ceil(p*i)/i),n=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,s&&(!vi(u)&&ut.almostWhole(u/p,p/1e3)&&(n=u),!vi(h)&&ut.almostWhole(h/p,p/1e3)&&(a=h)),o=(a-n)/p,o=ut.almostEquals(o,Math.round(o),p/1e3)?Math.round(o):Math.ceil(o),n=Math.round(n*i)/i,a=Math.round(a*i)/i,r.push(vi(u)?n:u);for(var m=1;mt.max&&(t.max=n))})});t.min=isFinite(t.min)&&!isNaN(t.min)?t.min:0,t.max=isFinite(t.max)&&!isNaN(t.max)?t.max:1,this.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=ut.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e=this,i=e.start,n=+e.getRightValue(t),a=e.end-i;return e.isHorizontal()?e.left+e.width/a*(n-i):e.bottom-e.height/a*(n-i)},getValueForPixel:function(t){var e=this,i=e.isHorizontal(),n=i?e.width:e.height,a=(i?t-e.left:e.bottom-t)/n;return e.start+(e.end-e.start)*a},getPixelForTick:function(t){return this.getPixelForValue(this.ticksAsNumbers[t])}}),ki=xi;yi._defaults=ki;var wi=ut.valueOrDefault;var Mi={position:"left",ticks:{callback:li.formatters.logarithmic}};function _i(t,e){return ut.isFinite(t)&&t>=0?t:e}var Ci=fi.extend({determineDataLimits:function(){var t=this,e=t.options,i=t.chart,n=i.data.datasets,a=t.isHorizontal();function o(e){return a?e.xAxisID===t.id:e.yAxisID===t.id}t.min=null,t.max=null,t.minNotZero=null;var r=e.stacked;if(void 0===r&&ut.each(n,function(t,e){if(!r){var n=i.getDatasetMeta(e);i.isDatasetVisible(e)&&o(n)&&void 0!==n.stack&&(r=!0)}}),e.stacked||r){var s={};ut.each(n,function(n,a){var r=i.getDatasetMeta(a),l=[r.type,void 0===e.stacked&&void 0===r.stack?a:"",r.stack].join(".");i.isDatasetVisible(a)&&o(r)&&(void 0===s[l]&&(s[l]=[]),ut.each(n.data,function(e,i){var n=s[l],a=+t.getRightValue(e);isNaN(a)||r.data[i].hidden||a<0||(n[i]=n[i]||0,n[i]+=a)}))}),ut.each(s,function(e){if(e.length>0){var i=ut.min(e),n=ut.max(e);t.min=null===t.min?i:Math.min(t.min,i),t.max=null===t.max?n:Math.max(t.max,n)}})}else ut.each(n,function(e,n){var a=i.getDatasetMeta(n);i.isDatasetVisible(n)&&o(a)&&ut.each(e.data,function(e,i){var n=+t.getRightValue(e);isNaN(n)||a.data[i].hidden||n<0||(null===t.min?t.min=n:nt.max&&(t.max=n),0!==n&&(null===t.minNotZero||n0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(ut.log10(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,i=!t.isHorizontal(),n={min:_i(e.min),max:_i(e.max)},a=t.ticks=function(t,e){var i,n,a=[],o=wi(t.min,Math.pow(10,Math.floor(ut.log10(e.min)))),r=Math.floor(ut.log10(e.max)),s=Math.ceil(e.max/Math.pow(10,r));0===o?(i=Math.floor(ut.log10(e.minNotZero)),n=Math.floor(e.minNotZero/Math.pow(10,i)),a.push(o),o=n*Math.pow(10,i)):(i=Math.floor(ut.log10(o)),n=Math.floor(o/Math.pow(10,i)));var l=i<0?Math.pow(10,Math.abs(i)):1;do{a.push(o),10==++n&&(n=1,l=++i>=0?1:l),o=Math.round(n*Math.pow(10,i)*l)/l}while(ia?{start:e-i,end:e}:{start:e,end:e+i}}function Ri(t){return 0===t||180===t?"center":t<180?"left":"right"}function Oi(t,e,i,n){var a,o,r=i.y+n/2;if(ut.isArray(e))for(a=0,o=e.length;a270||t<90)&&(i.y-=e.h)}function Bi(t){return ut.isNumber(t)?t:0}var Ni=bi.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Fi(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,i=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY;ut.each(e.data.datasets,function(a,o){if(e.isDatasetVisible(o)){var r=e.getDatasetMeta(o);ut.each(a.data,function(e,a){var o=+t.getRightValue(e);isNaN(o)||r.data[a].hidden||(i=Math.min(o,i),n=Math.max(o,n))})}}),t.min=i===Number.POSITIVE_INFINITY?0:i,t.max=n===Number.NEGATIVE_INFINITY?0:n,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Fi(this.options))},convertTicksToLabels:function(){var t=this;bi.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map(t.options.pointLabels.callback,t)},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,i,n,a=ut.options._parseFont(t.options.pointLabels),o={l:0,r:t.width,t:0,b:t.height-t.paddingTop},r={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,d,u=Ti(t);for(e=0;eo.r&&(o.r=f.end,r.r=h),g.starto.b&&(o.b=g.end,r.b=h)}t.setReductions(t.drawingArea,o,r)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,i){var n=this,a=e.l/Math.sin(i.l),o=Math.max(e.r-n.width,0)/Math.sin(i.r),r=-e.t/Math.cos(i.t),s=-Math.max(e.b-(n.height-n.paddingTop),0)/Math.cos(i.b);a=Bi(a),o=Bi(o),r=Bi(r),s=Bi(s),n.drawingArea=Math.min(Math.floor(t-(a+o)/2),Math.floor(t-(r+s)/2)),n.setCenterPoint(a,o,r,s)},setCenterPoint:function(t,e,i,n){var a=this,o=a.width-e-a.drawingArea,r=t+a.drawingArea,s=i+a.drawingArea,l=a.height-a.paddingTop-n-a.drawingArea;a.xCenter=Math.floor((r+o)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){return t*(2*Math.PI/Ti(this))+(this.chart.options&&this.chart.options.startAngle?this.chart.options.startAngle:0)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(null===t)return 0;var i=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*i:(t-e.min)*i},getPointPosition:function(t,e){var i=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(i)*e+this.xCenter,y:Math.sin(i)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(){var t=this.min,e=this.max;return this.getPointPositionForValue(0,this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0)},draw:function(){var t=this,e=t.options,i=e.gridLines,n=e.ticks;if(e.display){var a=t.ctx,o=this.getIndexAngle(0),r=ut.options._parseFont(n);(e.angleLines.display||e.pointLabels.display)&&function(t){var e=t.ctx,i=t.options,n=i.angleLines,a=i.gridLines,o=i.pointLabels,r=Pi(n.lineWidth,a.lineWidth),s=Pi(n.color,a.color),l=Fi(i);e.save(),e.lineWidth=r,e.strokeStyle=s,e.setLineDash&&(e.setLineDash(Ai([n.borderDash,a.borderDash,[]])),e.lineDashOffset=Ai([n.borderDashOffset,a.borderDashOffset,0]));var d=t.getDistanceFromCenterForValue(i.ticks.reverse?t.min:t.max),u=ut.options._parseFont(o);e.font=u.string,e.textBaseline="middle";for(var h=Ti(t)-1;h>=0;h--){if(n.display&&r&&s){var c=t.getPointPosition(h,d);e.beginPath(),e.moveTo(t.xCenter,t.yCenter),e.lineTo(c.x,c.y),e.stroke()}if(o.display){var f=0===h?l/2:0,g=t.getPointPosition(h,d+f+5),p=Ii(o.fontColor,h,st.global.defaultFontColor);e.fillStyle=p;var m=t.getIndexAngle(h),v=ut.toDegrees(m);e.textAlign=Ri(v),zi(v,t._pointLabelSizes[h],g),Oi(e,t.pointLabels[h]||"",g,u.lineHeight)}}e.restore()}(t),ut.each(t.ticks,function(e,s){if(s>0||n.reverse){var l=t.getDistanceFromCenterForValue(t.ticksAsNumbers[s]);if(i.display&&0!==s&&function(t,e,i,n){var a,o=t.ctx,r=e.circular,s=Ti(t),l=Ii(e.color,n-1),d=Ii(e.lineWidth,n-1);if((r||s)&&l&&d){if(o.save(),o.strokeStyle=l,o.lineWidth=d,o.setLineDash&&(o.setLineDash(e.borderDash||[]),o.lineDashOffset=e.borderDashOffset||0),o.beginPath(),r)o.arc(t.xCenter,t.yCenter,i,0,2*Math.PI);else{a=t.getPointPosition(0,i),o.moveTo(a.x,a.y);for(var u=1;u=0&&r<=s;){if(a=t[(n=r+s>>1)-1]||null,o=t[n],!a)return{lo:null,hi:o};if(o[e]i))return{lo:a,hi:o};s=n-1}}return{lo:o,hi:null}}(t,e,i),o=a.lo?a.hi?a.lo:t[t.length-2]:t[0],r=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=r[e]-o[e],l=s?(i-o[e])/s:0,d=(r[n]-o[n])*l;return o[n]+d}function Ki(t,e){var i=t._adapter,n=t.options.time,a=n.parser,o=a||n.format,r=e;return"function"==typeof a&&(r=a(r)),ut.isFinite(r)||(r="string"==typeof o?i.parse(r,o):i.parse(r)),null!==r?+r:(a||"function"!=typeof o||(r=o(e),ut.isFinite(r)||(r=i.parse(r))),r)}function Gi(t,e){if(ut.isNullOrUndef(e))return null;var i=t.options.time,n=Ki(t,t.getRightValue(e));return null===n?n:(i.round&&(n=+t._adapter.startOf(n,i.round)),n)}function Zi(t){for(var e=qi.indexOf(t)+1,i=qi.length;e=a&&i<=o&&d.push(i);return n.min=a,n.max=o,n._unit=s.unit||function(t,e,i,n,a){var o,r;for(o=qi.length-1;o>=qi.indexOf(i);o--)if(r=qi[o],ji[r].common&&t._adapter.diff(a,n,r)>=e.length)return r;return qi[i?qi.indexOf(i):0]}(n,d,s.minUnit,n.min,n.max),n._majorUnit=Zi(n._unit),n._table=function(t,e,i,n){if("linear"===n||!t.length)return[{time:e,pos:0},{time:i,pos:1}];var a,o,r,s,l,d=[],u=[e];for(a=0,o=t.length;ae&&s=0&&t0?r:1}}),Qi={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,format:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};Ji._defaults=Qi;var tn={category:gi,linear:yi,logarithmic:Ci,radialLinear:Ni,time:Ji},en={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};si._date.override("function"==typeof t?{_id:"moment",formats:function(){return en},parse:function(e,i){return"string"==typeof e&&"string"==typeof i?e=t(e,i):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,i){return t(e).format(i)},add:function(e,i,n){return t(e).add(i,n).valueOf()},diff:function(e,i,n){return t.duration(t(e).diff(t(i))).as(n)},startOf:function(e,i,n){return e=t(e),"isoWeek"===i?e.isoWeekday(n).valueOf():e.startOf(i).valueOf()},endOf:function(e,i){return t(e).endOf(i).valueOf()},_create:function(e){return t(e)}}:{}),st._set("global",{plugins:{filler:{propagate:!0}}});var nn={dataset:function(t){var e=t.fill,i=t.chart,n=i.getDatasetMeta(e),a=n&&i.isDatasetVisible(e)&&n.dataset._children||[],o=a.length||0;return o?function(t,e){return e=i)&&n;switch(o){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return o;default:return!1}}function on(t){var e,i=t.el._model||{},n=t.el._scale||{},a=t.fill,o=null;if(isFinite(a))return null;if("start"===a?o=void 0===i.scaleBottom?n.bottom:i.scaleBottom:"end"===a?o=void 0===i.scaleTop?n.top:i.scaleTop:void 0!==i.scaleZero?o=i.scaleZero:n.getBasePosition?o=n.getBasePosition():n.getBasePixel&&(o=n.getBasePixel()),null!=o){if(void 0!==o.x&&void 0!==o.y)return o;if(ut.isFinite(o))return{x:(e=n.isHorizontal())?o:null,y:e?null:o}}return null}function rn(t,e,i){var n,a=t[e].fill,o=[e];if(!i)return a;for(;!1!==a&&-1===o.indexOf(a);){if(!isFinite(a))return a;if(!(n=t[a]))return!1;if(n.visible)return a;o.push(a),a=n.fill}return!1}function sn(t){var e=t.fill,i="dataset";return!1===e?null:(isFinite(e)||(i="boundary"),nn[i](t))}function ln(t){return t&&!t.skip}function dn(t,e,i,n,a){var o;if(n&&a){for(t.moveTo(e[0].x,e[0].y),o=1;o0;--o)ut.canvas.lineTo(t,i[o],i[o-1],!0)}}var un={id:"filler",afterDatasetsUpdate:function(t,e){var i,n,a,o,r=(t.data.datasets||[]).length,s=e.propagate,l=[];for(n=0;ne?e:t.boxWidth}st._set("global",{legend:{display:!0,position:"top",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var i=e.datasetIndex,n=this.chart,a=n.getDatasetMeta(i);a.hidden=null===a.hidden?!n.data.datasets[i].hidden:null,n.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data;return ut.isArray(e.datasets)?e.datasets.map(function(e,i){return{text:e.label,fillStyle:ut.isArray(e.backgroundColor)?e.backgroundColor[0]:e.backgroundColor,hidden:!t.isDatasetVisible(i),lineCap:e.borderCapStyle,lineDash:e.borderDash,lineDashOffset:e.borderDashOffset,lineJoin:e.borderJoinStyle,lineWidth:e.borderWidth,strokeStyle:e.borderColor,pointStyle:e.pointStyle,datasetIndex:i}},this):[]}}},legendCallback:function(t){var e=[];e.push('
    ');for(var i=0;i'),t.data.datasets[i].label&&e.push(t.data.datasets[i].label),e.push("");return e.push("
"),e.join("")}});var gn=pt.extend({initialize:function(t){ut.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:hn,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:hn,beforeSetDimensions:hn,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:hn,beforeBuildLabels:hn,buildLabels:function(){var t=this,e=t.options.labels||{},i=ut.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(i=i.filter(function(i){return e.filter(i,t.chart.data)})),t.options.reverse&&i.reverse(),t.legendItems=i},afterBuildLabels:hn,beforeFit:hn,fit:function(){var t=this,e=t.options,i=e.labels,n=e.display,a=t.ctx,o=ut.options._parseFont(i),r=o.size,s=t.legendHitBoxes=[],l=t.minSize,d=t.isHorizontal();if(d?(l.width=t.maxWidth,l.height=n?10:0):(l.width=n?10:0,l.height=t.maxHeight),n)if(a.font=o.string,d){var u=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="top",ut.each(t.legendItems,function(t,e){var n=fn(i,r)+r/2+a.measureText(t.text).width;(0===e||u[u.length-1]+n+i.padding>l.width)&&(h+=r+i.padding,u[u.length-(e>0?0:1)]=i.padding),s[e]={left:0,top:0,width:n,height:r},u[u.length-1]+=n+i.padding}),l.height+=h}else{var c=i.padding,f=t.columnWidths=[],g=i.padding,p=0,m=0,v=r+c;ut.each(t.legendItems,function(t,e){var n=fn(i,r)+r/2+a.measureText(t.text).width;e>0&&m+v>l.height-c&&(g+=p+i.padding,f.push(p),p=0,m=0),p=Math.max(p,n),m+=v,s[e]={left:0,top:0,width:n,height:r}}),g+=p,f.push(p),l.width+=g}t.width=l.width,t.height=l.height},afterFit:hn,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,i=e.labels,n=st.global,a=n.defaultColor,o=n.elements.line,r=t.width,s=t.lineWidths;if(e.display){var l,d=t.ctx,u=cn(i.fontColor,n.defaultFontColor),h=ut.options._parseFont(i),c=h.size;d.textAlign="left",d.textBaseline="middle",d.lineWidth=.5,d.strokeStyle=u,d.fillStyle=u,d.font=h.string;var f=fn(i,c),g=t.legendHitBoxes,p=t.isHorizontal();l=p?{x:t.left+(r-s[0])/2+i.padding,y:t.top+i.padding,line:0}:{x:t.left+i.padding,y:t.top+i.padding,line:0};var m=c+i.padding;ut.each(t.legendItems,function(n,u){var h=d.measureText(n.text).width,v=f+c/2+h,b=l.x,x=l.y;p?u>0&&b+v+i.padding>t.left+t.minSize.width&&(x=l.y+=m,l.line++,b=l.x=t.left+(r-s[l.line])/2+i.padding):u>0&&x+m>t.top+t.minSize.height&&(b=l.x=b+t.columnWidths[l.line]+i.padding,x=l.y=t.top+i.padding,l.line++),function(t,i,n){if(!(isNaN(f)||f<=0)){d.save();var r=cn(n.lineWidth,o.borderWidth);if(d.fillStyle=cn(n.fillStyle,a),d.lineCap=cn(n.lineCap,o.borderCapStyle),d.lineDashOffset=cn(n.lineDashOffset,o.borderDashOffset),d.lineJoin=cn(n.lineJoin,o.borderJoinStyle),d.lineWidth=r,d.strokeStyle=cn(n.strokeStyle,a),d.setLineDash&&d.setLineDash(cn(n.lineDash,o.borderDash)),e.labels&&e.labels.usePointStyle){var s=f*Math.SQRT2/2,l=t+f/2,u=i+c/2;ut.canvas.drawPoint(d,n.pointStyle,s,l,u)}else 0!==r&&d.strokeRect(t,i,f,c),d.fillRect(t,i,f,c);d.restore()}}(b,x,n),g[u].left=b,g[u].top=x,function(t,e,i,n){var a=c/2,o=f+a+t,r=e+a;d.fillText(i.text,o,r),i.hidden&&(d.beginPath(),d.lineWidth=2,d.moveTo(o,r),d.lineTo(o+n,r),d.stroke())}(b,x,n,h),p?l.x+=v+i.padding:l.y+=m})}},_getLegendItemAt:function(t,e){var i,n,a,o=this;if(t>=o.left&&t<=o.right&&e>=o.top&&e<=o.bottom)for(a=o.legendHitBoxes,i=0;i=(n=a[i]).left&&t<=n.left+n.width&&e>=n.top&&e<=n.top+n.height)return o.legendItems[i];return null},handleEvent:function(t){var e,i=this,n=i.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!n.onHover&&!n.onLeave)return}else{if("click"!==a)return;if(!n.onClick)return}e=i._getLegendItemAt(t.x,t.y),"click"===a?e&&n.onClick&&n.onClick.call(i,t.native,e):(n.onLeave&&e!==i._hoveredItem&&(i._hoveredItem&&n.onLeave.call(i,t.native,i._hoveredItem),i._hoveredItem=e),n.onHover&&e&&n.onHover.call(i,t.native,e))}});function pn(t,e){var i=new gn({ctx:t.ctx,options:e,chart:t});ke.configure(t,i,e),ke.addBox(t,i),t.legend=i}var mn={id:"legend",_element:gn,beforeInit:function(t){var e=t.options.legend;e&&pn(t,e)},beforeUpdate:function(t){var e=t.options.legend,i=t.legend;e?(ut.mergeIf(e,st.global.legend),i?(ke.configure(t,i,e),i.options=e):pn(t,e)):i&&(ke.removeBox(t,i),delete t.legend)},afterEvent:function(t,e){var i=t.legend;i&&i.handleEvent(e)}},vn=ut.noop;st._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var bn=pt.extend({initialize:function(t){ut.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:vn,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:vn,beforeSetDimensions:vn,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:vn,beforeBuildLabels:vn,buildLabels:vn,afterBuildLabels:vn,beforeFit:vn,fit:function(){var t=this,e=t.options,i=e.display,n=t.minSize,a=ut.isArray(e.text)?e.text.length:1,o=ut.options._parseFont(e),r=i?a*o.lineHeight+2*e.padding:0;t.isHorizontal()?(n.width=t.maxWidth,n.height=r):(n.width=r,n.height=t.maxHeight),t.width=n.width,t.height=n.height},afterFit:vn,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,i=t.options;if(i.display){var n,a,o,r=ut.options._parseFont(i),s=r.lineHeight,l=s/2+i.padding,d=0,u=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=ut.valueOrDefault(i.fontColor,st.global.defaultFontColor),e.font=r.string,t.isHorizontal()?(a=h+(f-h)/2,o=u+l,n=f-h):(a="left"===i.position?h+l:f-l,o=u+(c-u)/2,n=c-u,d=Math.PI*("left"===i.position?-.5:.5)),e.save(),e.translate(a,o),e.rotate(d),e.textAlign="center",e.textBaseline="middle";var g=i.text;if(ut.isArray(g))for(var p=0,m=0;m=0;n--){var a=t[n];if(e(a))return a}},ut.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},ut.almostEquals=function(t,e,i){return Math.abs(t-e)t},ut.max=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.max(t,e)},Number.NEGATIVE_INFINITY)},ut.min=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.min(t,e)},Number.POSITIVE_INFINITY)},ut.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0==(t=+t)||isNaN(t)?t:t>0?1:-1},ut.log10=Math.log10?function(t){return Math.log10(t)}:function(t){var e=Math.log(t)*Math.LOG10E,i=Math.round(e);return t===Math.pow(10,i)?i:e},ut.toRadians=function(t){return t*(Math.PI/180)},ut.toDegrees=function(t){return t*(180/Math.PI)},ut._decimalPlaces=function(t){if(ut.isFinite(t)){for(var e=1,i=0;Math.round(t*e)/e!==t;)e*=10,i++;return i}},ut.getAngleFromPoint=function(t,e){var i=e.x-t.x,n=e.y-t.y,a=Math.sqrt(i*i+n*n),o=Math.atan2(n,i);return o<-.5*Math.PI&&(o+=2*Math.PI),{angle:o,distance:a}},ut.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},ut.aliasPixel=function(t){return t%2==0?0:.5},ut._alignPixel=function(t,e,i){var n=t.currentDevicePixelRatio,a=i/2;return Math.round((e-a)*n)/n+a},ut.splineCurve=function(t,e,i,n){var a=t.skip?e:t,o=e,r=i.skip?e:i,s=Math.sqrt(Math.pow(o.x-a.x,2)+Math.pow(o.y-a.y,2)),l=Math.sqrt(Math.pow(r.x-o.x,2)+Math.pow(r.y-o.y,2)),d=s/(s+l),u=l/(s+l),h=n*(d=isNaN(d)?0:d),c=n*(u=isNaN(u)?0:u);return{previous:{x:o.x-h*(r.x-a.x),y:o.y-h*(r.y-a.y)},next:{x:o.x+c*(r.x-a.x),y:o.y+c*(r.y-a.y)}}},ut.EPSILON=Number.EPSILON||1e-14,ut.splineCurveMonotone=function(t){var e,i,n,a,o,r,s,l,d,u=(t||[]).map(function(t){return{model:t._model,deltaK:0,mK:0}}),h=u.length;for(e=0;e0?u[e-1]:null,(a=e0?u[e-1]:null,a=e=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},ut.previousItem=function(t,e,i){return i?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},ut.niceNum=function(t,e){var i=Math.floor(ut.log10(t)),n=t/Math.pow(10,i);return(e?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10)*Math.pow(10,i)},ut.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},ut.getRelativePosition=function(t,e){var i,n,a=t.originalEvent||t,o=t.target||t.srcElement,r=o.getBoundingClientRect(),s=a.touches;s&&s.length>0?(i=s[0].clientX,n=s[0].clientY):(i=a.clientX,n=a.clientY);var l=parseFloat(ut.getStyle(o,"padding-left")),d=parseFloat(ut.getStyle(o,"padding-top")),u=parseFloat(ut.getStyle(o,"padding-right")),h=parseFloat(ut.getStyle(o,"padding-bottom")),c=r.right-r.left-l-u,f=r.bottom-r.top-d-h;return{x:i=Math.round((i-r.left-l)/c*o.width/e.currentDevicePixelRatio),y:n=Math.round((n-r.top-d)/f*o.height/e.currentDevicePixelRatio)}},ut.getConstraintWidth=function(t){return i(t,"max-width","clientWidth")},ut.getConstraintHeight=function(t){return i(t,"max-height","clientHeight")},ut._calculatePadding=function(t,e,i){return(e=ut.getStyle(t,e)).indexOf("%")>-1?i*parseInt(e,10)/100:parseInt(e,10)},ut._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},ut.getMaximumWidth=function(t){var e=ut._getParentNode(t);if(!e)return t.clientWidth;var i=e.clientWidth,n=i-ut._calculatePadding(e,"padding-left",i)-ut._calculatePadding(e,"padding-right",i),a=ut.getConstraintWidth(t);return isNaN(a)?n:Math.min(n,a)},ut.getMaximumHeight=function(t){var e=ut._getParentNode(t);if(!e)return t.clientHeight;var i=e.clientHeight,n=i-ut._calculatePadding(e,"padding-top",i)-ut._calculatePadding(e,"padding-bottom",i),a=ut.getConstraintHeight(t);return isNaN(a)?n:Math.min(n,a)},ut.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},ut.retinaScale=function(t,e){var i=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==i){var n=t.canvas,a=t.height,o=t.width;n.height=a*i,n.width=o*i,t.ctx.scale(i,i),n.style.height||n.style.width||(n.style.height=a+"px",n.style.width=o+"px")}},ut.fontString=function(t,e,i){return e+" "+t+"px "+i},ut.longestText=function(t,e,i,n){var a=(n=n||{}).data=n.data||{},o=n.garbageCollect=n.garbageCollect||[];n.font!==e&&(a=n.data={},o=n.garbageCollect=[],n.font=e),t.font=e;var r=0;ut.each(i,function(e){null!=e&&!0!==ut.isArray(e)?r=ut.measureText(t,a,o,r,e):ut.isArray(e)&&ut.each(e,function(e){null==e||ut.isArray(e)||(r=ut.measureText(t,a,o,r,e))})});var s=o.length/2;if(s>i.length){for(var l=0;ln&&(n=o),n},ut.numberOfLabelLines=function(t){var e=1;return ut.each(t,function(t){ut.isArray(t)&&t.length>e&&(e=t.length)}),e},ut.color=X?function(t){return t instanceof CanvasGradient&&(t=st.global.defaultColor),X(t)}:function(t){return console.error("Color.js not found!"),t},ut.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:ut.color(t).saturate(.5).darken(.1).rgbString()}}(),ai._adapters=si,ai.Animation=vt,ai.animationService=bt,ai.controllers=ue,ai.DatasetController=Mt,ai.defaults=st,ai.Element=pt,ai.elements=Wt,ai.Interaction=ve,ai.layouts=ke,ai.platform=Ve,ai.plugins=Ee,ai.Scale=fi,ai.scaleService=He,ai.Ticks=li,ai.Tooltip=Je,ai.helpers.each(tn,function(t,e){ai.scaleService.registerScaleType(e,t,t._defaults)}),yn)yn.hasOwnProperty(_n)&&ai.plugins.register(yn[_n]);ai.platform.initialize();var Cn=ai;return"undefined"!=typeof window&&(window.Chart=ai),ai.Chart=ai,ai.Legend=yn.legend._element,ai.Title=yn.title._element,ai.pluginService=ai.plugins,ai.PluginBase=ai.Element.extend({}),ai.canvasHelpers=ai.helpers.canvas,ai.layoutService=ai.layouts,ai.LinearScaleBase=bi,ai.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],function(t){ai[t]=function(e,i){return new ai(e,ai.helpers.merge(i||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}}),Cn}); diff --git a/www/bootstrap.config.json b/www/bootstrap.config.json deleted file mode 100644 index 3600c9c..0000000 --- a/www/bootstrap.config.json +++ /dev/null @@ -1,390 +0,0 @@ -{ - "vars": { - "@gray-base": "#000", - "@gray-darker": "lighten(@gray-base, 13.5%)", - "@gray-dark": "lighten(@gray-base, 20%)", - "@gray": "lighten(@gray-base, 33.5%)", - "@gray-light": "lighten(@gray-base, 46.7%)", - "@gray-lighter": "lighten(@gray-base, 93.5%)", - "@brand-primary": "darken(#428bca, 6.5%)", - "@brand-success": "#5cb85c", - "@brand-info": "#5bc0de", - "@brand-warning": "#f0ad4e", - "@brand-danger": "#d9534f", - "@body-bg": "#fff", - "@text-color": "@gray-dark", - "@link-color": "@brand-primary", - "@link-hover-color": "darken(@link-color, 15%)", - "@link-hover-decoration": "underline", - "@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif", - "@font-family-serif": "Georgia, \"Times New Roman\", Times, serif", - "@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace", - "@font-family-base": "@font-family-sans-serif", - "@font-size-base": "14px", - "@font-size-large": "ceil((@font-size-base * 1.25))", - "@font-size-small": "ceil((@font-size-base * .85))", - "@font-size-h1": "floor((@font-size-base * 2.6))", - "@font-size-h2": "floor((@font-size-base * 2.15))", - "@font-size-h3": "ceil((@font-size-base * 1.7))", - "@font-size-h4": "ceil((@font-size-base * 1.25))", - "@font-size-h5": "@font-size-base", - "@font-size-h6": "ceil((@font-size-base * .85))", - "@line-height-base": "1.428571429", - "@line-height-computed": "floor((@font-size-base * @line-height-base))", - "@headings-font-family": "inherit", - "@headings-font-weight": "500", - "@headings-line-height": "1.1", - "@headings-color": "inherit", - "@icon-font-path": "\"../fonts/\"", - "@icon-font-name": "\"glyphicons-halflings-regular\"", - "@icon-font-svg-id": "\"glyphicons_halflingsregular\"", - "@padding-base-vertical": "6px", - "@padding-base-horizontal": "12px", - "@padding-large-vertical": "10px", - "@padding-large-horizontal": "16px", - "@padding-small-vertical": "5px", - "@padding-small-horizontal": "10px", - "@padding-xs-vertical": "1px", - "@padding-xs-horizontal": "5px", - "@line-height-large": "1.3333333", - "@line-height-small": "1.5", - "@border-radius-base": "4px", - "@border-radius-large": "6px", - "@border-radius-small": "3px", - "@component-active-color": "#fff", - "@component-active-bg": "@brand-primary", - "@caret-width-base": "4px", - "@caret-width-large": "5px", - "@table-cell-padding": "8px", - "@table-condensed-cell-padding": "5px", - "@table-bg": "transparent", - "@table-bg-accent": "#f9f9f9", - "@table-bg-hover": "#f5f5f5", - "@table-bg-active": "@table-bg-hover", - "@table-border-color": "#ddd", - "@btn-font-weight": "normal", - "@btn-default-color": "#333", - "@btn-default-bg": "#fff", - "@btn-default-border": "#ccc", - "@btn-primary-color": "#fff", - "@btn-primary-bg": "@brand-primary", - "@btn-primary-border": "darken(@btn-primary-bg, 5%)", - "@btn-success-color": "#fff", - "@btn-success-bg": "@brand-success", - "@btn-success-border": "darken(@btn-success-bg, 5%)", - "@btn-info-color": "#fff", - "@btn-info-bg": "@brand-info", - "@btn-info-border": "darken(@btn-info-bg, 5%)", - "@btn-warning-color": "#fff", - "@btn-warning-bg": "@brand-warning", - "@btn-warning-border": "darken(@btn-warning-bg, 5%)", - "@btn-danger-color": "#fff", - "@btn-danger-bg": "@brand-danger", - "@btn-danger-border": "darken(@btn-danger-bg, 5%)", - "@btn-link-disabled-color": "@gray-light", - "@btn-border-radius-base": "@border-radius-base", - "@btn-border-radius-large": "@border-radius-large", - "@btn-border-radius-small": "@border-radius-small", - "@input-bg": "#fff", - "@input-bg-disabled": "@gray-lighter", - "@input-color": "@gray", - "@input-border": "#ccc", - "@input-border-radius": "@border-radius-base", - "@input-border-radius-large": "@border-radius-large", - "@input-border-radius-small": "@border-radius-small", - "@input-border-focus": "#66afe9", - "@input-color-placeholder": "#999", - "@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)", - "@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)", - "@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)", - "@form-group-margin-bottom": "15px", - "@legend-color": "@gray-dark", - "@legend-border-color": "#e5e5e5", - "@input-group-addon-bg": "@gray-lighter", - "@input-group-addon-border-color": "@input-border", - "@cursor-disabled": "not-allowed", - "@dropdown-bg": "#fff", - "@dropdown-border": "rgba(0, 0, 0, .15)", - "@dropdown-fallback-border": "#ccc", - "@dropdown-divider-bg": "#e5e5e5", - "@dropdown-link-color": "@gray-dark", - "@dropdown-link-hover-color": "darken(@gray-dark, 5%)", - "@dropdown-link-hover-bg": "#f5f5f5", - "@dropdown-link-active-color": "@component-active-color", - "@dropdown-link-active-bg": "@component-active-bg", - "@dropdown-link-disabled-color": "@gray-light", - "@dropdown-header-color": "@gray-light", - "@dropdown-caret-color": "#000", - "@screen-xs": "480px", - "@screen-xs-min": "@screen-xs", - "@screen-phone": "@screen-xs-min", - "@screen-sm": "768px", - "@screen-sm-min": "@screen-sm", - "@screen-tablet": "@screen-sm-min", - "@screen-md": "992px", - "@screen-md-min": "@screen-md", - "@screen-desktop": "@screen-md-min", - "@screen-lg": "1200px", - "@screen-lg-min": "@screen-lg", - "@screen-lg-desktop": "@screen-lg-min", - "@screen-xs-max": "(@screen-sm-min - 1)", - "@screen-sm-max": "(@screen-md-min - 1)", - "@screen-md-max": "(@screen-lg-min - 1)", - "@grid-columns": "12", - "@grid-gutter-width": "30px", - "@grid-float-breakpoint": "@screen-sm-min", - "@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)", - "@container-tablet": "(720px + @grid-gutter-width)", - "@container-sm": "@container-tablet", - "@container-desktop": "(940px + @grid-gutter-width)", - "@container-md": "@container-desktop", - "@container-large-desktop": "(1140px + @grid-gutter-width)", - "@container-lg": "@container-large-desktop", - "@navbar-height": "50px", - "@navbar-margin-bottom": "@line-height-computed", - "@navbar-border-radius": "@border-radius-base", - "@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))", - "@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)", - "@navbar-collapse-max-height": "340px", - "@navbar-default-color": "#777", - "@navbar-default-bg": "#f8f8f8", - "@navbar-default-border": "darken(@navbar-default-bg, 6.5%)", - "@navbar-default-link-color": "#777", - "@navbar-default-link-hover-color": "#333", - "@navbar-default-link-hover-bg": "transparent", - "@navbar-default-link-active-color": "#555", - "@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)", - "@navbar-default-link-disabled-color": "#ccc", - "@navbar-default-link-disabled-bg": "transparent", - "@navbar-default-brand-color": "@navbar-default-link-color", - "@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)", - "@navbar-default-brand-hover-bg": "transparent", - "@navbar-default-toggle-hover-bg": "#ddd", - "@navbar-default-toggle-icon-bar-bg": "#888", - "@navbar-default-toggle-border-color": "#ddd", - "@navbar-inverse-color": "lighten(@gray-light, 15%)", - "@navbar-inverse-bg": "#222", - "@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)", - "@navbar-inverse-link-color": "lighten(@gray-light, 15%)", - "@navbar-inverse-link-hover-color": "#fff", - "@navbar-inverse-link-hover-bg": "transparent", - "@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color", - "@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)", - "@navbar-inverse-link-disabled-color": "#444", - "@navbar-inverse-link-disabled-bg": "transparent", - "@navbar-inverse-brand-color": "@navbar-inverse-link-color", - "@navbar-inverse-brand-hover-color": "#fff", - "@navbar-inverse-brand-hover-bg": "transparent", - "@navbar-inverse-toggle-hover-bg": "#333", - "@navbar-inverse-toggle-icon-bar-bg": "#fff", - "@navbar-inverse-toggle-border-color": "#333", - "@nav-link-padding": "10px 15px", - "@nav-link-hover-bg": "@gray-lighter", - "@nav-disabled-link-color": "@gray-light", - "@nav-disabled-link-hover-color": "@gray-light", - "@nav-tabs-border-color": "#ddd", - "@nav-tabs-link-hover-border-color": "@gray-lighter", - "@nav-tabs-active-link-hover-bg": "@body-bg", - "@nav-tabs-active-link-hover-color": "@gray", - "@nav-tabs-active-link-hover-border-color": "#ddd", - "@nav-tabs-justified-link-border-color": "#ddd", - "@nav-tabs-justified-active-link-border-color": "@body-bg", - "@nav-pills-border-radius": "@border-radius-base", - "@nav-pills-active-link-hover-bg": "@component-active-bg", - "@nav-pills-active-link-hover-color": "@component-active-color", - "@pagination-color": "@link-color", - "@pagination-bg": "#fff", - "@pagination-border": "#ddd", - "@pagination-hover-color": "@link-hover-color", - "@pagination-hover-bg": "@gray-lighter", - "@pagination-hover-border": "#ddd", - "@pagination-active-color": "#fff", - "@pagination-active-bg": "@brand-primary", - "@pagination-active-border": "@brand-primary", - "@pagination-disabled-color": "@gray-light", - "@pagination-disabled-bg": "#fff", - "@pagination-disabled-border": "#ddd", - "@pager-bg": "@pagination-bg", - "@pager-border": "@pagination-border", - "@pager-border-radius": "15px", - "@pager-hover-bg": "@pagination-hover-bg", - "@pager-active-bg": "@pagination-active-bg", - "@pager-active-color": "@pagination-active-color", - "@pager-disabled-color": "@pagination-disabled-color", - "@jumbotron-padding": "30px", - "@jumbotron-color": "inherit", - "@jumbotron-bg": "@gray-lighter", - "@jumbotron-heading-color": "inherit", - "@jumbotron-font-size": "ceil((@font-size-base * 1.5))", - "@jumbotron-heading-font-size": "ceil((@font-size-base * 4.5))", - "@state-success-text": "#3c763d", - "@state-success-bg": "#dff0d8", - "@state-success-border": "darken(spin(@state-success-bg, -10), 5%)", - "@state-info-text": "#31708f", - "@state-info-bg": "#d9edf7", - "@state-info-border": "darken(spin(@state-info-bg, -10), 7%)", - "@state-warning-text": "#8a6d3b", - "@state-warning-bg": "#fcf8e3", - "@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)", - "@state-danger-text": "#a94442", - "@state-danger-bg": "#f2dede", - "@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)", - "@tooltip-max-width": "200px", - "@tooltip-color": "#fff", - "@tooltip-bg": "#000", - "@tooltip-opacity": ".9", - "@tooltip-arrow-width": "5px", - "@tooltip-arrow-color": "@tooltip-bg", - "@popover-bg": "#fff", - "@popover-max-width": "276px", - "@popover-border-color": "rgba(0, 0, 0, .2)", - "@popover-fallback-border-color": "#ccc", - "@popover-title-bg": "darken(@popover-bg, 3%)", - "@popover-arrow-width": "10px", - "@popover-arrow-color": "@popover-bg", - "@popover-arrow-outer-width": "(@popover-arrow-width + 1)", - "@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)", - "@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)", - "@label-default-bg": "@gray-light", - "@label-primary-bg": "@brand-primary", - "@label-success-bg": "@brand-success", - "@label-info-bg": "@brand-info", - "@label-warning-bg": "@brand-warning", - "@label-danger-bg": "@brand-danger", - "@label-color": "#fff", - "@label-link-hover-color": "#fff", - "@modal-inner-padding": "15px", - "@modal-title-padding": "15px", - "@modal-title-line-height": "@line-height-base", - "@modal-content-bg": "#fff", - "@modal-content-border-color": "rgba(0, 0, 0, .2)", - "@modal-content-fallback-border-color": "#999", - "@modal-backdrop-bg": "#000", - "@modal-backdrop-opacity": ".5", - "@modal-header-border-color": "#e5e5e5", - "@modal-footer-border-color": "@modal-header-border-color", - "@modal-lg": "900px", - "@modal-md": "600px", - "@modal-sm": "300px", - "@alert-padding": "15px", - "@alert-border-radius": "@border-radius-base", - "@alert-link-font-weight": "bold", - "@alert-success-bg": "@state-success-bg", - "@alert-success-text": "@state-success-text", - "@alert-success-border": "@state-success-border", - "@alert-info-bg": "@state-info-bg", - "@alert-info-text": "@state-info-text", - "@alert-info-border": "@state-info-border", - "@alert-warning-bg": "@state-warning-bg", - "@alert-warning-text": "@state-warning-text", - "@alert-warning-border": "@state-warning-border", - "@alert-danger-bg": "@state-danger-bg", - "@alert-danger-text": "@state-danger-text", - "@alert-danger-border": "@state-danger-border", - "@progress-bg": "#f5f5f5", - "@progress-bar-color": "#fff", - "@progress-border-radius": "@border-radius-base", - "@progress-bar-bg": "@brand-primary", - "@progress-bar-success-bg": "@brand-success", - "@progress-bar-warning-bg": "@brand-warning", - "@progress-bar-danger-bg": "@brand-danger", - "@progress-bar-info-bg": "@brand-info", - "@list-group-bg": "#fff", - "@list-group-border": "#ddd", - "@list-group-border-radius": "@border-radius-base", - "@list-group-hover-bg": "#f5f5f5", - "@list-group-active-color": "@component-active-color", - "@list-group-active-bg": "@component-active-bg", - "@list-group-active-border": "@list-group-active-bg", - "@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)", - "@list-group-disabled-color": "@gray-light", - "@list-group-disabled-bg": "@gray-lighter", - "@list-group-disabled-text-color": "@list-group-disabled-color", - "@list-group-link-color": "#555", - "@list-group-link-hover-color": "@list-group-link-color", - "@list-group-link-heading-color": "#333", - "@panel-bg": "#fff", - "@panel-body-padding": "15px", - "@panel-heading-padding": "10px 15px", - "@panel-footer-padding": "@panel-heading-padding", - "@panel-border-radius": "@border-radius-base", - "@panel-inner-border": "#ddd", - "@panel-footer-bg": "#f5f5f5", - "@panel-default-text": "@gray-dark", - "@panel-default-border": "#ddd", - "@panel-default-heading-bg": "#f5f5f5", - "@panel-primary-text": "#fff", - "@panel-primary-border": "@brand-primary", - "@panel-primary-heading-bg": "@brand-primary", - "@panel-success-text": "@state-success-text", - "@panel-success-border": "@state-success-border", - "@panel-success-heading-bg": "@state-success-bg", - "@panel-info-text": "@state-info-text", - "@panel-info-border": "@state-info-border", - "@panel-info-heading-bg": "@state-info-bg", - "@panel-warning-text": "@state-warning-text", - "@panel-warning-border": "@state-warning-border", - "@panel-warning-heading-bg": "@state-warning-bg", - "@panel-danger-text": "@state-danger-text", - "@panel-danger-border": "@state-danger-border", - "@panel-danger-heading-bg": "@state-danger-bg", - "@thumbnail-padding": "4px", - "@thumbnail-bg": "@body-bg", - "@thumbnail-border": "#ddd", - "@thumbnail-border-radius": "@border-radius-base", - "@thumbnail-caption-color": "@text-color", - "@thumbnail-caption-padding": "9px", - "@well-bg": "#f5f5f5", - "@well-border": "darken(@well-bg, 7%)", - "@badge-color": "#fff", - "@badge-link-hover-color": "#fff", - "@badge-bg": "@gray-light", - "@badge-active-color": "@link-color", - "@badge-active-bg": "#fff", - "@badge-font-weight": "bold", - "@badge-line-height": "1", - "@badge-border-radius": "10px", - "@breadcrumb-padding-vertical": "8px", - "@breadcrumb-padding-horizontal": "15px", - "@breadcrumb-bg": "#f5f5f5", - "@breadcrumb-color": "#ccc", - "@breadcrumb-active-color": "@gray-light", - "@breadcrumb-separator": "\"/\"", - "@carousel-text-shadow": "0 1px 2px rgba(0, 0, 0, .6)", - "@carousel-control-color": "#fff", - "@carousel-control-width": "15%", - "@carousel-control-opacity": ".5", - "@carousel-control-font-size": "20px", - "@carousel-indicator-active-bg": "#fff", - "@carousel-indicator-border-color": "#fff", - "@carousel-caption-color": "#fff", - "@close-font-weight": "bold", - "@close-color": "#000", - "@close-text-shadow": "0 1px 0 #fff", - "@code-color": "#c7254e", - "@code-bg": "#f9f2f4", - "@kbd-color": "#fff", - "@kbd-bg": "#333", - "@pre-bg": "#f5f5f5", - "@pre-color": "@gray-dark", - "@pre-border-color": "#ccc", - "@pre-scrollable-max-height": "340px", - "@component-offset-horizontal": "180px", - "@text-muted": "@gray-light", - "@abbr-border-color": "@gray-light", - "@headings-small-color": "@gray-light", - "@blockquote-small-color": "@gray-light", - "@blockquote-font-size": "(@font-size-base * 1.25)", - "@blockquote-border-color": "@gray-lighter", - "@page-header-border-color": "@gray-lighter", - "@dl-horizontal-offset": "@component-offset-horizontal", - "@dl-horizontal-breakpoint": "@grid-float-breakpoint", - "@hr-border": "@gray-lighter" - }, - "css": [ - "type.less", - "grid.less", - "tables.less" - ], - "js": [] -} \ No newline at end of file diff --git a/www/bootstrap.min.css b/www/bootstrap.min.css deleted file mode 100644 index ceb64ec..0000000 --- a/www/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/) - *//*! - * Bootstrap v3.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover,a.text-primary:focus{color:#286090}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover,a.bg-primary:focus{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed} \ No newline at end of file diff --git a/www/chartjs-plugin-zoom.min.js b/www/chartjs-plugin-zoom.min.js deleted file mode 100644 index bc5a311..0000000 --- a/www/chartjs-plugin-zoom.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * @license - * chartjs-plugin-zoom - * http://chartjs.org/ - * Version: 0.7.2 - * - * Copyright 2019 Chart.js Contributors - * Released under the MIT license - * https://github.com/chartjs/chartjs-plugin-zoom/blob/master/LICENSE.md - */ -!function(e,o){"object"==typeof exports&&"undefined"!=typeof module?module.exports=o(require("chart.js"),require("hammerjs")):"function"==typeof define&&define.amd?define(["chart.js","hammerjs"],o):(e=e||self).ChartZoom=o(e.Chart,e.Hammer)}(this,function(e,o){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e,o=o&&o.hasOwnProperty("default")?o.default:o;var n=e.helpers,t=e.Zoom=e.Zoom||{},a=t.zoomFunctions=t.zoomFunctions||{},i=t.panFunctions=t.panFunctions||{};function m(e,o){var t={};void 0!==e.options.pan&&(t.pan=e.options.pan),void 0!==e.options.pan&&(t.zoom=e.options.zoom);var a=e.$zoom;o=a._options=n.merge({},[o,t]);var i=a._node;o.zoom&&o.zoom.drag?i.removeEventListener("wheel",a._wheelHandler):i.addEventListener("wheel",a._wheelHandler),o.zoom&&o.zoom.drag?(i.addEventListener("mousedown",a._mouseDownHandler),i.ownerDocument.addEventListener("mouseup",a._mouseUpHandler)):(i.removeEventListener("mousedown",a._mouseDownHandler),i.removeEventListener("mousemove",a._mouseMoveHandler),i.ownerDocument.removeEventListener("mouseup",a._mouseUpHandler))}function r(e){var o=e.$zoom._originalOptions;n.each(e.scales,function(e){o[e.id]||(o[e.id]=n.clone(e.options))}),n.each(o,function(n,t){e.scales[t]||delete o[t]})}function l(e,o){return void 0===e||"string"==typeof e&&-1!==e.indexOf(o)}function s(e,o){if(e.scaleAxes&&e.rangeMax&&!n.isNullOrUndef(e.rangeMax[e.scaleAxes])){var t=e.rangeMax[e.scaleAxes];o>t&&(o=t)}return o}function c(e,o){if(e.scaleAxes&&e.rangeMin&&!n.isNullOrUndef(e.rangeMin[e.scaleAxes])){var t=e.rangeMin[e.scaleAxes];o=c&&s<=u?(i.min=l,i.max=s):lu&&(a=u-r,i.max=u,i.min=m+a)}function f(e,o,n){var t=i[e.type];t&&t(e,o,n)}e.Zoom.defaults=e.defaults.global.plugins.zoom={pan:{enabled:!1,mode:"xy",speed:20,threshold:10},zoom:{enabled:!1,mode:"xy",sensitivity:3,speed:.1}},t.zoomFunctions.category=function(e,o,n,a){var i=e.chart.data.labels,m=e.minIndex,r=i.length-1,l=e.maxIndex,u=a.sensitivity,d=e.isHorizontal()?e.left+e.width/2:e.top+e.height/2,p=e.isHorizontal()?n.x:n.y;t.zoomCumulativeDelta=o>1?t.zoomCumulativeDelta+1:t.zoomCumulativeDelta-1,Math.abs(t.zoomCumulativeDelta)>u&&(t.zoomCumulativeDelta<0?(p>=d?m<=0?l=Math.min(r,l+1):m=Math.max(0,m-1):p=r?m=Math.max(0,m-1):l=Math.min(r,l+1)),t.zoomCumulativeDelta=0):t.zoomCumulativeDelta>0&&(p>=d?m=mm?l=Math.max(m,l-1):l),t.zoomCumulativeDelta=0),e.options.ticks.min=c(a,i[m]),e.options.ticks.max=s(a,i[l]))},t.zoomFunctions.time=function(e,o,n,t){u(e,o,n,t),e.options.time.min=e.options.ticks.min,e.options.time.max=e.options.ticks.max},t.zoomFunctions.linear=u,t.zoomFunctions.logarithmic=u,t.panFunctions.category=function(e,o,n){var a,i=e.chart.data.labels,m=i.length-1,r=Math.max(e.ticks.length,1),l=n.speed,u=e.minIndex,d=Math.round(e.width/(r*l));t.panCumulativeDelta+=o,u=t.panCumulativeDelta>d?Math.max(0,u-1):t.panCumulativeDelta<-d?Math.min(m-r+1,u+1):u,t.panCumulativeDelta=u!==e.minIndex?0:t.panCumulativeDelta,a=Math.min(m,u+r-1),e.options.ticks.min=c(n,i[u]),e.options.ticks.max=s(n,i[a])},t.panFunctions.time=function(e,o,n){v(e,o,n);var t=e.options;t.time.min=t.ticks.min,t.time.max=t.ticks.max},t.panFunctions.linear=v,t.panFunctions.logarithmic=v,t.panCumulativeDelta=0,t.zoomCumulativeDelta=0;var h={id:"zoom",afterInit:function(e){e.resetZoom=function(){r(e);var o=e.$zoom._originalOptions;n.each(e.scales,function(e){var n=e.options.time,t=e.options.ticks;o[e.id]?(n&&(n.min=o[e.id].time.min,n.max=o[e.id].time.max),t&&(t.min=o[e.id].ticks.min,t.max=o[e.id].ticks.max)):(n&&(delete n.min,delete n.max),t&&(delete t.min,delete t.max))}),e.update()}},beforeUpdate:function(e,o){m(e,o)},beforeInit:function(e,a){e.$zoom={_originalOptions:{}};var i=e.$zoom._node=e.chart.ctx.canvas;m(e,a);var s=e.$zoom._options,c=s.pan&&s.pan.threshold;e.$zoom._mouseDownHandler=function(o){i.addEventListener("mousemove",e.$zoom._mouseMoveHandler),e.$zoom._dragZoomStart=o},e.$zoom._mouseMoveHandler=function(o){e.$zoom._dragZoomStart&&(e.$zoom._dragZoomEnd=o,e.update(0))},e.$zoom._mouseUpHandler=function(o){if(e.$zoom._dragZoomStart){i.removeEventListener("mousemove",e.$zoom._mouseMoveHandler);var n=e.$zoom._dragZoomStart,t=n.target.getBoundingClientRect().left,a=Math.min(n.clientX,o.clientX)-t,m=Math.max(n.clientX,o.clientX)-t,r=n.target.getBoundingClientRect().top,s=Math.min(n.clientY,o.clientY)-r,c=m-a,u=Math.max(n.clientY,o.clientY)-r-s;if(e.$zoom._dragZoomStart=null,e.$zoom._dragZoomEnd=null,!(c<=0&&u<=0)){var d=e.chartArea,v=e.$zoom._options.zoom,f=d.right-d.left,h=l(v.mode,"x")&&c?1+(f-c)/f:1,x=d.bottom-d.top,g=l(v.mode,"y");p(e,h,g&&u?1+(x-u)/x:1,{x:(a-d.left)/(1-c/f)+d.left,y:(s-d.top)/(1-u/x)+d.top}),"function"==typeof v.onZoomComplete&&v.onZoomComplete({chart:e})}}};var u=null;if(e.$zoom._wheelHandler=function(o){var n=o.target.getBoundingClientRect(),t={x:o.clientX-n.left,y:o.clientY-n.top},a=e.$zoom._options.zoom,i=a.speed;o.deltaY>=0&&(i=-i),p(e,1+i,1+i,t),clearTimeout(u),u=setTimeout(function(){"function"==typeof a.onZoomComplete&&a.onZoomComplete({chart:e})},250),o.cancelable&&o.preventDefault()},o){var d,v=new o.Manager(i);v.add(new o.Pinch),v.add(new o.Pan({threshold:c}));var h=function(o){var n=1/d*o.scale,t=o.target.getBoundingClientRect(),a={x:o.center.x-t.left,y:o.center.y-t.top},i=Math.abs(o.pointers[0].clientX-o.pointers[1].clientX),m=Math.abs(o.pointers[0].clientY-o.pointers[1].clientY),r=i/m;p(e,n,n,a,r>.3&&r<1.7?"xy":i>m?"x":"y"),d=o.scale};v.on("pinchstart",function(){d=1}),v.on("pinch",h),v.on("pinchend",function(e){h(e),d=null,t.zoomCumulativeDelta=0});var x=null,g=null,z=!1,_=function(o){if(null!==x&&null!==g){z=!0;var t=o.deltaX-x,a=o.deltaY-g;x=o.deltaX,g=o.deltaY,function(e,o,t){r(e);var a=e.$zoom._options.pan;if(a.enabled){var i=a.mode;n.each(e.scales,function(e){e.isHorizontal()&&l(i,"x")&&0!==o?(a.scaleAxes="x",f(e,o,a)):!e.isHorizontal()&&l(i,"y")&&0!==t&&(a.scaleAxes="y",f(e,t,a))}),e.update(0),"function"==typeof a.onPan&&a.onPan({chart:e})}}(e,t,a)}};v.on("panstart",function(e){x=0,g=0,_(e)}),v.on("panmove",_),v.on("panend",function(){x=null,g=null,t.panCumulativeDelta=0,setTimeout(function(){z=!1},500);var o=e.$zoom._options.pan;"function"==typeof o.onPanComplete&&o.onPanComplete({chart:e})}),e.$zoom._ghostClickHandler=function(e){z&&e.cancelable&&(e.stopImmediatePropagation(),e.preventDefault())},i.addEventListener("click",e.$zoom._ghostClickHandler),e._mc=v}},beforeDatasetsDraw:function(e){var o=e.chart.ctx;if(e.$zoom._dragZoomEnd){var n=function(e){for(var o=e.scales,n=Object.keys(o),t=0;t0&&(o.lineWidth=f.borderWidth,o.strokeStyle=f.borderColor||"rgba(225,225,225)",o.strokeRect(m,s,p,v)),o.restore()}},destroy:function(e){if(e.$zoom){var o=e.$zoom,n=o._node;n.removeEventListener("mousedown",o._mouseDownHandler),n.removeEventListener("mousemove",o._mouseMoveHandler),n.ownerDocument.removeEventListener("mouseup",o._mouseUpHandler),n.removeEventListener("wheel",o._wheelHandler),n.removeEventListener("click",o._ghostClickHandler),delete e.$zoom;var t=e._mc;t&&(t.remove("pinchstart"),t.remove("pinch"),t.remove("pinchend"),t.remove("panstart"),t.remove("pan"),t.remove("panend"))}}};return e.plugins.register(h),h}); diff --git a/www/hammer.min.js b/www/hammer.min.js deleted file mode 100644 index 6221643..0000000 --- a/www/hammer.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! Hammer.JS - v2.0.8 - 2016-04-23 - * http://hammerjs.github.io/ - * - * Copyright (c) 2016 Jorik Tangelder; - * Licensed under the MIT license */ -!function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&la(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==oa?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=ra(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=qa(j.x)>qa(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};b.eventType!==Ea&&f.eventType!==Ga||(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ha&&(i>Da||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=qa(l.x)>qa(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:pa(c/b),y:pa(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Ia:qa(a)>=qa(b)?0>a?Ja:Ka:0>b?La:Ma}function H(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Ra)+I(a[1],a[0],Ra)}function K(a,b){return H(b[0],b[1],Ra)/H(a[0],a[1],Ra)}function L(){this.evEl=Ta,this.evWin=Ua,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Xa,this.evWin=Ya,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=$a,this.evWin=_a,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Ga|Ha)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=bb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ea|Fa)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ea)for(e=0;e-1&&d.splice(a,1)};setTimeout(e,cb)}}function U(a){for(var b=a.srcEvent.clientX,c=a.srcEvent.clientY,d=0;d=f&&db>=g)return!0}return!1}function V(a,b){this.manager=a,this.set(b)}function W(a){if(p(a,jb))return jb;var b=p(a,kb),c=p(a,lb);return b&&c?jb:b||c?b?kb:lb:p(a,ib)?ib:hb}function X(){if(!fb)return!1;var b={},c=a.CSS&&a.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(d){b[d]=c?a.CSS.supports("touch-action",d):!0}),b}function Y(a){this.options=la({},this.defaults,a||{}),this.id=v(),this.manager=null,this.options.enable=l(this.options.enable,!0),this.state=nb,this.simultaneous={},this.requireFail=[]}function Z(a){return a&sb?"cancel":a&qb?"end":a&pb?"move":a&ob?"start":""}function $(a){return a==Ma?"down":a==La?"up":a==Ja?"left":a==Ka?"right":""}function _(a,b){var c=b.manager;return c?c.get(a):a}function aa(){Y.apply(this,arguments)}function ba(){aa.apply(this,arguments),this.pX=null,this.pY=null}function ca(){aa.apply(this,arguments)}function da(){Y.apply(this,arguments),this._timer=null,this._input=null}function ea(){aa.apply(this,arguments)}function fa(){aa.apply(this,arguments)}function ga(){Y.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ha(a,b){return b=b||{},b.recognizers=l(b.recognizers,ha.defaults.preset),new ia(a,b)}function ia(a,b){this.options=la({},ha.defaults,b||{}),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=a,this.input=y(this),this.touchAction=new V(this,this.options.touchAction),ja(this,!0),g(this.options.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ja(a,b){var c=a.element;if(c.style){var d;g(a.options.cssProps,function(e,f){d=u(c.style,f),b?(a.oldCssProps[d]=c.style[d],c.style[d]=e):c.style[d]=a.oldCssProps[d]||""}),b||(a.oldCssProps={})}}function ka(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var la,ma=["","webkit","Moz","MS","ms","o"],na=b.createElement("div"),oa="function",pa=Math.round,qa=Math.abs,ra=Date.now;la="function"!=typeof Object.assign?function(a){if(a===d||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var b=Object(a),c=1;ch&&(b.push(a),h=b.length-1):e&(Ga|Ha)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Za={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},$a="touchstart",_a="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Za[a.type];if(b===Ea&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Ga|Ha)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}}});var ab={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},bb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=ab[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}});var cb=2500,db=25;i(R,x,{handler:function(a,b,c){var d=c.pointerType==za,e=c.pointerType==Ba;if(!(e&&c.sourceCapabilities&&c.sourceCapabilities.firesTouchEvents)){if(d)S.call(this,b,c);else if(e&&U.call(this,c))return;this.callback(a,b,c)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var eb=u(na.style,"touchAction"),fb=eb!==d,gb="compute",hb="auto",ib="manipulation",jb="none",kb="pan-x",lb="pan-y",mb=X();V.prototype={set:function(a){a==gb&&(a=this.compute()),fb&&this.manager.element.style&&mb[a]&&(this.manager.element.style[eb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),W(a.join(" "))},preventDefaults:function(a){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,jb)&&!mb[jb],f=p(d,lb)&&!mb[lb],g=p(d,kb)&&!mb[kb];if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}return g&&f?void 0:e||f&&c&Na||g&&c&Oa?this.preventSrc(b):void 0},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var nb=1,ob=2,pb=4,qb=8,rb=qb,sb=16,tb=32;Y.prototype={defaults:{},set:function(a){return la(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=_(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=_(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=_(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;qb>d&&b(c.options.event+Z(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=qb&&b(c.options.event+Z(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=tb)},canEmit:function(){for(var a=0;af?Ja:Ka,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ia:0>g?La:Ma,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return aa.prototype.attrTest.call(this,a)&&(this.state&ob||!(this.state&ob)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i(ca,aa,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&ob)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(da,Y,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[hb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Ga|Ha)&&!f)this.reset();else if(a.eventType&Ea)this.reset(),this._timer=e(function(){this.state=rb,this.tryEmit()},b.time,this);else if(a.eventType&Ga)return rb;return tb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===rb&&(a&&a.eventType&Ga?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=ra(),this.manager.emit(this.options.event,this._input)))}}),i(ea,aa,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&ob)}}),i(fa,aa,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Na|Oa,pointers:1},getTouchAction:function(){return ba.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Na|Oa)?b=a.overallVelocity:c&Na?b=a.overallVelocityX:c&Oa&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&qa(b)>this.options.velocity&&a.eventType&Ga},emit:function(a){var b=$(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(ga,Y,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ib]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance - */ - -$config = Configuration::getInstance(); -$session = Session::getSessionFromRequest(); - -$t = new Template($config, 'proxystatistics:identityProviders-tpl.php'); -$t->data['lastDays'] = filter_input( - INPUT_GET, - 'lastDays', - FILTER_VALIDATE_INT, - ['options' => ['default' => 0, 'min_range' => 0]] -); -$t->data['tab'] = 1; -$t->show(); diff --git a/www/idpDetail.php b/www/idpDetail.php deleted file mode 100644 index 68d8968..0000000 --- a/www/idpDetail.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ - -const CONFIG_FILE_NAME_STATISTICSPROXY = 'module_statisticsproxy.php'; -const MODE = 'mode'; - -$config = Configuration::getInstance(); -$session = Session::getSessionFromRequest(); - -$configStatisticsproxy = Configuration::getConfig(CONFIG_FILE_NAME_STATISTICSPROXY); -$mode = $configStatisticsproxy->getString(MODE, 'PROXY'); - -$t = new Template($config, 'proxystatistics:idpDetail-tpl.php'); - -$t->data['lastDays'] = filter_input( - INPUT_POST, - 'lastDays', - FILTER_VALIDATE_INT, - ['options' => ['default' => 0, 'min_range' => 0]] -); -$t->data['entityId'] = filter_input(INPUT_GET, 'entityId', FILTER_SANITIZE_STRING); - -if ($mode === 'SP') { - $t->data['idpDetailGraphClass'] = 'hidden'; -} else { - $t->data['idpDetailGraphClass'] = ''; -} - -$t->show(); diff --git a/www/index.js b/www/index.js deleted file mode 100644 index 251ccf9..0000000 --- a/www/index.js +++ /dev/null @@ -1,382 +0,0 @@ -'use strict'; - -/* global Chart moment */ - -function getStatisticsData(name) { - return $.parseJSON($('#' + name).attr('content')); -} - -function getStatisticsDataYMDC(name) { - return getStatisticsData(name).map(function mapItemToDate(item) { - return { - t: new Date(item.year, item.month - 1, item.day), - y: item.count - }; - }); -} - -function getTranslation(str) { - return $.parseJSON($('#translations').attr('content'))[str]; -} - -function drawLoginsChart(getEl) { - var el = getEl(); - if (!el) return; - - var ctx = el.getContext('2d'); - - var data = getStatisticsDataYMDC('loginCountPerDay'); - - var minX = data[0].t; - var maxX = data[data.length - 1].t; - - var i = 0; - var extendedData = []; - for (var d = new Date(minX); d <= maxX; d.setDate(d.getDate() + 1)) { - if (data[i].t.getTime() === d.getTime()) { - extendedData.push(data[i]); - i += 1; - } else if (data[i].t.getTime() > d.getTime()) { - extendedData.push({ t: new Date(d), y: 0 }); - } else { - throw new Error("Data is not sorted"); - } - } - data = extendedData; - - new Chart(ctx, { // eslint-disable-line no-new - type: 'bar', - options: { - responsive: true, - maintainAspectRatio: false, - ticks: { - beginAtZero: true - }, - scales: { - xAxes: [{ - type: 'time', - time: { // do not set round: 'day', because it breaks zooming out from 7 or fewer days - isoWeekday: true, - minUnit: 'day', - tooltipFormat: 'l' - } - }], - yAxes: [{ - scaleLabel: { - display: false - } - }] - }, - legend: { - display: false - }, - tooltips: { - intersect: false, - mode: 'index', - callbacks: { - label: function showLabel(tooltipItem, myData) { - var label = myData.datasets[tooltipItem.datasetIndex].label || ''; - if (label) { - label += ': '; - } - label += parseInt(tooltipItem.value, 10); - return label; - } - } - }, - pan: { - enabled: true, - mode: 'x', - rangeMin: { - x: minX - }, - rangeMax: { - x: maxX - } - }, - zoom: { - enabled: true, - drag: false, - mode: 'x', - rangeMin: { - x: minX - }, - rangeMax: { - x: maxX - } - } - }, - "data": { - "datasets": [ - { - "label": "", - "data": data, - type: 'line', - pointRadius: 0, - fill: false, - lineTension: 0, - borderWidth: 2, - backgroundColor: '#00F', - borderColor: '#00F' - } - ] - } - }); -} - -var pieColors = [ - '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6', '#DD4477', '#66AA00', '#B82E2E', - '#316395', '#994499', '#22AA99', '#AAAA11', '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC', - '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6', '#DD4477', '#66AA00', '#B82E2E', - '#316395', '#994499', '#22AA99', '#AAAA11', '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' -]; - -var minPieFraction = 0.005; -var minPieOtherFraction = 0.01; -var maxPieOtherFraction = 0.20; -var pieOtherOnlyIfNeeded = false; - -function processDataForPieChart(data, viewCols) { - if (pieOtherOnlyIfNeeded && data.length <= pieColors.length) { - return data; - } - var col = viewCols || [0, 1]; - var total = data.reduce(function getSum(accumulator, currentValue) { - return accumulator + currentValue[col[1]]; - }, 0); - var i = data.length; - var othersFraction = 0; - while (i > 1 - && (i > pieColors.length - || (data[i - 1][col[1]] / total < minPieFraction - && data[i - 1][col[1]] / total + othersFraction < maxPieOtherFraction))) { - i -= 1; - othersFraction += data[i][col[1]] / total; - } - if (othersFraction < minPieOtherFraction) { - i = Math.min(data.length, pieColors.length); - othersFraction = 0; - } - var processedData = data.slice(0, i); - if (i < data.length && othersFraction > 0) { - var theOthers = [null, null, null]; - theOthers[col[1]] = Math.round(othersFraction * total); - theOthers[col[0]] = getTranslation('other'); - processedData.push(theOthers); - } - return { data: processedData, other: othersFraction > 0, total: total }; -} - -function drawPieChart(colNames, dataName, viewCols, url, getEl) { - var el = getEl(); - if (!el) return; - - var ctx = el.getContext('2d'); - var processedData = processDataForPieChart(getStatisticsData(dataName), viewCols); - var data = processedData.data; - var other = processedData.other; - var total = processedData.total; - var col = viewCols || [0, 1]; - var colors = pieColors.slice(); - if (other) { - colors[data.length - 1] = '#DDDDDD'; - } - var chart = new Chart(ctx, { - type: 'pie', - data: { - labels: data.map(function getFirst(row) { return row[col[0]]; }), - datasets: [{ - data: data.map(function getSecond(row) { return row[col[1]]; }), - backgroundColor: colors, - borderWidth: 1 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - legend: { - display: false - }, - tooltips: { - callbacks: { - label: function generateLabel(tooltipItem, myData) { - var label = myData.datasets[tooltipItem.datasetIndex].label || ''; - - if (label) { - label += ': '; - } - var value = myData.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - label += value + ' ('; - label += Math.round((value / total) * 1000) / 10; - label += ' %)'; - return label; - } - } - }, - legendCallback: function legendCallback(c) { - var text = []; - text.push('
    '); - - var datasets = c.data.datasets; - var labels = c.data.labels; - - if (datasets.length) { - for (var i = 0; i < datasets[0].data.length; ++i) { - var liClasses = ['chart-legend-item']; - if (other && i === datasets[0].data.length - 1) { - liClasses.push('other'); - } - text.push('
  • '); - if (labels[i]) { - var label = labels[i]; - if (url && (!other || i < datasets[0].data.length - 1)) { - label = '' + label + ''; - } else { - label = '' + label + ''; - } - text.push(label); - } - text.push('
  • '); - } - } - - text.push('
'); - return text.join(''); - } - } - }); - if (url) { - el.addEventListener('click', function pieClick(evt) { - var activePoints = chart.getElementsAtEvent(evt); - if (activePoints[0] - && data[activePoints[0]._index][1] !== null) { // eslint-disable-line no-underscore-dangle - window.location.href = url + encodeURIComponent( - data[activePoints[0]._index][1] // eslint-disable-line no-underscore-dangle - ); - } - }); - } - var legendContainer = el.parentNode.parentNode.querySelector('.legend-container'); - legendContainer.innerHTML = chart.generateLegend(); -} - -var drawIdpsChart = drawPieChart.bind(null, ['sourceIdpName', 'sourceIdPEntityId', 'Count'], 'loginCountPerIdp', - [0, 2], 'idpDetail.php?entityId='); - -var drawSpsChart = drawPieChart.bind(null, ['service', 'serviceIdentifier', 'Count'], 'accessCountPerService', - [0, 2], 'spDetail.php?identifier='); - -function drawCountTable(cols, dataCol, countCol, dataName, allowHTML, url, getEl) { - var el = getEl(); - if (!el) return; - - var viewCols = [dataCol, countCol]; - - var tableDiv = el.appendChild(document.createElement('div')); - tableDiv.className = 'table-responsive'; - - var table = tableDiv.appendChild(document.createElement('table')); - table.className = 'table table-striped table-hover table-condensed'; - - var data = getStatisticsData(dataName); - - var thead = table.appendChild(document.createElement('thead')); - var tr = thead.appendChild(document.createElement('tr')); - var th; - var i; - for (i = 0; i < viewCols.length; i++) { - th = tr.appendChild(document.createElement('th')); - th.innerText = getTranslation(cols[i]); - if (viewCols[i] === countCol) { - th.className = 'text-right'; - } - } - - var tbody = table.appendChild(document.createElement('tbody')); - var td; - var a; - for (var j = 0; j < data.length; j++) { - tr = tbody.appendChild(document.createElement('tr')); - for (i = 0; i < viewCols.length; i++) { - td = tr.appendChild(document.createElement('td')); - if (viewCols[i] === countCol) { - td.className = 'text-right'; - } - if (url && viewCols[i] === dataCol) { - a = document.createElement('a'); - a[allowHTML ? 'innerHTML' : 'innerText'] = data[j][viewCols[i]]; - a.href = url + encodeURIComponent(data[j][1]); - td.appendChild(a); - } else { - td[allowHTML ? 'innerHTML' : 'innerText'] = data[j][viewCols[i]]; - } - } - } - - el.addEventListener('scroll', function floatingTableHead() { - var scrolling = el.scrollTop > 0; - el.classList.toggle('scrolling', scrolling); - el.querySelectorAll('th').forEach(function floatTh(the) { - the.style.transform = scrolling ? ('translateY(' + el.scrollTop + 'px)') : ''; // eslint-disable-line no-param-reassign - }); - }); -} - -var drawIdpsTable = drawCountTable.bind(null, ['tables_identity_provider', 'count'], 0, 2, - 'loginCountPerIdp', false, 'idpDetail.php?entityId='); - -var drawAccessedSpsChart = drawPieChart.bind(null, ['service', 'Count'], - 'accessCountForIdentityProviderPerServiceProviders', null, null); - -var drawAccessedSpsTable = drawCountTable.bind(null, ['tables_service_provider', 'count'], 0, 1, - 'accessCountForIdentityProviderPerServiceProviders', true, null); - -var drawSpsTable = drawCountTable.bind(null, ['tables_service_provider', 'count'], 0, 2, - 'accessCountPerService', true, 'spDetail.php?identifier='); - -var drawUsedIdpsChart = drawPieChart.bind(null, ['service', 'Count'], - 'accessCountForServicePerIdentityProviders', null, null); - -var drawUsedIdpsTable = drawCountTable.bind(null, ['tables_identity_provider', 'count'], 0, 1, - 'accessCountForServicePerIdentityProviders', true, null); - -function getterLoadCallback(getEl, callback) { - callback(getEl); -} - -function classLoadCallback(className, callback) { - getterLoadCallback(function () { return $('.' + className + ':visible')[0]; }, callback); // eslint-disable-line func-names -} - -function idLoadCallback(id, callback) { - getterLoadCallback(document.getElementById.bind(document, id), callback); -} - -function chartInit() { - idLoadCallback('loginsDashboard', drawLoginsChart); - classLoadCallback('chart-idpsChart', drawIdpsChart); - classLoadCallback('chart-spsChart', drawSpsChart); - idLoadCallback('idpsTable', drawIdpsTable); - idLoadCallback('accessedSpsChartDetail', drawAccessedSpsChart); - idLoadCallback('accessedSpsTable', drawAccessedSpsTable); - idLoadCallback('spsTable', drawSpsTable); - idLoadCallback('usedIdPsChartDetail', drawUsedIdpsChart); - idLoadCallback('usedIdPsTable', drawUsedIdpsTable); - $('#dateSelector input[name=lastDays]').on('click', function submitForm() { - this.form.submit(); - }); -} - -$(document).ready(function docReady() { - Chart.platform.disableCSSInjection = true; - var loginsDashboard = document.getElementById('loginsDashboard'); - if (loginsDashboard !== null && loginsDashboard.dataset.locale === 'cs') { - moment.locale(loginsDashboard.dataset.locale); - } - - $('#tabdiv').tabs({ - selected: $('#tabdiv').data('activetab'), - load: chartInit - }); - chartInit(); -}); diff --git a/www/index.php b/www/index.php deleted file mode 100644 index 856d0ab..0000000 --- a/www/index.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ - -const CONFIG_FILE_NAME_STATISTICSPROXY = 'module_statisticsproxy.php'; -const MODE = 'mode'; - -$config = Configuration::getInstance(); -$session = Session::getSessionFromRequest(); - -$configStatisticsproxy = Configuration::getConfig(CONFIG_FILE_NAME_STATISTICSPROXY); - -$authSource = $configStatisticsproxy->getString('requireAuth.source', ''); -if ($authSource) { - $as = new \SimpleSAML\Auth\Simple($authSource); - $as->requireAuth(); -} - -$mode = $configStatisticsproxy->getString(MODE, 'PROXY'); - -$t = new Template($config, 'proxystatistics:statistics-tpl.php'); - -$lastDays = filter_input( - INPUT_POST, - 'lastDays', - FILTER_VALIDATE_INT, - ['options' => ['default' => 0, 'min_range' => 0]] -); - -$t->data['lastDays'] = $lastDays; - -$t->data['tab'] = filter_input( - INPUT_POST, - 'tab', - FILTER_VALIDATE_INT, - ['options' => ['default' => 0, 'min_range' => 1]] -); - -if ($mode === 'IDP') { - $t->data['tabsAttributes'] = [ - 'PROXY' => 'id="tab-1" href="summary.php?lastDays=' . $lastDays . '"', - 'IDP' => 'class="hidden" id="tab-2" href="identityProviders.php?lastDays=' . $lastDays . '"', - 'SP' => 'id="tab-3" href="serviceProviders.php?lastDays=' . $lastDays . '"', - ]; -} elseif ($mode === 'SP') { - $t->data['tabsAttributes'] = [ - 'PROXY' => 'id="tab-1" href="summary.php?lastDays=' . $lastDays . '"', - 'IDP' => 'id="tab-2" href="identityProviders.php?lastDays=' . $lastDays . '"', - 'SP' => 'class="hidden" id="tab-3" href="serviceProviders.php?lastDays=' . $lastDays . '"', - ]; -} elseif ($mode === 'PROXY') { - $t->data['tabsAttributes'] = [ - 'PROXY' => 'id="tab-1" href="summary.php?lastDays=' . $lastDays . '"', - 'IDP' => 'id="tab-2" href="identityProviders.php?lastDays=' . $lastDays . '"', - 'SP' => 'id="tab-3" href="serviceProviders.php?lastDays=' . $lastDays . '"', - ]; -} - -$t->show(); diff --git a/www/moment.cs.min.js b/www/moment.cs.min.js deleted file mode 100644 index 27809d5..0000000 --- a/www/moment.cs.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/* https://github.com/moment/moment/blob/2.24.0/min/locales.js */ -var months$2='leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec'.split('_'),monthsShort='led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro'.split('_');var monthsParse=[/^led/i,/^úno/i,/^bře/i,/^dub/i,/^kvě/i,/^(čvn|červen$|června)/i,/^(čvc|červenec|července)/i,/^srp/i,/^zář/i,/^říj/i,/^lis/i,/^pro/i];var monthsRegex=/^(leden|únor|březen|duben|květen|červenec|července|červen|června|srpen|září|říjen|listopad|prosinec|led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i;function plural$1(n){return(n>1)&&(n<5)&&(~~(n/10)!==1)}function translate$1(number,withoutSuffix,key,isFuture){var result=number+' ';switch(key){case 's':return(withoutSuffix||isFuture)?'pár sekund':'pár sekundami';case 'ss':if(withoutSuffix||isFuture){return result+(plural$1(number)?'sekundy':'sekund')}else{return result+'sekundami'}break;case 'm':return withoutSuffix?'minuta':(isFuture?'minutu':'minutou');case 'mm':if(withoutSuffix||isFuture){return result+(plural$1(number)?'minuty':'minut')}else{return result+'minutami'}break;case 'h':return withoutSuffix?'hodina':(isFuture?'hodinu':'hodinou');case 'hh':if(withoutSuffix||isFuture){return result+(plural$1(number)?'hodiny':'hodin')}else{return result+'hodinami'}break;case 'd':return(withoutSuffix||isFuture)?'den':'dnem';case 'dd':if(withoutSuffix||isFuture){return result+(plural$1(number)?'dny':'dní')}else{return result+'dny'}break;case 'M':return(withoutSuffix||isFuture)?'měsíc':'měsícem';case 'MM':if(withoutSuffix||isFuture){return result+(plural$1(number)?'měsíce':'měsíců')}else{return result+'měsíci'}break;case 'y':return(withoutSuffix||isFuture)?'rok':'rokem';case 'yy':if(withoutSuffix||isFuture){return result+(plural$1(number)?'roky':'let')}else{return result+'lety'}break}}moment.defineLocale('cs',{months:months$2,monthsShort:monthsShort,monthsRegex:monthsRegex,monthsShortRegex:monthsRegex,monthsStrictRegex:/^(leden|ledna|února|únor|březen|března|duben|dubna|květen|května|červenec|července|červen|června|srpen|srpna|září|říjen|října|listopadu|listopad|prosinec|prosince)/i,monthsShortStrictRegex:/^(led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i,monthsParse:monthsParse,longMonthsParse:monthsParse,shortMonthsParse:monthsParse,weekdays:'neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota'.split('_'),weekdaysShort:'ne_po_út_st_čt_pá_so'.split('_'),weekdaysMin:'ne_po_út_st_čt_pá_so'.split('_'),longDateFormat:{LT:'H:mm',LTS:'H:mm:ss',L:'DD.MM.YYYY',LL:'D. MMMM YYYY',LLL:'D. MMMM YYYY H:mm',LLLL:'dddd D. MMMM YYYY H:mm',l:'D. M. YYYY'},calendar:{sameDay:'[dnes v] LT',nextDay:'[zítra v] LT',nextWeek:function(){switch(this.day()){case 0:return '[v neděli v] LT';case 1:case 2:return '[v] dddd [v] LT';case 3:return '[ve středu v] LT';case 4:return '[ve čtvrtek v] LT';case 5:return '[v pátek v] LT';case 6:return '[v sobotu v] LT'}},lastDay:'[včera v] LT',lastWeek:function(){switch(this.day()){case 0:return '[minulou neděli v] LT';case 1:case 2:return '[minulé] dddd [v] LT';case 3:return '[minulou středu v] LT';case 4:case 5:return '[minulý] dddd [v] LT';case 6:return '[minulou sobotu v] LT'}},sameElse:'L'},relativeTime:{future:'za %s',past:'před %s',s:translate$1,ss:translate$1,m:translate$1,mm:translate$1,h:translate$1,hh:translate$1,d:translate$1,dd:translate$1,M:translate$1,MM:translate$1,y:translate$1,yy:translate$1},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:'%d.',week:{dow:1,doy:4}}); diff --git a/www/moment.min.js b/www/moment.min.js deleted file mode 100644 index 0177eb8..0000000 --- a/www/moment.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function c(){return e.apply(null,arguments)}function o(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function u(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function l(e){return void 0===e}function h(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function f(e,t){var n,s=[];for(n=0;n>>0,s=0;sSe(e)?(r=e+1,o-Se(e)):(r=e,o),{year:r,dayOfYear:a}}function Ie(e,t,n){var s,i,r=Ve(e.year(),t,n),a=Math.floor((e.dayOfYear()-r-1)/7)+1;return a<1?s=a+Ae(i=e.year()-1,t,n):a>Ae(e.year(),t,n)?(s=a-Ae(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function Ae(e,t,n){var s=Ve(e,t,n),i=Ve(e+1,t,n);return(Se(e)-s+i)/7}I("w",["ww",2],"wo","week"),I("W",["WW",2],"Wo","isoWeek"),C("week","w"),C("isoWeek","W"),F("week",5),F("isoWeek",5),ue("w",B),ue("ww",B,z),ue("W",B),ue("WW",B,z),fe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=D(e)});function je(e,t){return e.slice(t,7).concat(e.slice(0,t))}I("d",0,"do","day"),I("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),I("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),I("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),I("e",0,0,"weekday"),I("E",0,0,"isoWeekday"),C("day","d"),C("weekday","e"),C("isoWeekday","E"),F("day",11),F("weekday",11),F("isoWeekday",11),ue("d",B),ue("e",B),ue("E",B),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),fe(["dd","ddd","dddd"],function(e,t,n,s){var i=n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:g(n).invalidWeekday=e}),fe(["d","e","E"],function(e,t,n,s){t[s]=D(e)});var Ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var ze="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var $e="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var qe=ae;var Je=ae;var Be=ae;function Qe(){function e(e,t){return t.length-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=y([2e3,1]).day(t),s=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),r=this.weekdays(n,""),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a.sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=he(o[t]),u[t]=he(u[t]),l[t]=he(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Xe(){return this.hours()%12||12}function Ke(e,t){I(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}I("H",["HH",2],0,"hour"),I("h",["hh",2],0,Xe),I("k",["kk",2],0,function(){return this.hours()||24}),I("hmm",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)}),I("hmmss",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),I("Hmm",0,0,function(){return""+this.hours()+L(this.minutes(),2)}),I("Hmmss",0,0,function(){return""+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Ke("a",!0),Ke("A",!1),C("hour","h"),F("hour",13),ue("a",et),ue("A",et),ue("H",B),ue("h",B),ue("k",B),ue("HH",B,z),ue("hh",B,z),ue("kk",B,z),ue("hmm",Q),ue("hmmss",X),ue("Hmm",Q),ue("Hmmss",X),ce(["H","HH"],ge),ce(["k","kk"],function(e,t,n){var s=D(e);t[ge]=24===s?0:s}),ce(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),ce(["h","hh"],function(e,t,n){t[ge]=D(e),g(n).bigHour=!0}),ce("hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s)),g(n).bigHour=!0}),ce("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i)),g(n).bigHour=!0}),ce("Hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s))}),ce("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i))});var tt,nt=Te("Hours",!0),st={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ce,monthsShort:He,week:{dow:0,doy:6},weekdays:Ze,weekdaysMin:$e,weekdaysShort:ze,meridiemParse:/[ap]\.?m?\.?/i},it={},rt={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=tt._abbr,require("./locale/"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=l(t)?ht(e):lt(e,t))?tt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,s=st;if(t.abbr=e,null!=it[e])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])s=it[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return rt[t.parentLocale]||(rt[t.parentLocale]=[]),rt[t.parentLocale].push({name:e,config:t}),null;s=n._config}return it[e]=new P(x(s,t)),rt[e]&&rt[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ht(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!o(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,s,i,r=0;r=t&&a(i,n,!0)>=t-1)break;t--}r++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===g(e).overflow&&(t=n[_e]<0||11Pe(n[me],n[_e])?ye:n[ge]<0||24Ae(n,r,a)?g(e)._overflowWeeks=!0:null!=u?g(e)._overflowWeekday=!0:(o=Ee(n,s,i,r,a),e._a[me]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(r=ct(e._a[me],s[me]),(e._dayOfYear>Se(r)||0===e._dayOfYear)&&(g(e)._overflowDayOfYear=!0),n=Ge(r,0,e._dayOfYear),e._a[_e]=n.getUTCMonth(),e._a[ye]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=s[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[pe]&&0===e._a[we]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Ge:function(e,t,n,s,i,r,a){var o;return e<100&&0<=e?(o=new Date(e+400,t,n,s,i,r,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,s,i,r,a),o}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(g(e).weekdayMismatch=!0)}}var mt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_t=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,yt=/Z|[+-]\d\d(?::?\d\d)?/,gt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],vt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],pt=/^\/?Date\((\-?\d+)/i;function wt(e){var t,n,s,i,r,a,o=e._i,u=mt.exec(o)||_t.exec(o);if(u){for(g(e).iso=!0,t=0,n=gt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},mn.isLocal=function(){return!!this.isValid()&&!this._isUTC},mn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},mn.isUtc=Et,mn.isUTC=Et,mn.zoneAbbr=function(){return this._isUTC?"UTC":""},mn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},mn.dates=n("dates accessor is deprecated. Use date instead.",un),mn.months=n("months accessor is deprecated. Use month instead",Ue),mn.years=n("years accessor is deprecated. Use year instead",Oe),mn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),mn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e={};if(w(e,this),(e=Ot(e))._a){var t=e._isUTC?y(e._a):bt(e._a);this._isDSTShifted=this.isValid()&&0 - */ - -$config = Configuration::getInstance(); -$session = Session::getSessionFromRequest(); - -$t = new Template($config, 'proxystatistics:serviceProviders-tpl.php'); -$t->data['lastDays'] = filter_input( - INPUT_GET, - 'lastDays', - FILTER_VALIDATE_INT, - ['options' => ['default' => 0, 'min_range' => 0]] -); -$t->data['tab'] = 2; -$t->show(); diff --git a/www/spDetail.php b/www/spDetail.php deleted file mode 100644 index 75aed11..0000000 --- a/www/spDetail.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ - -const CONFIG_FILE_NAME_STATISTICSPROXY = 'module_statisticsproxy.php'; -const MODE = 'mode'; - -$config = Configuration::getInstance(); -$session = Session::getSessionFromRequest(); - -$configStatisticsproxy = Configuration::getConfig(CONFIG_FILE_NAME_STATISTICSPROXY); -$mode = $configStatisticsproxy->getString(MODE, 'PROXY'); - -$t = new Template($config, 'proxystatistics:spDetail-tpl.php'); - -$t->data['lastDays'] = filter_input( - INPUT_POST, - 'lastDays', - FILTER_VALIDATE_INT, - ['options' => ['default' => 0, 'min_range' => 0]] -); -$t->data['identifier'] = filter_input(INPUT_GET, 'identifier', FILTER_SANITIZE_STRING); - -if ($mode === 'IDP') { - $t->data['spDetailGraphClass'] = 'hidden'; -} else { - $t->data['spDetailGraphClass'] = ''; -} - -$t->show(); diff --git a/www/statisticsproxy.css b/www/statisticsproxy.css deleted file mode 100644 index dee2b43..0000000 --- a/www/statisticsproxy.css +++ /dev/null @@ -1,123 +0,0 @@ -#content, #languagebar, #header { - max-width: 100%; - padding: 0 40px; -} - -#content, #footer { - padding: 0; -} - -h1, h2, h3 { - padding: 0 40px; -} - -#wrap { - max-width: 100%; -} - -#loginsDashboard { - height: 500px; -} - -#line_div { - height: 260px; -} - -#control_div { - height: 40px; -} - -#dateSelector label{ - font-weight: normal; -} - -#summaryIdp { - padding-bottom: 20px; -} - -.tabset_tabs { - padding-left: 20px; -} - -.timeRange { - padding-left: 40px; -} - -.legend-logins { - padding-left: 40px; -} - -.legend { - padding-left: 40px; - padding-bottom: 20px; -} - -.go-to-stats-btn { - padding-left: 40px; -} - -.legend-container, .canvas-container, .table-container { - height: 90vh; - max-height: 500px; - max-width: 100%; -} - -.hidden { - display: none; -} - -.pie-chart-container .legend-container, .table-container { - overflow-y: auto; -} - -.chart-legend .item { - display: inline-block; - text-indent: -2.5em; - margin-left: 2.5em; -} - -.chart-legend li .item:before { - content: ''; - display: inline-block; - width: 2em; - height: 0.7em; - margin-right: 0.5em; -} -.chart-legend li:nth-child(20n+1) .item:before { background-color: #3366CC; } -.chart-legend li:nth-child(20n+2) .item:before { background-color: #DC3912; } -.chart-legend li:nth-child(20n+3) .item:before { background-color: #FF9900; } -.chart-legend li:nth-child(20n+4) .item:before { background-color: #109618; } -.chart-legend li:nth-child(20n+5) .item:before { background-color: #990099; } -.chart-legend li:nth-child(20n+6) .item:before { background-color: #3B3EAC; } -.chart-legend li:nth-child(20n+7) .item:before { background-color: #0099C6; } -.chart-legend li:nth-child(20n+8) .item:before { background-color: #DD4477; } -.chart-legend li:nth-child(20n+9) .item:before { background-color: #66AA00; } -.chart-legend li:nth-child(20n+10) .item:before { background-color: #B82E2E; } -.chart-legend li:nth-child(20n+11) .item:before { background-color: #316395; } -.chart-legend li:nth-child(20n+12) .item:before { background-color: #994499; } -.chart-legend li:nth-child(20n+13) .item:before { background-color: #22AA99; } -.chart-legend li:nth-child(20n+14) .item:before { background-color: #AAAA11; } -.chart-legend li:nth-child(20n+15) .item:before { background-color: #6633CC; } -.chart-legend li:nth-child(20n+16) .item:before { background-color: #E67300; } -.chart-legend li:nth-child(20n+17) .item:before { background-color: #8B0707; } -.chart-legend li:nth-child(20n+18) .item:before { background-color: #329262; } -.chart-legend li:nth-child(20n+19) .item:before { background-color: #5574A6; } -.chart-legend li:nth-child(20n) .item:before { background-color: #3B3EAC; } -.chart-legend li.other .item:before { background-color: #DDDDDD; } - -.legend-container ul, .legend-container li { - list-style-type: none; -} - -.table-container.scrolling th { - background-color: white; - box-shadow: 0 4px 5px -5px #666; -} - -.table-container a, .chart-legend a { - border: 0; -} -.table-container a:not(:hover):not(:focus):not(:active), -.chart-legend a:not(:hover):not(:focus):not(:active) { - text-decoration: none; -} diff --git a/www/summary.php b/www/summary.php deleted file mode 100644 index 48d58bd..0000000 --- a/www/summary.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ - -const CONFIG_FILE_NAME = 'module_statisticsproxy.php'; -const MODE = 'mode'; - -$configMode = Configuration::getConfig(CONFIG_FILE_NAME); -$config = Configuration::getInstance(); -$session = Session::getSessionFromRequest(); - -$mode = $configMode->getString(MODE, 'PROXY'); - -$t = new Template($config, 'proxystatistics:summary-tpl.php'); - -$t->data['lastDays'] = filter_input( - INPUT_GET, - 'lastDays', - FILTER_VALIDATE_INT, - ['options' => ['default' => 0, 'min_range' => 0]] -); -$t->data['tab'] = 0; - -if ($mode === 'IDP') { - $t->data['summaryGraphs'] = [ - 'identityProviders' => 'hidden', - 'identityProvidersLegend' => '', - 'identityProvidersGraph' => '', - 'serviceProviders' => 'col-md-12 graph', - 'serviceProvidersLegend' => 'col-md-6', - 'serviceProvidersGraph' => 'col-md-6 col-md-offset-3' - ]; -} elseif ($mode === 'SP') { - $t->data['summaryGraphs'] = [ - 'identityProviders' => 'col-md-12 graph', - 'identityProvidersLegend' => 'col-md-6', - 'identityProvidersGraph' => 'col-md-6 col-md-offset-3', - 'serviceProviders' => 'hidden', - 'serviceProvidersLegend' => '', - 'serviceProvidersGraph' => '' - ]; -} elseif ($mode === 'PROXY') { - $t->data['summaryGraphs'] = [ - 'identityProviders' => 'col-md-6 graph', - 'identityProvidersLegend' => 'col-md-12', - 'identityProvidersGraph' => 'col-md-12', - 'serviceProviders' => 'col-md-6 graph', - 'serviceProvidersLegend' => 'col-md-12', - 'serviceProvidersGraph' => 'col-md-12' - ]; -} else { - Logger::error('Unknown mode is set. Mode has to be one of the following: PROXY, IDP, SP.'); -} - -$t->show(); From 957a20896f92450d3824fccd04fcaa66a28abd31 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Wed, 13 Dec 2023 12:46:06 +0200 Subject: [PATCH 2/3] curl publish to ams --- src/Auth/Process/AmsConnector.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Auth/Process/AmsConnector.php b/src/Auth/Process/AmsConnector.php index 80435b1..82b9ac7 100644 --- a/src/Auth/Process/AmsConnector.php +++ b/src/Auth/Process/AmsConnector.php @@ -44,6 +44,12 @@ class AmsConnector const TABLE_PREFIX = 'database.prefix'; const AMS_INJEST_ENDPOINT = '/ams/ingest'; + const AMS_BASE_URL="https://msg-devel.argo.grnet.gr/v1"; + const AMS_USER_TOKEN="24e2b1fa7e367a6722e16b94765264082fca56022ac68dcc8728c26376d65dd6"; + const AMS_ADMIN_TOKEN="25bbd90ba4bb38df217bf02c5369dff30bb524a0ad0c4a5666bba602ee01d794"; + private $topic_name = "metrics"; + private $project_name = "AAIMETRICS"; + public function __construct() { $conf = Configuration::getConfig(self::CONFIG_FILE_NAME); @@ -112,4 +118,29 @@ public function getKeycloakSp() { return $this->keycloakSp; } + + public function sendToAms($data) { + $url = self::AMS_BASE_URL . "/projects/{$this->poject_name}/topics/{$this->topic_name}:publish"; + + $cURLConnection = curl_init(); + + curl_setopt($cURLConnection, CURLOPT_URL, $url); + curl_setopt($cURLConnection, CURLOPT_RETURNTRANSFER, true); + curl_setopt($cURLConnection, CURLOPT_HTTPHEADER, array( + "Content-Type: application/json", + "x-api-key: " . self::AMS_ADMIN_TOKEN, + )); + curl_setopt($cURLConnection, CURLOPT_HTTPHEADER, array( + "Content-Type: application/json", + "x-api-key: " . self::AMS_USER_TOKEN, + )); + curl_setopt($cURLConnection, CURLOPT_POSTFIELDS, json_encode($data)); + + $amd_response = curl_exec($cURLConnection); + curl_close($cURLConnection); + + $jsonArrayResponse = json_decode($amd_response); + + } + } \ No newline at end of file From 1877ebd5e9915df7f1772661a9f58dfcd0e83592 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Thu, 14 Dec 2023 10:27:49 +0200 Subject: [PATCH 3/3] remove database related code --- src/Auth/Process/AmsCommand.php | 19 +- src/Auth/Process/AmsConnector.php | 5 - src/Auth/Process/DatabaseCommand.php | 453 --------------------------- src/Auth/Process/Statistics.php | 5 +- 4 files changed, 11 insertions(+), 471 deletions(-) delete mode 100644 src/Auth/Process/DatabaseCommand.php diff --git a/src/Auth/Process/AmsCommand.php b/src/Auth/Process/AmsCommand.php index 467c0f9..d73703a 100644 --- a/src/Auth/Process/AmsCommand.php +++ b/src/Auth/Process/AmsCommand.php @@ -18,12 +18,10 @@ class AmsCommand public function __construct() { $this->amsConnector = new AmsConnector(); - $this->conn = $this->amsConnector->getConnection(); - assert($this->conn !== null); } private function writeLoginIp($sourceIdp, $service, $user, $ip, $date): void { - $params = [ + $data = [ 'ip' => $ip, 'user' => $user, 'sourceIdp' => $sourceIdp, @@ -32,11 +30,11 @@ private function writeLoginIp($sourceIdp, $service, $user, $ip, $date): void 'ipVersion' => (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 'ipv4' : 'ipv6') ]; - // todo: Send to ams + $this->amsConnector->sendToAms($data); } private function writeLogin($year, $month, $day, $sourceIdp, $service, $user = null) { - $params = [ + $data = [ 'year' => $year, 'month' => $month, 'day' => $day, @@ -44,12 +42,12 @@ private function writeLogin($year, $month, $day, $sourceIdp, $service, $user = n 'service' => $service, 'count' => 1, ]; - $table = $this->statisticsTableName; + if ($user && $this->amsConnector->getDetailedDays() > 0) { - $params['user'] = $user; + $data['user'] = $user; } - // todo: Send ams + $this->amsConnector->sendToAms($data); } public function insertLogin(&$request, &$date, &$userId) @@ -128,7 +126,7 @@ public function insertLogin(&$request, &$date, &$userId) if (!empty($idpName)) { $data = ['entityId' => $idpEntityID, 'idpName' => $idpName, 'idpName2' => $idpName]; - // todo: send to ams + $this->amsConnector->sendToAms($data); } if (!empty($spName)) { @@ -137,7 +135,8 @@ public function insertLogin(&$request, &$date, &$userId) 'spName' => $spName, 'spName2' => $spName ]; - // todo send to ams + $this->amsConnector->sendToAms($data); + } } } diff --git a/src/Auth/Process/AmsConnector.php b/src/Auth/Process/AmsConnector.php index 82b9ac7..5e7a3c7 100644 --- a/src/Auth/Process/AmsConnector.php +++ b/src/Auth/Process/AmsConnector.php @@ -69,11 +69,6 @@ public function __construct() $this->keycloakSp = $conf->getString(self::KEYCLOAK_SP, null); } - public function getConnection() - { - return Database::getInstance($this->storeConfig); - } - public function getMode() { return $this->mode; diff --git a/src/Auth/Process/DatabaseCommand.php b/src/Auth/Process/DatabaseCommand.php deleted file mode 100644 index 7d29faf..0000000 --- a/src/Auth/Process/DatabaseCommand.php +++ /dev/null @@ -1,453 +0,0 @@ - - */ -class DatabaseCommand -{ - private $databaseConnector; - private $conn; - private $dbDriver; - private $statisticsTableName; - private $detailedStatisticsTableName; - private $identityProvidersMapTableName; - private $serviceProvidersMapTableName; - - public function __construct() - { - $this->databaseConnector = new DatabaseConnector(); - $this->conn = $this->databaseConnector->getConnection(); - assert($this->conn !== null); - $this->dbDriver = $this->databaseConnector->getDbDriver(); - $this->statisticsTableName = $this->databaseConnector->getStatisticsTableName(); - $this->detailedStatisticsTableName = $this->databaseConnector->getDetailedStatisticsTableName(); - $this->ipStatisticsTableName = $this->databaseConnector->getIpStatisticsTableName(); - $this->identityProvidersMapTableName = $this->databaseConnector->getIdentityProvidersMapTableName(); - $this->serviceProvidersMapTableName = $this->databaseConnector->getServiceProvidersMapTableName(); - } - private function writeLoginIp($sourceIdp, $service, $user, $ip, $date) - { - $params = [ - 'ip' => ($this->dbDriver == 'pgsql' ? $ip : inet_pton($ip)), - 'sourceIdp' => $sourceIdp, - 'service' => $service, - 'accessed' => $date, - 'ipVersion' => (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 'ipv4' : 'ipv6') - ]; - if ($this->dbDriver == 'pgsql') { - // the word "user" is reserved from PostgreSQL - $params['userid'] = $user; - } else { - $params['user'] = $user; - } - $table = $this->ipStatisticsTableName; - $fields = array_keys($params); - $placeholders = array_map(function ($field) { - return ':' . $field; - }, $fields); - - if (empty($params['ip'])) { - Logger::error("[proxystatistics] Couldn't find ip for storing information to table"); - return false; - } - if ($this->dbDriver == 'pgsql') { - $query = "INSERT INTO " . $table . " (" . implode(', ', $fields) . ")" . - " VALUES (" . implode(', ', $placeholders) . ")"; - } else { - $query = "INSERT INTO " . $table . " (" . implode(', ', $fields) . ")" . - " VALUES (" . implode(', ', $placeholders) . ")"; - } - - return $this->conn->write($query, $params); - } - private function writeLogin($year, $month, $day, $sourceIdp, $service, $user = null) - { - $params = [ - 'year' => $year, - 'month' => $month, - 'day' => $day, - 'sourceIdp' => $sourceIdp, - 'service' => $service, - 'count' => 1, - ]; - $table = $this->statisticsTableName; - if ($user && $this->databaseConnector->getDetailedDays() > 0) { - // write also into aggregated statistics - self::writeLogin($year, $month, $day, $sourceIdp, $service); - if ($this->dbDriver == 'pgsql') { - // the word "user" is reserved from PostgreSQL - $params['userid'] = $user; - } else { - $params['user'] = $user; - } - $table = $this->detailedStatisticsTableName; - } - $fields = array_keys($params); - $placeholders = array_map(function ($field) { - return ':' . $field; - }, $fields); - if ($this->dbDriver == 'pgsql') { - // remove count field from ON CONFLICT statement - $conflictFields = $fields; - $pos = array_search('count', $conflictFields); - unset($conflictFields[$pos]); - $query = "INSERT INTO " . $table . " (" . implode(', ', $fields) . ")" . - " VALUES (" . implode(', ', $placeholders) . ")" . - " ON CONFLICT (" . implode(', ', $conflictFields) . ")" . - " DO UPDATE SET count = " . $table . ".count + 1"; - } else { - $query = "INSERT INTO " . $table . " (" . implode(', ', $fields) . ")" . - " VALUES (" . implode(', ', $placeholders) . ")" . - " ON DUPLICATE KEY UPDATE count = count + 1"; - } - - return $this->conn->write($query, $params); - } - - public function insertLogin(&$request, &$date, &$userId) - { - if (!in_array($this->databaseConnector->getMode(), ['PROXY', 'IDP', 'SP'])) { - throw new Exception('Unknown mode is set. Mode has to be one of the following: PROXY, IDP, SP.'); - } - if ($this->databaseConnector->getMode() !== 'IDP') { - if (!empty($request['saml:sp:IdP'])) { - $idpEntityID = $request['saml:sp:IdP']; - $idpMetadata = MetaDataStorageHandler::getMetadataHandler()->getMetaData($idpEntityID, 'saml20-idp-remote'); - } else { - $idpEntityID = $request['Source']['entityid']; - $idpMetadata = $request['Source']; - } - $idpName = self::getIdPDisplayName($idpMetadata); - } - if ($this->databaseConnector->getMode() !== 'SP') { - if ( - !empty($request['saml:RelayState']) - && !empty($this->databaseConnector->getKeycloakSp()) - && strpos($request['Destination']['entityid'], $this->databaseConnector->getKeycloakSp()) !== false - ) { - $spEntityId = explode('.', $request['saml:RelayState'], 3)[2]; - $spName = null; - } elseif ( - !empty($request['saml:RequesterID']) - && !empty($this->databaseConnector->getOidcIssuer()) - && (strpos($request['Destination']['entityid'], $this->databaseConnector->getOidcIssuer()) !== false) - ) { - $spEntityId = str_replace( - $this->databaseConnector->getOidcIssuer() . "/", - "", - $request['saml:RequesterID'][0] - ); - $spName = null; - } elseif ( - !empty($request['saml:RelayState']) - && !empty($this->databaseConnector->getOidcIssuer()) - && strpos($request['Destination']['entityid'], $this->databaseConnector->getOidcIssuer()) !== false - ) { - $spEntityId = $request['saml:RelayState']; - $spName = null; - } else { - $spEntityId = $request['Destination']['entityid']; - $spName = self::getSPDisplayName($request['Destination']); - } - } - - if ($this->databaseConnector->getMode() === 'IDP') { - $idpName = $this->databaseConnector->getIdpName(); - $idpEntityID = $this->databaseConnector->getIdpEntityId(); - } elseif ($this->databaseConnector->getMode() === 'SP') { - $spEntityId = $this->databaseConnector->getSpEntityId(); - $spName = $this->databaseConnector->getSpName(); - } - - $year = $date->format('Y'); - $month = $date->format('m'); - $day = $date->format('d'); - $dateTimestamp = $date->format('Y-m-d H:i:s T'); - $ip = Utils::getClientIpAddress(); - - if (empty($idpEntityID) || empty($spEntityId)) { - Logger::error( - "'idpEntityId' or 'spEntityId'" . - " is empty and login log wasn't inserted into the database." - ); - } else { - if ($this->writeLogin($year, $month, $day, $idpEntityID, $spEntityId, $userId) === false) { - Logger::error("The login log wasn't inserted into table: " . $this->statisticsTableName . "."); - } - if ($this->writeLoginIp($idpEntityID, $spEntityId, $userId, $ip, $dateTimestamp) === false) { - Logger::error("The login log for ip wasn't inserted into table: " . $this->ipStatisticsTableName . "."); - } - if (!empty($idpName)) { - if ($this->dbDriver == 'pgsql') { - $query = "INSERT INTO " . $this->identityProvidersMapTableName . - " (entityId, name) VALUES (:idp, :name1) ON CONFLICT (entityId) DO UPDATE SET name = :name2"; - } else { - $query = "INSERT INTO " . $this->identityProvidersMapTableName . - " (entityId, name) VALUES (:idp, :name1) ON DUPLICATE KEY UPDATE name = :name2"; - } - $this->conn->write( - $query, - ['idp' => $idpEntityID, 'name1' => $idpName, 'name2' => $idpName] - ); - } - - if (!empty($spName)) { - if ($this->dbDriver == 'pgsql') { - $query = "INSERT INTO " . $this->serviceProvidersMapTableName . - " (identifier, name) VALUES (:sp, :name1) ON CONFLICT (identifier) DO UPDATE SET name = :name2"; - } else { - $query = "INSERT INTO " . $this->serviceProvidersMapTableName . - " (identifier, name) VALUES (:sp, :name1) ON DUPLICATE KEY UPDATE name = :name2"; - } - $this->conn->write( - $query, - ['sp' => $spEntityId, 'name1' => $spName, 'name2' => $spName] - ); - } - } - } - - public function getSpNameBySpIdentifier($identifier) - { - return $this->conn->read( - "SELECT name " . - "FROM " . $this->serviceProvidersMapTableName . " " . - "WHERE identifier=:sp", - ['sp' => $identifier] - )->fetchColumn(); - } - - public function getIdPNameByEntityId($idpEntityId) - { - return $this->conn->read( - "SELECT name " . - "FROM " . $this->identityProvidersMapTableName . " " . - "WHERE entityId=:idp", - ['idp' => $idpEntityId] - )->fetchColumn(); - } - - public function getLoginCountPerDay($days) - { - $query = "SELECT year, month, day, SUM(count) AS count " . - "FROM " . $this->statisticsTableName . " " . - "WHERE service != '' "; - $params = []; - self::addDaysRange($days, $this->dbDriver, $query, $params); - $query .= "GROUP BY year,month,day " . - "ORDER BY year ASC,month ASC,day ASC"; - - return $this->conn->read($query, $params)->fetchAll(PDO::FETCH_ASSOC); - } - - public function getLoginCountPerDayForService($days, $spIdentifier) - { - $query = "SELECT year, month, day, SUM(count) AS count " . - "FROM " . $this->statisticsTableName . " " . - "WHERE service=:service "; - $params = ['service' => $spIdentifier]; - self::addDaysRange($days, $this->dbDriver, $query, $params); - $query .= "GROUP BY year,month,day " . - "ORDER BY year ASC,month ASC,day ASC"; - - return $this->conn->read($query, $params)->fetchAll(PDO::FETCH_ASSOC); - } - - public function getLoginCountPerDayForIdp($days, $idpIdentifier) - { - $query = "SELECT year, month, day, SUM(count) AS count " . - "FROM " . $this->statisticsTableName . " " . - "WHERE sourceIdP=:sourceIdP "; - $params = ['sourceIdP' => $idpIdentifier]; - self::addDaysRange($days, $this->dbDriver, $query, $params); - $query .= "GROUP BY year,month,day " . - "ORDER BY year ASC,month ASC,day ASC"; - - return $this->conn->read($query, $params)->fetchAll(PDO::FETCH_ASSOC); - } - - public function getAccessCountPerService($days) - { - if ($this->dbDriver == 'pgsql') { - $query = "SELECT COALESCE(name,service) AS spname, service, SUM(count) AS count " . - "FROM " . $this->serviceProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON service = identifier "; - $querySuffix = "GROUP BY service, name HAVING service != '' " . - "ORDER BY count DESC"; - } else { - $query = "SELECT IFNULL(name,service) AS spName, service, SUM(count) AS count " . - "FROM " . $this->serviceProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON service = identifier "; - $querySuffix = "GROUP BY service HAVING service != '' " . - "ORDER BY count DESC"; - } - $params = []; - self::addDaysRange($days, $this->dbDriver, $query, $params); - $query .= $querySuffix; - - return $this->conn->read($query, $params)->fetchAll(PDO::FETCH_NUM); - } - - public function getAccessCountForServicePerIdentityProviders($days, $spIdentifier) - { - if ($this->dbDriver == 'pgsql') { - $query = "SELECT COALESCE(name,sourceIdp) AS idpname, SUM(count) AS count " . - "FROM " . $this->identityProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON sourceIdp = entityId "; - $querySuffix = "GROUP BY sourceIdp, service, idpname HAVING sourceIdp != '' AND service=:service " . - "ORDER BY count DESC"; - } else { - $query = "SELECT IFNULL(name,sourceIdp) AS idpname, SUM(count) AS count " . - "FROM " . $this->identityProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON sourceIdp = entityId "; - $querySuffix = "GROUP BY sourceIdp, service HAVING sourceIdp != '' AND service=:service " . - "ORDER BY count DESC"; - } - $params = ['service' => $spIdentifier]; - self::addDaysRange($days, $this->dbDriver, $query, $params); - $query .= $querySuffix; - - return $this->conn->read($query, $params)->fetchAll(PDO::FETCH_NUM); - } - - public function getAccessCountForIdentityProviderPerServiceProviders($days, $idpEntityId) - { - if ($this->dbDriver == 'pgsql') { - $query = "SELECT COALESCE(name,service) AS spname, SUM(count) AS count " . - "FROM " . $this->serviceProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON service = identifier "; - $querySuffix = "GROUP BY sourceIdp, service, name HAVING service != '' AND sourceIdp=:sourceIdp " . - "ORDER BY count DESC"; - } else { - $query = "SELECT IFNULL(name,service) AS spname, SUM(count) AS count " . - "FROM " . $this->serviceProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON service = identifier "; - $querySuffix = "GROUP BY sourceIdp, service HAVING service != '' AND sourceIdp=:sourceIdp " . - "ORDER BY count DESC"; - } - $params = ['sourceIdp' => $idpEntityId]; - self::addDaysRange($days, $this->dbDriver, $query, $params); - $query .= $querySuffix; - - return $this->conn->read($query, $params)->fetchAll(PDO::FETCH_NUM); - } - - public function getLoginCountPerIdp($days) - { - if ($this->dbDriver == 'pgsql') { - $query = "SELECT COALESCE(name,sourceIdp) AS idpname, sourceidp, SUM(count) AS count " . - "FROM " . $this->identityProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON sourceIdp = entityId "; - $querySuffix = "GROUP BY sourceidp, name HAVING sourceIdp != '' " . - "ORDER BY count DESC"; - } else { - $query = "SELECT IFNULL(name,sourceIdp) AS idpName, sourceIdp, SUM(count) AS count " . - "FROM " . $this->identityProvidersMapTableName . " " . - "LEFT OUTER JOIN " . $this->statisticsTableName . " ON sourceIdp = entityId "; - $querySuffix = "GROUP BY sourceIdp HAVING sourceIdp != '' " . - "ORDER BY count DESC"; - } - $params = []; - self::addDaysRange($days, $this->dbDriver, $query, $params); - $query .= $querySuffix; - - return $this->conn->read($query, $params)->fetchAll(PDO::FETCH_NUM); - } - - private static function addDaysRange($days, $dbDriver, &$query, &$params, $not = false) - { - if ($days != 0) { // 0 = all time - if (stripos($query, "WHERE") === false) { - $query .= "WHERE"; - } else { - $query .= "AND"; - } - if ($dbDriver == 'pgsql') { - $query .= " CAST(CONCAT(year,'-',LPAD(CAST(month AS varchar),2,'0'),'-',LPAD(CAST(day AS varchar),2,'0')) AS date) "; - $querySuffix = "current_date - INTERVAL '1 days' * :days "; - if ($not) { - $query .= "< "; - } else { - $query .= "> "; - } - } else { - $query .= " CONCAT(year,'-',LPAD(month,2,'00'),'-',LPAD(day,2,'00')) "; - $querySuffix = "BETWEEN CURDATE() - INTERVAL :days DAY AND CURDATE() "; - if ($not) { - $query .= "NOT "; - } - } - $query .= $querySuffix; - $params['days'] = $days; - } - } - - public function deleteOldDetailedStatistics() - { - if ($this->databaseConnector->getDetailedDays() > 0) { - $query = "DELETE FROM " . $this->detailedStatisticsTableName . " "; - $params = []; - self::addDaysRange($this->databaseConnector->getDetailedDays(), $this->dbDriver, $query, $params, true); - return $this->conn->write($query, $params); - } - } - - public static function getIdPDisplayName($idpMetadata) - { - if (!empty($idpMetadata['UIInfo']['DisplayName'])) { - $displayName = $idpMetadata['UIInfo']['DisplayName']; - // Should always be an array of language code -> translation - assert(is_array($displayName)); - // TODO: Use \SimpleSAML\Locale\Translate::getPreferredTranslation() - // in SSP 2.0 - if (!empty($displayName['en'])) { - return $displayName['en']; - } - } - - if (!empty($idpMetadata['name'])) { - // TODO: Use \SimpleSAML\Locale\Translate::getPreferredTranslation() - // in SSP 2.0 - if (!empty($idpMetadata['name']['en'])) { - return $idpMetadata['name']['en']; - } else { - return $idpMetadata['name']; - } - } - - return null; - } - - public static function getSPDisplayName($spMetadata) - { - if (!empty($spMetadata['name'])) { - // TODO: Use \SimpleSAML\Locale\Translate::getPreferredTranslation() - // in SSP 2.0 - if (!empty($spMetadata['name']['en'])) { - return $spMetadata['name']['en']; - } else { - return $spMetadata['name']; - } - } - - if (!empty($spMetadata['OrganizationDisplayName'])) { - // TODO: Use \SimpleSAML\Locale\Translate::getPreferredTranslation() - // in SSP 2.0 - if (!empty($spMetadata['OrganizationDisplayName']['en'])) { - return $spMetadata['OrganizationDisplayName']['en']; - } else { - return $spMetadata['OrganizationDisplayName']; - } - } - - return null; - } -} diff --git a/src/Auth/Process/Statistics.php b/src/Auth/Process/Statistics.php index 98b634c..3bfdf44 100644 --- a/src/Auth/Process/Statistics.php +++ b/src/Auth/Process/Statistics.php @@ -55,9 +55,8 @@ public function process(array &$request): void $dateTime = new DateTime('now', new DateTimeZone('UTC')); - // TODO: This should become AMS send - $dbCmd = new DatabaseCommand(); - $dbCmd->insertLogin($request, $dateTime, $this->userIdAttribute[0]); + $amsCmd = new AmsCommand(); + $amsCmd->insertLogin($request, $dateTime, $this->userIdAttribute[0]); $spEntityId = $request['SPMetadata']['entityid']; $userIdentity = '';