From fad1926b987103513341240fe4ad13b45dd69fc3 Mon Sep 17 00:00:00 2001 From: Rodrigo Mady da Silva Date: Fri, 17 Jun 2022 19:23:07 -0300 Subject: [PATCH 01/49] MDL-74825 tool_mobile: Add customusermenuitems in mobile WS get_config --- admin/tool/mobile/classes/api.php | 4 ++++ admin/tool/mobile/tests/externallib_test.php | 1 + admin/tool/mobile/upgrade.txt | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/admin/tool/mobile/classes/api.php b/admin/tool/mobile/classes/api.php index 72e1151ab9c..93b1ddf2072 100644 --- a/admin/tool/mobile/classes/api.php +++ b/admin/tool/mobile/classes/api.php @@ -344,6 +344,10 @@ class api { $settings->enabledashboard = $CFG->enabledashboard; } + if (empty($section) || $section === 'themesettings') { + $settings->customusermenuitems = $CFG->customusermenuitems; + } + return $settings; } diff --git a/admin/tool/mobile/tests/externallib_test.php b/admin/tool/mobile/tests/externallib_test.php index a3af5eec152..b6d15d2fae6 100644 --- a/admin/tool/mobile/tests/externallib_test.php +++ b/admin/tool/mobile/tests/externallib_test.php @@ -234,6 +234,7 @@ class externallib_test extends externallib_advanced_testcase { $expected[] = ['name' => 'coursegraceperiodbefore', 'value' => $CFG->coursegraceperiodbefore]; $expected[] = ['name' => 'enabledashboard', 'value' => $CFG->enabledashboard]; + $expected[] = ['name' => 'customusermenuitems', 'value' => $CFG->customusermenuitems]; $this->assertCount(0, $result['warnings']); $this->assertEquals($expected, $result['settings']); diff --git a/admin/tool/mobile/upgrade.txt b/admin/tool/mobile/upgrade.txt index 3328072493d..9495d7ac1cd 100644 --- a/admin/tool/mobile/upgrade.txt +++ b/admin/tool/mobile/upgrade.txt @@ -1,6 +1,10 @@ This files describes changes in tool_mobile code. Information provided here is intended especially for developers. +=== 4.1 === + +* External function tool_mobile::get_config now returns the customusermenuitems setting. + === 4.0 === * The function tool_mobile\api::get_qrlogin_key() now requires as parameter an object with all the mobile plugin settings. From 892047d3c8dc72ce8b572170af6beb03e0a4eb7b Mon Sep 17 00:00:00 2001 From: Rodrigo Mady da Silva Date: Mon, 20 Jun 2022 12:08:49 -0300 Subject: [PATCH 02/49] MDL-74827 core_webservice: Add enablecustomreports in WS get_site_info --- webservice/externallib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webservice/externallib.php b/webservice/externallib.php index 89467a3dbbe..478064fdf67 100644 --- a/webservice/externallib.php +++ b/webservice/externallib.php @@ -168,7 +168,7 @@ class core_webservice_external extends external_api { // Retrieve some advanced features. Only enable/disable ones (bool). $advancedfeatures = array("usecomments", "usetags", "enablenotes", "messaging", "enableblogs", - "enablecompletion", "enablebadges", "messagingallusers"); + "enablecompletion", "enablebadges", "messagingallusers", "enablecustomreports"); foreach ($advancedfeatures as $feature) { if (isset($CFG->{$feature})) { $siteinfo['advancedfeatures'][] = array( From 7725abc4a8c74f2d578b2c7e613d81bfd0588a62 Mon Sep 17 00:00:00 2001 From: Mikhail Golenkov Date: Mon, 25 Oct 2021 17:52:33 +1100 Subject: [PATCH 03/49] MDL-72905 course: Add new callback for timeline classification --- course/lib.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/course/lib.php b/course/lib.php index 37c89ebc3e4..ef1e17f17e0 100644 --- a/course/lib.php +++ b/course/lib.php @@ -4109,16 +4109,24 @@ function course_classify_for_timeline($course, $user = null, $completioninfo = n $user = $USER; } + if ($completioninfo == null) { + $completioninfo = new completion_info($course); + } + + // Let plugins override data for timeline classification. + $pluginsfunction = get_plugins_with_function('extend_course_classify_for_timeline', 'lib.php'); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginfunction($course, $user, $completioninfo); + } + } + $today = time(); // End date past. if (!empty($course->enddate) && (course_classify_end_date($course) < $today)) { return COURSE_TIMELINE_PAST; } - if ($completioninfo == null) { - $completioninfo = new completion_info($course); - } - // Course was completed. if ($completioninfo->is_enabled() && $completioninfo->is_course_complete($user->id)) { return COURSE_TIMELINE_PAST; From dc60fe66627ac81d2d20fc470bd25a8895a47ff2 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Mon, 4 Jul 2022 12:34:15 +0100 Subject: [PATCH 04/49] MDL-75119 reportbuilder: preload context data for column callbacks. This prevents additional requests for context instances in the callback methods, by preloading all required data in the initial field select. --- .../reportbuilder/local/entities/badge.php | 22 +++++++++++---- cohort/classes/local/entities/cohort.php | 28 +++++++++++++------ .../local/entities/course_category.php | 24 +++++++++------- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/badges/classes/reportbuilder/local/entities/badge.php b/badges/classes/reportbuilder/local/entities/badge.php index 44bdb2f2d39..5fceb3980bc 100644 --- a/badges/classes/reportbuilder/local/entities/badge.php +++ b/badges/classes/reportbuilder/local/entities/badge.php @@ -19,6 +19,7 @@ declare(strict_types=1); namespace core_badges\reportbuilder\local\entities; use context_course; +use context_helper; use context_system; use html_writer; use lang_string; @@ -48,7 +49,10 @@ class badge extends base { * @return array */ protected function get_default_table_aliases(): array { - return ['badge' => 'b']; + return [ + 'badge' => 'b', + 'context' => 'bctx', + ]; } /** @@ -89,6 +93,7 @@ class badge extends base { */ protected function get_all_columns(): array { $badgealias = $this->get_table_alias('badge'); + $contextalias = $this->get_table_alias('context'); // Name. $columns[] = (new column( @@ -101,7 +106,7 @@ class badge extends base { ->add_field("{$badgealias}.name") ->set_is_sortable(true); - // Description. + // Description (note, this column contains plaintext so requires no post-processing). $columns[] = (new column( 'description', new lang_string('description', 'core_badges'), @@ -137,13 +142,20 @@ class badge extends base { $this->get_entity_name() )) ->add_joins($this->get_joins()) + ->add_join("LEFT JOIN {context} {$contextalias} + ON {$contextalias}.contextlevel = " . CONTEXT_COURSE . " + AND {$contextalias}.instanceid = {$badgealias}.courseid") ->set_type(column::TYPE_INTEGER) ->add_fields("{$badgealias}.id, {$badgealias}.type, {$badgealias}.courseid, {$badgealias}.imagecaption") + ->add_fields(context_helper::get_preload_record_columns_sql($contextalias)) ->set_disabled_aggregation_all() ->add_callback(static function(int $badgeid, stdClass $badge): string { - $context = $badge->type == BADGE_TYPE_SITE - ? context_system::instance() - : context_course::instance($badge->courseid); + if ($badge->type == BADGE_TYPE_SITE) { + $context = context_system::instance(); + } else { + context_helper::preload_from_record($badge); + $context = context_course::instance($badge->courseid); + } $badgeimage = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $badgeid, '/', 'f2'); return html_writer::img($badgeimage, $badge->imagecaption); diff --git a/cohort/classes/local/entities/cohort.php b/cohort/classes/local/entities/cohort.php index f70c644dd21..df8f11a8cb5 100644 --- a/cohort/classes/local/entities/cohort.php +++ b/cohort/classes/local/entities/cohort.php @@ -45,7 +45,10 @@ class cohort extends base { * @return array */ protected function get_default_table_aliases(): array { - return ['cohort' => 'c']; + return [ + 'cohort' => 'c', + 'context' => 'chctx', + ]; } /** @@ -86,6 +89,7 @@ class cohort extends base { */ protected function get_all_columns(): array { $tablealias = $this->get_table_alias('cohort'); + $contextalias = $this->get_table_alias('context'); // Category/context column. $columns[] = (new column( @@ -94,11 +98,13 @@ class cohort extends base { $this->get_entity_name() )) ->add_joins($this->get_joins()) + ->add_join("JOIN {context} {$contextalias} ON {$contextalias}.id = {$tablealias}.contextid") ->set_type(column::TYPE_TEXT) - ->add_fields("{$tablealias}.contextid") + ->add_fields("{$tablealias}.contextid, " . context_helper::get_preload_record_columns_sql($contextalias)) ->set_is_sortable(true) - ->add_callback(static function($contextid): string { - return context::instance_by_id((int) $contextid)->get_context_name(false); + ->add_callback(static function($contextid, stdClass $cohort): string { + context_helper::preload_from_record($cohort); + return context::instance_by_id($cohort->contextid)->get_context_name(false); }); // Name column. @@ -130,8 +136,10 @@ class cohort extends base { $this->get_entity_name() )) ->add_joins($this->get_joins()) - ->set_type(column::TYPE_TEXT) + ->add_join("JOIN {context} {$contextalias} ON {$contextalias}.id = {$tablealias}.contextid") + ->set_type(column::TYPE_LONGTEXT) ->add_fields("{$tablealias}.description, {$tablealias}.descriptionformat, {$tablealias}.id, {$tablealias}.contextid") + ->add_fields(context_helper::get_preload_record_columns_sql($contextalias)) ->add_callback(static function(?string $description, stdClass $cohort): string { global $CFG; require_once("{$CFG->libdir}/filelib.php"); @@ -140,12 +148,14 @@ class cohort extends base { return ''; } - $description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $cohort->contextid, 'cohort', + context_helper::preload_from_record($cohort); + $context = context::instance_by_id($cohort->contextid); + + $description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $context->id, 'cohort', 'description', $cohort->id); - return format_text($description, $cohort->descriptionformat, ['context' => $cohort->contextid]); - }) - ->set_is_sortable(false); + return format_text($description, $cohort->descriptionformat, ['context' => $context->id]); + }); // Visible column. $columns[] = (new column( diff --git a/course/classes/local/entities/course_category.php b/course/classes/local/entities/course_category.php index 6c923b0058a..7bd1272e02a 100644 --- a/course/classes/local/entities/course_category.php +++ b/course/classes/local/entities/course_category.php @@ -18,6 +18,8 @@ declare(strict_types=1); namespace core_course\local\entities; +use context_coursecat; +use context_helper; use lang_string; use stdClass; use core_course_category; @@ -135,12 +137,12 @@ class course_category extends base { $this->get_entity_name() )) ->add_joins($this->get_joins()) - ->add_join(" - JOIN {context} {$tablealiascontext} - ON {$tablealiascontext}.instanceid = {$tablealias}.id - AND {$tablealiascontext}.contextlevel = " . CONTEXT_COURSECAT) - ->set_type(column::TYPE_TEXT) - ->add_fields("{$tablealias}.description, {$tablealias}.descriptionformat, {$tablealiascontext}.id AS contextid") + ->add_join("LEFT JOIN {context} {$tablealiascontext} + ON {$tablealiascontext}.contextlevel = " . CONTEXT_COURSECAT . " + AND {$tablealiascontext}.instanceid = {$tablealias}.id") + ->set_type(column::TYPE_LONGTEXT) + ->add_fields("{$tablealias}.description, {$tablealias}.descriptionformat, {$tablealias}.id") + ->add_fields(context_helper::get_preload_record_columns_sql($tablealiascontext)) ->add_callback(static function(?string $description, stdClass $category): string { global $CFG; require_once("{$CFG->libdir}/filelib.php"); @@ -149,12 +151,14 @@ class course_category extends base { return ''; } - $description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $category->contextid, 'coursecat', + context_helper::preload_from_record($category); + $context = context_coursecat::instance($category->id); + + $description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $context->id, 'coursecat', 'description', null); - return format_text($description, $category->descriptionformat, ['context' => $category->contextid]); - }) - ->set_is_sortable(false); + return format_text($description, $category->descriptionformat, ['context' => $context->id]); + }); return $columns; } From dc20ae685f5ad2e80127553531dd208f3ee7868c Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Fri, 8 Jul 2022 09:53:36 +0800 Subject: [PATCH 05/49] MDL-75182 enrol_lti: remove installed lang assumptions from unit tests Don't assume 'fr' isn't installed, or that 'en' is. Use known non-existent languages and known installed languages for the relevant test cases. --- .../entity/resource_link_test.php | 5 +- .../local/ltiadvantage/entity/user_test.php | 56 ++++++++++--------- .../resource_link_repository_test.php | 14 +++-- .../repository/user_repository_test.php | 17 ++++-- 4 files changed, 53 insertions(+), 39 deletions(-) diff --git a/enrol/lti/tests/local/ltiadvantage/entity/resource_link_test.php b/enrol/lti/tests/local/ltiadvantage/entity/resource_link_test.php index 33963c63345..04f35d0b01c 100644 --- a/enrol/lti/tests/local/ltiadvantage/entity/resource_link_test.php +++ b/enrol/lti/tests/local/ltiadvantage/entity/resource_link_test.php @@ -229,15 +229,16 @@ class resource_link_test extends \advanced_testcase { * @covers ::add_user */ public function test_add_user() { + global $CFG; $reslinkwithid = resource_link::create('res-link-id-123', 24, 44, 66, 33); - $user = $reslinkwithid->add_user(2, 'platform-user-id-123', 'en', 'Sydney', 'AU', 'Test university', '99'); + $user = $reslinkwithid->add_user(2, 'platform-user-id-123', $CFG->lang, 'Sydney', 'AU', 'Test university', '99'); $this->assertInstanceOf(user::class, $user); $this->assertEquals(33, $user->get_resourcelinkid()); $reslinkwithoutid = resource_link::create('res-link-id-123', 24, 44); $this->expectException(\coding_exception::class); $this->expectExceptionMessage("Can't add user to a resource_link that hasn't first been saved"); - $reslinkwithoutid->add_user(2, 'platform-user-id-123', 'en', 'Sydney', 'Australia', 'Test university', '99'); + $reslinkwithoutid->add_user(2, 'platform-user-id-123', $CFG->lang, 'Sydney', 'Australia', 'Test university', '99'); } /** diff --git a/enrol/lti/tests/local/ltiadvantage/entity/user_test.php b/enrol/lti/tests/local/ltiadvantage/entity/user_test.php index e456961fda2..ae594f9c8ce 100644 --- a/enrol/lti/tests/local/ltiadvantage/entity/user_test.php +++ b/enrol/lti/tests/local/ltiadvantage/entity/user_test.php @@ -65,6 +65,7 @@ class user_test extends \advanced_testcase { * @return array the data for testing. */ public function create_data_provider(): array { + global $CFG; return [ 'Valid create, only required args provided' => [ 'args' => [ @@ -72,7 +73,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99' ], 'expectations' => [ @@ -80,7 +81,7 @@ class user_test extends \advanced_testcase { 'resourceid' => 22, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => '', 'country' => '', @@ -99,7 +100,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'AU', @@ -115,7 +116,7 @@ class user_test extends \advanced_testcase { 'resourceid' => 22, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'AU', @@ -134,7 +135,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'AU', @@ -151,7 +152,7 @@ class user_test extends \advanced_testcase { 'resourceid' => 22, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'AU', @@ -164,19 +165,19 @@ class user_test extends \advanced_testcase { 'id' => null ] ], - 'Invalid create, lang with bad value (fr not installed)' => [ + 'Invalid create, lang with bad value (vvvv not installed)' => [ 'args' => [ 'resourceid' => 22, 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'fr', + 'lang' => 'vvvv', 'timezone' => '99', ], 'expectations' => [ 'valid' => false, 'exception' => \coding_exception::class, - 'exceptionmessage' => "Invalid lang 'fr' provided." + 'exceptionmessage' => "Invalid lang 'vvvv' provided." ] ], 'Invalid create, timezone with bad value' => [ @@ -185,7 +186,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => 'NOT/FOUND', ], 'expectations' => [ @@ -200,7 +201,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => '', 'country' => 'FFF', @@ -217,7 +218,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => '', 'country' => '', @@ -272,6 +273,7 @@ class user_test extends \advanced_testcase { * @return array the data for testing. */ public function create_from_resource_link_data_provider(): array { + global $CFG; return [ 'Valid creation, all args provided explicitly' => [ 'args' => [ @@ -280,7 +282,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'AU', @@ -295,7 +297,7 @@ class user_test extends \advanced_testcase { 'resourceid' => 22, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'AU', @@ -312,7 +314,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => 'UTC' ], 'expectations' => [ @@ -323,7 +325,7 @@ class user_test extends \advanced_testcase { 'resourceid' => 22, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => 'UTC', 'city' => '', 'country' => '', @@ -340,7 +342,7 @@ class user_test extends \advanced_testcase { 'user' => 2, 'deploymentid' => 33, 'sourceid' => '', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => 'UTC' ], 'expectations' => [ @@ -372,7 +374,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '' ], 'expectations' => [ @@ -381,20 +383,20 @@ class user_test extends \advanced_testcase { 'exceptionmessage' => 'Invalid timezone value. Cannot be an empty string.' ] ], - 'Invalid creation, only required args provided, invalid lang (fr not installed)' => [ + 'Invalid creation, only required args provided, invalid lang (vvvv not installed)' => [ 'args' => [ 'resourcelinkid' => 11, 'resourceid' => 22, 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'fr', + 'lang' => 'vvvv', 'timezone' => 'UTC' ], 'expectations' => [ 'valid' => false, 'exception' => \coding_exception::class, - 'exceptionmessage' => "Invalid lang 'fr' provided." + 'exceptionmessage' => "Invalid lang 'vvvv' provided." ] ], 'Invalid creation, only required args provided, invalid timezone' => [ @@ -404,7 +406,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => 'NOT/FOUND' ], 'expectations' => [ @@ -420,7 +422,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'FFF', @@ -440,7 +442,7 @@ class user_test extends \advanced_testcase { 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => '99', 'city' => 'Melbourne', 'country' => 'AU', @@ -462,13 +464,14 @@ class user_test extends \advanced_testcase { * @return user a user instance. */ protected function create_test_user(): user { + global $CFG; $args = [ 'resourcelinkid' => 11, 'resourceid' => 22, 'userid' => 2, 'deploymentid' => 33, 'sourceid' => 'user-id-123', - 'lang' => 'en', + 'lang' => $CFG->lang, 'timezone' => 'UTC' ]; return user::create_from_resource_link(...array_values($args)); @@ -509,6 +512,7 @@ class user_test extends \advanced_testcase { * @return array the array of test data. */ public function setters_getters_data_provider(): array { + global $CFG; return [ 'Testing set_resourcelinkid with valid id' => [ 'methodname' => 'resourcelinkid', @@ -641,7 +645,7 @@ class user_test extends \advanced_testcase { ], 'Testing set_lang with valid lang code' => [ 'methodname' => 'lang', - 'arg' => 'en', + 'arg' => $CFG->lang, 'expectations' => [ 'valid' => true, ] diff --git a/enrol/lti/tests/local/ltiadvantage/repository/resource_link_repository_test.php b/enrol/lti/tests/local/ltiadvantage/repository/resource_link_repository_test.php index 858a617d6d5..a9ff411f17c 100644 --- a/enrol/lti/tests/local/ltiadvantage/repository/resource_link_repository_test.php +++ b/enrol/lti/tests/local/ltiadvantage/repository/resource_link_repository_test.php @@ -197,6 +197,7 @@ class resource_link_repository_test extends \advanced_testcase { * @covers ::find_by_resource_and_user */ public function test_find_by_resource_and_user() { + global $CFG; $this->resetAfterTest(); $resourcelink = $this->generate_resource_link(); $repository = new resource_link_repository(); @@ -214,7 +215,7 @@ class resource_link_repository_test extends \advanced_testcase { $user = $newreslink->add_user( $user1->id, 'platform-user-id-123', - 'en', + $CFG->lang, 'Sydney', 'AU', 'Test university', @@ -227,7 +228,7 @@ class resource_link_repository_test extends \advanced_testcase { $user2 = $newreslink3->add_user( $user2->id, 'platform-user-id-777', - 'en', + $CFG->lang, 'Melbourne', 'AU', 'Test university', @@ -255,6 +256,7 @@ class resource_link_repository_test extends \advanced_testcase { * @covers ::delete */ public function test_delete() { + global $CFG; $this->resetAfterTest(); $resourcelink = $this->generate_resource_link(); $repository = new resource_link_repository(); @@ -265,7 +267,7 @@ class resource_link_repository_test extends \advanced_testcase { $user = $newreslink->add_user( 2, 'source-id-123', - 'en', + $CFG->lang, 'Perth', 'AU', 'An Example Institution', @@ -293,6 +295,7 @@ class resource_link_repository_test extends \advanced_testcase { * @covers ::delete_by_resource */ public function test_delete_by_resource() { + global $CFG; $this->resetAfterTest(); $resourcelink = $this->generate_resource_link(); $repository = new resource_link_repository(); @@ -302,7 +305,7 @@ class resource_link_repository_test extends \advanced_testcase { $user = $newreslink->add_user( 2, 'source-id-123', - 'en', + $CFG->lang, 'Perth', 'AU', 'An Example Institution', @@ -337,6 +340,7 @@ class resource_link_repository_test extends \advanced_testcase { * @covers ::delete_by_deployment */ public function test_delete_by_deployment() { + global $CFG; $this->resetAfterTest(); $resourcelink = $this->generate_resource_link(); $repository = new resource_link_repository(); @@ -347,7 +351,7 @@ class resource_link_repository_test extends \advanced_testcase { $user = $newreslink->add_user( 2, 'source-id-123', - 'en', + $CFG->lang, 'Perth', 'AU', 'An Example Institution', diff --git a/enrol/lti/tests/local/ltiadvantage/repository/user_repository_test.php b/enrol/lti/tests/local/ltiadvantage/repository/user_repository_test.php index c745a8a14f3..cd690379330 100644 --- a/enrol/lti/tests/local/ltiadvantage/repository/user_repository_test.php +++ b/enrol/lti/tests/local/ltiadvantage/repository/user_repository_test.php @@ -34,6 +34,7 @@ class user_repository_test extends \advanced_testcase { * @return user a user instance */ protected function generate_user(int $mockresourceid = 1): user { + global $CFG; $registration = application_registration::create( 'Test', 'a2c94a2c94', @@ -66,7 +67,7 @@ class user_repository_test extends \advanced_testcase { $ltiuser = $savedresourcelink->add_user( $user->id, 'source-id-123', - 'en', + $CFG->lang, 'Perth', 'AU', 'An Example Institution', @@ -215,6 +216,7 @@ class user_repository_test extends \advanced_testcase { * @covers ::save */ public function test_save_stale_id() { + global $CFG; $this->resetAfterTest(); $instructoruser = $this->getDataGenerator()->create_user(); $userrepo = new user_repository(); @@ -223,7 +225,7 @@ class user_repository_test extends \advanced_testcase { $instructoruser->id, 5, 'source-id-123', - 'en', + $CFG->lang, '99', '', '', @@ -280,6 +282,7 @@ class user_repository_test extends \advanced_testcase { * @covers ::find_by_resource */ public function test_find_by_resource() { + global $CFG; $this->resetAfterTest(); $user = $this->generate_user(); $userrepo = new user_repository(); @@ -291,7 +294,7 @@ class user_repository_test extends \advanced_testcase { $instructoruser->id, $saveduser->get_deploymentid(), 'another-user-123', - 'en', + $CFG->lang, '99', 'Perth', 'AU', @@ -314,6 +317,7 @@ class user_repository_test extends \advanced_testcase { * @covers ::find_by_resource_link */ public function test_find_by_resource_link() { + global $CFG; $this->resetAfterTest(); $user = $this->generate_user(); $user->set_resourcelinkid(33); @@ -326,7 +330,7 @@ class user_repository_test extends \advanced_testcase { $instructoruser->id, $saveduser->get_deploymentid(), 'another-user-123', - 'en', + $CFG->lang, '99', 'Perth', 'AU', @@ -390,6 +394,7 @@ class user_repository_test extends \advanced_testcase { * @covers ::delete_by_deployment */ public function test_delete_by_deployment() { + global $CFG; $this->resetAfterTest(); $user = $this->generate_user(); $userrepo = new user_repository(); @@ -402,7 +407,7 @@ class user_repository_test extends \advanced_testcase { $instructoruser->id, $saveduser->get_deploymentid(), 'another-user-123', - 'en', + $CFG->lang, '99', 'Perth', 'AU', @@ -415,7 +420,7 @@ class user_repository_test extends \advanced_testcase { $instructor2user->id, $saveduser->get_deploymentid() + 1, 'another-user-678', - 'en', + $CFG->lang, '99', 'Melbourne', 'AU', From 7c57a9749a3144d2a64066f31f9288b9303c155a Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Tue, 21 Jun 2022 12:33:51 +0800 Subject: [PATCH 06/49] MDL-74808 question: use the correct attr for checked checkboxes --- question/classes/bank/search/category_condition.php | 2 +- question/classes/bank/search/hidden_condition.php | 2 +- question/templates/showtext_checkbox.mustache | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/question/classes/bank/search/category_condition.php b/question/classes/bank/search/category_condition.php index 2bee9f54e5c..319905ce3b6 100644 --- a/question/classes/bank/search/category_condition.php +++ b/question/classes/bank/search/category_condition.php @@ -137,7 +137,7 @@ class category_condition extends condition { global $PAGE; $displaydata = []; if ($this->recurse) { - $displaydata['checked'] = 'checked="true"'; + $displaydata['checked'] = 'checked'; } return $PAGE->get_renderer('core_question', 'bank')->render_category_condition_advanced($displaydata); } diff --git a/question/classes/bank/search/hidden_condition.php b/question/classes/bank/search/hidden_condition.php index ace415690a7..a05fc35ebbb 100644 --- a/question/classes/bank/search/hidden_condition.php +++ b/question/classes/bank/search/hidden_condition.php @@ -69,7 +69,7 @@ class hidden_condition extends condition { global $PAGE; $displaydata = []; if (!$this->hide) { - $displaydata['checked'] = 'checked="true"'; + $displaydata['checked'] = 'checked'; } return $PAGE->get_renderer('core_question', 'bank')->render_hidden_condition_advanced($displaydata); } diff --git a/question/templates/showtext_checkbox.mustache b/question/templates/showtext_checkbox.mustache index 84610f3411c..0245357ef6d 100644 --- a/question/templates/showtext_checkbox.mustache +++ b/question/templates/showtext_checkbox.mustache @@ -28,6 +28,6 @@ }}
- +
From d1a0105b6db550d9b9f349a67482d4b6b2010581 Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Wed, 22 Jun 2022 08:42:26 +0800 Subject: [PATCH 07/49] MDL-74808 question: only add title attr if tooltip is set --- lib/templates/single_button.mustache | 2 +- question/bank/editquestion/classes/editquestion_helper.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/templates/single_button.mustache b/lib/templates/single_button.mustache index a10f9bc1505..7937a7007e6 100644 --- a/lib/templates/single_button.mustache +++ b/lib/templates/single_button.mustache @@ -58,7 +58,7 @@ {{/params}} diff --git a/question/bank/editquestion/classes/editquestion_helper.php b/question/bank/editquestion/classes/editquestion_helper.php index c6d45e25815..c806df5a9d1 100644 --- a/question/bank/editquestion/classes/editquestion_helper.php +++ b/question/bank/editquestion/classes/editquestion_helper.php @@ -78,9 +78,13 @@ class editquestion_helper { if ($canadd) { $params['category'] = $categoryid; $url = new \moodle_url('/question/bank/editquestion/addquestion.php', $params); + $buttonparams = ['disabled' => $disabled]; + if (!empty($tooltip)) { + $buttonparams['title'] = $tooltip; + } $addquestiondisplay['buttonhtml'] = $OUTPUT->single_button($url, get_string('createnewquestion', 'question'), - 'get', array('disabled' => $disabled, 'title' => $tooltip)); + 'get', $buttonparams); $addquestiondisplay['qtypeform'] = self::print_choose_qtype_to_add_form(array()); } return $PAGE->get_renderer('qbank_editquestion')->render_create_new_question_button($addquestiondisplay); From e7d988c85cb73c6a1ed4716dbacf0bfb6cae4c18 Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Wed, 22 Jun 2022 12:48:08 +0800 Subject: [PATCH 08/49] MDL-74808 question: remove span element from label --- .../question_name_idnumber_tags_column.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/question/bank/viewquestionname/classes/question_name_idnumber_tags_column.php b/question/bank/viewquestionname/classes/question_name_idnumber_tags_column.php index 0d2806fc87c..3270914c546 100644 --- a/question/bank/viewquestionname/classes/question_name_idnumber_tags_column.php +++ b/question/bank/viewquestionname/classes/question_name_idnumber_tags_column.php @@ -33,19 +33,17 @@ class question_name_idnumber_tags_column extends viewquestionname_column_helper protected function display_content($question, $rowclasses): void { global $OUTPUT; - $layoutclasses = 'd-inline-flex flex-nowrap overflow-hidden w-100'; + echo \html_writer::start_tag('div', ['class' => 'd-inline-flex flex-nowrap overflow-hidden w-100']); + + $questionname = format_string($question->name); $labelfor = $this->label_for($question); if ($labelfor) { - echo \html_writer::start_tag('label', ['for' => $labelfor, 'class' => $layoutclasses]); - $closetag = \html_writer::end_tag('label'); + echo \html_writer::label($questionname, $labelfor); } else { - echo \html_writer::start_tag('span', ['class' => $layoutclasses]); - echo \html_writer::end_tag('span'); + // Question name. + echo \html_writer::span($questionname, 'questionname flex-grow-1 flex-shrink-1 text-truncate'); } - // Question name. - echo \html_writer::span(format_string($question->name), 'questionname flex-grow-1 flex-shrink-1 text-truncate'); - // Question idnumber. if ($question->idnumber !== null && $question->idnumber !== '') { echo ' ' . \html_writer::span( @@ -59,7 +57,7 @@ class question_name_idnumber_tags_column extends viewquestionname_column_helper echo $OUTPUT->tag_list($tags, null, 'd-inline flex-shrink-1 text-truncate ml-1', 0, null, true); } - echo $closetag; // Computed above to ensure it matches. + echo \html_writer::end_tag('div'); } public function get_required_fields(): array { From 6b0bf2cd3848c3033644fa9d770cd25c17c4d330 Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Tue, 28 Jun 2022 14:55:18 +0800 Subject: [PATCH 09/49] MDL-74808 lib: replace type summary span by a div --- lib/templates/chooser_item.mustache | 6 +++--- theme/boost/scss/moodle/undo.scss | 2 +- theme/boost/style/moodle.css | 2 +- theme/classic/style/moodle.css | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/templates/chooser_item.mustache b/lib/templates/chooser_item.mustache index 722b2f57f76..f7a49e71d01 100644 --- a/lib/templates/chooser_item.mustache +++ b/lib/templates/chooser_item.mustache @@ -37,8 +37,8 @@ {{/icon}} {{label}} - - {{{description}}} - +
+ {{{description}}} +
diff --git a/theme/boost/scss/moodle/undo.scss b/theme/boost/scss/moodle/undo.scss index bd2f52444ba..c9c41a6d77f 100644 --- a/theme/boost/scss/moodle/undo.scss +++ b/theme/boost/scss/moodle/undo.scss @@ -28,7 +28,7 @@ li.activity.label, // Some of this dialog is sized in ems so a different font size // effects the whole layout. -.choosercontainer #chooseform .option label { +.choosercontainer #chooseform .option { font-size: 12px; } diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index d79c3c5f9bc..db5c34b937d 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -19683,7 +19683,7 @@ li.activity.label, text-align: right; padding: 8px; } -.choosercontainer #chooseform .option label { +.choosercontainer #chooseform .option { font-size: 12px; } /* block.invisible vs .invisible diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index fdcf06dee2c..020704987c4 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -19683,7 +19683,7 @@ li.activity.label, text-align: right; padding: 8px; } -.choosercontainer #chooseform .option label { +.choosercontainer #chooseform .option { font-size: 12px; } /* block.invisible vs .invisible From 5ce4efa27b2c834f62290e62829c7225e3e11df0 Mon Sep 17 00:00:00 2001 From: Johannes Burk Date: Wed, 29 Jun 2022 10:38:36 +0200 Subject: [PATCH 10/49] MDL-75083 login: prevent double submission Apply core_form/submit JavaScript code to prevent double submission of login form and guest login button. Add ID to guest login button to make it addressable. --- lib/templates/loginform.mustache | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/templates/loginform.mustache b/lib/templates/loginform.mustache index d563f5a43ad..7b8caddcb52 100644 --- a/lib/templates/loginform.mustache +++ b/lib/templates/loginform.mustache @@ -180,7 +180,7 @@ - + {{/canloginasguest}} @@ -213,4 +213,8 @@ {{#error}} document.getElementById('loginerrormessage').focus(); {{/error}} + require(['core_form/submit'], function(Submit) { + Submit.init("loginbtn"); + Submit.init("loginguestbtn"); + }); {{/js}} From 0fcea11234a9d5016e7c55fa8e51621ac218a878 Mon Sep 17 00:00:00 2001 From: Safat Shahin Date: Tue, 17 May 2022 09:30:44 +1000 Subject: [PATCH 11/49] MDL-74768 core_question: Fix for question category movement --- lib/questionlib.php | 3 -- mod/quiz/tests/behat/random_question.feature | 53 +++++++++++++++++++ .../classes/question_category_object.php | 2 + 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 mod/quiz/tests/behat/random_question.feature diff --git a/lib/questionlib.php b/lib/questionlib.php index abfda93f9b7..19de3ddc505 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -721,9 +721,6 @@ function question_move_questions_to_category($questionids, $newcategoryid): bool question_bank::get_qtype($question->qtype)->move_files( $question->id, $question->contextid, $newcategorydata->contextid); } - // Move set_reference records to new category. - move_question_set_references($question->category, $newcategoryid, - $question->contextid, $newcategorydata->contextid, true); // Check whether there could be a clash of idnumbers in the new category. list($idnumberclash, $rec) = idnumber_exist_in_question_category($question->idnumber, $newcategoryid); if ($idnumberclash) { diff --git a/mod/quiz/tests/behat/random_question.feature b/mod/quiz/tests/behat/random_question.feature new file mode 100644 index 00000000000..3aa05a7b9fb --- /dev/null +++ b/mod/quiz/tests/behat/random_question.feature @@ -0,0 +1,53 @@ +@mod @mod_quiz + +Feature: Moving a question to another category should not affect random questions in a quiz + In order for a quiz with random questions to work as expected + Teachers should be able to + Move a question to a different category without affecting the category the random questions in the quiz reference to + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | weeks | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | quiz | Quiz 1 | Quiz 1 for testing the Add random question form | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | questioncategory | name | + | Course | C1 | Top | top | + | Course | C1 | top | Default for C1 | + | Course | C1 | Default for C1 | Subcategory | + | Course | C1 | top | Used category | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Used category | essay | Test question to be moved | Write about whatever you want | + + @javascript + Scenario: Moving a question should not change the random question + Given I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" + When I open the "last" add to quiz menu + And I follow "a random question" + And I set the field "Category" to "Used category" + And I press "Add random question" + And I should see "Random (Used category)" on quiz page "1" + And I click on "(See questions)" "link" + And I should see "Used category" + And I click on "Test question to be moved" "checkbox" in the "Test question to be moved" "table_row" + And I click on "With selected" "button" + And I click on question bulk action "move" + And I set the field "Question category" to "Subcategory" + And I press "Move to" + Then I should see "Test question to be moved" + And the field "Select a category" matches value "      Subcategory (1)" + And the "Select a category" select box should contain "Used category" + And the "Select a category" select box should not contain "Used category (1)" + And I am on the "Quiz 1" "mod_quiz > Edit" page + And I should see "Random (Used category)" on quiz page "1" + And I click on "(See questions)" "link" + And I should see "Used category" diff --git a/question/bank/managecategories/classes/question_category_object.php b/question/bank/managecategories/classes/question_category_object.php index b84dfcc288d..da067490321 100644 --- a/question/bank/managecategories/classes/question_category_object.php +++ b/question/bank/managecategories/classes/question_category_object.php @@ -467,6 +467,8 @@ class question_category_object { $cat->stamp = make_unique_id_code(); } $DB->update_record('question_categories', $cat); + // Update the set_reference records when moving a category to a different context. + move_question_set_references($cat->id, $cat->id, $oldcat->contextid, $tocontextid); // Log the update of this category. $event = \core\event\question_category_updated::create_from_question_category_instance($cat); From 7760c5de071da16748514a236d1517fa52292fe5 Mon Sep 17 00:00:00 2001 From: Marty Date: Thu, 9 Dec 2021 16:34:54 -0500 Subject: [PATCH 12/49] MDL-73332 mustache: Fix shortentext helper In the docs, it claims you can provide length to the shortentext template helper as a context variable. In reality, that is not the case. --- lib/classes/output/mustache_shorten_text_helper.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/classes/output/mustache_shorten_text_helper.php b/lib/classes/output/mustache_shorten_text_helper.php index 16bb1a7d84b..8a470647903 100644 --- a/lib/classes/output/mustache_shorten_text_helper.php +++ b/lib/classes/output/mustache_shorten_text_helper.php @@ -55,8 +55,9 @@ class mustache_shorten_text_helper { $length = trim($length); $text = trim($text); - // Allow mustache tags in the text. + // Allow mustache tags in the length and text. $text = $helper->render($text); + $length = $helper->render($length); return shorten_text($text, $length); } From f1a800bb7c69f7486a6cd43fefd42b3cd9cf356c Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Sat, 12 Feb 2022 09:33:00 +0000 Subject: [PATCH 13/49] MDL-73867 reportbuilder: improve performance of the editable classes. When instantiating classes that extend `inplace_editable` we can improve performance by passing persistent instances we already have, rather than re-requesting them. Add test coverage to relevant classes. --- .../custom_report_filters_exporter.php | 2 +- .../local/systemreports/report_schedules.php | 7 +- .../local/systemreports/reports_list.php | 17 ++- .../output/audience_heading_editable.php | 5 + .../output/column_aggregation_editable.php | 5 + .../output/column_heading_editable.php | 5 + .../classes/output/dynamictabs/audience.php | 2 +- .../output/filter_heading_editable.php | 5 + .../classes/output/report_name_editable.php | 8 +- .../classes/output/schedule_name_editable.php | 9 +- .../tests/behat/customreports.feature | 30 +++++ reportbuilder/tests/generator_test.php | 126 ++++++++++++++++++ .../output/audience_heading_editable_test.php | 105 +++++++++++++++ .../column_aggregation_editable_test.php | 101 ++++++++++++++ .../output/column_heading_editable_test.php | 101 ++++++++++++++ .../output/filter_heading_editable_test.php | 101 ++++++++++++++ .../output/report_name_editable_test.php | 95 +++++++++++++ .../output/schedule_name_editable_test.php | 101 ++++++++++++++ 18 files changed, 812 insertions(+), 13 deletions(-) create mode 100644 reportbuilder/tests/generator_test.php create mode 100644 reportbuilder/tests/output/audience_heading_editable_test.php create mode 100644 reportbuilder/tests/output/column_aggregation_editable_test.php create mode 100644 reportbuilder/tests/output/column_heading_editable_test.php create mode 100644 reportbuilder/tests/output/filter_heading_editable_test.php create mode 100644 reportbuilder/tests/output/report_name_editable_test.php create mode 100644 reportbuilder/tests/output/schedule_name_editable_test.php diff --git a/reportbuilder/classes/external/custom_report_filters_exporter.php b/reportbuilder/classes/external/custom_report_filters_exporter.php index d94bcaaf4f7..a61b9b453e3 100644 --- a/reportbuilder/classes/external/custom_report_filters_exporter.php +++ b/reportbuilder/classes/external/custom_report_filters_exporter.php @@ -146,7 +146,7 @@ class custom_report_filters_exporter extends exporter { $entityname = $filterinstance->get_entity_name(); $displayvalue = $filterinstance->get_header(); - $editable = new filter_heading_editable($filter->get('id')); + $editable = new filter_heading_editable(0, $filter); $activefilters[] = [ 'id' => $filter->get('id'), diff --git a/reportbuilder/classes/local/systemreports/report_schedules.php b/reportbuilder/classes/local/systemreports/report_schedules.php index c42dce210cf..b25b564c193 100644 --- a/reportbuilder/classes/local/systemreports/report_schedules.php +++ b/reportbuilder/classes/local/systemreports/report_schedules.php @@ -147,12 +147,13 @@ class report_schedules extends system_report { $this->get_schedule_entity_name() )) ->set_type(column::TYPE_TEXT) - ->add_fields("{$tablealias}.name, {$tablealias}.id") - ->set_is_sortable(true) + // We need enough fields to re-create the persistent and pass to the editable component. + ->add_fields("{$tablealias}.id, {$tablealias}.name, {$tablealias}.reportid") + ->set_is_sortable(true, ["{$tablealias}.name"]) ->add_callback(function(string $value, stdClass $schedule): string { global $PAGE; - $editable = new schedule_name_editable((int) $schedule->id); + $editable = new schedule_name_editable(0, new schedule(0, $schedule)); return $editable->render($PAGE->get_renderer('core')); }) ); diff --git a/reportbuilder/classes/local/systemreports/reports_list.php b/reportbuilder/classes/local/systemreports/reports_list.php index 58c32722398..55f9c3b9751 100644 --- a/reportbuilder/classes/local/systemreports/reports_list.php +++ b/reportbuilder/classes/local/systemreports/reports_list.php @@ -126,14 +126,19 @@ class reports_list extends system_report { $this->get_report_entity_name() )) ->set_type(column::TYPE_TEXT) - ->add_fields("{$tablealias}.name, {$tablealias}.id") - ->set_is_sortable(true) - ->add_callback(function(string $value, stdClass $row) { + // We need enough fields to re-create the persistent and pass to the editable component. + ->add_fields(implode(', ', [ + "{$tablealias}.id", + "{$tablealias}.name", + "{$tablealias}.contextid", + "{$tablealias}.type", + "{$tablealias}.usercreated", + ])) + ->set_is_sortable(true, ["{$tablealias}.name"]) + ->add_callback(static function(string $value, stdClass $report): string { global $PAGE; - $reportid = (int) $row->id; - - $editable = new report_name_editable($reportid); + $editable = new report_name_editable(0, new report(0, $report)); return $editable->render($PAGE->get_renderer('core')); }) ); diff --git a/reportbuilder/classes/output/audience_heading_editable.php b/reportbuilder/classes/output/audience_heading_editable.php index cbb119e0539..c2e0b122dd0 100644 --- a/reportbuilder/classes/output/audience_heading_editable.php +++ b/reportbuilder/classes/output/audience_heading_editable.php @@ -24,6 +24,11 @@ use core_reportbuilder\permission; use core_reportbuilder\local\audiences\base; use core_reportbuilder\local\models\audience; +defined('MOODLE_INTERNAL') || die; + +global $CFG; +require_once("{$CFG->libdir}/external/externallib.php"); + /** * Audience heading editable component * diff --git a/reportbuilder/classes/output/column_aggregation_editable.php b/reportbuilder/classes/output/column_aggregation_editable.php index 2631e72292c..7de8314d00b 100644 --- a/reportbuilder/classes/output/column_aggregation_editable.php +++ b/reportbuilder/classes/output/column_aggregation_editable.php @@ -25,6 +25,11 @@ use core_reportbuilder\permission; use core_reportbuilder\local\helpers\aggregation; use core_reportbuilder\local\models\column; +defined('MOODLE_INTERNAL') || die; + +global $CFG; +require_once("{$CFG->libdir}/external/externallib.php"); + /** * Column aggregation editable component * diff --git a/reportbuilder/classes/output/column_heading_editable.php b/reportbuilder/classes/output/column_heading_editable.php index aa70507a199..e892890b760 100644 --- a/reportbuilder/classes/output/column_heading_editable.php +++ b/reportbuilder/classes/output/column_heading_editable.php @@ -24,6 +24,11 @@ use core_reportbuilder\manager; use core_reportbuilder\permission; use core_reportbuilder\local\models\column; +defined('MOODLE_INTERNAL') || die; + +global $CFG; +require_once("{$CFG->libdir}/external/externallib.php"); + /** * Column heading editable component * diff --git a/reportbuilder/classes/output/dynamictabs/audience.php b/reportbuilder/classes/output/dynamictabs/audience.php index 15f9f59e26f..f52d98e4d22 100644 --- a/reportbuilder/classes/output/dynamictabs/audience.php +++ b/reportbuilder/classes/output/dynamictabs/audience.php @@ -106,7 +106,7 @@ class audience extends base { $persistent = $reportaudience->get_persistent(); $canedit = $reportaudience->user_can_edit(); - $editable = new audience_heading_editable($persistent->get('id')); + $editable = new audience_heading_editable(0, $persistent); $params = [ 'instanceid' => $persistent->get('id'), diff --git a/reportbuilder/classes/output/filter_heading_editable.php b/reportbuilder/classes/output/filter_heading_editable.php index ec7d8273163..9114c2e6a47 100644 --- a/reportbuilder/classes/output/filter_heading_editable.php +++ b/reportbuilder/classes/output/filter_heading_editable.php @@ -24,6 +24,11 @@ use core_reportbuilder\manager; use core_reportbuilder\permission; use core_reportbuilder\local\models\filter; +defined('MOODLE_INTERNAL') || die; + +global $CFG; +require_once("{$CFG->libdir}/external/externallib.php"); + /** * Filter heading editable component * diff --git a/reportbuilder/classes/output/report_name_editable.php b/reportbuilder/classes/output/report_name_editable.php index 882865df7d2..ab29172e012 100644 --- a/reportbuilder/classes/output/report_name_editable.php +++ b/reportbuilder/classes/output/report_name_editable.php @@ -25,6 +25,11 @@ use core\output\inplace_editable; use core_reportbuilder\permission; use core_reportbuilder\local\models\report; +defined('MOODLE_INTERNAL') || die; + +global $CFG; +require_once("{$CFG->libdir}/external/externallib.php"); + /** * Report name editable component * @@ -38,7 +43,8 @@ class report_name_editable extends inplace_editable { * Class constructor * * @param int $reportid - * @param report|null $report + * @param report|null $report The report persistent, note that in addition to id/name properties being present we also + * require the following to be correctly set in order to perform permission checks: contextid/type/usercreated */ public function __construct(int $reportid, ?report $report = null) { if ($report === null) { diff --git a/reportbuilder/classes/output/schedule_name_editable.php b/reportbuilder/classes/output/schedule_name_editable.php index 336eb70fe88..88e80d53ef4 100644 --- a/reportbuilder/classes/output/schedule_name_editable.php +++ b/reportbuilder/classes/output/schedule_name_editable.php @@ -23,6 +23,11 @@ use core\output\inplace_editable; use core_reportbuilder\permission; use core_reportbuilder\local\models\schedule; +defined('MOODLE_INTERNAL') || die; + +global $CFG; +require_once("{$CFG->libdir}/external/externallib.php"); + /** * Schedule name editable component * @@ -46,7 +51,9 @@ class schedule_name_editable extends inplace_editable { $report = $schedule->get_report(); $editable = permission::can_edit_report($report); - parent::__construct('core_reportbuilder', 'schedulename', $schedule->get('id'), $editable, $schedule->get_formatted_name(), + $displayvalue = $schedule->get_formatted_name($report->get_context()); + + parent::__construct('core_reportbuilder', 'schedulename', $schedule->get('id'), $editable, $displayvalue, $schedule->get('name'), get_string('editschedulename', 'core_reportbuilder')); } diff --git a/reportbuilder/tests/behat/customreports.feature b/reportbuilder/tests/behat/customreports.feature index 4f223119564..6fa5df5bf85 100644 --- a/reportbuilder/tests/behat/customreports.feature +++ b/reportbuilder/tests/behat/customreports.feature @@ -66,6 +66,36 @@ Feature: Manage custom reports | Name | Report source | | My report | Users | + Scenario: Create custom report as a manager + # Create a report that our manager can access, but not edit. + Given the following "core_reportbuilder > Report" exists: + | name | My report | + | source | core_user\reportbuilder\datasource\users | + And the following "core_reportbuilder > Audience" exists: + | report | My report | + | classname | core_reportbuilder\reportbuilder\audience\allusers | + | configdata | | + And the following "users" exist: + | username | firstname | lastname | + | manager1 | Manager | One | + And the following "role assigns" exist: + | user | role | contextlevel | reference | + | manager1 | manager | System | | + And I log in as "manager1" + When I navigate to "Reports > Report builder > Custom reports" in site administration + And I click on "New report" "button" + And I set the following fields in the "New report" "dialogue" to these values: + | Name | Manager report | + | Report source | Users | + And I click on "Save" "button" in the "New report" "dialogue" + And I click on "Close 'Manager report' editor" "button" + # Manager can edit their own report, but not those of other users. + And I set the field "Edit report name" in the "Manager report" "table_row" to "Manager report (renamed)" + And I open the action menu in "Manager report (renamed)" "table_row" + Then "Edit report content" "link" should be visible + And "Edit report name" "link" should not exist in the "My report" "table_row" + And ".dropdown-toggle" "css_element" should not exist in the "My report" "table_row" + Scenario: Rename custom report Given the following "core_reportbuilder > Reports" exist: | name | source | diff --git a/reportbuilder/tests/generator_test.php b/reportbuilder/tests/generator_test.php new file mode 100644 index 00000000000..77cf29f918d --- /dev/null +++ b/reportbuilder/tests/generator_test.php @@ -0,0 +1,126 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder; + +use advanced_testcase; +use core_reportbuilder_generator; +use core_reportbuilder\local\models\{audience, column, filter, report, schedule}; +use core_user\reportbuilder\datasource\users; + +/** + * Unit tests for the test data generator + * + * Note that assertions of created data content is performed in other testcases of the relevant classes, in the majority of cases + * here we just want to assert that the thing we created actually exists + * + * @package core_reportbuilder + * @covers \core_reportbuilder_generator + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class generator_test extends advanced_testcase { + + /** + * Test creating a report + */ + public function test_create_report(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + + $this->assertTrue(report::record_exists($report->get('id'))); + } + + /** + * Test creating a column + */ + public function test_create_column(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $this->assertTrue(column::record_exists($column->get('id'))); + } + + /** + * Test creating a filter + */ + public function test_create_filter(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $filter = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $this->assertTrue(filter::record_exists($filter->get('id'))); + } + + /** + * Test creating a condition + */ + public function test_create_condition(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $condition = $generator->create_condition(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $this->assertTrue(filter::record_exists($condition->get('id'))); + } + + /** + * Test creating an audience + */ + public function test_create_audience(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $audience = $generator->create_audience(['reportid' => $report->get('id'), 'configdata' => []]); + + $this->assertTrue(audience::record_exists($audience->get_persistent()->get('id'))); + } + + /** + * Test creating a schedule + */ + public function test_create_schedule(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']); + + $this->assertTrue(schedule::record_exists($schedule->get('id'))); + } +} diff --git a/reportbuilder/tests/output/audience_heading_editable_test.php b/reportbuilder/tests/output/audience_heading_editable_test.php new file mode 100644 index 00000000000..266cb49b99a --- /dev/null +++ b/reportbuilder/tests/output/audience_heading_editable_test.php @@ -0,0 +1,105 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\output; + +use advanced_testcase; +use core_reportbuilder_generator; +use core_reportbuilder\report_access_exception; +use core_user\reportbuilder\datasource\users; + +/** + * Unit tests for the audience heading editable class + * + * @package core_reportbuilder + * @covers \core_reportbuilder\output\audience_heading_editable + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class audience_heading_editable_test extends advanced_testcase { + + /** + * Test update method + */ + public function test_update(): void { + global $PAGE; + + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $audience = $generator->create_audience(['reportid' => $report->get('id'), 'configdata' => []]); + + $persistent = $audience->get_persistent(); + + $editable = audience_heading_editable::update($persistent->get('id'), 'New name'); + $result = $editable->export_for_template($PAGE->get_renderer('core')); + $this->assertEquals('New name', $result['value']); + + // Reload persistent, assert update. + $this->assertEquals('New name', $persistent->read()->get('heading')); + } + + /** + * Test update method for a user without permission to edit reports + */ + public function test_update_access_exception(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $audience = $generator->create_audience(['reportid' => $report->get('id'), 'configdata' => []]); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $this->expectException(report_access_exception::class); + $this->expectExceptionMessage('You cannot edit this report'); + audience_heading_editable::update($audience->get_persistent()->get('id'), 'New name'); + } + + /** + * Test update method via component callback + * + * @covers ::core_reportbuilder_inplace_editable + */ + public function test_update_callback(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $audience = $generator->create_audience(['reportid' => $report->get('id'), 'configdata' => []]); + + $persistent = $audience->get_persistent(); + + $params = ['audienceheading', $persistent->get('id'), 'New name']; + $editable = component_callback('core_reportbuilder', 'inplace_editable', $params); + $this->assertInstanceOf(audience_heading_editable::class, $editable); + + // Reload persistent, assert update. + $this->assertEquals('New name', $persistent->read()->get('heading')); + } +} diff --git a/reportbuilder/tests/output/column_aggregation_editable_test.php b/reportbuilder/tests/output/column_aggregation_editable_test.php new file mode 100644 index 00000000000..1c7bf1dbd87 --- /dev/null +++ b/reportbuilder/tests/output/column_aggregation_editable_test.php @@ -0,0 +1,101 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\output; + +use advanced_testcase; +use core_reportbuilder_generator; +use core_reportbuilder\report_access_exception; +use core_user\reportbuilder\datasource\users; + +/** + * Unit tests for the column aggregation editable class + * + * @package core_reportbuilder + * @covers \core_reportbuilder\output\column_aggregation_editable + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class column_aggregation_editable_test extends advanced_testcase { + + /** + * Test update method + */ + public function test_update(): void { + global $PAGE; + + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $editable = column_aggregation_editable::update($column->get('id'), 'count'); + $result = $editable->export_for_template($PAGE->get_renderer('core')); + $this->assertEquals('count', $result['value']); + + // Reload persistent, assert update. + $this->assertEquals('count', $column->read()->get('aggregation')); + } + + /** + * Test update method for a user without permission to edit reports + */ + public function test_update_access_exception(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $this->expectException(report_access_exception::class); + $this->expectExceptionMessage('You cannot edit this report'); + column_aggregation_editable::update($column->get('id'), 'New name'); + } + + /** + * Test update method via component callback + * + * @covers ::core_reportbuilder_inplace_editable + */ + public function test_update_callback(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $params = ['columnaggregation', $column->get('id'), 'count']; + $editable = component_callback('core_reportbuilder', 'inplace_editable', $params); + $this->assertInstanceOf(column_aggregation_editable::class, $editable); + + // Reload persistent, assert update. + $this->assertEquals('count', $column->read()->get('aggregation')); + } +} diff --git a/reportbuilder/tests/output/column_heading_editable_test.php b/reportbuilder/tests/output/column_heading_editable_test.php new file mode 100644 index 00000000000..9f02bb10109 --- /dev/null +++ b/reportbuilder/tests/output/column_heading_editable_test.php @@ -0,0 +1,101 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\output; + +use advanced_testcase; +use core_reportbuilder_generator; +use core_reportbuilder\report_access_exception; +use core_user\reportbuilder\datasource\users; + +/** + * Unit tests for the column heading editable class + * + * @package core_reportbuilder + * @covers \core_reportbuilder\output\column_heading_editable + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class column_heading_editable_test extends advanced_testcase { + + /** + * Test update method + */ + public function test_update(): void { + global $PAGE; + + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $editable = column_heading_editable::update($column->get('id'), 'New name'); + $result = $editable->export_for_template($PAGE->get_renderer('core')); + $this->assertEquals('New name', $result['value']); + + // Reload persistent, assert update. + $this->assertEquals('New name', $column->read()->get('heading')); + } + + /** + * Test update method for a user without permission to edit reports + */ + public function test_update_access_exception(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $this->expectException(report_access_exception::class); + $this->expectExceptionMessage('You cannot edit this report'); + column_heading_editable::update($column->get('id'), 'New name'); + } + + /** + * Test update method via component callback + * + * @covers ::core_reportbuilder_inplace_editable + */ + public function test_update_callback(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $params = ['columnheading', $column->get('id'), 'New name']; + $editable = component_callback('core_reportbuilder', 'inplace_editable', $params); + $this->assertInstanceOf(column_heading_editable::class, $editable); + + // Reload persistent, assert update. + $this->assertEquals('New name', $column->read()->get('heading')); + } +} diff --git a/reportbuilder/tests/output/filter_heading_editable_test.php b/reportbuilder/tests/output/filter_heading_editable_test.php new file mode 100644 index 00000000000..f4d5d5af5e4 --- /dev/null +++ b/reportbuilder/tests/output/filter_heading_editable_test.php @@ -0,0 +1,101 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\output; + +use advanced_testcase; +use core_reportbuilder_generator; +use core_reportbuilder\report_access_exception; +use core_user\reportbuilder\datasource\users; + +/** + * Unit tests for the filter heading editable class + * + * @package core_reportbuilder + * @covers \core_reportbuilder\output\filter_heading_editable + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class filter_heading_editable_test extends advanced_testcase { + + /** + * Test update method + */ + public function test_update(): void { + global $PAGE; + + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $filter = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $editable = filter_heading_editable::update($filter->get('id'), 'New name'); + $result = $editable->export_for_template($PAGE->get_renderer('core')); + $this->assertEquals('New name', $result['value']); + + // Reload persistent, assert update. + $this->assertEquals('New name', $filter->read()->get('heading')); + } + + /** + * Test update method for a user without permission to edit reports + */ + public function test_update_access_exception(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $filter = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $this->expectException(report_access_exception::class); + $this->expectExceptionMessage('You cannot edit this report'); + filter_heading_editable::update($filter->get('id'), 'New name'); + } + + /** + * Test update method via component callback + * + * @covers ::core_reportbuilder_inplace_editable + */ + public function test_update_callback(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $filter = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + + $params = ['filterheading', $filter->get('id'), 'New name']; + $editable = component_callback('core_reportbuilder', 'inplace_editable', $params); + $this->assertInstanceOf(filter_heading_editable::class, $editable); + + // Reload persistent, assert update. + $this->assertEquals('New name', $filter->read()->get('heading')); + } +} diff --git a/reportbuilder/tests/output/report_name_editable_test.php b/reportbuilder/tests/output/report_name_editable_test.php new file mode 100644 index 00000000000..78aac9d72c9 --- /dev/null +++ b/reportbuilder/tests/output/report_name_editable_test.php @@ -0,0 +1,95 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\output; + +use advanced_testcase; +use core_reportbuilder_generator; +use core_reportbuilder\report_access_exception; +use core_user\reportbuilder\datasource\users; + +/** + * Unit tests for the report name editable class + * + * @package core_reportbuilder + * @covers \core_reportbuilder\output\report_name_editable + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class report_name_editable_test extends advanced_testcase { + + /** + * Test update method + */ + public function test_update(): void { + global $PAGE; + + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + + $editable = report_name_editable::update($report->get('id'), 'New name'); + $result = $editable->export_for_template($PAGE->get_renderer('core')); + $this->assertEquals('New name', $result['value']); + + // Reload persistent, assert update. + $this->assertEquals('New name', $report->read()->get('name')); + } + + /** + * Test update method for a user without permission to edit reports + */ + public function test_update_access_exception(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $this->expectException(report_access_exception::class); + $this->expectExceptionMessage('You cannot edit this report'); + report_name_editable::update($report->get('id'), 'New name'); + } + + /** + * Test update method via component callback + * + * @covers ::core_reportbuilder_inplace_editable + */ + public function test_update_callback(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + + $params = ['reportname', $report->get('id'), 'New name']; + $editable = component_callback('core_reportbuilder', 'inplace_editable', $params); + $this->assertInstanceOf(report_name_editable::class, $editable); + + // Reload persistent, assert update. + $this->assertEquals('New name', $report->read()->get('name')); + } +} diff --git a/reportbuilder/tests/output/schedule_name_editable_test.php b/reportbuilder/tests/output/schedule_name_editable_test.php new file mode 100644 index 00000000000..639626de94a --- /dev/null +++ b/reportbuilder/tests/output/schedule_name_editable_test.php @@ -0,0 +1,101 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\output; + +use advanced_testcase; +use core_reportbuilder_generator; +use core_reportbuilder\report_access_exception; +use core_user\reportbuilder\datasource\users; + +/** + * Unit tests for the schedule name editable class + * + * @package core_reportbuilder + * @covers \core_reportbuilder\output\schedule_name_editable + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class schedule_name_editable_test extends advanced_testcase { + + /** + * Test update method + */ + public function test_update(): void { + global $PAGE; + + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']); + + $editable = schedule_name_editable::update($schedule->get('id'), 'New name'); + $result = $editable->export_for_template($PAGE->get_renderer('core')); + $this->assertEquals('New name', $result['value']); + + // Reload persistent, assert update. + $this->assertEquals('New name', $schedule->read()->get('name')); + } + + /** + * Test update method for a user without permission to edit reports + */ + public function test_update_access_exception(): void { + $this->resetAfterTest(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $this->expectException(report_access_exception::class); + $this->expectExceptionMessage('You cannot edit this report'); + schedule_name_editable::update($schedule->get('id'), 'New name'); + } + + /** + * Test update method via component callback + * + * @covers ::core_reportbuilder_inplace_editable + */ + public function test_update_callback(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + $report = $generator->create_report(['name' => 'My report', 'source' => users::class]); + $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']); + + $params = ['schedulename', $schedule->get('id'), 'New name']; + $editable = component_callback('core_reportbuilder', 'inplace_editable', $params); + $this->assertInstanceOf(schedule_name_editable::class, $editable); + + // Reload persistent, assert update. + $this->assertEquals('New name', $schedule->read()->get('name')); + } +} From 86f2dde3ed56384c62d1bb6f4d570b7bed5a60ad Mon Sep 17 00:00:00 2001 From: AMOS bot Date: Sat, 16 Jul 2022 00:08:10 +0000 Subject: [PATCH 14/49] Automatically generated installer lang files --- install/lang/de_du/langconfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/lang/de_du/langconfig.php b/install/lang/de_du/langconfig.php index d598e3e274e..6c02b128fb3 100644 --- a/install/lang/de_du/langconfig.php +++ b/install/lang/de_du/langconfig.php @@ -31,4 +31,4 @@ defined('MOODLE_INTERNAL') || die(); $string['parentlanguage'] = 'de'; $string['thisdirection'] = 'ltr'; -$string['thislanguage'] = 'Deutsch (Du)'; +$string['thislanguage'] = 'Deutsch (du)'; From e2cd526456069fb0438f39ab35237b36cabc47e7 Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Thu, 7 Jul 2022 10:36:52 +0800 Subject: [PATCH 15/49] MDL-74925 phpunit: add missing course/format test suite --- phpunit.xml.dist | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f2ef3acee87..34dd79c3558 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -103,6 +103,9 @@ course/tests + + course/format/tests + privacy/tests From 45dfb5ac84f7bd7fc8abd7363386b08f5272fd72 Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Thu, 7 Jul 2022 10:37:46 +0800 Subject: [PATCH 16/49] MDL-74925 course: use the correct 'remove' action --- course/format/classes/stateactions.php | 4 ++-- course/format/classes/stateupdates.php | 20 ++++++++++++++++++- .../tests/external/update_course_test.php | 2 +- .../format_theunittest_stateactions.php | 2 +- course/format/tests/stateactions_test.php | 6 +++--- course/format/tests/stateupdates_test.php | 14 ++++++------- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/course/format/classes/stateactions.php b/course/format/classes/stateactions.php index 935c6e6690b..31c3b0968ff 100644 --- a/course/format/classes/stateactions.php +++ b/course/format/classes/stateactions.php @@ -247,11 +247,11 @@ class stateactions { if (!empty($modinfo->sections[$section->section])) { foreach ($modinfo->sections[$section->section] as $modnumber) { $cm = $modinfo->cms[$modnumber]; - $updates->add_cm_delete($cm->id); + $updates->add_cm_remove($cm->id); } } course_delete_section($course, $section, true, true); - $updates->add_section_delete($sectionid); + $updates->add_section_remove($sectionid); } // Removing a section affects the full course structure. diff --git a/course/format/classes/stateupdates.php b/course/format/classes/stateupdates.php index a3921da0082..e66931f3848 100644 --- a/course/format/classes/stateupdates.php +++ b/course/format/classes/stateupdates.php @@ -134,6 +134,15 @@ class stateupdates implements JsonSerializable { $this->add_update('section', 'remove', (object)['id' => $sectionid]); } + /** + * Add track about a section removed. + * + * @param int $sectionid The affected section id. + */ + public function add_section_remove(int $sectionid): void { + $this->add_update('section', 'remove', (object)['id' => $sectionid]); + } + /** * Add track about a course module state update. * @@ -184,11 +193,20 @@ class stateupdates implements JsonSerializable { $this->add_update('cm', 'remove', (object)['id' => $cmid]); } + /** + * Add track about a course module removed. + * + * @param int $cmid the affected course module id + */ + public function add_cm_remove(int $cmid): void { + $this->add_update('cm', 'remove', (object)['id' => $cmid]); + } + /** * Add a valid update message to the update list. * * @param string $name the update name - * @param string $action the update action (usually update, create, delete) + * @param string $action the update action (usually update, create, remove) * @param stdClass $fields the object fields */ protected function add_update(string $name, string $action, stdClass $fields): void { diff --git a/course/format/tests/external/update_course_test.php b/course/format/tests/external/update_course_test.php index 355e14923ba..56d64f889bd 100644 --- a/course/format/tests/external/update_course_test.php +++ b/course/format/tests/external/update_course_test.php @@ -133,7 +133,7 @@ class update_course_test extends \externallib_advanced_testcase { 'action' => 'format_do_something', 'expected' => [ 'count' => 1, - 'action' => 'delete', + 'action' => 'remove', 'visible' => null, ], 'expectexception' => false, diff --git a/course/format/tests/fixtures/format_theunittest_stateactions.php b/course/format/tests/fixtures/format_theunittest_stateactions.php index 207f83acfb0..379fd929f64 100644 --- a/course/format/tests/fixtures/format_theunittest_stateactions.php +++ b/course/format/tests/fixtures/format_theunittest_stateactions.php @@ -65,7 +65,7 @@ class stateactions extends core_actions { ?int $targetcmid = null ): void { - $updates->add_cm_delete(array_pop($ids)); + $updates->add_cm_remove(array_pop($ids)); } /** diff --git a/course/format/tests/stateactions_test.php b/course/format/tests/stateactions_test.php index 56c1f14c057..b0733c9885b 100644 --- a/course/format/tests/stateactions_test.php +++ b/course/format/tests/stateactions_test.php @@ -143,7 +143,7 @@ class stateactions_test extends \advanced_testcase { 'cm' => [], 'count' => 0, ], - 'delete' => [ + 'remove' => [ 'course' => [], 'section' => [], 'cm' => [], @@ -236,10 +236,10 @@ class stateactions_test extends \advanced_testcase { // Format results in a way we can compare easily. $results = $this->summarize_updates($updates); - // The state actions does not use create or delete actions because they are designed + // The state actions does not use create or remove actions because they are designed // to refresh parts of the state. $this->assertEquals(0, $results['create']['count']); - $this->assertEquals(0, $results['delete']['count']); + $this->assertEquals(0, $results['remove']['count']); // Validate we have all the expected entries. $expectedtotal = count($expectedresults['course']) + count($expectedresults['section']) + count($expectedresults['cm']); diff --git a/course/format/tests/stateupdates_test.php b/course/format/tests/stateupdates_test.php index d3eca691e60..e15f6415363 100644 --- a/course/format/tests/stateupdates_test.php +++ b/course/format/tests/stateupdates_test.php @@ -186,7 +186,7 @@ class stateupdates_test extends \advanced_testcase { $this->assertEquals($action, $update->action); $this->assertEquals('section', $update->name); // Delete does not provide all fields. - if ($action == 'delete') { + if ($action == 'remove') { $this->assertEquals($section->id, $update->fields->id); } else { $this->assertEquals($expected, $update->fields); @@ -203,7 +203,7 @@ class stateupdates_test extends \advanced_testcase { return array_merge( $this->add_section_provider_helper('put'), $this->add_section_provider_helper('create'), - $this->add_section_provider_helper('delete'), + $this->add_section_provider_helper('remove'), ); } @@ -215,7 +215,7 @@ class stateupdates_test extends \advanced_testcase { */ private function add_section_provider_helper(string $action): array { // Delete does not depends on user permissions. - if ($action == 'delete') { + if ($action == 'remove') { $studentsections = [0, 1, 2]; } else { $studentsections = [0, 1]; @@ -247,7 +247,7 @@ class stateupdates_test extends \advanced_testcase { * @dataProvider add_cm_provider * @covers ::add_cm_update * @covers ::add_cm_create - * @covers ::add_cm_delete + * @covers ::add_cm_remove * * @param string $action the action name * @param string $role the user role name @@ -330,7 +330,7 @@ class stateupdates_test extends \advanced_testcase { $this->assertEquals($action, $update->action); $this->assertEquals('cm', $update->name); // Delete does not provide all fields. - if ($action == 'delete') { + if ($action == 'remove') { $this->assertEquals($cm->id, $update->fields->id); } else { $this->assertEquals($expected, $update->fields); @@ -347,7 +347,7 @@ class stateupdates_test extends \advanced_testcase { return array_merge( $this->add_cm_provider_helper('put'), $this->add_cm_provider_helper('create'), - $this->add_cm_provider_helper('delete'), + $this->add_cm_provider_helper('remove'), ); } @@ -359,7 +359,7 @@ class stateupdates_test extends \advanced_testcase { */ private function add_cm_provider_helper(string $action): array { // Delete does not depends on user permissions. - if ($action == 'delete') { + if ($action == 'remove') { $studentcms = [0, 1, 2, 3]; } else { $studentcms = [0]; From 5294f81c27b1e1bb580ca4ff1ff0d08603e0ee37 Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Wed, 13 Jul 2022 12:07:27 +0800 Subject: [PATCH 17/49] MDL-74925 core_course: initial deprecation of '_delete' methods --- course/format/classes/stateupdates.php | 6 ++++++ course/format/upgrade.txt | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/course/format/classes/stateupdates.php b/course/format/classes/stateupdates.php index e66931f3848..a1b095bf74c 100644 --- a/course/format/classes/stateupdates.php +++ b/course/format/classes/stateupdates.php @@ -128,9 +128,12 @@ class stateupdates implements JsonSerializable { /** * Add track about a section deleted. * + * @deprecated since Moodle 4.1 MDL-74925 - please call add_section_remove() instead. * @param int $sectionid The affected section id. */ public function add_section_delete(int $sectionid): void { + debugging('add_section_delete() is deprecated. Please use add_section_remove() instead.', DEBUG_DEVELOPER); + $this->add_update('section', 'remove', (object)['id' => $sectionid]); } @@ -187,9 +190,12 @@ class stateupdates implements JsonSerializable { /** * Add track about a course module deleted. * + * @deprecated since Moodle 4.1 MDL-74925 - please call add_cm_remove() instead. * @param int $cmid the affected course module id */ public function add_cm_delete(int $cmid): void { + debugging('add_cm_delete() is deprecated. Please use add_cm_remove() instead.', DEBUG_DEVELOPER); + $this->add_update('cm', 'remove', (object)['id' => $cmid]); } diff --git a/course/format/upgrade.txt b/course/format/upgrade.txt index 8707452361c..e284e13ba24 100644 --- a/course/format/upgrade.txt +++ b/course/format/upgrade.txt @@ -2,6 +2,10 @@ This files describes API changes for course formats Overview of this plugin type at http://docs.moodle.org/dev/Course_formats +=== 4.1 === +* New \core_courseformat\stateupdates methods add_section_remove() and add_cm_remove() have been added to replace + the deprecated methods add_section_delete() and add_cm_delete(). + === 4.0 === * New core_courseformat\base::uses_course_index() to define whether the course format uses course index or not. * New core_courseformat\base::supports_components() to specify if the format is compatible with reactive components. From 44508bfe93c391fe8bd490ca8e848ab5516793b3 Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Fri, 27 May 2022 13:51:38 +0800 Subject: [PATCH 18/49] MDL-68843 enrol_lti: add deleted activity warning to published tools Adds a warning to the 'name' column of the tables, letting teachers know that the activity exposed by the method has been deleted and what their options are. --- .../table/published_resources_table.php | 35 +++++++++++++++++- enrol/lti/classes/manage_table.php | 35 +++++++++++++++++- enrol/lti/lang/en/enrol_lti.php | 3 ++ enrol/lti/lib.php | 3 +- enrol/lti/pix/enrolinstancewarning.png | Bin 0 -> 339 bytes enrol/lti/pix/enrolinstancewarning.svg | 2 + 6 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 enrol/lti/pix/enrolinstancewarning.png create mode 100644 enrol/lti/pix/enrolinstancewarning.svg diff --git a/enrol/lti/classes/local/ltiadvantage/table/published_resources_table.php b/enrol/lti/classes/local/ltiadvantage/table/published_resources_table.php index d375d9163c5..8367b634da2 100644 --- a/enrol/lti/classes/local/ltiadvantage/table/published_resources_table.php +++ b/enrol/lti/classes/local/ltiadvantage/table/published_resources_table.php @@ -92,7 +92,8 @@ class published_resources_table extends \table_sql { * @return string */ public function col_name($tool) { - $name = helper::get_name($tool); + $toolcontext = \context::instance_by_id($tool->contextid, IGNORE_MISSING); + $name = $toolcontext ? helper::get_name($tool) : $this->get_deleted_activity_name_html($tool); return $this->get_display_text($tool, $name); } @@ -215,9 +216,39 @@ class published_resources_table extends \table_sql { */ protected function get_display_text($tool, $text) { if ($tool->status != ENROL_INSTANCE_ENABLED) { - return \html_writer::tag('span', $text, array('class' => 'dimmed_text')); + return \html_writer::tag('div', $text, array('class' => 'dimmed_text')); } return $text; } + + /** + * Get a warning icon, with tooltip, describing enrolment instances sharing activities which have been deleted. + * + * @param \stdClass $tool the tool instance record. + * @return string the HTML for the name column. + */ + protected function get_deleted_activity_name_html(\stdClass $tool): string { + global $OUTPUT; + $icon = \html_writer::tag( + 'a', + $OUTPUT->pix_icon('enrolinstancewarning', get_string('deletedactivityalt' , 'enrol_lti'), 'enrol_lti'), [ + "class" => "btn btn-link p-0", + "role" => "button", + "data-container" => "body", + "data-toggle" => "popover", + "data-placement" => right_to_left() ? "left" : "right", + "data-content" => get_string('deletedactivitydescription', 'enrol_lti'), + "data-html" => "true", + "tabindex" => "0", + "data-trigger" => "focus" + ] + ); + $name = \html_writer::span($icon . get_string('deletedactivity', 'enrol_lti')); + if ($tool->name) { + $name .= \html_writer::empty_tag('br') . \html_writer::empty_tag('br') . $tool->name; + } + + return $name; + } } diff --git a/enrol/lti/classes/manage_table.php b/enrol/lti/classes/manage_table.php index bd9ccc208da..262010103ba 100644 --- a/enrol/lti/classes/manage_table.php +++ b/enrol/lti/classes/manage_table.php @@ -101,7 +101,8 @@ class manage_table extends \table_sql { * @return string */ public function col_name($tool) { - $name = helper::get_name($tool); + $toolcontext = \context::instance_by_id($tool->contextid, IGNORE_MISSING); + $name = $toolcontext ? helper::get_name($tool) : $this->get_deleted_activity_name_html($tool); return $this->get_display_text($tool, $name); } @@ -247,9 +248,39 @@ class manage_table extends \table_sql { */ protected function get_display_text($tool, $text) { if ($tool->status != ENROL_INSTANCE_ENABLED) { - return \html_writer::tag('span', $text, array('class' => 'dimmed_text')); + return \html_writer::tag('div', $text, array('class' => 'dimmed_text')); } return $text; } + + /** + * Get a warning icon, with tooltip, describing enrolment instances sharing activities which have been deleted. + * + * @param \stdClass $tool the tool instance record. + * @return string the HTML for the name column. + */ + protected function get_deleted_activity_name_html(\stdClass $tool): string { + global $OUTPUT; + $icon = \html_writer::tag( + 'a', + $OUTPUT->pix_icon('enrolinstancewarning', get_string('deletedactivityalt' , 'enrol_lti'), 'enrol_lti'), [ + "class" => "btn btn-link p-0", + "role" => "button", + "data-container" => "body", + "data-toggle" => "popover", + "data-placement" => right_to_left() ? "left" : "right", + "data-content" => get_string('deletedactivitydescription', 'enrol_lti'), + "data-html" => "true", + "tabindex" => "0", + "data-trigger" => "focus" + ] + ); + $name = \html_writer::span($icon . get_string('deletedactivity', 'enrol_lti')); + if ($tool->name) { + $name .= \html_writer::empty_tag('br') . \html_writer::empty_tag('br') . $tool->name; + } + + return $name; + } } diff --git a/enrol/lti/lang/en/enrol_lti.php b/enrol/lti/lang/en/enrol_lti.php index 80f89612bcf..5808a0334d5 100644 --- a/enrol/lti/lang/en/enrol_lti.php +++ b/enrol/lti/lang/en/enrol_lti.php @@ -36,6 +36,9 @@ $string['copytoclipboard'] = 'Copy to clipboard'; $string['couldnotestablishproxy'] = 'Could not establish proxy with consumer.'; $string['customproperties'] = 'Custom properties'; $string['deeplinkingurl'] = 'Deep linking URL'; +$string['deletedactivity'] = 'Deleted activity'; +$string['deletedactivityalt'] = 'The activity shared by this instance has been deleted'; +$string['deletedactivitydescription'] = 'The activity shared by this instance has been deleted. You can select another activity to share by editing the instance or, if no longer required, you can simply delete the instance. Deleting the instance will remove any associated user enrolments.'; $string['deploymentadd'] = 'Add a deployment'; $string['deploymentaddnotice'] = 'Deployment added'; $string['deploymentdelete'] = 'Delete deployment'; diff --git a/enrol/lti/lib.php b/enrol/lti/lib.php index 7c8f78e3251..684993a20bf 100644 --- a/enrol/lti/lib.php +++ b/enrol/lti/lib.php @@ -475,6 +475,7 @@ function enrol_lti_extend_navigation_course($navigation, $course, $context) { function enrol_lti_get_fontawesome_icon_map() { return [ 'enrol_lti:managedeployments' => 'fa-sitemap', - 'enrol_lti:platformdetails' => 'fa-pencil-square-o' + 'enrol_lti:platformdetails' => 'fa-pencil-square-o', + 'enrol_lti:enrolinstancewarning' => 'fa-exclamation-circle text-danger', ]; } diff --git a/enrol/lti/pix/enrolinstancewarning.png b/enrol/lti/pix/enrolinstancewarning.png new file mode 100644 index 0000000000000000000000000000000000000000..edb89e473ea64b0ef85a779f2229c83e26607281 GIT binary patch literal 339 zcmV-Z0j&OsP)Au7Z)wjsz?+Giu;NZy?!0{0Gz0c)++T7Lof1aJTV5t#s7oO9>8u78sR zecwNM?*{-c3Bl2O|6Wzq&jN6$91Y)$F>H)s0Jj+{n0c_)>J34j=Vhe)Ktw`B0w4?U zLMe6H5QxY}WLGD05YgJp0e<+0AdhO#djxUl*%<*)siyUAA~F%tF{p9dw%=XX0o(7M zHUt1J0FF`P>wLNp6p`zyvLw#AGk`H=HHdNU#l~9GG>^ + \ No newline at end of file From 3387e637360568cedf4d21b175e2e2af42e0aa44 Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Tue, 14 Jun 2022 10:55:52 +0800 Subject: [PATCH 19/49] MDL-68843 enrol_lti: auto disable orphaned enrolment instances - pre_course_module_deletion hook to disable on mod delete - upgrade step dealing with legacy orphaned records --- enrol/lti/db/upgrade.php | 12 +++++++ enrol/lti/lib.php | 15 +++++++++ enrol/lti/tests/lib_test.php | 32 +++++++++++++++++++ .../ltiadvantage/task/sync_grades_test.php | 7 ++-- enrol/lti/version.php | 2 +- 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/enrol/lti/db/upgrade.php b/enrol/lti/db/upgrade.php index 92da6cab9b9..5cf89474132 100644 --- a/enrol/lti/db/upgrade.php +++ b/enrol/lti/db/upgrade.php @@ -450,5 +450,17 @@ function xmldb_enrol_lti_upgrade($oldversion) { // Automatically generated Moodle v4.0.0 release upgrade line. // Put any upgrade step following this. + if ($oldversion < 2022061500) { + // Disable all orphaned enrolment method instances. + $sql = "id IN (SELECT t.enrolid + FROM {enrol_lti_tools} t + LEFT JOIN {context} c ON (t.contextid = c.id) + WHERE c.id IS NULL)"; + $DB->set_field_select('enrol', 'status', 1, $sql); + + // Lti savepoint reached. + upgrade_plugin_savepoint(true, 2022061500, 'enrol', 'lti'); + } + return true; } diff --git a/enrol/lti/lib.php b/enrol/lti/lib.php index 684993a20bf..f01522cb9e1 100644 --- a/enrol/lti/lib.php +++ b/enrol/lti/lib.php @@ -479,3 +479,18 @@ function enrol_lti_get_fontawesome_icon_map() { 'enrol_lti:enrolinstancewarning' => 'fa-exclamation-circle text-danger', ]; } + +/** + * Pre-delete course module hook which disables any methods referring to the deleted module, preventing launches and allowing remap. + * + * @param stdClass $cm The deleted course module record. + */ +function enrol_lti_pre_course_module_delete(stdClass $cm) { + global $DB; + $sql = "id IN (SELECT t.enrolid + FROM {enrol_lti_tools} t + JOIN {context} c ON (t.contextid = c.id) + WHERE c.contextlevel = :contextlevel + AND c.instanceid = :cmid)"; + $DB->set_field_select('enrol', 'status', ENROL_INSTANCE_DISABLED, $sql, ['contextlevel' => CONTEXT_MODULE, 'cmid' => $cm->id]); +} diff --git a/enrol/lti/tests/lib_test.php b/enrol/lti/tests/lib_test.php index d11e0f82699..9698d5e818b 100644 --- a/enrol/lti/tests/lib_test.php +++ b/enrol/lti/tests/lib_test.php @@ -216,4 +216,36 @@ class lib_test extends \lti_advantage_testcase { // LTI enrolment has 1 enrol actions for active users -- unenrol. $this->assertCount(1, $actions); } + + /** + * Test the behaviour of an enrolment method when the activity to which it provides access is deleted. + * + * @covers \enrol_lti_pre_course_module_delete + */ + public function test_course_module_deletion() { + // Create two modules and publish them. + $course = $this->getDataGenerator()->create_course(); + $mod = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); + $mod2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); + $tooldata = [ + 'cmid' => $mod->cmid, + 'courseid' => $course->id, + ]; + $tool = $this->getDataGenerator()->create_lti_tool((object)$tooldata); + $tooldata['cmid'] = $mod2->cmid; + $tool2 = $this->getDataGenerator()->create_lti_tool((object)$tooldata); + + // Verify the instances are both enabled. + $modinstance = helper::get_lti_tool($tool->id); + $mod2instance = helper::get_lti_tool($tool2->id); + $this->assertEquals(ENROL_INSTANCE_ENABLED, $modinstance->status); + $this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status); + + // Delete a module and verify the associated instance is disabled. + course_delete_module($mod->cmid); + $modinstance = helper::get_lti_tool($tool->id); + $mod2instance = helper::get_lti_tool($tool2->id); + $this->assertEquals(ENROL_INSTANCE_DISABLED, $modinstance->status); + $this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status); + } } diff --git a/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php b/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php index e8680a5f238..76cabc0184d 100644 --- a/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php +++ b/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php @@ -419,6 +419,8 @@ class sync_grades_test extends \lti_advantage_testcase { */ public function test_sync_grades_deleted_context() { $this->resetAfterTest(); + global $DB; + [$course, $resource] = $this->create_test_environment(); $launchservice = $this->get_tool_launch_service(); @@ -427,10 +429,11 @@ class sync_grades_test extends \lti_advantage_testcase { $instructoruser = $this->getDataGenerator()->create_user(); [$userid] = $launchservice->user_launches_tool($instructoruser, $teachermocklaunch); - global $CFG; - require_once($CFG->dirroot . '/course/lib.php'); + // Delete the activity, then enable the enrolment method (it is disabled during activity deletion). $modcontext = \context::instance_by_id($resource->contextid); course_delete_module($modcontext->instanceid); + $enrol = ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_ENABLED]; + $DB->update_record('enrol', $enrol); $task = $this->get_task_with_mocked_grade_service(); $this->expectOutputRegex( diff --git a/enrol/lti/version.php b/enrol/lti/version.php index 87b900db4a1..5aa032eedb6 100644 --- a/enrol/lti/version.php +++ b/enrol/lti/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2022041900; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2022061500; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2022041200; // Requires this Moodle version. $plugin->component = 'enrol_lti'; // Full name of the plugin (used for diagnostics). $plugin->dependencies = [ From 273d9d561d7b957b0d8cd9b14767340dc2cd5231 Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Tue, 14 Jun 2022 10:56:07 +0800 Subject: [PATCH 20/49] MDL-68843 enrol_lti: add tests covering disabled instances in tasks Verify the expected behaviour of the sync_members and sync_grades tasks when enrolment instances are disabled, whether as a byproduct of module deletion, or as status change made by the user. --- .../ltiadvantage/task/sync_grades_test.php | 31 +++++++++++++++++- .../ltiadvantage/task/sync_members_test.php | 32 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php b/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php index 76cabc0184d..88e01c9dfd3 100644 --- a/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php +++ b/enrol/lti/tests/local/ltiadvantage/task/sync_grades_test.php @@ -412,6 +412,35 @@ class sync_grades_test extends \lti_advantage_testcase { $task->execute(); } + /** + * Test syncing grades when the enrolment instance is disabled. + * + * @covers ::execute + */ + public function test_sync_grades_disabled_instance() { + $this->resetAfterTest(); + global $DB; + + [$course, $resource, $resource2, $resource3] = $this->create_test_environment(); + + // Disable resource 1. + $enrol = (object) ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_DISABLED]; + $DB->update_record('enrol', $enrol); + + // Delete the activity being shared by resource 2, leaving resource 2 disabled as a result. + $modcontext = \context::instance_by_id($resource2->contextid); + course_delete_module($modcontext->instanceid); + + // Only the enabled resource 3 should sync grades. + $task = $this->get_task_with_mocked_grade_service(); + $this->expectOutputRegex( + "/^Starting - LTI Advantage grade sync for shared resource '$resource3->id' in course '$course->id'.\n". + "Completed - Synced grades for tool '$resource3->id' in the course '$course->id'. Processed 0 users; ". + "sent 0 grades.\n$/" + ); + $task->execute(); + } + /** * Test the grade sync when the context has been deleted in between launch and when the grade sync task is run. * @@ -432,7 +461,7 @@ class sync_grades_test extends \lti_advantage_testcase { // Delete the activity, then enable the enrolment method (it is disabled during activity deletion). $modcontext = \context::instance_by_id($resource->contextid); course_delete_module($modcontext->instanceid); - $enrol = ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_ENABLED]; + $enrol = (object) ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_ENABLED]; $DB->update_record('enrol', $enrol); $task = $this->get_task_with_mocked_grade_service(); diff --git a/enrol/lti/tests/local/ltiadvantage/task/sync_members_test.php b/enrol/lti/tests/local/ltiadvantage/task/sync_members_test.php index 7a2c3d3044c..ec265de1b91 100644 --- a/enrol/lti/tests/local/ltiadvantage/task/sync_members_test.php +++ b/enrol/lti/tests/local/ltiadvantage/task/sync_members_test.php @@ -609,6 +609,38 @@ class sync_members_test extends \lti_advantage_testcase { $this->assertCount(1, $userrepo->find_by_resource($resource->id)); } + /** + * Test syncing members when the enrolment instance is disabled. + * + * @covers ::execute + */ + public function test_sync_members_disabled_instance() { + $this->resetAfterTest(); + global $DB; + + [$course, $resource, $resource2, $resource3] = $this->create_test_environment(); + $userrepo = new user_repository(); + + // Disable resource 1. + $enrol = (object) ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_DISABLED]; + $DB->update_record('enrol', $enrol); + + // Delete the activity being shared by resource2, leaving resource 2 disabled as a result. + $modcontext = \context::instance_by_id($resource2->contextid); + course_delete_module($modcontext->instanceid); + + // Only the enabled resource 3 should sync members. + $task = $this->get_mock_task_with_users($this->get_mock_members_with_ids(range(1, 1))); + $task->execute(); + + $this->expectOutputRegex( + "/^Starting - Member sync for published resource '$resource3->id' for course '$course->id'.\n". + "Completed - Synced members for tool '$resource3->id' in the course '$course->id'. Processed 0 users; ". + "enrolled 0 members; unenrolled 0 members.\n$/" + ); + $this->assertCount(0, $userrepo->find_by_resource($resource->id)); + } + /** * Test syncing members for a membersync-enabled resource when the launch omits the NRPS service endpoints. * From 919ea1dde192a4db589dbc0da25a21e17c21b058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1igo=20Zendegi?= Date: Thu, 9 Jun 2022 16:02:55 +0200 Subject: [PATCH 21/49] MDL-74857 language: Start using the 'statusok' string for status --- admin/renderer.php | 2 +- backup/util/helper/backup_cron_helper.class.php | 2 +- course/reset.php | 2 +- lib/classes/plugin_manager.php | 2 +- report/backups/index.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/admin/renderer.php b/admin/renderer.php index 57d4f98c76f..0c66010ed73 100644 --- a/admin/renderer.php +++ b/admin/renderer.php @@ -2080,7 +2080,7 @@ class core_admin_renderer extends plugin_renderer_base { $errorline = true; } else { if ($status) { //Handle ok result (ok) - $status = get_string('ok'); + $status = get_string('statusok'); } else { if ($environment_result->getLevel() == 'optional') {//Handle check result (warning) $status = get_string('check'); diff --git a/backup/util/helper/backup_cron_helper.class.php b/backup/util/helper/backup_cron_helper.class.php index f21631d182e..ff7610398b9 100644 --- a/backup/util/helper/backup_cron_helper.class.php +++ b/backup/util/helper/backup_cron_helper.class.php @@ -202,7 +202,7 @@ abstract class backup_cron_automated_helper { $message .= get_string('summary') . "\n"; $message .= "==================================================\n"; $message .= ' ' . get_string('courses') . ': ' . array_sum($count) . "\n"; - $message .= ' ' . get_string('ok') . ': ' . $count[self::BACKUP_STATUS_OK] . "\n"; + $message .= ' ' . get_string('statusok') . ': ' . $count[self::BACKUP_STATUS_OK] . "\n"; $message .= ' ' . get_string('skipped') . ': ' . $count[self::BACKUP_STATUS_SKIPPED] . "\n"; $message .= ' ' . get_string('error') . ': ' . $count[self::BACKUP_STATUS_ERROR] . "\n"; $message .= ' ' . get_string('unfinished') . ': ' . $count[self::BACKUP_STATUS_UNFINISHED] . "\n"; diff --git a/course/reset.php b/course/reset.php index 412fe2b2f88..dba445de3b1 100644 --- a/course/reset.php +++ b/course/reset.php @@ -79,7 +79,7 @@ if ($mform->is_cancelled()) { $line = array(); $line[] = $item['component']; $line[] = $item['item']; - $line[] = ($item['error']===false) ? get_string('ok') : '
'.$item['error'].'
'; + $line[] = ($item['error'] === false) ? get_string('statusok') : '
'.$item['error'].'
'; $data[] = $line; } diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index 8b75ead7947..30731d6f18a 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1320,7 +1320,7 @@ class core_plugin_manager { return false; } - $ok = get_string('ok', 'core'); + $ok = get_string('statusok', 'core'); // Let admins know they can expect more verbose output. $silent or $this->mtrace(get_string('packagesdebug', 'core_plugin'), PHP_EOL, DEBUG_NORMAL); diff --git a/report/backups/index.php b/report/backups/index.php index 22b128859a8..ffd2145f80e 100644 --- a/report/backups/index.php +++ b/report/backups/index.php @@ -40,7 +40,7 @@ admin_externalpage_setup('reportbackups', '', null, '', array('pagelayout'=>'rep $strftimedatetime = get_string('strftimerecent'); $strerror = get_string('error'); -$strok = get_string('ok'); +$strok = get_string('statusok'); $strunfinished = get_string('unfinished'); $strskipped = get_string('skipped'); $strwarning = get_string('warning'); From 2b28996a2e80b28bb825921f2605b03003735b9d Mon Sep 17 00:00:00 2001 From: Angelia Dela Cruz Date: Mon, 17 Jan 2022 17:47:48 +0800 Subject: [PATCH 22/49] MDL-73788 behat: URL behat generators use UI Replace steps that manually add URL instances via the UI and use Behat generators. This improves the speed of Behat test runs. --- .../behat/restore_moodle2_courses.feature | 37 +++------- .../tests/behat/add_url.feature | 71 +++++++++++++------ .../videojs/tests/behat/modules.feature | 13 ++-- 3 files changed, 66 insertions(+), 55 deletions(-) diff --git a/backup/util/ui/tests/behat/restore_moodle2_courses.feature b/backup/util/ui/tests/behat/restore_moodle2_courses.feature index 949192f6c6e..f14bf80cfe8 100644 --- a/backup/util/ui/tests/behat/restore_moodle2_courses.feature +++ b/backup/util/ui/tests/behat/restore_moodle2_courses.feature @@ -12,9 +12,10 @@ Feature: Restore Moodle 2 course backups | Course 3 | C3 | 0 | topics | 2 | 0 | | Course 4 | C4 | 0 | topics | 20 | 0 | And the following "activities" exist: - | activity | course | idnumber | name | intro | section | - | assign | C3 | assign1 | Test assign name | Assign description | 1 | - | data | C3 | data1 | Test database name | Database description | 2 | + | activity | course | idnumber | name | intro | section | externalurl | + | assign | C3 | assign1 | Test assign name | Assign description | 1 | | + | data | C3 | data1 | Test database name | Database description | 2 | | + | url | C1 | url1 | Test URL name | Test URL description | 3 | http://www.moodle.org | And I log in as "admin" And I am on "Course 1" course homepage with editing mode on And I add a "Forum" to section "1" and I fill the form with: @@ -108,11 +109,7 @@ Feature: Restore Moodle 2 course backups @javascript Scenario: Restore a backup in an existing course retaining the backup course settings - Given I add a "URL" to section "3" and I fill the form with: - | Name | Test URL name | - | Description | Test URL description | - | id_externalurl | http://www.moodle.org | - And I hide section "3" + Given I hide section "3" And I hide section "7" When I backup "Course 1" course using this options: | Confirmation | Filename | test_backup.mbz | @@ -134,11 +131,7 @@ Feature: Restore Moodle 2 course backups @javascript Scenario: Restore a backup in an existing course keeping the target course settings - Given I add a "URL" to section "3" and I fill the form with: - | Name | Test URL name | - | Description | Test URL description | - | id_externalurl | http://www.moodle.org | - And I hide section "3" + Given I hide section "3" And I hide section "7" When I backup "Course 1" course using this options: | Confirmation | Filename | test_backup.mbz | @@ -160,11 +153,7 @@ Feature: Restore Moodle 2 course backups @javascript Scenario: Restore a backup in an existing course deleting contents and retaining the backup course settings - Given I add a "URL" to section "3" and I fill the form with: - | Name | Test URL name | - | Description | Test URL description | - | id_externalurl | http://www.moodle.org | - And I hide section "3" + Given I hide section "3" And I hide section "7" When I backup "Course 1" course using this options: | Initial | Include enrolled users | 0 | @@ -188,11 +177,7 @@ Feature: Restore Moodle 2 course backups @javascript Scenario: Restore a backup in an existing course deleting contents and keeping the current course settings - Given I add a "URL" to section "3" and I fill the form with: - | Name | Test URL name | - | Description | Test URL description | - | id_externalurl | http://www.moodle.org | - And I hide section "3" + Given I hide section "3" And I hide section "7" When I backup "Course 1" course using this options: | Initial | Include enrolled users | 0 | @@ -216,11 +201,7 @@ Feature: Restore Moodle 2 course backups @javascript Scenario: Restore a backup in an existing course deleting contents decreasing the number of sections - Given I add a "URL" to section "3" and I fill the form with: - | Name | Test URL name | - | Description | Test URL description | - | id_externalurl | http://www.moodle.org | - And I hide section "3" + Given I hide section "3" And I hide section "7" When I backup "Course 1" course using this options: | Initial | Include enrolled users | 0 | diff --git a/blocks/site_main_menu/tests/behat/add_url.feature b/blocks/site_main_menu/tests/behat/add_url.feature index 779e862fe42..cd4483cf748 100644 --- a/blocks/site_main_menu/tests/behat/add_url.feature +++ b/blocks/site_main_menu/tests/behat/add_url.feature @@ -10,11 +10,18 @@ Feature: Add URL to main menu block And I am on site homepage And I turn editing mode on And I add the "Main menu" block - When I add a "URL" to section "0" and I fill the form with: - | Name | reference link | - | Description | mooooooooodle | - | External URL | http://www.moodle.com | - | id_display | In pop-up | + And the following "activity" exists: + | activity | url | + | course | Acceptance test site | + | name | reference link | + | intro | mooooooooodle | + | externalurl | http://www.moodle.com | + | section | 0 | + When I am on the "reference link" "url activity editing" page + And I expand all fieldsets + And I set the following fields to these values: + | id_display | In pop-up | + And I press "Save and return to course" Then "reference link" "link" should exist in the "Main menu" "block" And "Add an activity or resource" "button" should exist in the "Main menu" "block" @@ -33,11 +40,18 @@ Feature: Add URL to main menu block And I set the following fields to these values: | Page contexts | Display throughout the entire site | And I press "Save changes" - And I add a "URL" to section "0" and I fill the form with: - | Name | reference link | - | Description | mooooooooodle | - | External URL | http://www.moodle.com | - | id_display | Embed | + And the following "activity" exists: + | activity | url | + | course | Acceptance test site | + | name | reference link | + | intro | mooooooooodle | + | externalurl | http://www.moodle.com | + | section | 0 | + And I am on the "reference link" "url activity editing" page + And I expand all fieldsets + And I set the following fields to these values: + | id_display | Embed | + And I press "Save and return to course" Then I click on "reference link" "link" in the "Main menu" "block" And "reference link" "link" should exist in the "Main menu" "block" And I am on the "C1" "Course" page @@ -60,11 +74,18 @@ Feature: Add URL to main menu block And I set the following fields to these values: | Page contexts | Display on the site home and any pages added to the site home. | And I press "Save changes" - And I add a "URL" to section "0" and I fill the form with: - | Name | reference link | - | Description | mooooooooodle | - | External URL | http://www.moodle.com | - | id_display | Embed | + And the following "activity" exists: + | activity | url | + | course | Acceptance test site | + | name | reference link | + | intro | mooooooooodle | + | externalurl | http://www.moodle.com | + | section | 0 | + And I am on the "reference link" "url activity editing" page + And I expand all fieldsets + And I set the following fields to these values: + | id_display | Embed | + And I press "Save and return to course" Then I click on "reference link" "link" in the "Main menu" "block" And "reference link" "link" should exist in the "Main menu" "block" And I am on the "C1" "Course" page @@ -87,12 +108,18 @@ Feature: Add URL to main menu block And I set the following fields to these values: | Page contexts | Display throughout the entire site | And I press "Save changes" - When I am on the "C1" Course page - And I add a "URL" to section "0" and I fill the form with: - | Name | reference link | - | Description | mooooooooodle | - | External URL | http://www.moodle.com | - | Display description on course page | 1 | - | id_display | In pop-up | + When the following "activity" exists: + | activity | url | + | course | C1 | + | name | reference link | + | intro | mooooooooodle | + | externalurl | http://www.moodle.com | + | section | 0 | + | showdescription | 1 | + And I am on the "reference link" "url activity editing" page + And I expand all fieldsets + And I set the following fields to these values: + | id_display | In pop-up | + And I press "Save and return to course" Then "reference link" "link" should not exist in the "Main menu" "block" And I should see "mooooooooodle" in the "region-main" "region" diff --git a/media/player/videojs/tests/behat/modules.feature b/media/player/videojs/tests/behat/modules.feature index 951bedf842d..36c900f67b5 100644 --- a/media/player/videojs/tests/behat/modules.feature +++ b/media/player/videojs/tests/behat/modules.feature @@ -11,11 +11,14 @@ Feature: Embed videos without the media filter @javascript Scenario: Add a video in a URL resource. Make sure media filters work - When I add a "URL" to section "1" and I fill the form with: - | Name | Video URL | - | Description | Example of a video url | - | External URL | http://download.moodle.org/mediatest/quicktime_320_180.mov | - And I follow "Video URL" + Given the following "activity" exists: + | activity | url | + | course | Acceptance test site | + | name | Video URL | + | intro | Example of a video url | + | externalurl | http://download.moodle.org/mediatest/quicktime_320_180.mov | + | section | 1 | + When I am on the "Video URL" "url activity" page Then ".video-js" "css_element" should exist And I am on site homepage From 2ca262a5e3bc1f09c75b475c27d161fab4f54c3c Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Fri, 17 Jun 2022 13:48:31 +0800 Subject: [PATCH 23/49] MDL-68011 core: Final deprecation of get_module_metadata() --- lib/deprecatedlib.php | 99 ++----------------------------------------- lib/upgrade.txt | 1 + 2 files changed, 5 insertions(+), 95 deletions(-) diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index 7c7a439922e..9bd6669838f 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -2770,102 +2770,11 @@ function report_insights_context_insights(\context $context) { } /** - * Retrieve all metadata for the requested modules - * - * @deprecated since 3.9. - * @param object $course The Course - * @param array $modnames An array containing the list of modules and their - * names - * @param int $sectionreturn The section to return to - * @return array A list of stdClass objects containing metadata about each - * module + * @deprecated since 3.9 */ -function get_module_metadata($course, $modnames, $sectionreturn = null) { - global $OUTPUT; - - debugging('get_module_metadata is deprecated. Please use \core_course\local\service\content_item_service instead.'); - - // get_module_metadata will be called once per section on the page and courses may show - // different modules to one another - static $modlist = array(); - if (!isset($modlist[$course->id])) { - $modlist[$course->id] = array(); - } - - $return = array(); - $urlbase = new moodle_url('/course/mod.php', array('id' => $course->id, 'sesskey' => sesskey())); - if ($sectionreturn !== null) { - $urlbase->param('sr', $sectionreturn); - } - foreach($modnames as $modname => $modnamestr) { - if (!course_allowed_module($course, $modname)) { - continue; - } - if (isset($modlist[$course->id][$modname])) { - // This module is already cached - $return += $modlist[$course->id][$modname]; - continue; - } - $modlist[$course->id][$modname] = array(); - - // Create an object for a default representation of this module type in the activity chooser. It will be used - // if module does not implement callback get_shortcuts() and it will also be passed to the callback if it exists. - $defaultmodule = new stdClass(); - $defaultmodule->title = $modnamestr; - $defaultmodule->name = $modname; - $defaultmodule->link = new moodle_url($urlbase, array('add' => $modname)); - $defaultmodule->icon = $OUTPUT->pix_icon('icon', '', $defaultmodule->name, array('class' => 'icon')); - $sm = get_string_manager(); - if ($sm->string_exists('modulename_help', $modname)) { - $defaultmodule->help = get_string('modulename_help', $modname); - if ($sm->string_exists('modulename_link', $modname)) { // Link to further info in Moodle docs. - $link = get_string('modulename_link', $modname); - $linktext = get_string('morehelp'); - $defaultmodule->help .= html_writer::tag('div', - $OUTPUT->doc_link($link, $linktext, true), array('class' => 'helpdoclink')); - } - } - $defaultmodule->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER); - - // Each module can implement callback modulename_get_shortcuts() in its lib.php and return the list - // of elements to be added to activity chooser. - $items = component_callback($modname, 'get_shortcuts', array($defaultmodule), null); - if ($items !== null) { - foreach ($items as $item) { - // Add all items to the return array. All items must have different links, use them as a key in the return array. - if (!isset($item->archetype)) { - $item->archetype = $defaultmodule->archetype; - } - if (!isset($item->icon)) { - $item->icon = $defaultmodule->icon; - } - // If plugin returned the only one item with the same link as default item - cache it as $modname, - // otherwise append the link url to the module name. - $item->name = (count($items) == 1 && - $item->link->out() === $defaultmodule->link->out()) ? $modname : $modname . ':' . $item->link; - - // If the module provides the helptext property, append it to the help text to match the look and feel - // of the default course modules. - if (isset($item->help) && isset($item->helplink)) { - $linktext = get_string('morehelp'); - $item->help .= html_writer::tag('div', - $OUTPUT->doc_link($item->helplink, $linktext, true), array('class' => 'helpdoclink')); - } - $modlist[$course->id][$modname][$item->name] = $item; - } - $return += $modlist[$course->id][$modname]; - // If get_shortcuts() callback is defined, the default module action is not added. - // It is a responsibility of the callback to add it to the return value unless it is not needed. - continue; - } - - // The callback get_shortcuts() was not found, use the default item for the activity chooser. - $modlist[$course->id][$modname][$modname] = $defaultmodule; - $return[$modname] = $defaultmodule; - } - - core_collator::asort_objects_by_property($return, 'title'); - return $return; +function get_module_metadata() { + throw new coding_exception( + 'get_module_metadata() has been removed. Please use \core_course\local\service\content_item_service instead.'); } /** diff --git a/lib/upgrade.txt b/lib/upgrade.txt index bd358165798..cded6d8f4b1 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -37,6 +37,7 @@ information provided here is intended especially for developers. * Added $CFG->proxylogunsafe and proxyfixunsafe to detect code which doesn't honor the proxy config * Function admin_externalpage_setup() now has additional option 'nosearch' allowing to remove Site administration search form. * The function print_error has been deprecated. Kindly use moodle_exception. +* The function get_module_metadata() has been finally deprecated and can not be used anymore. === 4.0 === From 6a122984daf5d3426ada811e3e228638f0e333bc Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Fri, 17 Jun 2022 13:53:40 +0800 Subject: [PATCH 24/49] MDL-68011 course: Final deprecation of the callback get_shortcuts() Removal of the callback get_shortcuts() and its plugin implementations. The private methods in the content_item_readonly_repository class which were only used by the callback hook logic are also removed. --- .../content_item_readonly_repository.php | 111 +----------------- course/format/singleactivity/lib.php | 1 - mod/lti/deprecatedlib.php | 32 +++++ mod/lti/lib.php | 33 ------ mod/lti/source/upgrade.txt | 4 + mod/lti/upgrade.txt | 4 + mod/upgrade.txt | 4 + 7 files changed, 46 insertions(+), 143 deletions(-) create mode 100644 mod/lti/deprecatedlib.php diff --git a/course/classes/local/repository/content_item_readonly_repository.php b/course/classes/local/repository/content_item_readonly_repository.php index 54e58156d54..32bd29d2880 100644 --- a/course/classes/local/repository/content_item_readonly_repository.php +++ b/course/classes/local/repository/content_item_readonly_repository.php @@ -61,63 +61,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito return $help; } - /** - * Create a content_item object based on legacy data returned from the get_shortcuts hook implementations. - * - * @param \stdClass $item the stdClass of legacy data. - * @return content_item a content item object. - */ - private function content_item_from_legacy_data(\stdClass $item): content_item { - global $OUTPUT; - - // Make sure the legacy data results in a content_item with id = 0. - // Even with an id, we can't uniquely identify the item, because we can't guarantee what component it came from. - // An id of -1, signifies this. - $item->id = -1; - - // If the module provides the helplink property, append it to the help text to match the look and feel - // of the default course modules. - if (isset($item->help) && isset($item->helplink)) { - $linktext = get_string('morehelp'); - $item->help .= \html_writer::tag('div', - $OUTPUT->doc_link($item->helplink, $linktext, true), ['class' => 'helpdoclink']); - } - - if (is_string($item->title)) { - $item->title = new string_title($item->title); - } else if ($item->title instanceof \lang_string) { - $item->title = new lang_string_title($item->title->get_identifier(), $item->title->get_component()); - } - - // Legacy items had names which are in one of 2 forms: - // modname, i.e. 'assign' or - // modname:link, i.e. lti:http://etc... - // We need to grab the module name out to create the componentname. - $modname = (strpos($item->name, ':') !== false) ? explode(':', $item->name)[0] : $item->name; - $purpose = plugin_supports('mod', $modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER); - return new content_item($item->id, $item->name, $item->title, $item->link, $item->icon, $item->help ?? '', - $item->archetype, 'mod_' . $modname, $purpose); - } - - /** - * Create a stdClass type object based on a content_item instance. - * - * @param content_item $contentitem - * @return \stdClass the legacy data. - */ - private function content_item_to_legacy_data(content_item $contentitem): \stdClass { - $item = new \stdClass(); - $item->id = $contentitem->get_id(); - $item->name = $contentitem->get_name(); - $item->title = $contentitem->get_title(); - $item->link = $contentitem->get_link(); - $item->icon = $contentitem->get_icon(); - $item->help = $contentitem->get_help(); - $item->archetype = $contentitem->get_archetype(); - $item->componentname = $contentitem->get_component_name(); - return $item; - } - /** * Helper to get the contentitems from all subplugin hooks for a given module plugin. * @@ -165,20 +108,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito return $contentitems; } - /** - * Helper to make sure any legacy items have certain properties, which, if missing are inherited from the parent module item. - * - * @param \stdClass $legacyitem the legacy information, a stdClass coming from get_shortcuts() hook. - * @param content_item $modulecontentitem The module's content item information, to inherit if needed. - * @return \stdClass the updated legacy item stdClass - */ - private function legacy_item_inherit_missing(\stdClass $legacyitem, content_item $modulecontentitem): \stdClass { - // Fall back to the plugin parent value if the subtype didn't provide anything. - $legacyitem->archetype = $legacyitem->archetype ?? $modulecontentitem->get_archetype(); - $legacyitem->icon = $legacyitem->icon ?? $modulecontentitem->get_icon(); - return $legacyitem; - } - /** * Find all the available content items, not restricted to course or user. * @@ -254,9 +183,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito $modules = $DB->get_records('modules', ['visible' => 1]); $return = []; - // A moodle_url is expected and required by modules in their implementation of the hook 'get_shortcuts'. - $urlbase = new \moodle_url('/course/mod.php', ['id' => $course->id]); - // Now, generate the content_items. foreach ($modules as $modid => $mod) { // Exclude modules if the code doesn't exist. @@ -273,7 +199,7 @@ class content_item_readonly_repository implements content_item_readonly_reposito $mod->id, $mod->name, new lang_string_title("modulename", $mod->name), - new \moodle_url($urlbase, ['add' => $mod->name]), + new \moodle_url('/course/mod.php', ['id' => $course->id, 'add' => $mod->name]), $OUTPUT->pix_icon('monologo', '', $mod->name, ['class' => 'icon activityicon']), $help, $archetype, @@ -281,10 +207,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito $purpose, ); - // Legacy vs new hooks. - // If the new hook is found for a module plugin, use that path (calling mod plugins and their subplugins directly) - // If not, check the legacy hook. This won't provide us with enough information to identify items uniquely within their - // component (lti + lti source being an example), but we can still list these items. $modcontentitemreference = clone($contentitem); if (component_callback_exists('mod_' . $mod->name, 'get_course_content_items')) { @@ -301,37 +223,8 @@ class content_item_readonly_repository implements content_item_readonly_reposito array_push($return, ...$subpluginitems); } - } else if (component_callback_exists('mod_' . $mod->name, 'get_shortcuts')) { - // TODO: MDL-68011 this block needs to be removed in 4.1. - debugging('The callback get_shortcuts has been deprecated. Please use get_course_content_items and - get_all_content_items instead. Some features of the activity chooser, such as favourites and recommendations - are not supported when providing content items via the deprecated callback.'); - - // If get_shortcuts() callback is defined, the default module action is not added. - // It is a responsibility of the callback to add it to the return value unless it is not needed. - // The legacy hook, get_shortcuts, expects a stdClass representation of the core module content_item entry. - $modcontentitemreference = $this->content_item_to_legacy_data($contentitem); - - $legacyitems = component_callback($mod->name, 'get_shortcuts', [$modcontentitemreference], null); - if (!is_null($legacyitems)) { - foreach ($legacyitems as $legacyitem) { - - $legacyitem = $this->legacy_item_inherit_missing($legacyitem, $contentitem); - - // All items must have different links, use them as a key in the return array. - // If plugin returned the only one item with the same link as default item - keep $modname, - // otherwise append the link url to the module name. - $legacyitem->name = (count($legacyitems) == 1 && - $legacyitem->link->out() === $contentitem->get_link()->out()) ? $mod->name : $mod->name . ':' . - $legacyitem->link; - - $plugincontentitem = $this->content_item_from_legacy_data($legacyitem); - - $return[] = $plugincontentitem; - } - } } else { - // Neither callback was found, so just use the default module content item. + // Callback was not found, so just use the default module content item. $return[] = $contentitem; } } diff --git a/course/format/singleactivity/lib.php b/course/format/singleactivity/lib.php index 7f6161ce47a..6a2dd522006 100644 --- a/course/format/singleactivity/lib.php +++ b/course/format/singleactivity/lib.php @@ -378,7 +378,6 @@ class format_singleactivity extends core_courseformat\base { /** * Checks if the activity type has multiple items in the activity chooser. - * This may happen as a result of defining callback modulename_get_shortcuts(). * * @return bool|null (null if the check is not possible) */ diff --git a/mod/lti/deprecatedlib.php b/mod/lti/deprecatedlib.php new file mode 100644 index 00000000000..bf00925c88d --- /dev/null +++ b/mod/lti/deprecatedlib.php @@ -0,0 +1,32 @@ +. + +/** + * List of deprecated mod_lti functions. + * + * @package mod_lti + * @copyright 2022 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * @deprecated since 3.9 + **/ +function lti_get_shortcuts() { + throw new coding_exception( + 'The callback get_shortcuts() is now removed. Please use get_course_content_items and get_all_content_items instead.' + ); +} diff --git a/mod/lti/lib.php b/mod/lti/lib.php index 528bc2c863c..bfe7cd71e28 100644 --- a/mod/lti/lib.php +++ b/mod/lti/lib.php @@ -225,39 +225,6 @@ function lti_delete_instance($id) { } -/** - * Return aliases of this activity. LTI should have an alias for each configured tool type - * This is so you can add an external tool types directly to the activity chooser - * - * @deprecated since 3.9 - * @todo MDL-68011 This is to be moved from here to deprecatedlib.php in Moodle 4.1 - * @param stdClass $defaultitem default item that would be added to the activity chooser if this callback was not present. - * It has properties: archetype, name, title, help, icon, link - * @return array An array of aliases for this activity. Each element is an object with same list of properties as $defaultitem, - * plus an additional property, helplink. - * Properties title and link are required - **/ -function lti_get_shortcuts($defaultitem) { - global $CFG, $COURSE; - require_once($CFG->dirroot.'/mod/lti/locallib.php'); - - $types = lti_get_configured_types($COURSE->id, $defaultitem->link->param('sr')); - if (has_capability('mod/lti:addmanualinstance', context_course::instance($COURSE->id))) { - $types[] = $defaultitem; - } - - // Add items defined in ltisource plugins. - foreach (core_component::get_plugin_list('ltisource') as $pluginname => $dir) { - // LTISOURCE plugins can also implement callback get_shortcuts() to add items to the activity chooser. - // The return values are the same as of the 'mod' callbacks except that $defaultitem is only passed for reference and - // should not be added to the return value. - if ($moretypes = component_callback("ltisource_$pluginname", 'get_shortcuts', array($defaultitem))) { - $types = array_merge($types, $moretypes); - } - } - return $types; -} - /** * Return the preconfigured tools which are configured for inclusion in the activity picker. * diff --git a/mod/lti/source/upgrade.txt b/mod/lti/source/upgrade.txt index 59239721ab2..0951dd1e3b3 100644 --- a/mod/lti/source/upgrade.txt +++ b/mod/lti/source/upgrade.txt @@ -1,6 +1,10 @@ This files describes API changes in /mod/lti/source/* - LTI source plugins, information provided here is intended especially for developers. +=== 4.1 === + +* The callback get_shortcuts() is now removed. Please use get_course_content_items and get_all_content_items instead. + === 3.9 === * The callback get_shortcuts() is now deprecated. Please use get_course_content_items and get_all_content_items instead. diff --git a/mod/lti/upgrade.txt b/mod/lti/upgrade.txt index 318c625ce78..5fed05c84b1 100644 --- a/mod/lti/upgrade.txt +++ b/mod/lti/upgrade.txt @@ -1,5 +1,9 @@ This files describes API changes in the lti code. +=== 4.1 === + +* The callback get_shortcuts() is now removed. Please use get_course_content_items and get_all_content_items instead. + === 3.10 === * Select Content supports multiple, allowing a tool to return more than one link at a time. diff --git a/mod/upgrade.txt b/mod/upgrade.txt index 3c5fd2d0040..df69896e334 100644 --- a/mod/upgrade.txt +++ b/mod/upgrade.txt @@ -1,6 +1,10 @@ This files describes API changes in /mod/* - activity modules, information provided here is intended especially for developers. +=== 4.1 === + +* The callback get_shortcuts() is now removed. Please use get_course_content_items and get_all_content_items instead. + === 4.0 === * A new API function introduced to handle custom completion logic. Refer to completion/upgrade.txt for additional information. * Modules that extend the settings navigation via '_extend_settings_navigation()' should use the get_page() method from From 49693c6629a61388e636ebd91a24205a5f518818 Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 19 Jul 2022 14:33:39 +0800 Subject: [PATCH 25/49] MDL-68011 coourse: Final deprecation of course_modchooser() --- course/renderer.php | 14 ++++---------- course/upgrade.txt | 4 ++++ theme/upgrade.txt | 4 ++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/course/renderer.php b/course/renderer.php index 27c53644075..5e01445dc5d 100644 --- a/course/renderer.php +++ b/course/renderer.php @@ -134,17 +134,11 @@ class core_course_renderer extends plugin_renderer_base { } /** - * Build the HTML for the module chooser javascript popup - * - * @param array $modules A set of modules as returned form @see - * get_module_metadata - * @param object $course The course that will be displayed - * @return string The composed HTML for the module + * @deprecated since 3.9 */ - public function course_modchooser($modules, $course) { - debugging('course_modchooser() is deprecated. Please use course_activitychooser() instead.', DEBUG_DEVELOPER); - - return $this->course_activitychooser($course->id); + public function course_modchooser() { + throw new coding_exception( + 'course_modchooser() can not be used anymore, please use course_activitychooser() instead.'); } /** diff --git a/course/upgrade.txt b/course/upgrade.txt index 776509a3cdb..d5e7973311b 100644 --- a/course/upgrade.txt +++ b/course/upgrade.txt @@ -1,6 +1,10 @@ This files describes API changes in /course/*, information provided here is intended especially for developers. +=== 4.1 === +* The function course_modchooser() has been finally deprecated and can not be used anymore. Please use + course_activitychooser() instead. + === 4.0 === * All activity icons have been replaced with black monochrome icons. The background colour for these icons is defined using a new 'FEATURE_MOD_PURPOSE' support variable in the module lib.php file diff --git a/theme/upgrade.txt b/theme/upgrade.txt index d4e6038ff70..c2fa7046373 100644 --- a/theme/upgrade.txt +++ b/theme/upgrade.txt @@ -1,6 +1,10 @@ This files describes API changes in /theme/* themes, information provided here is intended especially for theme designer. +=== 4.1 === +* The function core_course_renderer::course_modchooser() has been finally deprecated and can not be used anymore. + Please use core_course_renderer::course_activitychooser() instead. + === 4.0 === * A new theme config 'removedprimarynavitems' allows a theme to customise primary navigation by specifying the list of items to remove. * A new theme config 'usescourseindex' allows a theme to specify whether it implements and uses course index. From 0b616158cbb2704514c60e620cfffc0569c62a3b Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Tue, 19 Jul 2022 08:23:50 +0100 Subject: [PATCH 26/49] MDL-74656 badges: add test generator class for creating badges. --- badges/tests/generator/lib.php | 76 +++++++++++++++++++ .../reportbuilder/datasource/badges_test.php | 55 +++----------- 2 files changed, 86 insertions(+), 45 deletions(-) create mode 100644 badges/tests/generator/lib.php diff --git a/badges/tests/generator/lib.php b/badges/tests/generator/lib.php new file mode 100644 index 00000000000..6f990c189b2 --- /dev/null +++ b/badges/tests/generator/lib.php @@ -0,0 +1,76 @@ +. + +declare(strict_types=1); + +use core_badges\badge; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once("{$CFG->libdir}/badgeslib.php"); + +/** + * Badges test generator + * + * @package core_badges + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_badges_generator extends component_generator_base { + + /** + * Create badge + * + * TODO: MDL-73648 Use from Behat too + * + * @param array|stdClass $record + */ + public function create_badge($record): badge { + global $DB, $USER; + + $record = (object) array_merge([ + 'name' => 'Test badge', + 'description' => 'Testing badges', + 'timecreated' => time(), + 'timemodified' => time(), + 'usercreated' => $USER->id, + 'usermodified' => $USER->id, + 'issuername' => 'Test issuer', + 'issuerurl' => 'http://issuer-url.domain.co.nz', + 'issuercontact' => 'issuer@example.com', + 'expiredate' => null, + 'expireperiod' => null, + 'type' => BADGE_TYPE_SITE, + 'courseid' => null, + 'messagesubject' => 'Test message subject', + 'message' => 'Test message body', + 'attachment' => 1, + 'notification' => 0, + 'status' => BADGE_STATUS_ACTIVE, + 'version' => OPEN_BADGES_V2, + 'language' => 'en', + 'imageauthorname' => 'Image author', + 'imageauthoremail' => 'author@example.com', + 'imageauthorurl' => 'http://image.example.com/', + 'imagecaption' => 'Image caption' + ], (array) $record); + + $record->id = $DB->insert_record('badge', $record); + + return new badge($record->id); + } +} diff --git a/badges/tests/reportbuilder/datasource/badges_test.php b/badges/tests/reportbuilder/datasource/badges_test.php index 7a420a0c1b1..fc7d60ceb74 100644 --- a/badges/tests/reportbuilder/datasource/badges_test.php +++ b/badges/tests/reportbuilder/datasource/badges_test.php @@ -18,7 +18,7 @@ declare(strict_types=1); namespace core_badges\reportbuilder\datasource; -use core_badges\badge; +use core_badges_generator; use core_reportbuilder_generator; use core_reportbuilder_testcase; @@ -56,13 +56,16 @@ class badges_test extends core_reportbuilder_testcase { $user1 = $this->getDataGenerator()->create_user(['firstname' => 'Alan', 'lastname' => 'Apple']); $user2 = $this->getDataGenerator()->create_user(['firstname' => 'Barry', 'lastname' => 'Banana']); - $sitebadge = $this->create_badge(['name' => 'Badge 1']); + /** @var core_badges_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); + + $sitebadge = $generator->create_badge(['name' => 'Badge 1']); $sitebadge->issue($user1->id, true); $sitebadge->issue($user2->id, true); // Another badge, in a course, no issues. $course = $this->getDataGenerator()->create_course(); - $coursebadge = $this->create_badge(['name' => 'Badge 2', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]); + $coursebadge = $generator->create_badge(['name' => 'Badge 2', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); @@ -102,8 +105,11 @@ class badges_test extends core_reportbuilder_testcase { $course = $this->getDataGenerator()->create_course(['tags' => ['horse']]); $user = $this->getDataGenerator()->create_user(['interests' => ['pie']]); + /** @var core_badges_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); + // Create course badge, issue to user. - $badge = $this->create_badge(['name' => 'Course badge', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]); + $badge = $generator->create_badge(['name' => 'Course badge', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]); $badge->issue($user->id, true); /** @var core_reportbuilder_generator $generator */ @@ -128,45 +134,4 @@ class badges_test extends core_reportbuilder_testcase { 'pie', ], array_values($content[0])); } - - /** - * Helper method to create a badge - * - * @param array $params - * @return badge - */ - protected function create_badge(array $params = []): badge { - global $DB, $USER; - - $record = (object) array_merge([ - 'name' => 'Test badge', - 'description' => 'Testing badges', - 'timecreated' => time(), - 'timemodified' => time(), - 'usercreated' => $USER->id, - 'usermodified' => $USER->id, - 'issuername' => 'Test issuer', - 'issuerurl' => 'http://issuer-url.domain.co.nz', - 'issuercontact' => 'issuer@example.com', - 'expiredate' => null, - 'expireperiod' => null, - 'type' => BADGE_TYPE_SITE, - 'courseid' => null, - 'messagesubject' => 'Test message subject', - 'message' => 'Test message body', - 'attachment' => 1, - 'notification' => 0, - 'status' => BADGE_STATUS_ACTIVE, - 'version' => OPEN_BADGES_V2, - 'language' => 'en', - 'imageauthorname' => 'Image author', - 'imageauthoremail' => 'author@example.com', - 'imageauthorurl' => 'http://image.example.com/', - 'imagecaption' => 'Image caption' - ], $params); - - $record->id = $DB->insert_record('badge', $record); - - return new badge($record->id); - } } From 5582bc2ded3dcc77097cb749a4e9467eaace02c5 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Mon, 9 May 2022 11:00:56 +0100 Subject: [PATCH 27/49] MDL-74656 reportbuilder: ensure nulls preserved in column callbacks. When concatenating column fields in order to perform aggregation on them (e.g. group concatenation), we need to preserve all null values in the data passed to each column callback. Co-authored-by: Carlos Castillo --- .../classes/local/aggregation/base.php | 11 ++-- .../classes/local/aggregation/groupconcat.php | 11 +++- .../local/aggregation/groupconcat_test.php | 51 +++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/reportbuilder/classes/local/aggregation/base.php b/reportbuilder/classes/local/aggregation/base.php index 19d5686a0a2..bc2a5e20fdb 100644 --- a/reportbuilder/classes/local/aggregation/base.php +++ b/reportbuilder/classes/local/aggregation/base.php @@ -81,9 +81,14 @@ abstract class base { * * @param string[] $sqlfields * @param string $delimeter + * @param string $coalescechar * @return string */ - final protected static function get_column_fields_concat(array $sqlfields, string $delimeter = ','): string { + final protected static function get_column_fields_concat( + array $sqlfields, + string $delimeter = ',', + string $coalescechar = ' ' + ): string { global $DB; // We need to ensure all values are char. @@ -95,8 +100,8 @@ abstract class base { $sqlfield = $DB->sql_cast_to_char($sqlfield); } - // Coalesce all the SQL fields, to remove all nulls. - $concatfields[] = "COALESCE({$sqlfield}, ' ')"; + // Coalesce all the SQL fields. Ensure cross-DB compatibility, and that we always get string data back. + $concatfields[] = "COALESCE({$sqlfield}, '{$coalescechar}')"; $concatfields[] = "'{$delimeter}'"; } diff --git a/reportbuilder/classes/local/aggregation/groupconcat.php b/reportbuilder/classes/local/aggregation/groupconcat.php index e3572a9eba7..4cd07b47aba 100644 --- a/reportbuilder/classes/local/aggregation/groupconcat.php +++ b/reportbuilder/classes/local/aggregation/groupconcat.php @@ -34,6 +34,9 @@ class groupconcat extends base { /** @var string Character to use as a delimeter between column fields */ protected const COLUMN_FIELD_DELIMETER = '<|>'; + /** @var string Character to use a null coalesce value */ + protected const COLUMN_NULL_COALESCE = '<^>'; + /** @var string Character to use as a delimeter between field values */ protected const FIELD_VALUE_DELIMETER = '<,>'; @@ -79,7 +82,7 @@ class groupconcat extends base { return parent::get_column_field_sql($sqlfields); } - return self::get_column_fields_concat($sqlfields, self::COLUMN_FIELD_DELIMETER); + return self::get_column_fields_concat($sqlfields, self::COLUMN_FIELD_DELIMETER, self::COLUMN_NULL_COALESCE); } /** @@ -124,7 +127,11 @@ class groupconcat extends base { continue; } - $originalvalues = array_combine($valuenames, $valuedata); + // Re-construct original values, also ensuring any nulls contained within are restored. + $originalvalues = array_map(static function(string $value): ?string { + return $value === self::COLUMN_NULL_COALESCE ? null : $value; + }, array_combine($valuenames, $valuedata)); + $originalvalue = column::get_default_value($originalvalues, $columntype); // Once we've re-constructed each value, we can apply callbacks to it. diff --git a/reportbuilder/tests/local/aggregation/groupconcat_test.php b/reportbuilder/tests/local/aggregation/groupconcat_test.php index e162d09cf81..c339e626c16 100644 --- a/reportbuilder/tests/local/aggregation/groupconcat_test.php +++ b/reportbuilder/tests/local/aggregation/groupconcat_test.php @@ -18,6 +18,8 @@ declare(strict_types=1); namespace core_reportbuilder\local\aggregation; +use core_badges_generator; +use core_badges\reportbuilder\datasource\badges; use core_reportbuilder_testcase; use core_reportbuilder_generator; use core_user\reportbuilder\datasource\users; @@ -141,4 +143,53 @@ class groupconcat_test extends core_reportbuilder_testcase { ], ], $content); } + + /** + * Test aggregation when applied to column with callback that expects/handles null values + */ + public function test_datasource_aggregate_column_callback_with_null(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $userone = $this->getDataGenerator()->create_user(['description' => 'First user']); + $usertwo = $this->getDataGenerator()->create_user(['description' => 'Second user']); + + /** @var core_badges_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); + + // Create course badge, issue to both users. + $badgeone = $generator->create_badge(['name' => 'First badge']); + $badgeone->issue($userone->id, true); + $badgeone->issue($usertwo->id, true); + + // Create second badge, without issuing to anyone. + $badgetwo = $generator->create_badge(['name' => 'Second badge']); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'Badges', 'source' => badges::class, 'default' => 0]); + + // First column, sorted. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:name']) + ->set('sortenabled', true) + ->update(); + + // This is the column we'll aggregate. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:description']) + ->set('aggregation', groupconcat::get_class_name()) + ->update(); + + // Assert description column was aggregated, with callbacks accounting for null values. + $content = $this->get_custom_report_content($report->get('id')); + $this->assertEquals([ + [ + 'c0_name' => $badgeone->name, + 'c1_description' => "{$userone->description}, {$usertwo->description}", + ], + [ + 'c0_name' => $badgetwo->name, + 'c1_description' => '', + ], + ], $content); + } } From ca7890f2fc14e1408d1ab4a1edd641d9edca49c6 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Wed, 13 Jul 2022 23:44:16 +0200 Subject: [PATCH 28/49] MDL-75208 oracle: Amend a couple of queries to make Oracle happy The changes introduced here are completely safe, just we stop binding SITEID and, instead, embed it in the SQL. Why? Because Oracle 21 has started to return non-sense results when SITEID is bound. After lots of tests, attempts, debugging... we have been unable to find any logic to the need of this change and also, have been unable to reproduce the problem with a standalone script that pretty much runs the same queries that the ones changed here. I'm sure that there is something, somewhere, but have failed to find it, grrr. Please read MDL-75208 and linked issues to find more information about this problem, that is one of the biggest mysteries I've seen recently. Maybe at the end there is a tiny detail that explains it all, but it's really well hidden. --- course/lib.php | 7 ++----- lib/enrollib.php | 8 ++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/course/lib.php b/course/lib.php index 8fb28d6aa7d..16a6a4220dd 100644 --- a/course/lib.php +++ b/course/lib.php @@ -2470,9 +2470,7 @@ function update_course($data, $editoroptions = NULL) { function average_number_of_participants(bool $onlyactive = false, int $lastloginsince = null): float { global $DB; - $params = [ - 'siteid' => SITEID, - ]; + $params = []; $sql = "SELECT DISTINCT ue.userid, e.courseid FROM {user_enrolments} ue @@ -2483,8 +2481,7 @@ function average_number_of_participants(bool $onlyactive = false, int $lastlogin $sql .= "JOIN {user} u ON u.id = ue.userid "; } - $sql .= "WHERE e.courseid <> :siteid - AND c.visible = 1 "; + $sql .= "WHERE e.courseid <> " . SITEID . " AND c.visible = 1 "; if ($onlyactive) { $sql .= "AND ue.status = :active diff --git a/lib/enrollib.php b/lib/enrollib.php index 05564d2054f..caef6930967 100644 --- a/lib/enrollib.php +++ b/lib/enrollib.php @@ -688,8 +688,8 @@ function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $coursei $orderby = "ORDER BY $sort"; } - $wheres = array("c.id <> :siteid"); - $params = array('siteid'=>SITEID); + $wheres = ['c.id <> ' . SITEID]; + $params = []; if (isset($USER->loginascontext) and $USER->loginascontext->contextlevel == CONTEXT_COURSE) { // list _only_ this course - anything else is asking for trouble... @@ -1093,7 +1093,7 @@ function enrol_get_all_users_courses($userid, $onlyactive = false, $fields = nul $orderby = "ORDER BY $sort"; } - $params = array('siteid'=>SITEID); + $params = []; if ($onlyactive) { $subwhere = "WHERE ue.status = :active AND e.status = :enabled AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)"; @@ -1119,7 +1119,7 @@ function enrol_get_all_users_courses($userid, $onlyactive = false, $fields = nul $subwhere ) en ON (en.courseid = c.id) $ccjoin - WHERE c.id <> :siteid + WHERE c.id <> " . SITEID . " $orderby"; $params['userid'] = $userid; From de07d85f2389688ecb089bf7cd094c98e9e261c0 Mon Sep 17 00:00:00 2001 From: Christina Thee Roperto Date: Mon, 18 Jul 2022 22:46:01 +1000 Subject: [PATCH 29/49] MDL-72349 filelib: update strip_double_headers function --- lib/filelib.php | 4 +- lib/tests/filelib_test.php | 149 +++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/lib/filelib.php b/lib/filelib.php index 2449a64766a..ba22c9da3cc 100644 --- a/lib/filelib.php +++ b/lib/filelib.php @@ -4201,14 +4201,14 @@ class curl { $crlf = "\r\n"; return preg_replace( // HTTP version and status code (ignore value of code). - '~^HTTP/1\..*' . $crlf . + '~^HTTP/[1-9](\.[0-9])?.*' . $crlf . // Header name: character between 33 and 126 decimal, except colon. // Colon. Header value: any character except \r and \n. CRLF. '(?:[\x21-\x39\x3b-\x7e]+:[^' . $crlf . ']+' . $crlf . ')*' . // Headers are terminated by another CRLF (blank line). $crlf . // Second HTTP status code, this time must be 200. - '(HTTP/1.[01] 200 )~', '$1', $input); + '(HTTP/[1-9](\.[0-9])? 200)~', '$2', $input); } } diff --git a/lib/tests/filelib_test.php b/lib/tests/filelib_test.php index b66a2481273..faaa198a801 100644 --- a/lib/tests/filelib_test.php +++ b/lib/tests/filelib_test.php @@ -976,6 +976,155 @@ EOF; $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample)); // Test it does nothing to the 'plain' data. $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected)); + + $httpsexample = << +... +EOF; + $httpsexpected = << +... +EOF; + // For HTTP, replace the \n with \r\n. + $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); + $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); + + // Test stripping works OK. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample)); + // Test it does nothing to the 'plain' data. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected)); + + $httpsexample = << +... +EOF; + $httpsexpected = << +... +EOF; + // For HTTP, replace the \n with \r\n. + $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); + $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); + + // Test stripping works OK. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample)); + // Test it does nothing to the 'plain' data. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected)); + + $httpsexample = << +... +EOF; + $httpsexpected = << +... +EOF; + // For HTTP, replace the \n with \r\n. + $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); + $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); + + // Test stripping works OK. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample)); + // Test it does nothing to the 'plain' data. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected)); + + $httpsexample = << +... +EOF; + $httpsexpected = << +... +EOF; + // For HTTP, replace the \n with \r\n. + $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); + $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); + + // Test stripping works OK. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample)); + // Test it does nothing to the 'plain' data. + $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected)); + } /** From f9bb9797c207d6cb7ff5668698320dde1638bd83 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Thu, 20 Jan 2022 23:00:35 +0000 Subject: [PATCH 30/49] MDL-73481 qtype_truefalse: optional standard instructions for output. AMOS BEGIN CPY [privacy:preference:showstandardinstruction,qtype_multichoice],[privacy:preference:showstandardinstruction,qtype_truefalse] CPY [showstandardinstruction,qtype_multichoice],[showstandardinstruction,qtype_truefalse] AMOS END --- .../backup_qtype_truefalse_plugin.class.php | 2 +- .../truefalse/classes/privacy/provider.php | 47 ++++++++++------- question/type/truefalse/db/install.xml | 3 +- question/type/truefalse/db/upgrade.php | 52 +++++++++++++++++++ .../type/truefalse/edit_truefalse_form.php | 8 +++ .../type/truefalse/lang/en/deprecated.txt | 1 + .../truefalse/lang/en/qtype_truefalse.php | 5 ++ question/type/truefalse/questiontype.php | 8 +++ question/type/truefalse/renderer.php | 4 +- .../type/truefalse/tests/behat/add.feature | 8 +++ .../type/truefalse/tests/walkthrough_test.php | 34 ++++++++++++ question/type/truefalse/version.php | 2 +- 12 files changed, 152 insertions(+), 22 deletions(-) create mode 100644 question/type/truefalse/db/upgrade.php create mode 100644 question/type/truefalse/lang/en/deprecated.txt diff --git a/question/type/truefalse/backup/moodle2/backup_qtype_truefalse_plugin.class.php b/question/type/truefalse/backup/moodle2/backup_qtype_truefalse_plugin.class.php index e3482f1215f..721a5f49de1 100644 --- a/question/type/truefalse/backup/moodle2/backup_qtype_truefalse_plugin.class.php +++ b/question/type/truefalse/backup/moodle2/backup_qtype_truefalse_plugin.class.php @@ -53,7 +53,7 @@ class backup_qtype_truefalse_plugin extends backup_qtype_plugin { // Now create the qtype own structures. $truefalse = new backup_nested_element('truefalse', array('id'), array( - 'trueanswer', 'falseanswer')); + 'trueanswer', 'falseanswer', 'showstandardinstruction')); // Now the own qtype tree. $pluginwrapper->add_child($truefalse); diff --git a/question/type/truefalse/classes/privacy/provider.php b/question/type/truefalse/classes/privacy/provider.php index 22979789649..32839a770df 100644 --- a/question/type/truefalse/classes/privacy/provider.php +++ b/question/type/truefalse/classes/privacy/provider.php @@ -14,33 +14,44 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace qtype_truefalse\privacy; + +use core_privacy\local\metadata\collection; +use core_privacy\local\request\transform; +use core_privacy\local\request\writer; + /** - * Privacy Subsystem implementation for qtype_truefalse. + * Privacy Subsystem implementation for qtype_truefalse * * @package qtype_truefalse * @copyright 2018 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - -namespace qtype_truefalse\privacy; - -defined('MOODLE_INTERNAL') || die(); - -/** - * Privacy Subsystem for qtype_truefalse implementing null_provider. - * - * @copyright 2018 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class provider implements \core_privacy\local\metadata\null_provider { +class provider implements + \core_privacy\local\metadata\provider, + \core_privacy\local\request\user_preference_provider { /** - * Get the language string identifier with the component's language - * file to explain why this plugin stores no data. + * Returns metadata about this system * - * @return string + * @param collection $collection + * @return collection */ - public static function get_reason() : string { - return 'privacy:metadata'; + public static function get_metadata(collection $collection): collection { + $collection->add_user_preference('qtype_truefalse_showstandardinstruction', 'privacy:preference:showstandardinstruction'); + return $collection; + } + + /** + * Export plugin user preferences for the given user + * + * @param int $userid + */ + public static function export_user_preferences(int $userid): void { + $preference = get_user_preferences('qtype_truefalse_showstandardinstruction', null, $userid); + if ($preference !== null) { + writer::export_user_preference('qtype_truefalse', 'showstandardinstruction', transform::yesno($preference), + get_string('privacy:preference:showstandardinstruction', 'qtype_truefalse')); + } } } diff --git a/question/type/truefalse/db/install.xml b/question/type/truefalse/db/install.xml index dc7daf3f7b3..2d35debb970 100644 --- a/question/type/truefalse/db/install.xml +++ b/question/type/truefalse/db/install.xml @@ -1,5 +1,5 @@ - @@ -10,6 +10,7 @@ + diff --git a/question/type/truefalse/db/upgrade.php b/question/type/truefalse/db/upgrade.php new file mode 100644 index 00000000000..155b6b82be8 --- /dev/null +++ b/question/type/truefalse/db/upgrade.php @@ -0,0 +1,52 @@ +. + +/** + * True/false question type upgrade code + * + * @package qtype_truefalse + * @copyright 2022 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Method to perform upgrade steps between versions + * + * @param int $oldversion + * @return bool + */ +function xmldb_qtype_truefalse_upgrade(int $oldversion): bool { + global $DB; + + $dbman = $DB->get_manager(); + + if ($oldversion < 2022071900) { + + // Define field showstandardinstruction to be added to question_truefalse. + $table = new xmldb_table('question_truefalse'); + $field = new xmldb_field('showstandardinstruction', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1', 'falseanswer'); + + // Conditionally launch add field showstandardinstruction. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Truefalse savepoint reached. + upgrade_plugin_savepoint(true, 2022071900, 'qtype', 'truefalse'); + } + + return true; +} diff --git a/question/type/truefalse/edit_truefalse_form.php b/question/type/truefalse/edit_truefalse_form.php index fc8f3170e23..81003706c42 100644 --- a/question/type/truefalse/edit_truefalse_form.php +++ b/question/type/truefalse/edit_truefalse_form.php @@ -48,6 +48,10 @@ class qtype_truefalse_edit_form extends question_edit_form { 0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse'))); + $mform->addElement('selectyesno', 'showstandardinstruction', get_string('showstandardinstruction', 'qtype_truefalse')); + $mform->addHelpButton('showstandardinstruction', 'showstandardinstruction', 'qtype_truefalse'); + $mform->setDefault('showstandardinstruction', $this->get_default_value('showstandardinstruction', 0)); + $mform->addElement('editor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'), array('rows' => 10), $this->editoroptions); $mform->setType('feedbacktrue', PARAM_RAW); @@ -111,6 +115,10 @@ class qtype_truefalse_edit_form extends question_edit_form { $question->feedbackfalse['itemid'] = $draftid; } + if (!empty($question->options)) { + $question->showstandardinstruction = !empty($question->options->showstandardinstruction); + } + return $question; } diff --git a/question/type/truefalse/lang/en/deprecated.txt b/question/type/truefalse/lang/en/deprecated.txt new file mode 100644 index 00000000000..93dc2272af8 --- /dev/null +++ b/question/type/truefalse/lang/en/deprecated.txt @@ -0,0 +1 @@ +privacy:metadata,qtype_truefalse diff --git a/question/type/truefalse/lang/en/qtype_truefalse.php b/question/type/truefalse/lang/en/qtype_truefalse.php index d14a5c72e5a..ae80a296781 100644 --- a/question/type/truefalse/lang/en/qtype_truefalse.php +++ b/question/type/truefalse/lang/en/qtype_truefalse.php @@ -38,4 +38,9 @@ $string['pluginname_link'] = 'question/type/truefalse'; $string['pluginnameadding'] = 'Adding a True/False question'; $string['pluginnameediting'] = 'Editing a True/False question'; $string['pluginnamesummary'] = 'A simple form of multiple choice question with just the two choices \'True\' and \'False\'.'; +$string['privacy:preference:showstandardinstruction'] = 'Whether showing standard instruction.'; +$string['showstandardinstruction'] = 'Show standard instructions'; +$string['showstandardinstruction_help'] = 'Whether to show the instructions "Select one:" before true/false answers.'; + +// Deprecated since Moodle 4.1. $string['privacy:metadata'] = 'The True/False question type plugin does not store any personal data.'; diff --git a/question/type/truefalse/questiontype.php b/question/type/truefalse/questiontype.php index 1969ae3ecb4..3f3b5693115 100644 --- a/question/type/truefalse/questiontype.php +++ b/question/type/truefalse/questiontype.php @@ -94,12 +94,14 @@ class qtype_truefalse extends question_type { // But we'll do it anyway, just for robustness. $options->trueanswer = $trueid; $options->falseanswer = $falseid; + $options->showstandardinstruction = !empty($question->showstandardinstruction); $DB->update_record('question_truefalse', $options); } else { $options = new stdClass(); $options->question = $question->id; $options->trueanswer = $trueid; $options->falseanswer = $falseid; + $options->showstandardinstruction = !empty($question->showstandardinstruction); $DB->insert_record('question_truefalse', $options); } @@ -108,6 +110,11 @@ class qtype_truefalse extends question_type { return true; } + public function save_defaults_for_new_questions(stdClass $fromform): void { + parent::save_defaults_for_new_questions($fromform); + $this->set_default_value('showstandardinstruction', $fromform->showstandardinstruction); + } + /** * Loads the question type specific options for the question. */ @@ -148,6 +155,7 @@ class qtype_truefalse extends question_type { $answers[$questiondata->options->falseanswer]->feedbackformat; $question->trueanswerid = $questiondata->options->trueanswer; $question->falseanswerid = $questiondata->options->falseanswer; + $question->showstandardinstruction = $questiondata->options->showstandardinstruction; } public function delete_question($questionid, $contextid) { diff --git a/question/type/truefalse/renderer.php b/question/type/truefalse/renderer.php index 6abb1e6d257..9b3df0133b0 100644 --- a/question/type/truefalse/renderer.php +++ b/question/type/truefalse/renderer.php @@ -100,8 +100,10 @@ class qtype_truefalse_renderer extends qtype_renderer { array('class' => 'qtext')); $result .= html_writer::start_tag('div', array('class' => 'ablock')); - $result .= html_writer::tag('div', get_string('selectone', 'qtype_truefalse'), + if (!empty($question->showstandardinstruction)) { + $result .= html_writer::tag('div', get_string('selectone', 'qtype_truefalse'), array('class' => 'prompt')); + } $result .= html_writer::start_tag('div', array('class' => 'answer')); $result .= html_writer::tag('div', $radiotrue . ' ' . $truefeedbackimg, diff --git a/question/type/truefalse/tests/behat/add.feature b/question/type/truefalse/tests/behat/add.feature index fc2f133e340..05e467f3155 100644 --- a/question/type/truefalse/tests/behat/add.feature +++ b/question/type/truefalse/tests/behat/add.feature @@ -27,6 +27,7 @@ Feature: Test creating a True/False question | Feedback for the response 'False'. | Read more about England. | Then I should see "true-false-001" + @javascript Scenario: Create a True/False question with Correct answer as True When I am on the "Course 1" "core_question > course question bank" page logged in as teacher And I add a "True/False" question filling the form with: @@ -35,6 +36,13 @@ Feature: Test creating a True/False question | Default mark | 1 | | General feedback | London is the capital city of England. | | Correct answer | True | + | Show standard instructions | Yes | | Feedback for the response 'True'. | Well done! | | Feedback for the response 'False'. | Read more about England. | Then I should see "true-false-002" + # Checking that the next new question form displays user preferences settings. + And I press "Create a new question ..." + And I set the field "True/False" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + And the following fields match these values: + | Show standard instructions | Yes | diff --git a/question/type/truefalse/tests/walkthrough_test.php b/question/type/truefalse/tests/walkthrough_test.php index 4b46aa05018..ae7ec1d2ef1 100644 --- a/question/type/truefalse/tests/walkthrough_test.php +++ b/question/type/truefalse/tests/walkthrough_test.php @@ -66,4 +66,38 @@ class walkthrough_test extends \qbehaviour_walkthrough_test_base { new \question_no_pattern_expectation('/' . preg_quote($tf->falsefeedback, '/') . '/')); } + + /** + * @covers \qtype_truefalse_renderer::formulation_and_controls + */ + public function test_deferredfeedback_feedback_multichoice_single_showstandardinstruction_yes() { + + // Create a true-false question with correct answer false. + $tf = \test_question_maker::make_question('truefalse', 'false'); + $tf->showstandardinstruction = true; + + $this->start_attempt_at_question($tf, 'deferredfeedback', 1); + $this->render(); + + // Check for 'Show standard instruction'. + $standardinstruction = get_string('selectone', 'qtype_truefalse'); + $this->assertStringContainsString($standardinstruction, $this->currentoutput); + } + + /** + * @covers \qtype_truefalse_renderer::formulation_and_controls + */ + public function test_deferredfeedback_feedback_multichoice_single_showstandardinstruction_no() { + + // Create a true-false question with correct answer false. + $tf = \test_question_maker::make_question('truefalse', 'false'); + $tf->showstandardinstruction = false; + + $this->start_attempt_at_question($tf, 'deferredfeedback', 1); + $this->render(); + + // Check for 'Show standard instruction'. + $standardinstruction = get_string('selectone', 'qtype_truefalse'); + $this->assertStringNotContainsString($standardinstruction, $this->currentoutput); + } } diff --git a/question/type/truefalse/version.php b/question/type/truefalse/version.php index 49c59da28b6..c68323cbacf 100644 --- a/question/type/truefalse/version.php +++ b/question/type/truefalse/version.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'qtype_truefalse'; -$plugin->version = 2022041900; +$plugin->version = 2022071900; $plugin->requires = 2022041200; From ea22e495cf237f6ffb139f755d8c3f131c424449 Mon Sep 17 00:00:00 2001 From: Laurent David Date: Thu, 7 Jul 2022 17:42:13 -0400 Subject: [PATCH 31/49] MDL-75181 mod_bigbluebuttonbn: Fix activity completion exception * Fix exception raised in core completion module due to the absence of completionview in get_sort_order --- mod/bigbluebuttonbn/classes/completion/custom_completion.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mod/bigbluebuttonbn/classes/completion/custom_completion.php b/mod/bigbluebuttonbn/classes/completion/custom_completion.php index 6d84a1f6687..3bd6fca5358 100644 --- a/mod/bigbluebuttonbn/classes/completion/custom_completion.php +++ b/mod/bigbluebuttonbn/classes/completion/custom_completion.php @@ -138,12 +138,13 @@ class custom_completion extends activity_custom_completion { */ public function get_sort_order(): array { return [ + 'completionview', 'completionengagementchats', 'completionengagementtalks', 'completionengagementraisehand', 'completionengagementpollvotes', 'completionengagementemojis', - 'completionattendance', + 'completionattendance' ]; } From 562041adda9db01f1fec96eb15cc5e6c91e5156b Mon Sep 17 00:00:00 2001 From: Carlos Castillo Date: Mon, 24 Jan 2022 15:09:10 -0500 Subject: [PATCH 32/49] MDL-71708 libraries: Upgrade chartjs to latest version Changes in chart config file and the process to import library. Co-authored-by: Andrew Lyons --- lib/amd/build/chart_output_chartjs.min.js | 2 +- lib/amd/build/chart_output_chartjs.min.js.map | 2 +- lib/amd/build/chartjs-lazy.min.js | 2 +- lib/amd/build/chartjs-lazy.min.js.map | 2 +- lib/amd/src/chart_output_chartjs.js | 68 +- lib/amd/src/chartjs-lazy.js | 33759 ++++++---------- lib/thirdpartylibs.xml | 2 +- theme/boost/scss/moodle/core.scss | 8 + theme/boost/style/moodle.css | 7 + theme/classic/style/moodle.css | 7 + 10 files changed, 13247 insertions(+), 20612 deletions(-) diff --git a/lib/amd/build/chart_output_chartjs.min.js b/lib/amd/build/chart_output_chartjs.min.js index b716b77873a..37237e0b650 100644 --- a/lib/amd/build/chart_output_chartjs.min.js +++ b/lib/amd/build/chart_output_chartjs.min.js @@ -5,6 +5,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @module core/chart_output_chartjs */ -define("core/chart_output_chartjs",["jquery","core/chartjs","core/chart_axis","core/chart_bar","core/chart_output_base","core/chart_line","core/chart_pie","core/chart_series"],(function($,Chartjs,Axis,Bar,Base,Line,Pie,Series){var makeAxisId=function(xy,index){return"axis-"+xy+"-"+index};function Output(){Base.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=$(""),this._node.append(this._canvas)),this._build()}return Output.prototype=Object.create(Base.prototype),Output.prototype._config=null,Output.prototype._chartjs=null,Output.prototype._canvas=null,Output.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new Chartjs(this._canvas[0],this._config)},Output.prototype._cleanData=function(data){return data instanceof Array?data.map((function(value){return $("").html(value).text()})):$("").html(data).text()},Output.prototype._getChartType=function(){var type=this._chart.getType();return this._chart.getType()===Bar.prototype.TYPE&&!0===this._chart.getHorizontal()?type="horizontalBar":this._chart.getType()===Pie.prototype.TYPE&&!0===this._chart.getDoughnut()&&(type="doughnut"),type},Output.prototype._makeAxisConfig=function(axis,xy,index){var scaleData={id:makeAxisId(xy,index)};return axis.getPosition()!==Axis.prototype.POS_DEFAULT&&(scaleData.position=axis.getPosition()),null!==axis.getLabel()&&(scaleData.scaleLabel={display:!0,labelString:this._cleanData(axis.getLabel())}),null!==axis.getStepSize()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.stepSize=axis.getStepSize()),null!==axis.getMax()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.max=axis.getMax()),null!==axis.getMin()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.min=axis.getMin()),scaleData},Output.prototype._makeConfig=function(){var config={type:this._getChartType(),data:{labels:this._cleanData(this._chart.getLabels()),datasets:this._makeDatasetsConfig()},options:{title:{display:null!==this._chart.getTitle(),text:this._cleanData(this._chart.getTitle())}}},legendOptions=this._chart.getLegendOptions();return legendOptions&&(config.options.legend=legendOptions),this._chart.getXAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.xAxes=config.options.scales.xAxes||[],config.options.scales.xAxes[i]=this._makeAxisConfig(axis,"x",i),null!==axisLabels&&(config.options.scales.xAxes[i].ticks.callback=function(value,index){return axisLabels[index]||""}),config.options.scales.xAxes[i].stacked=this._isStacked()}.bind(this)),this._chart.getYAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.yAxes=config.options.scales.yAxes||[],config.options.scales.yAxes[i]=this._makeAxisConfig(axis,"y",i),null!==axisLabels&&(config.options.scales.yAxes[i].ticks.callback=function(value){return axisLabels[parseInt(value,10)]||""}),config.options.scales.yAxes[i].stacked=this._isStacked()}.bind(this)),config.options.tooltips={callbacks:{label:this._makeTooltip.bind(this)}},config},Output.prototype._makeDatasetsConfig=function(){return this._chart.getSeries().map(function(series){var colors=series.hasColoredValues()?series.getColors():series.getColor(),dataset={label:this._cleanData(series.getLabel()),data:series.getValues(),type:series.getType(),fill:series.getFill(),backgroundColor:colors,borderColor:this._chart.getType()==Pie.prototype.TYPE?"#fff":colors,lineTension:this._isSmooth(series)?.3:0};return null!==series.getXAxis()&&(dataset.xAxisID=makeAxisId("x",series.getXAxis())),null!==series.getYAxis()&&(dataset.yAxisID=makeAxisId("y",series.getYAxis())),dataset}.bind(this))},Output.prototype._makeTooltip=function(tooltipItem,data){var series=this._chart.getSeries()[tooltipItem.datasetIndex],serieLabel=series.getLabel(),serieLabels=series.getLabels(),tooltipData=data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index],tooltip=[];if(""==tooltipItem.xLabel&&""==tooltipItem.yLabel){var chartLabels=this._cleanData(this._chart.getLabels());tooltip.push(chartLabels[tooltipItem.index])}return null!==serieLabels?tooltip.push(this._cleanData(serieLabels[tooltipItem.index])):tooltip.push(this._cleanData(serieLabel)+": "+tooltipData),tooltip},Output.prototype._isSmooth=function(series){var smooth=!1;return this._chart.getType()===Line.prototype.TYPE?null===(smooth=series.getSmooth())&&(smooth=this._chart.getSmooth()):series.getType()===Series.prototype.TYPE_LINE&&(smooth=series.getSmooth()),smooth},Output.prototype._isStacked=function(){var stacked=!1;return this._chart.getType()===Bar.prototype.TYPE&&(stacked=this._chart.getStacked()),stacked},Output.prototype.update=function(){$.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},Output})); +define("core/chart_output_chartjs",["jquery","core/chartjs","core/chart_axis","core/chart_bar","core/chart_output_base","core/chart_line","core/chart_pie","core/chart_series"],(function($,Chartjs,Axis,Bar,Base,Line,Pie,Series){var makeAxisId=function(xy,index){return"axis-"+xy+"-"+index};function Output(){Base.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=$(""),this._node.append(this._canvas)),this._build()}return Output.prototype=Object.create(Base.prototype),Output.prototype._config=null,Output.prototype._chartjs=null,Output.prototype._canvas=null,Output.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new Chartjs(this._canvas[0],this._config)},Output.prototype._cleanData=function(data){return data instanceof Array?data.map((function(value){return $("").html(value).text()})):$("").html(data).text()},Output.prototype._getChartType=function(){var type=this._chart.getType();return this._chart.getType()===Bar.prototype.TYPE&&!0===this._chart.getHorizontal()?type="horizontalBar":this._chart.getType()===Pie.prototype.TYPE&&!0===this._chart.getDoughnut()&&(type="doughnut"),type},Output.prototype._makeAxisConfig=function(axis,xy,index){var scaleData={id:makeAxisId(xy,index)};return axis.getPosition()!==Axis.prototype.POS_DEFAULT&&(scaleData.position=axis.getPosition()),null!==axis.getLabel()&&(scaleData.title={display:!0,text:this._cleanData(axis.getLabel())}),null!==axis.getStepSize()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.stepSize=axis.getStepSize()),null!==axis.getMax()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.max=axis.getMax()),null!==axis.getMin()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.min=axis.getMin()),scaleData},Output.prototype._makeConfig=function(){var charType=this._getChartType(),config={type:charType,data:{labels:this._cleanData(this._chart.getLabels()),datasets:this._makeDatasetsConfig()},options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:null!==this._chart.getTitle(),text:this._cleanData(this._chart.getTitle())}}}};"horizontalBar"===charType&&(config.type="bar",config.options.indexAxis="y");var legendOptions=this._chart.getLegendOptions();return legendOptions&&(config.options.plugins.legend=legendOptions),this._chart.getXAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.x=config.options.scales.x||{},config.options.scales.x[i]=this._makeAxisConfig(axis,"x",i),null!==axisLabels&&(config.options.scales.x[i].ticks.callback=function(value,index){return axisLabels[index]||""}),config.options.scales.x.stacked=this._isStacked()}.bind(this)),this._chart.getYAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.y=config.options.scales.yAxes||{},config.options.scales.y[i]=this._makeAxisConfig(axis,"y",i),null!==axisLabels&&(config.options.scales.y[i].ticks.callback=function(value){return axisLabels[parseInt(value,10)]||""}),config.options.scales.y.stacked=this._isStacked()}.bind(this)),config.options.plugins.tooltip={callbacks:{label:this._makeTooltip.bind(this)}},config},Output.prototype._makeDatasetsConfig=function(){return this._chart.getSeries().map(function(series){var colors=series.hasColoredValues()?series.getColors():series.getColor(),dataset={label:this._cleanData(series.getLabel()),data:series.getValues(),type:series.getType(),fill:series.getFill(),backgroundColor:colors,borderColor:this._chart.getType()==Pie.prototype.TYPE?"#fff":colors,tension:this._isSmooth(series)?.3:0};return null!==series.getXAxis()&&(dataset.xAxisID=makeAxisId("x",series.getXAxis())),null!==series.getYAxis()&&(dataset.yAxisID=makeAxisId("y",series.getYAxis())),dataset}.bind(this))},Output.prototype._makeTooltip=function(tooltipItem){var serieLabel=this._chart.getSeries()[tooltipItem.datasetIndex].getLabel(),tooltipData=tooltipItem.dataset.data[tooltipItem.dataIndex],tooltip=[];if(this._chart.getType()===Pie.prototype.TYPE){var chartLabels=this._cleanData(this._chart.getLabels());tooltip.push(chartLabels[tooltipItem.dataIndex]+" - "+this._cleanData(serieLabel)+": "+tooltipData)}else tooltip.push(this._cleanData(serieLabel)+": "+tooltipData);return tooltip},Output.prototype._isSmooth=function(series){var smooth=!1;return this._chart.getType()===Line.prototype.TYPE?null===(smooth=series.getSmooth())&&(smooth=this._chart.getSmooth()):series.getType()===Series.prototype.TYPE_LINE&&(smooth=series.getSmooth()),smooth},Output.prototype._isStacked=function(){var stacked=!1;return this._chart.getType()===Bar.prototype.TYPE&&(stacked=this._chart.getStacked()),stacked},Output.prototype.update=function(){$.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},Output})); //# sourceMappingURL=chart_output_chartjs.min.js.map \ No newline at end of file diff --git a/lib/amd/build/chart_output_chartjs.min.js.map b/lib/amd/build/chart_output_chartjs.min.js.map index 83404ec3294..556377c15f5 100644 --- a/lib/amd/build/chart_output_chartjs.min.js.map +++ b/lib/amd/build/chart_output_chartjs.min.js.map @@ -1 +1 @@ -{"version":3,"file":"chart_output_chartjs.min.js","sources":["../src/chart_output_chartjs.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Chart output for chart.js.\n *\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @module core/chart_output_chartjs\n */\ndefine([\n 'jquery',\n 'core/chartjs',\n 'core/chart_axis',\n 'core/chart_bar',\n 'core/chart_output_base',\n 'core/chart_line',\n 'core/chart_pie',\n 'core/chart_series'\n], function($, Chartjs, Axis, Bar, Base, Line, Pie, Series) {\n\n /**\n * Makes an axis ID.\n *\n * @param {String} xy Accepts 'x' and 'y'.\n * @param {Number} index The axis index.\n * @return {String}\n */\n var makeAxisId = function(xy, index) {\n return 'axis-' + xy + '-' + index;\n };\n\n /**\n * Chart output for Chart.js.\n *\n * @class\n * @extends {module:core/chart_output_base}\n */\n function Output() {\n Base.prototype.constructor.apply(this, arguments);\n\n // Make sure that we've got a canvas tag.\n this._canvas = this._node;\n if (this._canvas.prop('tagName') != 'CANVAS') {\n this._canvas = $('');\n this._node.append(this._canvas);\n }\n\n this._build();\n }\n Output.prototype = Object.create(Base.prototype);\n\n /**\n * Reference to the chart config object.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._config = null;\n\n /**\n * Reference to the instance of chart.js.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._chartjs = null;\n\n /**\n * Reference to the canvas node.\n *\n * @type {Jquery}\n * @protected\n */\n Output.prototype._canvas = null;\n\n /**\n * Builds the config and the chart.\n *\n * @protected\n */\n Output.prototype._build = function() {\n this._config = this._makeConfig();\n this._chartjs = new Chartjs(this._canvas[0], this._config);\n };\n\n /**\n * Clean data.\n *\n * @param {(String|String[])} data A single string or an array of strings.\n * @returns {(String|String[])}\n * @protected\n */\n Output.prototype._cleanData = function(data) {\n if (data instanceof Array) {\n return data.map(function(value) {\n return $('').html(value).text();\n });\n } else {\n return $('').html(data).text();\n }\n };\n\n /**\n * Get the chart type and handles the Chart.js specific chart types.\n *\n * By default returns the current chart TYPE value. Also does the handling of specific chart types, for example\n * check if the bar chart should be horizontal and the pie chart should be displayed as a doughnut.\n *\n * @method getChartType\n * @returns {String} the chart type.\n * @protected\n */\n Output.prototype._getChartType = function() {\n var type = this._chart.getType();\n\n // Bars can be displayed vertically and horizontally, defining horizontalBar type.\n if (this._chart.getType() === Bar.prototype.TYPE && this._chart.getHorizontal() === true) {\n type = 'horizontalBar';\n } else if (this._chart.getType() === Pie.prototype.TYPE && this._chart.getDoughnut() === true) {\n // Pie chart can be displayed as doughnut.\n type = 'doughnut';\n }\n\n return type;\n };\n\n /**\n * Make the axis config.\n *\n * @protected\n * @param {module:core/chart_axis} axis The axis.\n * @param {String} xy Accepts 'x' or 'y'.\n * @param {Number} index The axis index.\n * @return {Object} The axis config.\n */\n Output.prototype._makeAxisConfig = function(axis, xy, index) {\n var scaleData = {\n id: makeAxisId(xy, index)\n };\n\n if (axis.getPosition() !== Axis.prototype.POS_DEFAULT) {\n scaleData.position = axis.getPosition();\n }\n\n if (axis.getLabel() !== null) {\n scaleData.scaleLabel = {\n display: true,\n labelString: this._cleanData(axis.getLabel())\n };\n }\n\n if (axis.getStepSize() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.stepSize = axis.getStepSize();\n }\n\n if (axis.getMax() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.max = axis.getMax();\n }\n\n if (axis.getMin() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.min = axis.getMin();\n }\n\n return scaleData;\n };\n\n /**\n * Make the config config.\n *\n * @protected\n * @return {Object} The axis config.\n */\n Output.prototype._makeConfig = function() {\n var config = {\n type: this._getChartType(),\n data: {\n labels: this._cleanData(this._chart.getLabels()),\n datasets: this._makeDatasetsConfig()\n },\n options: {\n title: {\n display: this._chart.getTitle() !== null,\n text: this._cleanData(this._chart.getTitle())\n }\n }\n };\n var legendOptions = this._chart.getLegendOptions();\n if (legendOptions) {\n config.options.legend = legendOptions;\n }\n\n\n this._chart.getXAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.xAxes = config.options.scales.xAxes || [];\n config.options.scales.xAxes[i] = this._makeAxisConfig(axis, 'x', i);\n\n if (axisLabels !== null) {\n config.options.scales.xAxes[i].ticks.callback = function(value, index) {\n return axisLabels[index] || '';\n };\n }\n config.options.scales.xAxes[i].stacked = this._isStacked();\n }.bind(this));\n\n this._chart.getYAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.yAxes = config.options.scales.yAxes || [];\n config.options.scales.yAxes[i] = this._makeAxisConfig(axis, 'y', i);\n\n if (axisLabels !== null) {\n config.options.scales.yAxes[i].ticks.callback = function(value) {\n return axisLabels[parseInt(value, 10)] || '';\n };\n }\n config.options.scales.yAxes[i].stacked = this._isStacked();\n }.bind(this));\n\n config.options.tooltips = {\n callbacks: {\n label: this._makeTooltip.bind(this)\n }\n };\n\n return config;\n };\n\n /**\n * Get the datasets configurations.\n *\n * @protected\n * @return {Object[]}\n */\n Output.prototype._makeDatasetsConfig = function() {\n var sets = this._chart.getSeries().map(function(series) {\n var colors = series.hasColoredValues() ? series.getColors() : series.getColor();\n var dataset = {\n label: this._cleanData(series.getLabel()),\n data: series.getValues(),\n type: series.getType(),\n fill: series.getFill(),\n backgroundColor: colors,\n // Pie charts look better without borders.\n borderColor: this._chart.getType() == Pie.prototype.TYPE ? '#fff' : colors,\n lineTension: this._isSmooth(series) ? 0.3 : 0\n };\n\n if (series.getXAxis() !== null) {\n dataset.xAxisID = makeAxisId('x', series.getXAxis());\n }\n if (series.getYAxis() !== null) {\n dataset.yAxisID = makeAxisId('y', series.getYAxis());\n }\n\n return dataset;\n }.bind(this));\n return sets;\n };\n\n /**\n * Get the chart data, add labels and rebuild the tooltip.\n *\n * @param {Object[]} tooltipItem The tooltip item data.\n * @param {Object[]} data The chart data.\n * @returns {String}\n * @protected\n */\n Output.prototype._makeTooltip = function(tooltipItem, data) {\n\n // Get series and chart data to rebuild the tooltip and add labels.\n var series = this._chart.getSeries()[tooltipItem.datasetIndex];\n var serieLabel = series.getLabel();\n var serieLabels = series.getLabels();\n var chartData = data.datasets[tooltipItem.datasetIndex].data;\n var tooltipData = chartData[tooltipItem.index];\n\n // Build default tooltip.\n var tooltip = [];\n\n // Pie and doughnut charts does not have axis.\n if (tooltipItem.xLabel == '' && tooltipItem.yLabel == '') {\n var chartLabels = this._cleanData(this._chart.getLabels());\n tooltip.push(chartLabels[tooltipItem.index]);\n }\n\n // Add series labels to the tooltip if any.\n if (serieLabels !== null) {\n tooltip.push(this._cleanData(serieLabels[tooltipItem.index]));\n } else {\n tooltip.push(this._cleanData(serieLabel) + ': ' + tooltipData);\n }\n\n return tooltip;\n };\n\n /**\n * Verify if the chart line is smooth or not.\n *\n * @protected\n * @param {module:core/chart_series} series The series.\n * @returns {Bool}\n */\n Output.prototype._isSmooth = function(series) {\n var smooth = false;\n if (this._chart.getType() === Line.prototype.TYPE) {\n smooth = series.getSmooth();\n if (smooth === null) {\n smooth = this._chart.getSmooth();\n }\n } else if (series.getType() === Series.prototype.TYPE_LINE) {\n smooth = series.getSmooth();\n }\n\n return smooth;\n };\n\n /**\n * Verify if the bar chart is stacked or not.\n *\n * @protected\n * @returns {Bool}\n */\n Output.prototype._isStacked = function() {\n var stacked = false;\n\n // Stacking is (currently) only supported for bar charts.\n if (this._chart.getType() === Bar.prototype.TYPE) {\n stacked = this._chart.getStacked();\n }\n\n return stacked;\n };\n\n /** @override */\n Output.prototype.update = function() {\n $.extend(true, this._config, this._makeConfig());\n this._chartjs.update();\n };\n\n return Output;\n\n});\n"],"names":["define","$","Chartjs","Axis","Bar","Base","Line","Pie","Series","makeAxisId","xy","index","Output","prototype","constructor","apply","this","arguments","_canvas","_node","prop","append","_build","Object","create","_config","_chartjs","_makeConfig","_cleanData","data","Array","map","value","html","text","_getChartType","type","_chart","getType","TYPE","getHorizontal","getDoughnut","_makeAxisConfig","axis","scaleData","id","getPosition","POS_DEFAULT","position","getLabel","scaleLabel","display","labelString","getStepSize","ticks","stepSize","getMax","max","getMin","min","config","labels","getLabels","datasets","_makeDatasetsConfig","options","title","getTitle","legendOptions","getLegendOptions","legend","getXAxes","forEach","i","axisLabels","scales","xAxes","callback","stacked","_isStacked","bind","getYAxes","yAxes","parseInt","tooltips","callbacks","label","_makeTooltip","getSeries","series","colors","hasColoredValues","getColors","getColor","dataset","getValues","fill","getFill","backgroundColor","borderColor","lineTension","_isSmooth","getXAxis","xAxisID","getYAxis","yAxisID","tooltipItem","datasetIndex","serieLabel","serieLabels","tooltipData","tooltip","xLabel","yLabel","chartLabels","push","smooth","getSmooth","TYPE_LINE","getStacked","update","extend"],"mappings":";;;;;;;AAsBAA,mCAAO,CACH,SACA,eACA,kBACA,iBACA,yBACA,kBACA,iBACA,sBACD,SAASC,EAAGC,QAASC,KAAMC,IAAKC,KAAMC,KAAMC,IAAKC,YAS5CC,WAAa,SAASC,GAAIC,aACnB,QAAUD,GAAK,IAAMC,gBASvBC,SACLP,KAAKQ,UAAUC,YAAYC,MAAMC,KAAMC,gBAGlCC,QAAUF,KAAKG,MACgB,UAAhCH,KAAKE,QAAQE,KAAK,kBACbF,QAAUjB,EAAE,iBACZkB,MAAME,OAAOL,KAAKE,eAGtBI,gBAETV,OAAOC,UAAYU,OAAOC,OAAOnB,KAAKQ,WAQtCD,OAAOC,UAAUY,QAAU,KAQ3Bb,OAAOC,UAAUa,SAAW,KAQ5Bd,OAAOC,UAAUK,QAAU,KAO3BN,OAAOC,UAAUS,OAAS,gBACjBG,QAAUT,KAAKW,mBACfD,SAAW,IAAIxB,QAAQc,KAAKE,QAAQ,GAAIF,KAAKS,UAUtDb,OAAOC,UAAUe,WAAa,SAASC,aAC/BA,gBAAgBC,MACTD,KAAKE,KAAI,SAASC,cACd/B,EAAE,UAAUgC,KAAKD,OAAOE,UAG5BjC,EAAE,UAAUgC,KAAKJ,MAAMK,QActCtB,OAAOC,UAAUsB,cAAgB,eACzBC,KAAOpB,KAAKqB,OAAOC,iBAGnBtB,KAAKqB,OAAOC,YAAclC,IAAIS,UAAU0B,OAAwC,IAAhCvB,KAAKqB,OAAOG,gBAC5DJ,KAAO,gBACApB,KAAKqB,OAAOC,YAAc/B,IAAIM,UAAU0B,OAAsC,IAA9BvB,KAAKqB,OAAOI,gBAEnEL,KAAO,YAGJA,MAYXxB,OAAOC,UAAU6B,gBAAkB,SAASC,KAAMjC,GAAIC,WAC9CiC,UAAY,CACZC,GAAIpC,WAAWC,GAAIC,eAGnBgC,KAAKG,gBAAkB3C,KAAKU,UAAUkC,cACtCH,UAAUI,SAAWL,KAAKG,eAGN,OAApBH,KAAKM,aACLL,UAAUM,WAAa,CACnBC,SAAS,EACTC,YAAapC,KAAKY,WAAWe,KAAKM,cAIf,OAAvBN,KAAKU,gBACLT,UAAUU,MAAQV,UAAUU,OAAS,GACrCV,UAAUU,MAAMC,SAAWZ,KAAKU,eAGd,OAAlBV,KAAKa,WACLZ,UAAUU,MAAQV,UAAUU,OAAS,GACrCV,UAAUU,MAAMG,IAAMd,KAAKa,UAGT,OAAlBb,KAAKe,WACLd,UAAUU,MAAQV,UAAUU,OAAS,GACrCV,UAAUU,MAAMK,IAAMhB,KAAKe,UAGxBd,WASXhC,OAAOC,UAAUc,YAAc,eACvBiC,OAAS,CACTxB,KAAMpB,KAAKmB,gBACXN,KAAM,CACFgC,OAAQ7C,KAAKY,WAAWZ,KAAKqB,OAAOyB,aACpCC,SAAU/C,KAAKgD,uBAEnBC,QAAS,CACLC,MAAO,CACHf,QAAoC,OAA3BnC,KAAKqB,OAAO8B,WACrBjC,KAAMlB,KAAKY,WAAWZ,KAAKqB,OAAO8B,eAI1CC,cAAgBpD,KAAKqB,OAAOgC,0BAC5BD,gBACAR,OAAOK,QAAQK,OAASF,oBAIvB/B,OAAOkC,WAAWC,QAAQ,SAAS7B,KAAM8B,OACtCC,WAAa/B,KAAKmB,YAEtBF,OAAOK,QAAQU,OAASf,OAAOK,QAAQU,QAAU,GACjDf,OAAOK,QAAQU,OAAOC,MAAQhB,OAAOK,QAAQU,OAAOC,OAAS,GAC7DhB,OAAOK,QAAQU,OAAOC,MAAMH,GAAKzD,KAAK0B,gBAAgBC,KAAM,IAAK8B,GAE9C,OAAfC,aACAd,OAAOK,QAAQU,OAAOC,MAAMH,GAAGnB,MAAMuB,SAAW,SAAS7C,MAAOrB,cACrD+D,WAAW/D,QAAU,KAGpCiD,OAAOK,QAAQU,OAAOC,MAAMH,GAAGK,QAAU9D,KAAK+D,cAChDC,KAAKhE,YAEFqB,OAAO4C,WAAWT,QAAQ,SAAS7B,KAAM8B,OACtCC,WAAa/B,KAAKmB,YAEtBF,OAAOK,QAAQU,OAASf,OAAOK,QAAQU,QAAU,GACjDf,OAAOK,QAAQU,OAAOO,MAAQtB,OAAOK,QAAQU,OAAOO,OAAS,GAC7DtB,OAAOK,QAAQU,OAAOO,MAAMT,GAAKzD,KAAK0B,gBAAgBC,KAAM,IAAK8B,GAE9C,OAAfC,aACAd,OAAOK,QAAQU,OAAOO,MAAMT,GAAGnB,MAAMuB,SAAW,SAAS7C,cAC9C0C,WAAWS,SAASnD,MAAO,MAAQ,KAGlD4B,OAAOK,QAAQU,OAAOO,MAAMT,GAAGK,QAAU9D,KAAK+D,cAChDC,KAAKhE,OAEP4C,OAAOK,QAAQmB,SAAW,CACtBC,UAAW,CACPC,MAAOtE,KAAKuE,aAAaP,KAAKhE,QAI/B4C,QASXhD,OAAOC,UAAUmD,oBAAsB,kBACxBhD,KAAKqB,OAAOmD,YAAYzD,IAAI,SAAS0D,YACxCC,OAASD,OAAOE,mBAAqBF,OAAOG,YAAcH,OAAOI,WACjEC,QAAU,CACVR,MAAOtE,KAAKY,WAAW6D,OAAOxC,YAC9BpB,KAAM4D,OAAOM,YACb3D,KAAMqD,OAAOnD,UACb0D,KAAMP,OAAOQ,UACbC,gBAAiBR,OAEjBS,YAAanF,KAAKqB,OAAOC,WAAa/B,IAAIM,UAAU0B,KAAO,OAASmD,OACpEU,YAAapF,KAAKqF,UAAUZ,QAAU,GAAM,UAGtB,OAAtBA,OAAOa,aACPR,QAAQS,QAAU9F,WAAW,IAAKgF,OAAOa,aAEnB,OAAtBb,OAAOe,aACPV,QAAQW,QAAUhG,WAAW,IAAKgF,OAAOe,aAGtCV,SACTd,KAAKhE,QAYXJ,OAAOC,UAAU0E,aAAe,SAASmB,YAAa7E,UAG9C4D,OAASzE,KAAKqB,OAAOmD,YAAYkB,YAAYC,cAC7CC,WAAanB,OAAOxC,WACpB4D,YAAcpB,OAAO3B,YAErBgD,YADYjF,KAAKkC,SAAS2C,YAAYC,cAAc9E,KAC5B6E,YAAY/F,OAGpCoG,QAAU,MAGY,IAAtBL,YAAYM,QAAsC,IAAtBN,YAAYO,OAAc,KAClDC,YAAclG,KAAKY,WAAWZ,KAAKqB,OAAOyB,aAC9CiD,QAAQI,KAAKD,YAAYR,YAAY/F,eAIrB,OAAhBkG,YACAE,QAAQI,KAAKnG,KAAKY,WAAWiF,YAAYH,YAAY/F,SAErDoG,QAAQI,KAAKnG,KAAKY,WAAWgF,YAAc,KAAOE,aAG/CC,SAUXnG,OAAOC,UAAUwF,UAAY,SAASZ,YAC9B2B,QAAS,SACTpG,KAAKqB,OAAOC,YAAchC,KAAKO,UAAU0B,KAE1B,QADf6E,OAAS3B,OAAO4B,eAEZD,OAASpG,KAAKqB,OAAOgF,aAElB5B,OAAOnD,YAAc9B,OAAOK,UAAUyG,YAC7CF,OAAS3B,OAAO4B,aAGbD,QASXxG,OAAOC,UAAUkE,WAAa,eACtBD,SAAU,SAGV9D,KAAKqB,OAAOC,YAAclC,IAAIS,UAAU0B,OACxCuC,QAAU9D,KAAKqB,OAAOkF,cAGnBzC,SAIXlE,OAAOC,UAAU2G,OAAS,WACtBvH,EAAEwH,QAAO,EAAMzG,KAAKS,QAAST,KAAKW,oBAC7BD,SAAS8F,UAGX5G"} \ No newline at end of file +{"version":3,"file":"chart_output_chartjs.min.js","sources":["../src/chart_output_chartjs.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Chart output for chart.js.\n *\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @module core/chart_output_chartjs\n */\ndefine([\n 'jquery',\n 'core/chartjs',\n 'core/chart_axis',\n 'core/chart_bar',\n 'core/chart_output_base',\n 'core/chart_line',\n 'core/chart_pie',\n 'core/chart_series'\n], function($, Chartjs, Axis, Bar, Base, Line, Pie, Series) {\n\n /**\n * Makes an axis ID.\n *\n * @param {String} xy Accepts 'x' and 'y'.\n * @param {Number} index The axis index.\n * @return {String}\n */\n var makeAxisId = function(xy, index) {\n return 'axis-' + xy + '-' + index;\n };\n\n /**\n * Chart output for Chart.js.\n *\n * @class\n * @extends {module:core/chart_output_base}\n */\n function Output() {\n Base.prototype.constructor.apply(this, arguments);\n\n // Make sure that we've got a canvas tag.\n this._canvas = this._node;\n if (this._canvas.prop('tagName') != 'CANVAS') {\n this._canvas = $('');\n this._node.append(this._canvas);\n }\n\n this._build();\n }\n Output.prototype = Object.create(Base.prototype);\n\n /**\n * Reference to the chart config object.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._config = null;\n\n /**\n * Reference to the instance of chart.js.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._chartjs = null;\n\n /**\n * Reference to the canvas node.\n *\n * @type {Jquery}\n * @protected\n */\n Output.prototype._canvas = null;\n\n /**\n * Builds the config and the chart.\n *\n * @protected\n */\n Output.prototype._build = function() {\n this._config = this._makeConfig();\n this._chartjs = new Chartjs(this._canvas[0], this._config);\n };\n\n /**\n * Clean data.\n *\n * @param {(String|String[])} data A single string or an array of strings.\n * @returns {(String|String[])}\n * @protected\n */\n Output.prototype._cleanData = function(data) {\n if (data instanceof Array) {\n return data.map(function(value) {\n return $('').html(value).text();\n });\n } else {\n return $('').html(data).text();\n }\n };\n\n /**\n * Get the chart type and handles the Chart.js specific chart types.\n *\n * By default returns the current chart TYPE value. Also does the handling of specific chart types, for example\n * check if the bar chart should be horizontal and the pie chart should be displayed as a doughnut.\n *\n * @method getChartType\n * @returns {String} the chart type.\n * @protected\n */\n Output.prototype._getChartType = function() {\n var type = this._chart.getType();\n\n // Bars can be displayed vertically and horizontally, defining horizontalBar type.\n if (this._chart.getType() === Bar.prototype.TYPE && this._chart.getHorizontal() === true) {\n type = 'horizontalBar';\n } else if (this._chart.getType() === Pie.prototype.TYPE && this._chart.getDoughnut() === true) {\n // Pie chart can be displayed as doughnut.\n type = 'doughnut';\n }\n\n return type;\n };\n\n /**\n * Make the axis config.\n *\n * @protected\n * @param {module:core/chart_axis} axis The axis.\n * @param {String} xy Accepts 'x' or 'y'.\n * @param {Number} index The axis index.\n * @return {Object} The axis config.\n */\n Output.prototype._makeAxisConfig = function(axis, xy, index) {\n var scaleData = {\n id: makeAxisId(xy, index)\n };\n\n if (axis.getPosition() !== Axis.prototype.POS_DEFAULT) {\n scaleData.position = axis.getPosition();\n }\n\n if (axis.getLabel() !== null) {\n scaleData.title = {\n display: true,\n text: this._cleanData(axis.getLabel())\n };\n }\n\n if (axis.getStepSize() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.stepSize = axis.getStepSize();\n }\n\n if (axis.getMax() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.max = axis.getMax();\n }\n\n if (axis.getMin() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.min = axis.getMin();\n }\n\n return scaleData;\n };\n\n /**\n * Make the config config.\n *\n * @protected\n * @return {Object} The axis config.\n */\n Output.prototype._makeConfig = function() {\n var charType = this._getChartType();\n var config = {\n type: charType,\n data: {\n labels: this._cleanData(this._chart.getLabels()),\n datasets: this._makeDatasetsConfig()\n },\n options: {\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n title: {\n display: this._chart.getTitle() !== null,\n text: this._cleanData(this._chart.getTitle())\n }\n }\n }\n };\n\n if (charType === 'horizontalBar') {\n config.type = 'bar';\n config.options.indexAxis = 'y';\n }\n\n var legendOptions = this._chart.getLegendOptions();\n if (legendOptions) {\n config.options.plugins.legend = legendOptions;\n }\n\n\n this._chart.getXAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.x = config.options.scales.x || {};\n config.options.scales.x[i] = this._makeAxisConfig(axis, 'x', i);\n\n if (axisLabels !== null) {\n config.options.scales.x[i].ticks.callback = function(value, index) {\n return axisLabels[index] || '';\n };\n }\n config.options.scales.x.stacked = this._isStacked();\n }.bind(this));\n\n this._chart.getYAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.y = config.options.scales.yAxes || {};\n config.options.scales.y[i] = this._makeAxisConfig(axis, 'y', i);\n\n if (axisLabels !== null) {\n config.options.scales.y[i].ticks.callback = function(value) {\n return axisLabels[parseInt(value, 10)] || '';\n };\n }\n config.options.scales.y.stacked = this._isStacked();\n }.bind(this));\n\n config.options.plugins.tooltip = {\n callbacks: {\n label: this._makeTooltip.bind(this)\n }\n };\n\n return config;\n };\n\n /**\n * Get the datasets configurations.\n *\n * @protected\n * @return {Object[]}\n */\n Output.prototype._makeDatasetsConfig = function() {\n var sets = this._chart.getSeries().map(function(series) {\n var colors = series.hasColoredValues() ? series.getColors() : series.getColor();\n var dataset = {\n label: this._cleanData(series.getLabel()),\n data: series.getValues(),\n type: series.getType(),\n fill: series.getFill(),\n backgroundColor: colors,\n // Pie charts look better without borders.\n borderColor: this._chart.getType() == Pie.prototype.TYPE ? '#fff' : colors,\n tension: this._isSmooth(series) ? 0.3 : 0\n };\n\n if (series.getXAxis() !== null) {\n dataset.xAxisID = makeAxisId('x', series.getXAxis());\n }\n if (series.getYAxis() !== null) {\n dataset.yAxisID = makeAxisId('y', series.getYAxis());\n }\n\n return dataset;\n }.bind(this));\n return sets;\n };\n\n /**\n * Get the chart data, add labels and rebuild the tooltip.\n *\n * @param {Object[]} tooltipItem The tooltip item object.\n * @returns {*[]}\n * @protected\n */\n Output.prototype._makeTooltip = function(tooltipItem) {\n\n // Get series and chart data to rebuild the tooltip and add labels.\n var series = this._chart.getSeries()[tooltipItem.datasetIndex];\n var serieLabel = series.getLabel();\n var chartData = tooltipItem.dataset.data;\n var tooltipData = chartData[tooltipItem.dataIndex];\n\n // Build default tooltip.\n var tooltip = [];\n\n // Pie and doughnut charts tooltip are different.\n if (this._chart.getType() === Pie.prototype.TYPE) {\n var chartLabels = this._cleanData(this._chart.getLabels());\n tooltip.push(chartLabels[tooltipItem.dataIndex] + ' - ' + this._cleanData(serieLabel) + ': ' + tooltipData);\n } else {\n tooltip.push(this._cleanData(serieLabel) + ': ' + tooltipData);\n }\n\n return tooltip;\n };\n\n /**\n * Verify if the chart line is smooth or not.\n *\n * @protected\n * @param {module:core/chart_series} series The series.\n * @returns {Bool}\n */\n Output.prototype._isSmooth = function(series) {\n var smooth = false;\n if (this._chart.getType() === Line.prototype.TYPE) {\n smooth = series.getSmooth();\n if (smooth === null) {\n smooth = this._chart.getSmooth();\n }\n } else if (series.getType() === Series.prototype.TYPE_LINE) {\n smooth = series.getSmooth();\n }\n\n return smooth;\n };\n\n /**\n * Verify if the bar chart is stacked or not.\n *\n * @protected\n * @returns {Bool}\n */\n Output.prototype._isStacked = function() {\n var stacked = false;\n\n // Stacking is (currently) only supported for bar charts.\n if (this._chart.getType() === Bar.prototype.TYPE) {\n stacked = this._chart.getStacked();\n }\n\n return stacked;\n };\n\n /** @override */\n Output.prototype.update = function() {\n $.extend(true, this._config, this._makeConfig());\n this._chartjs.update();\n };\n\n return Output;\n\n});\n"],"names":["define","$","Chartjs","Axis","Bar","Base","Line","Pie","Series","makeAxisId","xy","index","Output","prototype","constructor","apply","this","arguments","_canvas","_node","prop","append","_build","Object","create","_config","_chartjs","_makeConfig","_cleanData","data","Array","map","value","html","text","_getChartType","type","_chart","getType","TYPE","getHorizontal","getDoughnut","_makeAxisConfig","axis","scaleData","id","getPosition","POS_DEFAULT","position","getLabel","title","display","getStepSize","ticks","stepSize","getMax","max","getMin","min","charType","config","labels","getLabels","datasets","_makeDatasetsConfig","options","responsive","maintainAspectRatio","plugins","getTitle","indexAxis","legendOptions","getLegendOptions","legend","getXAxes","forEach","i","axisLabels","scales","x","callback","stacked","_isStacked","bind","getYAxes","y","yAxes","parseInt","tooltip","callbacks","label","_makeTooltip","getSeries","series","colors","hasColoredValues","getColors","getColor","dataset","getValues","fill","getFill","backgroundColor","borderColor","tension","_isSmooth","getXAxis","xAxisID","getYAxis","yAxisID","tooltipItem","serieLabel","datasetIndex","tooltipData","dataIndex","chartLabels","push","smooth","getSmooth","TYPE_LINE","getStacked","update","extend"],"mappings":";;;;;;;AAsBAA,mCAAO,CACH,SACA,eACA,kBACA,iBACA,yBACA,kBACA,iBACA,sBACD,SAASC,EAAGC,QAASC,KAAMC,IAAKC,KAAMC,KAAMC,IAAKC,YAS5CC,WAAa,SAASC,GAAIC,aACnB,QAAUD,GAAK,IAAMC,gBASvBC,SACLP,KAAKQ,UAAUC,YAAYC,MAAMC,KAAMC,gBAGlCC,QAAUF,KAAKG,MACgB,UAAhCH,KAAKE,QAAQE,KAAK,kBACbF,QAAUjB,EAAE,iBACZkB,MAAME,OAAOL,KAAKE,eAGtBI,gBAETV,OAAOC,UAAYU,OAAOC,OAAOnB,KAAKQ,WAQtCD,OAAOC,UAAUY,QAAU,KAQ3Bb,OAAOC,UAAUa,SAAW,KAQ5Bd,OAAOC,UAAUK,QAAU,KAO3BN,OAAOC,UAAUS,OAAS,gBACjBG,QAAUT,KAAKW,mBACfD,SAAW,IAAIxB,QAAQc,KAAKE,QAAQ,GAAIF,KAAKS,UAUtDb,OAAOC,UAAUe,WAAa,SAASC,aAC/BA,gBAAgBC,MACTD,KAAKE,KAAI,SAASC,cACd/B,EAAE,UAAUgC,KAAKD,OAAOE,UAG5BjC,EAAE,UAAUgC,KAAKJ,MAAMK,QActCtB,OAAOC,UAAUsB,cAAgB,eACzBC,KAAOpB,KAAKqB,OAAOC,iBAGnBtB,KAAKqB,OAAOC,YAAclC,IAAIS,UAAU0B,OAAwC,IAAhCvB,KAAKqB,OAAOG,gBAC5DJ,KAAO,gBACApB,KAAKqB,OAAOC,YAAc/B,IAAIM,UAAU0B,OAAsC,IAA9BvB,KAAKqB,OAAOI,gBAEnEL,KAAO,YAGJA,MAYXxB,OAAOC,UAAU6B,gBAAkB,SAASC,KAAMjC,GAAIC,WAC9CiC,UAAY,CACZC,GAAIpC,WAAWC,GAAIC,eAGnBgC,KAAKG,gBAAkB3C,KAAKU,UAAUkC,cACtCH,UAAUI,SAAWL,KAAKG,eAGN,OAApBH,KAAKM,aACLL,UAAUM,MAAQ,CACdC,SAAS,EACTjB,KAAMlB,KAAKY,WAAWe,KAAKM,cAIR,OAAvBN,KAAKS,gBACLR,UAAUS,MAAQT,UAAUS,OAAS,GACrCT,UAAUS,MAAMC,SAAWX,KAAKS,eAGd,OAAlBT,KAAKY,WACLX,UAAUS,MAAQT,UAAUS,OAAS,GACrCT,UAAUS,MAAMG,IAAMb,KAAKY,UAGT,OAAlBZ,KAAKc,WACLb,UAAUS,MAAQT,UAAUS,OAAS,GACrCT,UAAUS,MAAMK,IAAMf,KAAKc,UAGxBb,WASXhC,OAAOC,UAAUc,YAAc,eACvBgC,SAAW3C,KAAKmB,gBAChByB,OAAS,CACTxB,KAAMuB,SACN9B,KAAM,CACFgC,OAAQ7C,KAAKY,WAAWZ,KAAKqB,OAAOyB,aACpCC,SAAU/C,KAAKgD,uBAEnBC,QAAS,CACLC,YAAY,EACZC,qBAAqB,EACrBC,QAAS,CACLlB,MAAO,CACHC,QAAoC,OAA3BnC,KAAKqB,OAAOgC,WACrBnC,KAAMlB,KAAKY,WAAWZ,KAAKqB,OAAOgC,gBAMjC,kBAAbV,WACAC,OAAOxB,KAAO,MACdwB,OAAOK,QAAQK,UAAY,SAG3BC,cAAgBvD,KAAKqB,OAAOmC,0BAC5BD,gBACAX,OAAOK,QAAQG,QAAQK,OAASF,oBAI/BlC,OAAOqC,WAAWC,QAAQ,SAAShC,KAAMiC,OACtCC,WAAalC,KAAKmB,YAEtBF,OAAOK,QAAQa,OAASlB,OAAOK,QAAQa,QAAU,GACjDlB,OAAOK,QAAQa,OAAOC,EAAInB,OAAOK,QAAQa,OAAOC,GAAK,GACrDnB,OAAOK,QAAQa,OAAOC,EAAEH,GAAK5D,KAAK0B,gBAAgBC,KAAM,IAAKiC,GAE1C,OAAfC,aACAjB,OAAOK,QAAQa,OAAOC,EAAEH,GAAGvB,MAAM2B,SAAW,SAAShD,MAAOrB,cACjDkE,WAAWlE,QAAU,KAGpCiD,OAAOK,QAAQa,OAAOC,EAAEE,QAAUjE,KAAKkE,cACzCC,KAAKnE,YAEFqB,OAAO+C,WAAWT,QAAQ,SAAShC,KAAMiC,OACtCC,WAAalC,KAAKmB,YAEtBF,OAAOK,QAAQa,OAASlB,OAAOK,QAAQa,QAAU,GACjDlB,OAAOK,QAAQa,OAAOO,EAAIzB,OAAOK,QAAQa,OAAOQ,OAAS,GACzD1B,OAAOK,QAAQa,OAAOO,EAAET,GAAK5D,KAAK0B,gBAAgBC,KAAM,IAAKiC,GAE1C,OAAfC,aACAjB,OAAOK,QAAQa,OAAOO,EAAET,GAAGvB,MAAM2B,SAAW,SAAShD,cAC1C6C,WAAWU,SAASvD,MAAO,MAAQ,KAGlD4B,OAAOK,QAAQa,OAAOO,EAAEJ,QAAUjE,KAAKkE,cACzCC,KAAKnE,OAEP4C,OAAOK,QAAQG,QAAQoB,QAAU,CAC7BC,UAAW,CACPC,MAAO1E,KAAK2E,aAAaR,KAAKnE,QAI/B4C,QASXhD,OAAOC,UAAUmD,oBAAsB,kBACxBhD,KAAKqB,OAAOuD,YAAY7D,IAAI,SAAS8D,YACxCC,OAASD,OAAOE,mBAAqBF,OAAOG,YAAcH,OAAOI,WACjEC,QAAU,CACVR,MAAO1E,KAAKY,WAAWiE,OAAO5C,YAC9BpB,KAAMgE,OAAOM,YACb/D,KAAMyD,OAAOvD,UACb8D,KAAMP,OAAOQ,UACbC,gBAAiBR,OAEjBS,YAAavF,KAAKqB,OAAOC,WAAa/B,IAAIM,UAAU0B,KAAO,OAASuD,OACpEU,QAASxF,KAAKyF,UAAUZ,QAAU,GAAM,UAGlB,OAAtBA,OAAOa,aACPR,QAAQS,QAAUlG,WAAW,IAAKoF,OAAOa,aAEnB,OAAtBb,OAAOe,aACPV,QAAQW,QAAUpG,WAAW,IAAKoF,OAAOe,aAGtCV,SACTf,KAAKnE,QAWXJ,OAAOC,UAAU8E,aAAe,SAASmB,iBAIjCC,WADS/F,KAAKqB,OAAOuD,YAAYkB,YAAYE,cACzB/D,WAEpBgE,YADYH,YAAYZ,QAAQrE,KACRiF,YAAYI,WAGpC1B,QAAU,MAGVxE,KAAKqB,OAAOC,YAAc/B,IAAIM,UAAU0B,KAAM,KAC1C4E,YAAcnG,KAAKY,WAAWZ,KAAKqB,OAAOyB,aAC9C0B,QAAQ4B,KAAKD,YAAYL,YAAYI,WAAa,MAAQlG,KAAKY,WAAWmF,YAAc,KAAOE,kBAE/FzB,QAAQ4B,KAAKpG,KAAKY,WAAWmF,YAAc,KAAOE,oBAG/CzB,SAUX5E,OAAOC,UAAU4F,UAAY,SAASZ,YAC9BwB,QAAS,SACTrG,KAAKqB,OAAOC,YAAchC,KAAKO,UAAU0B,KAE1B,QADf8E,OAASxB,OAAOyB,eAEZD,OAASrG,KAAKqB,OAAOiF,aAElBzB,OAAOvD,YAAc9B,OAAOK,UAAU0G,YAC7CF,OAASxB,OAAOyB,aAGbD,QASXzG,OAAOC,UAAUqE,WAAa,eACtBD,SAAU,SAGVjE,KAAKqB,OAAOC,YAAclC,IAAIS,UAAU0B,OACxC0C,QAAUjE,KAAKqB,OAAOmF,cAGnBvC,SAIXrE,OAAOC,UAAU4G,OAAS,WACtBxH,EAAEyH,QAAO,EAAM1G,KAAKS,QAAST,KAAKW,oBAC7BD,SAAS+F,UAGX7G"} \ No newline at end of file diff --git a/lib/amd/build/chartjs-lazy.min.js b/lib/amd/build/chartjs-lazy.min.js index 3864d442c2a..82033094c56 100644 --- a/lib/amd/build/chartjs-lazy.min.js +++ b/lib/amd/build/chartjs-lazy.min.js @@ -1,3 +1,3 @@ -!function(global,factory){"object"==typeof exports&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define("core/chartjs-lazy",factory):(global=global||self).Chart=factory()}(window,(function(){"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function commonjsRequire(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}function createCommonjsModule(fn,module){return fn(module={exports:{}},module.exports),module.exports}var colorName={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]},conversions=createCommonjsModule((function(module){var reverseKeywords={};for(var key in colorName)colorName.hasOwnProperty(key)&&(reverseKeywords[colorName[key]]=key);var convert=module.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var model in convert)if(convert.hasOwnProperty(model)){if(!("channels"in convert[model]))throw new Error("missing channels property: "+model);if(!("labels"in convert[model]))throw new Error("missing channel labels property: "+model);if(convert[model].labels.length!==convert[model].channels)throw new Error("channel and label counts mismatch: "+model);var channels=convert[model].channels,labels=convert[model].labels;delete convert[model].channels,delete convert[model].labels,Object.defineProperty(convert[model],"channels",{value:channels}),Object.defineProperty(convert[model],"labels",{value:labels})}convert.rgb.hsl=function(rgb){var h,l,r=rgb[0]/255,g=rgb[1]/255,b=rgb[2]/255,min=Math.min(r,g,b),max=Math.max(r,g,b),delta=max-min;return max===min?h=0:r===max?h=(g-b)/delta:g===max?h=2+(b-r)/delta:b===max&&(h=4+(r-g)/delta),(h=Math.min(60*h,360))<0&&(h+=360),l=(min+max)/2,[h,100*(max===min?0:l<=.5?delta/(max+min):delta/(2-max-min)),100*l]},convert.rgb.hsv=function(rgb){var rdif,gdif,bdif,h,s,r=rgb[0]/255,g=rgb[1]/255,b=rgb[2]/255,v=Math.max(r,g,b),diff=v-Math.min(r,g,b),diffc=function(c){return(v-c)/6/diff+.5};return 0===diff?h=s=0:(s=diff/v,rdif=diffc(r),gdif=diffc(g),bdif=diffc(b),r===v?h=bdif-gdif:g===v?h=1/3+rdif-bdif:b===v&&(h=2/3+gdif-rdif),h<0?h+=1:h>1&&(h-=1)),[360*h,100*s,100*v]},convert.rgb.hwb=function(rgb){var r=rgb[0],g=rgb[1],b=rgb[2];return[convert.rgb.hsl(rgb)[0],100*(1/255*Math.min(r,Math.min(g,b))),100*(b=1-1/255*Math.max(r,Math.max(g,b)))]},convert.rgb.cmyk=function(rgb){var k,r=rgb[0]/255,g=rgb[1]/255,b=rgb[2]/255;return[100*((1-r-(k=Math.min(1-r,1-g,1-b)))/(1-k)||0),100*((1-g-k)/(1-k)||0),100*((1-b-k)/(1-k)||0),100*k]},convert.rgb.keyword=function(rgb){var reversed=reverseKeywords[rgb];if(reversed)return reversed;var currentClosestKeyword,x,y,currentClosestDistance=1/0;for(var keyword in colorName)if(colorName.hasOwnProperty(keyword)){var value=colorName[keyword],distance=(x=rgb,y=value,Math.pow(x[0]-y[0],2)+Math.pow(x[1]-y[1],2)+Math.pow(x[2]-y[2],2));distance.04045?Math.pow((r+.055)/1.055,2.4):r/12.92)+.3576*(g=g>.04045?Math.pow((g+.055)/1.055,2.4):g/12.92)+.1805*(b=b>.04045?Math.pow((b+.055)/1.055,2.4):b/12.92)),100*(.2126*r+.7152*g+.0722*b),100*(.0193*r+.1192*g+.9505*b)]},convert.rgb.lab=function(rgb){var xyz=convert.rgb.xyz(rgb),x=xyz[0],y=xyz[1],z=xyz[2];return y/=100,z/=108.883,x=(x/=95.047)>.008856?Math.pow(x,1/3):7.787*x+16/116,[116*(y=y>.008856?Math.pow(y,1/3):7.787*y+16/116)-16,500*(x-y),200*(y-(z=z>.008856?Math.pow(z,1/3):7.787*z+16/116))]},convert.hsl.rgb=function(hsl){var t1,t2,t3,rgb,val,h=hsl[0]/360,s=hsl[1]/100,l=hsl[2]/100;if(0===s)return[val=255*l,val,val];t1=2*l-(t2=l<.5?l*(1+s):l+s-l*s),rgb=[0,0,0];for(var i=0;i<3;i++)(t3=h+1/3*-(i-1))<0&&t3++,t3>1&&t3--,val=6*t3<1?t1+6*(t2-t1)*t3:2*t3<1?t2:3*t3<2?t1+(t2-t1)*(2/3-t3)*6:t1,rgb[i]=255*val;return rgb},convert.hsl.hsv=function(hsl){var h=hsl[0],s=hsl[1]/100,l=hsl[2]/100,smin=s,lmin=Math.max(l,.01);return s*=(l*=2)<=1?l:2-l,smin*=lmin<=1?lmin:2-lmin,[h,100*(0===l?2*smin/(lmin+smin):2*s/(l+s)),100*((l+s)/2)]},convert.hsv.rgb=function(hsv){var h=hsv[0]/60,s=hsv[1]/100,v=hsv[2]/100,hi=Math.floor(h)%6,f=h-Math.floor(h),p=255*v*(1-s),q=255*v*(1-s*f),t=255*v*(1-s*(1-f));switch(v*=255,hi){case 0:return[v,t,p];case 1:return[q,v,p];case 2:return[p,v,t];case 3:return[p,q,v];case 4:return[t,p,v];case 5:return[v,p,q]}},convert.hsv.hsl=function(hsv){var lmin,sl,l,h=hsv[0],s=hsv[1]/100,v=hsv[2]/100,vmin=Math.max(v,.01);return l=(2-s)*v,sl=s*vmin,[h,100*(sl=(sl/=(lmin=(2-s)*vmin)<=1?lmin:2-lmin)||0),100*(l/=2)]},convert.hwb.rgb=function(hwb){var i,v,f,n,r,g,b,h=hwb[0]/360,wh=hwb[1]/100,bl=hwb[2]/100,ratio=wh+bl;switch(ratio>1&&(wh/=ratio,bl/=ratio),f=6*h-(i=Math.floor(6*h)),0!=(1&i)&&(f=1-f),n=wh+f*((v=1-bl)-wh),i){default:case 6:case 0:r=v,g=n,b=wh;break;case 1:r=n,g=v,b=wh;break;case 2:r=wh,g=v,b=n;break;case 3:r=wh,g=n,b=v;break;case 4:r=n,g=wh,b=v;break;case 5:r=v,g=wh,b=n}return[255*r,255*g,255*b]},convert.cmyk.rgb=function(cmyk){var c=cmyk[0]/100,m=cmyk[1]/100,y=cmyk[2]/100,k=cmyk[3]/100;return[255*(1-Math.min(1,c*(1-k)+k)),255*(1-Math.min(1,m*(1-k)+k)),255*(1-Math.min(1,y*(1-k)+k))]},convert.xyz.rgb=function(xyz){var r,g,b,x=xyz[0]/100,y=xyz[1]/100,z=xyz[2]/100;return g=-.9689*x+1.8758*y+.0415*z,b=.0557*x+-.204*y+1.057*z,r=(r=3.2406*x+-1.5372*y+-.4986*z)>.0031308?1.055*Math.pow(r,1/2.4)-.055:12.92*r,g=g>.0031308?1.055*Math.pow(g,1/2.4)-.055:12.92*g,b=b>.0031308?1.055*Math.pow(b,1/2.4)-.055:12.92*b,[255*(r=Math.min(Math.max(0,r),1)),255*(g=Math.min(Math.max(0,g),1)),255*(b=Math.min(Math.max(0,b),1))]},convert.xyz.lab=function(xyz){var x=xyz[0],y=xyz[1],z=xyz[2];return y/=100,z/=108.883,x=(x/=95.047)>.008856?Math.pow(x,1/3):7.787*x+16/116,[116*(y=y>.008856?Math.pow(y,1/3):7.787*y+16/116)-16,500*(x-y),200*(y-(z=z>.008856?Math.pow(z,1/3):7.787*z+16/116))]},convert.lab.xyz=function(lab){var x,y,z,l=lab[0];x=lab[1]/500+(y=(l+16)/116),z=y-lab[2]/200;var y2=Math.pow(y,3),x2=Math.pow(x,3),z2=Math.pow(z,3);return y=y2>.008856?y2:(y-16/116)/7.787,x=x2>.008856?x2:(x-16/116)/7.787,z=z2>.008856?z2:(z-16/116)/7.787,[x*=95.047,y*=100,z*=108.883]},convert.lab.lch=function(lab){var h,l=lab[0],a=lab[1],b=lab[2];return(h=360*Math.atan2(b,a)/2/Math.PI)<0&&(h+=360),[l,Math.sqrt(a*a+b*b),h]},convert.lch.lab=function(lch){var hr,l=lch[0],c=lch[1];return hr=lch[2]/360*2*Math.PI,[l,c*Math.cos(hr),c*Math.sin(hr)]},convert.rgb.ansi16=function(args){var r=args[0],g=args[1],b=args[2],value=1 in arguments?arguments[1]:convert.rgb.hsv(args)[2];if(0===(value=Math.round(value/50)))return 30;var ansi=30+(Math.round(b/255)<<2|Math.round(g/255)<<1|Math.round(r/255));return 2===value&&(ansi+=60),ansi},convert.hsv.ansi16=function(args){return convert.rgb.ansi16(convert.hsv.rgb(args),args[2])},convert.rgb.ansi256=function(args){var r=args[0],g=args[1],b=args[2];return r===g&&g===b?r<8?16:r>248?231:Math.round((r-8)/247*24)+232:16+36*Math.round(r/255*5)+6*Math.round(g/255*5)+Math.round(b/255*5)},convert.ansi16.rgb=function(args){var color=args%10;if(0===color||7===color)return args>50&&(color+=3.5),[color=color/10.5*255,color,color];var mult=.5*(1+~~(args>50));return[(1&color)*mult*255,(color>>1&1)*mult*255,(color>>2&1)*mult*255]},convert.ansi256.rgb=function(args){if(args>=232){var c=10*(args-232)+8;return[c,c,c]}var rem;return args-=16,[Math.floor(args/36)/5*255,Math.floor((rem=args%36)/6)/5*255,rem%6/5*255]},convert.rgb.hex=function(args){var string=(((255&Math.round(args[0]))<<16)+((255&Math.round(args[1]))<<8)+(255&Math.round(args[2]))).toString(16).toUpperCase();return"000000".substring(string.length)+string},convert.hex.rgb=function(args){var match=args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!match)return[0,0,0];var colorString=match[0];3===match[0].length&&(colorString=colorString.split("").map((function(char){return char+char})).join(""));var integer=parseInt(colorString,16);return[integer>>16&255,integer>>8&255,255&integer]},convert.rgb.hcg=function(rgb){var hue,r=rgb[0]/255,g=rgb[1]/255,b=rgb[2]/255,max=Math.max(Math.max(r,g),b),min=Math.min(Math.min(r,g),b),chroma=max-min;return hue=chroma<=0?0:max===r?(g-b)/chroma%6:max===g?2+(b-r)/chroma:4+(r-g)/chroma+4,hue/=6,[360*(hue%=1),100*chroma,100*(chroma<1?min/(1-chroma):0)]},convert.hsl.hcg=function(hsl){var s=hsl[1]/100,l=hsl[2]/100,c=1,f=0;return(c=l<.5?2*s*l:2*s*(1-l))<1&&(f=(l-.5*c)/(1-c)),[hsl[0],100*c,100*f]},convert.hsv.hcg=function(hsv){var s=hsv[1]/100,v=hsv[2]/100,c=s*v,f=0;return c<1&&(f=(v-c)/(1-c)),[hsv[0],100*c,100*f]},convert.hcg.rgb=function(hcg){var h=hcg[0]/360,c=hcg[1]/100,g=hcg[2]/100;if(0===c)return[255*g,255*g,255*g];var mg,pure=[0,0,0],hi=h%1*6,v=hi%1,w=1-v;switch(Math.floor(hi)){case 0:pure[0]=1,pure[1]=v,pure[2]=0;break;case 1:pure[0]=w,pure[1]=1,pure[2]=0;break;case 2:pure[0]=0,pure[1]=1,pure[2]=v;break;case 3:pure[0]=0,pure[1]=w,pure[2]=1;break;case 4:pure[0]=v,pure[1]=0,pure[2]=1;break;default:pure[0]=1,pure[1]=0,pure[2]=w}return mg=(1-c)*g,[255*(c*pure[0]+mg),255*(c*pure[1]+mg),255*(c*pure[2]+mg)]},convert.hcg.hsv=function(hcg){var c=hcg[1]/100,v=c+hcg[2]/100*(1-c),f=0;return v>0&&(f=c/v),[hcg[0],100*f,100*v]},convert.hcg.hsl=function(hcg){var c=hcg[1]/100,l=hcg[2]/100*(1-c)+.5*c,s=0;return l>0&&l<.5?s=c/(2*l):l>=.5&&l<1&&(s=c/(2*(1-l))),[hcg[0],100*s,100*l]},convert.hcg.hwb=function(hcg){var c=hcg[1]/100,v=c+hcg[2]/100*(1-c);return[hcg[0],100*(v-c),100*(1-v)]},convert.hwb.hcg=function(hwb){var w=hwb[1]/100,v=1-hwb[2]/100,c=v-w,g=0;return c<1&&(g=(v-c)/(1-c)),[hwb[0],100*c,100*g]},convert.apple.rgb=function(apple){return[apple[0]/65535*255,apple[1]/65535*255,apple[2]/65535*255]},convert.rgb.apple=function(rgb){return[rgb[0]/255*65535,rgb[1]/255*65535,rgb[2]/255*65535]},convert.gray.rgb=function(args){return[args[0]/100*255,args[0]/100*255,args[0]/100*255]},convert.gray.hsl=convert.gray.hsv=function(args){return[0,0,args[0]]},convert.gray.hwb=function(gray){return[0,100,gray[0]]},convert.gray.cmyk=function(gray){return[0,0,0,gray[0]]},convert.gray.lab=function(gray){return[gray[0],0,0]},convert.gray.hex=function(gray){var val=255&Math.round(gray[0]/100*255),string=((val<<16)+(val<<8)+val).toString(16).toUpperCase();return"000000".substring(string.length)+string},convert.rgb.gray=function(rgb){return[(rgb[0]+rgb[1]+rgb[2])/3/255*100]}}));conversions.rgb,conversions.hsl,conversions.hsv,conversions.hwb,conversions.cmyk,conversions.xyz,conversions.lab,conversions.lch,conversions.hex,conversions.keyword,conversions.ansi16,conversions.ansi256,conversions.hcg,conversions.apple,conversions.gray;function deriveBFS(fromModel){var graph=function(){for(var graph={},models=Object.keys(conversions),len=models.length,i=0;i1&&(args=Array.prototype.slice.call(arguments));var result=fn(args);if("object"==typeof result)for(var len=result.length,i=0;i1&&(args=Array.prototype.slice.call(arguments)),fn(args))};return"conversion"in fn&&(wrappedFn.conversion=fn.conversion),wrappedFn}(fn)}))}));var colorConvert=convert,colorName$1={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]},colorString={getRgba:getRgba,getHsla:getHsla,getRgb:function(string){var rgba=getRgba(string);return rgba&&rgba.slice(0,3)},getHsl:function(string){var hsla=getHsla(string);return hsla&&hsla.slice(0,3)},getHwb:getHwb,getAlpha:function(string){var vals=getRgba(string);if(vals)return vals[3];if(vals=getHsla(string))return vals[3];if(vals=getHwb(string))return vals[3]},hexString:function(rgba,a){a=void 0!==a&&3===rgba.length?a:rgba[3];return"#"+hexDouble(rgba[0])+hexDouble(rgba[1])+hexDouble(rgba[2])+(a>=0&&a<1?hexDouble(Math.round(255*a)):"")},rgbString:function(rgba,alpha){if(alpha<1||rgba[3]&&rgba[3]<1)return rgbaString(rgba,alpha);return"rgb("+rgba[0]+", "+rgba[1]+", "+rgba[2]+")"},rgbaString:rgbaString,percentString:function(rgba,alpha){if(alpha<1||rgba[3]&&rgba[3]<1)return percentaString(rgba,alpha);var r=Math.round(rgba[0]/255*100),g=Math.round(rgba[1]/255*100),b=Math.round(rgba[2]/255*100);return"rgb("+r+"%, "+g+"%, "+b+"%)"},percentaString:percentaString,hslString:function(hsla,alpha){if(alpha<1||hsla[3]&&hsla[3]<1)return hslaString(hsla,alpha);return"hsl("+hsla[0]+", "+hsla[1]+"%, "+hsla[2]+"%)"},hslaString:hslaString,hwbString:function(hwb,alpha){void 0===alpha&&(alpha=void 0!==hwb[3]?hwb[3]:1);return"hwb("+hwb[0]+", "+hwb[1]+"%, "+hwb[2]+"%"+(void 0!==alpha&&1!==alpha?", "+alpha:"")+")"},keyword:function(rgb){return reverseNames[rgb.slice(0,3)]}};function getRgba(string){if(string){var rgb=[0,0,0],a=1,match=string.match(/^#([a-fA-F0-9]{3,4})$/i),hexAlpha="";if(match){hexAlpha=(match=match[1])[3];for(var i=0;ilum2?(lum1+.05)/(lum2+.05):(lum2+.05)/(lum1+.05)},level:function(color2){var contrastRatio=this.contrast(color2);return contrastRatio>=7.1?"AAA":contrastRatio>=4.5?"AA":""},dark:function(){var rgb=this.values.rgb;return(299*rgb[0]+587*rgb[1]+114*rgb[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var rgb=[],i=0;i<3;i++)rgb[i]=255-this.values.rgb[i];return this.setValues("rgb",rgb),this},lighten:function(ratio){var hsl=this.values.hsl;return hsl[2]+=hsl[2]*ratio,this.setValues("hsl",hsl),this},darken:function(ratio){var hsl=this.values.hsl;return hsl[2]-=hsl[2]*ratio,this.setValues("hsl",hsl),this},saturate:function(ratio){var hsl=this.values.hsl;return hsl[1]+=hsl[1]*ratio,this.setValues("hsl",hsl),this},desaturate:function(ratio){var hsl=this.values.hsl;return hsl[1]-=hsl[1]*ratio,this.setValues("hsl",hsl),this},whiten:function(ratio){var hwb=this.values.hwb;return hwb[1]+=hwb[1]*ratio,this.setValues("hwb",hwb),this},blacken:function(ratio){var hwb=this.values.hwb;return hwb[2]+=hwb[2]*ratio,this.setValues("hwb",hwb),this},greyscale:function(){var rgb=this.values.rgb,val=.3*rgb[0]+.59*rgb[1]+.11*rgb[2];return this.setValues("rgb",[val,val,val]),this},clearer:function(ratio){var alpha=this.values.alpha;return this.setValues("alpha",alpha-alpha*ratio),this},opaquer:function(ratio){var alpha=this.values.alpha;return this.setValues("alpha",alpha+alpha*ratio),this},rotate:function(degrees){var hsl=this.values.hsl,hue=(hsl[0]+degrees)%360;return hsl[0]=hue<0?360+hue:hue,this.setValues("hsl",hsl),this},mix:function(mixinColor,weight){var color2=mixinColor,p=void 0===weight?.5:weight,w=2*p-1,a=this.alpha()-color2.alpha(),w1=((w*a==-1?w:(w+a)/(1+w*a))+1)/2,w2=1-w1;return this.rgb(w1*this.red()+w2*color2.red(),w1*this.green()+w2*color2.green(),w1*this.blue()+w2*color2.blue()).alpha(this.alpha()*p+color2.alpha()*(1-p))},toJSON:function(){return this.rgb()},clone:function(){var value,type,result=new Color,source=this.values,target=result.values;for(var prop in source)source.hasOwnProperty(prop)&&(value=source[prop],"[object Array]"===(type={}.toString.call(value))?target[prop]=value.slice(0):"[object Number]"===type?target[prop]=value:console.error("unexpected color value:",value));return result}},Color.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},Color.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},Color.prototype.getValues=function(space){for(var values=this.values,vals={},i=0;i=0;i--)fn.call(thisArg,loopable[i],i);else for(i=0;i=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 s=1.70158,p=0,a=1;return 0===t?0:1===t?1:(p||(p=.3),a<1?(a=1,s=p/4):s=p/(2*Math.PI)*Math.asin(1/a),-a*Math.pow(2,10*(t-=1))*Math.sin((t-s)*(2*Math.PI)/p))},easeOutElastic:function(t){var s=1.70158,p=0,a=1;return 0===t?0:1===t?1:(p||(p=.3),a<1?(a=1,s=p/4):s=p/(2*Math.PI)*Math.asin(1/a),a*Math.pow(2,-10*t)*Math.sin((t-s)*(2*Math.PI)/p)+1)},easeInOutElastic:function(t){var s=1.70158,p=0,a=1;return 0===t?0:2==(t/=.5)?1:(p||(p=.45),a<1?(a=1,s=p/4):s=p/(2*Math.PI)*Math.asin(1/a),t<1?a*Math.pow(2,10*(t-=1))*Math.sin((t-s)*(2*Math.PI)/p)*-.5:a*Math.pow(2,-10*(t-=1))*Math.sin((t-s)*(2*Math.PI)/p)*.5+1)},easeInBack:function(t){var s=1.70158;return t*t*((s+1)*t-s)},easeOutBack:function(t){var s=1.70158;return(t-=1)*t*((s+1)*t+s)+1},easeInOutBack:function(t){var s=1.70158;return(t/=.5)<1?t*t*((1+(s*=1.525))*t-s)*.5:.5*((t-=2)*t*((1+(s*=1.525))*t+s)+2)},easeInBounce:function(t){return 1-effects.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*effects.easeInBounce(2*t):.5*effects.easeOutBounce(2*t-1)+.5}},helpers_easing={effects:effects};helpers_core.easingEffects=effects;var PI=Math.PI,RAD_PER_DEG=PI/180,DOUBLE_PI=2*PI,HALF_PI=PI/2,QUARTER_PI=PI/4,TWO_THIRDS_PI=2*PI/3,exports$1={clear:function(chart){chart.ctx.clearRect(0,0,chart.width,chart.height)},roundedRect:function(ctx,x,y,width,height,radius){if(radius){var r=Math.min(radius,height/2,width/2),left=x+r,top=y+r,right=x+width-r,bottom=y+height-r;ctx.moveTo(x,top),leftarea.left-1e-6&&point.xarea.top-1e-6&&point.y0&&this.requestAnimationFrame()},advance:function(){for(var animation,chart,numSteps,nextStep,animations=this.animations,i=0;i=numSteps?(helpers$1.callback(animation.onAnimationComplete,[animation],chart),chart.animating=!1,animations.splice(i,1)):++i}},resolve=helpers$1.options.resolve,arrayEvents=["push","pop","shift","splice","unshift"];function unlistenArrayEvents(array,listener){var stub=array._chartjs;if(stub){var listeners=stub.listeners,index=listeners.indexOf(listener);-1!==index&&listeners.splice(index,1),listeners.length>0||(arrayEvents.forEach((function(key){delete array[key]})),delete array._chartjs)}}var DatasetController=function(chart,datasetIndex){this.initialize(chart,datasetIndex)};helpers$1.extend(DatasetController.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(chart,datasetIndex){var me=this;me.chart=chart,me.index=datasetIndex,me.linkScales(),me.addElements(),me._type=me.getMeta().type},updateIndex:function(datasetIndex){this.index=datasetIndex},linkScales:function(){var meta=this.getMeta(),chart=this.chart,scales=chart.scales,dataset=this.getDataset(),scalesOpts=chart.options.scales;null!==meta.xAxisID&&meta.xAxisID in scales&&!dataset.xAxisID||(meta.xAxisID=dataset.xAxisID||scalesOpts.xAxes[0].id),null!==meta.yAxisID&&meta.yAxisID in scales&&!dataset.yAxisID||(meta.yAxisID=dataset.yAxisID||scalesOpts.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(scaleID){return this.chart.scales[scaleID]},_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&&unlistenArrayEvents(this._data,this)},createMetaDataset:function(){var type=this.datasetElementType;return type&&new type({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(index){var type=this.dataElementType;return type&&new type({_chart:this.chart,_datasetIndex:this.index,_index:index})},addElements:function(){var i,ilen,meta=this.getMeta(),data=this.getDataset().data||[],metaData=meta.data;for(i=0,ilen=data.length;inumMeta&&this.insertElements(numMeta,numData-numMeta)},insertElements:function(start,count){for(var i=0;ipixelMargin?(angleMargin=pixelMargin/arc.innerRadius,ctx.arc(x,y,arc.innerRadius-pixelMargin,endAngle+angleMargin,startAngle-angleMargin,!0)):ctx.arc(x,y,pixelMargin,endAngle+Math.PI/2,startAngle-Math.PI/2),ctx.closePath(),ctx.clip()}function drawBorder(ctx,vm,arc){var inner="inner"===vm.borderAlign;inner?(ctx.lineWidth=2*vm.borderWidth,ctx.lineJoin="round"):(ctx.lineWidth=vm.borderWidth,ctx.lineJoin="bevel"),arc.fullCircles&&function(ctx,vm,arc,inner){var i,endAngle=arc.endAngle;for(inner&&(arc.endAngle=arc.startAngle+TAU,clipArc(ctx,arc),arc.endAngle=endAngle,arc.endAngle===arc.startAngle&&arc.fullCircles&&(arc.endAngle+=TAU,arc.fullCircles--)),ctx.beginPath(),ctx.arc(arc.x,arc.y,arc.innerRadius,arc.startAngle+TAU,arc.startAngle,!0),i=0;iendAngle;)angle-=TAU;for(;angle=startAngle&&angle<=endAngle,withinRadius=distance>=vm.innerRadius&&distance<=vm.outerRadius;return betweenAngles&&withinRadius}return!1},getCenterPoint:function(){var vm=this._view,halfAngle=(vm.startAngle+vm.endAngle)/2,halfRadius=(vm.innerRadius+vm.outerRadius)/2;return{x:vm.x+Math.cos(halfAngle)*halfRadius,y:vm.y+Math.sin(halfAngle)*halfRadius}},getArea:function(){var vm=this._view;return Math.PI*((vm.endAngle-vm.startAngle)/(2*Math.PI))*(Math.pow(vm.outerRadius,2)-Math.pow(vm.innerRadius,2))},tooltipPosition:function(){var vm=this._view,centreAngle=vm.startAngle+(vm.endAngle-vm.startAngle)/2,rangeFromCentre=(vm.outerRadius-vm.innerRadius)/2+vm.innerRadius;return{x:vm.x+Math.cos(centreAngle)*rangeFromCentre,y:vm.y+Math.sin(centreAngle)*rangeFromCentre}},draw:function(){var i,ctx=this._chart.ctx,vm=this._view,pixelMargin="inner"===vm.borderAlign?.33:0,arc={x:vm.x,y:vm.y,innerRadius:vm.innerRadius,outerRadius:Math.max(vm.outerRadius-pixelMargin,0),pixelMargin:pixelMargin,startAngle:vm.startAngle,endAngle:vm.endAngle,fullCircles:Math.floor(vm.circumference/TAU)};if(ctx.save(),ctx.fillStyle=vm.backgroundColor,ctx.strokeStyle=vm.borderColor,arc.fullCircles){for(arc.endAngle=arc.startAngle+TAU,ctx.beginPath(),ctx.arc(arc.x,arc.y,arc.outerRadius,arc.startAngle,arc.endAngle),ctx.arc(arc.x,arc.y,arc.innerRadius,arc.endAngle,arc.startAngle,!0),ctx.closePath(),i=0;ivm.x&&(edge=swap(edge,"left","right")):vm.basemaxH?maxH:t,r:skip.right||r<0?0:r>maxW?maxW:r,b:skip.bottom||b<0?0:b>maxH?maxH:b,l:skip.left||l<0?0:l>maxW?maxW:l}}function inRange(vm,x,y){var skipX=null===x,skipY=null===y,bounds=!(!vm||skipX&&skipY)&&getBarBounds(vm);return bounds&&(skipX||x>=bounds.left&&x<=bounds.right)&&(skipY||y>=bounds.top&&y<=bounds.bottom)}core_defaults._set("global",{elements:{rectangle:{backgroundColor:defaultColor$2,borderColor:defaultColor$2,borderSkipped:"bottom",borderWidth:0}}});var element_rectangle=core_element.extend({_type:"rectangle",draw:function(){var ctx=this._chart.ctx,vm=this._view,rects=function(vm){var bounds=getBarBounds(vm),width=bounds.right-bounds.left,height=bounds.bottom-bounds.top,border=parseBorderWidth(vm,width/2,height/2);return{outer:{x:bounds.left,y:bounds.top,w:width,h:height},inner:{x:bounds.left+border.l,y:bounds.top+border.t,w:width-border.l-border.r,h:height-border.t-border.b}}}(vm),outer=rects.outer,inner=rects.inner;ctx.fillStyle=vm.backgroundColor,ctx.fillRect(outer.x,outer.y,outer.w,outer.h),outer.w===inner.w&&outer.h===inner.h||(ctx.save(),ctx.beginPath(),ctx.rect(outer.x,outer.y,outer.w,outer.h),ctx.clip(),ctx.fillStyle=vm.borderColor,ctx.rect(inner.x,inner.y,inner.w,inner.h),ctx.fill("evenodd"),ctx.restore())},height:function(){var vm=this._view;return vm.base-vm.y},inRange:function(mouseX,mouseY){return inRange(this._view,mouseX,mouseY)},inLabelRange:function(mouseX,mouseY){var vm=this._view;return isVertical(vm)?inRange(vm,mouseX,null):inRange(vm,null,mouseY)},inXRange:function(mouseX){return inRange(this._view,mouseX,null)},inYRange:function(mouseY){return inRange(this._view,null,mouseY)},getCenterPoint:function(){var x,y,vm=this._view;return isVertical(vm)?(x=vm.x,y=(vm.y+vm.base)/2):(x=(vm.x+vm.base)/2,y=vm.y),{x:x,y:y}},getArea:function(){var vm=this._view;return isVertical(vm)?vm.width*Math.abs(vm.y-vm.base):vm.height*Math.abs(vm.x-vm.base)},tooltipPosition:function(){var vm=this._view;return{x:vm.x,y:vm.y}}}),elements={},Arc=element_arc,Line=element_line,Point=element_point,Rectangle=element_rectangle;elements.Arc=Arc,elements.Line=Line,elements.Point=Point,elements.Rectangle=Rectangle;var deprecated=helpers$1._deprecated,valueOrDefault$3=helpers$1.valueOrDefault;function computeFitCategoryTraits(index,ruler,options){var size,ratio,thickness=options.barThickness,count=ruler.stackCount,curr=ruler.pixels[index],min=helpers$1.isNullOrUndef(thickness)?function(scale,pixels){var prev,curr,i,ilen,min=scale._length;for(i=1,ilen=pixels.length;i0?Math.min(min,Math.abs(curr-prev)):min,prev=curr;return min}(ruler.scale,ruler.pixels):-1;return helpers$1.isNullOrUndef(thickness)?(size=min*options.categoryPercentage,ratio=options.barPercentage):(size=thickness*count,ratio=1),{chunk:size/count,ratio:ratio,start:curr-size/2}}core_defaults._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),core_defaults._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var controller_bar=core_datasetController.extend({dataElementType:elements.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var meta,scaleOpts,me=this;core_datasetController.prototype.initialize.apply(me,arguments),(meta=me.getMeta()).stack=me.getDataset().stack,meta.bar=!0,scaleOpts=me._getIndexScale().options,deprecated("bar chart",scaleOpts.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),deprecated("bar chart",scaleOpts.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),deprecated("bar chart",scaleOpts.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),deprecated("bar chart",me._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),deprecated("bar chart",scaleOpts.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(reset){var i,ilen,rects=this.getMeta().data;for(this._ruler=this.getRuler(),i=0,ilen=rects.length;i=0&&value.min>=0?value.min:value.max,length=void 0===value.start?value.end:value.max>=0&&value.min>=0?value.max-value.min:value.min-value.max,ilen=metasets.length;if(stacked||void 0===stacked&&void 0!==stack)for(i=0;i=0&&stackLength.max>=0?stackLength.max:stackLength.min,(value.min<0&&ivalue<0||value.max>=0&&ivalue>0)&&(start+=ivalue));return base=scale.getPixelForValue(start),size=(head=scale.getPixelForValue(start+length))-base,void 0!==minBarLength&&Math.abs(size)=0&&!isHorizontal||length<0&&isHorizontal?base-minBarLength:base+minBarLength),{size:size,base:base,head:head,center:head+size/2}},calculateBarIndexPixels:function(datasetIndex,index,ruler,options){var range="flex"===options.barThickness?function(index,ruler,options){var start,pixels=ruler.pixels,curr=pixels[index],prev=index>0?pixels[index-1]:null,next=index=PI$1?-DOUBLE_PI$1:startAngle<-PI$1?DOUBLE_PI$1:0)+circumference,startX=Math.cos(startAngle),startY=Math.sin(startAngle),endX=Math.cos(endAngle),endY=Math.sin(endAngle),contains0=startAngle<=0&&endAngle>=0||endAngle>=DOUBLE_PI$1,contains90=startAngle<=HALF_PI$1&&endAngle>=HALF_PI$1||endAngle>=DOUBLE_PI$1+HALF_PI$1,contains270=startAngle<=-HALF_PI$1&&endAngle>=-HALF_PI$1||endAngle>=PI$1+HALF_PI$1,minX=startAngle===-PI$1||endAngle>=PI$1?-1:Math.min(startX,startX*cutout,endX,endX*cutout),minY=contains270?-1:Math.min(startY,startY*cutout,endY,endY*cutout),maxX=contains0?1:Math.max(startX,startX*cutout,endX,endX*cutout),maxY=contains90?1:Math.max(startY,startY*cutout,endY,endY*cutout);ratioX=(maxX-minX)/2,ratioY=(maxY-minY)/2,offsetX=-(maxX+minX)/2,offsetY=-(maxY+minY)/2}for(i=0,ilen=arcs.length;i0&&!isNaN(value)?DOUBLE_PI$1*(Math.abs(value)/total):0},getMaxBorderWidth:function(arcs){var i,ilen,meta,arc,controller,options,borderWidth,hoverWidth,max=0,chart=this.chart;if(!arcs)for(i=0,ilen=chart.data.datasets.length;i(max=borderWidth>max?borderWidth:max)?hoverWidth:max);return max},setHoverStyle:function(arc){var model=arc._model,options=arc._options,getHoverColor=helpers$1.getHoverColor;arc.$previousStyle={backgroundColor:model.backgroundColor,borderColor:model.borderColor,borderWidth:model.borderWidth},model.backgroundColor=valueOrDefault$5(options.hoverBackgroundColor,getHoverColor(options.backgroundColor)),model.borderColor=valueOrDefault$5(options.hoverBorderColor,getHoverColor(options.borderColor)),model.borderWidth=valueOrDefault$5(options.hoverBorderWidth,options.borderWidth)},_getRingWeightOffset:function(datasetIndex){for(var ringWeightOffset=0,i=0;i0&&isPointInArea(points[i-1]._model,area)&&(model.controlPointPreviousX=capControlPoint(model.controlPointPreviousX,area.left,area.right),model.controlPointPreviousY=capControlPoint(model.controlPointPreviousY,area.top,area.bottom)),i0&&(items=chart.getDatasetMeta(items[0]._datasetIndex).data),items},"x-axis":function(chart,e){return indexMode(chart,e,{intersect:!1})},point:function(chart,e){return getIntersectItems(chart,getRelativePosition(e,chart))},nearest:function(chart,e,options){var position=getRelativePosition(e,chart);options.axis=options.axis||"xy";var distanceMetric=getDistanceMetricForAxis(options.axis);return getNearestItems(chart,position,options.intersect,distanceMetric)},x:function(chart,e,options){var position=getRelativePosition(e,chart),items=[],intersectsItem=!1;return parseVisibleItems(chart,(function(element){element.inXRange(position.x)&&items.push(element),element.inRange(position.x,position.y)&&(intersectsItem=!0)})),options.intersect&&!intersectsItem&&(items=[]),items},y:function(chart,e,options){var position=getRelativePosition(e,chart),items=[],intersectsItem=!1;return parseVisibleItems(chart,(function(element){element.inYRange(position.y)&&items.push(element),element.inRange(position.x,position.y)&&(intersectsItem=!0)})),options.intersect&&!intersectsItem&&(items=[]),items}}},extend=helpers$1.extend;function filterByPosition(array,position){return helpers$1.where(array,(function(v){return v.pos===position}))}function sortByWeight(array,reverse){return array.sort((function(a,b){var v0=reverse?b:a,v1=reverse?a:b;return v0.weight===v1.weight?v0.index-v1.index:v0.weight-v1.weight}))}function getCombinedMax(maxPadding,chartArea,a,b){return Math.max(maxPadding[a],chartArea[a])+Math.max(maxPadding[b],chartArea[b])}function updateDims(chartArea,params,layout){var newWidth,newHeight,box=layout.box,maxPadding=chartArea.maxPadding;if(layout.size&&(chartArea[layout.pos]-=layout.size),layout.size=layout.horizontal?box.height:box.width,chartArea[layout.pos]+=layout.size,box.getPadding){var boxPadding=box.getPadding();maxPadding.top=Math.max(maxPadding.top,boxPadding.top),maxPadding.left=Math.max(maxPadding.left,boxPadding.left),maxPadding.bottom=Math.max(maxPadding.bottom,boxPadding.bottom),maxPadding.right=Math.max(maxPadding.right,boxPadding.right)}if(newWidth=params.outerWidth-getCombinedMax(maxPadding,chartArea,"left","right"),newHeight=params.outerHeight-getCombinedMax(maxPadding,chartArea,"top","bottom"),newWidth!==chartArea.w||newHeight!==chartArea.h){chartArea.w=newWidth,chartArea.h=newHeight;var sizes=layout.horizontal?[newWidth,chartArea.w]:[newHeight,chartArea.h];return!(sizes[0]===sizes[1]||isNaN(sizes[0])&&isNaN(sizes[1]))}}function getMargins(horizontal,chartArea){var maxPadding=chartArea.maxPadding;function marginForPositions(positions){var margin={left:0,top:0,right:0,bottom:0};return positions.forEach((function(pos){margin[pos]=Math.max(chartArea[pos],maxPadding[pos])})),margin}return marginForPositions(horizontal?["left","right"]:["top","bottom"])}function fitBoxes(boxes,chartArea,params){var i,ilen,layout,box,refit,changed,refitBoxes=[];for(i=0,ilen=boxes.length;i div {\r\n\tposition: absolute;\r\n\twidth: 1000000px;\r\n\theight: 1000000px;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n\r\n.chartjs-size-monitor-shrink > div {\r\n\tposition: absolute;\r\n\twidth: 200%;\r\n\theight: 200%;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n"}),stylesheet=(n=platform_dom$1)&&n.default||n,ANIMATION_START_EVENTS=["animationstart","webkitAnimationStart"],EVENT_TYPES={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function readUsedSize(element,property){var value=helpers$1.getStyle(element,property),matches=value&&value.match(/^(\d+)(\.\d+)?px$/);return matches?Number(matches[1]):void 0}var supportsEventListenerOptions=function(){var supports=!1;try{var options=Object.defineProperty({},"passive",{get:function(){supports=!0}});window.addEventListener("e",null,options)}catch(e){}return supports}(),eventListenerOptions=!!supportsEventListenerOptions&&{passive:!0};function addListener(node,type,listener){node.addEventListener(type,listener,eventListenerOptions)}function removeListener(node,type,listener){node.removeEventListener(type,listener,eventListenerOptions)}function createEvent(type,chart,x,y,nativeEvent){return{type:type,chart:chart,native:nativeEvent||null,x:void 0!==x?x:null,y:void 0!==y?y:null}}function createDiv(cls){var el=document.createElement("div");return el.className=cls||"",el}function addResizeListener(node,listener,chart){var fn,thisArg,ticking,args,expando=node.$chartjs||(node.$chartjs={}),resizer=expando.resizer=function(handler){var resizer=createDiv("chartjs-size-monitor"),expand=createDiv("chartjs-size-monitor-expand"),shrink=createDiv("chartjs-size-monitor-shrink");expand.appendChild(createDiv()),shrink.appendChild(createDiv()),resizer.appendChild(expand),resizer.appendChild(shrink),resizer._reset=function(){expand.scrollLeft=1e6,expand.scrollTop=1e6,shrink.scrollLeft=1e6,shrink.scrollTop=1e6};var onScroll=function(){resizer._reset(),handler()};return addListener(expand,"scroll",onScroll.bind(expand,"expand")),addListener(shrink,"scroll",onScroll.bind(shrink,"shrink")),resizer}((fn=function(){if(expando.resizer){var container=chart.options.maintainAspectRatio&&node.parentNode,w=container?container.clientWidth:0;listener(createEvent("resize",chart)),container&&container.clientWidth0){var item=tooltipItems[0];item.label?title=item.label:item.xLabel?title=item.xLabel:labelCount>0&&item.index-1?str.split("\n"):str}function getBaseModel(tooltipOpts){var globalDefaults=core_defaults.global;return{xPadding:tooltipOpts.xPadding,yPadding:tooltipOpts.yPadding,xAlign:tooltipOpts.xAlign,yAlign:tooltipOpts.yAlign,rtl:tooltipOpts.rtl,textDirection:tooltipOpts.textDirection,bodyFontColor:tooltipOpts.bodyFontColor,_bodyFontFamily:valueOrDefault$8(tooltipOpts.bodyFontFamily,globalDefaults.defaultFontFamily),_bodyFontStyle:valueOrDefault$8(tooltipOpts.bodyFontStyle,globalDefaults.defaultFontStyle),_bodyAlign:tooltipOpts.bodyAlign,bodyFontSize:valueOrDefault$8(tooltipOpts.bodyFontSize,globalDefaults.defaultFontSize),bodySpacing:tooltipOpts.bodySpacing,titleFontColor:tooltipOpts.titleFontColor,_titleFontFamily:valueOrDefault$8(tooltipOpts.titleFontFamily,globalDefaults.defaultFontFamily),_titleFontStyle:valueOrDefault$8(tooltipOpts.titleFontStyle,globalDefaults.defaultFontStyle),titleFontSize:valueOrDefault$8(tooltipOpts.titleFontSize,globalDefaults.defaultFontSize),_titleAlign:tooltipOpts.titleAlign,titleSpacing:tooltipOpts.titleSpacing,titleMarginBottom:tooltipOpts.titleMarginBottom,footerFontColor:tooltipOpts.footerFontColor,_footerFontFamily:valueOrDefault$8(tooltipOpts.footerFontFamily,globalDefaults.defaultFontFamily),_footerFontStyle:valueOrDefault$8(tooltipOpts.footerFontStyle,globalDefaults.defaultFontStyle),footerFontSize:valueOrDefault$8(tooltipOpts.footerFontSize,globalDefaults.defaultFontSize),_footerAlign:tooltipOpts.footerAlign,footerSpacing:tooltipOpts.footerSpacing,footerMarginTop:tooltipOpts.footerMarginTop,caretSize:tooltipOpts.caretSize,cornerRadius:tooltipOpts.cornerRadius,backgroundColor:tooltipOpts.backgroundColor,opacity:0,legendColorBackground:tooltipOpts.multiKeyBackground,displayColors:tooltipOpts.displayColors,borderColor:tooltipOpts.borderColor,borderWidth:tooltipOpts.borderWidth}}function getAlignedX(vm,align){return"center"===align?vm.x+vm.width/2:"right"===align?vm.x+vm.width-vm.xPadding:vm.x+vm.xPadding}function getBeforeAfterBodyLines(callback){return pushOrConcat([],splitNewlines(callback))}var exports$4=core_element.extend({initialize:function(){this._model=getBaseModel(this._options),this._lastActive=[]},getTitle:function(){var me=this,opts=me._options,callbacks=opts.callbacks,beforeTitle=callbacks.beforeTitle.apply(me,arguments),title=callbacks.title.apply(me,arguments),afterTitle=callbacks.afterTitle.apply(me,arguments),lines=[];return lines=pushOrConcat(lines,splitNewlines(beforeTitle)),lines=pushOrConcat(lines,splitNewlines(title)),lines=pushOrConcat(lines,splitNewlines(afterTitle))},getBeforeBody:function(){return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(tooltipItems,data){var me=this,callbacks=me._options.callbacks,bodyItems=[];return helpers$1.each(tooltipItems,(function(tooltipItem){var bodyItem={before:[],lines:[],after:[]};pushOrConcat(bodyItem.before,splitNewlines(callbacks.beforeLabel.call(me,tooltipItem,data))),pushOrConcat(bodyItem.lines,callbacks.label.call(me,tooltipItem,data)),pushOrConcat(bodyItem.after,splitNewlines(callbacks.afterLabel.call(me,tooltipItem,data))),bodyItems.push(bodyItem)})),bodyItems},getAfterBody:function(){return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var me=this,callbacks=me._options.callbacks,beforeFooter=callbacks.beforeFooter.apply(me,arguments),footer=callbacks.footer.apply(me,arguments),afterFooter=callbacks.afterFooter.apply(me,arguments),lines=[];return lines=pushOrConcat(lines,splitNewlines(beforeFooter)),lines=pushOrConcat(lines,splitNewlines(footer)),lines=pushOrConcat(lines,splitNewlines(afterFooter))},update:function(changed){var i,len,element,xScale,yScale,index,datasetIndex,controller,indexScale,valueScale,me=this,opts=me._options,existingModel=me._model,model=me._model=getBaseModel(opts),active=me._active,data=me._data,alignment={xAlign:existingModel.xAlign,yAlign:existingModel.yAlign},backgroundPoint={x:existingModel.x,y:existingModel.y},tooltipSize={width:existingModel.width,height:existingModel.height},tooltipPosition={x:existingModel.caretX,y:existingModel.caretY};if(active.length){model.opacity=1;var labelColors=[],labelTextColors=[];tooltipPosition=positioners[opts.position].call(me,active,me._eventPosition);var tooltipItems=[];for(i=0,len=active.length;ichart.height-size.height&&(yAlign="bottom");var midX=(chartArea.left+chartArea.right)/2,midY=(chartArea.top+chartArea.bottom)/2;"center"===yAlign?(lf=function(x){return x<=midX},rf=function(x){return x>midX}):(lf=function(x){return x<=size.width/2},rf=function(x){return x>=chart.width-size.width/2}),olf=function(x){return x+size.width+model.caretSize+model.caretPadding>chart.width},orf=function(x){return x-size.width-model.caretSize-model.caretPadding<0},yf=function(y){return y<=midY?"top":"bottom"},lf(model.x)?(xAlign="left",olf(model.x)&&(xAlign="center",yAlign=yf(model.y))):rf(model.x)&&(xAlign="right",orf(model.x)&&(xAlign="center",yAlign=yf(model.y)));var opts=tooltip._options;return{xAlign:opts.xAlign?opts.xAlign:xAlign,yAlign:opts.yAlign?opts.yAlign:yAlign}}(this,tooltipSize),backgroundPoint=function(vm,size,alignment,chart){var x=vm.x,y=vm.y,caretSize=vm.caretSize,caretPadding=vm.caretPadding,cornerRadius=vm.cornerRadius,xAlign=alignment.xAlign,yAlign=alignment.yAlign,paddingAndSize=caretSize+caretPadding,radiusAndPadding=cornerRadius+caretPadding;return"right"===xAlign?x-=size.width:"center"===xAlign&&((x-=size.width/2)+size.width>chart.width&&(x=chart.width-size.width),x<0&&(x=0)),"top"===yAlign?y+=paddingAndSize:y-="bottom"===yAlign?size.height+paddingAndSize:size.height/2,"center"===yAlign?"left"===xAlign?x+=paddingAndSize:"right"===xAlign&&(x-=paddingAndSize):"left"===xAlign?x-=radiusAndPadding:"right"===xAlign&&(x+=radiusAndPadding),{x:x,y:y}}(model,tooltipSize,alignment,me._chart)}else model.opacity=0;return model.xAlign=alignment.xAlign,model.yAlign=alignment.yAlign,model.x=backgroundPoint.x,model.y=backgroundPoint.y,model.width=tooltipSize.width,model.height=tooltipSize.height,model.caretX=tooltipPosition.x,model.caretY=tooltipPosition.y,me._model=model,changed&&opts.custom&&opts.custom.call(me,model),me},drawCaret:function(tooltipPoint,size){var ctx=this._chart.ctx,vm=this._view,caretPosition=this.getCaretPosition(tooltipPoint,size,vm);ctx.lineTo(caretPosition.x1,caretPosition.y1),ctx.lineTo(caretPosition.x2,caretPosition.y2),ctx.lineTo(caretPosition.x3,caretPosition.y3)},getCaretPosition:function(tooltipPoint,size,vm){var x1,x2,x3,y1,y2,y3,caretSize=vm.caretSize,cornerRadius=vm.cornerRadius,xAlign=vm.xAlign,yAlign=vm.yAlign,ptX=tooltipPoint.x,ptY=tooltipPoint.y,width=size.width,height=size.height;if("center"===yAlign)y2=ptY+height/2,"left"===xAlign?(x2=(x1=ptX)-caretSize,x3=x1,y1=y2+caretSize,y3=y2-caretSize):(x2=(x1=ptX+width)+caretSize,x3=x1,y1=y2-caretSize,y3=y2+caretSize);else if("left"===xAlign?(x1=(x2=ptX+cornerRadius+caretSize)-caretSize,x3=x2+caretSize):"right"===xAlign?(x1=(x2=ptX+width-cornerRadius-caretSize)-caretSize,x3=x2+caretSize):(x1=(x2=vm.caretX)-caretSize,x3=x2+caretSize),"top"===yAlign)y2=(y1=ptY)-caretSize,y3=y1;else{y2=(y1=ptY+height)+caretSize,y3=y1;var tmp=x3;x3=x1,x1=tmp}return{x1:x1,x2:x2,x3:x3,y1:y1,y2:y2,y3:y3}},drawTitle:function(pt,vm,ctx){var titleFontSize,titleSpacing,i,title=vm.title,length=title.length;if(length){var rtlHelper=getRtlHelper(vm.rtl,vm.x,vm.width);for(pt.x=getAlignedX(vm,vm._titleAlign),ctx.textAlign=rtlHelper.textAlign(vm._titleAlign),ctx.textBaseline="middle",titleFontSize=vm.titleFontSize,titleSpacing=vm.titleSpacing,ctx.fillStyle=vm.titleFontColor,ctx.font=helpers$1.fontString(titleFontSize,vm._titleFontStyle,vm._titleFontFamily),i=0;i0&&ctx.stroke()},draw:function(){var ctx=this._chart.ctx,vm=this._view;if(0!==vm.opacity){var tooltipSize={width:vm.width,height:vm.height},pt={x:vm.x,y:vm.y},opacity=Math.abs(vm.opacity<.001)?0:vm.opacity,hasTooltipContent=vm.title.length||vm.beforeBody.length||vm.body.length||vm.afterBody.length||vm.footer.length;this._options.enabled&&hasTooltipContent&&(ctx.save(),ctx.globalAlpha=opacity,this.drawBackground(pt,vm,ctx,tooltipSize),pt.y+=vm.yPadding,helpers$1.rtl.overrideTextDirection(ctx,vm.textDirection),this.drawTitle(pt,vm,ctx),this.drawBody(pt,vm,ctx),this.drawFooter(pt,vm,ctx),helpers$1.rtl.restoreTextDirection(ctx,vm.textDirection),ctx.restore())}},handleEvent:function(e){var changed,me=this,options=me._options;return me._lastActive=me._lastActive||[],"mouseout"===e.type?me._active=[]:(me._active=me._chart.getElementsAtEventForMode(e,options.mode,options),options.reverse&&me._active.reverse()),(changed=!helpers$1.arrayEquals(me._active,me._lastActive))&&(me._lastActive=me._active,(options.enabled||options.custom)&&(me._eventPosition={x:e.x,y:e.y},me.update(!0),me.pivot())),changed}}),positioners_1=positioners,core_tooltip=exports$4;core_tooltip.positioners=positioners_1;var valueOrDefault$9=helpers$1.valueOrDefault;function mergeScaleConfig(){return helpers$1.merge(Object.create(null),[].slice.call(arguments),{merger:function(key,target,source,options){if("xAxes"===key||"yAxes"===key){var i,type,scale,slen=source[key].length;for(target[key]||(target[key]=[]),i=0;i=target[key].length&&target[key].push({}),!target[key][i].type||scale.type&&scale.type!==target[key][i].type?helpers$1.merge(target[key][i],[core_scaleService.getScaleDefaults(type),scale]):helpers$1.merge(target[key][i],scale)}else helpers$1._merger(key,target,source,options)}})}function mergeConfig(){return helpers$1.merge(Object.create(null),[].slice.call(arguments),{merger:function(key,target,source,options){var tval=target[key]||Object.create(null),sval=source[key];"scales"===key?target[key]=mergeScaleConfig(tval,sval):"scale"===key?target[key]=helpers$1.merge(tval,[core_scaleService.getScaleDefaults(sval.type),sval]):helpers$1._merger(key,target,source,options)}})}function updateConfig(chart){var newOptions=chart.options;helpers$1.each(chart.scales,(function(scale){core_layouts.removeBox(chart,scale)})),newOptions=mergeConfig(core_defaults.global,core_defaults[chart.config.type],newOptions),chart.options=chart.config.options=newOptions,chart.ensureScalesHaveIDs(),chart.buildOrUpdateScales(),chart.tooltip._options=newOptions.tooltips,chart.tooltip.initialize()}function nextAvailableScaleId(axesOpts,prefix,index){var id,hasId=function(obj){return obj.id===id};do{id=prefix+index++}while(helpers$1.findIndex(axesOpts,hasId)>=0);return id}function positionIsHorizontal(position){return"top"===position||"bottom"===position}function compare2Level(l1,l2){return function(a,b){return a[l1]===b[l1]?a[l2]-b[l2]:a[l1]-b[l1]}}core_defaults._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 Chart=function(item,config){return this.construct(item,config),this};helpers$1.extend(Chart.prototype,{construct:function(item,config){var me=this;config=function(config){var data=(config=config||Object.create(null)).data=config.data||{};return data.datasets=data.datasets||[],data.labels=data.labels||[],config.options=mergeConfig(core_defaults.global,core_defaults[config.type],config.options||{}),config}(config);var context=platform.acquireContext(item,config),canvas=context&&context.canvas,height=canvas&&canvas.height,width=canvas&&canvas.width;me.id=helpers$1.uid(),me.ctx=context,me.canvas=canvas,me.config=config,me.width=width,me.height=height,me.aspectRatio=height?width/height:null,me.options=config.options,me._bufferedRender=!1,me._layers=[],me.chart=me,me.controller=me,Chart.instances[me.id]=me,Object.defineProperty(me,"data",{get:function(){return me.config.data},set:function(value){me.config.data=value}}),context&&canvas?(me.initialize(),me.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var me=this;return core_plugins.notify(me,"beforeInit"),helpers$1.retinaScale(me,me.options.devicePixelRatio),me.bindEvents(),me.options.responsive&&me.resize(!0),me.initToolTip(),core_plugins.notify(me,"afterInit"),me},clear:function(){return helpers$1.canvas.clear(this),this},stop:function(){return core_animations.cancelAnimation(this),this},resize:function(silent){var me=this,options=me.options,canvas=me.canvas,aspectRatio=options.maintainAspectRatio&&me.aspectRatio||null,newWidth=Math.max(0,Math.floor(helpers$1.getMaximumWidth(canvas))),newHeight=Math.max(0,Math.floor(aspectRatio?newWidth/aspectRatio:helpers$1.getMaximumHeight(canvas)));if((me.width!==newWidth||me.height!==newHeight)&&(canvas.width=me.width=newWidth,canvas.height=me.height=newHeight,canvas.style.width=newWidth+"px",canvas.style.height=newHeight+"px",helpers$1.retinaScale(me,options.devicePixelRatio),!silent)){var newSize={width:newWidth,height:newHeight};core_plugins.notify(me,"resize",[newSize]),options.onResize&&options.onResize(me,newSize),me.stop(),me.update({duration:options.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var options=this.options,scalesOptions=options.scales||{},scaleOptions=options.scale;helpers$1.each(scalesOptions.xAxes,(function(xAxisOptions,index){xAxisOptions.id||(xAxisOptions.id=nextAvailableScaleId(scalesOptions.xAxes,"x-axis-",index))})),helpers$1.each(scalesOptions.yAxes,(function(yAxisOptions,index){yAxisOptions.id||(yAxisOptions.id=nextAvailableScaleId(scalesOptions.yAxes,"y-axis-",index))})),scaleOptions&&(scaleOptions.id=scaleOptions.id||"scale")},buildOrUpdateScales:function(){var me=this,options=me.options,scales=me.scales||{},items=[],updated=Object.keys(scales).reduce((function(obj,id){return obj[id]=!1,obj}),{});options.scales&&(items=items.concat((options.scales.xAxes||[]).map((function(xAxisOptions){return{options:xAxisOptions,dtype:"category",dposition:"bottom"}})),(options.scales.yAxes||[]).map((function(yAxisOptions){return{options:yAxisOptions,dtype:"linear",dposition:"left"}})))),options.scale&&items.push({options:options.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),helpers$1.each(items,(function(item){var scaleOptions=item.options,id=scaleOptions.id,scaleType=valueOrDefault$9(scaleOptions.type,item.dtype);positionIsHorizontal(scaleOptions.position)!==positionIsHorizontal(item.dposition)&&(scaleOptions.position=item.dposition),updated[id]=!0;var scale=null;if(id in scales&&scales[id].type===scaleType)(scale=scales[id]).options=scaleOptions,scale.ctx=me.ctx,scale.chart=me;else{var scaleClass=core_scaleService.getScaleConstructor(scaleType);if(!scaleClass)return;scale=new scaleClass({id:id,type:scaleType,options:scaleOptions,ctx:me.ctx,chart:me}),scales[scale.id]=scale}scale.mergeTicksOptions(),item.isDefault&&(me.scale=scale)})),helpers$1.each(updated,(function(hasUpdated,id){hasUpdated||delete scales[id]})),me.scales=scales,core_scaleService.addScalesToLayout(this)},buildOrUpdateControllers:function(){var i,ilen,me=this,newControllers=[],datasets=me.data.datasets;for(i=0,ilen=datasets.length;i=0;--i)this.drawDataset(metasets[i],easingValue);core_plugins.notify(this,"afterDatasetsDraw",[easingValue])}},drawDataset:function(meta,easingValue){var args={meta:meta,index:meta.index,easingValue:easingValue};!1!==core_plugins.notify(this,"beforeDatasetDraw",[args])&&(meta.controller.draw(easingValue),core_plugins.notify(this,"afterDatasetDraw",[args]))},_drawTooltip:function(easingValue){var tooltip=this.tooltip,args={tooltip:tooltip,easingValue:easingValue};!1!==core_plugins.notify(this,"beforeTooltipDraw",[args])&&(tooltip.draw(),core_plugins.notify(this,"afterTooltipDraw",[args]))},getElementAtEvent:function(e){return core_interaction.modes.single(this,e)},getElementsAtEvent:function(e){return core_interaction.modes.label(this,e,{intersect:!0})},getElementsAtXAxis:function(e){return core_interaction.modes["x-axis"](this,e,{intersect:!0})},getElementsAtEventForMode:function(e,mode,options){var method=core_interaction.modes[mode];return"function"==typeof method?method(this,e,options):[]},getDatasetAtEvent:function(e){return core_interaction.modes.dataset(this,e,{intersect:!0})},getDatasetMeta:function(datasetIndex){var dataset=this.data.datasets[datasetIndex];dataset._meta||(dataset._meta={});var meta=dataset._meta[this.id];return meta||(meta=dataset._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:dataset.order||0,index:datasetIndex}),meta},getVisibleDatasetCount:function(){for(var count=0,i=0,ilen=this.data.datasets.length;i3?ticks[2]-ticks[1]:ticks[1]-ticks[0];Math.abs(delta)>1&&tickValue!==Math.floor(tickValue)&&(delta=tickValue-Math.floor(tickValue));var logDelta=helpers$1.log10(Math.abs(delta)),tickString="";if(0!==tickValue)if(Math.max(Math.abs(ticks[0]),Math.abs(ticks[ticks.length-1]))<1e-4){var logTick=helpers$1.log10(Math.abs(tickValue)),numExponential=Math.floor(logTick)-Math.floor(logDelta);numExponential=Math.max(Math.min(numExponential,20),0),tickString=tickValue.toExponential(numExponential)}else{var numDecimal=-1*Math.floor(logDelta);numDecimal=Math.max(Math.min(numDecimal,20),0),tickString=tickValue.toFixed(numDecimal)}else tickString="0";return tickString},logarithmic:function(tickValue,index,ticks){var remain=tickValue/Math.pow(10,Math.floor(helpers$1.log10(tickValue)));return 0===tickValue?"0":1===remain||2===remain||5===remain||0===index||index===ticks.length-1?tickValue.toExponential():""}}},isArray=helpers$1.isArray,isNullOrUndef=helpers$1.isNullOrUndef,valueOrDefault$a=helpers$1.valueOrDefault,valueAtIndexOrDefault=helpers$1.valueAtIndexOrDefault;function getPixelForGridLine(scale,index,offsetGridLines){var offset,length=scale.getTicks().length,validIndex=Math.min(index,length-1),lineValue=scale.getPixelForTick(validIndex),start=scale._startPixel,end=scale._endPixel;if(!(offsetGridLines&&(offset=1===length?Math.max(lineValue-start,end-lineValue):0===index?(scale.getPixelForTick(1)-lineValue)/2:(lineValue-scale.getPixelForTick(validIndex-1))/2,(lineValue+=validIndexend+1e-6)))return lineValue}function computeLabelSizes(ctx,tickFonts,ticks,caches){var i,j,jlen,label,tickFont,fontString,cache,lineHeight,width,height,nestedLabel,widest,highest,length=ticks.length,widths=[],heights=[],offsets=[],widestLabelSize=0,highestLabelSize=0;for(i=0;ilength){for(i=0;i=maxRotation||numTicks<=1||!me.isHorizontal()?me.labelRotation=minRotation:(maxLabelWidth=(labelSizes=me._getLabelSizes()).widest.width,maxLabelHeight=labelSizes.highest.height-labelSizes.highest.offset,maxWidth=Math.min(me.maxWidth,me.chart.width-maxLabelWidth),maxLabelWidth+6>(tickWidth=options.offset?me.maxWidth/numTicks:maxWidth/(numTicks-1))&&(tickWidth=maxWidth/(numTicks-(options.offset?.5:1)),maxHeight=me.maxHeight-getTickMarkLength(options.gridLines)-tickOpts.padding-getScaleLabelHeight(options.scaleLabel),maxLabelDiagonal=Math.sqrt(maxLabelWidth*maxLabelWidth+maxLabelHeight*maxLabelHeight),labelRotation=helpers$1.toDegrees(Math.min(Math.asin(Math.min((labelSizes.highest.height+6)/tickWidth,1)),Math.asin(Math.min(maxHeight/maxLabelDiagonal,1))-Math.asin(maxLabelHeight/maxLabelDiagonal))),labelRotation=Math.max(minRotation,Math.min(maxRotation,labelRotation))),me.labelRotation=labelRotation)},afterCalculateTickRotation:function(){helpers$1.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){helpers$1.callback(this.options.beforeFit,[this])},fit:function(){var me=this,minSize=me.minSize={width:0,height:0},chart=me.chart,opts=me.options,tickOpts=opts.ticks,scaleLabelOpts=opts.scaleLabel,gridLineOpts=opts.gridLines,display=me._isVisible(),isBottom="bottom"===opts.position,isHorizontal=me.isHorizontal();if(isHorizontal?minSize.width=me.maxWidth:display&&(minSize.width=getTickMarkLength(gridLineOpts)+getScaleLabelHeight(scaleLabelOpts)),isHorizontal?display&&(minSize.height=getTickMarkLength(gridLineOpts)+getScaleLabelHeight(scaleLabelOpts)):minSize.height=me.maxHeight,tickOpts.display&&display){var tickFonts=parseTickFontOptions(tickOpts),labelSizes=me._getLabelSizes(),firstLabelSize=labelSizes.first,lastLabelSize=labelSizes.last,widestLabelSize=labelSizes.widest,highestLabelSize=labelSizes.highest,lineSpace=.4*tickFonts.minor.lineHeight,tickPadding=tickOpts.padding;if(isHorizontal){var isRotated=0!==me.labelRotation,angleRadians=helpers$1.toRadians(me.labelRotation),cosRotation=Math.cos(angleRadians),sinRotation=Math.sin(angleRadians),labelHeight=sinRotation*widestLabelSize.width+cosRotation*(highestLabelSize.height-(isRotated?highestLabelSize.offset:0))+(isRotated?0:lineSpace);minSize.height=Math.min(me.maxHeight,minSize.height+labelHeight+tickPadding);var paddingLeft,paddingRight,offsetLeft=me.getPixelForTick(0)-me.left,offsetRight=me.right-me.getPixelForTick(me.getTicks().length-1);isRotated?(paddingLeft=isBottom?cosRotation*firstLabelSize.width+sinRotation*firstLabelSize.offset:sinRotation*(firstLabelSize.height-firstLabelSize.offset),paddingRight=isBottom?sinRotation*(lastLabelSize.height-lastLabelSize.offset):cosRotation*lastLabelSize.width+sinRotation*lastLabelSize.offset):(paddingLeft=firstLabelSize.width/2,paddingRight=lastLabelSize.width/2),me.paddingLeft=Math.max((paddingLeft-offsetLeft)*me.width/(me.width-offsetLeft),0)+3,me.paddingRight=Math.max((paddingRight-offsetRight)*me.width/(me.width-offsetRight),0)+3}else{var labelWidth=tickOpts.mirror?0:widestLabelSize.width+tickPadding+lineSpace;minSize.width=Math.min(me.maxWidth,minSize.width+labelWidth),me.paddingTop=firstLabelSize.height/2,me.paddingBottom=lastLabelSize.height/2}}me.handleMargins(),isHorizontal?(me.width=me._length=chart.width-me.margins.left-me.margins.right,me.height=minSize.height):(me.width=minSize.width,me.height=me._length=chart.height-me.margins.top-me.margins.bottom)},handleMargins:function(){var me=this;me.margins&&(me.margins.left=Math.max(me.paddingLeft,me.margins.left),me.margins.top=Math.max(me.paddingTop,me.margins.top),me.margins.right=Math.max(me.paddingRight,me.margins.right),me.margins.bottom=Math.max(me.paddingBottom,me.margins.bottom))},afterFit:function(){helpers$1.callback(this.options.afterFit,[this])},isHorizontal:function(){var pos=this.options.position;return"top"===pos||"bottom"===pos},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(rawValue){if(isNullOrUndef(rawValue))return NaN;if(("number"==typeof rawValue||rawValue instanceof Number)&&!isFinite(rawValue))return NaN;if(rawValue)if(this.isHorizontal()){if(void 0!==rawValue.x)return this.getRightValue(rawValue.x)}else if(void 0!==rawValue.y)return this.getRightValue(rawValue.y);return rawValue},_convertTicksToLabels:function(ticks){var labels,i,ilen,me=this;for(me.ticks=ticks.map((function(tick){return tick.value})),me.beforeTickToLabelConversion(),labels=me.convertTicksToLabels(ticks)||me.ticks,me.afterTickToLabelConversion(),i=0,ilen=ticks.length;inumTicks-1?null:this.getPixelForDecimal(index*tickWidth+(offset?tickWidth/2:0))},getPixelForDecimal:function(decimal){return this._reversePixels&&(decimal=1-decimal),this._startPixel+decimal*this._length},getDecimalForPixel:function(pixel){var decimal=(pixel-this._startPixel)/this._length;return this._reversePixels?1-decimal:decimal},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var min=this.min,max=this.max;return this.beginAtZero?0:min<0&&max<0?max:min>0&&max>0?min:0},_autoSkip:function(ticks){var i,ilen,spacing,avgMajorSpacing,tickOpts=this.options.ticks,axisLength=this._length,ticksLimit=tickOpts.maxTicksLimit||axisLength/this._tickSize()+1,majorIndices=tickOpts.major.enabled?function(ticks){var i,ilen,result=[];for(i=0,ilen=ticks.length;iticksLimit)return function(ticks,majorIndices,spacing){var i,tick,count=0,next=majorIndices[0];for(spacing=Math.ceil(spacing),i=0;ispacing)return factor;return Math.max(spacing,1)}(majorIndices,ticks,0,ticksLimit),numMajorIndices>0){for(i=0,ilen=numMajorIndices-1;i1?(last-first)/(numMajorIndices-1):null,skip(ticks,spacing,helpers$1.isNullOrUndef(avgMajorSpacing)?0:first-avgMajorSpacing,first),skip(ticks,spacing,last,helpers$1.isNullOrUndef(avgMajorSpacing)?ticks.length:last+avgMajorSpacing),nonSkipped(ticks)}return skip(ticks,spacing),nonSkipped(ticks)},_tickSize:function(){var optionTicks=this.options.ticks,rot=helpers$1.toRadians(this.labelRotation),cos=Math.abs(Math.cos(rot)),sin=Math.abs(Math.sin(rot)),labelSizes=this._getLabelSizes(),padding=optionTicks.autoSkipPadding||0,w=labelSizes?labelSizes.widest.width+padding:0,h=labelSizes?labelSizes.highest.height+padding:0;return this.isHorizontal()?h*cos>w*sin?w/cos:h/sin:h*sin=0&&(minIndex=findIndex),void 0!==max&&(findIndex=labels.indexOf(max))>=0&&(maxIndex=findIndex),me.minIndex=minIndex,me.maxIndex=maxIndex,me.min=labels[minIndex],me.max=labels[maxIndex]},buildTicks:function(){var labels=this._getLabels(),minIndex=this.minIndex,maxIndex=this.maxIndex;this.ticks=0===minIndex&&maxIndex===labels.length-1?labels:labels.slice(minIndex,maxIndex+1)},getLabelForIndex:function(index,datasetIndex){var chart=this.chart;return chart.getDatasetMeta(datasetIndex).controller._getValueScaleId()===this.id?this.getRightValue(chart.data.datasets[datasetIndex].data[index]):this._getLabels()[index]},_configure:function(){var me=this,offset=me.options.offset,ticks=me.ticks;core_scale.prototype._configure.call(me),me.isHorizontal()||(me._reversePixels=!me._reversePixels),ticks&&(me._startValue=me.minIndex-(offset?.5:0),me._valueRange=Math.max(ticks.length-(offset?0:1),1))},getPixelForValue:function(value,index,datasetIndex){var valueCategory,labels,idx,me=this;return isNullOrUndef$1(index)||isNullOrUndef$1(datasetIndex)||(value=me.chart.data.datasets[datasetIndex].data[index]),isNullOrUndef$1(value)||(valueCategory=me.isHorizontal()?value.x:value.y),(void 0!==valueCategory||void 0!==value&&isNaN(index))&&(labels=me._getLabels(),value=helpers$1.valueOrDefault(valueCategory,value),index=-1!==(idx=labels.indexOf(value))?idx:index,isNaN(index)&&(index=value)),me.getPixelForDecimal((index-me._startValue)/me._valueRange)},getPixelForTick:function(index){var ticks=this.ticks;return index<0||index>ticks.length-1?null:this.getPixelForValue(ticks[index],index+this.minIndex)},getValueForPixel:function(pixel){var value=Math.round(this._startValue+this.getDecimalForPixel(pixel)*this._valueRange);return Math.min(Math.max(value,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),_defaults={position:"bottom"};scale_category._defaults=_defaults;var noop=helpers$1.noop,isNullOrUndef$2=helpers$1.isNullOrUndef;var scale_linearbase=core_scale.extend({getRightValue:function(value){return"string"==typeof value?+value:core_scale.prototype.getRightValue.call(this,value)},handleTickRangeOptions:function(){var me=this,tickOpts=me.options.ticks;if(tickOpts.beginAtZero){var minSign=helpers$1.sign(me.min),maxSign=helpers$1.sign(me.max);minSign<0&&maxSign<0?me.max=0:minSign>0&&maxSign>0&&(me.min=0)}var setMin=void 0!==tickOpts.min||void 0!==tickOpts.suggestedMin,setMax=void 0!==tickOpts.max||void 0!==tickOpts.suggestedMax;void 0!==tickOpts.min?me.min=tickOpts.min:void 0!==tickOpts.suggestedMin&&(null===me.min?me.min=tickOpts.suggestedMin:me.min=Math.min(me.min,tickOpts.suggestedMin)),void 0!==tickOpts.max?me.max=tickOpts.max:void 0!==tickOpts.suggestedMax&&(null===me.max?me.max=tickOpts.suggestedMax:me.max=Math.max(me.max,tickOpts.suggestedMax)),setMin!==setMax&&me.min>=me.max&&(setMin?me.max=me.min+1:me.min=me.max-1),me.min===me.max&&(me.max++,tickOpts.beginAtZero||me.min--)},getTickLimit:function(){var maxTicks,tickOpts=this.options.ticks,stepSize=tickOpts.stepSize,maxTicksLimit=tickOpts.maxTicksLimit;return stepSize?maxTicks=Math.ceil(this.max/stepSize)-Math.floor(this.min/stepSize)+1:(maxTicks=this._computeTickLimit(),maxTicksLimit=maxTicksLimit||11),maxTicksLimit&&(maxTicks=Math.min(maxTicksLimit,maxTicks)),maxTicks},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:noop,buildTicks:function(){var me=this,tickOpts=me.options.ticks,maxTicks=me.getTickLimit(),numericGeneratorOptions={maxTicks:maxTicks=Math.max(2,maxTicks),min:tickOpts.min,max:tickOpts.max,precision:tickOpts.precision,stepSize:helpers$1.valueOrDefault(tickOpts.fixedStepSize,tickOpts.stepSize)},ticks=me.ticks=function(generationOptions,dataRange){var factor,niceMin,niceMax,numSpaces,ticks=[],stepSize=generationOptions.stepSize,unit=stepSize||1,maxNumSpaces=generationOptions.maxTicks-1,min=generationOptions.min,max=generationOptions.max,precision=generationOptions.precision,rmin=dataRange.min,rmax=dataRange.max,spacing=helpers$1.niceNum((rmax-rmin)/maxNumSpaces/unit)*unit;if(spacing<1e-14&&isNullOrUndef$2(min)&&isNullOrUndef$2(max))return[rmin,rmax];(numSpaces=Math.ceil(rmax/spacing)-Math.floor(rmin/spacing))>maxNumSpaces&&(spacing=helpers$1.niceNum(numSpaces*spacing/maxNumSpaces/unit)*unit),stepSize||isNullOrUndef$2(precision)?factor=Math.pow(10,helpers$1._decimalPlaces(spacing)):(factor=Math.pow(10,precision),spacing=Math.ceil(spacing*factor)/factor),niceMin=Math.floor(rmin/spacing)*spacing,niceMax=Math.ceil(rmax/spacing)*spacing,stepSize&&(!isNullOrUndef$2(min)&&helpers$1.almostWhole(min/spacing,spacing/1e3)&&(niceMin=min),!isNullOrUndef$2(max)&&helpers$1.almostWhole(max/spacing,spacing/1e3)&&(niceMax=max)),numSpaces=(niceMax-niceMin)/spacing,numSpaces=helpers$1.almostEquals(numSpaces,Math.round(numSpaces),spacing/1e3)?Math.round(numSpaces):Math.ceil(numSpaces),niceMin=Math.round(niceMin*factor)/factor,niceMax=Math.round(niceMax*factor)/factor,ticks.push(isNullOrUndef$2(min)?niceMin:min);for(var j=1;jticks.length-1?null:this.getPixelForValue(ticks[index])}}),_defaults$1=defaultConfig$1;scale_linear._defaults=_defaults$1;var valueOrDefault$b=helpers$1.valueOrDefault,log10=helpers$1.math.log10;var defaultConfig$2={position:"left",ticks:{callback:core_ticks.formatters.logarithmic}};function nonNegativeOrDefault(value,defaultValue){return helpers$1.isFinite(value)&&value>=0?value:defaultValue}var scale_logarithmic=core_scale.extend({determineDataLimits:function(){var datasetIndex,meta,value,data,i,ilen,me=this,opts=me.options,chart=me.chart,datasets=chart.data.datasets,isHorizontal=me.isHorizontal();function IDMatches(meta){return isHorizontal?meta.xAxisID===me.id:meta.yAxisID===me.id}me.min=Number.POSITIVE_INFINITY,me.max=Number.NEGATIVE_INFINITY,me.minNotZero=Number.POSITIVE_INFINITY;var hasStacks=opts.stacked;if(void 0===hasStacks)for(datasetIndex=0;datasetIndex0){var minVal=helpers$1.min(valuesForType),maxVal=helpers$1.max(valuesForType);me.min=Math.min(me.min,minVal),me.max=Math.max(me.max,maxVal)}}))}else for(datasetIndex=0;datasetIndex0?me.minNotZero=me.min:me.max<1?me.minNotZero=Math.pow(10,Math.floor(log10(me.max))):me.minNotZero=1)},buildTicks:function(){var me=this,tickOpts=me.options.ticks,reverse=!me.isHorizontal(),generationOptions={min:nonNegativeOrDefault(tickOpts.min),max:nonNegativeOrDefault(tickOpts.max)},ticks=me.ticks=function(generationOptions,dataRange){var exp,significand,ticks=[],tickVal=valueOrDefault$b(generationOptions.min,Math.pow(10,Math.floor(log10(dataRange.min)))),endExp=Math.floor(log10(dataRange.max)),endSignificand=Math.ceil(dataRange.max/Math.pow(10,endExp));0===tickVal?(exp=Math.floor(log10(dataRange.minNotZero)),significand=Math.floor(dataRange.minNotZero/Math.pow(10,exp)),ticks.push(tickVal),tickVal=significand*Math.pow(10,exp)):(exp=Math.floor(log10(tickVal)),significand=Math.floor(tickVal/Math.pow(10,exp)));var precision=exp<0?Math.pow(10,Math.abs(exp)):1;do{ticks.push(tickVal),10==++significand&&(significand=1,precision=++exp>=0?1:precision),tickVal=Math.round(significand*Math.pow(10,exp)*precision)/precision}while(expticks.length-1?null:this.getPixelForValue(ticks[index])},_getFirstTickValue:function(value){var exp=Math.floor(log10(value));return Math.floor(value/Math.pow(10,exp))*Math.pow(10,exp)},_configure:function(){var me=this,start=me.min,offset=0;core_scale.prototype._configure.call(me),0===start&&(start=me._getFirstTickValue(me.minNotZero),offset=valueOrDefault$b(me.options.ticks.fontSize,core_defaults.global.defaultFontSize)/me._length),me._startValue=log10(start),me._valueOffset=offset,me._valueRange=(log10(me.max)-log10(start))/(1-offset)},getPixelForValue:function(value){var me=this,decimal=0;return(value=+me.getRightValue(value))>me.min&&value>0&&(decimal=(log10(value)-me._startValue)/me._valueRange+me._valueOffset),me.getPixelForDecimal(decimal)},getValueForPixel:function(pixel){var me=this,decimal=me.getDecimalForPixel(pixel);return 0===decimal&&0===me.min?0:Math.pow(10,me._startValue+(decimal-me._valueOffset)*me._valueRange)}}),_defaults$2=defaultConfig$2;scale_logarithmic._defaults=_defaults$2;var valueOrDefault$c=helpers$1.valueOrDefault,valueAtIndexOrDefault$1=helpers$1.valueAtIndexOrDefault,resolve$4=helpers$1.options.resolve,defaultConfig$3={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:core_ticks.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(label){return label}}};function getTickBackdropHeight(opts){var tickOpts=opts.ticks;return tickOpts.display&&opts.display?valueOrDefault$c(tickOpts.fontSize,core_defaults.global.defaultFontSize)+2*tickOpts.backdropPaddingY:0}function determineLimits(angle,pos,size,min,max){return angle===min||angle===max?{start:pos-size/2,end:pos+size/2}:anglemax?{start:pos-size,end:pos}:{start:pos,end:pos+size}}function getTextAlignForAngle(angle){return 0===angle||180===angle?"center":angle<180?"left":"right"}function fillText(ctx,text,position,lineHeight){var i,ilen,y=position.y+lineHeight/2;if(helpers$1.isArray(text))for(i=0,ilen=text.length;i270||angle<90)&&(position.y-=textSize.h)}function numberOrZero(param){return helpers$1.isNumber(param)?param:0}var scale_radialLinear=scale_linearbase.extend({setDimensions:function(){var me=this;me.width=me.maxWidth,me.height=me.maxHeight,me.paddingTop=getTickBackdropHeight(me.options)/2,me.xCenter=Math.floor(me.width/2),me.yCenter=Math.floor((me.height-me.paddingTop)/2),me.drawingArea=Math.min(me.height-me.paddingTop,me.width)/2},determineDataLimits:function(){var me=this,chart=me.chart,min=Number.POSITIVE_INFINITY,max=Number.NEGATIVE_INFINITY;helpers$1.each(chart.data.datasets,(function(dataset,datasetIndex){if(chart.isDatasetVisible(datasetIndex)){var meta=chart.getDatasetMeta(datasetIndex);helpers$1.each(dataset.data,(function(rawValue,index){var value=+me.getRightValue(rawValue);isNaN(value)||meta.data[index].hidden||(min=Math.min(value,min),max=Math.max(value,max))}))}})),me.min=min===Number.POSITIVE_INFINITY?0:min,me.max=max===Number.NEGATIVE_INFINITY?0:max,me.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/getTickBackdropHeight(this.options))},convertTicksToLabels:function(){var me=this;scale_linearbase.prototype.convertTicksToLabels.call(me),me.pointLabels=me.chart.data.labels.map((function(){var label=helpers$1.callback(me.options.pointLabels.callback,arguments,me);return label||0===label?label:""}))},getLabelForIndex:function(index,datasetIndex){return+this.getRightValue(this.chart.data.datasets[datasetIndex].data[index])},fit:function(){var opts=this.options;opts.display&&opts.pointLabels.display?function(scale){var i,textSize,pointPosition,plFont=helpers$1.options._parseFont(scale.options.pointLabels),furthestLimits={l:0,r:scale.width,t:0,b:scale.height-scale.paddingTop},furthestAngles={};scale.ctx.font=plFont.string,scale._pointLabelSizes=[];var ctx,lineHeight,label,valueCount=scale.chart.data.labels.length;for(i=0;ifurthestLimits.r&&(furthestLimits.r=hLimits.end,furthestAngles.r=angleRadians),vLimits.startfurthestLimits.b&&(furthestLimits.b=vLimits.end,furthestAngles.b=angleRadians)}scale.setReductions(scale.drawingArea,furthestLimits,furthestAngles)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(largestPossibleRadius,furthestLimits,furthestAngles){var me=this,radiusReductionLeft=furthestLimits.l/Math.sin(furthestAngles.l),radiusReductionRight=Math.max(furthestLimits.r-me.width,0)/Math.sin(furthestAngles.r),radiusReductionTop=-furthestLimits.t/Math.cos(furthestAngles.t),radiusReductionBottom=-Math.max(furthestLimits.b-(me.height-me.paddingTop),0)/Math.cos(furthestAngles.b);radiusReductionLeft=numberOrZero(radiusReductionLeft),radiusReductionRight=numberOrZero(radiusReductionRight),radiusReductionTop=numberOrZero(radiusReductionTop),radiusReductionBottom=numberOrZero(radiusReductionBottom),me.drawingArea=Math.min(Math.floor(largestPossibleRadius-(radiusReductionLeft+radiusReductionRight)/2),Math.floor(largestPossibleRadius-(radiusReductionTop+radiusReductionBottom)/2)),me.setCenterPoint(radiusReductionLeft,radiusReductionRight,radiusReductionTop,radiusReductionBottom)},setCenterPoint:function(leftMovement,rightMovement,topMovement,bottomMovement){var me=this,maxRight=me.width-rightMovement-me.drawingArea,maxLeft=leftMovement+me.drawingArea,maxTop=topMovement+me.drawingArea,maxBottom=me.height-me.paddingTop-bottomMovement-me.drawingArea;me.xCenter=Math.floor((maxLeft+maxRight)/2+me.left),me.yCenter=Math.floor((maxTop+maxBottom)/2+me.top+me.paddingTop)},getIndexAngle:function(index){var chart=this.chart,angle=(index*(360/chart.data.labels.length)+((chart.options||{}).startAngle||0))%360;return(angle<0?angle+360:angle)*Math.PI*2/360},getDistanceFromCenterForValue:function(value){var me=this;if(helpers$1.isNullOrUndef(value))return NaN;var scalingFactor=me.drawingArea/(me.max-me.min);return me.options.ticks.reverse?(me.max-value)*scalingFactor:(value-me.min)*scalingFactor},getPointPosition:function(index,distanceFromCenter){var thisAngle=this.getIndexAngle(index)-Math.PI/2;return{x:Math.cos(thisAngle)*distanceFromCenter+this.xCenter,y:Math.sin(thisAngle)*distanceFromCenter+this.yCenter}},getPointPositionForValue:function(index,value){return this.getPointPosition(index,this.getDistanceFromCenterForValue(value))},getBasePosition:function(index){var min=this.min,max=this.max;return this.getPointPositionForValue(index||0,this.beginAtZero?0:min<0&&max<0?max:min>0&&max>0?min:0)},_drawGrid:function(){var i,offset,position,me=this,ctx=me.ctx,opts=me.options,gridLineOpts=opts.gridLines,angleLineOpts=opts.angleLines,lineWidth=valueOrDefault$c(angleLineOpts.lineWidth,gridLineOpts.lineWidth),lineColor=valueOrDefault$c(angleLineOpts.color,gridLineOpts.color);if(opts.pointLabels.display&&function(scale){var ctx=scale.ctx,opts=scale.options,pointLabelOpts=opts.pointLabels,tickBackdropHeight=getTickBackdropHeight(opts),outerDistance=scale.getDistanceFromCenterForValue(opts.ticks.reverse?scale.min:scale.max),plFont=helpers$1.options._parseFont(pointLabelOpts);ctx.save(),ctx.font=plFont.string,ctx.textBaseline="middle";for(var i=scale.chart.data.labels.length-1;i>=0;i--){var extra=0===i?tickBackdropHeight/2:0,pointLabelPosition=scale.getPointPosition(i,outerDistance+extra+5),pointLabelFontColor=valueAtIndexOrDefault$1(pointLabelOpts.fontColor,i,core_defaults.global.defaultFontColor);ctx.fillStyle=pointLabelFontColor;var angleRadians=scale.getIndexAngle(i),angle=helpers$1.toDegrees(angleRadians);ctx.textAlign=getTextAlignForAngle(angle),adjustPointPositionForLabelHeight(angle,scale._pointLabelSizes[i],pointLabelPosition),fillText(ctx,scale.pointLabels[i],pointLabelPosition,plFont.lineHeight)}ctx.restore()}(me),gridLineOpts.display&&helpers$1.each(me.ticks,(function(label,index){0!==index&&(offset=me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]),function(scale,gridLineOpts,radius,index){var pointPosition,ctx=scale.ctx,circular=gridLineOpts.circular,valueCount=scale.chart.data.labels.length,lineColor=valueAtIndexOrDefault$1(gridLineOpts.color,index-1),lineWidth=valueAtIndexOrDefault$1(gridLineOpts.lineWidth,index-1);if((circular||valueCount)&&lineColor&&lineWidth){if(ctx.save(),ctx.strokeStyle=lineColor,ctx.lineWidth=lineWidth,ctx.setLineDash&&(ctx.setLineDash(gridLineOpts.borderDash||[]),ctx.lineDashOffset=gridLineOpts.borderDashOffset||0),ctx.beginPath(),circular)ctx.arc(scale.xCenter,scale.yCenter,radius,0,2*Math.PI);else{pointPosition=scale.getPointPosition(0,radius),ctx.moveTo(pointPosition.x,pointPosition.y);for(var i=1;i=0;i--)offset=me.getDistanceFromCenterForValue(opts.ticks.reverse?me.min:me.max),position=me.getPointPosition(i,offset),ctx.beginPath(),ctx.moveTo(me.xCenter,me.yCenter),ctx.lineTo(position.x,position.y),ctx.stroke();ctx.restore()}},_drawLabels:function(){var me=this,ctx=me.ctx,tickOpts=me.options.ticks;if(tickOpts.display){var offset,width,startAngle=me.getIndexAngle(0),tickFont=helpers$1.options._parseFont(tickOpts),tickFontColor=valueOrDefault$c(tickOpts.fontColor,core_defaults.global.defaultFontColor);ctx.save(),ctx.font=tickFont.string,ctx.translate(me.xCenter,me.yCenter),ctx.rotate(startAngle),ctx.textAlign="center",ctx.textBaseline="middle",helpers$1.each(me.ticks,(function(label,index){(0!==index||tickOpts.reverse)&&(offset=me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]),tickOpts.showLabelBackdrop&&(width=ctx.measureText(label).width,ctx.fillStyle=tickOpts.backdropColor,ctx.fillRect(-width/2-tickOpts.backdropPaddingX,-offset-tickFont.size/2-tickOpts.backdropPaddingY,width+2*tickOpts.backdropPaddingX,tickFont.size+2*tickOpts.backdropPaddingY)),ctx.fillStyle=tickFontColor,ctx.fillText(label,0,-offset))})),ctx.restore()}},_drawTitle:helpers$1.noop}),_defaults$3=defaultConfig$3;scale_radialLinear._defaults=_defaults$3;var deprecated$1=helpers$1._deprecated,resolve$5=helpers$1.options.resolve,valueOrDefault$d=helpers$1.valueOrDefault,MIN_INTEGER=Number.MIN_SAFE_INTEGER||-9007199254740991,MAX_INTEGER=Number.MAX_SAFE_INTEGER||9007199254740991,INTERVALS={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},UNITS=Object.keys(INTERVALS);function sorter(a,b){return a-b}function getMin(options){return helpers$1.valueOrDefault(options.time.min,options.ticks.min)}function getMax(options){return helpers$1.valueOrDefault(options.time.max,options.ticks.max)}function interpolate$1(table,skey,sval,tkey){var range=function(table,key,value){for(var mid,i0,i1,lo=0,hi=table.length-1;lo>=0&&lo<=hi;){if(i0=table[(mid=lo+hi>>1)-1]||null,i1=table[mid],!i0)return{lo:null,hi:i1};if(i1[key]value))return{lo:i0,hi:i1};hi=mid-1}}return{lo:i1,hi:null}}(table,skey,sval),prev=range.lo?range.hi?range.lo:table[table.length-2]:table[0],next=range.lo?range.hi?range.hi:table[table.length-1]:table[1],span=next[skey]-prev[skey],ratio=span?(sval-prev[skey])/span:0,offset=(next[tkey]-prev[tkey])*ratio;return prev[tkey]+offset}function toTimestamp(scale,input){var adapter=scale._adapter,options=scale.options.time,parser=options.parser,format=parser||options.format,value=input;return"function"==typeof parser&&(value=parser(value)),helpers$1.isFinite(value)||(value="string"==typeof format?adapter.parse(value,format):adapter.parse(value)),null!==value?+value:(parser||"function"!=typeof format||(value=format(input),helpers$1.isFinite(value)||(value=adapter.parse(value))),value)}function parse(scale,input){if(helpers$1.isNullOrUndef(input))return null;var options=scale.options.time,value=toTimestamp(scale,scale.getRightValue(input));return null===value||options.round&&(value=+scale._adapter.startOf(value,options.round)),value}function determineUnitForAutoTicks(minUnit,min,max,capacity){var i,interval,factor,ilen=UNITS.length;for(i=UNITS.indexOf(minUnit);i=0&&(ticks[index].major=!0);return ticks}(scale,ticks,map,majorUnit):ticks}var scale_time=core_scale.extend({initialize:function(){this.mergeTicksOptions(),core_scale.prototype.initialize.call(this)},update:function(){var me=this,options=me.options,time=options.time||(options.time={}),adapter=me._adapter=new core_adapters._date(options.adapters.date);return deprecated$1("time scale",time.format,"time.format","time.parser"),deprecated$1("time scale",time.min,"time.min","ticks.min"),deprecated$1("time scale",time.max,"time.max","ticks.max"),helpers$1.mergeIf(time.displayFormats,adapter.formats()),core_scale.prototype.update.apply(me,arguments)},getRightValue:function(rawValue){return rawValue&&void 0!==rawValue.t&&(rawValue=rawValue.t),core_scale.prototype.getRightValue.call(this,rawValue)},determineDataLimits:function(){var i,j,ilen,jlen,data,timestamp,labelsAdded,me=this,chart=me.chart,adapter=me._adapter,options=me.options,unit=options.time.unit||"day",min=MAX_INTEGER,max=MIN_INTEGER,timestamps=[],datasets=[],labels=[],dataLabels=me._getLabels();for(i=0,ilen=dataLabels.length;i1?function(items){var i,ilen,item,hash={},out=[];for(i=0,ilen=items.length;i1e5*stepSize)throw min+" and "+max+" are too far apart with stepSize of "+stepSize+" "+minor;for(time=first;time=min&×tamp<=max&&ticks.push(timestamp);return me.min=min,me.max=max,me._unit=timeOpts.unit||(tickOpts.autoSkip?determineUnitForAutoTicks(timeOpts.minUnit,me.min,me.max,capacity):function(scale,numTicks,minUnit,min,max){var i,unit;for(i=UNITS.length-1;i>=UNITS.indexOf(minUnit);i--)if(unit=UNITS[i],INTERVALS[unit].common&&scale._adapter.diff(max,min,unit)>=numTicks-1)return unit;return UNITS[minUnit?UNITS.indexOf(minUnit):0]}(me,ticks.length,timeOpts.minUnit,me.min,me.max)),me._majorUnit=tickOpts.major.enabled&&"year"!==me._unit?function(unit){for(var i=UNITS.indexOf(unit)+1,ilen=UNITS.length;imin&&curr=0&&index0?capacity:1}}),_defaults$4={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};scale_time._defaults=_defaults$4;var scales={category:scale_category,linear:scale_linear,logarithmic:scale_logarithmic,radialLinear:scale_radialLinear,time:scale_time},moment=createCommonjsModule((function(module,exports){module.exports=function(){var hookCallback,some;function hooks(){return hookCallback.apply(null,arguments)}function setHookCallback(callback){hookCallback=callback}function isArray(input){return input instanceof Array||"[object Array]"===Object.prototype.toString.call(input)}function isObject(input){return null!=input&&"[object Object]"===Object.prototype.toString.call(input)}function isObjectEmpty(obj){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(obj).length;var k;for(k in obj)if(obj.hasOwnProperty(k))return!1;return!0}function isUndefined(input){return void 0===input}function isNumber(input){return"number"==typeof input||"[object Number]"===Object.prototype.toString.call(input)}function isDate(input){return input instanceof Date||"[object Date]"===Object.prototype.toString.call(input)}function map(arr,fn){var i,res=[];for(i=0;i>>0,i=0;i0)for(i=0;i0?"future":"past"];return isFunction(format)?format(output):format.replace(/%s/i,output)}var aliases={};function addUnitAlias(unit,shorthand){var lowerCase=unit.toLowerCase();aliases[lowerCase]=aliases[lowerCase+"s"]=aliases[shorthand]=unit}function normalizeUnits(units){return"string"==typeof units?aliases[units]||aliases[units.toLowerCase()]:void 0}function normalizeObjectUnits(inputObject){var normalizedProp,prop,normalizedInput={};for(prop in inputObject)hasOwnProp(inputObject,prop)&&(normalizedProp=normalizeUnits(prop))&&(normalizedInput[normalizedProp]=inputObject[prop]);return normalizedInput}var priorities={};function addUnitPriority(unit,priority){priorities[unit]=priority}function getPrioritizedUnits(unitsObj){var units=[];for(var u in unitsObj)units.push({unit:u,priority:priorities[u]});return units.sort((function(a,b){return a.priority-b.priority})),units}function zeroFill(number,targetLength,forceSign){var absNumber=""+Math.abs(number),zerosToFill=targetLength-absNumber.length;return(number>=0?forceSign?"+":"":"-")+Math.pow(10,Math.max(0,zerosToFill)).toString().substr(1)+absNumber}var formattingTokens=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,localFormattingTokens=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,formatFunctions={},formatTokenFunctions={};function addFormatToken(token,padded,ordinal,callback){var func=callback;"string"==typeof callback&&(func=function(){return this[callback]()}),token&&(formatTokenFunctions[token]=func),padded&&(formatTokenFunctions[padded[0]]=function(){return zeroFill(func.apply(this,arguments),padded[1],padded[2])}),ordinal&&(formatTokenFunctions[ordinal]=function(){return this.localeData().ordinal(func.apply(this,arguments),token)})}function removeFormattingTokens(input){return input.match(/\[[\s\S]/)?input.replace(/^\[|\]$/g,""):input.replace(/\\/g,"")}function makeFormatFunction(format){var i,length,array=format.match(formattingTokens);for(i=0,length=array.length;i=0&&localFormattingTokens.test(format);)format=format.replace(localFormattingTokens,replaceLongDateFormatTokens),localFormattingTokens.lastIndex=0,i-=1;return format}var match1=/\d/,match2=/\d\d/,match3=/\d{3}/,match4=/\d{4}/,match6=/[+-]?\d{6}/,match1to2=/\d\d?/,match3to4=/\d\d\d\d?/,match5to6=/\d\d\d\d\d\d?/,match1to3=/\d{1,3}/,match1to4=/\d{1,4}/,match1to6=/[+-]?\d{1,6}/,matchUnsigned=/\d+/,matchSigned=/[+-]?\d+/,matchOffset=/Z|[+-]\d\d:?\d\d/gi,matchShortOffset=/Z|[+-]\d\d(?::?\d\d)?/gi,matchTimestamp=/[+-]?\d+(\.\d{1,3})?/,matchWord=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,regexes={};function addRegexToken(token,regex,strictRegex){regexes[token]=isFunction(regex)?regex:function(isStrict,localeData){return isStrict&&strictRegex?strictRegex:regex}}function getParseRegexForToken(token,config){return hasOwnProp(regexes,token)?regexes[token](config._strict,config._locale):new RegExp(unescapeFormat(token))}function unescapeFormat(s){return regexEscape(s.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(matched,p1,p2,p3,p4){return p1||p2||p3||p4})))}function regexEscape(s){return s.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var tokens={};function addParseToken(token,callback){var i,func=callback;for("string"==typeof token&&(token=[token]),isNumber(callback)&&(func=function(input,array){array[callback]=toInt(input)}),i=0;i68?1900:2e3)};var indexOf,getSetYear=makeGetSet("FullYear",!0);function getIsLeapYear(){return isLeapYear(this.year())}function makeGetSet(unit,keepTime){return function(value){return null!=value?(set$1(this,unit,value),hooks.updateOffset(this,keepTime),this):get(this,unit)}}function get(mom,unit){return mom.isValid()?mom._d["get"+(mom._isUTC?"UTC":"")+unit]():NaN}function set$1(mom,unit,value){mom.isValid()&&!isNaN(value)&&("FullYear"===unit&&isLeapYear(mom.year())&&1===mom.month()&&29===mom.date()?mom._d["set"+(mom._isUTC?"UTC":"")+unit](value,mom.month(),daysInMonth(value,mom.month())):mom._d["set"+(mom._isUTC?"UTC":"")+unit](value))}function stringGet(units){return isFunction(this[units=normalizeUnits(units)])?this[units]():this}function stringSet(units,value){if("object"==typeof units)for(var prioritized=getPrioritizedUnits(units=normalizeObjectUnits(units)),i=0;i=0?(date=new Date(y+400,m,d,h,M,s,ms),isFinite(date.getFullYear())&&date.setFullYear(y)):date=new Date(y,m,d,h,M,s,ms),date}function createUTCDate(y){var date;if(y<100&&y>=0){var args=Array.prototype.slice.call(arguments);args[0]=y+400,date=new Date(Date.UTC.apply(null,args)),isFinite(date.getUTCFullYear())&&date.setUTCFullYear(y)}else date=new Date(Date.UTC.apply(null,arguments));return date}function firstWeekOffset(year,dow,doy){var fwd=7+dow-doy;return-(7+createUTCDate(year,0,fwd).getUTCDay()-dow)%7+fwd-1}function dayOfYearFromWeeks(year,week,weekday,dow,doy){var resYear,resDayOfYear,dayOfYear=1+7*(week-1)+(7+weekday-dow)%7+firstWeekOffset(year,dow,doy);return dayOfYear<=0?resDayOfYear=daysInYear(resYear=year-1)+dayOfYear:dayOfYear>daysInYear(year)?(resYear=year+1,resDayOfYear=dayOfYear-daysInYear(year)):(resYear=year,resDayOfYear=dayOfYear),{year:resYear,dayOfYear:resDayOfYear}}function weekOfYear(mom,dow,doy){var resWeek,resYear,weekOffset=firstWeekOffset(mom.year(),dow,doy),week=Math.floor((mom.dayOfYear()-weekOffset-1)/7)+1;return week<1?resWeek=week+weeksInYear(resYear=mom.year()-1,dow,doy):week>weeksInYear(mom.year(),dow,doy)?(resWeek=week-weeksInYear(mom.year(),dow,doy),resYear=mom.year()+1):(resYear=mom.year(),resWeek=week),{week:resWeek,year:resYear}}function weeksInYear(year,dow,doy){var weekOffset=firstWeekOffset(year,dow,doy),weekOffsetNext=firstWeekOffset(year+1,dow,doy);return(daysInYear(year)-weekOffset+weekOffsetNext)/7}function localeWeek(mom){return weekOfYear(mom,this._week.dow,this._week.doy).week}addFormatToken("w",["ww",2],"wo","week"),addFormatToken("W",["WW",2],"Wo","isoWeek"),addUnitAlias("week","w"),addUnitAlias("isoWeek","W"),addUnitPriority("week",5),addUnitPriority("isoWeek",5),addRegexToken("w",match1to2),addRegexToken("ww",match1to2,match2),addRegexToken("W",match1to2),addRegexToken("WW",match1to2,match2),addWeekParseToken(["w","ww","W","WW"],(function(input,week,config,token){week[token.substr(0,1)]=toInt(input)}));var defaultLocaleWeek={dow:0,doy:6};function localeFirstDayOfWeek(){return this._week.dow}function localeFirstDayOfYear(){return this._week.doy}function getSetWeek(input){var week=this.localeData().week(this);return null==input?week:this.add(7*(input-week),"d")}function getSetISOWeek(input){var week=weekOfYear(this,1,4).week;return null==input?week:this.add(7*(input-week),"d")}function parseWeekday(input,locale){return"string"!=typeof input?input:isNaN(input)?"number"==typeof(input=locale.weekdaysParse(input))?input:null:parseInt(input,10)}function parseIsoWeekday(input,locale){return"string"==typeof input?locale.weekdaysParse(input)%7||7:isNaN(input)?null:input}function shiftWeekdays(ws,n){return ws.slice(n,7).concat(ws.slice(0,n))}addFormatToken("d",0,"do","day"),addFormatToken("dd",0,0,(function(format){return this.localeData().weekdaysMin(this,format)})),addFormatToken("ddd",0,0,(function(format){return this.localeData().weekdaysShort(this,format)})),addFormatToken("dddd",0,0,(function(format){return this.localeData().weekdays(this,format)})),addFormatToken("e",0,0,"weekday"),addFormatToken("E",0,0,"isoWeekday"),addUnitAlias("day","d"),addUnitAlias("weekday","e"),addUnitAlias("isoWeekday","E"),addUnitPriority("day",11),addUnitPriority("weekday",11),addUnitPriority("isoWeekday",11),addRegexToken("d",match1to2),addRegexToken("e",match1to2),addRegexToken("E",match1to2),addRegexToken("dd",(function(isStrict,locale){return locale.weekdaysMinRegex(isStrict)})),addRegexToken("ddd",(function(isStrict,locale){return locale.weekdaysShortRegex(isStrict)})),addRegexToken("dddd",(function(isStrict,locale){return locale.weekdaysRegex(isStrict)})),addWeekParseToken(["dd","ddd","dddd"],(function(input,week,config,token){var weekday=config._locale.weekdaysParse(input,token,config._strict);null!=weekday?week.d=weekday:getParsingFlags(config).invalidWeekday=input})),addWeekParseToken(["d","e","E"],(function(input,week,config,token){week[token]=toInt(input)}));var defaultLocaleWeekdays="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");function localeWeekdays(m,format){var weekdays=isArray(this._weekdays)?this._weekdays:this._weekdays[m&&!0!==m&&this._weekdays.isFormat.test(format)?"format":"standalone"];return!0===m?shiftWeekdays(weekdays,this._week.dow):m?weekdays[m.day()]:weekdays}var defaultLocaleWeekdaysShort="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");function localeWeekdaysShort(m){return!0===m?shiftWeekdays(this._weekdaysShort,this._week.dow):m?this._weekdaysShort[m.day()]:this._weekdaysShort}var defaultLocaleWeekdaysMin="Su_Mo_Tu_We_Th_Fr_Sa".split("_");function localeWeekdaysMin(m){return!0===m?shiftWeekdays(this._weekdaysMin,this._week.dow):m?this._weekdaysMin[m.day()]:this._weekdaysMin}function handleStrictParse$1(weekdayName,format,strict){var i,ii,mom,llc=weekdayName.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],i=0;i<7;++i)mom=createUTC([2e3,1]).day(i),this._minWeekdaysParse[i]=this.weekdaysMin(mom,"").toLocaleLowerCase(),this._shortWeekdaysParse[i]=this.weekdaysShort(mom,"").toLocaleLowerCase(),this._weekdaysParse[i]=this.weekdays(mom,"").toLocaleLowerCase();return strict?"dddd"===format?-1!==(ii=indexOf.call(this._weekdaysParse,llc))?ii:null:"ddd"===format?-1!==(ii=indexOf.call(this._shortWeekdaysParse,llc))?ii:null:-1!==(ii=indexOf.call(this._minWeekdaysParse,llc))?ii:null:"dddd"===format?-1!==(ii=indexOf.call(this._weekdaysParse,llc))||-1!==(ii=indexOf.call(this._shortWeekdaysParse,llc))||-1!==(ii=indexOf.call(this._minWeekdaysParse,llc))?ii:null:"ddd"===format?-1!==(ii=indexOf.call(this._shortWeekdaysParse,llc))||-1!==(ii=indexOf.call(this._weekdaysParse,llc))||-1!==(ii=indexOf.call(this._minWeekdaysParse,llc))?ii:null:-1!==(ii=indexOf.call(this._minWeekdaysParse,llc))||-1!==(ii=indexOf.call(this._weekdaysParse,llc))||-1!==(ii=indexOf.call(this._shortWeekdaysParse,llc))?ii:null}function localeWeekdaysParse(weekdayName,format,strict){var i,mom,regex;if(this._weekdaysParseExact)return handleStrictParse$1.call(this,weekdayName,format,strict);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),i=0;i<7;i++){if(mom=createUTC([2e3,1]).day(i),strict&&!this._fullWeekdaysParse[i]&&(this._fullWeekdaysParse[i]=new RegExp("^"+this.weekdays(mom,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[i]=new RegExp("^"+this.weekdaysShort(mom,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[i]=new RegExp("^"+this.weekdaysMin(mom,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[i]||(regex="^"+this.weekdays(mom,"")+"|^"+this.weekdaysShort(mom,"")+"|^"+this.weekdaysMin(mom,""),this._weekdaysParse[i]=new RegExp(regex.replace(".",""),"i")),strict&&"dddd"===format&&this._fullWeekdaysParse[i].test(weekdayName))return i;if(strict&&"ddd"===format&&this._shortWeekdaysParse[i].test(weekdayName))return i;if(strict&&"dd"===format&&this._minWeekdaysParse[i].test(weekdayName))return i;if(!strict&&this._weekdaysParse[i].test(weekdayName))return i}}function getSetDayOfWeek(input){if(!this.isValid())return null!=input?this:NaN;var day=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=input?(input=parseWeekday(input,this.localeData()),this.add(input-day,"d")):day}function getSetLocaleDayOfWeek(input){if(!this.isValid())return null!=input?this:NaN;var weekday=(this.day()+7-this.localeData()._week.dow)%7;return null==input?weekday:this.add(input-weekday,"d")}function getSetISODayOfWeek(input){if(!this.isValid())return null!=input?this:NaN;if(null!=input){var weekday=parseIsoWeekday(input,this.localeData());return this.day(this.day()%7?weekday:weekday-7)}return this.day()||7}var defaultWeekdaysRegex=matchWord;function weekdaysRegex(isStrict){return this._weekdaysParseExact?(hasOwnProp(this,"_weekdaysRegex")||computeWeekdaysParse.call(this),isStrict?this._weekdaysStrictRegex:this._weekdaysRegex):(hasOwnProp(this,"_weekdaysRegex")||(this._weekdaysRegex=defaultWeekdaysRegex),this._weekdaysStrictRegex&&isStrict?this._weekdaysStrictRegex:this._weekdaysRegex)}var defaultWeekdaysShortRegex=matchWord;function weekdaysShortRegex(isStrict){return this._weekdaysParseExact?(hasOwnProp(this,"_weekdaysRegex")||computeWeekdaysParse.call(this),isStrict?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(hasOwnProp(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=defaultWeekdaysShortRegex),this._weekdaysShortStrictRegex&&isStrict?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}var defaultWeekdaysMinRegex=matchWord;function weekdaysMinRegex(isStrict){return this._weekdaysParseExact?(hasOwnProp(this,"_weekdaysRegex")||computeWeekdaysParse.call(this),isStrict?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(hasOwnProp(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=defaultWeekdaysMinRegex),this._weekdaysMinStrictRegex&&isStrict?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function computeWeekdaysParse(){function cmpLenRev(a,b){return b.length-a.length}var i,mom,minp,shortp,longp,minPieces=[],shortPieces=[],longPieces=[],mixedPieces=[];for(i=0;i<7;i++)mom=createUTC([2e3,1]).day(i),minp=this.weekdaysMin(mom,""),shortp=this.weekdaysShort(mom,""),longp=this.weekdays(mom,""),minPieces.push(minp),shortPieces.push(shortp),longPieces.push(longp),mixedPieces.push(minp),mixedPieces.push(shortp),mixedPieces.push(longp);for(minPieces.sort(cmpLenRev),shortPieces.sort(cmpLenRev),longPieces.sort(cmpLenRev),mixedPieces.sort(cmpLenRev),i=0;i<7;i++)shortPieces[i]=regexEscape(shortPieces[i]),longPieces[i]=regexEscape(longPieces[i]),mixedPieces[i]=regexEscape(mixedPieces[i]);this._weekdaysRegex=new RegExp("^("+mixedPieces.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+longPieces.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+shortPieces.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+minPieces.join("|")+")","i")}function hFormat(){return this.hours()%12||12}function kFormat(){return this.hours()||24}function meridiem(token,lowercase){addFormatToken(token,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),lowercase)}))}function matchMeridiem(isStrict,locale){return locale._meridiemParse}function localeIsPM(input){return"p"===(input+"").toLowerCase().charAt(0)}addFormatToken("H",["HH",2],0,"hour"),addFormatToken("h",["hh",2],0,hFormat),addFormatToken("k",["kk",2],0,kFormat),addFormatToken("hmm",0,0,(function(){return""+hFormat.apply(this)+zeroFill(this.minutes(),2)})),addFormatToken("hmmss",0,0,(function(){return""+hFormat.apply(this)+zeroFill(this.minutes(),2)+zeroFill(this.seconds(),2)})),addFormatToken("Hmm",0,0,(function(){return""+this.hours()+zeroFill(this.minutes(),2)})),addFormatToken("Hmmss",0,0,(function(){return""+this.hours()+zeroFill(this.minutes(),2)+zeroFill(this.seconds(),2)})),meridiem("a",!0),meridiem("A",!1),addUnitAlias("hour","h"),addUnitPriority("hour",13),addRegexToken("a",matchMeridiem),addRegexToken("A",matchMeridiem),addRegexToken("H",match1to2),addRegexToken("h",match1to2),addRegexToken("k",match1to2),addRegexToken("HH",match1to2,match2),addRegexToken("hh",match1to2,match2),addRegexToken("kk",match1to2,match2),addRegexToken("hmm",match3to4),addRegexToken("hmmss",match5to6),addRegexToken("Hmm",match3to4),addRegexToken("Hmmss",match5to6),addParseToken(["H","HH"],HOUR),addParseToken(["k","kk"],(function(input,array,config){var kInput=toInt(input);array[HOUR]=24===kInput?0:kInput})),addParseToken(["a","A"],(function(input,array,config){config._isPm=config._locale.isPM(input),config._meridiem=input})),addParseToken(["h","hh"],(function(input,array,config){array[HOUR]=toInt(input),getParsingFlags(config).bigHour=!0})),addParseToken("hmm",(function(input,array,config){var pos=input.length-2;array[HOUR]=toInt(input.substr(0,pos)),array[MINUTE]=toInt(input.substr(pos)),getParsingFlags(config).bigHour=!0})),addParseToken("hmmss",(function(input,array,config){var pos1=input.length-4,pos2=input.length-2;array[HOUR]=toInt(input.substr(0,pos1)),array[MINUTE]=toInt(input.substr(pos1,2)),array[SECOND]=toInt(input.substr(pos2)),getParsingFlags(config).bigHour=!0})),addParseToken("Hmm",(function(input,array,config){var pos=input.length-2;array[HOUR]=toInt(input.substr(0,pos)),array[MINUTE]=toInt(input.substr(pos))})),addParseToken("Hmmss",(function(input,array,config){var pos1=input.length-4,pos2=input.length-2;array[HOUR]=toInt(input.substr(0,pos1)),array[MINUTE]=toInt(input.substr(pos1,2)),array[SECOND]=toInt(input.substr(pos2))}));var defaultLocaleMeridiemParse=/[ap]\.?m?\.?/i;function localeMeridiem(hours,minutes,isLower){return hours>11?isLower?"pm":"PM":isLower?"am":"AM"}var globalLocale,getSetHour=makeGetSet("Hours",!0),baseConfig={calendar:defaultCalendar,longDateFormat:defaultLongDateFormat,invalidDate:defaultInvalidDate,ordinal:defaultOrdinal,dayOfMonthOrdinalParse:defaultDayOfMonthOrdinalParse,relativeTime:defaultRelativeTime,months:defaultLocaleMonths,monthsShort:defaultLocaleMonthsShort,week:defaultLocaleWeek,weekdays:defaultLocaleWeekdays,weekdaysMin:defaultLocaleWeekdaysMin,weekdaysShort:defaultLocaleWeekdaysShort,meridiemParse:defaultLocaleMeridiemParse},locales={},localeFamilies={};function normalizeLocale(key){return key?key.toLowerCase().replace("_","-"):key}function chooseLocale(names){for(var j,next,locale,split,i=0;i0;){if(locale=loadLocale(split.slice(0,j).join("-")))return locale;if(next&&next.length>=j&&compareArrays(split,next,!0)>=j-1)break;j--}i++}return globalLocale}function loadLocale(name){var oldLocale=null;if(!locales[name]&&module&&module.exports)try{oldLocale=globalLocale._abbr,commonjsRequire(),getSetGlobalLocale(oldLocale)}catch(e){}return locales[name]}function getSetGlobalLocale(key,values){var data;return key&&((data=isUndefined(values)?getLocale(key):defineLocale(key,values))?globalLocale=data:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+key+" not found. Did you forget to load it?")),globalLocale._abbr}function defineLocale(name,config){if(null!==config){var locale,parentConfig=baseConfig;if(config.abbr=name,null!=locales[name])deprecateSimple("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."),parentConfig=locales[name]._config;else if(null!=config.parentLocale)if(null!=locales[config.parentLocale])parentConfig=locales[config.parentLocale]._config;else{if(null==(locale=loadLocale(config.parentLocale)))return localeFamilies[config.parentLocale]||(localeFamilies[config.parentLocale]=[]),localeFamilies[config.parentLocale].push({name:name,config:config}),null;parentConfig=locale._config}return locales[name]=new Locale(mergeConfigs(parentConfig,config)),localeFamilies[name]&&localeFamilies[name].forEach((function(x){defineLocale(x.name,x.config)})),getSetGlobalLocale(name),locales[name]}return delete locales[name],null}function updateLocale(name,config){if(null!=config){var locale,tmpLocale,parentConfig=baseConfig;null!=(tmpLocale=loadLocale(name))&&(parentConfig=tmpLocale._config),(locale=new Locale(config=mergeConfigs(parentConfig,config))).parentLocale=locales[name],locales[name]=locale,getSetGlobalLocale(name)}else null!=locales[name]&&(null!=locales[name].parentLocale?locales[name]=locales[name].parentLocale:null!=locales[name]&&delete locales[name]);return locales[name]}function getLocale(key){var locale;if(key&&key._locale&&key._locale._abbr&&(key=key._locale._abbr),!key)return globalLocale;if(!isArray(key)){if(locale=loadLocale(key))return locale;key=[key]}return chooseLocale(key)}function listLocales(){return keys(locales)}function checkOverflow(m){var overflow,a=m._a;return a&&-2===getParsingFlags(m).overflow&&(overflow=a[MONTH]<0||a[MONTH]>11?MONTH:a[DATE]<1||a[DATE]>daysInMonth(a[YEAR],a[MONTH])?DATE:a[HOUR]<0||a[HOUR]>24||24===a[HOUR]&&(0!==a[MINUTE]||0!==a[SECOND]||0!==a[MILLISECOND])?HOUR:a[MINUTE]<0||a[MINUTE]>59?MINUTE:a[SECOND]<0||a[SECOND]>59?SECOND:a[MILLISECOND]<0||a[MILLISECOND]>999?MILLISECOND:-1,getParsingFlags(m)._overflowDayOfYear&&(overflowDATE)&&(overflow=DATE),getParsingFlags(m)._overflowWeeks&&-1===overflow&&(overflow=WEEK),getParsingFlags(m)._overflowWeekday&&-1===overflow&&(overflow=WEEKDAY),getParsingFlags(m).overflow=overflow),m}function defaults(a,b,c){return null!=a?a:null!=b?b:c}function currentDateArray(config){var nowValue=new Date(hooks.now());return config._useUTC?[nowValue.getUTCFullYear(),nowValue.getUTCMonth(),nowValue.getUTCDate()]:[nowValue.getFullYear(),nowValue.getMonth(),nowValue.getDate()]}function configFromArray(config){var i,date,currentDate,expectedWeekday,yearToUse,input=[];if(!config._d){for(currentDate=currentDateArray(config),config._w&&null==config._a[DATE]&&null==config._a[MONTH]&&dayOfYearFromWeekInfo(config),null!=config._dayOfYear&&(yearToUse=defaults(config._a[YEAR],currentDate[YEAR]),(config._dayOfYear>daysInYear(yearToUse)||0===config._dayOfYear)&&(getParsingFlags(config)._overflowDayOfYear=!0),date=createUTCDate(yearToUse,0,config._dayOfYear),config._a[MONTH]=date.getUTCMonth(),config._a[DATE]=date.getUTCDate()),i=0;i<3&&null==config._a[i];++i)config._a[i]=input[i]=currentDate[i];for(;i<7;i++)config._a[i]=input[i]=null==config._a[i]?2===i?1:0:config._a[i];24===config._a[HOUR]&&0===config._a[MINUTE]&&0===config._a[SECOND]&&0===config._a[MILLISECOND]&&(config._nextDay=!0,config._a[HOUR]=0),config._d=(config._useUTC?createUTCDate:createDate).apply(null,input),expectedWeekday=config._useUTC?config._d.getUTCDay():config._d.getDay(),null!=config._tzm&&config._d.setUTCMinutes(config._d.getUTCMinutes()-config._tzm),config._nextDay&&(config._a[HOUR]=24),config._w&&void 0!==config._w.d&&config._w.d!==expectedWeekday&&(getParsingFlags(config).weekdayMismatch=!0)}}function dayOfYearFromWeekInfo(config){var w,weekYear,week,weekday,dow,doy,temp,weekdayOverflow;if(null!=(w=config._w).GG||null!=w.W||null!=w.E)dow=1,doy=4,weekYear=defaults(w.GG,config._a[YEAR],weekOfYear(createLocal(),1,4).year),week=defaults(w.W,1),((weekday=defaults(w.E,1))<1||weekday>7)&&(weekdayOverflow=!0);else{dow=config._locale._week.dow,doy=config._locale._week.doy;var curWeek=weekOfYear(createLocal(),dow,doy);weekYear=defaults(w.gg,config._a[YEAR],curWeek.year),week=defaults(w.w,curWeek.week),null!=w.d?((weekday=w.d)<0||weekday>6)&&(weekdayOverflow=!0):null!=w.e?(weekday=w.e+dow,(w.e<0||w.e>6)&&(weekdayOverflow=!0)):weekday=dow}week<1||week>weeksInYear(weekYear,dow,doy)?getParsingFlags(config)._overflowWeeks=!0:null!=weekdayOverflow?getParsingFlags(config)._overflowWeekday=!0:(temp=dayOfYearFromWeeks(weekYear,week,weekday,dow,doy),config._a[YEAR]=temp.year,config._dayOfYear=temp.dayOfYear)}var extendedIsoRegex=/^\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)?)?$/,basicIsoRegex=/^\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)?)?$/,tzRegex=/Z|[+-]\d\d(?::?\d\d)?/,isoDates=[["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}/]],isoTimes=[["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/]],aspNetJsonRegex=/^\/?Date\((\-?\d+)/i;function configFromISO(config){var i,l,allowTime,dateFormat,timeFormat,tzFormat,string=config._i,match=extendedIsoRegex.exec(string)||basicIsoRegex.exec(string);if(match){for(getParsingFlags(config).iso=!0,i=0,l=isoDates.length;i0&&getParsingFlags(config).unusedInput.push(skipped),string=string.slice(string.indexOf(parsedInput)+parsedInput.length),totalParsedInputLength+=parsedInput.length),formatTokenFunctions[token]?(parsedInput?getParsingFlags(config).empty=!1:getParsingFlags(config).unusedTokens.push(token),addTimeToArrayFromToken(token,parsedInput,config)):config._strict&&!parsedInput&&getParsingFlags(config).unusedTokens.push(token);getParsingFlags(config).charsLeftOver=stringLength-totalParsedInputLength,string.length>0&&getParsingFlags(config).unusedInput.push(string),config._a[HOUR]<=12&&!0===getParsingFlags(config).bigHour&&config._a[HOUR]>0&&(getParsingFlags(config).bigHour=void 0),getParsingFlags(config).parsedDateParts=config._a.slice(0),getParsingFlags(config).meridiem=config._meridiem,config._a[HOUR]=meridiemFixWrap(config._locale,config._a[HOUR],config._meridiem),configFromArray(config),checkOverflow(config)}else configFromRFC2822(config);else configFromISO(config)}function meridiemFixWrap(locale,hour,meridiem){var isPm;return null==meridiem?hour:null!=locale.meridiemHour?locale.meridiemHour(hour,meridiem):null!=locale.isPM?((isPm=locale.isPM(meridiem))&&hour<12&&(hour+=12),isPm||12!==hour||(hour=0),hour):hour}function configFromStringAndArray(config){var tempConfig,bestMoment,scoreToBeat,i,currentScore;if(0===config._f.length)return getParsingFlags(config).invalidFormat=!0,void(config._d=new Date(NaN));for(i=0;ithis?this:other:createInvalid()}));function pickBy(fn,moments){var res,i;if(1===moments.length&&isArray(moments[0])&&(moments=moments[0]),!moments.length)return createLocal();for(res=moments[0],i=1;ithis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function isDaylightSavingTimeShifted(){if(!isUndefined(this._isDSTShifted))return this._isDSTShifted;var c={};if(copyConfig(c,this),(c=prepareConfig(c))._a){var other=c._isUTC?createUTC(c._a):createLocal(c._a);this._isDSTShifted=this.isValid()&&compareArrays(c._a,other.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function isLocal(){return!!this.isValid()&&!this._isUTC}function isUtcOffset(){return!!this.isValid()&&this._isUTC}function isUtc(){return!!this.isValid()&&this._isUTC&&0===this._offset}hooks.updateOffset=function(){};var aspNetRegex=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,isoRegex=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function createDuration(input,key){var sign,ret,diffRes,duration=input,match=null;return isDuration(input)?duration={ms:input._milliseconds,d:input._days,M:input._months}:isNumber(input)?(duration={},key?duration[key]=input:duration.milliseconds=input):(match=aspNetRegex.exec(input))?(sign="-"===match[1]?-1:1,duration={y:0,d:toInt(match[DATE])*sign,h:toInt(match[HOUR])*sign,m:toInt(match[MINUTE])*sign,s:toInt(match[SECOND])*sign,ms:toInt(absRound(1e3*match[MILLISECOND]))*sign}):(match=isoRegex.exec(input))?(sign="-"===match[1]?-1:1,duration={y:parseIso(match[2],sign),M:parseIso(match[3],sign),w:parseIso(match[4],sign),d:parseIso(match[5],sign),h:parseIso(match[6],sign),m:parseIso(match[7],sign),s:parseIso(match[8],sign)}):null==duration?duration={}:"object"==typeof duration&&("from"in duration||"to"in duration)&&(diffRes=momentsDifference(createLocal(duration.from),createLocal(duration.to)),(duration={}).ms=diffRes.milliseconds,duration.M=diffRes.months),ret=new Duration(duration),isDuration(input)&&hasOwnProp(input,"_locale")&&(ret._locale=input._locale),ret}function parseIso(inp,sign){var res=inp&&parseFloat(inp.replace(",","."));return(isNaN(res)?0:res)*sign}function positiveMomentsDifference(base,other){var res={};return res.months=other.month()-base.month()+12*(other.year()-base.year()),base.clone().add(res.months,"M").isAfter(other)&&--res.months,res.milliseconds=+other-+base.clone().add(res.months,"M"),res}function momentsDifference(base,other){var res;return base.isValid()&&other.isValid()?(other=cloneWithOffset(other,base),base.isBefore(other)?res=positiveMomentsDifference(base,other):((res=positiveMomentsDifference(other,base)).milliseconds=-res.milliseconds,res.months=-res.months),res):{milliseconds:0,months:0}}function createAdder(direction,name){return function(val,period){var tmp;return null===period||isNaN(+period)||(deprecateSimple(name,"moment()."+name+"(period, number) is deprecated. Please use moment()."+name+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),tmp=val,val=period,period=tmp),addSubtract(this,createDuration(val="string"==typeof val?+val:val,period),direction),this}}function addSubtract(mom,duration,isAdding,updateOffset){var milliseconds=duration._milliseconds,days=absRound(duration._days),months=absRound(duration._months);mom.isValid()&&(updateOffset=null==updateOffset||updateOffset,months&&setMonth(mom,get(mom,"Month")+months*isAdding),days&&set$1(mom,"Date",get(mom,"Date")+days*isAdding),milliseconds&&mom._d.setTime(mom._d.valueOf()+milliseconds*isAdding),updateOffset&&hooks.updateOffset(mom,days||months))}createDuration.fn=Duration.prototype,createDuration.invalid=createInvalid$1;var add=createAdder(1,"add"),subtract=createAdder(-1,"subtract");function getCalendarFormat(myMoment,now){var diff=myMoment.diff(now,"days",!0);return diff<-6?"sameElse":diff<-1?"lastWeek":diff<0?"lastDay":diff<1?"sameDay":diff<2?"nextDay":diff<7?"nextWeek":"sameElse"}function calendar$1(time,formats){var now=time||createLocal(),sod=cloneWithOffset(now,this).startOf("day"),format=hooks.calendarFormat(this,sod)||"sameElse",output=formats&&(isFunction(formats[format])?formats[format].call(this,now):formats[format]);return this.format(output||this.localeData().calendar(format,this,createLocal(now)))}function clone(){return new Moment(this)}function isAfter(input,units){var localInput=isMoment(input)?input:createLocal(input);return!(!this.isValid()||!localInput.isValid())&&("millisecond"===(units=normalizeUnits(units)||"millisecond")?this.valueOf()>localInput.valueOf():localInput.valueOf()9999?formatMoment(m,utc?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):isFunction(Date.prototype.toISOString)?utc?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",formatMoment(m,"Z")):formatMoment(m,utc?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")}function inspect(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var func="moment",zone="";this.isLocal()||(func=0===this.utcOffset()?"moment.utc":"moment.parseZone",zone="Z");var prefix="["+func+'("]',year=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",datetime="-MM-DD[T]HH:mm:ss.SSS",suffix=zone+'[")]';return this.format(prefix+year+datetime+suffix)}function format(inputString){inputString||(inputString=this.isUtc()?hooks.defaultFormatUtc:hooks.defaultFormat);var output=formatMoment(this,inputString);return this.localeData().postformat(output)}function from(time,withoutSuffix){return this.isValid()&&(isMoment(time)&&time.isValid()||createLocal(time).isValid())?createDuration({to:this,from:time}).locale(this.locale()).humanize(!withoutSuffix):this.localeData().invalidDate()}function fromNow(withoutSuffix){return this.from(createLocal(),withoutSuffix)}function to(time,withoutSuffix){return this.isValid()&&(isMoment(time)&&time.isValid()||createLocal(time).isValid())?createDuration({from:this,to:time}).locale(this.locale()).humanize(!withoutSuffix):this.localeData().invalidDate()}function toNow(withoutSuffix){return this.to(createLocal(),withoutSuffix)}function locale(key){var newLocaleData;return void 0===key?this._locale._abbr:(null!=(newLocaleData=getLocale(key))&&(this._locale=newLocaleData),this)}hooks.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",hooks.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var lang=deprecate("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(key){return void 0===key?this.localeData():this.locale(key)}));function localeData(){return this._locale}var MS_PER_SECOND=1e3,MS_PER_MINUTE=60*MS_PER_SECOND,MS_PER_HOUR=60*MS_PER_MINUTE,MS_PER_400_YEARS=3506328*MS_PER_HOUR;function mod$1(dividend,divisor){return(dividend%divisor+divisor)%divisor}function localStartOfDate(y,m,d){return y<100&&y>=0?new Date(y+400,m,d)-MS_PER_400_YEARS:new Date(y,m,d).valueOf()}function utcStartOfDate(y,m,d){return y<100&&y>=0?Date.UTC(y+400,m,d)-MS_PER_400_YEARS:Date.UTC(y,m,d)}function startOf(units){var time;if(void 0===(units=normalizeUnits(units))||"millisecond"===units||!this.isValid())return this;var startOfDate=this._isUTC?utcStartOfDate:localStartOfDate;switch(units){case"year":time=startOfDate(this.year(),0,1);break;case"quarter":time=startOfDate(this.year(),this.month()-this.month()%3,1);break;case"month":time=startOfDate(this.year(),this.month(),1);break;case"week":time=startOfDate(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":time=startOfDate(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":time=startOfDate(this.year(),this.month(),this.date());break;case"hour":time=this._d.valueOf(),time-=mod$1(time+(this._isUTC?0:this.utcOffset()*MS_PER_MINUTE),MS_PER_HOUR);break;case"minute":time=this._d.valueOf(),time-=mod$1(time,MS_PER_MINUTE);break;case"second":time=this._d.valueOf(),time-=mod$1(time,MS_PER_SECOND)}return this._d.setTime(time),hooks.updateOffset(this,!0),this}function endOf(units){var time;if(void 0===(units=normalizeUnits(units))||"millisecond"===units||!this.isValid())return this;var startOfDate=this._isUTC?utcStartOfDate:localStartOfDate;switch(units){case"year":time=startOfDate(this.year()+1,0,1)-1;break;case"quarter":time=startOfDate(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":time=startOfDate(this.year(),this.month()+1,1)-1;break;case"week":time=startOfDate(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":time=startOfDate(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":time=startOfDate(this.year(),this.month(),this.date()+1)-1;break;case"hour":time=this._d.valueOf(),time+=MS_PER_HOUR-mod$1(time+(this._isUTC?0:this.utcOffset()*MS_PER_MINUTE),MS_PER_HOUR)-1;break;case"minute":time=this._d.valueOf(),time+=MS_PER_MINUTE-mod$1(time,MS_PER_MINUTE)-1;break;case"second":time=this._d.valueOf(),time+=MS_PER_SECOND-mod$1(time,MS_PER_SECOND)-1}return this._d.setTime(time),hooks.updateOffset(this,!0),this}function valueOf(){return this._d.valueOf()-6e4*(this._offset||0)}function unix(){return Math.floor(this.valueOf()/1e3)}function toDate(){return new Date(this.valueOf())}function toArray(){var m=this;return[m.year(),m.month(),m.date(),m.hour(),m.minute(),m.second(),m.millisecond()]}function toObject(){var m=this;return{years:m.year(),months:m.month(),date:m.date(),hours:m.hours(),minutes:m.minutes(),seconds:m.seconds(),milliseconds:m.milliseconds()}}function toJSON(){return this.isValid()?this.toISOString():null}function isValid$2(){return isValid(this)}function parsingFlags(){return extend({},getParsingFlags(this))}function invalidAt(){return getParsingFlags(this).overflow}function creationData(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function addWeekYearFormatToken(token,getter){addFormatToken(0,[token,token.length],0,getter)}function getSetWeekYear(input){return getSetWeekYearHelper.call(this,input,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)}function getSetISOWeekYear(input){return getSetWeekYearHelper.call(this,input,this.isoWeek(),this.isoWeekday(),1,4)}function getISOWeeksInYear(){return weeksInYear(this.year(),1,4)}function getWeeksInYear(){var weekInfo=this.localeData()._week;return weeksInYear(this.year(),weekInfo.dow,weekInfo.doy)}function getSetWeekYearHelper(input,week,weekday,dow,doy){var weeksTarget;return null==input?weekOfYear(this,dow,doy).year:(week>(weeksTarget=weeksInYear(input,dow,doy))&&(week=weeksTarget),setWeekAll.call(this,input,week,weekday,dow,doy))}function setWeekAll(weekYear,week,weekday,dow,doy){var dayOfYearData=dayOfYearFromWeeks(weekYear,week,weekday,dow,doy),date=createUTCDate(dayOfYearData.year,0,dayOfYearData.dayOfYear);return this.year(date.getUTCFullYear()),this.month(date.getUTCMonth()),this.date(date.getUTCDate()),this}function getSetQuarter(input){return null==input?Math.ceil((this.month()+1)/3):this.month(3*(input-1)+this.month()%3)}addFormatToken(0,["gg",2],0,(function(){return this.weekYear()%100})),addFormatToken(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),addWeekYearFormatToken("gggg","weekYear"),addWeekYearFormatToken("ggggg","weekYear"),addWeekYearFormatToken("GGGG","isoWeekYear"),addWeekYearFormatToken("GGGGG","isoWeekYear"),addUnitAlias("weekYear","gg"),addUnitAlias("isoWeekYear","GG"),addUnitPriority("weekYear",1),addUnitPriority("isoWeekYear",1),addRegexToken("G",matchSigned),addRegexToken("g",matchSigned),addRegexToken("GG",match1to2,match2),addRegexToken("gg",match1to2,match2),addRegexToken("GGGG",match1to4,match4),addRegexToken("gggg",match1to4,match4),addRegexToken("GGGGG",match1to6,match6),addRegexToken("ggggg",match1to6,match6),addWeekParseToken(["gggg","ggggg","GGGG","GGGGG"],(function(input,week,config,token){week[token.substr(0,2)]=toInt(input)})),addWeekParseToken(["gg","GG"],(function(input,week,config,token){week[token]=hooks.parseTwoDigitYear(input)})),addFormatToken("Q",0,"Qo","quarter"),addUnitAlias("quarter","Q"),addUnitPriority("quarter",7),addRegexToken("Q",match1),addParseToken("Q",(function(input,array){array[MONTH]=3*(toInt(input)-1)})),addFormatToken("D",["DD",2],"Do","date"),addUnitAlias("date","D"),addUnitPriority("date",9),addRegexToken("D",match1to2),addRegexToken("DD",match1to2,match2),addRegexToken("Do",(function(isStrict,locale){return isStrict?locale._dayOfMonthOrdinalParse||locale._ordinalParse:locale._dayOfMonthOrdinalParseLenient})),addParseToken(["D","DD"],DATE),addParseToken("Do",(function(input,array){array[DATE]=toInt(input.match(match1to2)[0])}));var getSetDayOfMonth=makeGetSet("Date",!0);function getSetDayOfYear(input){var dayOfYear=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==input?dayOfYear:this.add(input-dayOfYear,"d")}addFormatToken("DDD",["DDDD",3],"DDDo","dayOfYear"),addUnitAlias("dayOfYear","DDD"),addUnitPriority("dayOfYear",4),addRegexToken("DDD",match1to3),addRegexToken("DDDD",match3),addParseToken(["DDD","DDDD"],(function(input,array,config){config._dayOfYear=toInt(input)})),addFormatToken("m",["mm",2],0,"minute"),addUnitAlias("minute","m"),addUnitPriority("minute",14),addRegexToken("m",match1to2),addRegexToken("mm",match1to2,match2),addParseToken(["m","mm"],MINUTE);var getSetMinute=makeGetSet("Minutes",!1);addFormatToken("s",["ss",2],0,"second"),addUnitAlias("second","s"),addUnitPriority("second",15),addRegexToken("s",match1to2),addRegexToken("ss",match1to2,match2),addParseToken(["s","ss"],SECOND);var token,getSetSecond=makeGetSet("Seconds",!1);for(addFormatToken("S",0,0,(function(){return~~(this.millisecond()/100)})),addFormatToken(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),addFormatToken(0,["SSS",3],0,"millisecond"),addFormatToken(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),addFormatToken(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),addFormatToken(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),addFormatToken(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),addFormatToken(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),addFormatToken(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),addUnitAlias("millisecond","ms"),addUnitPriority("millisecond",16),addRegexToken("S",match1to3,match1),addRegexToken("SS",match1to3,match2),addRegexToken("SSS",match1to3,match3),token="SSSS";token.length<=9;token+="S")addRegexToken(token,matchUnsigned);function parseMs(input,array){array[MILLISECOND]=toInt(1e3*("0."+input))}for(token="S";token.length<=9;token+="S")addParseToken(token,parseMs);var getSetMillisecond=makeGetSet("Milliseconds",!1);function getZoneAbbr(){return this._isUTC?"UTC":""}function getZoneName(){return this._isUTC?"Coordinated Universal Time":""}addFormatToken("z",0,0,"zoneAbbr"),addFormatToken("zz",0,0,"zoneName");var proto=Moment.prototype;function createUnix(input){return createLocal(1e3*input)}function createInZone(){return createLocal.apply(null,arguments).parseZone()}function preParsePostFormat(string){return string}proto.add=add,proto.calendar=calendar$1,proto.clone=clone,proto.diff=diff,proto.endOf=endOf,proto.format=format,proto.from=from,proto.fromNow=fromNow,proto.to=to,proto.toNow=toNow,proto.get=stringGet,proto.invalidAt=invalidAt,proto.isAfter=isAfter,proto.isBefore=isBefore,proto.isBetween=isBetween,proto.isSame=isSame,proto.isSameOrAfter=isSameOrAfter,proto.isSameOrBefore=isSameOrBefore,proto.isValid=isValid$2,proto.lang=lang,proto.locale=locale,proto.localeData=localeData,proto.max=prototypeMax,proto.min=prototypeMin,proto.parsingFlags=parsingFlags,proto.set=stringSet,proto.startOf=startOf,proto.subtract=subtract,proto.toArray=toArray,proto.toObject=toObject,proto.toDate=toDate,proto.toISOString=toISOString,proto.inspect=inspect,proto.toJSON=toJSON,proto.toString=toString,proto.unix=unix,proto.valueOf=valueOf,proto.creationData=creationData,proto.year=getSetYear,proto.isLeapYear=getIsLeapYear,proto.weekYear=getSetWeekYear,proto.isoWeekYear=getSetISOWeekYear,proto.quarter=proto.quarters=getSetQuarter,proto.month=getSetMonth,proto.daysInMonth=getDaysInMonth,proto.week=proto.weeks=getSetWeek,proto.isoWeek=proto.isoWeeks=getSetISOWeek,proto.weeksInYear=getWeeksInYear,proto.isoWeeksInYear=getISOWeeksInYear,proto.date=getSetDayOfMonth,proto.day=proto.days=getSetDayOfWeek,proto.weekday=getSetLocaleDayOfWeek,proto.isoWeekday=getSetISODayOfWeek,proto.dayOfYear=getSetDayOfYear,proto.hour=proto.hours=getSetHour,proto.minute=proto.minutes=getSetMinute,proto.second=proto.seconds=getSetSecond,proto.millisecond=proto.milliseconds=getSetMillisecond,proto.utcOffset=getSetOffset,proto.utc=setOffsetToUTC,proto.local=setOffsetToLocal,proto.parseZone=setOffsetToParsedOffset,proto.hasAlignedHourOffset=hasAlignedHourOffset,proto.isDST=isDaylightSavingTime,proto.isLocal=isLocal,proto.isUtcOffset=isUtcOffset,proto.isUtc=isUtc,proto.isUTC=isUtc,proto.zoneAbbr=getZoneAbbr,proto.zoneName=getZoneName,proto.dates=deprecate("dates accessor is deprecated. Use date instead.",getSetDayOfMonth),proto.months=deprecate("months accessor is deprecated. Use month instead",getSetMonth),proto.years=deprecate("years accessor is deprecated. Use year instead",getSetYear),proto.zone=deprecate("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",getSetZone),proto.isDSTShifted=deprecate("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",isDaylightSavingTimeShifted);var proto$1=Locale.prototype;function get$1(format,index,field,setter){var locale=getLocale(),utc=createUTC().set(setter,index);return locale[field](utc,format)}function listMonthsImpl(format,index,field){if(isNumber(format)&&(index=format,format=void 0),format=format||"",null!=index)return get$1(format,index,field,"month");var i,out=[];for(i=0;i<12;i++)out[i]=get$1(format,i,field,"month");return out}function listWeekdaysImpl(localeSorted,format,index,field){"boolean"==typeof localeSorted?(isNumber(format)&&(index=format,format=void 0),format=format||""):(index=format=localeSorted,localeSorted=!1,isNumber(format)&&(index=format,format=void 0),format=format||"");var i,locale=getLocale(),shift=localeSorted?locale._week.dow:0;if(null!=index)return get$1(format,(index+shift)%7,field,"day");var out=[];for(i=0;i<7;i++)out[i]=get$1(format,(i+shift)%7,field,"day");return out}function listMonths(format,index){return listMonthsImpl(format,index,"months")}function listMonthsShort(format,index){return listMonthsImpl(format,index,"monthsShort")}function listWeekdays(localeSorted,format,index){return listWeekdaysImpl(localeSorted,format,index,"weekdays")}function listWeekdaysShort(localeSorted,format,index){return listWeekdaysImpl(localeSorted,format,index,"weekdaysShort")}function listWeekdaysMin(localeSorted,format,index){return listWeekdaysImpl(localeSorted,format,index,"weekdaysMin")}proto$1.calendar=calendar,proto$1.longDateFormat=longDateFormat,proto$1.invalidDate=invalidDate,proto$1.ordinal=ordinal,proto$1.preparse=preParsePostFormat,proto$1.postformat=preParsePostFormat,proto$1.relativeTime=relativeTime,proto$1.pastFuture=pastFuture,proto$1.set=set,proto$1.months=localeMonths,proto$1.monthsShort=localeMonthsShort,proto$1.monthsParse=localeMonthsParse,proto$1.monthsRegex=monthsRegex,proto$1.monthsShortRegex=monthsShortRegex,proto$1.week=localeWeek,proto$1.firstDayOfYear=localeFirstDayOfYear,proto$1.firstDayOfWeek=localeFirstDayOfWeek,proto$1.weekdays=localeWeekdays,proto$1.weekdaysMin=localeWeekdaysMin,proto$1.weekdaysShort=localeWeekdaysShort,proto$1.weekdaysParse=localeWeekdaysParse,proto$1.weekdaysRegex=weekdaysRegex,proto$1.weekdaysShortRegex=weekdaysShortRegex,proto$1.weekdaysMinRegex=weekdaysMinRegex,proto$1.isPM=localeIsPM,proto$1.meridiem=localeMeridiem,getSetGlobalLocale("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(number){var b=number%10;return number+(1===toInt(number%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th")}}),hooks.lang=deprecate("moment.lang is deprecated. Use moment.locale instead.",getSetGlobalLocale),hooks.langData=deprecate("moment.langData is deprecated. Use moment.localeData instead.",getLocale);var mathAbs=Math.abs;function abs(){var data=this._data;return this._milliseconds=mathAbs(this._milliseconds),this._days=mathAbs(this._days),this._months=mathAbs(this._months),data.milliseconds=mathAbs(data.milliseconds),data.seconds=mathAbs(data.seconds),data.minutes=mathAbs(data.minutes),data.hours=mathAbs(data.hours),data.months=mathAbs(data.months),data.years=mathAbs(data.years),this}function addSubtract$1(duration,input,value,direction){var other=createDuration(input,value);return duration._milliseconds+=direction*other._milliseconds,duration._days+=direction*other._days,duration._months+=direction*other._months,duration._bubble()}function add$1(input,value){return addSubtract$1(this,input,value,1)}function subtract$1(input,value){return addSubtract$1(this,input,value,-1)}function absCeil(number){return number<0?Math.floor(number):Math.ceil(number)}function bubble(){var seconds,minutes,hours,years,monthsFromDays,milliseconds=this._milliseconds,days=this._days,months=this._months,data=this._data;return milliseconds>=0&&days>=0&&months>=0||milliseconds<=0&&days<=0&&months<=0||(milliseconds+=864e5*absCeil(monthsToDays(months)+days),days=0,months=0),data.milliseconds=milliseconds%1e3,seconds=absFloor(milliseconds/1e3),data.seconds=seconds%60,minutes=absFloor(seconds/60),data.minutes=minutes%60,hours=absFloor(minutes/60),data.hours=hours%24,days+=absFloor(hours/24),months+=monthsFromDays=absFloor(daysToMonths(days)),days-=absCeil(monthsToDays(monthsFromDays)),years=absFloor(months/12),months%=12,data.days=days,data.months=months,data.years=years,this}function daysToMonths(days){return 4800*days/146097}function monthsToDays(months){return 146097*months/4800}function as(units){if(!this.isValid())return NaN;var days,months,milliseconds=this._milliseconds;if("month"===(units=normalizeUnits(units))||"quarter"===units||"year"===units)switch(days=this._days+milliseconds/864e5,months=this._months+daysToMonths(days),units){case"month":return months;case"quarter":return months/3;case"year":return months/12}else switch(days=this._days+Math.round(monthsToDays(this._months)),units){case"week":return days/7+milliseconds/6048e5;case"day":return days+milliseconds/864e5;case"hour":return 24*days+milliseconds/36e5;case"minute":return 1440*days+milliseconds/6e4;case"second":return 86400*days+milliseconds/1e3;case"millisecond":return Math.floor(864e5*days)+milliseconds;default:throw new Error("Unknown unit "+units)}}function valueOf$1(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*toInt(this._months/12):NaN}function makeAs(alias){return function(){return this.as(alias)}}var asMilliseconds=makeAs("ms"),asSeconds=makeAs("s"),asMinutes=makeAs("m"),asHours=makeAs("h"),asDays=makeAs("d"),asWeeks=makeAs("w"),asMonths=makeAs("M"),asQuarters=makeAs("Q"),asYears=makeAs("y");function clone$1(){return createDuration(this)}function get$2(units){return units=normalizeUnits(units),this.isValid()?this[units+"s"]():NaN}function makeGetter(name){return function(){return this.isValid()?this._data[name]:NaN}}var milliseconds=makeGetter("milliseconds"),seconds=makeGetter("seconds"),minutes=makeGetter("minutes"),hours=makeGetter("hours"),days=makeGetter("days"),months=makeGetter("months"),years=makeGetter("years");function weeks(){return absFloor(this.days()/7)}var round=Math.round,thresholds={ss:44,s:45,m:45,h:22,d:26,M:11};function substituteTimeAgo(string,number,withoutSuffix,isFuture,locale){return locale.relativeTime(number||1,!!withoutSuffix,string,isFuture)}function relativeTime$1(posNegDuration,withoutSuffix,locale){var duration=createDuration(posNegDuration).abs(),seconds=round(duration.as("s")),minutes=round(duration.as("m")),hours=round(duration.as("h")),days=round(duration.as("d")),months=round(duration.as("M")),years=round(duration.as("y")),a=seconds<=thresholds.ss&&["s",seconds]||seconds0,a[4]=locale,substituteTimeAgo.apply(null,a)}function getSetRelativeTimeRounding(roundingFunction){return void 0===roundingFunction?round:"function"==typeof roundingFunction&&(round=roundingFunction,!0)}function getSetRelativeTimeThreshold(threshold,limit){return void 0!==thresholds[threshold]&&(void 0===limit?thresholds[threshold]:(thresholds[threshold]=limit,"s"===threshold&&(thresholds.ss=limit-1),!0))}function humanize(withSuffix){if(!this.isValid())return this.localeData().invalidDate();var locale=this.localeData(),output=relativeTime$1(this,!withSuffix,locale);return withSuffix&&(output=locale.pastFuture(+this,output)),locale.postformat(output)}var abs$1=Math.abs;function sign(x){return(x>0)-(x<0)||+x}function toISOString$1(){if(!this.isValid())return this.localeData().invalidDate();var minutes,hours,seconds=abs$1(this._milliseconds)/1e3,days=abs$1(this._days),months=abs$1(this._months);minutes=absFloor(seconds/60),hours=absFloor(minutes/60),seconds%=60,minutes%=60;var Y=absFloor(months/12),M=months%=12,D=days,h=hours,m=minutes,s=seconds?seconds.toFixed(3).replace(/\.?0+$/,""):"",total=this.asSeconds();if(!total)return"P0D";var totalSign=total<0?"-":"",ymSign=sign(this._months)!==sign(total)?"-":"",daysSign=sign(this._days)!==sign(total)?"-":"",hmsSign=sign(this._milliseconds)!==sign(total)?"-":"";return totalSign+"P"+(Y?ymSign+Y+"Y":"")+(M?ymSign+M+"M":"")+(D?daysSign+D+"D":"")+(h||m||s?"T":"")+(h?hmsSign+h+"H":"")+(m?hmsSign+m+"M":"")+(s?hmsSign+s+"S":"")}var proto$2=Duration.prototype;return proto$2.isValid=isValid$1,proto$2.abs=abs,proto$2.add=add$1,proto$2.subtract=subtract$1,proto$2.as=as,proto$2.asMilliseconds=asMilliseconds,proto$2.asSeconds=asSeconds,proto$2.asMinutes=asMinutes,proto$2.asHours=asHours,proto$2.asDays=asDays,proto$2.asWeeks=asWeeks,proto$2.asMonths=asMonths,proto$2.asQuarters=asQuarters,proto$2.asYears=asYears,proto$2.valueOf=valueOf$1,proto$2._bubble=bubble,proto$2.clone=clone$1,proto$2.get=get$2,proto$2.milliseconds=milliseconds,proto$2.seconds=seconds,proto$2.minutes=minutes,proto$2.hours=hours,proto$2.days=days,proto$2.weeks=weeks,proto$2.months=months,proto$2.years=years,proto$2.humanize=humanize,proto$2.toISOString=toISOString$1,proto$2.toString=toISOString$1,proto$2.toJSON=toISOString$1,proto$2.locale=locale,proto$2.localeData=localeData,proto$2.toIsoString=deprecate("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",toISOString$1),proto$2.lang=lang,addFormatToken("X",0,0,"unix"),addFormatToken("x",0,0,"valueOf"),addRegexToken("x",matchSigned),addRegexToken("X",matchTimestamp),addParseToken("X",(function(input,array,config){config._d=new Date(1e3*parseFloat(input,10))})),addParseToken("x",(function(input,array,config){config._d=new Date(toInt(input))})),hooks.version="2.24.0",setHookCallback(createLocal),hooks.fn=proto,hooks.min=min,hooks.max=max,hooks.now=now,hooks.utc=createUTC,hooks.unix=createUnix,hooks.months=listMonths,hooks.isDate=isDate,hooks.locale=getSetGlobalLocale,hooks.invalid=createInvalid,hooks.duration=createDuration,hooks.isMoment=isMoment,hooks.weekdays=listWeekdays,hooks.parseZone=createInZone,hooks.localeData=getLocale,hooks.isDuration=isDuration,hooks.monthsShort=listMonthsShort,hooks.weekdaysMin=listWeekdaysMin,hooks.defineLocale=defineLocale,hooks.updateLocale=updateLocale,hooks.locales=listLocales,hooks.weekdaysShort=listWeekdaysShort,hooks.normalizeUnits=normalizeUnits,hooks.relativeTimeRounding=getSetRelativeTimeRounding,hooks.relativeTimeThreshold=getSetRelativeTimeThreshold,hooks.calendarFormat=getCalendarFormat,hooks.prototype=proto,hooks.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},hooks}()})),FORMATS={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"};core_adapters._date.override("function"==typeof moment?{_id:"moment",formats:function(){return FORMATS},parse:function(value,format){return"string"==typeof value&&"string"==typeof format?value=moment(value,format):value instanceof moment||(value=moment(value)),value.isValid()?value.valueOf():null},format:function(time,format){return moment(time).format(format)},add:function(time,amount,unit){return moment(time).add(amount,unit).valueOf()},diff:function(max,min,unit){return moment(max).diff(moment(min),unit)},startOf:function(time,unit,weekday){return time=moment(time),"isoWeek"===unit?time.isoWeekday(weekday).valueOf():time.startOf(unit).valueOf()},endOf:function(time,unit){return moment(time).endOf(unit).valueOf()},_create:function(time){return moment(time)}}:{}),core_defaults._set("global",{plugins:{filler:{propagate:!0}}});var mappers={dataset:function(source){var index=source.fill,chart=source.chart,meta=chart.getDatasetMeta(index),points=meta&&chart.isDatasetVisible(index)&&meta.dataset._children||[],length=points.length||0;return length?function(point,i){return i=count)&⌖switch(fill){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return fill;default:return!1}}function computeBoundary(source){return(source.el._scale||{}).getPointPositionForValue?function(source){var start,end,center,i,point,scale=source.el._scale,options=scale.options,length=scale.chart.data.labels.length,fill=source.fill,target=[];if(!length)return null;for(start=options.ticks.reverse?scale.max:scale.min,end=options.ticks.reverse?scale.min:scale.max,center=scale.getPointPositionForValue(0,start),i=0;i0;--i)helpers$1.canvas.lineTo(ctx,curve1[i],curve1[i-1],!0);else for(cx=curve1[0].cx,cy=curve1[0].cy,r=Math.sqrt(Math.pow(curve1[0].x-cx,2)+Math.pow(curve1[0].y-cy,2)),i=len1-1;i>0;--i)ctx.arc(cx,cy,r,curve1[i].angle,curve1[i-1].angle,!0)}}function doFill(ctx,points,mapper,view,color,loop){var i,ilen,index,p0,p1,d0,d1,loopOffset,count=points.length,span=view.spanGaps,curve0=[],curve1=[],len0=0,len1=0;for(ctx.beginPath(),i=0,ilen=count;i=0;--i)(meta=metasets[i].$filler)&&meta.visible&&(view=(el=meta.el)._view,points=el._children||[],mapper=meta.mapper,color=view.backgroundColor||core_defaults.global.defaultColor,mapper&&color&&points.length&&(helpers$1.canvas.clipArea(ctx,chart.chartArea),doFill(ctx,points,mapper,view,color,el._loop),helpers$1.canvas.unclipArea(ctx)))}},getRtlHelper$1=helpers$1.rtl.getRtlAdapter,noop$1=helpers$1.noop,valueOrDefault$e=helpers$1.valueOrDefault;function getBoxWidth(labelOpts,fontSize){return labelOpts.usePointStyle&&labelOpts.boxWidth>fontSize?fontSize:labelOpts.boxWidth}core_defaults._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(e,legendItem){var index=legendItem.datasetIndex,ci=this.chart,meta=ci.getDatasetMeta(index);meta.hidden=null===meta.hidden?!ci.data.datasets[index].hidden:null,ci.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(chart){var datasets=chart.data.datasets,options=chart.options.legend||{},usePointStyle=options.labels&&options.labels.usePointStyle;return chart._getSortedDatasetMetas().map((function(meta){var style=meta.controller.getStyle(usePointStyle?0:void 0);return{text:datasets[meta.index].label,fillStyle:style.backgroundColor,hidden:!chart.isDatasetVisible(meta.index),lineCap:style.borderCapStyle,lineDash:style.borderDash,lineDashOffset:style.borderDashOffset,lineJoin:style.borderJoinStyle,lineWidth:style.borderWidth,strokeStyle:style.borderColor,pointStyle:style.pointStyle,rotation:style.rotation,datasetIndex:meta.index}}),this)}}},legendCallback:function(chart){var i,ilen,listItem,list=document.createElement("ul"),datasets=chart.data.datasets;for(list.setAttribute("class",chart.id+"-legend"),i=0,ilen=datasets.length;iminSize.width)&&(totalHeight+=fontSize+labelOpts.padding,lineWidths[lineWidths.length-(i>0?0:1)]=0),hitboxes[i]={left:0,top:0,width:width,height:fontSize},lineWidths[lineWidths.length-1]+=width+labelOpts.padding})),minSize.height+=totalHeight}else{var vPadding=labelOpts.padding,columnWidths=me.columnWidths=[],columnHeights=me.columnHeights=[],totalWidth=labelOpts.padding,currentColWidth=0,currentColHeight=0;helpers$1.each(me.legendItems,(function(legendItem,i){var itemWidth=getBoxWidth(labelOpts,fontSize)+fontSize/2+ctx.measureText(legendItem.text).width;i>0&¤tColHeight+fontSize+2*vPadding>minSize.height&&(totalWidth+=currentColWidth+labelOpts.padding,columnWidths.push(currentColWidth),columnHeights.push(currentColHeight),currentColWidth=0,currentColHeight=0),currentColWidth=Math.max(currentColWidth,itemWidth),currentColHeight+=fontSize+vPadding,hitboxes[i]={left:0,top:0,width:itemWidth,height:fontSize}})),totalWidth+=currentColWidth,columnWidths.push(currentColWidth),columnHeights.push(currentColHeight),minSize.width+=totalWidth}me.width=minSize.width,me.height=minSize.height}else me.width=minSize.width=me.height=minSize.height=0},afterFit:noop$1,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var me=this,opts=me.options,labelOpts=opts.labels,globalDefaults=core_defaults.global,defaultColor=globalDefaults.defaultColor,lineDefault=globalDefaults.elements.line,legendHeight=me.height,columnHeights=me.columnHeights,legendWidth=me.width,lineWidths=me.lineWidths;if(opts.display){var cursor,rtlHelper=getRtlHelper$1(opts.rtl,me.left,me.minSize.width),ctx=me.ctx,fontColor=valueOrDefault$e(labelOpts.fontColor,globalDefaults.defaultFontColor),labelFont=helpers$1.options._parseFont(labelOpts),fontSize=labelFont.size;ctx.textAlign=rtlHelper.textAlign("left"),ctx.textBaseline="middle",ctx.lineWidth=.5,ctx.strokeStyle=fontColor,ctx.fillStyle=fontColor,ctx.font=labelFont.string;var boxWidth=getBoxWidth(labelOpts,fontSize),hitboxes=me.legendHitBoxes,alignmentOffset=function(dimension,blockSize){switch(opts.align){case"start":return labelOpts.padding;case"end":return dimension-blockSize;default:return(dimension-blockSize+labelOpts.padding)/2}},isHorizontal=me.isHorizontal();cursor=isHorizontal?{x:me.left+alignmentOffset(legendWidth,lineWidths[0]),y:me.top+labelOpts.padding,line:0}:{x:me.left+labelOpts.padding,y:me.top+alignmentOffset(legendHeight,columnHeights[0]),line:0},helpers$1.rtl.overrideTextDirection(me.ctx,opts.textDirection);var itemHeight=fontSize+labelOpts.padding;helpers$1.each(me.legendItems,(function(legendItem,i){var textWidth=ctx.measureText(legendItem.text).width,width=boxWidth+fontSize/2+textWidth,x=cursor.x,y=cursor.y;rtlHelper.setWidth(me.minSize.width),isHorizontal?i>0&&x+width+labelOpts.padding>me.left+me.minSize.width&&(y=cursor.y+=itemHeight,cursor.line++,x=cursor.x=me.left+alignmentOffset(legendWidth,lineWidths[cursor.line])):i>0&&y+itemHeight>me.top+me.minSize.height&&(x=cursor.x=x+me.columnWidths[cursor.line]+labelOpts.padding,cursor.line++,y=cursor.y=me.top+alignmentOffset(legendHeight,columnHeights[cursor.line]));var realX=rtlHelper.x(x);!function(x,y,legendItem){if(!(isNaN(boxWidth)||boxWidth<=0)){ctx.save();var lineWidth=valueOrDefault$e(legendItem.lineWidth,lineDefault.borderWidth);if(ctx.fillStyle=valueOrDefault$e(legendItem.fillStyle,defaultColor),ctx.lineCap=valueOrDefault$e(legendItem.lineCap,lineDefault.borderCapStyle),ctx.lineDashOffset=valueOrDefault$e(legendItem.lineDashOffset,lineDefault.borderDashOffset),ctx.lineJoin=valueOrDefault$e(legendItem.lineJoin,lineDefault.borderJoinStyle),ctx.lineWidth=lineWidth,ctx.strokeStyle=valueOrDefault$e(legendItem.strokeStyle,defaultColor),ctx.setLineDash&&ctx.setLineDash(valueOrDefault$e(legendItem.lineDash,lineDefault.borderDash)),labelOpts&&labelOpts.usePointStyle){var radius=boxWidth*Math.SQRT2/2,centerX=rtlHelper.xPlus(x,boxWidth/2),centerY=y+fontSize/2;helpers$1.canvas.drawPoint(ctx,legendItem.pointStyle,radius,centerX,centerY,legendItem.rotation)}else ctx.fillRect(rtlHelper.leftForLtr(x,boxWidth),y,boxWidth,fontSize),0!==lineWidth&&ctx.strokeRect(rtlHelper.leftForLtr(x,boxWidth),y,boxWidth,fontSize);ctx.restore()}}(realX,y,legendItem),hitboxes[i].left=rtlHelper.leftForLtr(realX,hitboxes[i].width),hitboxes[i].top=y,function(x,y,legendItem,textWidth){var halfFontSize=fontSize/2,xLeft=rtlHelper.xPlus(x,boxWidth+halfFontSize),yMiddle=y+halfFontSize;ctx.fillText(legendItem.text,xLeft,yMiddle),legendItem.hidden&&(ctx.beginPath(),ctx.lineWidth=2,ctx.moveTo(xLeft,yMiddle),ctx.lineTo(rtlHelper.xPlus(xLeft,textWidth),yMiddle),ctx.stroke())}(realX,y,legendItem,textWidth),isHorizontal?cursor.x+=width+labelOpts.padding:cursor.y+=itemHeight})),helpers$1.rtl.restoreTextDirection(me.ctx,opts.textDirection)}},_getLegendItemAt:function(x,y){var i,hitBox,lh,me=this;if(x>=me.left&&x<=me.right&&y>=me.top&&y<=me.bottom)for(lh=me.legendHitBoxes,i=0;i=(hitBox=lh[i]).left&&x<=hitBox.left+hitBox.width&&y>=hitBox.top&&y<=hitBox.top+hitBox.height)return me.legendItems[i];return null},handleEvent:function(e){var hoveredItem,me=this,opts=me.options,type="mouseup"===e.type?"click":e.type;if("mousemove"===type){if(!opts.onHover&&!opts.onLeave)return}else{if("click"!==type)return;if(!opts.onClick)return}hoveredItem=me._getLegendItemAt(e.x,e.y),"click"===type?hoveredItem&&opts.onClick&&opts.onClick.call(me,e.native,hoveredItem):(opts.onLeave&&hoveredItem!==me._hoveredItem&&(me._hoveredItem&&opts.onLeave.call(me,e.native,me._hoveredItem),me._hoveredItem=hoveredItem),opts.onHover&&hoveredItem&&opts.onHover.call(me,e.native,hoveredItem))}});function createNewLegendAndAttach(chart,legendOpts){var legend=new Legend({ctx:chart.ctx,options:legendOpts,chart:chart});core_layouts.configure(chart,legend,legendOpts),core_layouts.addBox(chart,legend),chart.legend=legend}var plugin_legend={id:"legend",_element:Legend,beforeInit:function(chart){var legendOpts=chart.options.legend;legendOpts&&createNewLegendAndAttach(chart,legendOpts)},beforeUpdate:function(chart){var legendOpts=chart.options.legend,legend=chart.legend;legendOpts?(helpers$1.mergeIf(legendOpts,core_defaults.global.legend),legend?(core_layouts.configure(chart,legend,legendOpts),legend.options=legendOpts):createNewLegendAndAttach(chart,legendOpts)):legend&&(core_layouts.removeBox(chart,legend),delete chart.legend)},afterEvent:function(chart,e){var legend=chart.legend;legend&&legend.handleEvent(e)}},noop$2=helpers$1.noop;core_defaults._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Title=core_element.extend({initialize:function(config){helpers$1.extend(this,config),this.legendHitBoxes=[]},beforeUpdate:noop$2,update:function(maxWidth,maxHeight,margins){var me=this;return me.beforeUpdate(),me.maxWidth=maxWidth,me.maxHeight=maxHeight,me.margins=margins,me.beforeSetDimensions(),me.setDimensions(),me.afterSetDimensions(),me.beforeBuildLabels(),me.buildLabels(),me.afterBuildLabels(),me.beforeFit(),me.fit(),me.afterFit(),me.afterUpdate(),me.minSize},afterUpdate:noop$2,beforeSetDimensions:noop$2,setDimensions:function(){var me=this;me.isHorizontal()?(me.width=me.maxWidth,me.left=0,me.right=me.width):(me.height=me.maxHeight,me.top=0,me.bottom=me.height),me.paddingLeft=0,me.paddingTop=0,me.paddingRight=0,me.paddingBottom=0,me.minSize={width:0,height:0}},afterSetDimensions:noop$2,beforeBuildLabels:noop$2,buildLabels:noop$2,afterBuildLabels:noop$2,beforeFit:noop$2,fit:function(){var textSize,me=this,opts=me.options,minSize=me.minSize={},isHorizontal=me.isHorizontal();opts.display?(textSize=(helpers$1.isArray(opts.text)?opts.text.length:1)*helpers$1.options._parseFont(opts).lineHeight+2*opts.padding,me.width=minSize.width=isHorizontal?me.maxWidth:textSize,me.height=minSize.height=isHorizontal?textSize:me.maxHeight):me.width=minSize.width=me.height=minSize.height=0},afterFit:noop$2,isHorizontal:function(){var pos=this.options.position;return"top"===pos||"bottom"===pos},draw:function(){var me=this,ctx=me.ctx,opts=me.options;if(opts.display){var maxWidth,titleX,titleY,fontOpts=helpers$1.options._parseFont(opts),lineHeight=fontOpts.lineHeight,offset=lineHeight/2+opts.padding,rotation=0,top=me.top,left=me.left,bottom=me.bottom,right=me.right;ctx.fillStyle=helpers$1.valueOrDefault(opts.fontColor,core_defaults.global.defaultFontColor),ctx.font=fontOpts.string,me.isHorizontal()?(titleX=left+(right-left)/2,titleY=top+offset,maxWidth=right-left):(titleX="left"===opts.position?left+offset:right-offset,titleY=top+(bottom-top)/2,maxWidth=bottom-top,rotation=Math.PI*("left"===opts.position?-.5:.5)),ctx.save(),ctx.translate(titleX,titleY),ctx.rotate(rotation),ctx.textAlign="center",ctx.textBaseline="middle";var text=opts.text;if(helpers$1.isArray(text))for(var y=0,i=0;i=0;i--){var currentItem=arrayToSearch[i];if(filterCallback(currentItem))return currentItem}},helpers$1.isNumber=function(n){return!isNaN(parseFloat(n))&&isFinite(n)},helpers$1.almostEquals=function(x,y,epsilon){return Math.abs(x-y)=x},helpers$1.max=function(array){return array.reduce((function(max,value){return isNaN(value)?max:Math.max(max,value)}),Number.NEGATIVE_INFINITY)},helpers$1.min=function(array){return array.reduce((function(min,value){return isNaN(value)?min:Math.min(min,value)}),Number.POSITIVE_INFINITY)},helpers$1.sign=Math.sign?function(x){return Math.sign(x)}:function(x){return 0===(x=+x)||isNaN(x)?x:x>0?1:-1},helpers$1.toRadians=function(degrees){return degrees*(Math.PI/180)},helpers$1.toDegrees=function(radians){return radians*(180/Math.PI)},helpers$1._decimalPlaces=function(x){if(helpers$1.isFinite(x)){for(var e=1,p=0;Math.round(x*e)/e!==x;)e*=10,p++;return p}},helpers$1.getAngleFromPoint=function(centrePoint,anglePoint){var distanceFromXCenter=anglePoint.x-centrePoint.x,distanceFromYCenter=anglePoint.y-centrePoint.y,radialDistanceFromCenter=Math.sqrt(distanceFromXCenter*distanceFromXCenter+distanceFromYCenter*distanceFromYCenter),angle=Math.atan2(distanceFromYCenter,distanceFromXCenter);return angle<-.5*Math.PI&&(angle+=2*Math.PI),{angle:angle,distance:radialDistanceFromCenter}},helpers$1.distanceBetweenPoints=function(pt1,pt2){return Math.sqrt(Math.pow(pt2.x-pt1.x,2)+Math.pow(pt2.y-pt1.y,2))},helpers$1.aliasPixel=function(pixelWidth){return pixelWidth%2==0?0:.5},helpers$1._alignPixel=function(chart,pixel,width){var devicePixelRatio=chart.currentDevicePixelRatio,halfWidth=width/2;return Math.round((pixel-halfWidth)*devicePixelRatio)/devicePixelRatio+halfWidth},helpers$1.splineCurve=function(firstPoint,middlePoint,afterPoint,t){var previous=firstPoint.skip?middlePoint:firstPoint,current=middlePoint,next=afterPoint.skip?middlePoint:afterPoint,d01=Math.sqrt(Math.pow(current.x-previous.x,2)+Math.pow(current.y-previous.y,2)),d12=Math.sqrt(Math.pow(next.x-current.x,2)+Math.pow(next.y-current.y,2)),s01=d01/(d01+d12),s12=d12/(d01+d12),fa=t*(s01=isNaN(s01)?0:s01),fb=t*(s12=isNaN(s12)?0:s12);return{previous:{x:current.x-fa*(next.x-previous.x),y:current.y-fa*(next.y-previous.y)},next:{x:current.x+fb*(next.x-previous.x),y:current.y+fb*(next.y-previous.y)}}},helpers$1.EPSILON=Number.EPSILON||1e-14,helpers$1.splineCurveMonotone=function(points){var i,pointBefore,pointCurrent,pointAfter,alphaK,betaK,tauK,squaredMagnitude,deltaX,pointsWithTangents=(points||[]).map((function(point){return{model:point._model,deltaK:0,mK:0}})),pointsLen=pointsWithTangents.length;for(i=0;i0?pointsWithTangents[i-1]:null,(pointAfter=i0?pointsWithTangents[i-1]:null,pointAfter=i=collection.length-1?collection[0]:collection[index+1]:index>=collection.length-1?collection[collection.length-1]:collection[index+1]},helpers$1.previousItem=function(collection,index,loop){return loop?index<=0?collection[collection.length-1]:collection[index-1]:index<=0?collection[0]:collection[index-1]},helpers$1.niceNum=function(range,round){var exponent=Math.floor(helpers$1.log10(range)),fraction=range/Math.pow(10,exponent);return(round?fraction<1.5?1:fraction<3?2:fraction<7?5:10:fraction<=1?1:fraction<=2?2:fraction<=5?5:10)*Math.pow(10,exponent)},helpers$1.requestAnimFrame="undefined"==typeof window?function(callback){callback()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(callback){return window.setTimeout(callback,1e3/60)},helpers$1.getRelativePosition=function(evt,chart){var mouseX,mouseY,e=evt.originalEvent||evt,canvas=evt.target||evt.srcElement,boundingRect=canvas.getBoundingClientRect(),touches=e.touches;touches&&touches.length>0?(mouseX=touches[0].clientX,mouseY=touches[0].clientY):(mouseX=e.clientX,mouseY=e.clientY);var paddingLeft=parseFloat(helpers$1.getStyle(canvas,"padding-left")),paddingTop=parseFloat(helpers$1.getStyle(canvas,"padding-top")),paddingRight=parseFloat(helpers$1.getStyle(canvas,"padding-right")),paddingBottom=parseFloat(helpers$1.getStyle(canvas,"padding-bottom")),width=boundingRect.right-boundingRect.left-paddingLeft-paddingRight,height=boundingRect.bottom-boundingRect.top-paddingTop-paddingBottom;return{x:mouseX=Math.round((mouseX-boundingRect.left-paddingLeft)/width*canvas.width/chart.currentDevicePixelRatio),y:mouseY=Math.round((mouseY-boundingRect.top-paddingTop)/height*canvas.height/chart.currentDevicePixelRatio)}},helpers$1.getConstraintWidth=function(domNode){return getConstraintDimension(domNode,"max-width","clientWidth")},helpers$1.getConstraintHeight=function(domNode){return getConstraintDimension(domNode,"max-height","clientHeight")},helpers$1._calculatePadding=function(container,padding,parentDimension){return(padding=helpers$1.getStyle(container,padding)).indexOf("%")>-1?parentDimension*parseInt(padding,10)/100:parseInt(padding,10)},helpers$1._getParentNode=function(domNode){var parent=domNode.parentNode;return parent&&"[object ShadowRoot]"===parent.toString()&&(parent=parent.host),parent},helpers$1.getMaximumWidth=function(domNode){var container=helpers$1._getParentNode(domNode);if(!container)return domNode.clientWidth;var clientWidth=container.clientWidth,w=clientWidth-helpers$1._calculatePadding(container,"padding-left",clientWidth)-helpers$1._calculatePadding(container,"padding-right",clientWidth),cw=helpers$1.getConstraintWidth(domNode);return isNaN(cw)?w:Math.min(w,cw)},helpers$1.getMaximumHeight=function(domNode){var container=helpers$1._getParentNode(domNode);if(!container)return domNode.clientHeight;var clientHeight=container.clientHeight,h=clientHeight-helpers$1._calculatePadding(container,"padding-top",clientHeight)-helpers$1._calculatePadding(container,"padding-bottom",clientHeight),ch=helpers$1.getConstraintHeight(domNode);return isNaN(ch)?h:Math.min(h,ch)},helpers$1.getStyle=function(el,property){return el.currentStyle?el.currentStyle[property]:document.defaultView.getComputedStyle(el,null).getPropertyValue(property)},helpers$1.retinaScale=function(chart,forceRatio){var pixelRatio=chart.currentDevicePixelRatio=forceRatio||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==pixelRatio){var canvas=chart.canvas,height=chart.height,width=chart.width;canvas.height=height*pixelRatio,canvas.width=width*pixelRatio,chart.ctx.scale(pixelRatio,pixelRatio),canvas.style.height||canvas.style.width||(canvas.style.height=height+"px",canvas.style.width=width+"px")}},helpers$1.fontString=function(pixelSize,fontStyle,fontFamily){return fontStyle+" "+pixelSize+"px "+fontFamily},helpers$1.longestText=function(ctx,font,arrayOfThings,cache){var data=(cache=cache||{}).data=cache.data||{},gc=cache.garbageCollect=cache.garbageCollect||[];cache.font!==font&&(data=cache.data={},gc=cache.garbageCollect=[],cache.font=font),ctx.font=font;var i,j,jlen,thing,nestedThing,longest=0,ilen=arrayOfThings.length;for(i=0;iarrayOfThings.length){for(i=0;ilongest&&(longest=textWidth),longest},helpers$1.numberOfLabelLines=function(arrayOfThings){var numberOfLines=1;return helpers$1.each(arrayOfThings,(function(thing){helpers$1.isArray(thing)&&thing.length>numberOfLines&&(numberOfLines=thing.length)})),numberOfLines},helpers$1.color=chartjsColor?function(value){return value instanceof CanvasGradient&&(value=core_defaults.global.defaultColor),chartjsColor(value)}:function(value){return console.error("Color.js not found!"),value},helpers$1.getHoverColor=function(colorValue){return colorValue instanceof CanvasPattern||colorValue instanceof CanvasGradient?colorValue:helpers$1.color(colorValue).saturate(.5).darken(.1).rgbString()}}(),core_controller._adapters=core_adapters,core_controller.Animation=core_animation,core_controller.animationService=core_animations,core_controller.controllers=controllers,core_controller.DatasetController=core_datasetController,core_controller.defaults=core_defaults,core_controller.Element=core_element,core_controller.elements=elements,core_controller.Interaction=core_interaction,core_controller.layouts=core_layouts,core_controller.platform=platform,core_controller.plugins=core_plugins,core_controller.Scale=core_scale,core_controller.scaleService=core_scaleService,core_controller.Ticks=core_ticks,core_controller.Tooltip=core_tooltip,core_controller.helpers.each(scales,(function(scale,type){core_controller.scaleService.registerScaleType(type,scale,scale._defaults)})),plugins)plugins.hasOwnProperty(k)&&core_controller.plugins.register(plugins[k]);core_controller.platform.initialize();var src=core_controller;return"undefined"!=typeof window&&(window.Chart=core_controller),core_controller.Chart=core_controller,core_controller.Legend=plugins.legend._element,core_controller.Title=plugins.title._element,core_controller.pluginService=core_controller.plugins,core_controller.PluginBase=core_controller.Element.extend({}),core_controller.canvasHelpers=core_controller.helpers.canvas,core_controller.layoutService=core_controller.layouts,core_controller.LinearScaleBase=scale_linearbase,core_controller.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(klass){core_controller[klass]=function(ctx,cfg){return new core_controller(ctx,core_controller.helpers.merge(cfg||{},{type:klass.charAt(0).toLowerCase()+klass.slice(1)}))}})),src})); +var global,factory;global=window,factory=function(){const requestAnimFrame="undefined"==typeof window?function(callback){return callback()}:window.requestAnimationFrame;function throttled(fn,thisArg,updateFn){const updateArgs=updateFn||(args=>Array.prototype.slice.call(args));let ticking=!1,args=[];return function(){for(var _len=arguments.length,rest=new Array(_len),_key=0;_key<_len;_key++)rest[_key]=arguments[_key];args=updateArgs(rest),ticking||(ticking=!0,requestAnimFrame.call(window,(()=>{ticking=!1,fn.apply(thisArg,args)})))}}function debounce(fn,delay){let timeout;return function(){for(var _len2=arguments.length,args=new Array(_len2),_key2=0;_key2<_len2;_key2++)args[_key2]=arguments[_key2];return delay?(clearTimeout(timeout),timeout=setTimeout(fn,delay,args)):fn.apply(this,args),delay}}const _toLeftRightCenter=align=>"start"===align?"left":"end"===align?"right":"center",_alignStartEnd=(align,start,end)=>"start"===align?start:"end"===align?end:(start+end)/2,_textX=(align,left,right,rtl)=>align===(rtl?"left":"right")?right:"center"===align?(left+right)/2:left;var animator=new class{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(chart,anims,date,type){const callbacks=anims.listeners[type],numSteps=anims.duration;callbacks.forEach((fn=>fn({chart:chart,initial:anims.initial,numSteps:numSteps,currentStep:Math.min(date-anims.start,numSteps)})))}_refresh(){this._request||(this._running=!0,this._request=requestAnimFrame.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(){let date=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Date.now(),remaining=0;this._charts.forEach(((anims,chart)=>{if(!anims.running||!anims.items.length)return;const items=anims.items;let item,i=items.length-1,draw=!1;for(;i>=0;--i)item=items[i],item._active?(item._total>anims.duration&&(anims.duration=item._total),item.tick(date),draw=!0):(items[i]=items[items.length-1],items.pop());draw&&(chart.draw(),this._notify(chart,anims,date,"progress")),items.length||(anims.running=!1,this._notify(chart,anims,date,"complete"),anims.initial=!1),remaining+=items.length})),this._lastDate=date,0===remaining&&(this._running=!1)}_getAnims(chart){const charts=this._charts;let anims=charts.get(chart);return anims||(anims={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},charts.set(chart,anims)),anims}listen(chart,event,cb){this._getAnims(chart).listeners[event].push(cb)}add(chart,items){items&&items.length&&this._getAnims(chart).items.push(...items)}has(chart){return this._getAnims(chart).items.length>0}start(chart){const anims=this._charts.get(chart);anims&&(anims.running=!0,anims.start=Date.now(),anims.duration=anims.items.reduce(((acc,cur)=>Math.max(acc,cur._duration)),0),this._refresh())}running(chart){if(!this._running)return!1;const anims=this._charts.get(chart);return!!(anims&&anims.running&&anims.items.length)}stop(chart){const anims=this._charts.get(chart);if(!anims||!anims.items.length)return;const items=anims.items;let i=items.length-1;for(;i>=0;--i)items[i].cancel();anims.items=[],this._notify(chart,anims,Date.now(),"complete")}remove(chart){return this._charts.delete(chart)}};function round(v){return v+.5|0}const lim=(v,l,h)=>Math.max(Math.min(v,h),l);function p2b(v){return lim(round(2.55*v),0,255)}function n2b(v){return lim(round(255*v),0,255)}function b2n(v){return lim(round(v/2.55)/100,0,1)}function n2p(v){return lim(round(100*v),0,100)}const map$1={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},hex=[..."0123456789ABCDEF"],h1=b=>hex[15&b],h2=b=>hex[(240&b)>>4]+hex[15&b],eq=b=>(240&b)>>4==(15&b);function hexString(v){var f=(v=>eq(v.r)&&eq(v.g)&&eq(v.b)&&eq(v.a))(v)?h1:h2;return v?"#"+f(v.r)+f(v.g)+f(v.b)+((a,f)=>a<255?f(a):"")(v.a,f):void 0}const HUE_RE=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function hsl2rgbn(h,s,l){const a=s*Math.min(l,1-l),f=function(n){let k=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(n+h/30)%12;return l-a*Math.max(Math.min(k-3,9-k,1),-1)};return[f(0),f(8),f(4)]}function hsv2rgbn(h,s,v){const f=function(n){let k=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(n+h/60)%6;return v-v*s*Math.max(Math.min(k,4-k,1),0)};return[f(5),f(3),f(1)]}function hwb2rgbn(h,w,b){const rgb=hsl2rgbn(h,1,.5);let i;for(w+b>1&&(i=1/(w+b),w*=i,b*=i),i=0;i<3;i++)rgb[i]*=1-w-b,rgb[i]+=w;return rgb}function rgb2hsl(v){const r=v.r/255,g=v.g/255,b=v.b/255,max=Math.max(r,g,b),min=Math.min(r,g,b),l=(max+min)/2;let h,s,d;return max!==min&&(d=max-min,s=l>.5?d/(2-max-min):d/(max+min),h=function(r,g,b,d,max){return r===max?(g-b)/d+(g>16&255,k>>8&255,255&k]}return unpacked}(),names.transparent=[0,0,0,0]);const a=names[str.toLowerCase()];return a&&{r:a[0],g:a[1],b:a[2],a:4===a.length?a[3]:255}}const RGB_RE=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/,to=v=>v<=.0031308?12.92*v:1.055*Math.pow(v,1/2.4)-.055,from=v=>v<=.04045?v/12.92:Math.pow((v+.055)/1.055,2.4);function modHSL(v,i,ratio){if(v){let tmp=rgb2hsl(v);tmp[i]=Math.max(0,Math.min(tmp[i]+tmp[i]*ratio,0===i?360:1)),tmp=hsl2rgb(tmp),v.r=tmp[0],v.g=tmp[1],v.b=tmp[2]}}function clone$1(v,proto){return v?Object.assign(proto||{},v):v}function fromObject(input){var v={r:0,g:0,b:0,a:255};return Array.isArray(input)?input.length>=3&&(v={r:input[0],g:input[1],b:input[2],a:255},input.length>3&&(v.a=n2b(input[3]))):(v=clone$1(input,{r:0,g:0,b:0,a:1})).a=n2b(v.a),v}function functionParse(str){return"r"===str.charAt(0)?function(str){const m=RGB_RE.exec(str);let r,g,b,a=255;if(m){if(m[7]!==r){const v=+m[7];a=m[8]?p2b(v):lim(255*v,0,255)}return r=+m[1],g=+m[3],b=+m[5],r=255&(m[2]?p2b(r):lim(r,0,255)),g=255&(m[4]?p2b(g):lim(g,0,255)),b=255&(m[6]?p2b(b):lim(b,0,255)),{r:r,g:g,b:b,a:a}}}(str):hueParse(str)}class Color{constructor(input){if(input instanceof Color)return input;const type=typeof input;let v;var str,ret,len;"object"===type?v=fromObject(input):"string"===type&&(len=(str=input).length,"#"===str[0]&&(4===len||5===len?ret={r:255&17*map$1[str[1]],g:255&17*map$1[str[2]],b:255&17*map$1[str[3]],a:5===len?17*map$1[str[4]]:255}:7!==len&&9!==len||(ret={r:map$1[str[1]]<<4|map$1[str[2]],g:map$1[str[3]]<<4|map$1[str[4]],b:map$1[str[5]]<<4|map$1[str[6]],a:9===len?map$1[str[7]]<<4|map$1[str[8]]:255})),v=ret||nameParse(input)||functionParse(input)),this._rgb=v,this._valid=!!v}get valid(){return this._valid}get rgb(){var v=clone$1(this._rgb);return v&&(v.a=b2n(v.a)),v}set rgb(obj){this._rgb=fromObject(obj)}rgbString(){return this._valid?(v=this._rgb)&&(v.a<255?"rgba(".concat(v.r,", ").concat(v.g,", ").concat(v.b,", ").concat(b2n(v.a),")"):"rgb(".concat(v.r,", ").concat(v.g,", ").concat(v.b,")")):void 0;var v}hexString(){return this._valid?hexString(this._rgb):void 0}hslString(){return this._valid?function(v){if(!v)return;const a=rgb2hsl(v),h=a[0],s=n2p(a[1]),l=n2p(a[2]);return v.a<255?"hsla(".concat(h,", ").concat(s,"%, ").concat(l,"%, ").concat(b2n(v.a),")"):"hsl(".concat(h,", ").concat(s,"%, ").concat(l,"%)")}(this._rgb):void 0}mix(color,weight){if(color){const c1=this.rgb,c2=color.rgb;let w2;const p=weight===w2?.5:weight,w=2*p-1,a=c1.a-c2.a,w1=((w*a==-1?w:(w+a)/(1+w*a))+1)/2;w2=1-w1,c1.r=255&w1*c1.r+w2*c2.r+.5,c1.g=255&w1*c1.g+w2*c2.g+.5,c1.b=255&w1*c1.b+w2*c2.b+.5,c1.a=p*c1.a+(1-p)*c2.a,this.rgb=c1}return this}interpolate(color,t){return color&&(this._rgb=function(rgb1,rgb2,t){const r=from(b2n(rgb1.r)),g=from(b2n(rgb1.g)),b=from(b2n(rgb1.b));return{r:n2b(to(r+t*(from(b2n(rgb2.r))-r))),g:n2b(to(g+t*(from(b2n(rgb2.g))-g))),b:n2b(to(b+t*(from(b2n(rgb2.b))-b))),a:rgb1.a+t*(rgb2.a-rgb1.a)}}(this._rgb,color._rgb,t)),this}clone(){return new Color(this.rgb)}alpha(a){return this._rgb.a=n2b(a),this}clearer(ratio){return this._rgb.a*=1-ratio,this}greyscale(){const rgb=this._rgb,val=round(.3*rgb.r+.59*rgb.g+.11*rgb.b);return rgb.r=rgb.g=rgb.b=val,this}opaquer(ratio){return this._rgb.a*=1+ratio,this}negate(){const v=this._rgb;return v.r=255-v.r,v.g=255-v.g,v.b=255-v.b,this}lighten(ratio){return modHSL(this._rgb,2,ratio),this}darken(ratio){return modHSL(this._rgb,2,-ratio),this}saturate(ratio){return modHSL(this._rgb,1,ratio),this}desaturate(ratio){return modHSL(this._rgb,1,-ratio),this}rotate(deg){return function(v,deg){var h=rgb2hsl(v);h[0]=hue(h[0]+deg),h=hsl2rgb(h),v.r=h[0],v.g=h[1],v.b=h[2]}(this._rgb,deg),this}}function index_esm(input){return new Color(input)}function isPatternOrGradient(value){if(value&&"object"==typeof value){const type=value.toString();return"[object CanvasPattern]"===type||"[object CanvasGradient]"===type}return!1}function color(value){return isPatternOrGradient(value)?value:index_esm(value)}function getHoverColor(value){return isPatternOrGradient(value)?value:index_esm(value).saturate(.5).darken(.1).hexString()}function noop(){}const uid=function(){let id=0;return function(){return id++}}();function isNullOrUndef(value){return null==value}function isArray(value){if(Array.isArray&&Array.isArray(value))return!0;const type=Object.prototype.toString.call(value);return"[object"===type.slice(0,7)&&"Array]"===type.slice(-6)}function isObject(value){return null!==value&&"[object Object]"===Object.prototype.toString.call(value)}const isNumberFinite=value=>("number"==typeof value||value instanceof Number)&&isFinite(+value);function finiteOrDefault(value,defaultValue){return isNumberFinite(value)?value:defaultValue}function valueOrDefault(value,defaultValue){return void 0===value?defaultValue:value}const toPercentage=(value,dimension)=>"string"==typeof value&&value.endsWith("%")?parseFloat(value)/100:value/dimension,toDimension=(value,dimension)=>"string"==typeof value&&value.endsWith("%")?parseFloat(value)/100*dimension:+value;function callback(fn,args,thisArg){if(fn&&"function"==typeof fn.call)return fn.apply(thisArg,args)}function each(loopable,fn,thisArg,reverse){let i,len,keys;if(isArray(loopable))if(len=loopable.length,reverse)for(i=len-1;i>=0;i--)fn.call(thisArg,loopable[i],i);else for(i=0;ipos;)obj=obj[key.slice(pos,idx)],pos=idx+1,idx=indexOfDotOrLength(key,pos);return obj}function _capitalize(str){return str.charAt(0).toUpperCase()+str.slice(1)}const defined=value=>void 0!==value,isFunction=value=>"function"==typeof value,setsEqual=(a,b)=>{if(a.size!==b.size)return!1;for(const item of a)if(!b.has(item))return!1;return!0};function _isClickEvent(e){return"mouseup"===e.type||"click"===e.type||"contextmenu"===e.type}const overrides=Object.create(null),descriptors=Object.create(null);function getScope$1(node,key){if(!key)return node;const keys=key.split(".");for(let i=0,n=keys.length;icontext.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(ctx,options)=>getHoverColor(options.backgroundColor),this.hoverBorderColor=(ctx,options)=>getHoverColor(options.borderColor),this.hoverColor=(ctx,options)=>getHoverColor(options.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(_descriptors)}set(scope,values){return set(this,scope,values)}get(scope){return getScope$1(this,scope)}describe(scope,values){return set(descriptors,scope,values)}override(scope,values){return set(overrides,scope,values)}route(scope,name,targetScope,targetName){const scopeObject=getScope$1(this,scope),targetScopeObject=getScope$1(this,targetScope),privateName="_"+name;Object.defineProperties(scopeObject,{[privateName]:{value:scopeObject[name],writable:!0},[name]:{enumerable:!0,get(){const local=this[privateName],target=targetScopeObject[targetName];return isObject(local)?Object.assign({},target,local):valueOrDefault(local,target)},set(value){this[privateName]=value}}})}}({_scriptable:name=>!name.startsWith("on"),_indexable:name=>"events"!==name,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}});function _lookup(table,value,cmp){cmp=cmp||(index=>table[index]1;)mid=lo+hi>>1,cmp(mid)?lo=mid:hi=mid;return{lo:lo,hi:hi}}const _lookupByKey=(table,key,value)=>_lookup(table,value,(index=>table[index][key]_lookup(table,value,(index=>table[index][key]>=value));function _filterBetween(values,min,max){let start=0,end=values.length;for(;startstart&&values[end-1]>max;)end--;return start>0||end{const method="_onData"+_capitalize(key),base=array[key];Object.defineProperty(array,key,{configurable:!0,enumerable:!1,value(){for(var _len3=arguments.length,args=new Array(_len3),_key3=0;_key3<_len3;_key3++)args[_key3]=arguments[_key3];const res=base.apply(this,args);return array._chartjs.listeners.forEach((object=>{"function"==typeof object[method]&&object[method](...args)})),res}})})))}function unlistenArrayEvents(array,listener){const stub=array._chartjs;if(!stub)return;const listeners=stub.listeners,index=listeners.indexOf(listener);-1!==index&&listeners.splice(index,1),listeners.length>0||(arrayEvents.forEach((key=>{delete array[key]})),delete array._chartjs)}function _arrayUnique(items){const set=new Set;let i,ilen;for(i=0,ilen=items.length;ia-b)).pop(),result}function isNumber(n){return!isNaN(parseFloat(n))&&isFinite(n)}function almostEquals(x,y,epsilon){return Math.abs(x-y)=x}function _setMinAndMaxByKey(array,target,property){let i,ilen,value;for(i=0,ilen=array.length;iangleToEnd&&startToAngle3&&void 0!==arguments[3]?arguments[3]:1e-6;return value>=Math.min(start,end)-epsilon&&value<=Math.max(start,end)+epsilon}function _isDomSupported(){return"undefined"!=typeof window&&"undefined"!=typeof document}function _getParentNode(domNode){let parent=domNode.parentNode;return parent&&"[object ShadowRoot]"===parent.toString()&&(parent=parent.host),parent}function parseMaxStyle(styleValue,node,parentProperty){let valueInPixels;return"string"==typeof styleValue?(valueInPixels=parseInt(styleValue,10),-1!==styleValue.indexOf("%")&&(valueInPixels=valueInPixels/100*node.parentNode[parentProperty])):valueInPixels=styleValue,valueInPixels}const getComputedStyle=element=>window.getComputedStyle(element,null);function getStyle(el,property){return getComputedStyle(el).getPropertyValue(property)}const positions=["top","right","bottom","left"];function getPositionedStyle(styles,style,suffix){const result={};suffix=suffix?"-"+suffix:"";for(let i=0;i<4;i++){const pos=positions[i];result[pos]=parseFloat(styles[style+"-"+pos+suffix])||0}return result.width=result.left+result.right,result.height=result.top+result.bottom,result}function getRelativePosition(evt,chart){if("native"in evt)return evt;const{canvas:canvas,currentDevicePixelRatio:currentDevicePixelRatio}=chart,style=getComputedStyle(canvas),borderBox="border-box"===style.boxSizing,paddings=getPositionedStyle(style,"padding"),borders=getPositionedStyle(style,"border","width"),{x:x,y:y,box:box}=function(e,canvas){const touches=e.touches,source=touches&&touches.length?touches[0]:e,{offsetX:offsetX,offsetY:offsetY}=source;let x,y,box=!1;if(((x,y,target)=>(x>0||y>0)&&(!target||!target.shadowRoot))(offsetX,offsetY,e.target))x=offsetX,y=offsetY;else{const rect=canvas.getBoundingClientRect();x=source.clientX-rect.left,y=source.clientY-rect.top,box=!0}return{x:x,y:y,box:box}}(evt,canvas),xOffset=paddings.left+(box&&borders.left),yOffset=paddings.top+(box&&borders.top);let{width:width,height:height}=chart;return borderBox&&(width-=paddings.width+borders.width,height-=paddings.height+borders.height),{x:Math.round((x-xOffset)/width*canvas.width/currentDevicePixelRatio),y:Math.round((y-yOffset)/height*canvas.height/currentDevicePixelRatio)}}const round1=v=>Math.round(10*v)/10;function getMaximumSize(canvas,bbWidth,bbHeight,aspectRatio){const style=getComputedStyle(canvas),margins=getPositionedStyle(style,"margin"),maxWidth=parseMaxStyle(style.maxWidth,canvas,"clientWidth")||INFINITY,maxHeight=parseMaxStyle(style.maxHeight,canvas,"clientHeight")||INFINITY,containerSize=function(canvas,width,height){let maxWidth,maxHeight;if(void 0===width||void 0===height){const container=_getParentNode(canvas);if(container){const rect=container.getBoundingClientRect(),containerStyle=getComputedStyle(container),containerBorder=getPositionedStyle(containerStyle,"border","width"),containerPadding=getPositionedStyle(containerStyle,"padding");width=rect.width-containerPadding.width-containerBorder.width,height=rect.height-containerPadding.height-containerBorder.height,maxWidth=parseMaxStyle(containerStyle.maxWidth,container,"clientWidth"),maxHeight=parseMaxStyle(containerStyle.maxHeight,container,"clientHeight")}else width=canvas.clientWidth,height=canvas.clientHeight}return{width:width,height:height,maxWidth:maxWidth||INFINITY,maxHeight:maxHeight||INFINITY}}(canvas,bbWidth,bbHeight);let{width:width,height:height}=containerSize;if("content-box"===style.boxSizing){const borders=getPositionedStyle(style,"border","width"),paddings=getPositionedStyle(style,"padding");width-=paddings.width+borders.width,height-=paddings.height+borders.height}return width=Math.max(0,width-margins.width),height=Math.max(0,aspectRatio?Math.floor(width/aspectRatio):height-margins.height),width=round1(Math.min(width,maxWidth,containerSize.maxWidth)),height=round1(Math.min(height,maxHeight,containerSize.maxHeight)),width&&!height&&(height=round1(width/2)),{width:width,height:height}}function retinaScale(chart,forceRatio,forceStyle){const pixelRatio=forceRatio||1,deviceHeight=Math.floor(chart.height*pixelRatio),deviceWidth=Math.floor(chart.width*pixelRatio);chart.height=deviceHeight/pixelRatio,chart.width=deviceWidth/pixelRatio;const canvas=chart.canvas;return canvas.style&&(forceStyle||!canvas.style.height&&!canvas.style.width)&&(canvas.style.height="".concat(chart.height,"px"),canvas.style.width="".concat(chart.width,"px")),(chart.currentDevicePixelRatio!==pixelRatio||canvas.height!==deviceHeight||canvas.width!==deviceWidth)&&(chart.currentDevicePixelRatio=pixelRatio,canvas.height=deviceHeight,canvas.width=deviceWidth,chart.ctx.setTransform(pixelRatio,0,0,pixelRatio,0,0),!0)}const supportsEventListenerOptions=function(){let passiveSupported=!1;try{const options={get passive(){return passiveSupported=!0,!1}};window.addEventListener("test",null,options),window.removeEventListener("test",null,options)}catch(e){}return passiveSupported}();function readUsedSize(element,property){const value=getStyle(element,property),matches=value&&value.match(/^(\d+)(\.\d+)?px$/);return matches?+matches[1]:void 0}function toFontString(font){return!font||isNullOrUndef(font.size)||isNullOrUndef(font.family)?null:(font.style?font.style+" ":"")+(font.weight?font.weight+" ":"")+font.size+"px "+font.family}function _measureText(ctx,data,gc,longest,string){let textWidth=data[string];return textWidth||(textWidth=data[string]=ctx.measureText(string).width,gc.push(string)),textWidth>longest&&(longest=textWidth),longest}function _longestText(ctx,font,arrayOfThings,cache){let data=(cache=cache||{}).data=cache.data||{},gc=cache.garbageCollect=cache.garbageCollect||[];cache.font!==font&&(data=cache.data={},gc=cache.garbageCollect=[],cache.font=font),ctx.save(),ctx.font=font;let longest=0;const ilen=arrayOfThings.length;let i,j,jlen,thing,nestedThing;for(i=0;iarrayOfThings.length){for(i=0;i0&&ctx.stroke()}}function _isPointInArea(point,area,margin){return margin=margin||.5,!area||point&&point.x>area.left-margin&&point.xarea.top-margin&&point.y5&&void 0!==arguments[5]?arguments[5]:{};const lines=isArray(text)?text:[text],stroke=opts.strokeWidth>0&&""!==opts.strokeColor;let i,line;for(ctx.save(),ctx.font=font.string,setRenderOpts(ctx,opts),i=0;i1&&void 0!==arguments[1]?arguments[1]:[""],rootScopes=arguments.length>2&&void 0!==arguments[2]?arguments[2]:scopes,fallback=arguments.length>3?arguments[3]:void 0,getTarget=arguments.length>4&&void 0!==arguments[4]?arguments[4]:()=>scopes[0];defined(fallback)||(fallback=_resolve("_fallback",scopes));const cache={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:scopes,_rootScopes:rootScopes,_fallback:fallback,_getTarget:getTarget,override:scope=>_createResolver([scope,...scopes],prefixes,rootScopes,fallback)};return new Proxy(cache,{deleteProperty:(target,prop)=>(delete target[prop],delete target._keys,delete scopes[0][prop],!0),get:(target,prop)=>_cached(target,prop,(()=>_resolveWithPrefixes(prop,prefixes,scopes,target))),getOwnPropertyDescriptor:(target,prop)=>Reflect.getOwnPropertyDescriptor(target._scopes[0],prop),getPrototypeOf:()=>Reflect.getPrototypeOf(scopes[0]),has:(target,prop)=>getKeysFromAllScopes(target).includes(prop),ownKeys:target=>getKeysFromAllScopes(target),set(target,prop,value){const storage=target._storage||(target._storage=getTarget());return target[prop]=storage[prop]=value,delete target._keys,!0}})}function _attachContext(proxy,context,subProxy,descriptorDefaults){const cache={_cacheable:!1,_proxy:proxy,_context:context,_subProxy:subProxy,_stack:new Set,_descriptors:_descriptors(proxy,descriptorDefaults),setContext:ctx=>_attachContext(proxy,ctx,subProxy,descriptorDefaults),override:scope=>_attachContext(proxy.override(scope),context,subProxy,descriptorDefaults)};return new Proxy(cache,{deleteProperty:(target,prop)=>(delete target[prop],delete proxy[prop],!0),get:(target,prop,receiver)=>_cached(target,prop,(()=>function(target,prop,receiver){const{_proxy:_proxy,_context:_context,_subProxy:_subProxy,_descriptors:descriptors}=target;let value=_proxy[prop];return isFunction(value)&&descriptors.isScriptable(prop)&&(value=function(prop,value,target,receiver){const{_proxy:_proxy,_context:_context,_subProxy:_subProxy,_stack:_stack}=target;if(_stack.has(prop))throw new Error("Recursion detected: "+Array.from(_stack).join("->")+"->"+prop);return _stack.add(prop),value=value(_context,_subProxy||receiver),_stack.delete(prop),needsSubResolver(prop,value)&&(value=createSubResolver(_proxy._scopes,_proxy,prop,value)),value}(prop,value,target,receiver)),isArray(value)&&value.length&&(value=function(prop,value,target,isIndexable){const{_proxy:_proxy,_context:_context,_subProxy:_subProxy,_descriptors:descriptors}=target;if(defined(_context.index)&&isIndexable(prop))value=value[_context.index%value.length];else if(isObject(value[0])){const arr=value,scopes=_proxy._scopes.filter((s=>s!==arr));value=[];for(const item of arr){const resolver=createSubResolver(scopes,_proxy,prop,item);value.push(_attachContext(resolver,_context,_subProxy&&_subProxy[prop],descriptors))}}return value}(prop,value,target,descriptors.isIndexable)),needsSubResolver(prop,value)&&(value=_attachContext(value,_context,_subProxy&&_subProxy[prop],descriptors)),value}(target,prop,receiver))),getOwnPropertyDescriptor:(target,prop)=>target._descriptors.allKeys?Reflect.has(proxy,prop)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(proxy,prop),getPrototypeOf:()=>Reflect.getPrototypeOf(proxy),has:(target,prop)=>Reflect.has(proxy,prop),ownKeys:()=>Reflect.ownKeys(proxy),set:(target,prop,value)=>(proxy[prop]=value,delete target[prop],!0)})}function _descriptors(proxy){let defaults=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{scriptable:!0,indexable:!0};const{_scriptable:_scriptable=defaults.scriptable,_indexable:_indexable=defaults.indexable,_allKeys:_allKeys=defaults.allKeys}=proxy;return{allKeys:_allKeys,scriptable:_scriptable,indexable:_indexable,isScriptable:isFunction(_scriptable)?_scriptable:()=>_scriptable,isIndexable:isFunction(_indexable)?_indexable:()=>_indexable}}const readKey=(prefix,name)=>prefix?prefix+_capitalize(name):name,needsSubResolver=(prop,value)=>isObject(value)&&"adapters"!==prop&&(null===Object.getPrototypeOf(value)||value.constructor===Object);function _cached(target,prop,resolve){if(Object.prototype.hasOwnProperty.call(target,prop))return target[prop];const value=resolve();return target[prop]=value,value}function resolveFallback(fallback,prop,value){return isFunction(fallback)?fallback(prop,value):fallback}const getScope=(key,parent)=>!0===key?parent:"string"==typeof key?resolveObjectKey(parent,key):void 0;function addScopes(set,parentScopes,key,parentFallback,value){for(const parent of parentScopes){const scope=getScope(key,parent);if(scope){set.add(scope);const fallback=resolveFallback(scope._fallback,key,value);if(defined(fallback)&&fallback!==key&&fallback!==parentFallback)return fallback}else if(!1===scope&&defined(parentFallback)&&key!==parentFallback)return null}return!1}function createSubResolver(parentScopes,resolver,prop,value){const rootScopes=resolver._rootScopes,fallback=resolveFallback(resolver._fallback,prop,value),allScopes=[...parentScopes,...rootScopes],set=new Set;set.add(value);let key=addScopesFromKey(set,allScopes,prop,fallback||prop,value);return null!==key&&(!defined(fallback)||fallback===prop||(key=addScopesFromKey(set,allScopes,fallback,key,value),null!==key))&&_createResolver(Array.from(set),[""],rootScopes,fallback,(()=>function(resolver,prop,value){const parent=resolver._getTarget();prop in parent||(parent[prop]={});const target=parent[prop];return isArray(target)&&isObject(value)?value:target}(resolver,prop,value)))}function addScopesFromKey(set,allScopes,key,fallback,item){for(;key;)key=addScopes(set,allScopes,key,fallback,item);return key}function _resolveWithPrefixes(prop,prefixes,scopes,proxy){let value;for(const prefix of prefixes)if(value=_resolve(readKey(prefix,prop),scopes),defined(value))return needsSubResolver(prop,value)?createSubResolver(scopes,proxy,prop,value):value}function _resolve(key,scopes){for(const scope of scopes){if(!scope)continue;const value=scope[key];if(defined(value))return value}}function getKeysFromAllScopes(target){let keys=target._keys;return keys||(keys=target._keys=function(scopes){const set=new Set;for(const scope of scopes)for(const key of Object.keys(scope).filter((k=>!k.startsWith("_"))))set.add(key);return Array.from(set)}(target._scopes)),keys}function _parseObjectDataRadialScale(meta,data,start,count){const{iScale:iScale}=meta,{key:key="r"}=this._parsing,parsed=new Array(count);let i,ilen,index,item;for(i=0,ilen=count;ii"x"===indexAxis?"y":"x";function splineCurve(firstPoint,middlePoint,afterPoint,t){const previous=firstPoint.skip?middlePoint:firstPoint,current=middlePoint,next=afterPoint.skip?middlePoint:afterPoint,d01=distanceBetweenPoints(current,previous),d12=distanceBetweenPoints(next,current);let s01=d01/(d01+d12),s12=d12/(d01+d12);s01=isNaN(s01)?0:s01,s12=isNaN(s12)?0:s12;const fa=t*s01,fb=t*s12;return{previous:{x:current.x-fa*(next.x-previous.x),y:current.y-fa*(next.y-previous.y)},next:{x:current.x+fb*(next.x-previous.x),y:current.y+fb*(next.y-previous.y)}}}function monotoneAdjust(points,deltaK,mK){const pointsLen=points.length;let alphaK,betaK,tauK,squaredMagnitude,pointCurrent,pointAfter=getPoint(points,0);for(let i=0;i2&&void 0!==arguments[2]?arguments[2]:"x";const valueAxis=getValueAxis(indexAxis),pointsLen=points.length;let delta,pointBefore,pointCurrent,pointAfter=getPoint(points,0);for(let i=0;i1&&void 0!==arguments[1]?arguments[1]:"x";const valueAxis=getValueAxis(indexAxis),pointsLen=points.length,deltaK=Array(pointsLen).fill(0),mK=Array(pointsLen);let i,pointBefore,pointCurrent,pointAfter=getPoint(points,0);for(i=0;i!pt.skip))),"monotone"===options.cubicInterpolationMode)splineCurveMonotone(points,indexAxis);else{let prev=loop?points[points.length-1]:points[0];for(i=0,ilen=points.length;i0===t||1===t,elasticIn=(t,s,p)=>-Math.pow(2,10*(t-=1))*Math.sin((t-s)*TAU/p),elasticOut=(t,s,p)=>Math.pow(2,-10*t)*Math.sin((t-s)*TAU/p)+1,effects={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*HALF_PI),easeOutSine:t=>Math.sin(t*HALF_PI),easeInOutSine:t=>-.5*(Math.cos(PI*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>atEdge(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>atEdge(t)?t:elasticIn(t,.075,.3),easeOutElastic:t=>atEdge(t)?t:elasticOut(t,.075,.3),easeInOutElastic(t){const s=.1125;return atEdge(t)?t:t<.5?.5*elasticIn(2*t,s,.45):.5+.5*elasticOut(2*t-1,s,.45)},easeInBack(t){const s=1.70158;return t*t*((s+1)*t-s)},easeOutBack(t){const s=1.70158;return(t-=1)*t*((s+1)*t+s)+1},easeInOutBack(t){let s=1.70158;return(t/=.5)<1?t*t*((1+(s*=1.525))*t-s)*.5:.5*((t-=2)*t*((1+(s*=1.525))*t+s)+2)},easeInBounce:t=>1-effects.easeOutBounce(1-t),easeOutBounce(t){const m=7.5625,d=2.75;return t<1/d?m*t*t:t<2/d?m*(t-=1.5/d)*t+.75:t<2.5/d?m*(t-=2.25/d)*t+.9375:m*(t-=2.625/d)*t+.984375},easeInOutBounce:t=>t<.5?.5*effects.easeInBounce(2*t):.5*effects.easeOutBounce(2*t-1)+.5};function _pointInLine(p1,p2,t,mode){return{x:p1.x+t*(p2.x-p1.x),y:p1.y+t*(p2.y-p1.y)}}function _steppedInterpolation(p1,p2,t,mode){return{x:p1.x+t*(p2.x-p1.x),y:"middle"===mode?t<.5?p1.y:p2.y:"after"===mode?t<1?p1.y:p2.y:t>0?p2.y:p1.y}}function _bezierInterpolation(p1,p2,t,mode){const cp1={x:p1.cp2x,y:p1.cp2y},cp2={x:p2.cp1x,y:p2.cp1y},a=_pointInLine(p1,cp1,t),b=_pointInLine(cp1,cp2,t),c=_pointInLine(cp2,p2,t),d=_pointInLine(a,b,t),e=_pointInLine(b,c,t);return _pointInLine(d,e,t)}const intlCache=new Map;function formatNumber(num,locale,options){return function(locale,options){options=options||{};const cacheKey=locale+JSON.stringify(options);let formatter=intlCache.get(cacheKey);return formatter||(formatter=new Intl.NumberFormat(locale,options),intlCache.set(cacheKey,formatter)),formatter}(locale,options).format(num)}const LINE_HEIGHT=new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/),FONT_STYLE=new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/);function toLineHeight(value,size){const matches=(""+value).match(LINE_HEIGHT);if(!matches||"normal"===matches[1])return 1.2*size;switch(value=+matches[2],matches[3]){case"px":return value;case"%":value/=100}return size*value}function _readValueToProps(value,props){const ret={},objProps=isObject(props),keys=objProps?Object.keys(props):props,read=isObject(value)?objProps?prop=>valueOrDefault(value[prop],value[props[prop]]):prop=>value[prop]:()=>value;for(const prop of keys)ret[prop]=+read(prop)||0;return ret}function toTRBL(value){return _readValueToProps(value,{top:"y",right:"x",bottom:"y",left:"x"})}function toTRBLCorners(value){return _readValueToProps(value,["topLeft","topRight","bottomLeft","bottomRight"])}function toPadding(value){const obj=toTRBL(value);return obj.width=obj.left+obj.right,obj.height=obj.top+obj.bottom,obj}function toFont(options,fallback){options=options||{},fallback=fallback||defaults.font;let size=valueOrDefault(options.size,fallback.size);"string"==typeof size&&(size=parseInt(size,10));let style=valueOrDefault(options.style,fallback.style);style&&!(""+style).match(FONT_STYLE)&&(console.warn('Invalid font style specified: "'+style+'"'),style="");const font={family:valueOrDefault(options.family,fallback.family),lineHeight:toLineHeight(valueOrDefault(options.lineHeight,fallback.lineHeight),size),size:size,style:style,weight:valueOrDefault(options.weight,fallback.weight),string:""};return font.string=toFontString(font),font}function resolve(inputs,context,index,info){let i,ilen,value,cacheable=!0;for(i=0,ilen=inputs.length;ibeginAtZero&&0===value?0:value+add;return{min:keepZero(min,-Math.abs(change)),max:keepZero(max,change)}}function createContext(parentContext,context){return Object.assign(Object.create(parentContext),context)}function getRtlAdapter(rtl,rectX,width){return rtl?function(rectX,width){return{x:x=>rectX+rectX+width-x,setWidth(w){width=w},textAlign:align=>"center"===align?align:"right"===align?"left":"right",xPlus:(x,value)=>x-value,leftForLtr:(x,itemWidth)=>x-itemWidth}}(rectX,width):{x:x=>x,setWidth(w){},textAlign:align=>align,xPlus:(x,value)=>x+value,leftForLtr:(x,_itemWidth)=>x}}function overrideTextDirection(ctx,direction){let style,original;"ltr"!==direction&&"rtl"!==direction||(style=ctx.canvas.style,original=[style.getPropertyValue("direction"),style.getPropertyPriority("direction")],style.setProperty("direction",direction,"important"),ctx.prevTextDirection=original)}function restoreTextDirection(ctx,original){void 0!==original&&(delete ctx.prevTextDirection,ctx.canvas.style.setProperty("direction",original[0],original[1]))}function propertyFn(property){return"angle"===property?{between:_angleBetween,compare:_angleDiff,normalize:_normalizeAngle}:{between:_isBetween,compare:(a,b)=>a-b,normalize:x=>x}}function normalizeSegment(_ref){let{start:start,end:end,count:count,loop:loop,style:style}=_ref;return{start:start%count,end:end%count,loop:loop&&(end-start+1)%count==0,style:style}}function _boundSegment(segment,points,bounds){if(!bounds)return[segment];const{property:property,start:startBound,end:endBound}=bounds,count=points.length,{compare:compare,between:between,normalize:normalize}=propertyFn(property),{start:start,end:end,loop:loop,style:style}=function(segment,points,bounds){const{property:property,start:startBound,end:endBound}=bounds,{between:between,normalize:normalize}=propertyFn(property),count=points.length;let i,ilen,{start:start,end:end,loop:loop}=segment;if(loop){for(start+=count,end+=count,i=0,ilen=count;iinside||between(startBound,prevValue,value)&&0!==compare(startBound,prevValue),shouldStop=()=>!inside||0===compare(endBound,value)||between(endBound,prevValue,value);for(let i=start,prev=start;i<=end;++i)point=points[i%count],point.skip||(value=normalize(point[property]),value!==prevValue&&(inside=between(value,startBound,endBound),null===subStart&&shouldStart()&&(subStart=0===compare(value,startBound)?i:prev),null!==subStart&&shouldStop()&&(result.push(normalizeSegment({start:subStart,end:i,loop:loop,count:count,style:style})),subStart=null),prev=i,prevValue=value));return null!==subStart&&result.push(normalizeSegment({start:subStart,end:end,loop:loop,count:count,style:style})),result}function _boundSegments(line,bounds){const result=[],segments=line.segments;for(let i=0;istart&&points[end%count].skip;)end--;return end%=count,{start:start,end:end}}(points,count,loop,spanGaps);return splitByStyles(line,!0===spanGaps?[{start:start,end:end,loop:loop}]:function(points,start,max,loop){const count=points.length,result=[];let end,last=start,prev=points[start];for(end=start+1;end<=max;++end){const cur=points[end%count];cur.skip||cur.stop?prev.skip||(loop=!1,result.push({start:start%count,end:(end-1)%count,loop:loop}),start=last=cur.stop?end:null):(last=end,prev.skip&&(start=end)),prev=cur}return null!==last&&result.push({start:start%count,end:last%count,loop:loop}),result}(points,start,end{element[rangeMethod](position[axis],useFinalPosition)&&(items.push({element:element,datasetIndex:datasetIndex,index:index}),intersectsItem=intersectsItem||element.inRange(position.x,position.y,useFinalPosition))})),intersect&&!intersectsItem?[]:items}var Interaction={evaluateInteractionItems:evaluateInteractionItems,modes:{index(chart,e,options,useFinalPosition){const position=getRelativePosition(e,chart),axis=options.axis||"x",includeInvisible=options.includeInvisible||!1,items=options.intersect?getIntersectItems(chart,position,axis,useFinalPosition,includeInvisible):getNearestItems(chart,position,axis,!1,useFinalPosition,includeInvisible),elements=[];return items.length?(chart.getSortedVisibleDatasetMetas().forEach((meta=>{const index=items[0].index,element=meta.data[index];element&&!element.skip&&elements.push({element:element,datasetIndex:meta.index,index:index})})),elements):[]},dataset(chart,e,options,useFinalPosition){const position=getRelativePosition(e,chart),axis=options.axis||"xy",includeInvisible=options.includeInvisible||!1;let items=options.intersect?getIntersectItems(chart,position,axis,useFinalPosition,includeInvisible):getNearestItems(chart,position,axis,!1,useFinalPosition,includeInvisible);if(items.length>0){const datasetIndex=items[0].datasetIndex,data=chart.getDatasetMeta(datasetIndex).data;items=[];for(let i=0;igetIntersectItems(chart,getRelativePosition(e,chart),options.axis||"xy",useFinalPosition,options.includeInvisible||!1),nearest(chart,e,options,useFinalPosition){const position=getRelativePosition(e,chart),axis=options.axis||"xy",includeInvisible=options.includeInvisible||!1;return getNearestItems(chart,position,axis,options.intersect,useFinalPosition,includeInvisible)},x:(chart,e,options,useFinalPosition)=>getAxisItems(chart,getRelativePosition(e,chart),"x",options.intersect,useFinalPosition),y:(chart,e,options,useFinalPosition)=>getAxisItems(chart,getRelativePosition(e,chart),"y",options.intersect,useFinalPosition)}};const STATIC_POSITIONS=["left","top","right","bottom"];function filterByPosition(array,position){return array.filter((v=>v.pos===position))}function filterDynamicPositionByAxis(array,axis){return array.filter((v=>-1===STATIC_POSITIONS.indexOf(v.pos)&&v.box.axis===axis))}function sortByWeight(array,reverse){return array.sort(((a,b)=>{const v0=reverse?b:a,v1=reverse?a:b;return v0.weight===v1.weight?v0.index-v1.index:v0.weight-v1.weight}))}function setLayoutDims(layouts,params){const stacks=function(layouts){const stacks={};for(const wrap of layouts){const{stack:stack,pos:pos,stackWeight:stackWeight}=wrap;if(!stack||!STATIC_POSITIONS.includes(pos))continue;const _stack=stacks[stack]||(stacks[stack]={count:0,placed:0,weight:0,size:0});_stack.count++,_stack.weight+=stackWeight}return stacks}(layouts),{vBoxMaxWidth:vBoxMaxWidth,hBoxMaxHeight:hBoxMaxHeight}=params;let i,ilen,layout;for(i=0,ilen=layouts.length;i{margin[pos]=Math.max(chartArea[pos],maxPadding[pos])})),margin}return marginForPositions(horizontal?["left","right"]:["top","bottom"])}function fitBoxes(boxes,chartArea,params,stacks){const refitBoxes=[];let i,ilen,layout,box,refit,changed;for(i=0,ilen=boxes.length,refit=0;iwrap.box.fullSize)),!0),left=sortByWeight(filterByPosition(layoutBoxes,"left"),!0),right=sortByWeight(filterByPosition(layoutBoxes,"right")),top=sortByWeight(filterByPosition(layoutBoxes,"top"),!0),bottom=sortByWeight(filterByPosition(layoutBoxes,"bottom")),centerHorizontal=filterDynamicPositionByAxis(layoutBoxes,"x"),centerVertical=filterDynamicPositionByAxis(layoutBoxes,"y");return{fullSize:fullSize,leftAndTop:left.concat(top),rightAndBottom:right.concat(centerVertical).concat(bottom).concat(centerHorizontal),chartArea:filterByPosition(layoutBoxes,"chartArea"),vertical:left.concat(right).concat(centerVertical),horizontal:top.concat(bottom).concat(centerHorizontal)}}(chart.boxes),verticalBoxes=boxes.vertical,horizontalBoxes=boxes.horizontal;each(chart.boxes,(box=>{"function"==typeof box.beforeLayout&&box.beforeLayout()}));const visibleVerticalBoxCount=verticalBoxes.reduce(((total,wrap)=>wrap.box.options&&!1===wrap.box.options.display?total:total+1),0)||1,params=Object.freeze({outerWidth:width,outerHeight:height,padding:padding,availableWidth:availableWidth,availableHeight:availableHeight,vBoxMaxWidth:availableWidth/2/visibleVerticalBoxCount,hBoxMaxHeight:availableHeight/2}),maxPadding=Object.assign({},padding);updateMaxPadding(maxPadding,toPadding(minPadding));const chartArea=Object.assign({maxPadding:maxPadding,w:availableWidth,h:availableHeight,x:padding.left,y:padding.top},padding),stacks=setLayoutDims(verticalBoxes.concat(horizontalBoxes),params);fitBoxes(boxes.fullSize,chartArea,params,stacks),fitBoxes(verticalBoxes,chartArea,params,stacks),fitBoxes(horizontalBoxes,chartArea,params,stacks)&&fitBoxes(verticalBoxes,chartArea,params,stacks),function(chartArea){const maxPadding=chartArea.maxPadding;function updatePos(pos){const change=Math.max(maxPadding[pos]-chartArea[pos],0);return chartArea[pos]+=change,change}chartArea.y+=updatePos("top"),chartArea.x+=updatePos("left"),updatePos("right"),updatePos("bottom")}(chartArea),placeBoxes(boxes.leftAndTop,chartArea,params,stacks),chartArea.x+=chartArea.w,chartArea.y+=chartArea.h,placeBoxes(boxes.rightAndBottom,chartArea,params,stacks),chart.chartArea={left:chartArea.left,top:chartArea.top,right:chartArea.left+chartArea.w,bottom:chartArea.top+chartArea.h,height:chartArea.h,width:chartArea.w},each(boxes.chartArea,(layout=>{const box=layout.box;Object.assign(box,chart.chartArea),box.update(chartArea.w,chartArea.h,{left:0,top:0,right:0,bottom:0})}))}};class BasePlatform{acquireContext(canvas,aspectRatio){}releaseContext(context){return!1}addEventListener(chart,type,listener){}removeEventListener(chart,type,listener){}getDevicePixelRatio(){return 1}getMaximumSize(element,width,height,aspectRatio){return width=Math.max(0,width||element.width),height=height||element.height,{width:width,height:Math.max(0,aspectRatio?Math.floor(width/aspectRatio):height)}}isAttached(canvas){return!0}updateConfig(config){}}class BasicPlatform extends BasePlatform{acquireContext(item){return item&&item.getContext&&item.getContext("2d")||null}updateConfig(config){config.options.animation=!1}}const EVENT_TYPES={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},isNullOrEmpty=value=>null===value||""===value,eventListenerOptions=!!supportsEventListenerOptions&&{passive:!0};function removeListener(chart,type,listener){chart.canvas.removeEventListener(type,listener,eventListenerOptions)}function nodeListContains(nodeList,canvas){for(const node of nodeList)if(node===canvas||node.contains(canvas))return!0}function createAttachObserver(chart,type,listener){const canvas=chart.canvas,observer=new MutationObserver((entries=>{let trigger=!1;for(const entry of entries)trigger=trigger||nodeListContains(entry.addedNodes,canvas),trigger=trigger&&!nodeListContains(entry.removedNodes,canvas);trigger&&listener()}));return observer.observe(document,{childList:!0,subtree:!0}),observer}function createDetachObserver(chart,type,listener){const canvas=chart.canvas,observer=new MutationObserver((entries=>{let trigger=!1;for(const entry of entries)trigger=trigger||nodeListContains(entry.removedNodes,canvas),trigger=trigger&&!nodeListContains(entry.addedNodes,canvas);trigger&&listener()}));return observer.observe(document,{childList:!0,subtree:!0}),observer}const drpListeningCharts=new Map;let oldDevicePixelRatio=0;function onWindowResize(){const dpr=window.devicePixelRatio;dpr!==oldDevicePixelRatio&&(oldDevicePixelRatio=dpr,drpListeningCharts.forEach(((resize,chart)=>{chart.currentDevicePixelRatio!==dpr&&resize()})))}function createResizeObserver(chart,type,listener){const canvas=chart.canvas,container=canvas&&_getParentNode(canvas);if(!container)return;const resize=throttled(((width,height)=>{const w=container.clientWidth;listener(width,height),w{const entry=entries[0],width=entry.contentRect.width,height=entry.contentRect.height;0===width&&0===height||resize(width,height)}));return observer.observe(container),function(chart,resize){drpListeningCharts.size||window.addEventListener("resize",onWindowResize),drpListeningCharts.set(chart,resize)}(chart,resize),observer}function releaseObserver(chart,type,observer){observer&&observer.disconnect(),"resize"===type&&function(chart){drpListeningCharts.delete(chart),drpListeningCharts.size||window.removeEventListener("resize",onWindowResize)}(chart)}function createProxyAndListen(chart,type,listener){const canvas=chart.canvas,proxy=throttled((event=>{null!==chart.ctx&&listener(function(event,chart){const type=EVENT_TYPES[event.type]||event.type,{x:x,y:y}=getRelativePosition(event,chart);return{type:type,chart:chart,native:event,x:void 0!==x?x:null,y:void 0!==y?y:null}}(event,chart))}),chart,(args=>{const event=args[0];return[event,event.offsetX,event.offsetY]}));return function(node,type,listener){node.addEventListener(type,listener,eventListenerOptions)}(canvas,type,proxy),proxy}class DomPlatform extends BasePlatform{acquireContext(canvas,aspectRatio){const context=canvas&&canvas.getContext&&canvas.getContext("2d");return context&&context.canvas===canvas?(function(canvas,aspectRatio){const style=canvas.style,renderHeight=canvas.getAttribute("height"),renderWidth=canvas.getAttribute("width");if(canvas.$chartjs={initial:{height:renderHeight,width:renderWidth,style:{display:style.display,height:style.height,width:style.width}}},style.display=style.display||"block",style.boxSizing=style.boxSizing||"border-box",isNullOrEmpty(renderWidth)){const displayWidth=readUsedSize(canvas,"width");void 0!==displayWidth&&(canvas.width=displayWidth)}if(isNullOrEmpty(renderHeight))if(""===canvas.style.height)canvas.height=canvas.width/(aspectRatio||2);else{const displayHeight=readUsedSize(canvas,"height");void 0!==displayHeight&&(canvas.height=displayHeight)}}(canvas,aspectRatio),context):null}releaseContext(context){const canvas=context.canvas;if(!canvas.$chartjs)return!1;const initial=canvas.$chartjs.initial;["height","width"].forEach((prop=>{const value=initial[prop];isNullOrUndef(value)?canvas.removeAttribute(prop):canvas.setAttribute(prop,value)}));const style=initial.style||{};return Object.keys(style).forEach((key=>{canvas.style[key]=style[key]})),canvas.width=canvas.width,delete canvas.$chartjs,!0}addEventListener(chart,type,listener){this.removeEventListener(chart,type);const proxies=chart.$proxies||(chart.$proxies={}),handler={attach:createAttachObserver,detach:createDetachObserver,resize:createResizeObserver}[type]||createProxyAndListen;proxies[type]=handler(chart,type,listener)}removeEventListener(chart,type){const proxies=chart.$proxies||(chart.$proxies={}),proxy=proxies[type];proxy&&(({attach:releaseObserver,detach:releaseObserver,resize:releaseObserver}[type]||removeListener)(chart,type,proxy),proxies[type]=void 0)}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(canvas,width,height,aspectRatio){return getMaximumSize(canvas,width,height,aspectRatio)}isAttached(canvas){const container=_getParentNode(canvas);return!(!container||!container.isConnected)}}function _detectPlatform(canvas){return!_isDomSupported()||"undefined"!=typeof OffscreenCanvas&&canvas instanceof OffscreenCanvas?BasicPlatform:DomPlatform}var platforms=Object.freeze({__proto__:null,_detectPlatform:_detectPlatform,BasePlatform:BasePlatform,BasicPlatform:BasicPlatform,DomPlatform:DomPlatform});const interpolators={boolean:(from,to,factor)=>factor>.5?to:from,color(from,to,factor){const c0=color(from||"transparent"),c1=c0.valid&&color(to||"transparent");return c1&&c1.valid?c1.mix(c0,factor).hexString():to},number:(from,to,factor)=>from+(to-from)*factor};class Animation{constructor(cfg,target,prop,to){const currentValue=target[prop];to=resolve([cfg.to,to,currentValue,cfg.from]);const from=resolve([cfg.from,currentValue,to]);this._active=!0,this._fn=cfg.fn||interpolators[cfg.type||typeof from],this._easing=effects[cfg.easing]||effects.linear,this._start=Math.floor(Date.now()+(cfg.delay||0)),this._duration=this._total=Math.floor(cfg.duration),this._loop=!!cfg.loop,this._target=target,this._prop=prop,this._from=from,this._to=to,this._promises=void 0}active(){return this._active}update(cfg,to,date){if(this._active){this._notify(!1);const currentValue=this._target[this._prop],elapsed=date-this._start,remain=this._duration-elapsed;this._start=date,this._duration=Math.floor(Math.max(remain,cfg.duration)),this._total+=elapsed,this._loop=!!cfg.loop,this._to=resolve([cfg.to,to,currentValue,cfg.from]),this._from=resolve([cfg.from,currentValue,to])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(date){const elapsed=date-this._start,duration=this._duration,prop=this._prop,from=this._from,loop=this._loop,to=this._to;let factor;if(this._active=from!==to&&(loop||elapsed1?2-factor:factor,factor=this._easing(Math.min(1,Math.max(0,factor))),this._target[prop]=this._fn(from,to,factor))}wait(){const promises=this._promises||(this._promises=[]);return new Promise(((res,rej)=>{promises.push({res:res,rej:rej})}))}_notify(resolved){const method=resolved?"res":"rej",promises=this._promises||[];for(let i=0;i"onProgress"!==name&&"onComplete"!==name&&"fn"!==name}),defaults.set("animations",{colors:{type:"color",properties:["color","borderColor","backgroundColor"]},numbers:{type:"number",properties:["x","y","borderWidth","radius","tension"]}}),defaults.describe("animations",{_fallback:"animation"}),defaults.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:v=>0|v}}}});class Animations{constructor(chart,config){this._chart=chart,this._properties=new Map,this.configure(config)}configure(config){if(!isObject(config))return;const animatedProps=this._properties;Object.getOwnPropertyNames(config).forEach((key=>{const cfg=config[key];if(!isObject(cfg))return;const resolved={};for(const option of animationOptions)resolved[option]=cfg[option];(isArray(cfg.properties)&&cfg.properties||[key]).forEach((prop=>{prop!==key&&animatedProps.has(prop)||animatedProps.set(prop,resolved)}))}))}_animateOptions(target,values){const newOptions=values.options,options=function(target,newOptions){if(!newOptions)return;let options=target.options;if(options)return options.$shared&&(target.options=options=Object.assign({},options,{$shared:!1,$animations:{}})),options;target.options=newOptions}(target,newOptions);if(!options)return[];const animations=this._createAnimations(options,newOptions);return newOptions.$shared&&function(animations,properties){const running=[],keys=Object.keys(properties);for(let i=0;i{target.options=newOptions}),(()=>{})),animations}_createAnimations(target,values){const animatedProps=this._properties,animations=[],running=target.$animations||(target.$animations={}),props=Object.keys(values),date=Date.now();let i;for(i=props.length-1;i>=0;--i){const prop=props[i];if("$"===prop.charAt(0))continue;if("options"===prop){animations.push(...this._animateOptions(target,values));continue}const value=values[prop];let animation=running[prop];const cfg=animatedProps.get(prop);if(animation){if(cfg&&animation.active()){animation.update(cfg,value,date);continue}animation.cancel()}cfg&&cfg.duration?(running[prop]=animation=new Animation(cfg,target,prop,value),animations.push(animation)):target[prop]=value}return animations}update(target,values){if(0===this._properties.size)return void Object.assign(target,values);const animations=this._createAnimations(target,values);return animations.length?(animator.add(this._chart,animations),!0):void 0}}function scaleClip(scale,allowedOverflow){const opts=scale&&scale.options||{},reverse=opts.reverse,min=void 0===opts.min?allowedOverflow:0,max=void 0===opts.max?allowedOverflow:0;return{start:reverse?max:min,end:reverse?min:max}}function getSortedDatasetIndices(chart,filterVisible){const keys=[],metasets=chart._getSortedDatasetMetas(filterVisible);let i,ilen;for(i=0,ilen=metasets.length;i3&&void 0!==arguments[3]?arguments[3]:{};const keys=stack.keys,singleMode="single"===options.mode;let i,ilen,datasetIndex,otherValue;if(null!==value){for(i=0,ilen=keys.length;i0||!positive&&value<0)return meta.index}return null}function updateStacks(controller,parsed){const{chart:chart,_cachedMeta:meta}=controller,stacks=chart._stacks||(chart._stacks={}),{iScale:iScale,vScale:vScale,index:datasetIndex}=meta,iAxis=iScale.axis,vAxis=vScale.axis,key=function(indexScale,valueScale,meta){return"".concat(indexScale.id,".").concat(valueScale.id,".").concat(meta.stack||meta.type)}(iScale,vScale,meta),ilen=parsed.length;let stack;for(let i=0;iscales[key].axis===axis)).shift()}function clearStacks(meta,items){const datasetIndex=meta.controller.index,axis=meta.vScale&&meta.vScale.axis;if(axis){items=items||meta._parsed;for(const parsed of items){const stacks=parsed._stacks;if(!stacks||void 0===stacks[axis]||void 0===stacks[axis][datasetIndex])return;delete stacks[axis][datasetIndex]}}}const isDirectUpdateMode=mode=>"reset"===mode||"none"===mode,cloneIfNotShared=(cached,shared)=>shared?cached:Object.assign({},cached);class DatasetController{constructor(chart,datasetIndex){this.chart=chart,this._ctx=chart.ctx,this.index=datasetIndex,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.initialize()}initialize(){const meta=this._cachedMeta;this.configure(),this.linkScales(),meta._stacked=isStacked(meta.vScale,meta),this.addElements()}updateIndex(datasetIndex){this.index!==datasetIndex&&clearStacks(this._cachedMeta),this.index=datasetIndex}linkScales(){const chart=this.chart,meta=this._cachedMeta,dataset=this.getDataset(),chooseId=(axis,x,y,r)=>"x"===axis?x:"r"===axis?r:y,xid=meta.xAxisID=valueOrDefault(dataset.xAxisID,getFirstScaleId(chart,"x")),yid=meta.yAxisID=valueOrDefault(dataset.yAxisID,getFirstScaleId(chart,"y")),rid=meta.rAxisID=valueOrDefault(dataset.rAxisID,getFirstScaleId(chart,"r")),indexAxis=meta.indexAxis,iid=meta.iAxisID=chooseId(indexAxis,xid,yid,rid),vid=meta.vAxisID=chooseId(indexAxis,yid,xid,rid);meta.xScale=this.getScaleForId(xid),meta.yScale=this.getScaleForId(yid),meta.rScale=this.getScaleForId(rid),meta.iScale=this.getScaleForId(iid),meta.vScale=this.getScaleForId(vid)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(scaleID){return this.chart.scales[scaleID]}_getOtherScale(scale){const meta=this._cachedMeta;return scale===meta.iScale?meta.vScale:meta.iScale}reset(){this._update("reset")}_destroy(){const meta=this._cachedMeta;this._data&&unlistenArrayEvents(this._data,this),meta._stacked&&clearStacks(meta)}_dataCheck(){const dataset=this.getDataset(),data=dataset.data||(dataset.data=[]),_data=this._data;if(isObject(data))this._data=function(data){const keys=Object.keys(data),adata=new Array(keys.length);let i,ilen,key;for(i=0,ilen=keys.length;i0&&meta._parsed[start-1];if(!1===this._parsing)meta._parsed=data,meta._sorted=!0,parsed=data;else{parsed=isArray(data[start])?this.parseArrayData(meta,data,start,count):isObject(data[start])?this.parseObjectData(meta,data,start,count):this.parsePrimitiveData(meta,data,start,count);const isNotInOrderComparedToPrev=()=>null===cur[iAxis]||prev&&cur[iAxis]canStack&&!meta.hidden&&meta._stacked&&{keys:getSortedDatasetIndices(chart,!0),values:null})(canStack,meta,this.chart),range={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:otherMin,max:otherMax}=function(scale){const{min:min,max:max,minDefined:minDefined,maxDefined:maxDefined}=scale.getUserBounds();return{min:minDefined?min:Number.NEGATIVE_INFINITY,max:maxDefined?max:Number.POSITIVE_INFINITY}}(otherScale);let i,parsed;function _skip(){parsed=_parsed[i];const otherValue=parsed[otherScale.axis];return!isNumberFinite(parsed[scale.axis])||otherMin>otherValue||otherMax=0;--i)if(!_skip()){this.updateRangeFromParsed(range,scale,parsed,stack);break}return range}getAllParsedValues(scale){const parsed=this._cachedMeta._parsed,values=[];let i,ilen,value;for(i=0,ilen=parsed.length;i=0&&index1&&void 0!==arguments[1]?arguments[1]:"default",index=arguments.length>2?arguments[2]:void 0;const active="active"===mode,cache=this._cachedDataOpts,cacheKey=elementType+"-"+mode,cached=cache[cacheKey],sharing=this.enableOptionSharing&&defined(index);if(cached)return cloneIfNotShared(cached,sharing);const config=this.chart.config,scopeKeys=config.datasetElementScopeKeys(this._type,elementType),prefixes=active?["".concat(elementType,"Hover"),"hover",elementType,""]:[elementType,""],scopes=config.getOptionScopes(this.getDataset(),scopeKeys),names=Object.keys(defaults.elements[elementType]),values=config.resolveNamedOptions(scopes,names,(()=>this.getContext(index,active)),prefixes);return values.$shared&&(values.$shared=sharing,cache[cacheKey]=Object.freeze(cloneIfNotShared(values,sharing))),values}_resolveAnimations(index,transition,active){const chart=this.chart,cache=this._cachedDataOpts,cacheKey="animation-".concat(transition),cached=cache[cacheKey];if(cached)return cached;let options;if(!1!==chart.options.animation){const config=this.chart.config,scopeKeys=config.datasetAnimationScopeKeys(this._type,transition),scopes=config.getOptionScopes(this.getDataset(),scopeKeys);options=config.createResolver(scopes,this.getContext(index,active,transition))}const animations=new Animations(chart,options&&options.animations);return options&&options._cacheable&&(cache[cacheKey]=Object.freeze(animations)),animations}getSharedOptions(options){if(options.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},options))}includeOptions(mode,sharedOptions){return!sharedOptions||isDirectUpdateMode(mode)||this.chart._animationsDisabled}updateElement(element,index,properties,mode){isDirectUpdateMode(mode)?Object.assign(element,properties):this._resolveAnimations(index,mode).update(element,properties)}updateSharedOptions(sharedOptions,mode,newOptions){sharedOptions&&!isDirectUpdateMode(mode)&&this._resolveAnimations(void 0,mode).update(sharedOptions,newOptions)}_setStyle(element,index,mode,active){element.active=active;const options=this.getStyle(index,active);this._resolveAnimations(index,mode,active).update(element,{options:!active&&this.getSharedOptions(options)||options})}removeHoverStyle(element,datasetIndex,index){this._setStyle(element,index,"active",!1)}setHoverStyle(element,datasetIndex,index){this._setStyle(element,index,"active",!0)}_removeDatasetHoverStyle(){const element=this._cachedMeta.dataset;element&&this._setStyle(element,void 0,"active",!1)}_setDatasetHoverStyle(){const element=this._cachedMeta.dataset;element&&this._setStyle(element,void 0,"active",!0)}_resyncElements(resetNewElements){const data=this._data,elements=this._cachedMeta.data;for(const[method,arg1,arg2]of this._syncList)this[method](arg1,arg2);this._syncList=[];const numMeta=elements.length,numData=data.length,count=Math.min(numData,numMeta);count&&this.parse(0,count),numData>numMeta?this._insertElements(numMeta,numData-numMeta,resetNewElements):numData2&&void 0!==arguments[2])||arguments[2];const meta=this._cachedMeta,data=meta.data,end=start+count;let i;const move=arr=>{for(arr.length+=count,i=arr.length-1;i>=end;i--)arr[i]=arr[i-count]};for(move(data),i=start;i{ret[prop]=anims[prop]&&anims[prop].active()?anims[prop]._to:this[prop]})),ret}}Element.defaults={},Element.defaultRoutes=void 0;const formatters={values:value=>isArray(value)?value:""+value,numeric(tickValue,index,ticks){if(0===tickValue)return"0";const locale=this.chart.options.locale;let notation,delta=tickValue;if(ticks.length>1){const maxTick=Math.max(Math.abs(ticks[0].value),Math.abs(ticks[ticks.length-1].value));(maxTick<1e-4||maxTick>1e15)&&(notation="scientific"),delta=function(tickValue,ticks){let delta=ticks.length>3?ticks[2].value-ticks[1].value:ticks[1].value-ticks[0].value;return Math.abs(delta)>=1&&tickValue!==Math.floor(tickValue)&&(delta=tickValue-Math.floor(tickValue)),delta}(tickValue,ticks)}const logDelta=log10(Math.abs(delta)),numDecimal=Math.max(Math.min(-1*Math.floor(logDelta),20),0),options={notation:notation,minimumFractionDigits:numDecimal,maximumFractionDigits:numDecimal};return Object.assign(options,this.options.ticks.format),formatNumber(tickValue,locale,options)},logarithmic(tickValue,index,ticks){if(0===tickValue)return"0";const remain=tickValue/Math.pow(10,Math.floor(log10(tickValue)));return 1===remain||2===remain||5===remain?formatters.numeric.call(this,tickValue,index,ticks):""}};var Ticks={formatters:formatters};function autoSkip(scale,ticks){const tickOpts=scale.options.ticks,ticksLimit=tickOpts.maxTicksLimit||function(scale){const offset=scale.options.offset,tickLength=scale._tickSize(),maxScale=scale._length/tickLength+(offset?0:1),maxChart=scale._maxLength/tickLength;return Math.floor(Math.min(maxScale,maxChart))}(scale),majorIndices=tickOpts.major.enabled?function(ticks){const result=[];let i,ilen;for(i=0,ilen=ticks.length;iticksLimit)return function(ticks,newTicks,majorIndices,spacing){let i,count=0,next=majorIndices[0];for(spacing=Math.ceil(spacing),i=0;ispacing)return factor}return Math.max(spacing,1)}(majorIndices,ticks,ticksLimit);if(numMajorIndices>0){let i,ilen;const avgMajorSpacing=numMajorIndices>1?Math.round((last-first)/(numMajorIndices-1)):null;for(skip(ticks,newTicks,spacing,isNullOrUndef(avgMajorSpacing)?0:first-avgMajorSpacing,first),i=0,ilen=numMajorIndices-1;ioptions.lineWidth,tickColor:(_ctx,options)=>options.color,offset:!1,borderDash:[],borderDashOffset:0,borderWidth:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:Ticks.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),defaults.route("scale.ticks","color","","color"),defaults.route("scale.grid","color","","borderColor"),defaults.route("scale.grid","borderColor","","borderColor"),defaults.route("scale.title","color","","color"),defaults.describe("scale",{_fallback:!1,_scriptable:name=>!name.startsWith("before")&&!name.startsWith("after")&&"callback"!==name&&"parser"!==name,_indexable:name=>"borderDash"!==name&&"tickBorderDash"!==name}),defaults.describe("scales",{_fallback:"scale"}),defaults.describe("scale.ticks",{_scriptable:name=>"backdropPadding"!==name&&"callback"!==name,_indexable:name=>"backdropPadding"!==name});const offsetFromEdge=(scale,edge,offset)=>"top"===edge||"left"===edge?scale[edge]+offset:scale[edge]-offset;function sample(arr,numItems){const result=[],increment=arr.length/numItems,len=arr.length;let i=0;for(;iend+1e-6)))return lineValue}function getTickMarkLength(options){return options.drawTicks?options.tickLength:0}function getTitleHeight(options,fallback){if(!options.display)return 0;const font=toFont(options.font,fallback),padding=toPadding(options.padding);return(isArray(options.text)?options.text.length:1)*font.lineHeight+padding.height}function titleAlign(align,position,reverse){let ret=_toLeftRightCenter(align);return(reverse&&"right"!==position||!reverse&&"right"===position)&&(ret=(align=>"left"===align?"right":"right"===align?"left":align)(ret)),ret}class Scale extends Element{constructor(cfg){super(),this.id=cfg.id,this.type=cfg.type,this.options=void 0,this.ctx=cfg.ctx,this.chart=cfg.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(options){this.options=options.setContext(this.getContext()),this.axis=options.axis,this._userMin=this.parse(options.min),this._userMax=this.parse(options.max),this._suggestedMin=this.parse(options.suggestedMin),this._suggestedMax=this.parse(options.suggestedMax)}parse(raw,index){return raw}getUserBounds(){let{_userMin:_userMin,_userMax:_userMax,_suggestedMin:_suggestedMin,_suggestedMax:_suggestedMax}=this;return _userMin=finiteOrDefault(_userMin,Number.POSITIVE_INFINITY),_userMax=finiteOrDefault(_userMax,Number.NEGATIVE_INFINITY),_suggestedMin=finiteOrDefault(_suggestedMin,Number.POSITIVE_INFINITY),_suggestedMax=finiteOrDefault(_suggestedMax,Number.NEGATIVE_INFINITY),{min:finiteOrDefault(_userMin,_suggestedMin),max:finiteOrDefault(_userMax,_suggestedMax),minDefined:isNumberFinite(_userMin),maxDefined:isNumberFinite(_userMax)}}getMinMax(canStack){let range,{min:min,max:max,minDefined:minDefined,maxDefined:maxDefined}=this.getUserBounds();if(minDefined&&maxDefined)return{min:min,max:max};const metas=this.getMatchingVisibleMetas();for(let i=0,ilen=metas.length;imax?max:min,max=minDefined&&min>max?min:max,{min:finiteOrDefault(min,finiteOrDefault(max,min)),max:finiteOrDefault(max,finiteOrDefault(min,max))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const data=this.chart.data;return this.options.labels||(this.isHorizontal()?data.xLabels:data.yLabels)||data.labels||[]}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){callback(this.options.beforeUpdate,[this])}update(maxWidth,maxHeight,margins){const{beginAtZero:beginAtZero,grace:grace,ticks:tickOpts}=this.options,sampleSize=tickOpts.sampleSize;this.beforeUpdate(),this.maxWidth=maxWidth,this.maxHeight=maxHeight,this._margins=margins=Object.assign({left:0,right:0,top:0,bottom:0},margins),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+margins.left+margins.right:this.height+margins.top+margins.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=_addGrace(this,grace,beginAtZero),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const samplingEnabled=sampleSize=maxRotation||numTicks<=1||!this.isHorizontal())return void(this.labelRotation=minRotation);const labelSizes=this._getLabelSizes(),maxLabelWidth=labelSizes.widest.width,maxLabelHeight=labelSizes.highest.height,maxWidth=_limitValue(this.chart.width-maxLabelWidth,0,this.maxWidth);tickWidth=options.offset?this.maxWidth/numTicks:maxWidth/(numTicks-1),maxLabelWidth+6>tickWidth&&(tickWidth=maxWidth/(numTicks-(options.offset?.5:1)),maxHeight=this.maxHeight-getTickMarkLength(options.grid)-tickOpts.padding-getTitleHeight(options.title,this.chart.options.font),maxLabelDiagonal=Math.sqrt(maxLabelWidth*maxLabelWidth+maxLabelHeight*maxLabelHeight),labelRotation=toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height+6)/tickWidth,-1,1)),Math.asin(_limitValue(maxHeight/maxLabelDiagonal,-1,1))-Math.asin(_limitValue(maxLabelHeight/maxLabelDiagonal,-1,1)))),labelRotation=Math.max(minRotation,Math.min(maxRotation,labelRotation))),this.labelRotation=labelRotation}afterCalculateLabelRotation(){callback(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){callback(this.options.beforeFit,[this])}fit(){const minSize={width:0,height:0},{chart:chart,options:{ticks:tickOpts,title:titleOpts,grid:gridOpts}}=this,display=this._isVisible(),isHorizontal=this.isHorizontal();if(display){const titleHeight=getTitleHeight(titleOpts,chart.options.font);if(isHorizontal?(minSize.width=this.maxWidth,minSize.height=getTickMarkLength(gridOpts)+titleHeight):(minSize.height=this.maxHeight,minSize.width=getTickMarkLength(gridOpts)+titleHeight),tickOpts.display&&this.ticks.length){const{first:first,last:last,widest:widest,highest:highest}=this._getLabelSizes(),tickPadding=2*tickOpts.padding,angleRadians=toRadians(this.labelRotation),cos=Math.cos(angleRadians),sin=Math.sin(angleRadians);if(isHorizontal){const labelHeight=tickOpts.mirror?0:sin*widest.width+cos*highest.height;minSize.height=Math.min(this.maxHeight,minSize.height+labelHeight+tickPadding)}else{const labelWidth=tickOpts.mirror?0:cos*widest.width+sin*highest.height;minSize.width=Math.min(this.maxWidth,minSize.width+labelWidth+tickPadding)}this._calculatePadding(first,last,sin,cos)}}this._handleMargins(),isHorizontal?(this.width=this._length=chart.width-this._margins.left-this._margins.right,this.height=minSize.height):(this.width=minSize.width,this.height=this._length=chart.height-this._margins.top-this._margins.bottom)}_calculatePadding(first,last,sin,cos){const{ticks:{align:align,padding:padding},position:position}=this.options,isRotated=0!==this.labelRotation,labelsBelowTicks="top"!==position&&"x"===this.axis;if(this.isHorizontal()){const offsetLeft=this.getPixelForTick(0)-this.left,offsetRight=this.right-this.getPixelForTick(this.ticks.length-1);let paddingLeft=0,paddingRight=0;isRotated?labelsBelowTicks?(paddingLeft=cos*first.width,paddingRight=sin*last.height):(paddingLeft=sin*first.height,paddingRight=cos*last.width):"start"===align?paddingRight=last.width:"end"===align?paddingLeft=first.width:"inner"!==align&&(paddingLeft=first.width/2,paddingRight=last.width/2),this.paddingLeft=Math.max((paddingLeft-offsetLeft+padding)*this.width/(this.width-offsetLeft),0),this.paddingRight=Math.max((paddingRight-offsetRight+padding)*this.width/(this.width-offsetRight),0)}else{let paddingTop=last.height/2,paddingBottom=first.height/2;"start"===align?(paddingTop=0,paddingBottom=first.height):"end"===align&&(paddingTop=last.height,paddingBottom=0),this.paddingTop=paddingTop+padding,this.paddingBottom=paddingBottom+padding}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){callback(this.options.afterFit,[this])}isHorizontal(){const{axis:axis,position:position}=this.options;return"top"===position||"bottom"===position||"x"===axis}isFullSize(){return this.options.fullSize}_convertTicksToLabels(ticks){let i,ilen;for(this.beforeTickToLabelConversion(),this.generateTickLabels(ticks),i=0,ilen=ticks.length;i{const gc=cache.gc,gcLen=gc.length/2;let i;if(gcLen>length){for(i=0;i({width:widths[idx]||0,height:heights[idx]||0});return{first:valueAt(0),last:valueAt(length-1),widest:valueAt(widest),highest:valueAt(highest),widths:widths,heights:heights}}getLabelForValue(value){return value}getPixelForValue(value,index){return NaN}getValueForPixel(pixel){}getPixelForTick(index){const ticks=this.ticks;return index<0||index>ticks.length-1?null:this.getPixelForValue(ticks[index].value)}getPixelForDecimal(decimal){this._reversePixels&&(decimal=1-decimal);const pixel=this._startPixel+decimal*this._length;return _int16Range(this._alignToPixels?_alignPixel(this.chart,pixel,0):pixel)}getDecimalForPixel(pixel){const decimal=(pixel-this._startPixel)/this._length;return this._reversePixels?1-decimal:decimal}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:min,max:max}=this;return min<0&&max<0?max:min>0&&max>0?min:0}getContext(index){const ticks=this.ticks||[];if(index>=0&&indexw*sin?w/cos:h/sin:h*sin0}_computeGridLineItems(chartArea){const axis=this.axis,chart=this.chart,options=this.options,{grid:grid,position:position}=options,offset=grid.offset,isHorizontal=this.isHorizontal(),ticksLength=this.ticks.length+(offset?1:0),tl=getTickMarkLength(grid),items=[],borderOpts=grid.setContext(this.getContext()),axisWidth=borderOpts.drawBorder?borderOpts.borderWidth:0,axisHalfWidth=axisWidth/2,alignBorderValue=function(pixel){return _alignPixel(chart,pixel,axisWidth)};let borderValue,i,lineValue,alignedLineValue,tx1,ty1,tx2,ty2,x1,y1,x2,y2;if("top"===position)borderValue=alignBorderValue(this.bottom),ty1=this.bottom-tl,ty2=borderValue-axisHalfWidth,y1=alignBorderValue(chartArea.top)+axisHalfWidth,y2=chartArea.bottom;else if("bottom"===position)borderValue=alignBorderValue(this.top),y1=chartArea.top,y2=alignBorderValue(chartArea.bottom)-axisHalfWidth,ty1=borderValue+axisHalfWidth,ty2=this.top+tl;else if("left"===position)borderValue=alignBorderValue(this.right),tx1=this.right-tl,tx2=borderValue-axisHalfWidth,x1=alignBorderValue(chartArea.left)+axisHalfWidth,x2=chartArea.right;else if("right"===position)borderValue=alignBorderValue(this.left),x1=chartArea.left,x2=alignBorderValue(chartArea.right)-axisHalfWidth,tx1=borderValue+axisHalfWidth,tx2=this.left+tl;else if("x"===axis){if("center"===position)borderValue=alignBorderValue((chartArea.top+chartArea.bottom)/2+.5);else if(isObject(position)){const positionAxisID=Object.keys(position)[0],value=position[positionAxisID];borderValue=alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value))}y1=chartArea.top,y2=chartArea.bottom,ty1=borderValue+axisHalfWidth,ty2=ty1+tl}else if("y"===axis){if("center"===position)borderValue=alignBorderValue((chartArea.left+chartArea.right)/2);else if(isObject(position)){const positionAxisID=Object.keys(position)[0],value=position[positionAxisID];borderValue=alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value))}tx1=borderValue-axisHalfWidth,tx2=tx1-tl,x1=chartArea.left,x2=chartArea.right}const limit=valueOrDefault(options.ticks.maxTicksLimit,ticksLength),step=Math.max(1,Math.ceil(ticksLength/limit));for(i=0;it.value===value));return index>=0?grid.setContext(this.getContext(index)).lineWidth:0}drawGrid(chartArea){const grid=this.options.grid,ctx=this.ctx,items=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(chartArea));let i,ilen;const drawLine=(p1,p2,style)=>{style.width&&style.color&&(ctx.save(),ctx.lineWidth=style.width,ctx.strokeStyle=style.color,ctx.setLineDash(style.borderDash||[]),ctx.lineDashOffset=style.borderDashOffset,ctx.beginPath(),ctx.moveTo(p1.x,p1.y),ctx.lineTo(p2.x,p2.y),ctx.stroke(),ctx.restore())};if(grid.display)for(i=0,ilen=items.length;i{this.drawBackground(),this.drawGrid(chartArea),this.drawTitle()}},{z:gz+1,draw:()=>{this.drawBorder()}},{z:tz,draw:chartArea=>{this.drawLabels(chartArea)}}]:[{z:tz,draw:chartArea=>{this.draw(chartArea)}}]}getMatchingVisibleMetas(type){const metas=this.chart.getSortedVisibleDatasetMetas(),axisID=this.axis+"AxisID",result=[];let i,ilen;for(i=0,ilen=metas.length;i{const propertyParts=property.split("."),sourceName=propertyParts.pop(),sourceScope=[scope].concat(propertyParts).join("."),parts=routes[property].split("."),targetName=parts.pop(),targetScope=parts.join(".");defaults.route(sourceScope,sourceName,targetScope,targetName)}))}(scope,item.defaultRoutes),item.descriptors&&defaults.describe(scope,item.descriptors)}(item,scope,parentScope),this.override&&defaults.override(item.id,item.overrides)),scope}get(id){return this.items[id]}unregister(item){const items=this.items,id=item.id,scope=this.scope;id in items&&delete items[id],scope&&id in defaults[scope]&&(delete defaults[scope][id],this.override&&delete overrides[id])}}var registry=new class{constructor(){this.controllers=new TypedRegistry(DatasetController,"datasets",!0),this.elements=new TypedRegistry(Element,"elements"),this.plugins=new TypedRegistry(Object,"plugins"),this.scales=new TypedRegistry(Scale,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(){for(var _len4=arguments.length,args=new Array(_len4),_key4=0;_key4<_len4;_key4++)args[_key4]=arguments[_key4];this._each("register",args)}remove(){for(var _len5=arguments.length,args=new Array(_len5),_key5=0;_key5<_len5;_key5++)args[_key5]=arguments[_key5];this._each("unregister",args)}addControllers(){for(var _len6=arguments.length,args=new Array(_len6),_key6=0;_key6<_len6;_key6++)args[_key6]=arguments[_key6];this._each("register",args,this.controllers)}addElements(){for(var _len7=arguments.length,args=new Array(_len7),_key7=0;_key7<_len7;_key7++)args[_key7]=arguments[_key7];this._each("register",args,this.elements)}addPlugins(){for(var _len8=arguments.length,args=new Array(_len8),_key8=0;_key8<_len8;_key8++)args[_key8]=arguments[_key8];this._each("register",args,this.plugins)}addScales(){for(var _len9=arguments.length,args=new Array(_len9),_key9=0;_key9<_len9;_key9++)args[_key9]=arguments[_key9];this._each("register",args,this.scales)}getController(id){return this._get(id,this.controllers,"controller")}getElement(id){return this._get(id,this.elements,"element")}getPlugin(id){return this._get(id,this.plugins,"plugin")}getScale(id){return this._get(id,this.scales,"scale")}removeControllers(){for(var _len10=arguments.length,args=new Array(_len10),_key10=0;_key10<_len10;_key10++)args[_key10]=arguments[_key10];this._each("unregister",args,this.controllers)}removeElements(){for(var _len11=arguments.length,args=new Array(_len11),_key11=0;_key11<_len11;_key11++)args[_key11]=arguments[_key11];this._each("unregister",args,this.elements)}removePlugins(){for(var _len12=arguments.length,args=new Array(_len12),_key12=0;_key12<_len12;_key12++)args[_key12]=arguments[_key12];this._each("unregister",args,this.plugins)}removeScales(){for(var _len13=arguments.length,args=new Array(_len13),_key13=0;_key13<_len13;_key13++)args[_key13]=arguments[_key13];this._each("unregister",args,this.scales)}_each(method,args,typedRegistry){[...args].forEach((arg=>{const reg=typedRegistry||this._getRegistryForType(arg);typedRegistry||reg.isForType(arg)||reg===this.plugins&&arg.id?this._exec(method,reg,arg):each(arg,(item=>{const itemReg=typedRegistry||this._getRegistryForType(item);this._exec(method,itemReg,item)}))}))}_exec(method,registry,component){const camelMethod=_capitalize(method);callback(component["before"+camelMethod],[],component),registry[method](component),callback(component["after"+camelMethod],[],component)}_getRegistryForType(type){for(let i=0;ia.filter((x=>!b.some((y=>x.plugin.id===y.plugin.id))));this._notify(diff(previousDescriptors,descriptors),chart,"stop"),this._notify(diff(descriptors,previousDescriptors),chart,"start")}}function getOpts(options,all){return all||!1!==options?!0===options?{}:options:null}function pluginOpts(config,plugin,opts,context){const keys=config.pluginScopeKeys(plugin),scopes=config.getOptionScopes(opts,keys);return config.createResolver(scopes,context,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function getIndexAxis(type,options){const datasetDefaults=defaults.datasets[type]||{};return((options.datasets||{})[type]||{}).indexAxis||options.indexAxis||datasetDefaults.indexAxis||"x"}function determineAxis(id,scaleOptions){return"x"===id||"y"===id?id:scaleOptions.axis||("top"===(position=scaleOptions.position)||"bottom"===position?"x":"left"===position||"right"===position?"y":void 0)||id.charAt(0).toLowerCase();var position}function initOptions(config){const options=config.options||(config.options={});options.plugins=valueOrDefault(options.plugins,{}),options.scales=function(config,options){const chartDefaults=overrides[config.type]||{scales:{}},configScales=options.scales||{},chartIndexAxis=getIndexAxis(config.type,options),firstIDs=Object.create(null),scales=Object.create(null);return Object.keys(configScales).forEach((id=>{const scaleConf=configScales[id];if(!isObject(scaleConf))return console.error("Invalid scale configuration for scale: ".concat(id));if(scaleConf._proxy)return console.warn("Ignoring resolver passed as options for scale: ".concat(id));const axis=determineAxis(id,scaleConf),defaultId=function(axis,indexAxis){return axis===indexAxis?"_index_":"_value_"}(axis,chartIndexAxis),defaultScaleOptions=chartDefaults.scales||{};firstIDs[axis]=firstIDs[axis]||id,scales[id]=mergeIf(Object.create(null),[{axis:axis},scaleConf,defaultScaleOptions[axis],defaultScaleOptions[defaultId]])})),config.data.datasets.forEach((dataset=>{const type=dataset.type||config.type,indexAxis=dataset.indexAxis||getIndexAxis(type,options),defaultScaleOptions=(overrides[type]||{}).scales||{};Object.keys(defaultScaleOptions).forEach((defaultID=>{const axis=function(id,indexAxis){let axis=id;return"_index_"===id?axis=indexAxis:"_value_"===id&&(axis="x"===indexAxis?"y":"x"),axis}(defaultID,indexAxis),id=dataset[axis+"AxisID"]||firstIDs[axis]||axis;scales[id]=scales[id]||Object.create(null),mergeIf(scales[id],[{axis:axis},configScales[id],defaultScaleOptions[defaultID]])}))})),Object.keys(scales).forEach((key=>{const scale=scales[key];mergeIf(scale,[defaults.scales[scale.type],defaults.scale])})),scales}(config,options)}function initData(data){return(data=data||{}).datasets=data.datasets||[],data.labels=data.labels||[],data}const keyCache=new Map,keysCached=new Set;function cachedKeys(cacheKey,generate){let keys=keyCache.get(cacheKey);return keys||(keys=generate(),keyCache.set(cacheKey,keys),keysCached.add(keys)),keys}const addIfFound=(set,obj,key)=>{const opts=resolveObjectKey(obj,key);void 0!==opts&&set.add(opts)};class Config{constructor(config){this._config=function(config){return(config=config||{}).data=initData(config.data),initOptions(config),config}(config),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(type){this._config.type=type}get data(){return this._config.data}set data(data){this._config.data=initData(data)}get options(){return this._config.options}set options(options){this._config.options=options}get plugins(){return this._config.plugins}update(){const config=this._config;this.clearCache(),initOptions(config)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(datasetType){return cachedKeys(datasetType,(()=>[["datasets.".concat(datasetType),""]]))}datasetAnimationScopeKeys(datasetType,transition){return cachedKeys("".concat(datasetType,".transition.").concat(transition),(()=>[["datasets.".concat(datasetType,".transitions.").concat(transition),"transitions.".concat(transition)],["datasets.".concat(datasetType),""]]))}datasetElementScopeKeys(datasetType,elementType){return cachedKeys("".concat(datasetType,"-").concat(elementType),(()=>[["datasets.".concat(datasetType,".elements.").concat(elementType),"datasets.".concat(datasetType),"elements.".concat(elementType),""]]))}pluginScopeKeys(plugin){const id=plugin.id,type=this.type;return cachedKeys("".concat(type,"-plugin-").concat(id),(()=>[["plugins.".concat(id),...plugin.additionalOptionScopes||[]]]))}_cachedScopes(mainScope,resetCache){const _scopeCache=this._scopeCache;let cache=_scopeCache.get(mainScope);return cache&&!resetCache||(cache=new Map,_scopeCache.set(mainScope,cache)),cache}getOptionScopes(mainScope,keyLists,resetCache){const{options:options,type:type}=this,cache=this._cachedScopes(mainScope,resetCache),cached=cache.get(keyLists);if(cached)return cached;const scopes=new Set;keyLists.forEach((keys=>{mainScope&&(scopes.add(mainScope),keys.forEach((key=>addIfFound(scopes,mainScope,key)))),keys.forEach((key=>addIfFound(scopes,options,key))),keys.forEach((key=>addIfFound(scopes,overrides[type]||{},key))),keys.forEach((key=>addIfFound(scopes,defaults,key))),keys.forEach((key=>addIfFound(scopes,descriptors,key)))}));const array=Array.from(scopes);return 0===array.length&&array.push(Object.create(null)),keysCached.has(keyLists)&&cache.set(keyLists,array),array}chartOptionScopes(){const{options:options,type:type}=this;return[options,overrides[type]||{},defaults.datasets[type]||{},{type:type},defaults,descriptors]}resolveNamedOptions(scopes,names,context){let prefixes=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[""];const result={$shared:!0},{resolver:resolver,subPrefixes:subPrefixes}=getResolver(this._resolverCache,scopes,prefixes);let options=resolver;(function(proxy,names){const{isScriptable:isScriptable,isIndexable:isIndexable}=_descriptors(proxy);for(const prop of names){const scriptable=isScriptable(prop),indexable=isIndexable(prop),value=(indexable||scriptable)&&proxy[prop];if(scriptable&&(isFunction(value)||hasFunction(value))||indexable&&isArray(value))return!0}return!1})(resolver,names)&&(result.$shared=!1,options=_attachContext(resolver,context=isFunction(context)?context():context,this.createResolver(scopes,context,subPrefixes)));for(const prop of names)result[prop]=options[prop];return result}createResolver(scopes,context){let prefixes=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[""],descriptorDefaults=arguments.length>3?arguments[3]:void 0;const{resolver:resolver}=getResolver(this._resolverCache,scopes,prefixes);return isObject(context)?_attachContext(resolver,context,void 0,descriptorDefaults):resolver}}function getResolver(resolverCache,scopes,prefixes){let cache=resolverCache.get(scopes);cache||(cache=new Map,resolverCache.set(scopes,cache));const cacheKey=prefixes.join();let cached=cache.get(cacheKey);return cached||(cached={resolver:_createResolver(scopes,prefixes),subPrefixes:prefixes.filter((p=>!p.toLowerCase().includes("hover")))},cache.set(cacheKey,cached)),cached}const hasFunction=value=>isObject(value)&&Object.getOwnPropertyNames(value).reduce(((acc,key)=>acc||isFunction(value[key])),!1),KNOWN_POSITIONS=["top","bottom","left","right","chartArea"];function positionIsHorizontal(position,axis){return"top"===position||"bottom"===position||-1===KNOWN_POSITIONS.indexOf(position)&&"x"===axis}function compare2Level(l1,l2){return function(a,b){return a[l1]===b[l1]?a[l2]-b[l2]:a[l1]-b[l1]}}function onAnimationsComplete(context){const chart=context.chart,animationOptions=chart.options.animation;chart.notifyPlugins("afterRender"),callback(animationOptions&&animationOptions.onComplete,[context],chart)}function onAnimationProgress(context){const chart=context.chart,animationOptions=chart.options.animation;callback(animationOptions&&animationOptions.onProgress,[context],chart)}function getCanvas(item){return _isDomSupported()&&"string"==typeof item?item=document.getElementById(item):item&&item.length&&(item=item[0]),item&&item.canvas&&(item=item.canvas),item}const instances={},getChart=key=>{const canvas=getCanvas(key);return Object.values(instances).filter((c=>c.canvas===canvas)).pop()};function moveNumericKeys(obj,start,move){const keys=Object.keys(obj);for(const key of keys){const intKey=+key;if(intKey>=start){const value=obj[key];delete obj[key],(move>0||intKey>start)&&(obj[intKey+move]=value)}}}class Chart{constructor(item,userConfig){const config=this.config=new Config(userConfig),initialCanvas=getCanvas(item),existingChart=getChart(initialCanvas);if(existingChart)throw new Error("Canvas is already in use. Chart with ID '"+existingChart.id+"' must be destroyed before the canvas can be reused.");const options=config.createResolver(config.chartOptionScopes(),this.getContext());this.platform=new(config.platform||_detectPlatform(initialCanvas)),this.platform.updateConfig(config);const context=this.platform.acquireContext(initialCanvas,options.aspectRatio),canvas=context&&context.canvas,height=canvas&&canvas.height,width=canvas&&canvas.width;this.id=uid(),this.ctx=context,this.canvas=canvas,this.width=width,this.height=height,this._options=options,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new PluginService,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=debounce((mode=>this.update(mode)),options.resizeDelay||0),this._dataChanges=[],instances[this.id]=this,context&&canvas?(animator.listen(this,"complete",onAnimationsComplete),animator.listen(this,"progress",onAnimationProgress),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:aspectRatio,maintainAspectRatio:maintainAspectRatio},width:width,height:height,_aspectRatio:_aspectRatio}=this;return isNullOrUndef(aspectRatio)?maintainAspectRatio&&_aspectRatio?_aspectRatio:height?width/height:null:aspectRatio}get data(){return this.config.data}set data(data){this.config.data=data}get options(){return this._options}set options(options){this.config.options=options}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():retinaScale(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return clearCanvas(this.canvas,this.ctx),this}stop(){return animator.stop(this),this}resize(width,height){animator.running(this)?this._resizeBeforeDraw={width:width,height:height}:this._resize(width,height)}_resize(width,height){const options=this.options,canvas=this.canvas,aspectRatio=options.maintainAspectRatio&&this.aspectRatio,newSize=this.platform.getMaximumSize(canvas,width,height,aspectRatio),newRatio=options.devicePixelRatio||this.platform.getDevicePixelRatio(),mode=this.width?"resize":"attach";this.width=newSize.width,this.height=newSize.height,this._aspectRatio=this.aspectRatio,retinaScale(this,newRatio,!0)&&(this.notifyPlugins("resize",{size:newSize}),callback(options.onResize,[this,newSize],this),this.attached&&this._doResize(mode)&&this.render())}ensureScalesHaveIDs(){each(this.options.scales||{},((axisOptions,axisID)=>{axisOptions.id=axisID}))}buildOrUpdateScales(){const options=this.options,scaleOpts=options.scales,scales=this.scales,updated=Object.keys(scales).reduce(((obj,id)=>(obj[id]=!1,obj)),{});let items=[];scaleOpts&&(items=items.concat(Object.keys(scaleOpts).map((id=>{const scaleOptions=scaleOpts[id],axis=determineAxis(id,scaleOptions),isRadial="r"===axis,isHorizontal="x"===axis;return{options:scaleOptions,dposition:isRadial?"chartArea":isHorizontal?"bottom":"left",dtype:isRadial?"radialLinear":isHorizontal?"category":"linear"}})))),each(items,(item=>{const scaleOptions=item.options,id=scaleOptions.id,axis=determineAxis(id,scaleOptions),scaleType=valueOrDefault(scaleOptions.type,item.dtype);void 0!==scaleOptions.position&&positionIsHorizontal(scaleOptions.position,axis)===positionIsHorizontal(item.dposition)||(scaleOptions.position=item.dposition),updated[id]=!0;let scale=null;id in scales&&scales[id].type===scaleType?scale=scales[id]:(scale=new(registry.getScale(scaleType))({id:id,type:scaleType,ctx:this.ctx,chart:this}),scales[scale.id]=scale),scale.init(scaleOptions,options)})),each(updated,((hasUpdated,id)=>{hasUpdated||delete scales[id]})),each(scales,(scale=>{layouts.configure(this,scale,scale.options),layouts.addBox(this,scale)}))}_updateMetasets(){const metasets=this._metasets,numData=this.data.datasets.length,numMeta=metasets.length;if(metasets.sort(((a,b)=>a.index-b.index)),numMeta>numData){for(let i=numData;idatasets.length&&delete this._stacks,metasets.forEach(((meta,index)=>{0===datasets.filter((x=>x===meta._dataset)).length&&this._destroyDatasetMeta(index)}))}buildOrUpdateControllers(){const newControllers=[],datasets=this.data.datasets;let i,ilen;for(this._removeUnreferencedMetasets(),i=0,ilen=datasets.length;i{this.getDatasetMeta(datasetIndex).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(mode){const config=this.config;config.update();const options=this._options=config.createResolver(config.chartOptionScopes(),this.getContext()),animsDisabled=this._animationsDisabled=!options.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:mode,cancelable:!0}))return;const newControllers=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let minPadding=0;for(let i=0,ilen=this.data.datasets.length;i{controller.reset()})),this._updateDatasets(mode),this.notifyPlugins("afterUpdate",{mode:mode}),this._layers.sort(compare2Level("z","_idx"));const{_active:_active,_lastEvent:_lastEvent}=this;_lastEvent?this._eventHandler(_lastEvent,!0):_active.length&&this._updateHoverStyles(_active,_active,!0),this.render()}_updateScales(){each(this.scales,(scale=>{layouts.removeBox(this,scale)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const options=this.options,existingEvents=new Set(Object.keys(this._listeners)),newEvents=new Set(options.events);setsEqual(existingEvents,newEvents)&&!!this._responsiveListeners===options.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:_hiddenIndices}=this,changes=this._getUniformDataChanges()||[];for(const{method:method,start:start,count:count}of changes)moveNumericKeys(_hiddenIndices,start,"_removeElements"===method?-count:count)}_getUniformDataChanges(){const _dataChanges=this._dataChanges;if(!_dataChanges||!_dataChanges.length)return;this._dataChanges=[];const datasetCount=this.data.datasets.length,makeSet=idx=>new Set(_dataChanges.filter((c=>c[0]===idx)).map(((c,i)=>i+","+c.splice(1).join(",")))),changeSet=makeSet(0);for(let i=1;ic.split(","))).map((a=>({method:a[1],start:+a[2],count:+a[3]})))}_updateLayout(minPadding){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;layouts.update(this,this.width,this.height,minPadding);const area=this.chartArea,noArea=area.width<=0||area.height<=0;this._layers=[],each(this.boxes,(box=>{noArea&&"chartArea"===box.position||(box.configure&&box.configure(),this._layers.push(...box._layers()))}),this),this._layers.forEach(((item,index)=>{item._idx=index})),this.notifyPlugins("afterLayout")}_updateDatasets(mode){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:mode,cancelable:!0})){for(let i=0,ilen=this.data.datasets.length;i=0;--i)this._drawDataset(metasets[i]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(meta){const ctx=this.ctx,clip=meta._clip,useClip=!clip.disabled,area=this.chartArea,args={meta:meta,index:meta.index,cancelable:!0};!1!==this.notifyPlugins("beforeDatasetDraw",args)&&(useClip&&clipArea(ctx,{left:!1===clip.left?0:area.left-clip.left,right:!1===clip.right?this.width:area.right+clip.right,top:!1===clip.top?0:area.top-clip.top,bottom:!1===clip.bottom?this.height:area.bottom+clip.bottom}),meta.controller.draw(),useClip&&unclipArea(ctx),args.cancelable=!1,this.notifyPlugins("afterDatasetDraw",args))}isPointInArea(point){return _isPointInArea(point,this.chartArea,this._minPadding)}getElementsAtEventForMode(e,mode,options,useFinalPosition){const method=Interaction.modes[mode];return"function"==typeof method?method(this,e,options,useFinalPosition):[]}getDatasetMeta(datasetIndex){const dataset=this.data.datasets[datasetIndex],metasets=this._metasets;let meta=metasets.filter((x=>x&&x._dataset===dataset)).pop();return meta||(meta={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:dataset&&dataset.order||0,index:datasetIndex,_dataset:dataset,_parsed:[],_sorted:!1},metasets.push(meta)),meta}getContext(){return this.$context||(this.$context=createContext(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(datasetIndex){const dataset=this.data.datasets[datasetIndex];if(!dataset)return!1;const meta=this.getDatasetMeta(datasetIndex);return"boolean"==typeof meta.hidden?!meta.hidden:!dataset.hidden}setDatasetVisibility(datasetIndex,visible){this.getDatasetMeta(datasetIndex).hidden=!visible}toggleDataVisibility(index){this._hiddenIndices[index]=!this._hiddenIndices[index]}getDataVisibility(index){return!this._hiddenIndices[index]}_updateVisibility(datasetIndex,dataIndex,visible){const mode=visible?"show":"hide",meta=this.getDatasetMeta(datasetIndex),anims=meta.controller._resolveAnimations(void 0,mode);defined(dataIndex)?(meta.data[dataIndex].hidden=!visible,this.update()):(this.setDatasetVisibility(datasetIndex,visible),anims.update(meta,{visible:visible}),this.update((ctx=>ctx.datasetIndex===datasetIndex?mode:void 0)))}hide(datasetIndex,dataIndex){this._updateVisibility(datasetIndex,dataIndex,!1)}show(datasetIndex,dataIndex){this._updateVisibility(datasetIndex,dataIndex,!0)}_destroyDatasetMeta(datasetIndex){const meta=this._metasets[datasetIndex];meta&&meta.controller&&meta.controller._destroy(),delete this._metasets[datasetIndex]}_stop(){let i,ilen;for(this.stop(),animator.remove(this),i=0,ilen=this.data.datasets.length;i{platform.addEventListener(this,type,listener),listeners[type]=listener},listener=(e,x,y)=>{e.offsetX=x,e.offsetY=y,this._eventHandler(e)};each(this.options.events,(type=>_add(type,listener)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const listeners=this._responsiveListeners,platform=this.platform,_add=(type,listener)=>{platform.addEventListener(this,type,listener),listeners[type]=listener},_remove=(type,listener)=>{listeners[type]&&(platform.removeEventListener(this,type,listener),delete listeners[type])},listener=(width,height)=>{this.canvas&&this.resize(width,height)};let detached;const attached=()=>{_remove("attach",attached),this.attached=!0,this.resize(),_add("resize",listener),_add("detach",detached)};detached=()=>{this.attached=!1,_remove("resize",listener),this._stop(),this._resize(0,0),_add("attach",attached)},platform.isAttached(this.canvas)?attached():detached()}unbindEvents(){each(this._listeners,((listener,type)=>{this.platform.removeEventListener(this,type,listener)})),this._listeners={},each(this._responsiveListeners,((listener,type)=>{this.platform.removeEventListener(this,type,listener)})),this._responsiveListeners=void 0}updateHoverStyle(items,mode,enabled){const prefix=enabled?"set":"remove";let meta,item,i,ilen;for("dataset"===mode&&(meta=this.getDatasetMeta(items[0].datasetIndex),meta.controller["_"+prefix+"DatasetHoverStyle"]()),i=0,ilen=items.length;i{let{datasetIndex:datasetIndex,index:index}=_ref2;const meta=this.getDatasetMeta(datasetIndex);if(!meta)throw new Error("No dataset found at index "+datasetIndex);return{datasetIndex:datasetIndex,element:meta.data[index],index:index}}));!_elementsEqual(active,lastActive)&&(this._active=active,this._lastEvent=null,this._updateHoverStyles(active,lastActive))}notifyPlugins(hook,args,filter){return this._plugins.notify(this,hook,args,filter)}_updateHoverStyles(active,lastActive,replay){const hoverOptions=this.options.hover,diff=(a,b)=>a.filter((x=>!b.some((y=>x.datasetIndex===y.datasetIndex&&x.index===y.index)))),deactivated=diff(lastActive,active),activated=replay?active:diff(active,lastActive);deactivated.length&&this.updateHoverStyle(deactivated,hoverOptions.mode,!1),activated.length&&hoverOptions.mode&&this.updateHoverStyle(activated,hoverOptions.mode,!0)}_eventHandler(e,replay){const args={event:e,replay:replay,cancelable:!0,inChartArea:this.isPointInArea(e)},eventFilter=plugin=>(plugin.options.events||this.options.events).includes(e.native.type);if(!1===this.notifyPlugins("beforeEvent",args,eventFilter))return;const changed=this._handleEvent(e,replay,args.inChartArea);return args.cancelable=!1,this.notifyPlugins("afterEvent",args,eventFilter),(changed||args.changed)&&this.render(),this}_handleEvent(e,replay,inChartArea){const{_active:lastActive=[],options:options}=this,useFinalPosition=replay,active=this._getActiveElements(e,lastActive,inChartArea,useFinalPosition),isClick=_isClickEvent(e),lastEvent=function(e,lastEvent,inChartArea,isClick){return inChartArea&&"mouseout"!==e.type?isClick?lastEvent:e:null}(e,this._lastEvent,inChartArea,isClick);inChartArea&&(this._lastEvent=null,callback(options.onHover,[e,active,this],this),isClick&&callback(options.onClick,[e,active,this],this));const changed=!_elementsEqual(active,lastActive);return(changed||replay)&&(this._active=active,this._updateHoverStyles(active,lastActive,replay)),this._lastEvent=lastEvent,changed}_getActiveElements(e,lastActive,inChartArea,useFinalPosition){if("mouseout"===e.type)return[];if(!inChartArea)return lastActive;const hoverOptions=this.options.hover;return this.getElementsAtEventForMode(e,hoverOptions.mode,hoverOptions,useFinalPosition)}}const invalidatePlugins=()=>each(Chart.instances,(chart=>chart._plugins.invalidate()));function abstract(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}Object.defineProperties(Chart,{defaults:{enumerable:!0,value:defaults},instances:{enumerable:!0,value:instances},overrides:{enumerable:!0,value:overrides},registry:{enumerable:!0,value:registry},version:{enumerable:!0,value:"3.8.0"},getChart:{enumerable:!0,value:getChart},register:{enumerable:!0,value:function(){registry.add(...arguments),invalidatePlugins()}},unregister:{enumerable:!0,value:function(){registry.remove(...arguments),invalidatePlugins()}}});class DateAdapter{constructor(options){this.options=options||{}}formats(){return abstract()}parse(value,format){return abstract()}format(timestamp,format){return abstract()}add(timestamp,amount,unit){return abstract()}diff(a,b,unit){return abstract()}startOf(timestamp,unit,weekday){return abstract()}endOf(timestamp,unit){return abstract()}}DateAdapter.override=function(members){Object.assign(DateAdapter.prototype,members)};var _adapters={_date:DateAdapter};function computeMinSampleSize(meta){const scale=meta.iScale,values=function(scale,type){if(!scale._cache.$bar){const visibleMetas=scale.getMatchingVisibleMetas(type);let values=[];for(let i=0,ilen=visibleMetas.length;ia-b)))}return scale._cache.$bar}(scale,meta.type);let i,ilen,curr,prev,min=scale._length;const updateMinAndPrev=()=>{32767!==curr&&-32768!==curr&&(defined(prev)&&(min=Math.min(min,Math.abs(curr-prev)||min)),prev=curr)};for(i=0,ilen=values.length;iMath.abs(max)&&(barStart=max,barEnd=min),item[vScale.axis]=barEnd,item._custom={barStart:barStart,barEnd:barEnd,start:startValue,end:endValue,min:min,max:max}}(entry,item,vScale,i):item[vScale.axis]=vScale.parse(entry,i),item}function parseArrayOrPrimitive(meta,data,start,count){const iScale=meta.iScale,vScale=meta.vScale,labels=iScale.getLabels(),singleScale=iScale===vScale,parsed=[];let i,ilen,item,entry;for(i=start,ilen=start+count;iproperties.x,start="left",end="right"):(reverse=properties.base=actualBase?1:-1)}(size,vScale,actualBase)*minBarLength,value===actualBase&&(base-=size/2);const startPixel=vScale.getPixelForDecimal(0),endPixel=vScale.getPixelForDecimal(1),min=Math.min(startPixel,endPixel),max=Math.max(startPixel,endPixel);base=Math.max(Math.min(base,max),min),head=base+size}if(base===vScale.getPixelForValue(actualBase)){const halfGrid=sign(size)*vScale.getLineWidthForValue(actualBase)/2;base+=halfGrid,size-=halfGrid}return{size:size,base:base,head:head,center:head+size/2}}_calculateBarIndexPixels(index,ruler){const scale=ruler.scale,options=this.options,skipNull=options.skipNull,maxBarThickness=valueOrDefault(options.maxBarThickness,1/0);let center,size;if(ruler.grouped){const stackCount=skipNull?this._getStackCount(index):ruler.stackCount,range="flex"===options.barThickness?function(index,ruler,options,stackCount){const pixels=ruler.pixels,curr=pixels[index];let prev=index>0?pixels[index-1]:null,next=index=0;--i)max=Math.max(max,data[i].size(this.resolveDataElementOptions(i))/2);return max>0&&max}getLabelAndValue(index){const meta=this._cachedMeta,{xScale:xScale,yScale:yScale}=meta,parsed=this.getParsed(index),x=xScale.getLabelForValue(parsed.x),y=yScale.getLabelForValue(parsed.y),r=parsed._custom;return{label:meta.label,value:"("+x+", "+y+(r?", "+r:"")+")"}}update(mode){const points=this._cachedMeta.data;this.updateElements(points,0,points.length,mode)}updateElements(points,start,count,mode){const reset="reset"===mode,{iScale:iScale,vScale:vScale}=this._cachedMeta,firstOpts=this.resolveDataElementOptions(start,mode),sharedOptions=this.getSharedOptions(firstOpts),includeOptions=this.includeOptions(mode,sharedOptions),iAxis=iScale.axis,vAxis=vScale.axis;for(let i=start;i""}}}};class DoughnutController extends DatasetController{constructor(chart,datasetIndex){super(chart,datasetIndex),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(start,count){const data=this.getDataset().data,meta=this._cachedMeta;if(!1===this._parsing)meta._parsed=data;else{let i,ilen,getter=i=>+data[i];if(isObject(data[start])){const{key:key="value"}=this._parsing;getter=i=>+resolveObjectKey(data[i],key)}for(i=start,ilen=start+count;i_angleBetween(angle,startAngle,endAngle,!0)?1:Math.max(a,a*cutout,b,b*cutout),calcMin=(angle,a,b)=>_angleBetween(angle,startAngle,endAngle,!0)?-1:Math.min(a,a*cutout,b,b*cutout),maxX=calcMax(0,startX,endX),maxY=calcMax(HALF_PI,startY,endY),minX=calcMin(PI,startX,endX),minY=calcMin(PI+HALF_PI,startY,endY);ratioX=(maxX-minX)/2,ratioY=(maxY-minY)/2,offsetX=-(maxX+minX)/2,offsetY=-(maxY+minY)/2}return{ratioX:ratioX,ratioY:ratioY,offsetX:offsetX,offsetY:offsetY}}(rotation,circumference,cutout),maxWidth=(chartArea.width-spacing)/ratioX,maxHeight=(chartArea.height-spacing)/ratioY,maxRadius=Math.max(Math.min(maxWidth,maxHeight)/2,0),outerRadius=toDimension(this.options.radius,maxRadius),radiusLength=(outerRadius-Math.max(outerRadius*cutout,0))/this._getVisibleDatasetWeightTotal();this.offsetX=offsetX*outerRadius,this.offsetY=offsetY*outerRadius,meta.total=this.calculateTotal(),this.outerRadius=outerRadius-radiusLength*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-radiusLength*chartWeight,0),this.updateElements(arcs,0,arcs.length,mode)}_circumference(i,reset){const opts=this.options,meta=this._cachedMeta,circumference=this._getCircumference();return reset&&opts.animation.animateRotate||!this.chart.getDataVisibility(i)||null===meta._parsed[i]||meta.data[i].hidden?0:this.calculateCircumference(meta._parsed[i]*circumference/TAU)}updateElements(arcs,start,count,mode){const reset="reset"===mode,chart=this.chart,chartArea=chart.chartArea,animationOpts=chart.options.animation,centerX=(chartArea.left+chartArea.right)/2,centerY=(chartArea.top+chartArea.bottom)/2,animateScale=reset&&animationOpts.animateScale,innerRadius=animateScale?0:this.innerRadius,outerRadius=animateScale?0:this.outerRadius,firstOpts=this.resolveDataElementOptions(start,mode),sharedOptions=this.getSharedOptions(firstOpts),includeOptions=this.includeOptions(mode,sharedOptions);let i,startAngle=this._getRotation();for(i=0;i0&&!isNaN(value)?TAU*(Math.abs(value)/total):0}getLabelAndValue(index){const meta=this._cachedMeta,chart=this.chart,labels=chart.data.labels||[],value=formatNumber(meta._parsed[index],chart.options.locale);return{label:labels[index]||"",value:value}}getMaxBorderWidth(arcs){let max=0;const chart=this.chart;let i,ilen,meta,controller,options;if(!arcs)for(i=0,ilen=chart.data.datasets.length;i"spacing"!==name,_indexable:name=>"spacing"!==name},DoughnutController.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(chart){const data=chart.data;if(data.labels.length&&data.datasets.length){const{labels:{pointStyle:pointStyle}}=chart.legend.options;return data.labels.map(((label,i)=>{const style=chart.getDatasetMeta(0).controller.getStyle(i);return{text:label,fillStyle:style.backgroundColor,strokeStyle:style.borderColor,lineWidth:style.borderWidth,pointStyle:pointStyle,hidden:!chart.getDataVisibility(i),index:i}}))}return[]}},onClick(e,legendItem,legend){legend.chart.toggleDataVisibility(legendItem.index),legend.chart.update()}},tooltip:{callbacks:{title:()=>"",label(tooltipItem){let dataLabel=tooltipItem.label;const value=": "+tooltipItem.formattedValue;return isArray(dataLabel)?(dataLabel=dataLabel.slice(),dataLabel[0]+=value):dataLabel+=value,dataLabel}}}}};class LineController extends DatasetController{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(mode){const meta=this._cachedMeta,{dataset:line,data:points=[],_dataset:_dataset}=meta,animationsDisabled=this.chart._animationsDisabled;let{start:start,count:count}=function(meta,points,animationsDisabled){const pointCount=points.length;let start=0,count=pointCount;if(meta._sorted){const{iScale:iScale,_parsed:_parsed}=meta,axis=iScale.axis,{min:min,max:max,minDefined:minDefined,maxDefined:maxDefined}=iScale.getUserBounds();minDefined&&(start=_limitValue(Math.min(_lookupByKey(_parsed,iScale.axis,min).lo,animationsDisabled?pointCount:_lookupByKey(points,axis,iScale.getPixelForValue(min)).lo),0,pointCount-1)),count=maxDefined?_limitValue(Math.max(_lookupByKey(_parsed,iScale.axis,max).hi+1,animationsDisabled?0:_lookupByKey(points,axis,iScale.getPixelForValue(max)).hi+1),start,pointCount)-start:pointCount-start}return{start:start,count:count}}(meta,points,animationsDisabled);this._drawStart=start,this._drawCount=count,function(meta){const{xScale:xScale,yScale:yScale,_scaleRanges:_scaleRanges}=meta,newRanges={xmin:xScale.min,xmax:xScale.max,ymin:yScale.min,ymax:yScale.max};if(!_scaleRanges)return meta._scaleRanges=newRanges,!0;const changed=_scaleRanges.xmin!==xScale.min||_scaleRanges.xmax!==xScale.max||_scaleRanges.ymin!==yScale.min||_scaleRanges.ymax!==yScale.max;return Object.assign(_scaleRanges,newRanges),changed}(meta)&&(start=0,count=points.length),line._chart=this.chart,line._datasetIndex=this.index,line._decimated=!!_dataset._decimated,line.points=points;const options=this.resolveDatasetElementOptions(mode);this.options.showLine||(options.borderWidth=0),options.segment=this.options.segment,this.updateElement(line,void 0,{animated:!animationsDisabled,options:options},mode),this.updateElements(points,start,count,mode)}updateElements(points,start,count,mode){const reset="reset"===mode,{iScale:iScale,vScale:vScale,_stacked:_stacked,_dataset:_dataset}=this._cachedMeta,firstOpts=this.resolveDataElementOptions(start,mode),sharedOptions=this.getSharedOptions(firstOpts),includeOptions=this.includeOptions(mode,sharedOptions),iAxis=iScale.axis,vAxis=vScale.axis,{spanGaps:spanGaps,segment:segment}=this.options,maxGapLength=isNumber(spanGaps)?spanGaps:Number.POSITIVE_INFINITY,directUpdate=this.chart._animationsDisabled||reset||"none"===mode;let prevParsed=start>0&&this.getParsed(start-1);for(let i=start;i0&&Math.abs(parsed[iAxis]-prevParsed[iAxis])>maxGapLength,segment&&(properties.parsed=parsed,properties.raw=_dataset.data[i]),includeOptions&&(properties.options=sharedOptions||this.resolveDataElementOptions(i,point.active?"active":mode)),directUpdate||this.updateElement(point,i,properties,mode),prevParsed=parsed}this.updateSharedOptions(sharedOptions,mode,firstOpts)}getMaxOverflow(){const meta=this._cachedMeta,dataset=meta.dataset,border=dataset.options&&dataset.options.borderWidth||0,data=meta.data||[];if(!data.length)return border;const firstPoint=data[0].size(this.resolveDataElementOptions(0)),lastPoint=data[data.length-1].size(this.resolveDataElementOptions(data.length-1));return Math.max(border,firstPoint,lastPoint)/2}draw(){const meta=this._cachedMeta;meta.dataset.updateControlPoints(this.chart.chartArea,meta.iScale.axis),super.draw()}}LineController.id="line",LineController.defaults={datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1},LineController.overrides={scales:{_index_:{type:"category"},_value_:{type:"linear"}}};class PolarAreaController extends DatasetController{constructor(chart,datasetIndex){super(chart,datasetIndex),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(index){const meta=this._cachedMeta,chart=this.chart,labels=chart.data.labels||[],value=formatNumber(meta._parsed[index].r,chart.options.locale);return{label:labels[index]||"",value:value}}parseObjectData(meta,data,start,count){return _parseObjectDataRadialScale.bind(this)(meta,data,start,count)}update(mode){const arcs=this._cachedMeta.data;this._updateRadius(),this.updateElements(arcs,0,arcs.length,mode)}getMinMax(){const meta=this._cachedMeta,range={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return meta.data.forEach(((element,index)=>{const parsed=this.getParsed(index).r;!isNaN(parsed)&&this.chart.getDataVisibility(index)&&(parsedrange.max&&(range.max=parsed))})),range}_updateRadius(){const chart=this.chart,chartArea=chart.chartArea,opts=chart.options,minSize=Math.min(chartArea.right-chartArea.left,chartArea.bottom-chartArea.top),outerRadius=Math.max(minSize/2,0),radiusLength=(outerRadius-Math.max(opts.cutoutPercentage?outerRadius/100*opts.cutoutPercentage:1,0))/chart.getVisibleDatasetCount();this.outerRadius=outerRadius-radiusLength*this.index,this.innerRadius=this.outerRadius-radiusLength}updateElements(arcs,start,count,mode){const reset="reset"===mode,chart=this.chart,animationOpts=chart.options.animation,scale=this._cachedMeta.rScale,centerX=scale.xCenter,centerY=scale.yCenter,datasetStartAngle=scale.getIndexAngle(0)-.5*PI;let i,angle=datasetStartAngle;const defaultAngle=360/this.countVisibleElements();for(i=0;i{!isNaN(this.getParsed(index).r)&&this.chart.getDataVisibility(index)&&count++})),count}_computeAngle(index,mode,defaultAngle){return this.chart.getDataVisibility(index)?toRadians(this.resolveDataElementOptions(index,mode).angle||defaultAngle):0}}PolarAreaController.id="polarArea",PolarAreaController.defaults={dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0},PolarAreaController.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(chart){const data=chart.data;if(data.labels.length&&data.datasets.length){const{labels:{pointStyle:pointStyle}}=chart.legend.options;return data.labels.map(((label,i)=>{const style=chart.getDatasetMeta(0).controller.getStyle(i);return{text:label,fillStyle:style.backgroundColor,strokeStyle:style.borderColor,lineWidth:style.borderWidth,pointStyle:pointStyle,hidden:!chart.getDataVisibility(i),index:i}}))}return[]}},onClick(e,legendItem,legend){legend.chart.toggleDataVisibility(legendItem.index),legend.chart.update()}},tooltip:{callbacks:{title:()=>"",label:context=>context.chart.data.labels[context.dataIndex]+": "+context.formattedValue}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};class PieController extends DoughnutController{}PieController.id="pie",PieController.defaults={cutout:0,rotation:0,circumference:360,radius:"100%"};class RadarController extends DatasetController{getLabelAndValue(index){const vScale=this._cachedMeta.vScale,parsed=this.getParsed(index);return{label:vScale.getLabels()[index],value:""+vScale.getLabelForValue(parsed[vScale.axis])}}parseObjectData(meta,data,start,count){return _parseObjectDataRadialScale.bind(this)(meta,data,start,count)}update(mode){const meta=this._cachedMeta,line=meta.dataset,points=meta.data||[],labels=meta.iScale.getLabels();if(line.points=points,"resize"!==mode){const options=this.resolveDatasetElementOptions(mode);this.options.showLine||(options.borderWidth=0);const properties={_loop:!0,_fullLoop:labels.length===points.length,options:options};this.updateElement(line,void 0,properties,mode)}this.updateElements(points,0,points.length,mode)}updateElements(points,start,count,mode){const scale=this._cachedMeta.rScale,reset="reset"===mode;for(let i=start;i"",label:item=>"("+item.label+", "+item.formattedValue+")"}}},scales:{x:{type:"linear"},y:{type:"linear"}}};var controllers=Object.freeze({__proto__:null,BarController:BarController,BubbleController:BubbleController,DoughnutController:DoughnutController,LineController:LineController,PolarAreaController:PolarAreaController,PieController:PieController,RadarController:RadarController,ScatterController:ScatterController});function clipArc(ctx,element,endAngle){const{startAngle:startAngle,pixelMargin:pixelMargin,x:x,y:y,outerRadius:outerRadius,innerRadius:innerRadius}=element;let angleMargin=pixelMargin/outerRadius;ctx.beginPath(),ctx.arc(x,y,outerRadius,startAngle-angleMargin,endAngle+angleMargin),innerRadius>pixelMargin?(angleMargin=pixelMargin/innerRadius,ctx.arc(x,y,innerRadius,endAngle+angleMargin,startAngle-angleMargin,!0)):ctx.arc(x,y,pixelMargin,endAngle+HALF_PI,startAngle-HALF_PI),ctx.closePath(),ctx.clip()}function parseBorderRadius$1(arc,innerRadius,outerRadius,angleDelta){const o=_readValueToProps(arc.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]),halfThickness=(outerRadius-innerRadius)/2,innerLimit=Math.min(halfThickness,angleDelta*innerRadius/2),computeOuterLimit=val=>{const outerArcLimit=(outerRadius-Math.min(halfThickness,val))*angleDelta/2;return _limitValue(val,0,Math.min(halfThickness,outerArcLimit))};return{outerStart:computeOuterLimit(o.outerStart),outerEnd:computeOuterLimit(o.outerEnd),innerStart:_limitValue(o.innerStart,0,innerLimit),innerEnd:_limitValue(o.innerEnd,0,innerLimit)}}function rThetaToXY(r,theta,x,y){return{x:x+r*Math.cos(theta),y:y+r*Math.sin(theta)}}function pathArc(ctx,element,offset,spacing,end){const{x:x,y:y,startAngle:start,pixelMargin:pixelMargin,innerRadius:innerR}=element,outerRadius=Math.max(element.outerRadius+spacing+offset-pixelMargin,0),innerRadius=innerR>0?innerR+spacing+offset+pixelMargin:0;let spacingOffset=0;const alpha=end-start;if(spacing){const avNogSpacingRadius=((innerR>0?innerR-spacing:0)+(outerRadius>0?outerRadius-spacing:0))/2;spacingOffset=(alpha-(0!==avNogSpacingRadius?alpha*avNogSpacingRadius/(avNogSpacingRadius+spacing):alpha))/2}const angleOffset=(alpha-Math.max(.001,alpha*outerRadius-offset/PI)/outerRadius)/2,startAngle=start+angleOffset+spacingOffset,endAngle=end-angleOffset-spacingOffset,{outerStart:outerStart,outerEnd:outerEnd,innerStart:innerStart,innerEnd:innerEnd}=parseBorderRadius$1(element,innerRadius,outerRadius,endAngle-startAngle),outerStartAdjustedRadius=outerRadius-outerStart,outerEndAdjustedRadius=outerRadius-outerEnd,outerStartAdjustedAngle=startAngle+outerStart/outerStartAdjustedRadius,outerEndAdjustedAngle=endAngle-outerEnd/outerEndAdjustedRadius,innerStartAdjustedRadius=innerRadius+innerStart,innerEndAdjustedRadius=innerRadius+innerEnd,innerStartAdjustedAngle=startAngle+innerStart/innerStartAdjustedRadius,innerEndAdjustedAngle=endAngle-innerEnd/innerEndAdjustedRadius;if(ctx.beginPath(),ctx.arc(x,y,outerRadius,outerStartAdjustedAngle,outerEndAdjustedAngle),outerEnd>0){const pCenter=rThetaToXY(outerEndAdjustedRadius,outerEndAdjustedAngle,x,y);ctx.arc(pCenter.x,pCenter.y,outerEnd,outerEndAdjustedAngle,endAngle+HALF_PI)}const p4=rThetaToXY(innerEndAdjustedRadius,endAngle,x,y);if(ctx.lineTo(p4.x,p4.y),innerEnd>0){const pCenter=rThetaToXY(innerEndAdjustedRadius,innerEndAdjustedAngle,x,y);ctx.arc(pCenter.x,pCenter.y,innerEnd,endAngle+HALF_PI,innerEndAdjustedAngle+Math.PI)}if(ctx.arc(x,y,innerRadius,endAngle-innerEnd/innerRadius,startAngle+innerStart/innerRadius,!0),innerStart>0){const pCenter=rThetaToXY(innerStartAdjustedRadius,innerStartAdjustedAngle,x,y);ctx.arc(pCenter.x,pCenter.y,innerStart,innerStartAdjustedAngle+Math.PI,startAngle-HALF_PI)}const p8=rThetaToXY(outerStartAdjustedRadius,startAngle,x,y);if(ctx.lineTo(p8.x,p8.y),outerStart>0){const pCenter=rThetaToXY(outerStartAdjustedRadius,outerStartAdjustedAngle,x,y);ctx.arc(pCenter.x,pCenter.y,outerStart,startAngle-HALF_PI,outerStartAdjustedAngle)}ctx.closePath()}function drawBorder(ctx,element,offset,spacing,endAngle){const{options:options}=element,{borderWidth:borderWidth,borderJoinStyle:borderJoinStyle}=options,inner="inner"===options.borderAlign;borderWidth&&(inner?(ctx.lineWidth=2*borderWidth,ctx.lineJoin=borderJoinStyle||"round"):(ctx.lineWidth=borderWidth,ctx.lineJoin=borderJoinStyle||"bevel"),element.fullCircles&&function(ctx,element,inner){const{x:x,y:y,startAngle:startAngle,pixelMargin:pixelMargin,fullCircles:fullCircles}=element,outerRadius=Math.max(element.outerRadius-pixelMargin,0),innerRadius=element.innerRadius+pixelMargin;let i;for(inner&&clipArc(ctx,element,startAngle+TAU),ctx.beginPath(),ctx.arc(x,y,innerRadius,startAngle+TAU,startAngle,!0),i=0;i=TAU||_angleBetween(angle,startAngle,endAngle),withinRadius=_isBetween(distance,innerRadius+rAdjust,outerRadius+rAdjust);return betweenAngles&&withinRadius}getCenterPoint(useFinalPosition){const{x:x,y:y,startAngle:startAngle,endAngle:endAngle,innerRadius:innerRadius,outerRadius:outerRadius}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius","circumference"],useFinalPosition),{offset:offset,spacing:spacing}=this.options,halfAngle=(startAngle+endAngle)/2,halfRadius=(innerRadius+outerRadius+spacing+offset)/2;return{x:x+Math.cos(halfAngle)*halfRadius,y:y+Math.sin(halfAngle)*halfRadius}}tooltipPosition(useFinalPosition){return this.getCenterPoint(useFinalPosition)}draw(ctx){const{options:options,circumference:circumference}=this,offset=(options.offset||0)/2,spacing=(options.spacing||0)/2;if(this.pixelMargin="inner"===options.borderAlign?.33:0,this.fullCircles=circumference>TAU?Math.floor(circumference/TAU):0,0===circumference||this.innerRadius<0||this.outerRadius<0)return;ctx.save();let radiusOffset=0;if(offset){radiusOffset=offset/2;const halfAngle=(this.startAngle+this.endAngle)/2;ctx.translate(Math.cos(halfAngle)*radiusOffset,Math.sin(halfAngle)*radiusOffset),this.circumference>=PI&&(radiusOffset=offset)}ctx.fillStyle=options.backgroundColor,ctx.strokeStyle=options.borderColor;const endAngle=function(ctx,element,offset,spacing){const{fullCircles:fullCircles,startAngle:startAngle,circumference:circumference}=element;let endAngle=element.endAngle;if(fullCircles){pathArc(ctx,element,offset,spacing,startAngle+TAU);for(let i=0;i2&&void 0!==arguments[2]?arguments[2]:options;ctx.lineCap=valueOrDefault(style.borderCapStyle,options.borderCapStyle),ctx.setLineDash(valueOrDefault(style.borderDash,options.borderDash)),ctx.lineDashOffset=valueOrDefault(style.borderDashOffset,options.borderDashOffset),ctx.lineJoin=valueOrDefault(style.borderJoinStyle,options.borderJoinStyle),ctx.lineWidth=valueOrDefault(style.borderWidth,options.borderWidth),ctx.strokeStyle=valueOrDefault(style.borderColor,options.borderColor)}function lineTo(ctx,previous,target){ctx.lineTo(target.x,target.y)}function pathVars(points,segment){let params=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const count=points.length,{start:paramsStart=0,end:paramsEnd=count-1}=params,{start:segmentStart,end:segmentEnd}=segment,start=Math.max(paramsStart,segmentStart),end=Math.min(paramsEnd,segmentEnd),outside=paramsStartsegmentEnd&¶msEnd>segmentEnd;return{count:count,start:start,loop:segment.loop,ilen:end(start+(reverse?ilen-index:index))%count,drawX=()=>{minY!==maxY&&(ctx.lineTo(avgX,maxY),ctx.lineTo(avgX,minY),ctx.lineTo(avgX,lastY))};for(move&&(point=points[pointIndex(0)],ctx.moveTo(point.x,point.y)),i=0;i<=ilen;++i){if(point=points[pointIndex(i)],point.skip)continue;const x=point.x,y=point.y,truncX=0|x;truncX===prevX?(ymaxY&&(maxY=y),avgX=(countX*avgX+x)/++countX):(drawX(),ctx.lineTo(x,y),prevX=truncX,countX=0,minY=maxY=y),lastY=y}drawX()}function _getSegmentMethod(line){const opts=line.options,borderDash=opts.borderDash&&opts.borderDash.length;return line._decimated||line._loop||opts.tension||"monotone"===opts.cubicInterpolationMode||opts.stepped||borderDash?pathSegment:fastPathSegment}ArcElement.id="arc",ArcElement.defaults={borderAlign:"center",borderColor:"#fff",borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0},ArcElement.defaultRoutes={backgroundColor:"backgroundColor"};const usePath2D="function"==typeof Path2D;function draw(ctx,line,start,count){usePath2D&&!line.options.segment?function(ctx,line,start,count){let path=line._path;path||(path=line._path=new Path2D,line.path(path,start,count)&&path.closePath()),setStyle(ctx,line.options),ctx.stroke(path)}(ctx,line,start,count):function(ctx,line,start,count){const{segments:segments,options:options}=line,segmentMethod=_getSegmentMethod(line);for(const segment of segments)setStyle(ctx,options,segment.style),ctx.beginPath(),segmentMethod(ctx,line,segment,{start:start,end:start+count-1})&&ctx.closePath(),ctx.stroke()}(ctx,line,start,count)}class LineElement extends Element{constructor(cfg){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,cfg&&Object.assign(this,cfg)}updateControlPoints(chartArea,indexAxis){const options=this.options;if((options.tension||"monotone"===options.cubicInterpolationMode)&&!options.stepped&&!this._pointsUpdated){const loop=options.spanGaps?this._loop:this._fullLoop;_updateBezierControlPoints(this._points,options,chartArea,loop,indexAxis),this._pointsUpdated=!0}}set points(points){this._points=points,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=_computeSegments(this,this.options.segment))}first(){const segments=this.segments,points=this.points;return segments.length&&points[segments[0].start]}last(){const segments=this.segments,points=this.points,count=segments.length;return count&&points[segments[count-1].end]}interpolate(point,property){const options=this.options,value=point[property],points=this.points,segments=_boundSegments(this,{property:property,start:value,end:value});if(!segments.length)return;const result=[],_interpolate=function(options){return options.stepped?_steppedInterpolation:options.tension||"monotone"===options.cubicInterpolationMode?_bezierInterpolation:_pointInLine}(options);let i,ilen;for(i=0,ilen=segments.length;i"borderDash"!==name&&"fill"!==name};class PointElement extends Element{constructor(cfg){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,cfg&&Object.assign(this,cfg)}inRange(mouseX,mouseY,useFinalPosition){const options=this.options,{x:x,y:y}=this.getProps(["x","y"],useFinalPosition);return Math.pow(mouseX-x,2)+Math.pow(mouseY-y,2)2&&void 0!==arguments[2]?arguments[2]:{};const x=rect.x!==refRect.x?-amount:0,y=rect.y!==refRect.y?-amount:0,w=(rect.x+rect.w!==refRect.x+refRect.w?amount:0)-x,h=(rect.y+rect.h!==refRect.y+refRect.h?amount:0)-y;return{x:rect.x+x,y:rect.y+y,w:rect.w+w,h:rect.h+h,radius:rect.radius}}PointElement.id="point",PointElement.defaults={borderWidth:1,hitRadius:1,hoverBorderWidth:1,hoverRadius:4,pointStyle:"circle",radius:3,rotation:0},PointElement.defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};class BarElement extends Element{constructor(cfg){super(),this.options=void 0,this.horizontal=void 0,this.base=void 0,this.width=void 0,this.height=void 0,this.inflateAmount=void 0,cfg&&Object.assign(this,cfg)}draw(ctx){const{inflateAmount:inflateAmount,options:{borderColor:borderColor,backgroundColor:backgroundColor}}=this,{inner:inner,outer:outer}=boundingRects(this),addRectPath=(radius=outer.radius).topLeft||radius.topRight||radius.bottomLeft||radius.bottomRight?addRoundedRectPath:addNormalRectPath;var radius;ctx.save(),outer.w===inner.w&&outer.h===inner.h||(ctx.beginPath(),addRectPath(ctx,inflateRect(outer,inflateAmount,inner)),ctx.clip(),addRectPath(ctx,inflateRect(inner,-inflateAmount,outer)),ctx.fillStyle=borderColor,ctx.fill("evenodd")),ctx.beginPath(),addRectPath(ctx,inflateRect(inner,inflateAmount)),ctx.fillStyle=backgroundColor,ctx.fill(),ctx.restore()}inRange(mouseX,mouseY,useFinalPosition){return inRange(this,mouseX,mouseY,useFinalPosition)}inXRange(mouseX,useFinalPosition){return inRange(this,mouseX,null,useFinalPosition)}inYRange(mouseY,useFinalPosition){return inRange(this,null,mouseY,useFinalPosition)}getCenterPoint(useFinalPosition){const{x:x,y:y,base:base,horizontal:horizontal}=this.getProps(["x","y","base","horizontal"],useFinalPosition);return{x:horizontal?(x+base)/2:x,y:horizontal?y:(y+base)/2}}getRange(axis){return"x"===axis?this.width/2:this.height/2}}BarElement.id="bar",BarElement.defaults={borderSkipped:"start",borderWidth:0,borderRadius:0,inflateAmount:"auto",pointStyle:void 0},BarElement.defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};var elements=Object.freeze({__proto__:null,ArcElement:ArcElement,LineElement:LineElement,PointElement:PointElement,BarElement:BarElement});function cleanDecimatedDataset(dataset){if(dataset._decimated){const data=dataset._data;delete dataset._decimated,delete dataset._data,Object.defineProperty(dataset,"data",{value:data})}}function cleanDecimatedData(chart){chart.data.datasets.forEach((dataset=>{cleanDecimatedDataset(dataset)}))}var plugin_decimation={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(chart,args,options)=>{if(!options.enabled)return void cleanDecimatedData(chart);const availableWidth=chart.width;chart.data.datasets.forEach(((dataset,datasetIndex)=>{const{_data:_data,indexAxis:indexAxis}=dataset,meta=chart.getDatasetMeta(datasetIndex),data=_data||dataset.data;if("y"===resolve([indexAxis,chart.options.indexAxis]))return;if(!meta.controller.supportsDecimation)return;const xAxis=chart.scales[meta.xAxisID];if("linear"!==xAxis.type&&"time"!==xAxis.type)return;if(chart.options.parsing)return;let decimated,{start:start,count:count}=function(meta,points){const pointCount=points.length;let count,start=0;const{iScale:iScale}=meta,{min:min,max:max,minDefined:minDefined,maxDefined:maxDefined}=iScale.getUserBounds();return minDefined&&(start=_limitValue(_lookupByKey(points,iScale.axis,min).lo,0,pointCount-1)),count=maxDefined?_limitValue(_lookupByKey(points,iScale.axis,max).hi+1,start,pointCount)-start:pointCount-start,{start:start,count:count}}(meta,data);if(count<=(options.threshold||4*availableWidth))cleanDecimatedDataset(dataset);else{switch(isNullOrUndef(_data)&&(dataset._data=data,delete dataset.data,Object.defineProperty(dataset,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(d){this._data=d}})),options.algorithm){case"lttb":decimated=function(data,start,count,availableWidth,options){const samples=options.samples||availableWidth;if(samples>=count)return data.slice(start,start+count);const decimated=[],bucketWidth=(count-2)/(samples-2);let sampledIndex=0;const endIndex=start+count-1;let i,maxAreaPoint,maxArea,area,nextA,a=start;for(decimated[sampledIndex++]=data[a],i=0;imaxArea&&(maxArea=area,maxAreaPoint=data[j],nextA=j);decimated[sampledIndex++]=maxAreaPoint,a=nextA}return decimated[sampledIndex++]=data[endIndex],decimated}(data,start,count,availableWidth,options);break;case"min-max":decimated=function(data,start,count,availableWidth){let i,point,x,y,prevX,minIndex,maxIndex,startIndex,minY,maxY,avgX=0,countX=0;const decimated=[],endIndex=start+count-1,xMin=data[start].x,dx=data[endIndex].x-xMin;for(i=start;imaxY&&(maxY=y,maxIndex=i),avgX=(countX*avgX+point.x)/++countX;else{const lastIndex=i-1;if(!isNullOrUndef(minIndex)&&!isNullOrUndef(maxIndex)){const intermediateIndex1=Math.min(minIndex,maxIndex),intermediateIndex2=Math.max(minIndex,maxIndex);intermediateIndex1!==startIndex&&intermediateIndex1!==lastIndex&&decimated.push({...data[intermediateIndex1],x:avgX}),intermediateIndex2!==startIndex&&intermediateIndex2!==lastIndex&&decimated.push({...data[intermediateIndex2],x:avgX})}i>0&&lastIndex!==startIndex&&decimated.push(data[lastIndex]),decimated.push(point),prevX=truncX,countX=0,minY=maxY=y,minIndex=maxIndex=startIndex=i}}return decimated}(data,start,count,availableWidth);break;default:throw new Error("Unsupported decimation algorithm '".concat(options.algorithm,"'"))}dataset._decimated=decimated}}))},destroy(chart){cleanDecimatedData(chart)}};function _getBounds(property,first,last,loop){if(loop)return;let start=first[property],end=last[property];return"angle"===property&&(start=_normalizeAngle(start),end=_normalizeAngle(end)),{property:property,start:start,end:end}}function _findSegmentEnd(start,end,points){for(;end>start;end--){const point=points[end];if(!isNaN(point.x)&&!isNaN(point.y))break}return end}function _getEdge(a,b,prop,fn){return a&&b?fn(a[prop],b[prop]):a?a[prop]:b?b[prop]:0}function _createBoundaryLine(boundary,line){let points=[],_loop=!1;return isArray(boundary)?(_loop=!0,points=boundary):points=function(boundary,line){const{x:x=null,y:y=null}=boundary||{},linePoints=line.points,points=[];return line.segments.forEach((_ref4=>{let{start:start,end:end}=_ref4;end=_findSegmentEnd(start,end,linePoints);const first=linePoints[start],last=linePoints[end];null!==y?(points.push({x:first.x,y:y}),points.push({x:last.x,y:y})):null!==x&&(points.push({x:x,y:first.y}),points.push({x:x,y:last.y}))})),points}(boundary,line),points.length?new LineElement({points:points,options:{tension:0},_loop:_loop,_fullLoop:_loop}):null}function _resolveTarget(sources,index,propagate){let fill=sources[index].fill;const visited=[index];let target;if(!propagate)return fill;for(;!1!==fill&&-1===visited.indexOf(fill);){if(!isNumberFinite(fill))return fill;if(target=sources[fill],!target)return!1;if(target.visible)return fill;visited.push(fill),fill=target.fill}return!1}function _decodeFill(line,index,count){const fill=function(line){const options=line.options,fillOption=options.fill;let fill=valueOrDefault(fillOption&&fillOption.target,fillOption);return void 0===fill&&(fill=!!options.backgroundColor),!1!==fill&&null!==fill&&(!0===fill?"origin":fill)}(line);if(isObject(fill))return!isNaN(fill.value)&&fill;let target=parseFloat(fill);return isNumberFinite(target)&&Math.floor(target)===target?function(firstCh,index,target,count){return"-"!==firstCh&&"+"!==firstCh||(target=index+target),!(target===index||target<0||target>=count)&&target}(fill[0],index,target,count):["origin","start","end","stack","shape"].indexOf(fill)>=0&&fill}function addPointsBelow(points,sourcePoint,linesBelow){const postponed=[];for(let j=0;j=0;--i){const source=metasets[i].$filler;source&&(source.line.updateControlPoints(area,source.axis),draw&&_drawfill(chart.ctx,source,area))}},beforeDatasetsDraw(chart,_args,options){if("beforeDatasetsDraw"!==options.drawTime)return;const metasets=chart.getSortedVisibleDatasetMetas();for(let i=metasets.length-1;i>=0;--i){const source=metasets[i].$filler;source&&_drawfill(chart.ctx,source,chart.chartArea)}},beforeDatasetDraw(chart,args,options){const source=args.meta.$filler;source&&!1!==source.fill&&"beforeDatasetDraw"===options.drawTime&&_drawfill(chart.ctx,source,chart.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const getBoxSize=(labelOpts,fontSize)=>{let{boxHeight:boxHeight=fontSize,boxWidth:boxWidth=fontSize}=labelOpts;return labelOpts.usePointStyle&&(boxHeight=Math.min(boxHeight,fontSize),boxWidth=Math.min(boxWidth,fontSize)),{boxWidth:boxWidth,boxHeight:boxHeight,itemHeight:Math.max(fontSize,boxHeight)}};class Legend extends Element{constructor(config){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=config.chart,this.options=config.options,this.ctx=config.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(maxWidth,maxHeight,margins){this.maxWidth=maxWidth,this.maxHeight=maxHeight,this._margins=margins,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const labelOpts=this.options.labels||{};let legendItems=callback(labelOpts.generateLabels,[this.chart],this)||[];labelOpts.filter&&(legendItems=legendItems.filter((item=>labelOpts.filter(item,this.chart.data)))),labelOpts.sort&&(legendItems=legendItems.sort(((a,b)=>labelOpts.sort(a,b,this.chart.data)))),this.options.reverse&&legendItems.reverse(),this.legendItems=legendItems}fit(){const{options:options,ctx:ctx}=this;if(!options.display)return void(this.width=this.height=0);const labelOpts=options.labels,labelFont=toFont(labelOpts.font),fontSize=labelFont.size,titleHeight=this._computeTitleHeight(),{boxWidth:boxWidth,itemHeight:itemHeight}=getBoxSize(labelOpts,fontSize);let width,height;ctx.font=labelFont.string,this.isHorizontal()?(width=this.maxWidth,height=this._fitRows(titleHeight,fontSize,boxWidth,itemHeight)+10):(height=this.maxHeight,width=this._fitCols(titleHeight,fontSize,boxWidth,itemHeight)+10),this.width=Math.min(width,options.maxWidth||this.maxWidth),this.height=Math.min(height,options.maxHeight||this.maxHeight)}_fitRows(titleHeight,fontSize,boxWidth,itemHeight){const{ctx:ctx,maxWidth:maxWidth,options:{labels:{padding:padding}}}=this,hitboxes=this.legendHitBoxes=[],lineWidths=this.lineWidths=[0],lineHeight=itemHeight+padding;let totalHeight=titleHeight;ctx.textAlign="left",ctx.textBaseline="middle";let row=-1,top=-lineHeight;return this.legendItems.forEach(((legendItem,i)=>{const itemWidth=boxWidth+fontSize/2+ctx.measureText(legendItem.text).width;(0===i||lineWidths[lineWidths.length-1]+itemWidth+2*padding>maxWidth)&&(totalHeight+=lineHeight,lineWidths[lineWidths.length-(i>0?0:1)]=0,top+=lineHeight,row++),hitboxes[i]={left:0,top:top,row:row,width:itemWidth,height:itemHeight},lineWidths[lineWidths.length-1]+=itemWidth+padding})),totalHeight}_fitCols(titleHeight,fontSize,boxWidth,itemHeight){const{ctx:ctx,maxHeight:maxHeight,options:{labels:{padding:padding}}}=this,hitboxes=this.legendHitBoxes=[],columnSizes=this.columnSizes=[],heightLimit=maxHeight-titleHeight;let totalWidth=padding,currentColWidth=0,currentColHeight=0,left=0,col=0;return this.legendItems.forEach(((legendItem,i)=>{const itemWidth=boxWidth+fontSize/2+ctx.measureText(legendItem.text).width;i>0&¤tColHeight+itemHeight+2*padding>heightLimit&&(totalWidth+=currentColWidth+padding,columnSizes.push({width:currentColWidth,height:currentColHeight}),left+=currentColWidth+padding,col++,currentColWidth=currentColHeight=0),hitboxes[i]={left:left,top:currentColHeight,col:col,width:itemWidth,height:itemHeight},currentColWidth=Math.max(currentColWidth,itemWidth),currentColHeight+=itemHeight+padding})),totalWidth+=currentColWidth,columnSizes.push({width:currentColWidth,height:currentColHeight}),totalWidth}adjustHitBoxes(){if(!this.options.display)return;const titleHeight=this._computeTitleHeight(),{legendHitBoxes:hitboxes,options:{align:align,labels:{padding:padding},rtl:rtl}}=this,rtlHelper=getRtlAdapter(rtl,this.left,this.width);if(this.isHorizontal()){let row=0,left=_alignStartEnd(align,this.left+padding,this.right-this.lineWidths[row]);for(const hitbox of hitboxes)row!==hitbox.row&&(row=hitbox.row,left=_alignStartEnd(align,this.left+padding,this.right-this.lineWidths[row])),hitbox.top+=this.top+titleHeight+padding,hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(left),hitbox.width),left+=hitbox.width+padding}else{let col=0,top=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-this.columnSizes[col].height);for(const hitbox of hitboxes)hitbox.col!==col&&(col=hitbox.col,top=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-this.columnSizes[col].height)),hitbox.top=top,hitbox.left+=this.left+padding,hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(hitbox.left),hitbox.width),top+=hitbox.height+padding}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const ctx=this.ctx;clipArea(ctx,this),this._draw(),unclipArea(ctx)}}_draw(){const{options:opts,columnSizes:columnSizes,lineWidths:lineWidths,ctx:ctx}=this,{align:align,labels:labelOpts}=opts,defaultColor=defaults.color,rtlHelper=getRtlAdapter(opts.rtl,this.left,this.width),labelFont=toFont(labelOpts.font),{color:fontColor,padding:padding}=labelOpts,fontSize=labelFont.size,halfFontSize=fontSize/2;let cursor;this.drawTitle(),ctx.textAlign=rtlHelper.textAlign("left"),ctx.textBaseline="middle",ctx.lineWidth=.5,ctx.font=labelFont.string;const{boxWidth:boxWidth,boxHeight:boxHeight,itemHeight:itemHeight}=getBoxSize(labelOpts,fontSize),isHorizontal=this.isHorizontal(),titleHeight=this._computeTitleHeight();cursor=isHorizontal?{x:_alignStartEnd(align,this.left+padding,this.right-lineWidths[0]),y:this.top+padding+titleHeight,line:0}:{x:this.left+padding,y:_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-columnSizes[0].height),line:0},overrideTextDirection(this.ctx,opts.textDirection);const lineHeight=itemHeight+padding;this.legendItems.forEach(((legendItem,i)=>{ctx.strokeStyle=legendItem.fontColor||fontColor,ctx.fillStyle=legendItem.fontColor||fontColor;const textWidth=ctx.measureText(legendItem.text).width,textAlign=rtlHelper.textAlign(legendItem.textAlign||(legendItem.textAlign=labelOpts.textAlign)),width=boxWidth+halfFontSize+textWidth;let x=cursor.x,y=cursor.y;rtlHelper.setWidth(this.width),isHorizontal?i>0&&x+width+padding>this.right&&(y=cursor.y+=lineHeight,cursor.line++,x=cursor.x=_alignStartEnd(align,this.left+padding,this.right-lineWidths[cursor.line])):i>0&&y+lineHeight>this.bottom&&(x=cursor.x=x+columnSizes[cursor.line].width+padding,cursor.line++,y=cursor.y=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-columnSizes[cursor.line].height)),function(x,y,legendItem){if(isNaN(boxWidth)||boxWidth<=0||isNaN(boxHeight)||boxHeight<0)return;ctx.save();const lineWidth=valueOrDefault(legendItem.lineWidth,1);if(ctx.fillStyle=valueOrDefault(legendItem.fillStyle,defaultColor),ctx.lineCap=valueOrDefault(legendItem.lineCap,"butt"),ctx.lineDashOffset=valueOrDefault(legendItem.lineDashOffset,0),ctx.lineJoin=valueOrDefault(legendItem.lineJoin,"miter"),ctx.lineWidth=lineWidth,ctx.strokeStyle=valueOrDefault(legendItem.strokeStyle,defaultColor),ctx.setLineDash(valueOrDefault(legendItem.lineDash,[])),labelOpts.usePointStyle){const drawOptions={radius:boxWidth*Math.SQRT2/2,pointStyle:legendItem.pointStyle,rotation:legendItem.rotation,borderWidth:lineWidth},centerX=rtlHelper.xPlus(x,boxWidth/2);drawPoint(ctx,drawOptions,centerX,y+halfFontSize)}else{const yBoxTop=y+Math.max((fontSize-boxHeight)/2,0),xBoxLeft=rtlHelper.leftForLtr(x,boxWidth),borderRadius=toTRBLCorners(legendItem.borderRadius);ctx.beginPath(),Object.values(borderRadius).some((v=>0!==v))?addRoundedRectPath(ctx,{x:xBoxLeft,y:yBoxTop,w:boxWidth,h:boxHeight,radius:borderRadius}):ctx.rect(xBoxLeft,yBoxTop,boxWidth,boxHeight),ctx.fill(),0!==lineWidth&&ctx.stroke()}ctx.restore()}(rtlHelper.x(x),y,legendItem),x=_textX(textAlign,x+boxWidth+halfFontSize,isHorizontal?x+width:this.right,opts.rtl),function(x,y,legendItem){renderText(ctx,legendItem.text,x,y+itemHeight/2,labelFont,{strikethrough:legendItem.hidden,textAlign:rtlHelper.textAlign(legendItem.textAlign)})}(rtlHelper.x(x),y,legendItem),isHorizontal?cursor.x+=width+padding:cursor.y+=lineHeight})),restoreTextDirection(this.ctx,opts.textDirection)}drawTitle(){const opts=this.options,titleOpts=opts.title,titleFont=toFont(titleOpts.font),titlePadding=toPadding(titleOpts.padding);if(!titleOpts.display)return;const rtlHelper=getRtlAdapter(opts.rtl,this.left,this.width),ctx=this.ctx,position=titleOpts.position,halfFontSize=titleFont.size/2,topPaddingPlusHalfFontSize=titlePadding.top+halfFontSize;let y,left=this.left,maxWidth=this.width;if(this.isHorizontal())maxWidth=Math.max(...this.lineWidths),y=this.top+topPaddingPlusHalfFontSize,left=_alignStartEnd(opts.align,left,this.right-maxWidth);else{const maxHeight=this.columnSizes.reduce(((acc,size)=>Math.max(acc,size.height)),0);y=topPaddingPlusHalfFontSize+_alignStartEnd(opts.align,this.top,this.bottom-maxHeight-opts.labels.padding-this._computeTitleHeight())}const x=_alignStartEnd(position,left,left+maxWidth);ctx.textAlign=rtlHelper.textAlign(_toLeftRightCenter(position)),ctx.textBaseline="middle",ctx.strokeStyle=titleOpts.color,ctx.fillStyle=titleOpts.color,ctx.font=titleFont.string,renderText(ctx,titleOpts.text,x,y,titleFont)}_computeTitleHeight(){const titleOpts=this.options.title,titleFont=toFont(titleOpts.font),titlePadding=toPadding(titleOpts.padding);return titleOpts.display?titleFont.lineHeight+titlePadding.height:0}_getLegendItemAt(x,y){let i,hitBox,lh;if(_isBetween(x,this.left,this.right)&&_isBetween(y,this.top,this.bottom))for(lh=this.legendHitBoxes,i=0;ictx.chart.options.color,boxWidth:40,padding:10,generateLabels(chart){const datasets=chart.data.datasets,{labels:{usePointStyle:usePointStyle,pointStyle:pointStyle,textAlign:textAlign,color:color}}=chart.legend.options;return chart._getSortedDatasetMetas().map((meta=>{const style=meta.controller.getStyle(usePointStyle?0:void 0),borderWidth=toPadding(style.borderWidth);return{text:datasets[meta.index].label,fillStyle:style.backgroundColor,fontColor:color,hidden:!meta.visible,lineCap:style.borderCapStyle,lineDash:style.borderDash,lineDashOffset:style.borderDashOffset,lineJoin:style.borderJoinStyle,lineWidth:(borderWidth.width+borderWidth.height)/4,strokeStyle:style.borderColor,pointStyle:pointStyle||style.pointStyle,rotation:style.rotation,textAlign:textAlign||style.textAlign,borderRadius:0,datasetIndex:meta.index}}),this)}},title:{color:ctx=>ctx.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:name=>!name.startsWith("on"),labels:{_scriptable:name=>!["generateLabels","filter","sort"].includes(name)}}};class Title extends Element{constructor(config){super(),this.chart=config.chart,this.options=config.options,this.ctx=config.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(maxWidth,maxHeight){const opts=this.options;if(this.left=0,this.top=0,!opts.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=maxWidth,this.height=this.bottom=maxHeight;const lineCount=isArray(opts.text)?opts.text.length:1;this._padding=toPadding(opts.padding);const textSize=lineCount*toFont(opts.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=textSize:this.width=textSize}isHorizontal(){const pos=this.options.position;return"top"===pos||"bottom"===pos}_drawArgs(offset){const{top:top,left:left,bottom:bottom,right:right,options:options}=this,align=options.align;let maxWidth,titleX,titleY,rotation=0;return this.isHorizontal()?(titleX=_alignStartEnd(align,left,right),titleY=top+offset,maxWidth=right-left):("left"===options.position?(titleX=left+offset,titleY=_alignStartEnd(align,bottom,top),rotation=-.5*PI):(titleX=right-offset,titleY=_alignStartEnd(align,top,bottom),rotation=.5*PI),maxWidth=bottom-top),{titleX:titleX,titleY:titleY,maxWidth:maxWidth,rotation:rotation}}draw(){const ctx=this.ctx,opts=this.options;if(!opts.display)return;const fontOpts=toFont(opts.font),offset=fontOpts.lineHeight/2+this._padding.top,{titleX:titleX,titleY:titleY,maxWidth:maxWidth,rotation:rotation}=this._drawArgs(offset);renderText(ctx,opts.text,0,0,fontOpts,{color:opts.color,maxWidth:maxWidth,rotation:rotation,textAlign:_toLeftRightCenter(opts.align),textBaseline:"middle",translation:[titleX,titleY]})}}var plugin_title={id:"title",_element:Title,start(chart,_args,options){!function(chart,titleOpts){const title=new Title({ctx:chart.ctx,options:titleOpts,chart:chart});layouts.configure(chart,title,titleOpts),layouts.addBox(chart,title),chart.titleBlock=title}(chart,options)},stop(chart){const titleBlock=chart.titleBlock;layouts.removeBox(chart,titleBlock),delete chart.titleBlock},beforeUpdate(chart,_args,options){const title=chart.titleBlock;layouts.configure(chart,title,options),title.options=options},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const map=new WeakMap;var plugin_subtitle={id:"subtitle",start(chart,_args,options){const title=new Title({ctx:chart.ctx,options:options,chart:chart});layouts.configure(chart,title,options),layouts.addBox(chart,title),map.set(chart,title)},stop(chart){layouts.removeBox(chart,map.get(chart)),map.delete(chart)},beforeUpdate(chart,_args,options){const title=map.get(chart);layouts.configure(chart,title,options),title.options=options},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const positioners={average(items){if(!items.length)return!1;let i,len,x=0,y=0,count=0;for(i=0,len=items.length;i-1?str.split("\n"):str}function createTooltipItem(chart,item){const{element:element,datasetIndex:datasetIndex,index:index}=item,controller=chart.getDatasetMeta(datasetIndex).controller,{label:label,value:value}=controller.getLabelAndValue(index);return{chart:chart,label:label,parsed:controller.getParsed(index),raw:chart.data.datasets[datasetIndex].data[index],formattedValue:value,dataset:controller.getDataset(),dataIndex:index,datasetIndex:datasetIndex,element:element}}function getTooltipSize(tooltip,options){const ctx=tooltip.chart.ctx,{body:body,footer:footer,title:title}=tooltip,{boxWidth:boxWidth,boxHeight:boxHeight}=options,bodyFont=toFont(options.bodyFont),titleFont=toFont(options.titleFont),footerFont=toFont(options.footerFont),titleLineCount=title.length,footerLineCount=footer.length,bodyLineItemCount=body.length,padding=toPadding(options.padding);let height=padding.height,width=0,combinedBodyLength=body.reduce(((count,bodyItem)=>count+bodyItem.before.length+bodyItem.lines.length+bodyItem.after.length),0);combinedBodyLength+=tooltip.beforeBody.length+tooltip.afterBody.length,titleLineCount&&(height+=titleLineCount*titleFont.lineHeight+(titleLineCount-1)*options.titleSpacing+options.titleMarginBottom),combinedBodyLength&&(height+=bodyLineItemCount*(options.displayColors?Math.max(boxHeight,bodyFont.lineHeight):bodyFont.lineHeight)+(combinedBodyLength-bodyLineItemCount)*bodyFont.lineHeight+(combinedBodyLength-1)*options.bodySpacing),footerLineCount&&(height+=options.footerMarginTop+footerLineCount*footerFont.lineHeight+(footerLineCount-1)*options.footerSpacing);let widthPadding=0;const maxLineWidth=function(line){width=Math.max(width,ctx.measureText(line).width+widthPadding)};return ctx.save(),ctx.font=titleFont.string,each(tooltip.title,maxLineWidth),ctx.font=bodyFont.string,each(tooltip.beforeBody.concat(tooltip.afterBody),maxLineWidth),widthPadding=options.displayColors?boxWidth+2+options.boxPadding:0,each(body,(bodyItem=>{each(bodyItem.before,maxLineWidth),each(bodyItem.lines,maxLineWidth),each(bodyItem.after,maxLineWidth)})),widthPadding=0,ctx.font=footerFont.string,each(tooltip.footer,maxLineWidth),ctx.restore(),width+=padding.width,{width:width,height:height}}function determineXAlign(chart,options,size,yAlign){const{x:x,width:width}=size,{width:chartWidth,chartArea:{left:left,right:right}}=chart;let xAlign="center";return"center"===yAlign?xAlign=x<=(left+right)/2?"left":"right":x<=width/2?xAlign="left":x>=chartWidth-width/2&&(xAlign="right"),function(xAlign,chart,options,size){const{x:x,width:width}=size,caret=options.caretSize+options.caretPadding;return"left"===xAlign&&x+width+caret>chart.width||"right"===xAlign&&x-width-caret<0||void 0}(xAlign,chart,options,size)&&(xAlign="center"),xAlign}function determineAlignment(chart,options,size){const yAlign=size.yAlign||options.yAlign||function(chart,size){const{y:y,height:height}=size;return ychart.height-height/2?"bottom":"center"}(chart,size);return{xAlign:size.xAlign||options.xAlign||determineXAlign(chart,options,size,yAlign),yAlign:yAlign}}function getBackgroundPoint(options,size,alignment,chart){const{caretSize:caretSize,caretPadding:caretPadding,cornerRadius:cornerRadius}=options,{xAlign:xAlign,yAlign:yAlign}=alignment,paddingAndSize=caretSize+caretPadding,{topLeft:topLeft,topRight:topRight,bottomLeft:bottomLeft,bottomRight:bottomRight}=toTRBLCorners(cornerRadius);let x=function(size,xAlign){let{x:x,width:width}=size;return"right"===xAlign?x-=width:"center"===xAlign&&(x-=width/2),x}(size,xAlign);const y=function(size,yAlign,paddingAndSize){let{y:y,height:height}=size;return"top"===yAlign?y+=paddingAndSize:y-="bottom"===yAlign?height+paddingAndSize:height/2,y}(size,yAlign,paddingAndSize);return"center"===yAlign?"left"===xAlign?x+=paddingAndSize:"right"===xAlign&&(x-=paddingAndSize):"left"===xAlign?x-=Math.max(topLeft,bottomLeft)+caretSize:"right"===xAlign&&(x+=Math.max(topRight,bottomRight)+caretSize),{x:_limitValue(x,0,chart.width-size.width),y:_limitValue(y,0,chart.height-size.height)}}function getAlignedX(tooltip,align,options){const padding=toPadding(options.padding);return"center"===align?tooltip.x+tooltip.width/2:"right"===align?tooltip.x+tooltip.width-padding.right:tooltip.x+padding.left}function getBeforeAfterBodyLines(callback){return pushOrConcat([],splitNewlines(callback))}function overrideCallbacks(callbacks,context){const override=context&&context.dataset&&context.dataset.tooltip&&context.dataset.tooltip.callbacks;return override?callbacks.override(override):callbacks}class Tooltip extends Element{constructor(config){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=config.chart||config._chart,this._chart=this.chart,this.options=config.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(options){this.options=options,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const cached=this._cachedAnimations;if(cached)return cached;const chart=this.chart,options=this.options.setContext(this.getContext()),opts=options.enabled&&chart.options.animation&&options.animations,animations=new Animations(this.chart,opts);return opts._cacheable&&(this._cachedAnimations=Object.freeze(animations)),animations}getContext(){return this.$context||(this.$context=(parent=this.chart.getContext(),tooltip=this,tooltipItems=this._tooltipItems,createContext(parent,{tooltip:tooltip,tooltipItems:tooltipItems,type:"tooltip"})));var parent,tooltip,tooltipItems}getTitle(context,options){const{callbacks:callbacks}=options,beforeTitle=callbacks.beforeTitle.apply(this,[context]),title=callbacks.title.apply(this,[context]),afterTitle=callbacks.afterTitle.apply(this,[context]);let lines=[];return lines=pushOrConcat(lines,splitNewlines(beforeTitle)),lines=pushOrConcat(lines,splitNewlines(title)),lines=pushOrConcat(lines,splitNewlines(afterTitle)),lines}getBeforeBody(tooltipItems,options){return getBeforeAfterBodyLines(options.callbacks.beforeBody.apply(this,[tooltipItems]))}getBody(tooltipItems,options){const{callbacks:callbacks}=options,bodyItems=[];return each(tooltipItems,(context=>{const bodyItem={before:[],lines:[],after:[]},scoped=overrideCallbacks(callbacks,context);pushOrConcat(bodyItem.before,splitNewlines(scoped.beforeLabel.call(this,context))),pushOrConcat(bodyItem.lines,scoped.label.call(this,context)),pushOrConcat(bodyItem.after,splitNewlines(scoped.afterLabel.call(this,context))),bodyItems.push(bodyItem)})),bodyItems}getAfterBody(tooltipItems,options){return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this,[tooltipItems]))}getFooter(tooltipItems,options){const{callbacks:callbacks}=options,beforeFooter=callbacks.beforeFooter.apply(this,[tooltipItems]),footer=callbacks.footer.apply(this,[tooltipItems]),afterFooter=callbacks.afterFooter.apply(this,[tooltipItems]);let lines=[];return lines=pushOrConcat(lines,splitNewlines(beforeFooter)),lines=pushOrConcat(lines,splitNewlines(footer)),lines=pushOrConcat(lines,splitNewlines(afterFooter)),lines}_createItems(options){const active=this._active,data=this.chart.data,labelColors=[],labelPointStyles=[],labelTextColors=[];let i,len,tooltipItems=[];for(i=0,len=active.length;ioptions.filter(element,index,array,data)))),options.itemSort&&(tooltipItems=tooltipItems.sort(((a,b)=>options.itemSort(a,b,data)))),each(tooltipItems,(context=>{const scoped=overrideCallbacks(options.callbacks,context);labelColors.push(scoped.labelColor.call(this,context)),labelPointStyles.push(scoped.labelPointStyle.call(this,context)),labelTextColors.push(scoped.labelTextColor.call(this,context))})),this.labelColors=labelColors,this.labelPointStyles=labelPointStyles,this.labelTextColors=labelTextColors,this.dataPoints=tooltipItems,tooltipItems}update(changed,replay){const options=this.options.setContext(this.getContext()),active=this._active;let properties,tooltipItems=[];if(active.length){const position=positioners[options.position].call(this,active,this._eventPosition);tooltipItems=this._createItems(options),this.title=this.getTitle(tooltipItems,options),this.beforeBody=this.getBeforeBody(tooltipItems,options),this.body=this.getBody(tooltipItems,options),this.afterBody=this.getAfterBody(tooltipItems,options),this.footer=this.getFooter(tooltipItems,options);const size=this._size=getTooltipSize(this,options),positionAndSize=Object.assign({},position,size),alignment=determineAlignment(this.chart,options,positionAndSize),backgroundPoint=getBackgroundPoint(options,positionAndSize,alignment,this.chart);this.xAlign=alignment.xAlign,this.yAlign=alignment.yAlign,properties={opacity:1,x:backgroundPoint.x,y:backgroundPoint.y,width:size.width,height:size.height,caretX:position.x,caretY:position.y}}else 0!==this.opacity&&(properties={opacity:0});this._tooltipItems=tooltipItems,this.$context=void 0,properties&&this._resolveAnimations().update(this,properties),changed&&options.external&&options.external.call(this,{chart:this.chart,tooltip:this,replay:replay})}drawCaret(tooltipPoint,ctx,size,options){const caretPosition=this.getCaretPosition(tooltipPoint,size,options);ctx.lineTo(caretPosition.x1,caretPosition.y1),ctx.lineTo(caretPosition.x2,caretPosition.y2),ctx.lineTo(caretPosition.x3,caretPosition.y3)}getCaretPosition(tooltipPoint,size,options){const{xAlign:xAlign,yAlign:yAlign}=this,{caretSize:caretSize,cornerRadius:cornerRadius}=options,{topLeft:topLeft,topRight:topRight,bottomLeft:bottomLeft,bottomRight:bottomRight}=toTRBLCorners(cornerRadius),{x:ptX,y:ptY}=tooltipPoint,{width:width,height:height}=size;let x1,x2,x3,y1,y2,y3;return"center"===yAlign?(y2=ptY+height/2,"left"===xAlign?(x1=ptX,x2=x1-caretSize,y1=y2+caretSize,y3=y2-caretSize):(x1=ptX+width,x2=x1+caretSize,y1=y2-caretSize,y3=y2+caretSize),x3=x1):(x2="left"===xAlign?ptX+Math.max(topLeft,bottomLeft)+caretSize:"right"===xAlign?ptX+width-Math.max(topRight,bottomRight)-caretSize:this.caretX,"top"===yAlign?(y1=ptY,y2=y1-caretSize,x1=x2-caretSize,x3=x2+caretSize):(y1=ptY+height,y2=y1+caretSize,x1=x2+caretSize,x3=x2-caretSize),y3=y1),{x1:x1,x2:x2,x3:x3,y1:y1,y2:y2,y3:y3}}drawTitle(pt,ctx,options){const title=this.title,length=title.length;let titleFont,titleSpacing,i;if(length){const rtlHelper=getRtlAdapter(options.rtl,this.x,this.width);for(pt.x=getAlignedX(this,options.titleAlign,options),ctx.textAlign=rtlHelper.textAlign(options.titleAlign),ctx.textBaseline="middle",titleFont=toFont(options.titleFont),titleSpacing=options.titleSpacing,ctx.fillStyle=options.titleColor,ctx.font=titleFont.string,i=0;i0!==v))?(ctx.beginPath(),ctx.fillStyle=options.multiKeyBackground,addRoundedRectPath(ctx,{x:outerX,y:colorY,w:boxWidth,h:boxHeight,radius:borderRadius}),ctx.fill(),ctx.stroke(),ctx.fillStyle=labelColors.backgroundColor,ctx.beginPath(),addRoundedRectPath(ctx,{x:innerX,y:colorY+1,w:boxWidth-2,h:boxHeight-2,radius:borderRadius}),ctx.fill()):(ctx.fillStyle=options.multiKeyBackground,ctx.fillRect(outerX,colorY,boxWidth,boxHeight),ctx.strokeRect(outerX,colorY,boxWidth,boxHeight),ctx.fillStyle=labelColors.backgroundColor,ctx.fillRect(innerX,colorY+1,boxWidth-2,boxHeight-2))}ctx.fillStyle=this.labelTextColors[i]}drawBody(pt,ctx,options){const{body:body}=this,{bodySpacing:bodySpacing,bodyAlign:bodyAlign,displayColors:displayColors,boxHeight:boxHeight,boxWidth:boxWidth,boxPadding:boxPadding}=options,bodyFont=toFont(options.bodyFont);let bodyLineHeight=bodyFont.lineHeight,xLinePadding=0;const rtlHelper=getRtlAdapter(options.rtl,this.x,this.width),fillLineOfText=function(line){ctx.fillText(line,rtlHelper.x(pt.x+xLinePadding),pt.y+bodyLineHeight/2),pt.y+=bodyLineHeight+bodySpacing},bodyAlignForCalculation=rtlHelper.textAlign(bodyAlign);let bodyItem,textColor,lines,i,j,ilen,jlen;for(ctx.textAlign=bodyAlign,ctx.textBaseline="middle",ctx.font=bodyFont.string,pt.x=getAlignedX(this,bodyAlignForCalculation,options),ctx.fillStyle=options.bodyColor,each(this.beforeBody,fillLineOfText),xLinePadding=displayColors&&"right"!==bodyAlignForCalculation?"center"===bodyAlign?boxWidth/2+boxPadding:boxWidth+2+boxPadding:0,i=0,ilen=body.length;i0&&ctx.stroke()}_updateAnimationTarget(options){const chart=this.chart,anims=this.$animations,animX=anims&&anims.x,animY=anims&&anims.y;if(animX||animY){const position=positioners[options.position].call(this,this._active,this._eventPosition);if(!position)return;const size=this._size=getTooltipSize(this,options),positionAndSize=Object.assign({},position,this._size),alignment=determineAlignment(chart,options,positionAndSize),point=getBackgroundPoint(options,positionAndSize,alignment,chart);animX._to===point.x&&animY._to===point.y||(this.xAlign=alignment.xAlign,this.yAlign=alignment.yAlign,this.width=size.width,this.height=size.height,this.caretX=position.x,this.caretY=position.y,this._resolveAnimations().update(this,point))}}_willRender(){return!!this.opacity}draw(ctx){const options=this.options.setContext(this.getContext());let opacity=this.opacity;if(!opacity)return;this._updateAnimationTarget(options);const tooltipSize={width:this.width,height:this.height},pt={x:this.x,y:this.y};opacity=Math.abs(opacity)<.001?0:opacity;const padding=toPadding(options.padding),hasTooltipContent=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;options.enabled&&hasTooltipContent&&(ctx.save(),ctx.globalAlpha=opacity,this.drawBackground(pt,ctx,tooltipSize,options),overrideTextDirection(ctx,options.textDirection),pt.y+=padding.top,this.drawTitle(pt,ctx,options),this.drawBody(pt,ctx,options),this.drawFooter(pt,ctx,options),restoreTextDirection(ctx,options.textDirection),ctx.restore())}getActiveElements(){return this._active||[]}setActiveElements(activeElements,eventPosition){const lastActive=this._active,active=activeElements.map((_ref5=>{let{datasetIndex:datasetIndex,index:index}=_ref5;const meta=this.chart.getDatasetMeta(datasetIndex);if(!meta)throw new Error("Cannot find a dataset at index "+datasetIndex);return{datasetIndex:datasetIndex,element:meta.data[index],index:index}})),changed=!_elementsEqual(lastActive,active),positionChanged=this._positionChanged(active,eventPosition);(changed||positionChanged)&&(this._active=active,this._eventPosition=eventPosition,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(e,replay){let inChartArea=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(replay&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const options=this.options,lastActive=this._active||[],active=this._getActiveElements(e,lastActive,replay,inChartArea),positionChanged=this._positionChanged(active,e),changed=replay||!_elementsEqual(active,lastActive)||positionChanged;return changed&&(this._active=active,(options.enabled||options.external)&&(this._eventPosition={x:e.x,y:e.y},this.update(!0,replay))),changed}_getActiveElements(e,lastActive,replay,inChartArea){const options=this.options;if("mouseout"===e.type)return[];if(!inChartArea)return lastActive;const active=this.chart.getElementsAtEventForMode(e,options.mode,options,replay);return options.reverse&&active.reverse(),active}_positionChanged(active,e){const{caretX:caretX,caretY:caretY,options:options}=this,position=positioners[options.position].call(this,active,e);return!1!==position&&(caretX!==position.x||caretY!==position.y)}}Tooltip.positioners=positioners;var plugin_tooltip={id:"tooltip",_element:Tooltip,positioners:positioners,afterInit(chart,_args,options){options&&(chart.tooltip=new Tooltip({chart:chart,options:options}))},beforeUpdate(chart,_args,options){chart.tooltip&&chart.tooltip.initialize(options)},reset(chart,_args,options){chart.tooltip&&chart.tooltip.initialize(options)},afterDraw(chart){const tooltip=chart.tooltip;if(tooltip&&tooltip._willRender()){const args={tooltip:tooltip};if(!1===chart.notifyPlugins("beforeTooltipDraw",args))return;tooltip.draw(chart.ctx),chart.notifyPlugins("afterTooltipDraw",args)}},afterEvent(chart,args){if(chart.tooltip){const useFinalPosition=args.replay;chart.tooltip.handleEvent(args.event,useFinalPosition,args.inChartArea)&&(args.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(ctx,opts)=>opts.bodyFont.size,boxWidth:(ctx,opts)=>opts.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:{beforeTitle:noop,title(tooltipItems){if(tooltipItems.length>0){const item=tooltipItems[0],labels=item.chart.data.labels,labelCount=labels?labels.length:0;if(this&&this.options&&"dataset"===this.options.mode)return item.dataset.label||"";if(item.label)return item.label;if(labelCount>0&&item.dataIndex"filter"!==name&&"itemSort"!==name&&"external"!==name,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]},plugins=Object.freeze({__proto__:null,Decimation:plugin_decimation,Filler:index,Legend:plugin_legend,SubTitle:plugin_subtitle,Title:plugin_title,Tooltip:plugin_tooltip});function findOrAddLabel(labels,raw,index,addedLabels){const first=labels.indexOf(raw);return-1===first?((labels,raw,index,addedLabels)=>("string"==typeof raw?(index=labels.push(raw)-1,addedLabels.unshift({index:index,label:raw})):isNaN(raw)&&(index=null),index))(labels,raw,index,addedLabels):first!==labels.lastIndexOf(raw)?index:first}class CategoryScale extends Scale{constructor(cfg){super(cfg),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(scaleOptions){const added=this._addedLabels;if(added.length){const labels=this.getLabels();for(const{index:index,label:label}of added)labels[index]===label&&labels.splice(index,1);this._addedLabels=[]}super.init(scaleOptions)}parse(raw,index){if(isNullOrUndef(raw))return null;const labels=this.getLabels();return((index,max)=>null===index?null:_limitValue(Math.round(index),0,max))(index=isFinite(index)&&labels[index]===raw?index:findOrAddLabel(labels,raw,valueOrDefault(index,raw),this._addedLabels),labels.length-1)}determineDataLimits(){const{minDefined:minDefined,maxDefined:maxDefined}=this.getUserBounds();let{min:min,max:max}=this.getMinMax(!0);"ticks"===this.options.bounds&&(minDefined||(min=0),maxDefined||(max=this.getLabels().length-1)),this.min=min,this.max=max}buildTicks(){const min=this.min,max=this.max,offset=this.options.offset,ticks=[];let labels=this.getLabels();labels=0===min&&max===labels.length-1?labels:labels.slice(min,max+1),this._valueRange=Math.max(labels.length-(offset?0:1),1),this._startValue=this.min-(offset?.5:0);for(let value=min;value<=max;value++)ticks.push({value:value});return ticks}getLabelForValue(value){const labels=this.getLabels();return value>=0&&valueticks.length-1?null:this.getPixelForValue(ticks[index].value)}getValueForPixel(pixel){return Math.round(this._startValue+this.getDecimalForPixel(pixel)*this._valueRange)}getBasePixel(){return this.bottom}}function relativeLabelSize(value,minSpacing,_ref6){let{horizontal:horizontal,minRotation:minRotation}=_ref6;const rad=toRadians(minRotation),ratio=(horizontal?Math.sin(rad):Math.cos(rad))||.001,length=.75*minSpacing*(""+value).length;return Math.min(minSpacing/ratio,length)}CategoryScale.id="category",CategoryScale.defaults={ticks:{callback:CategoryScale.prototype.getLabelForValue}};class LinearScaleBase extends Scale{constructor(cfg){super(cfg),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(raw,index){return isNullOrUndef(raw)||("number"==typeof raw||raw instanceof Number)&&!isFinite(+raw)?null:+raw}handleTickRangeOptions(){const{beginAtZero:beginAtZero}=this.options,{minDefined:minDefined,maxDefined:maxDefined}=this.getUserBounds();let{min:min,max:max}=this;const setMin=v=>min=minDefined?min:v,setMax=v=>max=maxDefined?max:v;if(beginAtZero){const minSign=sign(min),maxSign=sign(max);minSign<0&&maxSign<0?setMax(0):minSign>0&&maxSign>0&&setMin(0)}if(min===max){let offset=1;(max>=Number.MAX_SAFE_INTEGER||min<=Number.MIN_SAFE_INTEGER)&&(offset=Math.abs(.05*max)),setMax(max+offset),beginAtZero||setMin(min-offset)}this.min=min,this.max=max}getTickLimit(){const tickOpts=this.options.ticks;let maxTicks,{maxTicksLimit:maxTicksLimit,stepSize:stepSize}=tickOpts;return stepSize?(maxTicks=Math.ceil(this.max/stepSize)-Math.floor(this.min/stepSize)+1,maxTicks>1e3&&(console.warn("scales.".concat(this.id,".ticks.stepSize: ").concat(stepSize," would result generating up to ").concat(maxTicks," ticks. Limiting to 1000.")),maxTicks=1e3)):(maxTicks=this.computeTickLimit(),maxTicksLimit=maxTicksLimit||11),maxTicksLimit&&(maxTicks=Math.min(maxTicksLimit,maxTicks)),maxTicks}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const opts=this.options,tickOpts=opts.ticks;let maxTicks=this.getTickLimit();maxTicks=Math.max(2,maxTicks);const ticks=function(generationOptions,dataRange){const ticks=[],{bounds:bounds,step:step,min:min,max:max,precision:precision,count:count,maxTicks:maxTicks,maxDigits:maxDigits,includeBounds:includeBounds}=generationOptions,unit=step||1,maxSpaces=maxTicks-1,{min:rmin,max:rmax}=dataRange,minDefined=!isNullOrUndef(min),maxDefined=!isNullOrUndef(max),countDefined=!isNullOrUndef(count),minSpacing=(rmax-rmin)/(maxDigits+1);let factor,niceMin,niceMax,numSpaces,spacing=niceNum((rmax-rmin)/maxSpaces/unit)*unit;if(spacing<1e-14&&!minDefined&&!maxDefined)return[{value:rmin},{value:rmax}];numSpaces=Math.ceil(rmax/spacing)-Math.floor(rmin/spacing),numSpaces>maxSpaces&&(spacing=niceNum(numSpaces*spacing/maxSpaces/unit)*unit),isNullOrUndef(precision)||(factor=Math.pow(10,precision),spacing=Math.ceil(spacing*factor)/factor),"ticks"===bounds?(niceMin=Math.floor(rmin/spacing)*spacing,niceMax=Math.ceil(rmax/spacing)*spacing):(niceMin=rmin,niceMax=rmax),minDefined&&maxDefined&&step&&almostWhole((max-min)/step,spacing/1e3)?(numSpaces=Math.round(Math.min((max-min)/spacing,maxTicks)),spacing=(max-min)/numSpaces,niceMin=min,niceMax=max):countDefined?(niceMin=minDefined?min:niceMin,niceMax=maxDefined?max:niceMax,numSpaces=count-1,spacing=(niceMax-niceMin)/numSpaces):(numSpaces=(niceMax-niceMin)/spacing,numSpaces=almostEquals(numSpaces,Math.round(numSpaces),spacing/1e3)?Math.round(numSpaces):Math.ceil(numSpaces));const decimalPlaces=Math.max(_decimalPlaces(spacing),_decimalPlaces(niceMin));factor=Math.pow(10,isNullOrUndef(precision)?decimalPlaces:precision),niceMin=Math.round(niceMin*factor)/factor,niceMax=Math.round(niceMax*factor)/factor;let j=0;for(minDefined&&(includeBounds&&niceMin!==min?(ticks.push({value:min}),niceMin0?value:null;this._zero=!0}determineDataLimits(){const{min:min,max:max}=this.getMinMax(!0);this.min=isNumberFinite(min)?Math.max(0,min):null,this.max=isNumberFinite(max)?Math.max(0,max):null,this.options.beginAtZero&&(this._zero=!0),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:minDefined,maxDefined:maxDefined}=this.getUserBounds();let min=this.min,max=this.max;const setMin=v=>min=minDefined?min:v,setMax=v=>max=maxDefined?max:v,exp=(v,m)=>Math.pow(10,Math.floor(log10(v))+m);min===max&&(min<=0?(setMin(1),setMax(10)):(setMin(exp(min,-1)),setMax(exp(max,1)))),min<=0&&setMin(exp(max,-1)),max<=0&&setMax(exp(min,1)),this._zero&&this.min!==this._suggestedMin&&min===exp(this.min,0)&&setMin(exp(min,-1)),this.min=min,this.max=max}buildTicks(){const opts=this.options,ticks=function(generationOptions,dataRange){const endExp=Math.floor(log10(dataRange.max)),endSignificand=Math.ceil(dataRange.max/Math.pow(10,endExp)),ticks=[];let tickVal=finiteOrDefault(generationOptions.min,Math.pow(10,Math.floor(log10(dataRange.min)))),exp=Math.floor(log10(tickVal)),significand=Math.floor(tickVal/Math.pow(10,exp)),precision=exp<0?Math.pow(10,Math.abs(exp)):1;do{ticks.push({value:tickVal,major:isMajor(tickVal)}),++significand,10===significand&&(significand=1,++exp,precision=exp>=0?1:precision),tickVal=Math.round(significand*Math.pow(10,exp)*precision)/precision}while(expmax?{start:pos-size,end:pos}:{start:pos,end:pos+size}}function fitWithPointLabels(scale){const orig={l:scale.left+scale._padding.left,r:scale.right-scale._padding.right,t:scale.top+scale._padding.top,b:scale.bottom-scale._padding.bottom},limits=Object.assign({},orig),labelSizes=[],padding=[],valueCount=scale._pointLabels.length,pointLabelOpts=scale.options.pointLabels,additionalAngle=pointLabelOpts.centerPointLabels?PI/valueCount:0;for(let i=0;iorig.r&&(x=(hLimits.end-orig.r)/sin,limits.r=Math.max(limits.r,orig.r+x)),vLimits.startorig.b&&(y=(vLimits.end-orig.b)/cos,limits.b=Math.max(limits.b,orig.b+y))}function getTextAlignForAngle(angle){return 0===angle||180===angle?"center":angle<180?"left":"right"}function leftForTextAlign(x,w,align){return"right"===align?x-=w:"center"===align&&(x-=w/2),x}function yForAngle(y,h,angle){return 90===angle||270===angle?y-=h/2:(angle>270||angle<90)&&(y-=h),y}function pathRadiusLine(scale,radius,circular,labelCount){const{ctx:ctx}=scale;if(circular)ctx.arc(scale.xCenter,scale.yCenter,radius,0,TAU);else{let pointPosition=scale.getPointPosition(0,radius);ctx.moveTo(pointPosition.x,pointPosition.y);for(let i=1;i{const label=callback(this.options.pointLabels.callback,[value,index],this);return label||0===label?label:""})).filter(((v,i)=>this.chart.getDataVisibility(i)))}fit(){const opts=this.options;opts.display&&opts.pointLabels.display?fitWithPointLabels(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(leftMovement,rightMovement,topMovement,bottomMovement){this.xCenter+=Math.floor((leftMovement-rightMovement)/2),this.yCenter+=Math.floor((topMovement-bottomMovement)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(leftMovement,rightMovement,topMovement,bottomMovement))}getIndexAngle(index){return _normalizeAngle(index*(TAU/(this._pointLabels.length||1))+toRadians(this.options.startAngle||0))}getDistanceFromCenterForValue(value){if(isNullOrUndef(value))return NaN;const scalingFactor=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-value)*scalingFactor:(value-this.min)*scalingFactor}getValueForDistanceFromCenter(distance){if(isNullOrUndef(distance))return NaN;const scaledDistance=distance/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-scaledDistance:this.min+scaledDistance}getPointLabelContext(index){const pointLabels=this._pointLabels||[];if(index>=0&&index2&&void 0!==arguments[2]?arguments[2]:0;const angle=this.getIndexAngle(index)-HALF_PI+additionalAngle;return{x:Math.cos(angle)*distanceFromCenter+this.xCenter,y:Math.sin(angle)*distanceFromCenter+this.yCenter,angle:angle}}getPointPositionForValue(index,value){return this.getPointPosition(index,this.getDistanceFromCenterForValue(value))}getBasePosition(index){return this.getPointPositionForValue(index||0,this.getBaseValue())}getPointLabelPosition(index){const{left:left,top:top,right:right,bottom:bottom}=this._pointLabelItems[index];return{left:left,top:top,right:right,bottom:bottom}}drawBackground(){const{backgroundColor:backgroundColor,grid:{circular:circular}}=this.options;if(backgroundColor){const ctx=this.ctx;ctx.save(),ctx.beginPath(),pathRadiusLine(this,this.getDistanceFromCenterForValue(this._endValue),circular,this._pointLabels.length),ctx.closePath(),ctx.fillStyle=backgroundColor,ctx.fill(),ctx.restore()}}drawGrid(){const ctx=this.ctx,opts=this.options,{angleLines:angleLines,grid:grid}=opts,labelCount=this._pointLabels.length;let i,offset,position;if(opts.pointLabels.display&&function(scale,labelCount){const{ctx:ctx,options:{pointLabels:pointLabels}}=scale;for(let i=labelCount-1;i>=0;i--){const optsAtIndex=pointLabels.setContext(scale.getPointLabelContext(i)),plFont=toFont(optsAtIndex.font),{x:x,y:y,textAlign:textAlign,left:left,top:top,right:right,bottom:bottom}=scale._pointLabelItems[i],{backdropColor:backdropColor}=optsAtIndex;if(!isNullOrUndef(backdropColor)){const borderRadius=toTRBLCorners(optsAtIndex.borderRadius),padding=toPadding(optsAtIndex.backdropPadding);ctx.fillStyle=backdropColor;const backdropLeft=left-padding.left,backdropTop=top-padding.top,backdropWidth=right-left+padding.width,backdropHeight=bottom-top+padding.height;Object.values(borderRadius).some((v=>0!==v))?(ctx.beginPath(),addRoundedRectPath(ctx,{x:backdropLeft,y:backdropTop,w:backdropWidth,h:backdropHeight,radius:borderRadius}),ctx.fill()):ctx.fillRect(backdropLeft,backdropTop,backdropWidth,backdropHeight)}renderText(ctx,scale._pointLabels[i],x,y+plFont.lineHeight/2,plFont,{color:optsAtIndex.color,textAlign:textAlign,textBaseline:"middle"})}}(this,labelCount),grid.display&&this.ticks.forEach(((tick,index)=>{0!==index&&(offset=this.getDistanceFromCenterForValue(tick.value),function(scale,gridLineOpts,radius,labelCount){const ctx=scale.ctx,circular=gridLineOpts.circular,{color:color,lineWidth:lineWidth}=gridLineOpts;!circular&&!labelCount||!color||!lineWidth||radius<0||(ctx.save(),ctx.strokeStyle=color,ctx.lineWidth=lineWidth,ctx.setLineDash(gridLineOpts.borderDash),ctx.lineDashOffset=gridLineOpts.borderDashOffset,ctx.beginPath(),pathRadiusLine(scale,radius,circular,labelCount),ctx.closePath(),ctx.stroke(),ctx.restore())}(this,grid.setContext(this.getContext(index-1)),offset,labelCount))})),angleLines.display){for(ctx.save(),i=labelCount-1;i>=0;i--){const optsAtIndex=angleLines.setContext(this.getPointLabelContext(i)),{color:color,lineWidth:lineWidth}=optsAtIndex;lineWidth&&color&&(ctx.lineWidth=lineWidth,ctx.strokeStyle=color,ctx.setLineDash(optsAtIndex.borderDash),ctx.lineDashOffset=optsAtIndex.borderDashOffset,offset=this.getDistanceFromCenterForValue(opts.ticks.reverse?this.min:this.max),position=this.getPointPosition(i,offset),ctx.beginPath(),ctx.moveTo(this.xCenter,this.yCenter),ctx.lineTo(position.x,position.y),ctx.stroke())}ctx.restore()}}drawBorder(){}drawLabels(){const ctx=this.ctx,opts=this.options,tickOpts=opts.ticks;if(!tickOpts.display)return;const startAngle=this.getIndexAngle(0);let offset,width;ctx.save(),ctx.translate(this.xCenter,this.yCenter),ctx.rotate(startAngle),ctx.textAlign="center",ctx.textBaseline="middle",this.ticks.forEach(((tick,index)=>{if(0===index&&!opts.reverse)return;const optsAtIndex=tickOpts.setContext(this.getContext(index)),tickFont=toFont(optsAtIndex.font);if(offset=this.getDistanceFromCenterForValue(this.ticks[index].value),optsAtIndex.showLabelBackdrop){ctx.font=tickFont.string,width=ctx.measureText(tick.label).width,ctx.fillStyle=optsAtIndex.backdropColor;const padding=toPadding(optsAtIndex.backdropPadding);ctx.fillRect(-width/2-padding.left,-offset-tickFont.size/2-padding.top,width+padding.width,tickFont.size+padding.height)}renderText(ctx,tick.label,0,-offset,tickFont,{color:optsAtIndex.color})})),ctx.restore()}drawTitle(){}}RadialLinearScale.id="radialLinear",RadialLinearScale.defaults={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:Ticks.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:label=>label,padding:5,centerPointLabels:!1}},RadialLinearScale.defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"},RadialLinearScale.descriptors={angleLines:{_fallback:"grid"}};const INTERVALS={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},UNITS=Object.keys(INTERVALS);function sorter(a,b){return a-b}function parse(scale,input){if(isNullOrUndef(input))return null;const adapter=scale._adapter,{parser:parser,round:round,isoWeekday:isoWeekday}=scale._parseOpts;let value=input;return"function"==typeof parser&&(value=parser(value)),isNumberFinite(value)||(value="string"==typeof parser?adapter.parse(value,parser):adapter.parse(value)),null===value?null:(round&&(value="week"!==round||!isNumber(isoWeekday)&&!0!==isoWeekday?adapter.startOf(value,round):adapter.startOf(value,"isoWeek",isoWeekday)),+value)}function determineUnitForAutoTicks(minUnit,min,max,capacity){const ilen=UNITS.length;for(let i=UNITS.indexOf(minUnit);i=time?timestamps[lo]:timestamps[hi]]=!0}}else ticks[time]=!0}function ticksFromTimestamps(scale,values,majorUnit){const ticks=[],map={},ilen=values.length;let i,value;for(i=0;i=0&&(ticks[index].major=!0);return ticks}(scale,ticks,map,majorUnit):ticks}class TimeScale extends Scale{constructor(props){super(props),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(scaleOpts,opts){const time=scaleOpts.time||(scaleOpts.time={}),adapter=this._adapter=new _adapters._date(scaleOpts.adapters.date);mergeIf(time.displayFormats,adapter.formats()),this._parseOpts={parser:time.parser,round:time.round,isoWeekday:time.isoWeekday},super.init(scaleOpts),this._normalized=opts.normalized}parse(raw,index){return void 0===raw?null:parse(this,raw)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const options=this.options,adapter=this._adapter,unit=options.time.unit||"day";let{min:min,max:max,minDefined:minDefined,maxDefined:maxDefined}=this.getUserBounds();function _applyBounds(bounds){minDefined||isNaN(bounds.min)||(min=Math.min(min,bounds.min)),maxDefined||isNaN(bounds.max)||(max=Math.max(max,bounds.max))}minDefined&&maxDefined||(_applyBounds(this._getLabelBounds()),"ticks"===options.bounds&&"labels"===options.ticks.source||_applyBounds(this.getMinMax(!1))),min=isNumberFinite(min)&&!isNaN(min)?min:+adapter.startOf(Date.now(),unit),max=isNumberFinite(max)&&!isNaN(max)?max:+adapter.endOf(Date.now(),unit)+1,this.min=Math.min(min,max-1),this.max=Math.max(min+1,max)}_getLabelBounds(){const arr=this.getLabelTimestamps();let min=Number.POSITIVE_INFINITY,max=Number.NEGATIVE_INFINITY;return arr.length&&(min=arr[0],max=arr[arr.length-1]),{min:min,max:max}}buildTicks(){const options=this.options,timeOpts=options.time,tickOpts=options.ticks,timestamps="labels"===tickOpts.source?this.getLabelTimestamps():this._generate();"ticks"===options.bounds&×tamps.length&&(this.min=this._userMin||timestamps[0],this.max=this._userMax||timestamps[timestamps.length-1]);const min=this.min,ticks=_filterBetween(timestamps,min,this.max);return this._unit=timeOpts.unit||(tickOpts.autoSkip?determineUnitForAutoTicks(timeOpts.minUnit,this.min,this.max,this._getLabelCapacity(min)):function(scale,numTicks,minUnit,min,max){for(let i=UNITS.length-1;i>=UNITS.indexOf(minUnit);i--){const unit=UNITS[i];if(INTERVALS[unit].common&&scale._adapter.diff(max,min,unit)>=numTicks-1)return unit}return UNITS[minUnit?UNITS.indexOf(minUnit):0]}(this,ticks.length,timeOpts.minUnit,this.min,this.max)),this._majorUnit=tickOpts.major.enabled&&"year"!==this._unit?function(unit){for(let i=UNITS.indexOf(unit)+1,ilen=UNITS.length;i+tick.value)))}initOffsets(timestamps){let first,last,start=0,end=0;this.options.offset&×tamps.length&&(first=this.getDecimalForValue(timestamps[0]),start=1===timestamps.length?1-first:(this.getDecimalForValue(timestamps[1])-first)/2,last=this.getDecimalForValue(timestamps[timestamps.length-1]),end=1===timestamps.length?last:(last-this.getDecimalForValue(timestamps[timestamps.length-2]))/2);const limit=timestamps.length<3?.5:.25;start=_limitValue(start,0,limit),end=_limitValue(end,0,limit),this._offsets={start:start,end:end,factor:1/(start+1+end)}}_generate(){const adapter=this._adapter,min=this.min,max=this.max,options=this.options,timeOpts=options.time,minor=timeOpts.unit||determineUnitForAutoTicks(timeOpts.minUnit,min,max,this._getLabelCapacity(min)),stepSize=valueOrDefault(timeOpts.stepSize,1),weekday="week"===minor&&timeOpts.isoWeekday,hasWeekday=isNumber(weekday)||!0===weekday,ticks={};let time,count,first=min;if(hasWeekday&&(first=+adapter.startOf(first,"isoWeek",weekday)),first=+adapter.startOf(first,hasWeekday?"day":minor),adapter.diff(max,min,minor)>1e5*stepSize)throw new Error(min+" and "+max+" are too far apart with stepSize of "+stepSize+" "+minor);const timestamps="data"===options.ticks.source&&this.getDataTimestamps();for(time=first,count=0;timea-b)).map((x=>+x))}getLabelForValue(value){const adapter=this._adapter,timeOpts=this.options.time;return timeOpts.tooltipFormat?adapter.format(value,timeOpts.tooltipFormat):adapter.format(value,timeOpts.displayFormats.datetime)}_tickFormatFunction(time,index,ticks,format){const options=this.options,formats=options.time.displayFormats,unit=this._unit,majorUnit=this._majorUnit,minorFormat=unit&&formats[unit],majorFormat=majorUnit&&formats[majorUnit],tick=ticks[index],major=majorUnit&&majorFormat&&tick&&tick.major,label=this._adapter.format(time,format||(major?majorFormat:minorFormat)),formatter=options.ticks.callback;return formatter?callback(formatter,[label,index,ticks],this):label}generateTickLabels(ticks){let i,ilen,tick;for(i=0,ilen=ticks.length;i0?capacity:1}getDataTimestamps(){let i,ilen,timestamps=this._cache.data||[];if(timestamps.length)return timestamps;const metas=this.getMatchingVisibleMetas();if(this._normalized&&metas.length)return this._cache.data=metas[0].controller.getAllParsedValues(this);for(i=0,ilen=metas.length;i=table[lo].pos&&val<=table[hi].pos&&({lo:lo,hi:hi}=_lookupByKey(table,"pos",val)),({pos:prevSource,time:prevTarget}=table[lo]),({pos:nextSource,time:nextTarget}=table[hi])):(val>=table[lo].time&&val<=table[hi].time&&({lo:lo,hi:hi}=_lookupByKey(table,"time",val)),({time:prevSource,pos:prevTarget}=table[lo]),({time:nextSource,pos:nextTarget}=table[hi]));const span=nextSource-prevSource;return span?prevTarget+(nextTarget-prevTarget)*(val-prevSource)/span:prevTarget}TimeScale.id="time",TimeScale.defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",major:{enabled:!1}}};class TimeSeriesScale extends TimeScale{constructor(props){super(props),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const timestamps=this._getTimestampsForTable(),table=this._table=this.buildLookupTable(timestamps);this._minPos=interpolate(table,this.min),this._tableRange=interpolate(table,this.max)-this._minPos,super.initOffsets(timestamps)}buildLookupTable(timestamps){const{min:min,max:max}=this,items=[],table=[];let i,ilen,prev,curr,next;for(i=0,ilen=timestamps.length;i=min&&curr<=max&&items.push(curr);if(items.length<2)return[{time:min,pos:0},{time:max,pos:1}];for(i=0,ilen=items.length;i 1) {\n\t\t\th -= 1;\n\t\t}\n\t}\n\n\treturn [\n\t\th * 360,\n\t\ts * 100,\n\t\tv * 100\n\t];\n};\n\nconvert.rgb.hwb = function (rgb) {\n\tvar r = rgb[0];\n\tvar g = rgb[1];\n\tvar b = rgb[2];\n\tvar h = convert.rgb.hsl(rgb)[0];\n\tvar w = 1 / 255 * Math.min(r, Math.min(g, b));\n\n\tb = 1 - 1 / 255 * Math.max(r, Math.max(g, b));\n\n\treturn [h, w * 100, b * 100];\n};\n\nconvert.rgb.cmyk = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar c;\n\tvar m;\n\tvar y;\n\tvar k;\n\n\tk = Math.min(1 - r, 1 - g, 1 - b);\n\tc = (1 - r - k) / (1 - k) || 0;\n\tm = (1 - g - k) / (1 - k) || 0;\n\ty = (1 - b - k) / (1 - k) || 0;\n\n\treturn [c * 100, m * 100, y * 100, k * 100];\n};\n\n/**\n * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance\n * */\nfunction comparativeDistance(x, y) {\n\treturn (\n\t\tMath.pow(x[0] - y[0], 2) +\n\t\tMath.pow(x[1] - y[1], 2) +\n\t\tMath.pow(x[2] - y[2], 2)\n\t);\n}\n\nconvert.rgb.keyword = function (rgb) {\n\tvar reversed = reverseKeywords[rgb];\n\tif (reversed) {\n\t\treturn reversed;\n\t}\n\n\tvar currentClosestDistance = Infinity;\n\tvar currentClosestKeyword;\n\n\tfor (var keyword in colorName) {\n\t\tif (colorName.hasOwnProperty(keyword)) {\n\t\t\tvar value = colorName[keyword];\n\n\t\t\t// Compute comparative distance\n\t\t\tvar distance = comparativeDistance(rgb, value);\n\n\t\t\t// Check if its less, if so set as closest\n\t\t\tif (distance < currentClosestDistance) {\n\t\t\t\tcurrentClosestDistance = distance;\n\t\t\t\tcurrentClosestKeyword = keyword;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn currentClosestKeyword;\n};\n\nconvert.keyword.rgb = function (keyword) {\n\treturn colorName[keyword];\n};\n\nconvert.rgb.xyz = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\n\t// assume sRGB\n\tr = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);\n\tg = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);\n\tb = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);\n\n\tvar x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);\n\tvar y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);\n\tvar z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);\n\n\treturn [x * 100, y * 100, z * 100];\n};\n\nconvert.rgb.lab = function (rgb) {\n\tvar xyz = convert.rgb.xyz(rgb);\n\tvar x = xyz[0];\n\tvar y = xyz[1];\n\tvar z = xyz[2];\n\tvar l;\n\tvar a;\n\tvar b;\n\n\tx /= 95.047;\n\ty /= 100;\n\tz /= 108.883;\n\n\tx = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);\n\ty = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);\n\tz = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);\n\n\tl = (116 * y) - 16;\n\ta = 500 * (x - y);\n\tb = 200 * (y - z);\n\n\treturn [l, a, b];\n};\n\nconvert.hsl.rgb = function (hsl) {\n\tvar h = hsl[0] / 360;\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar t1;\n\tvar t2;\n\tvar t3;\n\tvar rgb;\n\tvar val;\n\n\tif (s === 0) {\n\t\tval = l * 255;\n\t\treturn [val, val, val];\n\t}\n\n\tif (l < 0.5) {\n\t\tt2 = l * (1 + s);\n\t} else {\n\t\tt2 = l + s - l * s;\n\t}\n\n\tt1 = 2 * l - t2;\n\n\trgb = [0, 0, 0];\n\tfor (var i = 0; i < 3; i++) {\n\t\tt3 = h + 1 / 3 * -(i - 1);\n\t\tif (t3 < 0) {\n\t\t\tt3++;\n\t\t}\n\t\tif (t3 > 1) {\n\t\t\tt3--;\n\t\t}\n\n\t\tif (6 * t3 < 1) {\n\t\t\tval = t1 + (t2 - t1) * 6 * t3;\n\t\t} else if (2 * t3 < 1) {\n\t\t\tval = t2;\n\t\t} else if (3 * t3 < 2) {\n\t\t\tval = t1 + (t2 - t1) * (2 / 3 - t3) * 6;\n\t\t} else {\n\t\t\tval = t1;\n\t\t}\n\n\t\trgb[i] = val * 255;\n\t}\n\n\treturn rgb;\n};\n\nconvert.hsl.hsv = function (hsl) {\n\tvar h = hsl[0];\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar smin = s;\n\tvar lmin = Math.max(l, 0.01);\n\tvar sv;\n\tvar v;\n\n\tl *= 2;\n\ts *= (l <= 1) ? l : 2 - l;\n\tsmin *= lmin <= 1 ? lmin : 2 - lmin;\n\tv = (l + s) / 2;\n\tsv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);\n\n\treturn [h, sv * 100, v * 100];\n};\n\nconvert.hsv.rgb = function (hsv) {\n\tvar h = hsv[0] / 60;\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\tvar hi = Math.floor(h) % 6;\n\n\tvar f = h - Math.floor(h);\n\tvar p = 255 * v * (1 - s);\n\tvar q = 255 * v * (1 - (s * f));\n\tvar t = 255 * v * (1 - (s * (1 - f)));\n\tv *= 255;\n\n\tswitch (hi) {\n\t\tcase 0:\n\t\t\treturn [v, t, p];\n\t\tcase 1:\n\t\t\treturn [q, v, p];\n\t\tcase 2:\n\t\t\treturn [p, v, t];\n\t\tcase 3:\n\t\t\treturn [p, q, v];\n\t\tcase 4:\n\t\t\treturn [t, p, v];\n\t\tcase 5:\n\t\t\treturn [v, p, q];\n\t}\n};\n\nconvert.hsv.hsl = function (hsv) {\n\tvar h = hsv[0];\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\tvar vmin = Math.max(v, 0.01);\n\tvar lmin;\n\tvar sl;\n\tvar l;\n\n\tl = (2 - s) * v;\n\tlmin = (2 - s) * vmin;\n\tsl = s * vmin;\n\tsl /= (lmin <= 1) ? lmin : 2 - lmin;\n\tsl = sl || 0;\n\tl /= 2;\n\n\treturn [h, sl * 100, l * 100];\n};\n\n// http://dev.w3.org/csswg/css-color/#hwb-to-rgb\nconvert.hwb.rgb = function (hwb) {\n\tvar h = hwb[0] / 360;\n\tvar wh = hwb[1] / 100;\n\tvar bl = hwb[2] / 100;\n\tvar ratio = wh + bl;\n\tvar i;\n\tvar v;\n\tvar f;\n\tvar n;\n\n\t// wh + bl cant be > 1\n\tif (ratio > 1) {\n\t\twh /= ratio;\n\t\tbl /= ratio;\n\t}\n\n\ti = Math.floor(6 * h);\n\tv = 1 - bl;\n\tf = 6 * h - i;\n\n\tif ((i & 0x01) !== 0) {\n\t\tf = 1 - f;\n\t}\n\n\tn = wh + f * (v - wh); // linear interpolation\n\n\tvar r;\n\tvar g;\n\tvar b;\n\tswitch (i) {\n\t\tdefault:\n\t\tcase 6:\n\t\tcase 0: r = v; g = n; b = wh; break;\n\t\tcase 1: r = n; g = v; b = wh; break;\n\t\tcase 2: r = wh; g = v; b = n; break;\n\t\tcase 3: r = wh; g = n; b = v; break;\n\t\tcase 4: r = n; g = wh; b = v; break;\n\t\tcase 5: r = v; g = wh; b = n; break;\n\t}\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.cmyk.rgb = function (cmyk) {\n\tvar c = cmyk[0] / 100;\n\tvar m = cmyk[1] / 100;\n\tvar y = cmyk[2] / 100;\n\tvar k = cmyk[3] / 100;\n\tvar r;\n\tvar g;\n\tvar b;\n\n\tr = 1 - Math.min(1, c * (1 - k) + k);\n\tg = 1 - Math.min(1, m * (1 - k) + k);\n\tb = 1 - Math.min(1, y * (1 - k) + k);\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.xyz.rgb = function (xyz) {\n\tvar x = xyz[0] / 100;\n\tvar y = xyz[1] / 100;\n\tvar z = xyz[2] / 100;\n\tvar r;\n\tvar g;\n\tvar b;\n\n\tr = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);\n\tg = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);\n\tb = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);\n\n\t// assume sRGB\n\tr = r > 0.0031308\n\t\t? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)\n\t\t: r * 12.92;\n\n\tg = g > 0.0031308\n\t\t? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)\n\t\t: g * 12.92;\n\n\tb = b > 0.0031308\n\t\t? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)\n\t\t: b * 12.92;\n\n\tr = Math.min(Math.max(0, r), 1);\n\tg = Math.min(Math.max(0, g), 1);\n\tb = Math.min(Math.max(0, b), 1);\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.xyz.lab = function (xyz) {\n\tvar x = xyz[0];\n\tvar y = xyz[1];\n\tvar z = xyz[2];\n\tvar l;\n\tvar a;\n\tvar b;\n\n\tx /= 95.047;\n\ty /= 100;\n\tz /= 108.883;\n\n\tx = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);\n\ty = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);\n\tz = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);\n\n\tl = (116 * y) - 16;\n\ta = 500 * (x - y);\n\tb = 200 * (y - z);\n\n\treturn [l, a, b];\n};\n\nconvert.lab.xyz = function (lab) {\n\tvar l = lab[0];\n\tvar a = lab[1];\n\tvar b = lab[2];\n\tvar x;\n\tvar y;\n\tvar z;\n\n\ty = (l + 16) / 116;\n\tx = a / 500 + y;\n\tz = y - b / 200;\n\n\tvar y2 = Math.pow(y, 3);\n\tvar x2 = Math.pow(x, 3);\n\tvar z2 = Math.pow(z, 3);\n\ty = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;\n\tx = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;\n\tz = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;\n\n\tx *= 95.047;\n\ty *= 100;\n\tz *= 108.883;\n\n\treturn [x, y, z];\n};\n\nconvert.lab.lch = function (lab) {\n\tvar l = lab[0];\n\tvar a = lab[1];\n\tvar b = lab[2];\n\tvar hr;\n\tvar h;\n\tvar c;\n\n\thr = Math.atan2(b, a);\n\th = hr * 360 / 2 / Math.PI;\n\n\tif (h < 0) {\n\t\th += 360;\n\t}\n\n\tc = Math.sqrt(a * a + b * b);\n\n\treturn [l, c, h];\n};\n\nconvert.lch.lab = function (lch) {\n\tvar l = lch[0];\n\tvar c = lch[1];\n\tvar h = lch[2];\n\tvar a;\n\tvar b;\n\tvar hr;\n\n\thr = h / 360 * 2 * Math.PI;\n\ta = c * Math.cos(hr);\n\tb = c * Math.sin(hr);\n\n\treturn [l, a, b];\n};\n\nconvert.rgb.ansi16 = function (args) {\n\tvar r = args[0];\n\tvar g = args[1];\n\tvar b = args[2];\n\tvar value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization\n\n\tvalue = Math.round(value / 50);\n\n\tif (value === 0) {\n\t\treturn 30;\n\t}\n\n\tvar ansi = 30\n\t\t+ ((Math.round(b / 255) << 2)\n\t\t| (Math.round(g / 255) << 1)\n\t\t| Math.round(r / 255));\n\n\tif (value === 2) {\n\t\tansi += 60;\n\t}\n\n\treturn ansi;\n};\n\nconvert.hsv.ansi16 = function (args) {\n\t// optimization here; we already know the value and don't need to get\n\t// it converted for us.\n\treturn convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);\n};\n\nconvert.rgb.ansi256 = function (args) {\n\tvar r = args[0];\n\tvar g = args[1];\n\tvar b = args[2];\n\n\t// we use the extended greyscale palette here, with the exception of\n\t// black and white. normal palette only has 4 greyscale shades.\n\tif (r === g && g === b) {\n\t\tif (r < 8) {\n\t\t\treturn 16;\n\t\t}\n\n\t\tif (r > 248) {\n\t\t\treturn 231;\n\t\t}\n\n\t\treturn Math.round(((r - 8) / 247) * 24) + 232;\n\t}\n\n\tvar ansi = 16\n\t\t+ (36 * Math.round(r / 255 * 5))\n\t\t+ (6 * Math.round(g / 255 * 5))\n\t\t+ Math.round(b / 255 * 5);\n\n\treturn ansi;\n};\n\nconvert.ansi16.rgb = function (args) {\n\tvar color = args % 10;\n\n\t// handle greyscale\n\tif (color === 0 || color === 7) {\n\t\tif (args > 50) {\n\t\t\tcolor += 3.5;\n\t\t}\n\n\t\tcolor = color / 10.5 * 255;\n\n\t\treturn [color, color, color];\n\t}\n\n\tvar mult = (~~(args > 50) + 1) * 0.5;\n\tvar r = ((color & 1) * mult) * 255;\n\tvar g = (((color >> 1) & 1) * mult) * 255;\n\tvar b = (((color >> 2) & 1) * mult) * 255;\n\n\treturn [r, g, b];\n};\n\nconvert.ansi256.rgb = function (args) {\n\t// handle greyscale\n\tif (args >= 232) {\n\t\tvar c = (args - 232) * 10 + 8;\n\t\treturn [c, c, c];\n\t}\n\n\targs -= 16;\n\n\tvar rem;\n\tvar r = Math.floor(args / 36) / 5 * 255;\n\tvar g = Math.floor((rem = args % 36) / 6) / 5 * 255;\n\tvar b = (rem % 6) / 5 * 255;\n\n\treturn [r, g, b];\n};\n\nconvert.rgb.hex = function (args) {\n\tvar integer = ((Math.round(args[0]) & 0xFF) << 16)\n\t\t+ ((Math.round(args[1]) & 0xFF) << 8)\n\t\t+ (Math.round(args[2]) & 0xFF);\n\n\tvar string = integer.toString(16).toUpperCase();\n\treturn '000000'.substring(string.length) + string;\n};\n\nconvert.hex.rgb = function (args) {\n\tvar match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);\n\tif (!match) {\n\t\treturn [0, 0, 0];\n\t}\n\n\tvar colorString = match[0];\n\n\tif (match[0].length === 3) {\n\t\tcolorString = colorString.split('').map(function (char) {\n\t\t\treturn char + char;\n\t\t}).join('');\n\t}\n\n\tvar integer = parseInt(colorString, 16);\n\tvar r = (integer >> 16) & 0xFF;\n\tvar g = (integer >> 8) & 0xFF;\n\tvar b = integer & 0xFF;\n\n\treturn [r, g, b];\n};\n\nconvert.rgb.hcg = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar max = Math.max(Math.max(r, g), b);\n\tvar min = Math.min(Math.min(r, g), b);\n\tvar chroma = (max - min);\n\tvar grayscale;\n\tvar hue;\n\n\tif (chroma < 1) {\n\t\tgrayscale = min / (1 - chroma);\n\t} else {\n\t\tgrayscale = 0;\n\t}\n\n\tif (chroma <= 0) {\n\t\thue = 0;\n\t} else\n\tif (max === r) {\n\t\thue = ((g - b) / chroma) % 6;\n\t} else\n\tif (max === g) {\n\t\thue = 2 + (b - r) / chroma;\n\t} else {\n\t\thue = 4 + (r - g) / chroma + 4;\n\t}\n\n\thue /= 6;\n\thue %= 1;\n\n\treturn [hue * 360, chroma * 100, grayscale * 100];\n};\n\nconvert.hsl.hcg = function (hsl) {\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar c = 1;\n\tvar f = 0;\n\n\tif (l < 0.5) {\n\t\tc = 2.0 * s * l;\n\t} else {\n\t\tc = 2.0 * s * (1.0 - l);\n\t}\n\n\tif (c < 1.0) {\n\t\tf = (l - 0.5 * c) / (1.0 - c);\n\t}\n\n\treturn [hsl[0], c * 100, f * 100];\n};\n\nconvert.hsv.hcg = function (hsv) {\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\n\tvar c = s * v;\n\tvar f = 0;\n\n\tif (c < 1.0) {\n\t\tf = (v - c) / (1 - c);\n\t}\n\n\treturn [hsv[0], c * 100, f * 100];\n};\n\nconvert.hcg.rgb = function (hcg) {\n\tvar h = hcg[0] / 360;\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tif (c === 0.0) {\n\t\treturn [g * 255, g * 255, g * 255];\n\t}\n\n\tvar pure = [0, 0, 0];\n\tvar hi = (h % 1) * 6;\n\tvar v = hi % 1;\n\tvar w = 1 - v;\n\tvar mg = 0;\n\n\tswitch (Math.floor(hi)) {\n\t\tcase 0:\n\t\t\tpure[0] = 1; pure[1] = v; pure[2] = 0; break;\n\t\tcase 1:\n\t\t\tpure[0] = w; pure[1] = 1; pure[2] = 0; break;\n\t\tcase 2:\n\t\t\tpure[0] = 0; pure[1] = 1; pure[2] = v; break;\n\t\tcase 3:\n\t\t\tpure[0] = 0; pure[1] = w; pure[2] = 1; break;\n\t\tcase 4:\n\t\t\tpure[0] = v; pure[1] = 0; pure[2] = 1; break;\n\t\tdefault:\n\t\t\tpure[0] = 1; pure[1] = 0; pure[2] = w;\n\t}\n\n\tmg = (1.0 - c) * g;\n\n\treturn [\n\t\t(c * pure[0] + mg) * 255,\n\t\t(c * pure[1] + mg) * 255,\n\t\t(c * pure[2] + mg) * 255\n\t];\n};\n\nconvert.hcg.hsv = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tvar v = c + g * (1.0 - c);\n\tvar f = 0;\n\n\tif (v > 0.0) {\n\t\tf = c / v;\n\t}\n\n\treturn [hcg[0], f * 100, v * 100];\n};\n\nconvert.hcg.hsl = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tvar l = g * (1.0 - c) + 0.5 * c;\n\tvar s = 0;\n\n\tif (l > 0.0 && l < 0.5) {\n\t\ts = c / (2 * l);\n\t} else\n\tif (l >= 0.5 && l < 1.0) {\n\t\ts = c / (2 * (1 - l));\n\t}\n\n\treturn [hcg[0], s * 100, l * 100];\n};\n\nconvert.hcg.hwb = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\tvar v = c + g * (1.0 - c);\n\treturn [hcg[0], (v - c) * 100, (1 - v) * 100];\n};\n\nconvert.hwb.hcg = function (hwb) {\n\tvar w = hwb[1] / 100;\n\tvar b = hwb[2] / 100;\n\tvar v = 1 - b;\n\tvar c = v - w;\n\tvar g = 0;\n\n\tif (c < 1) {\n\t\tg = (v - c) / (1 - c);\n\t}\n\n\treturn [hwb[0], c * 100, g * 100];\n};\n\nconvert.apple.rgb = function (apple) {\n\treturn [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];\n};\n\nconvert.rgb.apple = function (rgb) {\n\treturn [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];\n};\n\nconvert.gray.rgb = function (args) {\n\treturn [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];\n};\n\nconvert.gray.hsl = convert.gray.hsv = function (args) {\n\treturn [0, 0, args[0]];\n};\n\nconvert.gray.hwb = function (gray) {\n\treturn [0, 100, gray[0]];\n};\n\nconvert.gray.cmyk = function (gray) {\n\treturn [0, 0, 0, gray[0]];\n};\n\nconvert.gray.lab = function (gray) {\n\treturn [gray[0], 0, 0];\n};\n\nconvert.gray.hex = function (gray) {\n\tvar val = Math.round(gray[0] / 100 * 255) & 0xFF;\n\tvar integer = (val << 16) + (val << 8) + val;\n\n\tvar string = integer.toString(16).toUpperCase();\n\treturn '000000'.substring(string.length) + string;\n};\n\nconvert.rgb.gray = function (rgb) {\n\tvar val = (rgb[0] + rgb[1] + rgb[2]) / 3;\n\treturn [val / 255 * 100];\n};\n});\nvar conversions_1 = conversions.rgb;\nvar conversions_2 = conversions.hsl;\nvar conversions_3 = conversions.hsv;\nvar conversions_4 = conversions.hwb;\nvar conversions_5 = conversions.cmyk;\nvar conversions_6 = conversions.xyz;\nvar conversions_7 = conversions.lab;\nvar conversions_8 = conversions.lch;\nvar conversions_9 = conversions.hex;\nvar conversions_10 = conversions.keyword;\nvar conversions_11 = conversions.ansi16;\nvar conversions_12 = conversions.ansi256;\nvar conversions_13 = conversions.hcg;\nvar conversions_14 = conversions.apple;\nvar conversions_15 = conversions.gray;\n\n/*\n\tthis function routes a model to all other models.\n\n\tall functions that are routed have a property `.conversion` attached\n\tto the returned synthetic function. This property is an array\n\tof strings, each with the steps in between the 'from' and 'to'\n\tcolor models (inclusive).\n\n\tconversions that are not possible simply are not included.\n*/\n\nfunction buildGraph() {\n\tvar graph = {};\n\t// https://jsperf.com/object-keys-vs-for-in-with-closure/3\n\tvar models = Object.keys(conversions);\n\n\tfor (var len = models.length, i = 0; i < len; i++) {\n\t\tgraph[models[i]] = {\n\t\t\t// http://jsperf.com/1-vs-infinity\n\t\t\t// micro-opt, but this is simple.\n\t\t\tdistance: -1,\n\t\t\tparent: null\n\t\t};\n\t}\n\n\treturn graph;\n}\n\n// https://en.wikipedia.org/wiki/Breadth-first_search\nfunction deriveBFS(fromModel) {\n\tvar graph = buildGraph();\n\tvar queue = [fromModel]; // unshift -> queue -> pop\n\n\tgraph[fromModel].distance = 0;\n\n\twhile (queue.length) {\n\t\tvar current = queue.pop();\n\t\tvar adjacents = Object.keys(conversions[current]);\n\n\t\tfor (var len = adjacents.length, i = 0; i < len; i++) {\n\t\t\tvar adjacent = adjacents[i];\n\t\t\tvar node = graph[adjacent];\n\n\t\t\tif (node.distance === -1) {\n\t\t\t\tnode.distance = graph[current].distance + 1;\n\t\t\t\tnode.parent = current;\n\t\t\t\tqueue.unshift(adjacent);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn graph;\n}\n\nfunction link(from, to) {\n\treturn function (args) {\n\t\treturn to(from(args));\n\t};\n}\n\nfunction wrapConversion(toModel, graph) {\n\tvar path = [graph[toModel].parent, toModel];\n\tvar fn = conversions[graph[toModel].parent][toModel];\n\n\tvar cur = graph[toModel].parent;\n\twhile (graph[cur].parent) {\n\t\tpath.unshift(graph[cur].parent);\n\t\tfn = link(conversions[graph[cur].parent][cur], fn);\n\t\tcur = graph[cur].parent;\n\t}\n\n\tfn.conversion = path;\n\treturn fn;\n}\n\nvar route = function (fromModel) {\n\tvar graph = deriveBFS(fromModel);\n\tvar conversion = {};\n\n\tvar models = Object.keys(graph);\n\tfor (var len = models.length, i = 0; i < len; i++) {\n\t\tvar toModel = models[i];\n\t\tvar node = graph[toModel];\n\n\t\tif (node.parent === null) {\n\t\t\t// no possible conversion, or this node is the source model.\n\t\t\tcontinue;\n\t\t}\n\n\t\tconversion[toModel] = wrapConversion(toModel, graph);\n\t}\n\n\treturn conversion;\n};\n\nvar convert = {};\n\nvar models = Object.keys(conversions);\n\nfunction wrapRaw(fn) {\n\tvar wrappedFn = function (args) {\n\t\tif (args === undefined || args === null) {\n\t\t\treturn args;\n\t\t}\n\n\t\tif (arguments.length > 1) {\n\t\t\targs = Array.prototype.slice.call(arguments);\n\t\t}\n\n\t\treturn fn(args);\n\t};\n\n\t// preserve .conversion property if there is one\n\tif ('conversion' in fn) {\n\t\twrappedFn.conversion = fn.conversion;\n\t}\n\n\treturn wrappedFn;\n}\n\nfunction wrapRounded(fn) {\n\tvar wrappedFn = function (args) {\n\t\tif (args === undefined || args === null) {\n\t\t\treturn args;\n\t\t}\n\n\t\tif (arguments.length > 1) {\n\t\t\targs = Array.prototype.slice.call(arguments);\n\t\t}\n\n\t\tvar result = fn(args);\n\n\t\t// we're assuming the result is an array here.\n\t\t// see notice in conversions.js; don't use box types\n\t\t// in conversion functions.\n\t\tif (typeof result === 'object') {\n\t\t\tfor (var len = result.length, i = 0; i < len; i++) {\n\t\t\t\tresult[i] = Math.round(result[i]);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t};\n\n\t// preserve .conversion property if there is one\n\tif ('conversion' in fn) {\n\t\twrappedFn.conversion = fn.conversion;\n\t}\n\n\treturn wrappedFn;\n}\n\nmodels.forEach(function (fromModel) {\n\tconvert[fromModel] = {};\n\n\tObject.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});\n\tObject.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});\n\n\tvar routes = route(fromModel);\n\tvar routeModels = Object.keys(routes);\n\n\trouteModels.forEach(function (toModel) {\n\t\tvar fn = routes[toModel];\n\n\t\tconvert[fromModel][toModel] = wrapRounded(fn);\n\t\tconvert[fromModel][toModel].raw = wrapRaw(fn);\n\t});\n});\n\nvar colorConvert = convert;\n\nvar colorName$1 = {\n\t\"aliceblue\": [240, 248, 255],\n\t\"antiquewhite\": [250, 235, 215],\n\t\"aqua\": [0, 255, 255],\n\t\"aquamarine\": [127, 255, 212],\n\t\"azure\": [240, 255, 255],\n\t\"beige\": [245, 245, 220],\n\t\"bisque\": [255, 228, 196],\n\t\"black\": [0, 0, 0],\n\t\"blanchedalmond\": [255, 235, 205],\n\t\"blue\": [0, 0, 255],\n\t\"blueviolet\": [138, 43, 226],\n\t\"brown\": [165, 42, 42],\n\t\"burlywood\": [222, 184, 135],\n\t\"cadetblue\": [95, 158, 160],\n\t\"chartreuse\": [127, 255, 0],\n\t\"chocolate\": [210, 105, 30],\n\t\"coral\": [255, 127, 80],\n\t\"cornflowerblue\": [100, 149, 237],\n\t\"cornsilk\": [255, 248, 220],\n\t\"crimson\": [220, 20, 60],\n\t\"cyan\": [0, 255, 255],\n\t\"darkblue\": [0, 0, 139],\n\t\"darkcyan\": [0, 139, 139],\n\t\"darkgoldenrod\": [184, 134, 11],\n\t\"darkgray\": [169, 169, 169],\n\t\"darkgreen\": [0, 100, 0],\n\t\"darkgrey\": [169, 169, 169],\n\t\"darkkhaki\": [189, 183, 107],\n\t\"darkmagenta\": [139, 0, 139],\n\t\"darkolivegreen\": [85, 107, 47],\n\t\"darkorange\": [255, 140, 0],\n\t\"darkorchid\": [153, 50, 204],\n\t\"darkred\": [139, 0, 0],\n\t\"darksalmon\": [233, 150, 122],\n\t\"darkseagreen\": [143, 188, 143],\n\t\"darkslateblue\": [72, 61, 139],\n\t\"darkslategray\": [47, 79, 79],\n\t\"darkslategrey\": [47, 79, 79],\n\t\"darkturquoise\": [0, 206, 209],\n\t\"darkviolet\": [148, 0, 211],\n\t\"deeppink\": [255, 20, 147],\n\t\"deepskyblue\": [0, 191, 255],\n\t\"dimgray\": [105, 105, 105],\n\t\"dimgrey\": [105, 105, 105],\n\t\"dodgerblue\": [30, 144, 255],\n\t\"firebrick\": [178, 34, 34],\n\t\"floralwhite\": [255, 250, 240],\n\t\"forestgreen\": [34, 139, 34],\n\t\"fuchsia\": [255, 0, 255],\n\t\"gainsboro\": [220, 220, 220],\n\t\"ghostwhite\": [248, 248, 255],\n\t\"gold\": [255, 215, 0],\n\t\"goldenrod\": [218, 165, 32],\n\t\"gray\": [128, 128, 128],\n\t\"green\": [0, 128, 0],\n\t\"greenyellow\": [173, 255, 47],\n\t\"grey\": [128, 128, 128],\n\t\"honeydew\": [240, 255, 240],\n\t\"hotpink\": [255, 105, 180],\n\t\"indianred\": [205, 92, 92],\n\t\"indigo\": [75, 0, 130],\n\t\"ivory\": [255, 255, 240],\n\t\"khaki\": [240, 230, 140],\n\t\"lavender\": [230, 230, 250],\n\t\"lavenderblush\": [255, 240, 245],\n\t\"lawngreen\": [124, 252, 0],\n\t\"lemonchiffon\": [255, 250, 205],\n\t\"lightblue\": [173, 216, 230],\n\t\"lightcoral\": [240, 128, 128],\n\t\"lightcyan\": [224, 255, 255],\n\t\"lightgoldenrodyellow\": [250, 250, 210],\n\t\"lightgray\": [211, 211, 211],\n\t\"lightgreen\": [144, 238, 144],\n\t\"lightgrey\": [211, 211, 211],\n\t\"lightpink\": [255, 182, 193],\n\t\"lightsalmon\": [255, 160, 122],\n\t\"lightseagreen\": [32, 178, 170],\n\t\"lightskyblue\": [135, 206, 250],\n\t\"lightslategray\": [119, 136, 153],\n\t\"lightslategrey\": [119, 136, 153],\n\t\"lightsteelblue\": [176, 196, 222],\n\t\"lightyellow\": [255, 255, 224],\n\t\"lime\": [0, 255, 0],\n\t\"limegreen\": [50, 205, 50],\n\t\"linen\": [250, 240, 230],\n\t\"magenta\": [255, 0, 255],\n\t\"maroon\": [128, 0, 0],\n\t\"mediumaquamarine\": [102, 205, 170],\n\t\"mediumblue\": [0, 0, 205],\n\t\"mediumorchid\": [186, 85, 211],\n\t\"mediumpurple\": [147, 112, 219],\n\t\"mediumseagreen\": [60, 179, 113],\n\t\"mediumslateblue\": [123, 104, 238],\n\t\"mediumspringgreen\": [0, 250, 154],\n\t\"mediumturquoise\": [72, 209, 204],\n\t\"mediumvioletred\": [199, 21, 133],\n\t\"midnightblue\": [25, 25, 112],\n\t\"mintcream\": [245, 255, 250],\n\t\"mistyrose\": [255, 228, 225],\n\t\"moccasin\": [255, 228, 181],\n\t\"navajowhite\": [255, 222, 173],\n\t\"navy\": [0, 0, 128],\n\t\"oldlace\": [253, 245, 230],\n\t\"olive\": [128, 128, 0],\n\t\"olivedrab\": [107, 142, 35],\n\t\"orange\": [255, 165, 0],\n\t\"orangered\": [255, 69, 0],\n\t\"orchid\": [218, 112, 214],\n\t\"palegoldenrod\": [238, 232, 170],\n\t\"palegreen\": [152, 251, 152],\n\t\"paleturquoise\": [175, 238, 238],\n\t\"palevioletred\": [219, 112, 147],\n\t\"papayawhip\": [255, 239, 213],\n\t\"peachpuff\": [255, 218, 185],\n\t\"peru\": [205, 133, 63],\n\t\"pink\": [255, 192, 203],\n\t\"plum\": [221, 160, 221],\n\t\"powderblue\": [176, 224, 230],\n\t\"purple\": [128, 0, 128],\n\t\"rebeccapurple\": [102, 51, 153],\n\t\"red\": [255, 0, 0],\n\t\"rosybrown\": [188, 143, 143],\n\t\"royalblue\": [65, 105, 225],\n\t\"saddlebrown\": [139, 69, 19],\n\t\"salmon\": [250, 128, 114],\n\t\"sandybrown\": [244, 164, 96],\n\t\"seagreen\": [46, 139, 87],\n\t\"seashell\": [255, 245, 238],\n\t\"sienna\": [160, 82, 45],\n\t\"silver\": [192, 192, 192],\n\t\"skyblue\": [135, 206, 235],\n\t\"slateblue\": [106, 90, 205],\n\t\"slategray\": [112, 128, 144],\n\t\"slategrey\": [112, 128, 144],\n\t\"snow\": [255, 250, 250],\n\t\"springgreen\": [0, 255, 127],\n\t\"steelblue\": [70, 130, 180],\n\t\"tan\": [210, 180, 140],\n\t\"teal\": [0, 128, 128],\n\t\"thistle\": [216, 191, 216],\n\t\"tomato\": [255, 99, 71],\n\t\"turquoise\": [64, 224, 208],\n\t\"violet\": [238, 130, 238],\n\t\"wheat\": [245, 222, 179],\n\t\"white\": [255, 255, 255],\n\t\"whitesmoke\": [245, 245, 245],\n\t\"yellow\": [255, 255, 0],\n\t\"yellowgreen\": [154, 205, 50]\n};\n\n/* MIT license */\n\n\nvar colorString = {\n getRgba: getRgba,\n getHsla: getHsla,\n getRgb: getRgb,\n getHsl: getHsl,\n getHwb: getHwb,\n getAlpha: getAlpha,\n\n hexString: hexString,\n rgbString: rgbString,\n rgbaString: rgbaString,\n percentString: percentString,\n percentaString: percentaString,\n hslString: hslString,\n hslaString: hslaString,\n hwbString: hwbString,\n keyword: keyword\n};\n\nfunction getRgba(string) {\n if (!string) {\n return;\n }\n var abbr = /^#([a-fA-F0-9]{3,4})$/i,\n hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i,\n rgba = /^rgba?\\(\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/i,\n per = /^rgba?\\(\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/i,\n keyword = /(\\w+)/;\n\n var rgb = [0, 0, 0],\n a = 1,\n match = string.match(abbr),\n hexAlpha = \"\";\n if (match) {\n match = match[1];\n hexAlpha = match[3];\n for (var i = 0; i < rgb.length; i++) {\n rgb[i] = parseInt(match[i] + match[i], 16);\n }\n if (hexAlpha) {\n a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;\n }\n }\n else if (match = string.match(hex)) {\n hexAlpha = match[2];\n match = match[1];\n for (var i = 0; i < rgb.length; i++) {\n rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);\n }\n if (hexAlpha) {\n a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;\n }\n }\n else if (match = string.match(rgba)) {\n for (var i = 0; i < rgb.length; i++) {\n rgb[i] = parseInt(match[i + 1]);\n }\n a = parseFloat(match[4]);\n }\n else if (match = string.match(per)) {\n for (var i = 0; i < rgb.length; i++) {\n rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);\n }\n a = parseFloat(match[4]);\n }\n else if (match = string.match(keyword)) {\n if (match[1] == \"transparent\") {\n return [0, 0, 0, 0];\n }\n rgb = colorName$1[match[1]];\n if (!rgb) {\n return;\n }\n }\n\n for (var i = 0; i < rgb.length; i++) {\n rgb[i] = scale(rgb[i], 0, 255);\n }\n if (!a && a != 0) {\n a = 1;\n }\n else {\n a = scale(a, 0, 1);\n }\n rgb[3] = a;\n return rgb;\n}\n\nfunction getHsla(string) {\n if (!string) {\n return;\n }\n var hsl = /^hsla?\\(\\s*([+-]?\\d+)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)/;\n var match = string.match(hsl);\n if (match) {\n var alpha = parseFloat(match[4]);\n var h = scale(parseInt(match[1]), 0, 360),\n s = scale(parseFloat(match[2]), 0, 100),\n l = scale(parseFloat(match[3]), 0, 100),\n a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);\n return [h, s, l, a];\n }\n}\n\nfunction getHwb(string) {\n if (!string) {\n return;\n }\n var hwb = /^hwb\\(\\s*([+-]?\\d+)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)/;\n var match = string.match(hwb);\n if (match) {\n var alpha = parseFloat(match[4]);\n var h = scale(parseInt(match[1]), 0, 360),\n w = scale(parseFloat(match[2]), 0, 100),\n b = scale(parseFloat(match[3]), 0, 100),\n a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);\n return [h, w, b, a];\n }\n}\n\nfunction getRgb(string) {\n var rgba = getRgba(string);\n return rgba && rgba.slice(0, 3);\n}\n\nfunction getHsl(string) {\n var hsla = getHsla(string);\n return hsla && hsla.slice(0, 3);\n}\n\nfunction getAlpha(string) {\n var vals = getRgba(string);\n if (vals) {\n return vals[3];\n }\n else if (vals = getHsla(string)) {\n return vals[3];\n }\n else if (vals = getHwb(string)) {\n return vals[3];\n }\n}\n\n// generators\nfunction hexString(rgba, a) {\n var a = (a !== undefined && rgba.length === 3) ? a : rgba[3];\n return \"#\" + hexDouble(rgba[0]) \n + hexDouble(rgba[1])\n + hexDouble(rgba[2])\n + (\n (a >= 0 && a < 1)\n ? hexDouble(Math.round(a * 255))\n : \"\"\n );\n}\n\nfunction rgbString(rgba, alpha) {\n if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {\n return rgbaString(rgba, alpha);\n }\n return \"rgb(\" + rgba[0] + \", \" + rgba[1] + \", \" + rgba[2] + \")\";\n}\n\nfunction rgbaString(rgba, alpha) {\n if (alpha === undefined) {\n alpha = (rgba[3] !== undefined ? rgba[3] : 1);\n }\n return \"rgba(\" + rgba[0] + \", \" + rgba[1] + \", \" + rgba[2]\n + \", \" + alpha + \")\";\n}\n\nfunction percentString(rgba, alpha) {\n if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {\n return percentaString(rgba, alpha);\n }\n var r = Math.round(rgba[0]/255 * 100),\n g = Math.round(rgba[1]/255 * 100),\n b = Math.round(rgba[2]/255 * 100);\n\n return \"rgb(\" + r + \"%, \" + g + \"%, \" + b + \"%)\";\n}\n\nfunction percentaString(rgba, alpha) {\n var r = Math.round(rgba[0]/255 * 100),\n g = Math.round(rgba[1]/255 * 100),\n b = Math.round(rgba[2]/255 * 100);\n return \"rgba(\" + r + \"%, \" + g + \"%, \" + b + \"%, \" + (alpha || rgba[3] || 1) + \")\";\n}\n\nfunction hslString(hsla, alpha) {\n if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {\n return hslaString(hsla, alpha);\n }\n return \"hsl(\" + hsla[0] + \", \" + hsla[1] + \"%, \" + hsla[2] + \"%)\";\n}\n\nfunction hslaString(hsla, alpha) {\n if (alpha === undefined) {\n alpha = (hsla[3] !== undefined ? hsla[3] : 1);\n }\n return \"hsla(\" + hsla[0] + \", \" + hsla[1] + \"%, \" + hsla[2] + \"%, \"\n + alpha + \")\";\n}\n\n// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax\n// (hwb have alpha optional & 1 is default value)\nfunction hwbString(hwb, alpha) {\n if (alpha === undefined) {\n alpha = (hwb[3] !== undefined ? hwb[3] : 1);\n }\n return \"hwb(\" + hwb[0] + \", \" + hwb[1] + \"%, \" + hwb[2] + \"%\"\n + (alpha !== undefined && alpha !== 1 ? \", \" + alpha : \"\") + \")\";\n}\n\nfunction keyword(rgb) {\n return reverseNames[rgb.slice(0, 3)];\n}\n\n// helpers\nfunction scale(num, min, max) {\n return Math.min(Math.max(min, num), max);\n}\n\nfunction hexDouble(num) {\n var str = num.toString(16).toUpperCase();\n return (str.length < 2) ? \"0\" + str : str;\n}\n\n\n//create a list of reverse color names\nvar reverseNames = {};\nfor (var name in colorName$1) {\n reverseNames[colorName$1[name]] = name;\n}\n\n/* MIT license */\n\n\n\nvar Color = function (obj) {\n\tif (obj instanceof Color) {\n\t\treturn obj;\n\t}\n\tif (!(this instanceof Color)) {\n\t\treturn new Color(obj);\n\t}\n\n\tthis.valid = false;\n\tthis.values = {\n\t\trgb: [0, 0, 0],\n\t\thsl: [0, 0, 0],\n\t\thsv: [0, 0, 0],\n\t\thwb: [0, 0, 0],\n\t\tcmyk: [0, 0, 0, 0],\n\t\talpha: 1\n\t};\n\n\t// parse Color() argument\n\tvar vals;\n\tif (typeof obj === 'string') {\n\t\tvals = colorString.getRgba(obj);\n\t\tif (vals) {\n\t\t\tthis.setValues('rgb', vals);\n\t\t} else if (vals = colorString.getHsla(obj)) {\n\t\t\tthis.setValues('hsl', vals);\n\t\t} else if (vals = colorString.getHwb(obj)) {\n\t\t\tthis.setValues('hwb', vals);\n\t\t}\n\t} else if (typeof obj === 'object') {\n\t\tvals = obj;\n\t\tif (vals.r !== undefined || vals.red !== undefined) {\n\t\t\tthis.setValues('rgb', vals);\n\t\t} else if (vals.l !== undefined || vals.lightness !== undefined) {\n\t\t\tthis.setValues('hsl', vals);\n\t\t} else if (vals.v !== undefined || vals.value !== undefined) {\n\t\t\tthis.setValues('hsv', vals);\n\t\t} else if (vals.w !== undefined || vals.whiteness !== undefined) {\n\t\t\tthis.setValues('hwb', vals);\n\t\t} else if (vals.c !== undefined || vals.cyan !== undefined) {\n\t\t\tthis.setValues('cmyk', vals);\n\t\t}\n\t}\n};\n\nColor.prototype = {\n\tisValid: function () {\n\t\treturn this.valid;\n\t},\n\trgb: function () {\n\t\treturn this.setSpace('rgb', arguments);\n\t},\n\thsl: function () {\n\t\treturn this.setSpace('hsl', arguments);\n\t},\n\thsv: function () {\n\t\treturn this.setSpace('hsv', arguments);\n\t},\n\thwb: function () {\n\t\treturn this.setSpace('hwb', arguments);\n\t},\n\tcmyk: function () {\n\t\treturn this.setSpace('cmyk', arguments);\n\t},\n\n\trgbArray: function () {\n\t\treturn this.values.rgb;\n\t},\n\thslArray: function () {\n\t\treturn this.values.hsl;\n\t},\n\thsvArray: function () {\n\t\treturn this.values.hsv;\n\t},\n\thwbArray: function () {\n\t\tvar values = this.values;\n\t\tif (values.alpha !== 1) {\n\t\t\treturn values.hwb.concat([values.alpha]);\n\t\t}\n\t\treturn values.hwb;\n\t},\n\tcmykArray: function () {\n\t\treturn this.values.cmyk;\n\t},\n\trgbaArray: function () {\n\t\tvar values = this.values;\n\t\treturn values.rgb.concat([values.alpha]);\n\t},\n\thslaArray: function () {\n\t\tvar values = this.values;\n\t\treturn values.hsl.concat([values.alpha]);\n\t},\n\talpha: function (val) {\n\t\tif (val === undefined) {\n\t\t\treturn this.values.alpha;\n\t\t}\n\t\tthis.setValues('alpha', val);\n\t\treturn this;\n\t},\n\n\tred: function (val) {\n\t\treturn this.setChannel('rgb', 0, val);\n\t},\n\tgreen: function (val) {\n\t\treturn this.setChannel('rgb', 1, val);\n\t},\n\tblue: function (val) {\n\t\treturn this.setChannel('rgb', 2, val);\n\t},\n\thue: function (val) {\n\t\tif (val) {\n\t\t\tval %= 360;\n\t\t\tval = val < 0 ? 360 + val : val;\n\t\t}\n\t\treturn this.setChannel('hsl', 0, val);\n\t},\n\tsaturation: function (val) {\n\t\treturn this.setChannel('hsl', 1, val);\n\t},\n\tlightness: function (val) {\n\t\treturn this.setChannel('hsl', 2, val);\n\t},\n\tsaturationv: function (val) {\n\t\treturn this.setChannel('hsv', 1, val);\n\t},\n\twhiteness: function (val) {\n\t\treturn this.setChannel('hwb', 1, val);\n\t},\n\tblackness: function (val) {\n\t\treturn this.setChannel('hwb', 2, val);\n\t},\n\tvalue: function (val) {\n\t\treturn this.setChannel('hsv', 2, val);\n\t},\n\tcyan: function (val) {\n\t\treturn this.setChannel('cmyk', 0, val);\n\t},\n\tmagenta: function (val) {\n\t\treturn this.setChannel('cmyk', 1, val);\n\t},\n\tyellow: function (val) {\n\t\treturn this.setChannel('cmyk', 2, val);\n\t},\n\tblack: function (val) {\n\t\treturn this.setChannel('cmyk', 3, val);\n\t},\n\n\thexString: function () {\n\t\treturn colorString.hexString(this.values.rgb);\n\t},\n\trgbString: function () {\n\t\treturn colorString.rgbString(this.values.rgb, this.values.alpha);\n\t},\n\trgbaString: function () {\n\t\treturn colorString.rgbaString(this.values.rgb, this.values.alpha);\n\t},\n\tpercentString: function () {\n\t\treturn colorString.percentString(this.values.rgb, this.values.alpha);\n\t},\n\thslString: function () {\n\t\treturn colorString.hslString(this.values.hsl, this.values.alpha);\n\t},\n\thslaString: function () {\n\t\treturn colorString.hslaString(this.values.hsl, this.values.alpha);\n\t},\n\thwbString: function () {\n\t\treturn colorString.hwbString(this.values.hwb, this.values.alpha);\n\t},\n\tkeyword: function () {\n\t\treturn colorString.keyword(this.values.rgb, this.values.alpha);\n\t},\n\n\trgbNumber: function () {\n\t\tvar rgb = this.values.rgb;\n\t\treturn (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];\n\t},\n\n\tluminosity: function () {\n\t\t// http://www.w3.org/TR/WCAG20/#relativeluminancedef\n\t\tvar rgb = this.values.rgb;\n\t\tvar lum = [];\n\t\tfor (var i = 0; i < rgb.length; i++) {\n\t\t\tvar chan = rgb[i] / 255;\n\t\t\tlum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);\n\t\t}\n\t\treturn 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];\n\t},\n\n\tcontrast: function (color2) {\n\t\t// http://www.w3.org/TR/WCAG20/#contrast-ratiodef\n\t\tvar lum1 = this.luminosity();\n\t\tvar lum2 = color2.luminosity();\n\t\tif (lum1 > lum2) {\n\t\t\treturn (lum1 + 0.05) / (lum2 + 0.05);\n\t\t}\n\t\treturn (lum2 + 0.05) / (lum1 + 0.05);\n\t},\n\n\tlevel: function (color2) {\n\t\tvar contrastRatio = this.contrast(color2);\n\t\tif (contrastRatio >= 7.1) {\n\t\t\treturn 'AAA';\n\t\t}\n\n\t\treturn (contrastRatio >= 4.5) ? 'AA' : '';\n\t},\n\n\tdark: function () {\n\t\t// YIQ equation from http://24ways.org/2010/calculating-color-contrast\n\t\tvar rgb = this.values.rgb;\n\t\tvar yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n\t\treturn yiq < 128;\n\t},\n\n\tlight: function () {\n\t\treturn !this.dark();\n\t},\n\n\tnegate: function () {\n\t\tvar rgb = [];\n\t\tfor (var i = 0; i < 3; i++) {\n\t\t\trgb[i] = 255 - this.values.rgb[i];\n\t\t}\n\t\tthis.setValues('rgb', rgb);\n\t\treturn this;\n\t},\n\n\tlighten: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[2] += hsl[2] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\tdarken: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[2] -= hsl[2] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\tsaturate: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[1] += hsl[1] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\tdesaturate: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[1] -= hsl[1] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\twhiten: function (ratio) {\n\t\tvar hwb = this.values.hwb;\n\t\thwb[1] += hwb[1] * ratio;\n\t\tthis.setValues('hwb', hwb);\n\t\treturn this;\n\t},\n\n\tblacken: function (ratio) {\n\t\tvar hwb = this.values.hwb;\n\t\thwb[2] += hwb[2] * ratio;\n\t\tthis.setValues('hwb', hwb);\n\t\treturn this;\n\t},\n\n\tgreyscale: function () {\n\t\tvar rgb = this.values.rgb;\n\t\t// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale\n\t\tvar val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;\n\t\tthis.setValues('rgb', [val, val, val]);\n\t\treturn this;\n\t},\n\n\tclearer: function (ratio) {\n\t\tvar alpha = this.values.alpha;\n\t\tthis.setValues('alpha', alpha - (alpha * ratio));\n\t\treturn this;\n\t},\n\n\topaquer: function (ratio) {\n\t\tvar alpha = this.values.alpha;\n\t\tthis.setValues('alpha', alpha + (alpha * ratio));\n\t\treturn this;\n\t},\n\n\trotate: function (degrees) {\n\t\tvar hsl = this.values.hsl;\n\t\tvar hue = (hsl[0] + degrees) % 360;\n\t\thsl[0] = hue < 0 ? 360 + hue : hue;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\t/**\n\t * Ported from sass implementation in C\n\t * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209\n\t */\n\tmix: function (mixinColor, weight) {\n\t\tvar color1 = this;\n\t\tvar color2 = mixinColor;\n\t\tvar p = weight === undefined ? 0.5 : weight;\n\n\t\tvar w = 2 * p - 1;\n\t\tvar a = color1.alpha() - color2.alpha();\n\n\t\tvar w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;\n\t\tvar w2 = 1 - w1;\n\n\t\treturn this\n\t\t\t.rgb(\n\t\t\t\tw1 * color1.red() + w2 * color2.red(),\n\t\t\t\tw1 * color1.green() + w2 * color2.green(),\n\t\t\t\tw1 * color1.blue() + w2 * color2.blue()\n\t\t\t)\n\t\t\t.alpha(color1.alpha() * p + color2.alpha() * (1 - p));\n\t},\n\n\ttoJSON: function () {\n\t\treturn this.rgb();\n\t},\n\n\tclone: function () {\n\t\t// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,\n\t\t// making the final build way to big to embed in Chart.js. So let's do it manually,\n\t\t// assuming that values to clone are 1 dimension arrays containing only numbers,\n\t\t// except 'alpha' which is a number.\n\t\tvar result = new Color();\n\t\tvar source = this.values;\n\t\tvar target = result.values;\n\t\tvar value, type;\n\n\t\tfor (var prop in source) {\n\t\t\tif (source.hasOwnProperty(prop)) {\n\t\t\t\tvalue = source[prop];\n\t\t\t\ttype = ({}).toString.call(value);\n\t\t\t\tif (type === '[object Array]') {\n\t\t\t\t\ttarget[prop] = value.slice(0);\n\t\t\t\t} else if (type === '[object Number]') {\n\t\t\t\t\ttarget[prop] = value;\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error('unexpected color value:', value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n};\n\nColor.prototype.spaces = {\n\trgb: ['red', 'green', 'blue'],\n\thsl: ['hue', 'saturation', 'lightness'],\n\thsv: ['hue', 'saturation', 'value'],\n\thwb: ['hue', 'whiteness', 'blackness'],\n\tcmyk: ['cyan', 'magenta', 'yellow', 'black']\n};\n\nColor.prototype.maxes = {\n\trgb: [255, 255, 255],\n\thsl: [360, 100, 100],\n\thsv: [360, 100, 100],\n\thwb: [360, 100, 100],\n\tcmyk: [100, 100, 100, 100]\n};\n\nColor.prototype.getValues = function (space) {\n\tvar values = this.values;\n\tvar vals = {};\n\n\tfor (var i = 0; i < space.length; i++) {\n\t\tvals[space.charAt(i)] = values[space][i];\n\t}\n\n\tif (values.alpha !== 1) {\n\t\tvals.a = values.alpha;\n\t}\n\n\t// {r: 255, g: 255, b: 255, a: 0.4}\n\treturn vals;\n};\n\nColor.prototype.setValues = function (space, vals) {\n\tvar values = this.values;\n\tvar spaces = this.spaces;\n\tvar maxes = this.maxes;\n\tvar alpha = 1;\n\tvar i;\n\n\tthis.valid = true;\n\n\tif (space === 'alpha') {\n\t\talpha = vals;\n\t} else if (vals.length) {\n\t\t// [10, 10, 10]\n\t\tvalues[space] = vals.slice(0, space.length);\n\t\talpha = vals[space.length];\n\t} else if (vals[space.charAt(0)] !== undefined) {\n\t\t// {r: 10, g: 10, b: 10}\n\t\tfor (i = 0; i < space.length; i++) {\n\t\t\tvalues[space][i] = vals[space.charAt(i)];\n\t\t}\n\n\t\talpha = vals.a;\n\t} else if (vals[spaces[space][0]] !== undefined) {\n\t\t// {red: 10, green: 10, blue: 10}\n\t\tvar chans = spaces[space];\n\n\t\tfor (i = 0; i < space.length; i++) {\n\t\t\tvalues[space][i] = vals[chans[i]];\n\t\t}\n\n\t\talpha = vals.alpha;\n\t}\n\n\tvalues.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));\n\n\tif (space === 'alpha') {\n\t\treturn false;\n\t}\n\n\tvar capped;\n\n\t// cap values of the space prior converting all values\n\tfor (i = 0; i < space.length; i++) {\n\t\tcapped = Math.max(0, Math.min(maxes[space][i], values[space][i]));\n\t\tvalues[space][i] = Math.round(capped);\n\t}\n\n\t// convert to all the other color spaces\n\tfor (var sname in spaces) {\n\t\tif (sname !== space) {\n\t\t\tvalues[sname] = colorConvert[space][sname](values[space]);\n\t\t}\n\t}\n\n\treturn true;\n};\n\nColor.prototype.setSpace = function (space, args) {\n\tvar vals = args[0];\n\n\tif (vals === undefined) {\n\t\t// color.rgb()\n\t\treturn this.getValues(space);\n\t}\n\n\t// color.rgb(10, 10, 10)\n\tif (typeof vals === 'number') {\n\t\tvals = Array.prototype.slice.call(args);\n\t}\n\n\tthis.setValues(space, vals);\n\treturn this;\n};\n\nColor.prototype.setChannel = function (space, index, val) {\n\tvar svalues = this.values[space];\n\tif (val === undefined) {\n\t\t// color.red()\n\t\treturn svalues[index];\n\t} else if (val === svalues[index]) {\n\t\t// color.red(color.red())\n\t\treturn this;\n\t}\n\n\t// color.red(100)\n\tsvalues[index] = val;\n\tthis.setValues(space, svalues);\n\n\treturn this;\n};\n\nif (typeof window !== 'undefined') {\n\twindow.Color = Color;\n}\n\nvar chartjsColor = Color;\n\nfunction isValidKey(key) {\n\treturn ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;\n}\n\n/**\n * @namespace Chart.helpers\n */\nvar helpers = {\n\t/**\n\t * An empty function that can be used, for example, for optional callback.\n\t */\n\tnoop: function() {},\n\n\t/**\n\t * Returns a unique id, sequentially generated from a global variable.\n\t * @returns {number}\n\t * @function\n\t */\n\tuid: (function() {\n\t\tvar id = 0;\n\t\treturn function() {\n\t\t\treturn id++;\n\t\t};\n\t}()),\n\n\t/**\n\t * Returns true if `value` is neither null nor undefined, else returns false.\n\t * @param {*} value - The value to test.\n\t * @returns {boolean}\n\t * @since 2.7.0\n\t */\n\tisNullOrUndef: function(value) {\n\t\treturn value === null || typeof value === 'undefined';\n\t},\n\n\t/**\n\t * Returns true if `value` is an array (including typed arrays), else returns false.\n\t * @param {*} value - The value to test.\n\t * @returns {boolean}\n\t * @function\n\t */\n\tisArray: function(value) {\n\t\tif (Array.isArray && Array.isArray(value)) {\n\t\t\treturn true;\n\t\t}\n\t\tvar type = Object.prototype.toString.call(value);\n\t\tif (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\n\t/**\n\t * Returns true if `value` is an object (excluding null), else returns false.\n\t * @param {*} value - The value to test.\n\t * @returns {boolean}\n\t * @since 2.7.0\n\t */\n\tisObject: function(value) {\n\t\treturn value !== null && Object.prototype.toString.call(value) === '[object Object]';\n\t},\n\n\t/**\n\t * Returns true if `value` is a finite number, else returns false\n\t * @param {*} value - The value to test.\n\t * @returns {boolean}\n\t */\n\tisFinite: function(value) {\n\t\treturn (typeof value === 'number' || value instanceof Number) && isFinite(value);\n\t},\n\n\t/**\n\t * Returns `value` if defined, else returns `defaultValue`.\n\t * @param {*} value - The value to return if defined.\n\t * @param {*} defaultValue - The value to return if `value` is undefined.\n\t * @returns {*}\n\t */\n\tvalueOrDefault: function(value, defaultValue) {\n\t\treturn typeof value === 'undefined' ? defaultValue : value;\n\t},\n\n\t/**\n\t * Returns value at the given `index` in array if defined, else returns `defaultValue`.\n\t * @param {Array} value - The array to lookup for value at `index`.\n\t * @param {number} index - The index in `value` to lookup for value.\n\t * @param {*} defaultValue - The value to return if `value[index]` is undefined.\n\t * @returns {*}\n\t */\n\tvalueAtIndexOrDefault: function(value, index, defaultValue) {\n\t\treturn helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);\n\t},\n\n\t/**\n\t * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n\t * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n\t * @param {function} fn - The function to call.\n\t * @param {Array|undefined|null} args - The arguments with which `fn` should be called.\n\t * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.\n\t * @returns {*}\n\t */\n\tcallback: function(fn, args, thisArg) {\n\t\tif (fn && typeof fn.call === 'function') {\n\t\t\treturn fn.apply(thisArg, args);\n\t\t}\n\t},\n\n\t/**\n\t * Note(SB) for performance sake, this method should only be used when loopable type\n\t * is unknown or in none intensive code (not called often and small loopable). Else\n\t * it's preferable to use a regular for() loop and save extra function calls.\n\t * @param {object|Array} loopable - The object or array to be iterated.\n\t * @param {function} fn - The function to call for each item.\n\t * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.\n\t * @param {boolean} [reverse] - If true, iterates backward on the loopable.\n\t */\n\teach: function(loopable, fn, thisArg, reverse) {\n\t\tvar i, len, keys;\n\t\tif (helpers.isArray(loopable)) {\n\t\t\tlen = loopable.length;\n\t\t\tif (reverse) {\n\t\t\t\tfor (i = len - 1; i >= 0; i--) {\n\t\t\t\t\tfn.call(thisArg, loopable[i], i);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (i = 0; i < len; i++) {\n\t\t\t\t\tfn.call(thisArg, loopable[i], i);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (helpers.isObject(loopable)) {\n\t\t\tkeys = Object.keys(loopable);\n\t\t\tlen = keys.length;\n\t\t\tfor (i = 0; i < len; i++) {\n\t\t\t\tfn.call(thisArg, loopable[keys[i]], keys[i]);\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n\t * @see https://stackoverflow.com/a/14853974\n\t * @param {Array} a0 - The array to compare\n\t * @param {Array} a1 - The array to compare\n\t * @returns {boolean}\n\t */\n\tarrayEquals: function(a0, a1) {\n\t\tvar i, ilen, v0, v1;\n\n\t\tif (!a0 || !a1 || a0.length !== a1.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (i = 0, ilen = a0.length; i < ilen; ++i) {\n\t\t\tv0 = a0[i];\n\t\t\tv1 = a1[i];\n\n\t\t\tif (v0 instanceof Array && v1 instanceof Array) {\n\t\t\t\tif (!helpers.arrayEquals(v0, v1)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else if (v0 !== v1) {\n\t\t\t\t// NOTE: two different object instances will never be equal: {x:20} != {x:20}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t},\n\n\t/**\n\t * Returns a deep copy of `source` without keeping references on objects and arrays.\n\t * @param {*} source - The value to clone.\n\t * @returns {*}\n\t */\n\tclone: function(source) {\n\t\tif (helpers.isArray(source)) {\n\t\t\treturn source.map(helpers.clone);\n\t\t}\n\n\t\tif (helpers.isObject(source)) {\n\t\t\tvar target = Object.create(source);\n\t\t\tvar keys = Object.keys(source);\n\t\t\tvar klen = keys.length;\n\t\t\tvar k = 0;\n\n\t\t\tfor (; k < klen; ++k) {\n\t\t\t\ttarget[keys[k]] = helpers.clone(source[keys[k]]);\n\t\t\t}\n\n\t\t\treturn target;\n\t\t}\n\n\t\treturn source;\n\t},\n\n\t/**\n\t * The default merger when Chart.helpers.merge is called without merger option.\n\t * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.\n\t * @private\n\t */\n\t_merger: function(key, target, source, options) {\n\t\tif (!isValidKey(key)) {\n\t\t\t// We want to ensure we do not copy prototypes over\n\t\t\t// as this can pollute global namespaces\n\t\t\treturn;\n\t\t}\n\n\t\tvar tval = target[key];\n\t\tvar sval = source[key];\n\n\t\tif (helpers.isObject(tval) && helpers.isObject(sval)) {\n\t\t\thelpers.merge(tval, sval, options);\n\t\t} else {\n\t\t\ttarget[key] = helpers.clone(sval);\n\t\t}\n\t},\n\n\t/**\n\t * Merges source[key] in target[key] only if target[key] is undefined.\n\t * @private\n\t */\n\t_mergerIf: function(key, target, source) {\n\t\tif (!isValidKey(key)) {\n\t\t\t// We want to ensure we do not copy prototypes over\n\t\t\t// as this can pollute global namespaces\n\t\t\treturn;\n\t\t}\n\n\t\tvar tval = target[key];\n\t\tvar sval = source[key];\n\n\t\tif (helpers.isObject(tval) && helpers.isObject(sval)) {\n\t\t\thelpers.mergeIf(tval, sval);\n\t\t} else if (!target.hasOwnProperty(key)) {\n\t\t\ttarget[key] = helpers.clone(sval);\n\t\t}\n\t},\n\n\t/**\n\t * Recursively deep copies `source` properties into `target` with the given `options`.\n\t * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n\t * @param {object} target - The target object in which all sources are merged into.\n\t * @param {object|object[]} source - Object(s) to merge into `target`.\n\t * @param {object} [options] - Merging options:\n\t * @param {function} [options.merger] - The merge method (key, target, source, options)\n\t * @returns {object} The `target` object.\n\t */\n\tmerge: function(target, source, options) {\n\t\tvar sources = helpers.isArray(source) ? source : [source];\n\t\tvar ilen = sources.length;\n\t\tvar merge, i, keys, klen, k;\n\n\t\tif (!helpers.isObject(target)) {\n\t\t\treturn target;\n\t\t}\n\n\t\toptions = options || {};\n\t\tmerge = options.merger || helpers._merger;\n\n\t\tfor (i = 0; i < ilen; ++i) {\n\t\t\tsource = sources[i];\n\t\t\tif (!helpers.isObject(source)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tkeys = Object.keys(source);\n\t\t\tfor (k = 0, klen = keys.length; k < klen; ++k) {\n\t\t\t\tmerge(keys[k], target, source, options);\n\t\t\t}\n\t\t}\n\n\t\treturn target;\n\t},\n\n\t/**\n\t * Recursively deep copies `source` properties into `target` *only* if not defined in target.\n\t * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n\t * @param {object} target - The target object in which all sources are merged into.\n\t * @param {object|object[]} source - Object(s) to merge into `target`.\n\t * @returns {object} The `target` object.\n\t */\n\tmergeIf: function(target, source) {\n\t\treturn helpers.merge(target, source, {merger: helpers._mergerIf});\n\t},\n\n\t/**\n\t * Applies the contents of two or more objects together into the first object.\n\t * @param {object} target - The target object in which all objects are merged into.\n\t * @param {object} arg1 - Object containing additional properties to merge in target.\n\t * @param {object} argN - Additional objects containing properties to merge in target.\n\t * @returns {object} The `target` object.\n\t */\n\textend: Object.assign || function(target) {\n\t\treturn helpers.merge(target, [].slice.call(arguments, 1), {\n\t\t\tmerger: function(key, dst, src) {\n\t\t\t\tdst[key] = src[key];\n\t\t\t}\n\t\t});\n\t},\n\n\t/**\n\t * Basic javascript inheritance based on the model created in Backbone.js\n\t */\n\tinherits: function(extensions) {\n\t\tvar me = this;\n\t\tvar ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {\n\t\t\treturn me.apply(this, arguments);\n\t\t};\n\n\t\tvar Surrogate = function() {\n\t\t\tthis.constructor = ChartElement;\n\t\t};\n\n\t\tSurrogate.prototype = me.prototype;\n\t\tChartElement.prototype = new Surrogate();\n\t\tChartElement.extend = helpers.inherits;\n\n\t\tif (extensions) {\n\t\t\thelpers.extend(ChartElement.prototype, extensions);\n\t\t}\n\n\t\tChartElement.__super__ = me.prototype;\n\t\treturn ChartElement;\n\t},\n\n\t_deprecated: function(scope, value, previous, current) {\n\t\tif (value !== undefined) {\n\t\t\tconsole.warn(scope + ': \"' + previous +\n\t\t\t\t'\" is deprecated. Please use \"' + current + '\" instead');\n\t\t}\n\t}\n};\n\nvar helpers_core = helpers;\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.helpers.callback instead.\n * @function Chart.helpers.callCallback\n * @deprecated since version 2.6.0\n * @todo remove at version 3\n * @private\n */\nhelpers.callCallback = helpers.callback;\n\n/**\n * Provided for backward compatibility, use Array.prototype.indexOf instead.\n * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+\n * @function Chart.helpers.indexOf\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.indexOf = function(array, item, fromIndex) {\n\treturn Array.prototype.indexOf.call(array, item, fromIndex);\n};\n\n/**\n * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.\n * @function Chart.helpers.getValueOrDefault\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.getValueOrDefault = helpers.valueOrDefault;\n\n/**\n * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.\n * @function Chart.helpers.getValueAtIndexOrDefault\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;\n\n/**\n * Easing functions adapted from Robert Penner's easing equations.\n * @namespace Chart.helpers.easingEffects\n * @see http://www.robertpenner.com/easing/\n */\nvar effects = {\n\tlinear: function(t) {\n\t\treturn t;\n\t},\n\n\teaseInQuad: function(t) {\n\t\treturn t * t;\n\t},\n\n\teaseOutQuad: function(t) {\n\t\treturn -t * (t - 2);\n\t},\n\n\teaseInOutQuad: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t;\n\t\t}\n\t\treturn -0.5 * ((--t) * (t - 2) - 1);\n\t},\n\n\teaseInCubic: function(t) {\n\t\treturn t * t * t;\n\t},\n\n\teaseOutCubic: function(t) {\n\t\treturn (t = t - 1) * t * t + 1;\n\t},\n\n\teaseInOutCubic: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t * t;\n\t\t}\n\t\treturn 0.5 * ((t -= 2) * t * t + 2);\n\t},\n\n\teaseInQuart: function(t) {\n\t\treturn t * t * t * t;\n\t},\n\n\teaseOutQuart: function(t) {\n\t\treturn -((t = t - 1) * t * t * t - 1);\n\t},\n\n\teaseInOutQuart: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t * t * t;\n\t\t}\n\t\treturn -0.5 * ((t -= 2) * t * t * t - 2);\n\t},\n\n\teaseInQuint: function(t) {\n\t\treturn t * t * t * t * t;\n\t},\n\n\teaseOutQuint: function(t) {\n\t\treturn (t = t - 1) * t * t * t * t + 1;\n\t},\n\n\teaseInOutQuint: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t * t * t * t;\n\t\t}\n\t\treturn 0.5 * ((t -= 2) * t * t * t * t + 2);\n\t},\n\n\teaseInSine: function(t) {\n\t\treturn -Math.cos(t * (Math.PI / 2)) + 1;\n\t},\n\n\teaseOutSine: function(t) {\n\t\treturn Math.sin(t * (Math.PI / 2));\n\t},\n\n\teaseInOutSine: function(t) {\n\t\treturn -0.5 * (Math.cos(Math.PI * t) - 1);\n\t},\n\n\teaseInExpo: function(t) {\n\t\treturn (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));\n\t},\n\n\teaseOutExpo: function(t) {\n\t\treturn (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;\n\t},\n\n\teaseInOutExpo: function(t) {\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (t === 1) {\n\t\t\treturn 1;\n\t\t}\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * Math.pow(2, 10 * (t - 1));\n\t\t}\n\t\treturn 0.5 * (-Math.pow(2, -10 * --t) + 2);\n\t},\n\n\teaseInCirc: function(t) {\n\t\tif (t >= 1) {\n\t\t\treturn t;\n\t\t}\n\t\treturn -(Math.sqrt(1 - t * t) - 1);\n\t},\n\n\teaseOutCirc: function(t) {\n\t\treturn Math.sqrt(1 - (t = t - 1) * t);\n\t},\n\n\teaseInOutCirc: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn -0.5 * (Math.sqrt(1 - t * t) - 1);\n\t\t}\n\t\treturn 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);\n\t},\n\n\teaseInElastic: function(t) {\n\t\tvar s = 1.70158;\n\t\tvar p = 0;\n\t\tvar a = 1;\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (t === 1) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (!p) {\n\t\t\tp = 0.3;\n\t\t}\n\t\tif (a < 1) {\n\t\t\ta = 1;\n\t\t\ts = p / 4;\n\t\t} else {\n\t\t\ts = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t}\n\t\treturn -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));\n\t},\n\n\teaseOutElastic: function(t) {\n\t\tvar s = 1.70158;\n\t\tvar p = 0;\n\t\tvar a = 1;\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (t === 1) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (!p) {\n\t\t\tp = 0.3;\n\t\t}\n\t\tif (a < 1) {\n\t\t\ta = 1;\n\t\t\ts = p / 4;\n\t\t} else {\n\t\t\ts = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t}\n\t\treturn a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;\n\t},\n\n\teaseInOutElastic: function(t) {\n\t\tvar s = 1.70158;\n\t\tvar p = 0;\n\t\tvar a = 1;\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif ((t /= 0.5) === 2) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (!p) {\n\t\t\tp = 0.45;\n\t\t}\n\t\tif (a < 1) {\n\t\t\ta = 1;\n\t\t\ts = p / 4;\n\t\t} else {\n\t\t\ts = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t}\n\t\tif (t < 1) {\n\t\t\treturn -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));\n\t\t}\n\t\treturn a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;\n\t},\n\teaseInBack: function(t) {\n\t\tvar s = 1.70158;\n\t\treturn t * t * ((s + 1) * t - s);\n\t},\n\n\teaseOutBack: function(t) {\n\t\tvar s = 1.70158;\n\t\treturn (t = t - 1) * t * ((s + 1) * t + s) + 1;\n\t},\n\n\teaseInOutBack: function(t) {\n\t\tvar s = 1.70158;\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));\n\t\t}\n\t\treturn 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);\n\t},\n\n\teaseInBounce: function(t) {\n\t\treturn 1 - effects.easeOutBounce(1 - t);\n\t},\n\n\teaseOutBounce: function(t) {\n\t\tif (t < (1 / 2.75)) {\n\t\t\treturn 7.5625 * t * t;\n\t\t}\n\t\tif (t < (2 / 2.75)) {\n\t\t\treturn 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;\n\t\t}\n\t\tif (t < (2.5 / 2.75)) {\n\t\t\treturn 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;\n\t\t}\n\t\treturn 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;\n\t},\n\n\teaseInOutBounce: function(t) {\n\t\tif (t < 0.5) {\n\t\t\treturn effects.easeInBounce(t * 2) * 0.5;\n\t\t}\n\t\treturn effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;\n\t}\n};\n\nvar helpers_easing = {\n\teffects: effects\n};\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.helpers.easing.effects instead.\n * @function Chart.helpers.easingEffects\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers_core.easingEffects = effects;\n\nvar PI = Math.PI;\nvar RAD_PER_DEG = PI / 180;\nvar DOUBLE_PI = PI * 2;\nvar HALF_PI = PI / 2;\nvar QUARTER_PI = PI / 4;\nvar TWO_THIRDS_PI = PI * 2 / 3;\n\n/**\n * @namespace Chart.helpers.canvas\n */\nvar exports$1 = {\n\t/**\n\t * Clears the entire canvas associated to the given `chart`.\n\t * @param {Chart} chart - The chart for which to clear the canvas.\n\t */\n\tclear: function(chart) {\n\t\tchart.ctx.clearRect(0, 0, chart.width, chart.height);\n\t},\n\n\t/**\n\t * Creates a \"path\" for a rectangle with rounded corners at position (x, y) with a\n\t * given size (width, height) and the same `radius` for all corners.\n\t * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.\n\t * @param {number} x - The x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - The y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t * @param {number} radius - The rounded amount (in pixels) for the four corners.\n\t * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?\n\t */\n\troundedRect: function(ctx, x, y, width, height, radius) {\n\t\tif (radius) {\n\t\t\tvar r = Math.min(radius, height / 2, width / 2);\n\t\t\tvar left = x + r;\n\t\t\tvar top = y + r;\n\t\t\tvar right = x + width - r;\n\t\t\tvar bottom = y + height - r;\n\n\t\t\tctx.moveTo(x, top);\n\t\t\tif (left < right && top < bottom) {\n\t\t\t\tctx.arc(left, top, r, -PI, -HALF_PI);\n\t\t\t\tctx.arc(right, top, r, -HALF_PI, 0);\n\t\t\t\tctx.arc(right, bottom, r, 0, HALF_PI);\n\t\t\t\tctx.arc(left, bottom, r, HALF_PI, PI);\n\t\t\t} else if (left < right) {\n\t\t\t\tctx.moveTo(left, y);\n\t\t\t\tctx.arc(right, top, r, -HALF_PI, HALF_PI);\n\t\t\t\tctx.arc(left, top, r, HALF_PI, PI + HALF_PI);\n\t\t\t} else if (top < bottom) {\n\t\t\t\tctx.arc(left, top, r, -PI, 0);\n\t\t\t\tctx.arc(left, bottom, r, 0, PI);\n\t\t\t} else {\n\t\t\t\tctx.arc(left, top, r, -PI, PI);\n\t\t\t}\n\t\t\tctx.closePath();\n\t\t\tctx.moveTo(x, y);\n\t\t} else {\n\t\t\tctx.rect(x, y, width, height);\n\t\t}\n\t},\n\n\tdrawPoint: function(ctx, style, radius, x, y, rotation) {\n\t\tvar type, xOffset, yOffset, size, cornerRadius;\n\t\tvar rad = (rotation || 0) * RAD_PER_DEG;\n\n\t\tif (style && typeof style === 'object') {\n\t\t\ttype = style.toString();\n\t\t\tif (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {\n\t\t\t\tctx.save();\n\t\t\t\tctx.translate(x, y);\n\t\t\t\tctx.rotate(rad);\n\t\t\t\tctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);\n\t\t\t\tctx.restore();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (isNaN(radius) || radius <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tctx.beginPath();\n\n\t\tswitch (style) {\n\t\t// Default includes circle\n\t\tdefault:\n\t\t\tctx.arc(x, y, radius, 0, DOUBLE_PI);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'triangle':\n\t\t\tctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);\n\t\t\trad += TWO_THIRDS_PI;\n\t\t\tctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);\n\t\t\trad += TWO_THIRDS_PI;\n\t\t\tctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'rectRounded':\n\t\t\t// NOTE: the rounded rect implementation changed to use `arc` instead of\n\t\t\t// `quadraticCurveTo` since it generates better results when rect is\n\t\t\t// almost a circle. 0.516 (instead of 0.5) produces results with visually\n\t\t\t// closer proportion to the previous impl and it is inscribed in the\n\t\t\t// circle with `radius`. For more details, see the following PRs:\n\t\t\t// https://github.com/chartjs/Chart.js/issues/5597\n\t\t\t// https://github.com/chartjs/Chart.js/issues/5858\n\t\t\tcornerRadius = radius * 0.516;\n\t\t\tsize = radius - cornerRadius;\n\t\t\txOffset = Math.cos(rad + QUARTER_PI) * size;\n\t\t\tyOffset = Math.sin(rad + QUARTER_PI) * size;\n\t\t\tctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);\n\t\t\tctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);\n\t\t\tctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);\n\t\t\tctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'rect':\n\t\t\tif (!rotation) {\n\t\t\t\tsize = Math.SQRT1_2 * radius;\n\t\t\t\tctx.rect(x - size, y - size, 2 * size, 2 * size);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trad += QUARTER_PI;\n\t\t\t/* falls through */\n\t\tcase 'rectRot':\n\t\t\txOffset = Math.cos(rad) * radius;\n\t\t\tyOffset = Math.sin(rad) * radius;\n\t\t\tctx.moveTo(x - xOffset, y - yOffset);\n\t\t\tctx.lineTo(x + yOffset, y - xOffset);\n\t\t\tctx.lineTo(x + xOffset, y + yOffset);\n\t\t\tctx.lineTo(x - yOffset, y + xOffset);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'crossRot':\n\t\t\trad += QUARTER_PI;\n\t\t\t/* falls through */\n\t\tcase 'cross':\n\t\t\txOffset = Math.cos(rad) * radius;\n\t\t\tyOffset = Math.sin(rad) * radius;\n\t\t\tctx.moveTo(x - xOffset, y - yOffset);\n\t\t\tctx.lineTo(x + xOffset, y + yOffset);\n\t\t\tctx.moveTo(x + yOffset, y - xOffset);\n\t\t\tctx.lineTo(x - yOffset, y + xOffset);\n\t\t\tbreak;\n\t\tcase 'star':\n\t\t\txOffset = Math.cos(rad) * radius;\n\t\t\tyOffset = Math.sin(rad) * radius;\n\t\t\tctx.moveTo(x - xOffset, y - yOffset);\n\t\t\tctx.lineTo(x + xOffset, y + yOffset);\n\t\t\tctx.moveTo(x + yOffset, y - xOffset);\n\t\t\tctx.lineTo(x - yOffset, y + xOffset);\n\t\t\trad += QUARTER_PI;\n\t\t\txOffset = Math.cos(rad) * radius;\n\t\t\tyOffset = Math.sin(rad) * radius;\n\t\t\tctx.moveTo(x - xOffset, y - yOffset);\n\t\t\tctx.lineTo(x + xOffset, y + yOffset);\n\t\t\tctx.moveTo(x + yOffset, y - xOffset);\n\t\t\tctx.lineTo(x - yOffset, y + xOffset);\n\t\t\tbreak;\n\t\tcase 'line':\n\t\t\txOffset = Math.cos(rad) * radius;\n\t\t\tyOffset = Math.sin(rad) * radius;\n\t\t\tctx.moveTo(x - xOffset, y - yOffset);\n\t\t\tctx.lineTo(x + xOffset, y + yOffset);\n\t\t\tbreak;\n\t\tcase 'dash':\n\t\t\tctx.moveTo(x, y);\n\t\t\tctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);\n\t\t\tbreak;\n\t\t}\n\n\t\tctx.fill();\n\t\tctx.stroke();\n\t},\n\n\t/**\n\t * Returns true if the point is inside the rectangle\n\t * @param {object} point - The point to test\n\t * @param {object} area - The rectangle\n\t * @returns {boolean}\n\t * @private\n\t */\n\t_isPointInArea: function(point, area) {\n\t\tvar epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.\n\n\t\treturn point.x > area.left - epsilon && point.x < area.right + epsilon &&\n\t\t\tpoint.y > area.top - epsilon && point.y < area.bottom + epsilon;\n\t},\n\n\tclipArea: function(ctx, area) {\n\t\tctx.save();\n\t\tctx.beginPath();\n\t\tctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);\n\t\tctx.clip();\n\t},\n\n\tunclipArea: function(ctx) {\n\t\tctx.restore();\n\t},\n\n\tlineTo: function(ctx, previous, target, flip) {\n\t\tvar stepped = target.steppedLine;\n\t\tif (stepped) {\n\t\t\tif (stepped === 'middle') {\n\t\t\t\tvar midpoint = (previous.x + target.x) / 2.0;\n\t\t\t\tctx.lineTo(midpoint, flip ? target.y : previous.y);\n\t\t\t\tctx.lineTo(midpoint, flip ? previous.y : target.y);\n\t\t\t} else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) {\n\t\t\t\tctx.lineTo(previous.x, target.y);\n\t\t\t} else {\n\t\t\t\tctx.lineTo(target.x, previous.y);\n\t\t\t}\n\t\t\tctx.lineTo(target.x, target.y);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!target.tension) {\n\t\t\tctx.lineTo(target.x, target.y);\n\t\t\treturn;\n\t\t}\n\n\t\tctx.bezierCurveTo(\n\t\t\tflip ? previous.controlPointPreviousX : previous.controlPointNextX,\n\t\t\tflip ? previous.controlPointPreviousY : previous.controlPointNextY,\n\t\t\tflip ? target.controlPointNextX : target.controlPointPreviousX,\n\t\t\tflip ? target.controlPointNextY : target.controlPointPreviousY,\n\t\t\ttarget.x,\n\t\t\ttarget.y);\n\t}\n};\n\nvar helpers_canvas = exports$1;\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.\n * @namespace Chart.helpers.clear\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers_core.clear = exports$1.clear;\n\n/**\n * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.\n * @namespace Chart.helpers.drawRoundedRectangle\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers_core.drawRoundedRectangle = function(ctx) {\n\tctx.beginPath();\n\texports$1.roundedRect.apply(exports$1, arguments);\n};\n\nvar defaults = {\n\t/**\n\t * @private\n\t */\n\t_set: function(scope, values) {\n\t\treturn helpers_core.merge(this[scope] || (this[scope] = {}), values);\n\t}\n};\n\n// TODO(v3): remove 'global' from namespace. all default are global and\n// there's inconsistency around which options are under 'global'\ndefaults._set('global', {\n\tdefaultColor: 'rgba(0,0,0,0.1)',\n\tdefaultFontColor: '#666',\n\tdefaultFontFamily: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n\tdefaultFontSize: 12,\n\tdefaultFontStyle: 'normal',\n\tdefaultLineHeight: 1.2,\n\tshowLines: true\n});\n\nvar core_defaults = defaults;\n\nvar valueOrDefault = helpers_core.valueOrDefault;\n\n/**\n * Converts the given font object into a CSS font string.\n * @param {object} font - A font object.\n * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font\n * @private\n */\nfunction toFontString(font) {\n\tif (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) {\n\t\treturn null;\n\t}\n\n\treturn (font.style ? font.style + ' ' : '')\n\t\t+ (font.weight ? font.weight + ' ' : '')\n\t\t+ font.size + 'px '\n\t\t+ font.family;\n}\n\n/**\n * @alias Chart.helpers.options\n * @namespace\n */\nvar helpers_options = {\n\t/**\n\t * Converts the given line height `value` in pixels for a specific font `size`.\n\t * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n\t * @param {number} size - The font size (in pixels) used to resolve relative `value`.\n\t * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid).\n\t * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n\t * @since 2.7.0\n\t */\n\ttoLineHeight: function(value, size) {\n\t\tvar matches = ('' + value).match(/^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/);\n\t\tif (!matches || matches[1] === 'normal') {\n\t\t\treturn size * 1.2;\n\t\t}\n\n\t\tvalue = +matches[2];\n\n\t\tswitch (matches[3]) {\n\t\tcase 'px':\n\t\t\treturn value;\n\t\tcase '%':\n\t\t\tvalue /= 100;\n\t\t\tbreak;\n\t\t}\n\n\t\treturn size * value;\n\t},\n\n\t/**\n\t * Converts the given value into a padding object with pre-computed width/height.\n\t * @param {number|object} value - If a number, set the value to all TRBL component,\n\t * else, if and object, use defined properties and sets undefined ones to 0.\n\t * @returns {object} The padding values (top, right, bottom, left, width, height)\n\t * @since 2.7.0\n\t */\n\ttoPadding: function(value) {\n\t\tvar t, r, b, l;\n\n\t\tif (helpers_core.isObject(value)) {\n\t\t\tt = +value.top || 0;\n\t\t\tr = +value.right || 0;\n\t\t\tb = +value.bottom || 0;\n\t\t\tl = +value.left || 0;\n\t\t} else {\n\t\t\tt = r = b = l = +value || 0;\n\t\t}\n\n\t\treturn {\n\t\t\ttop: t,\n\t\t\tright: r,\n\t\t\tbottom: b,\n\t\t\tleft: l,\n\t\t\theight: t + b,\n\t\t\twidth: l + r\n\t\t};\n\t},\n\n\t/**\n\t * Parses font options and returns the font object.\n\t * @param {object} options - A object that contains font options to be parsed.\n\t * @return {object} The font object.\n\t * @todo Support font.* options and renamed to toFont().\n\t * @private\n\t */\n\t_parseFont: function(options) {\n\t\tvar globalDefaults = core_defaults.global;\n\t\tvar size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);\n\t\tvar font = {\n\t\t\tfamily: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily),\n\t\t\tlineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size),\n\t\t\tsize: size,\n\t\t\tstyle: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle),\n\t\t\tweight: null,\n\t\t\tstring: ''\n\t\t};\n\n\t\tfont.string = toFontString(font);\n\t\treturn font;\n\t},\n\n\t/**\n\t * Evaluates the given `inputs` sequentially and returns the first defined value.\n\t * @param {Array} inputs - An array of values, falling back to the last value.\n\t * @param {object} [context] - If defined and the current value is a function, the value\n\t * is called with `context` as first argument and the result becomes the new input.\n\t * @param {number} [index] - If defined and the current value is an array, the value\n\t * at `index` become the new input.\n\t * @param {object} [info] - object to return information about resolution in\n\t * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable.\n\t * @since 2.7.0\n\t */\n\tresolve: function(inputs, context, index, info) {\n\t\tvar cacheable = true;\n\t\tvar i, ilen, value;\n\n\t\tfor (i = 0, ilen = inputs.length; i < ilen; ++i) {\n\t\t\tvalue = inputs[i];\n\t\t\tif (value === undefined) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (context !== undefined && typeof value === 'function') {\n\t\t\t\tvalue = value(context);\n\t\t\t\tcacheable = false;\n\t\t\t}\n\t\t\tif (index !== undefined && helpers_core.isArray(value)) {\n\t\t\t\tvalue = value[index];\n\t\t\t\tcacheable = false;\n\t\t\t}\n\t\t\tif (value !== undefined) {\n\t\t\t\tif (info && !cacheable) {\n\t\t\t\t\tinfo.cacheable = false;\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * @alias Chart.helpers.math\n * @namespace\n */\nvar exports$2 = {\n\t/**\n\t * Returns an array of factors sorted from 1 to sqrt(value)\n\t * @private\n\t */\n\t_factorize: function(value) {\n\t\tvar result = [];\n\t\tvar sqrt = Math.sqrt(value);\n\t\tvar i;\n\n\t\tfor (i = 1; i < sqrt; i++) {\n\t\t\tif (value % i === 0) {\n\t\t\t\tresult.push(i);\n\t\t\t\tresult.push(value / i);\n\t\t\t}\n\t\t}\n\t\tif (sqrt === (sqrt | 0)) { // if value is a square number\n\t\t\tresult.push(sqrt);\n\t\t}\n\n\t\tresult.sort(function(a, b) {\n\t\t\treturn a - b;\n\t\t}).pop();\n\t\treturn result;\n\t},\n\n\tlog10: Math.log10 || function(x) {\n\t\tvar exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.\n\t\t// Check for whole powers of 10,\n\t\t// which due to floating point rounding error should be corrected.\n\t\tvar powerOf10 = Math.round(exponent);\n\t\tvar isPowerOf10 = x === Math.pow(10, powerOf10);\n\n\t\treturn isPowerOf10 ? powerOf10 : exponent;\n\t}\n};\n\nvar helpers_math = exports$2;\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.helpers.math.log10 instead.\n * @namespace Chart.helpers.log10\n * @deprecated since version 2.9.0\n * @todo remove at version 3\n * @private\n */\nhelpers_core.log10 = exports$2.log10;\n\nvar getRtlAdapter = function(rectX, width) {\n\treturn {\n\t\tx: function(x) {\n\t\t\treturn rectX + rectX + width - x;\n\t\t},\n\t\tsetWidth: function(w) {\n\t\t\twidth = w;\n\t\t},\n\t\ttextAlign: function(align) {\n\t\t\tif (align === 'center') {\n\t\t\t\treturn align;\n\t\t\t}\n\t\t\treturn align === 'right' ? 'left' : 'right';\n\t\t},\n\t\txPlus: function(x, value) {\n\t\t\treturn x - value;\n\t\t},\n\t\tleftForLtr: function(x, itemWidth) {\n\t\t\treturn x - itemWidth;\n\t\t},\n\t};\n};\n\nvar getLtrAdapter = function() {\n\treturn {\n\t\tx: function(x) {\n\t\t\treturn x;\n\t\t},\n\t\tsetWidth: function(w) { // eslint-disable-line no-unused-vars\n\t\t},\n\t\ttextAlign: function(align) {\n\t\t\treturn align;\n\t\t},\n\t\txPlus: function(x, value) {\n\t\t\treturn x + value;\n\t\t},\n\t\tleftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars\n\t\t\treturn x;\n\t\t},\n\t};\n};\n\nvar getAdapter = function(rtl, rectX, width) {\n\treturn rtl ? getRtlAdapter(rectX, width) : getLtrAdapter();\n};\n\nvar overrideTextDirection = function(ctx, direction) {\n\tvar style, original;\n\tif (direction === 'ltr' || direction === 'rtl') {\n\t\tstyle = ctx.canvas.style;\n\t\toriginal = [\n\t\t\tstyle.getPropertyValue('direction'),\n\t\t\tstyle.getPropertyPriority('direction'),\n\t\t];\n\n\t\tstyle.setProperty('direction', direction, 'important');\n\t\tctx.prevTextDirection = original;\n\t}\n};\n\nvar restoreTextDirection = function(ctx) {\n\tvar original = ctx.prevTextDirection;\n\tif (original !== undefined) {\n\t\tdelete ctx.prevTextDirection;\n\t\tctx.canvas.style.setProperty('direction', original[0], original[1]);\n\t}\n};\n\nvar helpers_rtl = {\n\tgetRtlAdapter: getAdapter,\n\toverrideTextDirection: overrideTextDirection,\n\trestoreTextDirection: restoreTextDirection,\n};\n\nvar helpers$1 = helpers_core;\nvar easing = helpers_easing;\nvar canvas = helpers_canvas;\nvar options = helpers_options;\nvar math = helpers_math;\nvar rtl = helpers_rtl;\nhelpers$1.easing = easing;\nhelpers$1.canvas = canvas;\nhelpers$1.options = options;\nhelpers$1.math = math;\nhelpers$1.rtl = rtl;\n\nfunction interpolate(start, view, model, ease) {\n\tvar keys = Object.keys(model);\n\tvar i, ilen, key, actual, origin, target, type, c0, c1;\n\n\tfor (i = 0, ilen = keys.length; i < ilen; ++i) {\n\t\tkey = keys[i];\n\n\t\ttarget = model[key];\n\n\t\t// if a value is added to the model after pivot() has been called, the view\n\t\t// doesn't contain it, so let's initialize the view to the target value.\n\t\tif (!view.hasOwnProperty(key)) {\n\t\t\tview[key] = target;\n\t\t}\n\n\t\tactual = view[key];\n\n\t\tif (actual === target || key[0] === '_') {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!start.hasOwnProperty(key)) {\n\t\t\tstart[key] = actual;\n\t\t}\n\n\t\torigin = start[key];\n\n\t\ttype = typeof target;\n\n\t\tif (type === typeof origin) {\n\t\t\tif (type === 'string') {\n\t\t\t\tc0 = chartjsColor(origin);\n\t\t\t\tif (c0.valid) {\n\t\t\t\t\tc1 = chartjsColor(target);\n\t\t\t\t\tif (c1.valid) {\n\t\t\t\t\t\tview[key] = c1.mix(c0, ease).rgbString();\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) {\n\t\t\t\tview[key] = origin + (target - origin) * ease;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tview[key] = target;\n\t}\n}\n\nvar Element = function(configuration) {\n\thelpers$1.extend(this, configuration);\n\tthis.initialize.apply(this, arguments);\n};\n\nhelpers$1.extend(Element.prototype, {\n\t_type: undefined,\n\n\tinitialize: function() {\n\t\tthis.hidden = false;\n\t},\n\n\tpivot: function() {\n\t\tvar me = this;\n\t\tif (!me._view) {\n\t\t\tme._view = helpers$1.extend({}, me._model);\n\t\t}\n\t\tme._start = {};\n\t\treturn me;\n\t},\n\n\ttransition: function(ease) {\n\t\tvar me = this;\n\t\tvar model = me._model;\n\t\tvar start = me._start;\n\t\tvar view = me._view;\n\n\t\t// No animation -> No Transition\n\t\tif (!model || ease === 1) {\n\t\t\tme._view = helpers$1.extend({}, model);\n\t\t\tme._start = null;\n\t\t\treturn me;\n\t\t}\n\n\t\tif (!view) {\n\t\t\tview = me._view = {};\n\t\t}\n\n\t\tif (!start) {\n\t\t\tstart = me._start = {};\n\t\t}\n\n\t\tinterpolate(start, view, model, ease);\n\n\t\treturn me;\n\t},\n\n\ttooltipPosition: function() {\n\t\treturn {\n\t\t\tx: this._model.x,\n\t\t\ty: this._model.y\n\t\t};\n\t},\n\n\thasValue: function() {\n\t\treturn helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y);\n\t}\n});\n\nElement.extend = helpers$1.inherits;\n\nvar core_element = Element;\n\nvar exports$3 = core_element.extend({\n\tchart: null, // the animation associated chart instance\n\tcurrentStep: 0, // the current animation step\n\tnumSteps: 60, // default number of steps\n\teasing: '', // the easing to use for this animation\n\trender: null, // render function used by the animation service\n\n\tonAnimationProgress: null, // user specified callback to fire on each step of the animation\n\tonAnimationComplete: null, // user specified callback to fire when the animation finishes\n});\n\nvar core_animation = exports$3;\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.Animation instead\n * @prop Chart.Animation#animationObject\n * @deprecated since version 2.6.0\n * @todo remove at version 3\n */\nObject.defineProperty(exports$3.prototype, 'animationObject', {\n\tget: function() {\n\t\treturn this;\n\t}\n});\n\n/**\n * Provided for backward compatibility, use Chart.Animation#chart instead\n * @prop Chart.Animation#chartInstance\n * @deprecated since version 2.6.0\n * @todo remove at version 3\n */\nObject.defineProperty(exports$3.prototype, 'chartInstance', {\n\tget: function() {\n\t\treturn this.chart;\n\t},\n\tset: function(value) {\n\t\tthis.chart = value;\n\t}\n});\n\ncore_defaults._set('global', {\n\tanimation: {\n\t\tduration: 1000,\n\t\teasing: 'easeOutQuart',\n\t\tonProgress: helpers$1.noop,\n\t\tonComplete: helpers$1.noop\n\t}\n});\n\nvar core_animations = {\n\tanimations: [],\n\trequest: null,\n\n\t/**\n\t * @param {Chart} chart - The chart to animate.\n\t * @param {Chart.Animation} animation - The animation that we will animate.\n\t * @param {number} duration - The animation duration in ms.\n\t * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions\n\t */\n\taddAnimation: function(chart, animation, duration, lazy) {\n\t\tvar animations = this.animations;\n\t\tvar i, ilen;\n\n\t\tanimation.chart = chart;\n\t\tanimation.startTime = Date.now();\n\t\tanimation.duration = duration;\n\n\t\tif (!lazy) {\n\t\t\tchart.animating = true;\n\t\t}\n\n\t\tfor (i = 0, ilen = animations.length; i < ilen; ++i) {\n\t\t\tif (animations[i].chart === chart) {\n\t\t\t\tanimations[i] = animation;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tanimations.push(animation);\n\n\t\t// If there are no animations queued, manually kickstart a digest, for lack of a better word\n\t\tif (animations.length === 1) {\n\t\t\tthis.requestAnimationFrame();\n\t\t}\n\t},\n\n\tcancelAnimation: function(chart) {\n\t\tvar index = helpers$1.findIndex(this.animations, function(animation) {\n\t\t\treturn animation.chart === chart;\n\t\t});\n\n\t\tif (index !== -1) {\n\t\t\tthis.animations.splice(index, 1);\n\t\t\tchart.animating = false;\n\t\t}\n\t},\n\n\trequestAnimationFrame: function() {\n\t\tvar me = this;\n\t\tif (me.request === null) {\n\t\t\t// Skip animation frame requests until the active one is executed.\n\t\t\t// This can happen when processing mouse events, e.g. 'mousemove'\n\t\t\t// and 'mouseout' events will trigger multiple renders.\n\t\t\tme.request = helpers$1.requestAnimFrame.call(window, function() {\n\t\t\t\tme.request = null;\n\t\t\t\tme.startDigest();\n\t\t\t});\n\t\t}\n\t},\n\n\t/**\n\t * @private\n\t */\n\tstartDigest: function() {\n\t\tvar me = this;\n\n\t\tme.advance();\n\n\t\t// Do we have more stuff to animate?\n\t\tif (me.animations.length > 0) {\n\t\t\tme.requestAnimationFrame();\n\t\t}\n\t},\n\n\t/**\n\t * @private\n\t */\n\tadvance: function() {\n\t\tvar animations = this.animations;\n\t\tvar animation, chart, numSteps, nextStep;\n\t\tvar i = 0;\n\n\t\t// 1 animation per chart, so we are looping charts here\n\t\twhile (i < animations.length) {\n\t\t\tanimation = animations[i];\n\t\t\tchart = animation.chart;\n\t\t\tnumSteps = animation.numSteps;\n\n\t\t\t// Make sure that currentStep starts at 1\n\t\t\t// https://github.com/chartjs/Chart.js/issues/6104\n\t\t\tnextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1;\n\t\t\tanimation.currentStep = Math.min(nextStep, numSteps);\n\n\t\t\thelpers$1.callback(animation.render, [chart, animation], chart);\n\t\t\thelpers$1.callback(animation.onAnimationProgress, [animation], chart);\n\n\t\t\tif (animation.currentStep >= numSteps) {\n\t\t\t\thelpers$1.callback(animation.onAnimationComplete, [animation], chart);\n\t\t\t\tchart.animating = false;\n\t\t\t\tanimations.splice(i, 1);\n\t\t\t} else {\n\t\t\t\t++i;\n\t\t\t}\n\t\t}\n\t}\n};\n\nvar resolve = helpers$1.options.resolve;\n\nvar arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];\n\n/**\n * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',\n * 'unshift') and notify the listener AFTER the array has been altered. Listeners are\n * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.\n */\nfunction listenArrayEvents(array, listener) {\n\tif (array._chartjs) {\n\t\tarray._chartjs.listeners.push(listener);\n\t\treturn;\n\t}\n\n\tObject.defineProperty(array, '_chartjs', {\n\t\tconfigurable: true,\n\t\tenumerable: false,\n\t\tvalue: {\n\t\t\tlisteners: [listener]\n\t\t}\n\t});\n\n\tarrayEvents.forEach(function(key) {\n\t\tvar method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);\n\t\tvar base = array[key];\n\n\t\tObject.defineProperty(array, key, {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: false,\n\t\t\tvalue: function() {\n\t\t\t\tvar args = Array.prototype.slice.call(arguments);\n\t\t\t\tvar res = base.apply(this, args);\n\n\t\t\t\thelpers$1.each(array._chartjs.listeners, function(object) {\n\t\t\t\t\tif (typeof object[method] === 'function') {\n\t\t\t\t\t\tobject[method].apply(object, args);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn res;\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * Removes the given array event listener and cleanup extra attached properties (such as\n * the _chartjs stub and overridden methods) if array doesn't have any more listeners.\n */\nfunction unlistenArrayEvents(array, listener) {\n\tvar stub = array._chartjs;\n\tif (!stub) {\n\t\treturn;\n\t}\n\n\tvar listeners = stub.listeners;\n\tvar index = listeners.indexOf(listener);\n\tif (index !== -1) {\n\t\tlisteners.splice(index, 1);\n\t}\n\n\tif (listeners.length > 0) {\n\t\treturn;\n\t}\n\n\tarrayEvents.forEach(function(key) {\n\t\tdelete array[key];\n\t});\n\n\tdelete array._chartjs;\n}\n\n// Base class for all dataset controllers (line, bar, etc)\nvar DatasetController = function(chart, datasetIndex) {\n\tthis.initialize(chart, datasetIndex);\n};\n\nhelpers$1.extend(DatasetController.prototype, {\n\n\t/**\n\t * Element type used to generate a meta dataset (e.g. Chart.element.Line).\n\t * @type {Chart.core.element}\n\t */\n\tdatasetElementType: null,\n\n\t/**\n\t * Element type used to generate a meta data (e.g. Chart.element.Point).\n\t * @type {Chart.core.element}\n\t */\n\tdataElementType: null,\n\n\t/**\n\t * Dataset element option keys to be resolved in _resolveDatasetElementOptions.\n\t * A derived controller may override this to resolve controller-specific options.\n\t * The keys defined here are for backward compatibility for legend styles.\n\t * @private\n\t */\n\t_datasetElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderCapStyle',\n\t\t'borderColor',\n\t\t'borderDash',\n\t\t'borderDashOffset',\n\t\t'borderJoinStyle',\n\t\t'borderWidth'\n\t],\n\n\t/**\n\t * Data element option keys to be resolved in _resolveDataElementOptions.\n\t * A derived controller may override this to resolve controller-specific options.\n\t * The keys defined here are for backward compatibility for legend styles.\n\t * @private\n\t */\n\t_dataElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderColor',\n\t\t'borderWidth',\n\t\t'pointStyle'\n\t],\n\n\tinitialize: function(chart, datasetIndex) {\n\t\tvar me = this;\n\t\tme.chart = chart;\n\t\tme.index = datasetIndex;\n\t\tme.linkScales();\n\t\tme.addElements();\n\t\tme._type = me.getMeta().type;\n\t},\n\n\tupdateIndex: function(datasetIndex) {\n\t\tthis.index = datasetIndex;\n\t},\n\n\tlinkScales: function() {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar chart = me.chart;\n\t\tvar scales = chart.scales;\n\t\tvar dataset = me.getDataset();\n\t\tvar scalesOpts = chart.options.scales;\n\n\t\tif (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) {\n\t\t\tmeta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id;\n\t\t}\n\t\tif (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) {\n\t\t\tmeta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id;\n\t\t}\n\t},\n\n\tgetDataset: function() {\n\t\treturn this.chart.data.datasets[this.index];\n\t},\n\n\tgetMeta: function() {\n\t\treturn this.chart.getDatasetMeta(this.index);\n\t},\n\n\tgetScaleForId: function(scaleID) {\n\t\treturn this.chart.scales[scaleID];\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getValueScaleId: function() {\n\t\treturn this.getMeta().yAxisID;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getIndexScaleId: function() {\n\t\treturn this.getMeta().xAxisID;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getValueScale: function() {\n\t\treturn this.getScaleForId(this._getValueScaleId());\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getIndexScale: function() {\n\t\treturn this.getScaleForId(this._getIndexScaleId());\n\t},\n\n\treset: function() {\n\t\tthis._update(true);\n\t},\n\n\t/**\n\t * @private\n\t */\n\tdestroy: function() {\n\t\tif (this._data) {\n\t\t\tunlistenArrayEvents(this._data, this);\n\t\t}\n\t},\n\n\tcreateMetaDataset: function() {\n\t\tvar me = this;\n\t\tvar type = me.datasetElementType;\n\t\treturn type && new type({\n\t\t\t_chart: me.chart,\n\t\t\t_datasetIndex: me.index\n\t\t});\n\t},\n\n\tcreateMetaData: function(index) {\n\t\tvar me = this;\n\t\tvar type = me.dataElementType;\n\t\treturn type && new type({\n\t\t\t_chart: me.chart,\n\t\t\t_datasetIndex: me.index,\n\t\t\t_index: index\n\t\t});\n\t},\n\n\taddElements: function() {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar data = me.getDataset().data || [];\n\t\tvar metaData = meta.data;\n\t\tvar i, ilen;\n\n\t\tfor (i = 0, ilen = data.length; i < ilen; ++i) {\n\t\t\tmetaData[i] = metaData[i] || me.createMetaData(i);\n\t\t}\n\n\t\tmeta.dataset = meta.dataset || me.createMetaDataset();\n\t},\n\n\taddElementAndReset: function(index) {\n\t\tvar element = this.createMetaData(index);\n\t\tthis.getMeta().data.splice(index, 0, element);\n\t\tthis.updateElement(element, index, true);\n\t},\n\n\tbuildOrUpdateElements: function() {\n\t\tvar me = this;\n\t\tvar dataset = me.getDataset();\n\t\tvar data = dataset.data || (dataset.data = []);\n\n\t\t// In order to correctly handle data addition/deletion animation (an thus simulate\n\t\t// real-time charts), we need to monitor these data modifications and synchronize\n\t\t// the internal meta data accordingly.\n\t\tif (me._data !== data) {\n\t\t\tif (me._data) {\n\t\t\t\t// This case happens when the user replaced the data array instance.\n\t\t\t\tunlistenArrayEvents(me._data, me);\n\t\t\t}\n\n\t\t\tif (data && Object.isExtensible(data)) {\n\t\t\t\tlistenArrayEvents(data, me);\n\t\t\t}\n\t\t\tme._data = data;\n\t\t}\n\n\t\t// Re-sync meta data in case the user replaced the data array or if we missed\n\t\t// any updates and so make sure that we handle number of datapoints changing.\n\t\tme.resyncElements();\n\t},\n\n\t/**\n\t * Returns the merged user-supplied and default dataset-level options\n\t * @private\n\t */\n\t_configure: function() {\n\t\tvar me = this;\n\t\tme._config = helpers$1.merge(Object.create(null), [\n\t\t\tme.chart.options.datasets[me._type],\n\t\t\tme.getDataset(),\n\t\t], {\n\t\t\tmerger: function(key, target, source) {\n\t\t\t\tif (key !== '_meta' && key !== 'data') {\n\t\t\t\t\thelpers$1._merger(key, target, source);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t},\n\n\t_update: function(reset) {\n\t\tvar me = this;\n\t\tme._configure();\n\t\tme._cachedDataOpts = null;\n\t\tme.update(reset);\n\t},\n\n\tupdate: helpers$1.noop,\n\n\ttransition: function(easingValue) {\n\t\tvar meta = this.getMeta();\n\t\tvar elements = meta.data || [];\n\t\tvar ilen = elements.length;\n\t\tvar i = 0;\n\n\t\tfor (; i < ilen; ++i) {\n\t\t\telements[i].transition(easingValue);\n\t\t}\n\n\t\tif (meta.dataset) {\n\t\t\tmeta.dataset.transition(easingValue);\n\t\t}\n\t},\n\n\tdraw: function() {\n\t\tvar meta = this.getMeta();\n\t\tvar elements = meta.data || [];\n\t\tvar ilen = elements.length;\n\t\tvar i = 0;\n\n\t\tif (meta.dataset) {\n\t\t\tmeta.dataset.draw();\n\t\t}\n\n\t\tfor (; i < ilen; ++i) {\n\t\t\telements[i].draw();\n\t\t}\n\t},\n\n\t/**\n\t * Returns a set of predefined style properties that should be used to represent the dataset\n\t * or the data if the index is specified\n\t * @param {number} index - data index\n\t * @return {IStyleInterface} style object\n\t */\n\tgetStyle: function(index) {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar dataset = meta.dataset;\n\t\tvar style;\n\n\t\tme._configure();\n\t\tif (dataset && index === undefined) {\n\t\t\tstyle = me._resolveDatasetElementOptions(dataset || {});\n\t\t} else {\n\t\t\tindex = index || 0;\n\t\t\tstyle = me._resolveDataElementOptions(meta.data[index] || {}, index);\n\t\t}\n\n\t\tif (style.fill === false || style.fill === null) {\n\t\t\tstyle.backgroundColor = style.borderColor;\n\t\t}\n\n\t\treturn style;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_resolveDatasetElementOptions: function(element, hover) {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar datasetOpts = me._config;\n\t\tvar custom = element.custom || {};\n\t\tvar options = chart.options.elements[me.datasetElementType.prototype._type] || {};\n\t\tvar elementOptions = me._datasetElementOptions;\n\t\tvar values = {};\n\t\tvar i, ilen, key, readKey;\n\n\t\t// Scriptable options\n\t\tvar context = {\n\t\t\tchart: chart,\n\t\t\tdataset: me.getDataset(),\n\t\t\tdatasetIndex: me.index,\n\t\t\thover: hover\n\t\t};\n\n\t\tfor (i = 0, ilen = elementOptions.length; i < ilen; ++i) {\n\t\t\tkey = elementOptions[i];\n\t\t\treadKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;\n\t\t\tvalues[key] = resolve([\n\t\t\t\tcustom[readKey],\n\t\t\t\tdatasetOpts[readKey],\n\t\t\t\toptions[readKey]\n\t\t\t], context);\n\t\t}\n\n\t\treturn values;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_resolveDataElementOptions: function(element, index) {\n\t\tvar me = this;\n\t\tvar custom = element && element.custom;\n\t\tvar cached = me._cachedDataOpts;\n\t\tif (cached && !custom) {\n\t\t\treturn cached;\n\t\t}\n\t\tvar chart = me.chart;\n\t\tvar datasetOpts = me._config;\n\t\tvar options = chart.options.elements[me.dataElementType.prototype._type] || {};\n\t\tvar elementOptions = me._dataElementOptions;\n\t\tvar values = {};\n\n\t\t// Scriptable options\n\t\tvar context = {\n\t\t\tchart: chart,\n\t\t\tdataIndex: index,\n\t\t\tdataset: me.getDataset(),\n\t\t\tdatasetIndex: me.index\n\t\t};\n\n\t\t// `resolve` sets cacheable to `false` if any option is indexed or scripted\n\t\tvar info = {cacheable: !custom};\n\n\t\tvar keys, i, ilen, key;\n\n\t\tcustom = custom || {};\n\n\t\tif (helpers$1.isArray(elementOptions)) {\n\t\t\tfor (i = 0, ilen = elementOptions.length; i < ilen; ++i) {\n\t\t\t\tkey = elementOptions[i];\n\t\t\t\tvalues[key] = resolve([\n\t\t\t\t\tcustom[key],\n\t\t\t\t\tdatasetOpts[key],\n\t\t\t\t\toptions[key]\n\t\t\t\t], context, index, info);\n\t\t\t}\n\t\t} else {\n\t\t\tkeys = Object.keys(elementOptions);\n\t\t\tfor (i = 0, ilen = keys.length; i < ilen; ++i) {\n\t\t\t\tkey = keys[i];\n\t\t\t\tvalues[key] = resolve([\n\t\t\t\t\tcustom[key],\n\t\t\t\t\tdatasetOpts[elementOptions[key]],\n\t\t\t\t\tdatasetOpts[key],\n\t\t\t\t\toptions[key]\n\t\t\t\t], context, index, info);\n\t\t\t}\n\t\t}\n\n\t\tif (info.cacheable) {\n\t\t\tme._cachedDataOpts = Object.freeze(values);\n\t\t}\n\n\t\treturn values;\n\t},\n\n\tremoveHoverStyle: function(element) {\n\t\thelpers$1.merge(element._model, element.$previousStyle || {});\n\t\tdelete element.$previousStyle;\n\t},\n\n\tsetHoverStyle: function(element) {\n\t\tvar dataset = this.chart.data.datasets[element._datasetIndex];\n\t\tvar index = element._index;\n\t\tvar custom = element.custom || {};\n\t\tvar model = element._model;\n\t\tvar getHoverColor = helpers$1.getHoverColor;\n\n\t\telement.$previousStyle = {\n\t\t\tbackgroundColor: model.backgroundColor,\n\t\t\tborderColor: model.borderColor,\n\t\t\tborderWidth: model.borderWidth\n\t\t};\n\n\t\tmodel.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index);\n\t\tmodel.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index);\n\t\tmodel.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index);\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_removeDatasetHoverStyle: function() {\n\t\tvar element = this.getMeta().dataset;\n\n\t\tif (element) {\n\t\t\tthis.removeHoverStyle(element);\n\t\t}\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_setDatasetHoverStyle: function() {\n\t\tvar element = this.getMeta().dataset;\n\t\tvar prev = {};\n\t\tvar i, ilen, key, keys, hoverOptions, model;\n\n\t\tif (!element) {\n\t\t\treturn;\n\t\t}\n\n\t\tmodel = element._model;\n\t\thoverOptions = this._resolveDatasetElementOptions(element, true);\n\n\t\tkeys = Object.keys(hoverOptions);\n\t\tfor (i = 0, ilen = keys.length; i < ilen; ++i) {\n\t\t\tkey = keys[i];\n\t\t\tprev[key] = model[key];\n\t\t\tmodel[key] = hoverOptions[key];\n\t\t}\n\n\t\telement.$previousStyle = prev;\n\t},\n\n\t/**\n\t * @private\n\t */\n\tresyncElements: function() {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar data = me.getDataset().data;\n\t\tvar numMeta = meta.data.length;\n\t\tvar numData = data.length;\n\n\t\tif (numData < numMeta) {\n\t\t\tmeta.data.splice(numData, numMeta - numData);\n\t\t} else if (numData > numMeta) {\n\t\t\tme.insertElements(numMeta, numData - numMeta);\n\t\t}\n\t},\n\n\t/**\n\t * @private\n\t */\n\tinsertElements: function(start, count) {\n\t\tfor (var i = 0; i < count; ++i) {\n\t\t\tthis.addElementAndReset(start + i);\n\t\t}\n\t},\n\n\t/**\n\t * @private\n\t */\n\tonDataPush: function() {\n\t\tvar count = arguments.length;\n\t\tthis.insertElements(this.getDataset().data.length - count, count);\n\t},\n\n\t/**\n\t * @private\n\t */\n\tonDataPop: function() {\n\t\tthis.getMeta().data.pop();\n\t},\n\n\t/**\n\t * @private\n\t */\n\tonDataShift: function() {\n\t\tthis.getMeta().data.shift();\n\t},\n\n\t/**\n\t * @private\n\t */\n\tonDataSplice: function(start, count) {\n\t\tthis.getMeta().data.splice(start, count);\n\t\tthis.insertElements(start, arguments.length - 2);\n\t},\n\n\t/**\n\t * @private\n\t */\n\tonDataUnshift: function() {\n\t\tthis.insertElements(0, arguments.length);\n\t}\n});\n\nDatasetController.extend = helpers$1.inherits;\n\nvar core_datasetController = DatasetController;\n\nvar TAU = Math.PI * 2;\n\ncore_defaults._set('global', {\n\telements: {\n\t\tarc: {\n\t\t\tbackgroundColor: core_defaults.global.defaultColor,\n\t\t\tborderColor: '#fff',\n\t\t\tborderWidth: 2,\n\t\t\tborderAlign: 'center'\n\t\t}\n\t}\n});\n\nfunction clipArc(ctx, arc) {\n\tvar startAngle = arc.startAngle;\n\tvar endAngle = arc.endAngle;\n\tvar pixelMargin = arc.pixelMargin;\n\tvar angleMargin = pixelMargin / arc.outerRadius;\n\tvar x = arc.x;\n\tvar y = arc.y;\n\n\t// Draw an inner border by cliping the arc and drawing a double-width border\n\t// Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders\n\tctx.beginPath();\n\tctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin);\n\tif (arc.innerRadius > pixelMargin) {\n\t\tangleMargin = pixelMargin / arc.innerRadius;\n\t\tctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true);\n\t} else {\n\t\tctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2);\n\t}\n\tctx.closePath();\n\tctx.clip();\n}\n\nfunction drawFullCircleBorders(ctx, vm, arc, inner) {\n\tvar endAngle = arc.endAngle;\n\tvar i;\n\n\tif (inner) {\n\t\tarc.endAngle = arc.startAngle + TAU;\n\t\tclipArc(ctx, arc);\n\t\tarc.endAngle = endAngle;\n\t\tif (arc.endAngle === arc.startAngle && arc.fullCircles) {\n\t\t\tarc.endAngle += TAU;\n\t\t\tarc.fullCircles--;\n\t\t}\n\t}\n\n\tctx.beginPath();\n\tctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true);\n\tfor (i = 0; i < arc.fullCircles; ++i) {\n\t\tctx.stroke();\n\t}\n\n\tctx.beginPath();\n\tctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU);\n\tfor (i = 0; i < arc.fullCircles; ++i) {\n\t\tctx.stroke();\n\t}\n}\n\nfunction drawBorder(ctx, vm, arc) {\n\tvar inner = vm.borderAlign === 'inner';\n\n\tif (inner) {\n\t\tctx.lineWidth = vm.borderWidth * 2;\n\t\tctx.lineJoin = 'round';\n\t} else {\n\t\tctx.lineWidth = vm.borderWidth;\n\t\tctx.lineJoin = 'bevel';\n\t}\n\n\tif (arc.fullCircles) {\n\t\tdrawFullCircleBorders(ctx, vm, arc, inner);\n\t}\n\n\tif (inner) {\n\t\tclipArc(ctx, arc);\n\t}\n\n\tctx.beginPath();\n\tctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle);\n\tctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);\n\tctx.closePath();\n\tctx.stroke();\n}\n\nvar element_arc = core_element.extend({\n\t_type: 'arc',\n\n\tinLabelRange: function(mouseX) {\n\t\tvar vm = this._view;\n\n\t\tif (vm) {\n\t\t\treturn (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));\n\t\t}\n\t\treturn false;\n\t},\n\n\tinRange: function(chartX, chartY) {\n\t\tvar vm = this._view;\n\n\t\tif (vm) {\n\t\t\tvar pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY});\n\t\t\tvar angle = pointRelativePosition.angle;\n\t\t\tvar distance = pointRelativePosition.distance;\n\n\t\t\t// Sanitise angle range\n\t\t\tvar startAngle = vm.startAngle;\n\t\t\tvar endAngle = vm.endAngle;\n\t\t\twhile (endAngle < startAngle) {\n\t\t\t\tendAngle += TAU;\n\t\t\t}\n\t\t\twhile (angle > endAngle) {\n\t\t\t\tangle -= TAU;\n\t\t\t}\n\t\t\twhile (angle < startAngle) {\n\t\t\t\tangle += TAU;\n\t\t\t}\n\n\t\t\t// Check if within the range of the open/close angle\n\t\t\tvar betweenAngles = (angle >= startAngle && angle <= endAngle);\n\t\t\tvar withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);\n\n\t\t\treturn (betweenAngles && withinRadius);\n\t\t}\n\t\treturn false;\n\t},\n\n\tgetCenterPoint: function() {\n\t\tvar vm = this._view;\n\t\tvar halfAngle = (vm.startAngle + vm.endAngle) / 2;\n\t\tvar halfRadius = (vm.innerRadius + vm.outerRadius) / 2;\n\t\treturn {\n\t\t\tx: vm.x + Math.cos(halfAngle) * halfRadius,\n\t\t\ty: vm.y + Math.sin(halfAngle) * halfRadius\n\t\t};\n\t},\n\n\tgetArea: function() {\n\t\tvar vm = this._view;\n\t\treturn Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));\n\t},\n\n\ttooltipPosition: function() {\n\t\tvar vm = this._view;\n\t\tvar centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);\n\t\tvar rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;\n\n\t\treturn {\n\t\t\tx: vm.x + (Math.cos(centreAngle) * rangeFromCentre),\n\t\t\ty: vm.y + (Math.sin(centreAngle) * rangeFromCentre)\n\t\t};\n\t},\n\n\tdraw: function() {\n\t\tvar ctx = this._chart.ctx;\n\t\tvar vm = this._view;\n\t\tvar pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;\n\t\tvar arc = {\n\t\t\tx: vm.x,\n\t\t\ty: vm.y,\n\t\t\tinnerRadius: vm.innerRadius,\n\t\t\touterRadius: Math.max(vm.outerRadius - pixelMargin, 0),\n\t\t\tpixelMargin: pixelMargin,\n\t\t\tstartAngle: vm.startAngle,\n\t\t\tendAngle: vm.endAngle,\n\t\t\tfullCircles: Math.floor(vm.circumference / TAU)\n\t\t};\n\t\tvar i;\n\n\t\tctx.save();\n\n\t\tctx.fillStyle = vm.backgroundColor;\n\t\tctx.strokeStyle = vm.borderColor;\n\n\t\tif (arc.fullCircles) {\n\t\t\tarc.endAngle = arc.startAngle + TAU;\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);\n\t\t\tctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);\n\t\t\tctx.closePath();\n\t\t\tfor (i = 0; i < arc.fullCircles; ++i) {\n\t\t\t\tctx.fill();\n\t\t\t}\n\t\t\tarc.endAngle = arc.startAngle + vm.circumference % TAU;\n\t\t}\n\n\t\tctx.beginPath();\n\t\tctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);\n\t\tctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);\n\t\tctx.closePath();\n\t\tctx.fill();\n\n\t\tif (vm.borderWidth) {\n\t\t\tdrawBorder(ctx, vm, arc);\n\t\t}\n\n\t\tctx.restore();\n\t}\n});\n\nvar valueOrDefault$1 = helpers$1.valueOrDefault;\n\nvar defaultColor = core_defaults.global.defaultColor;\n\ncore_defaults._set('global', {\n\telements: {\n\t\tline: {\n\t\t\ttension: 0.4,\n\t\t\tbackgroundColor: defaultColor,\n\t\t\tborderWidth: 3,\n\t\t\tborderColor: defaultColor,\n\t\t\tborderCapStyle: 'butt',\n\t\t\tborderDash: [],\n\t\t\tborderDashOffset: 0.0,\n\t\t\tborderJoinStyle: 'miter',\n\t\t\tcapBezierPoints: true,\n\t\t\tfill: true, // do we fill in the area between the line and its base axis\n\t\t}\n\t}\n});\n\nvar element_line = core_element.extend({\n\t_type: 'line',\n\n\tdraw: function() {\n\t\tvar me = this;\n\t\tvar vm = me._view;\n\t\tvar ctx = me._chart.ctx;\n\t\tvar spanGaps = vm.spanGaps;\n\t\tvar points = me._children.slice(); // clone array\n\t\tvar globalDefaults = core_defaults.global;\n\t\tvar globalOptionLineElements = globalDefaults.elements.line;\n\t\tvar lastDrawnIndex = -1;\n\t\tvar closePath = me._loop;\n\t\tvar index, previous, currentVM;\n\n\t\tif (!points.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (me._loop) {\n\t\t\tfor (index = 0; index < points.length; ++index) {\n\t\t\t\tprevious = helpers$1.previousItem(points, index);\n\t\t\t\t// If the line has an open path, shift the point array\n\t\t\t\tif (!points[index]._view.skip && previous._view.skip) {\n\t\t\t\t\tpoints = points.slice(index).concat(points.slice(0, index));\n\t\t\t\t\tclosePath = spanGaps;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If the line has a close path, add the first point again\n\t\t\tif (closePath) {\n\t\t\t\tpoints.push(points[0]);\n\t\t\t}\n\t\t}\n\n\t\tctx.save();\n\n\t\t// Stroke Line Options\n\t\tctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;\n\n\t\t// IE 9 and 10 do not support line dash\n\t\tif (ctx.setLineDash) {\n\t\t\tctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);\n\t\t}\n\n\t\tctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset);\n\t\tctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;\n\t\tctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth);\n\t\tctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;\n\n\t\t// Stroke Line\n\t\tctx.beginPath();\n\n\t\t// First point moves to it's starting position no matter what\n\t\tcurrentVM = points[0]._view;\n\t\tif (!currentVM.skip) {\n\t\t\tctx.moveTo(currentVM.x, currentVM.y);\n\t\t\tlastDrawnIndex = 0;\n\t\t}\n\n\t\tfor (index = 1; index < points.length; ++index) {\n\t\t\tcurrentVM = points[index]._view;\n\t\t\tprevious = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex];\n\n\t\t\tif (!currentVM.skip) {\n\t\t\t\tif ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {\n\t\t\t\t\t// There was a gap and this is the first point after the gap\n\t\t\t\t\tctx.moveTo(currentVM.x, currentVM.y);\n\t\t\t\t} else {\n\t\t\t\t\t// Line to next point\n\t\t\t\t\thelpers$1.canvas.lineTo(ctx, previous._view, currentVM);\n\t\t\t\t}\n\t\t\t\tlastDrawnIndex = index;\n\t\t\t}\n\t\t}\n\n\t\tif (closePath) {\n\t\t\tctx.closePath();\n\t\t}\n\n\t\tctx.stroke();\n\t\tctx.restore();\n\t}\n});\n\nvar valueOrDefault$2 = helpers$1.valueOrDefault;\n\nvar defaultColor$1 = core_defaults.global.defaultColor;\n\ncore_defaults._set('global', {\n\telements: {\n\t\tpoint: {\n\t\t\tradius: 3,\n\t\t\tpointStyle: 'circle',\n\t\t\tbackgroundColor: defaultColor$1,\n\t\t\tborderColor: defaultColor$1,\n\t\t\tborderWidth: 1,\n\t\t\t// Hover\n\t\t\thitRadius: 1,\n\t\t\thoverRadius: 4,\n\t\t\thoverBorderWidth: 1\n\t\t}\n\t}\n});\n\nfunction xRange(mouseX) {\n\tvar vm = this._view;\n\treturn vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;\n}\n\nfunction yRange(mouseY) {\n\tvar vm = this._view;\n\treturn vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;\n}\n\nvar element_point = core_element.extend({\n\t_type: 'point',\n\n\tinRange: function(mouseX, mouseY) {\n\t\tvar vm = this._view;\n\t\treturn vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;\n\t},\n\n\tinLabelRange: xRange,\n\tinXRange: xRange,\n\tinYRange: yRange,\n\n\tgetCenterPoint: function() {\n\t\tvar vm = this._view;\n\t\treturn {\n\t\t\tx: vm.x,\n\t\t\ty: vm.y\n\t\t};\n\t},\n\n\tgetArea: function() {\n\t\treturn Math.PI * Math.pow(this._view.radius, 2);\n\t},\n\n\ttooltipPosition: function() {\n\t\tvar vm = this._view;\n\t\treturn {\n\t\t\tx: vm.x,\n\t\t\ty: vm.y,\n\t\t\tpadding: vm.radius + vm.borderWidth\n\t\t};\n\t},\n\n\tdraw: function(chartArea) {\n\t\tvar vm = this._view;\n\t\tvar ctx = this._chart.ctx;\n\t\tvar pointStyle = vm.pointStyle;\n\t\tvar rotation = vm.rotation;\n\t\tvar radius = vm.radius;\n\t\tvar x = vm.x;\n\t\tvar y = vm.y;\n\t\tvar globalDefaults = core_defaults.global;\n\t\tvar defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow\n\n\t\tif (vm.skip) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Clipping for Points.\n\t\tif (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) {\n\t\t\tctx.strokeStyle = vm.borderColor || defaultColor;\n\t\t\tctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth);\n\t\t\tctx.fillStyle = vm.backgroundColor || defaultColor;\n\t\t\thelpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);\n\t\t}\n\t}\n});\n\nvar defaultColor$2 = core_defaults.global.defaultColor;\n\ncore_defaults._set('global', {\n\telements: {\n\t\trectangle: {\n\t\t\tbackgroundColor: defaultColor$2,\n\t\t\tborderColor: defaultColor$2,\n\t\t\tborderSkipped: 'bottom',\n\t\t\tborderWidth: 0\n\t\t}\n\t}\n});\n\nfunction isVertical(vm) {\n\treturn vm && vm.width !== undefined;\n}\n\n/**\n * Helper function to get the bounds of the bar regardless of the orientation\n * @param bar {Chart.Element.Rectangle} the bar\n * @return {Bounds} bounds of the bar\n * @private\n */\nfunction getBarBounds(vm) {\n\tvar x1, x2, y1, y2, half;\n\n\tif (isVertical(vm)) {\n\t\thalf = vm.width / 2;\n\t\tx1 = vm.x - half;\n\t\tx2 = vm.x + half;\n\t\ty1 = Math.min(vm.y, vm.base);\n\t\ty2 = Math.max(vm.y, vm.base);\n\t} else {\n\t\thalf = vm.height / 2;\n\t\tx1 = Math.min(vm.x, vm.base);\n\t\tx2 = Math.max(vm.x, vm.base);\n\t\ty1 = vm.y - half;\n\t\ty2 = vm.y + half;\n\t}\n\n\treturn {\n\t\tleft: x1,\n\t\ttop: y1,\n\t\tright: x2,\n\t\tbottom: y2\n\t};\n}\n\nfunction swap(orig, v1, v2) {\n\treturn orig === v1 ? v2 : orig === v2 ? v1 : orig;\n}\n\nfunction parseBorderSkipped(vm) {\n\tvar edge = vm.borderSkipped;\n\tvar res = {};\n\n\tif (!edge) {\n\t\treturn res;\n\t}\n\n\tif (vm.horizontal) {\n\t\tif (vm.base > vm.x) {\n\t\t\tedge = swap(edge, 'left', 'right');\n\t\t}\n\t} else if (vm.base < vm.y) {\n\t\tedge = swap(edge, 'bottom', 'top');\n\t}\n\n\tres[edge] = true;\n\treturn res;\n}\n\nfunction parseBorderWidth(vm, maxW, maxH) {\n\tvar value = vm.borderWidth;\n\tvar skip = parseBorderSkipped(vm);\n\tvar t, r, b, l;\n\n\tif (helpers$1.isObject(value)) {\n\t\tt = +value.top || 0;\n\t\tr = +value.right || 0;\n\t\tb = +value.bottom || 0;\n\t\tl = +value.left || 0;\n\t} else {\n\t\tt = r = b = l = +value || 0;\n\t}\n\n\treturn {\n\t\tt: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t,\n\t\tr: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r,\n\t\tb: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b,\n\t\tl: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l\n\t};\n}\n\nfunction boundingRects(vm) {\n\tvar bounds = getBarBounds(vm);\n\tvar width = bounds.right - bounds.left;\n\tvar height = bounds.bottom - bounds.top;\n\tvar border = parseBorderWidth(vm, width / 2, height / 2);\n\n\treturn {\n\t\touter: {\n\t\t\tx: bounds.left,\n\t\t\ty: bounds.top,\n\t\t\tw: width,\n\t\t\th: height\n\t\t},\n\t\tinner: {\n\t\t\tx: bounds.left + border.l,\n\t\t\ty: bounds.top + border.t,\n\t\t\tw: width - border.l - border.r,\n\t\t\th: height - border.t - border.b\n\t\t}\n\t};\n}\n\nfunction inRange(vm, x, y) {\n\tvar skipX = x === null;\n\tvar skipY = y === null;\n\tvar bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm);\n\n\treturn bounds\n\t\t&& (skipX || x >= bounds.left && x <= bounds.right)\n\t\t&& (skipY || y >= bounds.top && y <= bounds.bottom);\n}\n\nvar element_rectangle = core_element.extend({\n\t_type: 'rectangle',\n\n\tdraw: function() {\n\t\tvar ctx = this._chart.ctx;\n\t\tvar vm = this._view;\n\t\tvar rects = boundingRects(vm);\n\t\tvar outer = rects.outer;\n\t\tvar inner = rects.inner;\n\n\t\tctx.fillStyle = vm.backgroundColor;\n\t\tctx.fillRect(outer.x, outer.y, outer.w, outer.h);\n\n\t\tif (outer.w === inner.w && outer.h === inner.h) {\n\t\t\treturn;\n\t\t}\n\n\t\tctx.save();\n\t\tctx.beginPath();\n\t\tctx.rect(outer.x, outer.y, outer.w, outer.h);\n\t\tctx.clip();\n\t\tctx.fillStyle = vm.borderColor;\n\t\tctx.rect(inner.x, inner.y, inner.w, inner.h);\n\t\tctx.fill('evenodd');\n\t\tctx.restore();\n\t},\n\n\theight: function() {\n\t\tvar vm = this._view;\n\t\treturn vm.base - vm.y;\n\t},\n\n\tinRange: function(mouseX, mouseY) {\n\t\treturn inRange(this._view, mouseX, mouseY);\n\t},\n\n\tinLabelRange: function(mouseX, mouseY) {\n\t\tvar vm = this._view;\n\t\treturn isVertical(vm)\n\t\t\t? inRange(vm, mouseX, null)\n\t\t\t: inRange(vm, null, mouseY);\n\t},\n\n\tinXRange: function(mouseX) {\n\t\treturn inRange(this._view, mouseX, null);\n\t},\n\n\tinYRange: function(mouseY) {\n\t\treturn inRange(this._view, null, mouseY);\n\t},\n\n\tgetCenterPoint: function() {\n\t\tvar vm = this._view;\n\t\tvar x, y;\n\t\tif (isVertical(vm)) {\n\t\t\tx = vm.x;\n\t\t\ty = (vm.y + vm.base) / 2;\n\t\t} else {\n\t\t\tx = (vm.x + vm.base) / 2;\n\t\t\ty = vm.y;\n\t\t}\n\n\t\treturn {x: x, y: y};\n\t},\n\n\tgetArea: function() {\n\t\tvar vm = this._view;\n\n\t\treturn isVertical(vm)\n\t\t\t? vm.width * Math.abs(vm.y - vm.base)\n\t\t\t: vm.height * Math.abs(vm.x - vm.base);\n\t},\n\n\ttooltipPosition: function() {\n\t\tvar vm = this._view;\n\t\treturn {\n\t\t\tx: vm.x,\n\t\t\ty: vm.y\n\t\t};\n\t}\n});\n\nvar elements = {};\nvar Arc = element_arc;\nvar Line = element_line;\nvar Point = element_point;\nvar Rectangle = element_rectangle;\nelements.Arc = Arc;\nelements.Line = Line;\nelements.Point = Point;\nelements.Rectangle = Rectangle;\n\nvar deprecated = helpers$1._deprecated;\nvar valueOrDefault$3 = helpers$1.valueOrDefault;\n\ncore_defaults._set('bar', {\n\thover: {\n\t\tmode: 'label'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'category',\n\t\t\toffset: true,\n\t\t\tgridLines: {\n\t\t\t\toffsetGridLines: true\n\t\t\t}\n\t\t}],\n\n\t\tyAxes: [{\n\t\t\ttype: 'linear'\n\t\t}]\n\t}\n});\n\ncore_defaults._set('global', {\n\tdatasets: {\n\t\tbar: {\n\t\t\tcategoryPercentage: 0.8,\n\t\t\tbarPercentage: 0.9\n\t\t}\n\t}\n});\n\n/**\n * Computes the \"optimal\" sample size to maintain bars equally sized while preventing overlap.\n * @private\n */\nfunction computeMinSampleSize(scale, pixels) {\n\tvar min = scale._length;\n\tvar prev, curr, i, ilen;\n\n\tfor (i = 1, ilen = pixels.length; i < ilen; ++i) {\n\t\tmin = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));\n\t}\n\n\tfor (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) {\n\t\tcurr = scale.getPixelForTick(i);\n\t\tmin = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;\n\t\tprev = curr;\n\t}\n\n\treturn min;\n}\n\n/**\n * Computes an \"ideal\" category based on the absolute bar thickness or, if undefined or null,\n * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This\n * mode currently always generates bars equally sized (until we introduce scriptable options?).\n * @private\n */\nfunction computeFitCategoryTraits(index, ruler, options) {\n\tvar thickness = options.barThickness;\n\tvar count = ruler.stackCount;\n\tvar curr = ruler.pixels[index];\n\tvar min = helpers$1.isNullOrUndef(thickness)\n\t\t? computeMinSampleSize(ruler.scale, ruler.pixels)\n\t\t: -1;\n\tvar size, ratio;\n\n\tif (helpers$1.isNullOrUndef(thickness)) {\n\t\tsize = min * options.categoryPercentage;\n\t\tratio = options.barPercentage;\n\t} else {\n\t\t// When bar thickness is enforced, category and bar percentages are ignored.\n\t\t// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')\n\t\t// and deprecate barPercentage since this value is ignored when thickness is absolute.\n\t\tsize = thickness * count;\n\t\tratio = 1;\n\t}\n\n\treturn {\n\t\tchunk: size / count,\n\t\tratio: ratio,\n\t\tstart: curr - (size / 2)\n\t};\n}\n\n/**\n * Computes an \"optimal\" category that globally arranges bars side by side (no gap when\n * percentage options are 1), based on the previous and following categories. This mode\n * generates bars with different widths when data are not evenly spaced.\n * @private\n */\nfunction computeFlexCategoryTraits(index, ruler, options) {\n\tvar pixels = ruler.pixels;\n\tvar curr = pixels[index];\n\tvar prev = index > 0 ? pixels[index - 1] : null;\n\tvar next = index < pixels.length - 1 ? pixels[index + 1] : null;\n\tvar percent = options.categoryPercentage;\n\tvar start, size;\n\n\tif (prev === null) {\n\t\t// first data: its size is double based on the next point or,\n\t\t// if it's also the last data, we use the scale size.\n\t\tprev = curr - (next === null ? ruler.end - ruler.start : next - curr);\n\t}\n\n\tif (next === null) {\n\t\t// last data: its size is also double based on the previous point.\n\t\tnext = curr + curr - prev;\n\t}\n\n\tstart = curr - (curr - Math.min(prev, next)) / 2 * percent;\n\tsize = Math.abs(next - prev) / 2 * percent;\n\n\treturn {\n\t\tchunk: size / ruler.stackCount,\n\t\tratio: options.barPercentage,\n\t\tstart: start\n\t};\n}\n\nvar controller_bar = core_datasetController.extend({\n\n\tdataElementType: elements.Rectangle,\n\n\t/**\n\t * @private\n\t */\n\t_dataElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderColor',\n\t\t'borderSkipped',\n\t\t'borderWidth',\n\t\t'barPercentage',\n\t\t'barThickness',\n\t\t'categoryPercentage',\n\t\t'maxBarThickness',\n\t\t'minBarLength'\n\t],\n\n\tinitialize: function() {\n\t\tvar me = this;\n\t\tvar meta, scaleOpts;\n\n\t\tcore_datasetController.prototype.initialize.apply(me, arguments);\n\n\t\tmeta = me.getMeta();\n\t\tmeta.stack = me.getDataset().stack;\n\t\tmeta.bar = true;\n\n\t\tscaleOpts = me._getIndexScale().options;\n\t\tdeprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage');\n\t\tdeprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness');\n\t\tdeprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage');\n\t\tdeprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength');\n\t\tdeprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness');\n\t},\n\n\tupdate: function(reset) {\n\t\tvar me = this;\n\t\tvar rects = me.getMeta().data;\n\t\tvar i, ilen;\n\n\t\tme._ruler = me.getRuler();\n\n\t\tfor (i = 0, ilen = rects.length; i < ilen; ++i) {\n\t\t\tme.updateElement(rects[i], i, reset);\n\t\t}\n\t},\n\n\tupdateElement: function(rectangle, index, reset) {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar dataset = me.getDataset();\n\t\tvar options = me._resolveDataElementOptions(rectangle, index);\n\n\t\trectangle._xScale = me.getScaleForId(meta.xAxisID);\n\t\trectangle._yScale = me.getScaleForId(meta.yAxisID);\n\t\trectangle._datasetIndex = me.index;\n\t\trectangle._index = index;\n\t\trectangle._model = {\n\t\t\tbackgroundColor: options.backgroundColor,\n\t\t\tborderColor: options.borderColor,\n\t\t\tborderSkipped: options.borderSkipped,\n\t\t\tborderWidth: options.borderWidth,\n\t\t\tdatasetLabel: dataset.label,\n\t\t\tlabel: me.chart.data.labels[index]\n\t\t};\n\n\t\tif (helpers$1.isArray(dataset.data[index])) {\n\t\t\trectangle._model.borderSkipped = null;\n\t\t}\n\n\t\tme._updateElementGeometry(rectangle, index, reset, options);\n\n\t\trectangle.pivot();\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_updateElementGeometry: function(rectangle, index, reset, options) {\n\t\tvar me = this;\n\t\tvar model = rectangle._model;\n\t\tvar vscale = me._getValueScale();\n\t\tvar base = vscale.getBasePixel();\n\t\tvar horizontal = vscale.isHorizontal();\n\t\tvar ruler = me._ruler || me.getRuler();\n\t\tvar vpixels = me.calculateBarValuePixels(me.index, index, options);\n\t\tvar ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options);\n\n\t\tmodel.horizontal = horizontal;\n\t\tmodel.base = reset ? base : vpixels.base;\n\t\tmodel.x = horizontal ? reset ? base : vpixels.head : ipixels.center;\n\t\tmodel.y = horizontal ? ipixels.center : reset ? base : vpixels.head;\n\t\tmodel.height = horizontal ? ipixels.size : undefined;\n\t\tmodel.width = horizontal ? undefined : ipixels.size;\n\t},\n\n\t/**\n\t * Returns the stacks based on groups and bar visibility.\n\t * @param {number} [last] - The dataset index\n\t * @returns {string[]} The list of stack IDs\n\t * @private\n\t */\n\t_getStacks: function(last) {\n\t\tvar me = this;\n\t\tvar scale = me._getIndexScale();\n\t\tvar metasets = scale._getMatchingVisibleMetas(me._type);\n\t\tvar stacked = scale.options.stacked;\n\t\tvar ilen = metasets.length;\n\t\tvar stacks = [];\n\t\tvar i, meta;\n\n\t\tfor (i = 0; i < ilen; ++i) {\n\t\t\tmeta = metasets[i];\n\t\t\t// stacked | meta.stack\n\t\t\t// | found | not found | undefined\n\t\t\t// false | x | x | x\n\t\t\t// true | | x |\n\t\t\t// undefined | | x | x\n\t\t\tif (stacked === false || stacks.indexOf(meta.stack) === -1 ||\n\t\t\t\t(stacked === undefined && meta.stack === undefined)) {\n\t\t\t\tstacks.push(meta.stack);\n\t\t\t}\n\t\t\tif (meta.index === last) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn stacks;\n\t},\n\n\t/**\n\t * Returns the effective number of stacks based on groups and bar visibility.\n\t * @private\n\t */\n\tgetStackCount: function() {\n\t\treturn this._getStacks().length;\n\t},\n\n\t/**\n\t * Returns the stack index for the given dataset based on groups and bar visibility.\n\t * @param {number} [datasetIndex] - The dataset index\n\t * @param {string} [name] - The stack name to find\n\t * @returns {number} The stack index\n\t * @private\n\t */\n\tgetStackIndex: function(datasetIndex, name) {\n\t\tvar stacks = this._getStacks(datasetIndex);\n\t\tvar index = (name !== undefined)\n\t\t\t? stacks.indexOf(name)\n\t\t\t: -1; // indexOf returns -1 if element is not present\n\n\t\treturn (index === -1)\n\t\t\t? stacks.length - 1\n\t\t\t: index;\n\t},\n\n\t/**\n\t * @private\n\t */\n\tgetRuler: function() {\n\t\tvar me = this;\n\t\tvar scale = me._getIndexScale();\n\t\tvar pixels = [];\n\t\tvar i, ilen;\n\n\t\tfor (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {\n\t\t\tpixels.push(scale.getPixelForValue(null, i, me.index));\n\t\t}\n\n\t\treturn {\n\t\t\tpixels: pixels,\n\t\t\tstart: scale._startPixel,\n\t\t\tend: scale._endPixel,\n\t\t\tstackCount: me.getStackCount(),\n\t\t\tscale: scale\n\t\t};\n\t},\n\n\t/**\n\t * Note: pixel values are not clamped to the scale area.\n\t * @private\n\t */\n\tcalculateBarValuePixels: function(datasetIndex, index, options) {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar scale = me._getValueScale();\n\t\tvar isHorizontal = scale.isHorizontal();\n\t\tvar datasets = chart.data.datasets;\n\t\tvar metasets = scale._getMatchingVisibleMetas(me._type);\n\t\tvar value = scale._parseValue(datasets[datasetIndex].data[index]);\n\t\tvar minBarLength = options.minBarLength;\n\t\tvar stacked = scale.options.stacked;\n\t\tvar stack = me.getMeta().stack;\n\t\tvar start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max;\n\t\tvar length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max;\n\t\tvar ilen = metasets.length;\n\t\tvar i, imeta, ivalue, base, head, size, stackLength;\n\n\t\tif (stacked || (stacked === undefined && stack !== undefined)) {\n\t\t\tfor (i = 0; i < ilen; ++i) {\n\t\t\t\timeta = metasets[i];\n\n\t\t\t\tif (imeta.index === datasetIndex) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (imeta.stack === stack) {\n\t\t\t\t\tstackLength = scale._parseValue(datasets[imeta.index].data[index]);\n\t\t\t\t\tivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min;\n\n\t\t\t\t\tif ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) {\n\t\t\t\t\t\tstart += ivalue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tbase = scale.getPixelForValue(start);\n\t\thead = scale.getPixelForValue(start + length);\n\t\tsize = head - base;\n\n\t\tif (minBarLength !== undefined && Math.abs(size) < minBarLength) {\n\t\t\tsize = minBarLength;\n\t\t\tif (length >= 0 && !isHorizontal || length < 0 && isHorizontal) {\n\t\t\t\thead = base - minBarLength;\n\t\t\t} else {\n\t\t\t\thead = base + minBarLength;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tsize: size,\n\t\t\tbase: base,\n\t\t\thead: head,\n\t\t\tcenter: head + size / 2\n\t\t};\n\t},\n\n\t/**\n\t * @private\n\t */\n\tcalculateBarIndexPixels: function(datasetIndex, index, ruler, options) {\n\t\tvar me = this;\n\t\tvar range = options.barThickness === 'flex'\n\t\t\t? computeFlexCategoryTraits(index, ruler, options)\n\t\t\t: computeFitCategoryTraits(index, ruler, options);\n\n\t\tvar stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);\n\t\tvar center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);\n\t\tvar size = Math.min(\n\t\t\tvalueOrDefault$3(options.maxBarThickness, Infinity),\n\t\t\trange.chunk * range.ratio);\n\n\t\treturn {\n\t\t\tbase: center - size / 2,\n\t\t\thead: center + size / 2,\n\t\t\tcenter: center,\n\t\t\tsize: size\n\t\t};\n\t},\n\n\tdraw: function() {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar scale = me._getValueScale();\n\t\tvar rects = me.getMeta().data;\n\t\tvar dataset = me.getDataset();\n\t\tvar ilen = rects.length;\n\t\tvar i = 0;\n\n\t\thelpers$1.canvas.clipArea(chart.ctx, chart.chartArea);\n\n\t\tfor (; i < ilen; ++i) {\n\t\t\tvar val = scale._parseValue(dataset.data[i]);\n\t\t\tif (!isNaN(val.min) && !isNaN(val.max)) {\n\t\t\t\trects[i].draw();\n\t\t\t}\n\t\t}\n\n\t\thelpers$1.canvas.unclipArea(chart.ctx);\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_resolveDataElementOptions: function() {\n\t\tvar me = this;\n\t\tvar values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments));\n\t\tvar indexOpts = me._getIndexScale().options;\n\t\tvar valueOpts = me._getValueScale().options;\n\n\t\tvalues.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage);\n\t\tvalues.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness);\n\t\tvalues.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage);\n\t\tvalues.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness);\n\t\tvalues.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength);\n\n\t\treturn values;\n\t}\n\n});\n\nvar valueOrDefault$4 = helpers$1.valueOrDefault;\nvar resolve$1 = helpers$1.options.resolve;\n\ncore_defaults._set('bubble', {\n\thover: {\n\t\tmode: 'single'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'linear', // bubble should probably use a linear scale by default\n\t\t\tposition: 'bottom',\n\t\t\tid: 'x-axis-0' // need an ID so datasets can reference the scale\n\t\t}],\n\t\tyAxes: [{\n\t\t\ttype: 'linear',\n\t\t\tposition: 'left',\n\t\t\tid: 'y-axis-0'\n\t\t}]\n\t},\n\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\t// Title doesn't make sense for scatter since we format the data as a point\n\t\t\t\treturn '';\n\t\t\t},\n\t\t\tlabel: function(item, data) {\n\t\t\t\tvar datasetLabel = data.datasets[item.datasetIndex].label || '';\n\t\t\t\tvar dataPoint = data.datasets[item.datasetIndex].data[item.index];\n\t\t\t\treturn datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';\n\t\t\t}\n\t\t}\n\t}\n});\n\nvar controller_bubble = core_datasetController.extend({\n\t/**\n\t * @protected\n\t */\n\tdataElementType: elements.Point,\n\n\t/**\n\t * @private\n\t */\n\t_dataElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderColor',\n\t\t'borderWidth',\n\t\t'hoverBackgroundColor',\n\t\t'hoverBorderColor',\n\t\t'hoverBorderWidth',\n\t\t'hoverRadius',\n\t\t'hitRadius',\n\t\t'pointStyle',\n\t\t'rotation'\n\t],\n\n\t/**\n\t * @protected\n\t */\n\tupdate: function(reset) {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar points = meta.data;\n\n\t\t// Update Points\n\t\thelpers$1.each(points, function(point, index) {\n\t\t\tme.updateElement(point, index, reset);\n\t\t});\n\t},\n\n\t/**\n\t * @protected\n\t */\n\tupdateElement: function(point, index, reset) {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar custom = point.custom || {};\n\t\tvar xScale = me.getScaleForId(meta.xAxisID);\n\t\tvar yScale = me.getScaleForId(meta.yAxisID);\n\t\tvar options = me._resolveDataElementOptions(point, index);\n\t\tvar data = me.getDataset().data[index];\n\t\tvar dsIndex = me.index;\n\n\t\tvar x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);\n\t\tvar y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);\n\n\t\tpoint._xScale = xScale;\n\t\tpoint._yScale = yScale;\n\t\tpoint._options = options;\n\t\tpoint._datasetIndex = dsIndex;\n\t\tpoint._index = index;\n\t\tpoint._model = {\n\t\t\tbackgroundColor: options.backgroundColor,\n\t\t\tborderColor: options.borderColor,\n\t\t\tborderWidth: options.borderWidth,\n\t\t\thitRadius: options.hitRadius,\n\t\t\tpointStyle: options.pointStyle,\n\t\t\trotation: options.rotation,\n\t\t\tradius: reset ? 0 : options.radius,\n\t\t\tskip: custom.skip || isNaN(x) || isNaN(y),\n\t\t\tx: x,\n\t\t\ty: y,\n\t\t};\n\n\t\tpoint.pivot();\n\t},\n\n\t/**\n\t * @protected\n\t */\n\tsetHoverStyle: function(point) {\n\t\tvar model = point._model;\n\t\tvar options = point._options;\n\t\tvar getHoverColor = helpers$1.getHoverColor;\n\n\t\tpoint.$previousStyle = {\n\t\t\tbackgroundColor: model.backgroundColor,\n\t\t\tborderColor: model.borderColor,\n\t\t\tborderWidth: model.borderWidth,\n\t\t\tradius: model.radius\n\t\t};\n\n\t\tmodel.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));\n\t\tmodel.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor));\n\t\tmodel.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth);\n\t\tmodel.radius = options.radius + options.hoverRadius;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_resolveDataElementOptions: function(point, index) {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar dataset = me.getDataset();\n\t\tvar custom = point.custom || {};\n\t\tvar data = dataset.data[index] || {};\n\t\tvar values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments);\n\n\t\t// Scriptable options\n\t\tvar context = {\n\t\t\tchart: chart,\n\t\t\tdataIndex: index,\n\t\t\tdataset: dataset,\n\t\t\tdatasetIndex: me.index\n\t\t};\n\n\t\t// In case values were cached (and thus frozen), we need to clone the values\n\t\tif (me._cachedDataOpts === values) {\n\t\t\tvalues = helpers$1.extend({}, values);\n\t\t}\n\n\t\t// Custom radius resolution\n\t\tvalues.radius = resolve$1([\n\t\t\tcustom.radius,\n\t\t\tdata.r,\n\t\t\tme._config.radius,\n\t\t\tchart.options.elements.point.radius\n\t\t], context, index);\n\n\t\treturn values;\n\t}\n});\n\nvar valueOrDefault$5 = helpers$1.valueOrDefault;\n\nvar PI$1 = Math.PI;\nvar DOUBLE_PI$1 = PI$1 * 2;\nvar HALF_PI$1 = PI$1 / 2;\n\ncore_defaults._set('doughnut', {\n\tanimation: {\n\t\t// Boolean - Whether we animate the rotation of the Doughnut\n\t\tanimateRotate: true,\n\t\t// Boolean - Whether we animate scaling the Doughnut from the centre\n\t\tanimateScale: false\n\t},\n\thover: {\n\t\tmode: 'single'\n\t},\n\tlegendCallback: function(chart) {\n\t\tvar list = document.createElement('ul');\n\t\tvar data = chart.data;\n\t\tvar datasets = data.datasets;\n\t\tvar labels = data.labels;\n\t\tvar i, ilen, listItem, listItemSpan;\n\n\t\tlist.setAttribute('class', chart.id + '-legend');\n\t\tif (datasets.length) {\n\t\t\tfor (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {\n\t\t\t\tlistItem = list.appendChild(document.createElement('li'));\n\t\t\t\tlistItemSpan = listItem.appendChild(document.createElement('span'));\n\t\t\t\tlistItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];\n\t\t\t\tif (labels[i]) {\n\t\t\t\t\tlistItem.appendChild(document.createTextNode(labels[i]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn list.outerHTML;\n\t},\n\tlegend: {\n\t\tlabels: {\n\t\t\tgenerateLabels: function(chart) {\n\t\t\t\tvar data = chart.data;\n\t\t\t\tif (data.labels.length && data.datasets.length) {\n\t\t\t\t\treturn data.labels.map(function(label, i) {\n\t\t\t\t\t\tvar meta = chart.getDatasetMeta(0);\n\t\t\t\t\t\tvar style = meta.controller.getStyle(i);\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: label,\n\t\t\t\t\t\t\tfillStyle: style.backgroundColor,\n\t\t\t\t\t\t\tstrokeStyle: style.borderColor,\n\t\t\t\t\t\t\tlineWidth: style.borderWidth,\n\t\t\t\t\t\t\thidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,\n\n\t\t\t\t\t\t\t// Extra data used for toggling the correct item\n\t\t\t\t\t\t\tindex: i\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn [];\n\t\t\t}\n\t\t},\n\n\t\tonClick: function(e, legendItem) {\n\t\t\tvar index = legendItem.index;\n\t\t\tvar chart = this.chart;\n\t\t\tvar i, ilen, meta;\n\n\t\t\tfor (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {\n\t\t\t\tmeta = chart.getDatasetMeta(i);\n\t\t\t\t// toggle visibility of index if exists\n\t\t\t\tif (meta.data[index]) {\n\t\t\t\t\tmeta.data[index].hidden = !meta.data[index].hidden;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tchart.update();\n\t\t}\n\t},\n\n\t// The percentage of the chart that we cut out of the middle.\n\tcutoutPercentage: 50,\n\n\t// The rotation of the chart, where the first data arc begins.\n\trotation: -HALF_PI$1,\n\n\t// The total circumference of the chart.\n\tcircumference: DOUBLE_PI$1,\n\n\t// Need to override these to give a nice default\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\treturn '';\n\t\t\t},\n\t\t\tlabel: function(tooltipItem, data) {\n\t\t\t\tvar dataLabel = data.labels[tooltipItem.index];\n\t\t\t\tvar value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n\n\t\t\t\tif (helpers$1.isArray(dataLabel)) {\n\t\t\t\t\t// show value on first line of multiline label\n\t\t\t\t\t// need to clone because we are changing the value\n\t\t\t\t\tdataLabel = dataLabel.slice();\n\t\t\t\t\tdataLabel[0] += value;\n\t\t\t\t} else {\n\t\t\t\t\tdataLabel += value;\n\t\t\t\t}\n\n\t\t\t\treturn dataLabel;\n\t\t\t}\n\t\t}\n\t}\n});\n\nvar controller_doughnut = core_datasetController.extend({\n\n\tdataElementType: elements.Arc,\n\n\tlinkScales: helpers$1.noop,\n\n\t/**\n\t * @private\n\t */\n\t_dataElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderColor',\n\t\t'borderWidth',\n\t\t'borderAlign',\n\t\t'hoverBackgroundColor',\n\t\t'hoverBorderColor',\n\t\t'hoverBorderWidth',\n\t],\n\n\t// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly\n\tgetRingIndex: function(datasetIndex) {\n\t\tvar ringIndex = 0;\n\n\t\tfor (var j = 0; j < datasetIndex; ++j) {\n\t\t\tif (this.chart.isDatasetVisible(j)) {\n\t\t\t\t++ringIndex;\n\t\t\t}\n\t\t}\n\n\t\treturn ringIndex;\n\t},\n\n\tupdate: function(reset) {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar chartArea = chart.chartArea;\n\t\tvar opts = chart.options;\n\t\tvar ratioX = 1;\n\t\tvar ratioY = 1;\n\t\tvar offsetX = 0;\n\t\tvar offsetY = 0;\n\t\tvar meta = me.getMeta();\n\t\tvar arcs = meta.data;\n\t\tvar cutout = opts.cutoutPercentage / 100 || 0;\n\t\tvar circumference = opts.circumference;\n\t\tvar chartWeight = me._getRingWeight(me.index);\n\t\tvar maxWidth, maxHeight, i, ilen;\n\n\t\t// If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc\n\t\tif (circumference < DOUBLE_PI$1) {\n\t\t\tvar startAngle = opts.rotation % DOUBLE_PI$1;\n\t\t\tstartAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0;\n\t\t\tvar endAngle = startAngle + circumference;\n\t\t\tvar startX = Math.cos(startAngle);\n\t\t\tvar startY = Math.sin(startAngle);\n\t\t\tvar endX = Math.cos(endAngle);\n\t\t\tvar endY = Math.sin(endAngle);\n\t\t\tvar contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1;\n\t\t\tvar contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1;\n\t\t\tvar contains180 = startAngle === -PI$1 || endAngle >= PI$1;\n\t\t\tvar contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1;\n\t\t\tvar minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);\n\t\t\tvar minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);\n\t\t\tvar maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);\n\t\t\tvar maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);\n\t\t\tratioX = (maxX - minX) / 2;\n\t\t\tratioY = (maxY - minY) / 2;\n\t\t\toffsetX = -(maxX + minX) / 2;\n\t\t\toffsetY = -(maxY + minY) / 2;\n\t\t}\n\n\t\tfor (i = 0, ilen = arcs.length; i < ilen; ++i) {\n\t\t\tarcs[i]._options = me._resolveDataElementOptions(arcs[i], i);\n\t\t}\n\n\t\tchart.borderWidth = me.getMaxBorderWidth();\n\t\tmaxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;\n\t\tmaxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;\n\t\tchart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);\n\t\tchart.innerRadius = Math.max(chart.outerRadius * cutout, 0);\n\t\tchart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);\n\t\tchart.offsetX = offsetX * chart.outerRadius;\n\t\tchart.offsetY = offsetY * chart.outerRadius;\n\n\t\tmeta.total = me.calculateTotal();\n\n\t\tme.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);\n\t\tme.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);\n\n\t\tfor (i = 0, ilen = arcs.length; i < ilen; ++i) {\n\t\t\tme.updateElement(arcs[i], i, reset);\n\t\t}\n\t},\n\n\tupdateElement: function(arc, index, reset) {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar chartArea = chart.chartArea;\n\t\tvar opts = chart.options;\n\t\tvar animationOpts = opts.animation;\n\t\tvar centerX = (chartArea.left + chartArea.right) / 2;\n\t\tvar centerY = (chartArea.top + chartArea.bottom) / 2;\n\t\tvar startAngle = opts.rotation; // non reset case handled later\n\t\tvar endAngle = opts.rotation; // non reset case handled later\n\t\tvar dataset = me.getDataset();\n\t\tvar circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1);\n\t\tvar innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;\n\t\tvar outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;\n\t\tvar options = arc._options || {};\n\n\t\thelpers$1.extend(arc, {\n\t\t\t// Utility\n\t\t\t_datasetIndex: me.index,\n\t\t\t_index: index,\n\n\t\t\t// Desired view properties\n\t\t\t_model: {\n\t\t\t\tbackgroundColor: options.backgroundColor,\n\t\t\t\tborderColor: options.borderColor,\n\t\t\t\tborderWidth: options.borderWidth,\n\t\t\t\tborderAlign: options.borderAlign,\n\t\t\t\tx: centerX + chart.offsetX,\n\t\t\t\ty: centerY + chart.offsetY,\n\t\t\t\tstartAngle: startAngle,\n\t\t\t\tendAngle: endAngle,\n\t\t\t\tcircumference: circumference,\n\t\t\t\touterRadius: outerRadius,\n\t\t\t\tinnerRadius: innerRadius,\n\t\t\t\tlabel: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])\n\t\t\t}\n\t\t});\n\n\t\tvar model = arc._model;\n\n\t\t// Set correct angles if not resetting\n\t\tif (!reset || !animationOpts.animateRotate) {\n\t\t\tif (index === 0) {\n\t\t\t\tmodel.startAngle = opts.rotation;\n\t\t\t} else {\n\t\t\t\tmodel.startAngle = me.getMeta().data[index - 1]._model.endAngle;\n\t\t\t}\n\n\t\t\tmodel.endAngle = model.startAngle + model.circumference;\n\t\t}\n\n\t\tarc.pivot();\n\t},\n\n\tcalculateTotal: function() {\n\t\tvar dataset = this.getDataset();\n\t\tvar meta = this.getMeta();\n\t\tvar total = 0;\n\t\tvar value;\n\n\t\thelpers$1.each(meta.data, function(element, index) {\n\t\t\tvalue = dataset.data[index];\n\t\t\tif (!isNaN(value) && !element.hidden) {\n\t\t\t\ttotal += Math.abs(value);\n\t\t\t}\n\t\t});\n\n\t\t/* if (total === 0) {\n\t\t\ttotal = NaN;\n\t\t}*/\n\n\t\treturn total;\n\t},\n\n\tcalculateCircumference: function(value) {\n\t\tvar total = this.getMeta().total;\n\t\tif (total > 0 && !isNaN(value)) {\n\t\t\treturn DOUBLE_PI$1 * (Math.abs(value) / total);\n\t\t}\n\t\treturn 0;\n\t},\n\n\t// gets the max border or hover width to properly scale pie charts\n\tgetMaxBorderWidth: function(arcs) {\n\t\tvar me = this;\n\t\tvar max = 0;\n\t\tvar chart = me.chart;\n\t\tvar i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;\n\n\t\tif (!arcs) {\n\t\t\t// Find the outmost visible dataset\n\t\t\tfor (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {\n\t\t\t\tif (chart.isDatasetVisible(i)) {\n\t\t\t\t\tmeta = chart.getDatasetMeta(i);\n\t\t\t\t\tarcs = meta.data;\n\t\t\t\t\tif (i !== me.index) {\n\t\t\t\t\t\tcontroller = meta.controller;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!arcs) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tfor (i = 0, ilen = arcs.length; i < ilen; ++i) {\n\t\t\tarc = arcs[i];\n\t\t\tif (controller) {\n\t\t\t\tcontroller._configure();\n\t\t\t\toptions = controller._resolveDataElementOptions(arc, i);\n\t\t\t} else {\n\t\t\t\toptions = arc._options;\n\t\t\t}\n\t\t\tif (options.borderAlign !== 'inner') {\n\t\t\t\tborderWidth = options.borderWidth;\n\t\t\t\thoverWidth = options.hoverBorderWidth;\n\n\t\t\t\tmax = borderWidth > max ? borderWidth : max;\n\t\t\t\tmax = hoverWidth > max ? hoverWidth : max;\n\t\t\t}\n\t\t}\n\t\treturn max;\n\t},\n\n\t/**\n\t * @protected\n\t */\n\tsetHoverStyle: function(arc) {\n\t\tvar model = arc._model;\n\t\tvar options = arc._options;\n\t\tvar getHoverColor = helpers$1.getHoverColor;\n\n\t\tarc.$previousStyle = {\n\t\t\tbackgroundColor: model.backgroundColor,\n\t\t\tborderColor: model.borderColor,\n\t\t\tborderWidth: model.borderWidth,\n\t\t};\n\n\t\tmodel.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));\n\t\tmodel.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor));\n\t\tmodel.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth);\n\t},\n\n\t/**\n\t * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly\n\t * @private\n\t */\n\t_getRingWeightOffset: function(datasetIndex) {\n\t\tvar ringWeightOffset = 0;\n\n\t\tfor (var i = 0; i < datasetIndex; ++i) {\n\t\t\tif (this.chart.isDatasetVisible(i)) {\n\t\t\t\tringWeightOffset += this._getRingWeight(i);\n\t\t\t}\n\t\t}\n\n\t\treturn ringWeightOffset;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getRingWeight: function(dataSetIndex) {\n\t\treturn Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0);\n\t},\n\n\t/**\n\t * Returns the sum of all visibile data set weights. This value can be 0.\n\t * @private\n\t */\n\t_getVisibleDatasetWeightTotal: function() {\n\t\treturn this._getRingWeightOffset(this.chart.data.datasets.length);\n\t}\n});\n\ncore_defaults._set('horizontalBar', {\n\thover: {\n\t\tmode: 'index',\n\t\taxis: 'y'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'linear',\n\t\t\tposition: 'bottom'\n\t\t}],\n\n\t\tyAxes: [{\n\t\t\ttype: 'category',\n\t\t\tposition: 'left',\n\t\t\toffset: true,\n\t\t\tgridLines: {\n\t\t\t\toffsetGridLines: true\n\t\t\t}\n\t\t}]\n\t},\n\n\telements: {\n\t\trectangle: {\n\t\t\tborderSkipped: 'left'\n\t\t}\n\t},\n\n\ttooltips: {\n\t\tmode: 'index',\n\t\taxis: 'y'\n\t}\n});\n\ncore_defaults._set('global', {\n\tdatasets: {\n\t\thorizontalBar: {\n\t\t\tcategoryPercentage: 0.8,\n\t\t\tbarPercentage: 0.9\n\t\t}\n\t}\n});\n\nvar controller_horizontalBar = controller_bar.extend({\n\t/**\n\t * @private\n\t */\n\t_getValueScaleId: function() {\n\t\treturn this.getMeta().xAxisID;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getIndexScaleId: function() {\n\t\treturn this.getMeta().yAxisID;\n\t}\n});\n\nvar valueOrDefault$6 = helpers$1.valueOrDefault;\nvar resolve$2 = helpers$1.options.resolve;\nvar isPointInArea = helpers$1.canvas._isPointInArea;\n\ncore_defaults._set('line', {\n\tshowLines: true,\n\tspanGaps: false,\n\n\thover: {\n\t\tmode: 'label'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'category',\n\t\t\tid: 'x-axis-0'\n\t\t}],\n\t\tyAxes: [{\n\t\t\ttype: 'linear',\n\t\t\tid: 'y-axis-0'\n\t\t}]\n\t}\n});\n\nfunction scaleClip(scale, halfBorderWidth) {\n\tvar tickOpts = scale && scale.options.ticks || {};\n\tvar reverse = tickOpts.reverse;\n\tvar min = tickOpts.min === undefined ? halfBorderWidth : 0;\n\tvar max = tickOpts.max === undefined ? halfBorderWidth : 0;\n\treturn {\n\t\tstart: reverse ? max : min,\n\t\tend: reverse ? min : max\n\t};\n}\n\nfunction defaultClip(xScale, yScale, borderWidth) {\n\tvar halfBorderWidth = borderWidth / 2;\n\tvar x = scaleClip(xScale, halfBorderWidth);\n\tvar y = scaleClip(yScale, halfBorderWidth);\n\n\treturn {\n\t\ttop: y.end,\n\t\tright: x.end,\n\t\tbottom: y.start,\n\t\tleft: x.start\n\t};\n}\n\nfunction toClip(value) {\n\tvar t, r, b, l;\n\n\tif (helpers$1.isObject(value)) {\n\t\tt = value.top;\n\t\tr = value.right;\n\t\tb = value.bottom;\n\t\tl = value.left;\n\t} else {\n\t\tt = r = b = l = value;\n\t}\n\n\treturn {\n\t\ttop: t,\n\t\tright: r,\n\t\tbottom: b,\n\t\tleft: l\n\t};\n}\n\n\nvar controller_line = core_datasetController.extend({\n\n\tdatasetElementType: elements.Line,\n\n\tdataElementType: elements.Point,\n\n\t/**\n\t * @private\n\t */\n\t_datasetElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderCapStyle',\n\t\t'borderColor',\n\t\t'borderDash',\n\t\t'borderDashOffset',\n\t\t'borderJoinStyle',\n\t\t'borderWidth',\n\t\t'cubicInterpolationMode',\n\t\t'fill'\n\t],\n\n\t/**\n\t * @private\n\t */\n\t_dataElementOptions: {\n\t\tbackgroundColor: 'pointBackgroundColor',\n\t\tborderColor: 'pointBorderColor',\n\t\tborderWidth: 'pointBorderWidth',\n\t\thitRadius: 'pointHitRadius',\n\t\thoverBackgroundColor: 'pointHoverBackgroundColor',\n\t\thoverBorderColor: 'pointHoverBorderColor',\n\t\thoverBorderWidth: 'pointHoverBorderWidth',\n\t\thoverRadius: 'pointHoverRadius',\n\t\tpointStyle: 'pointStyle',\n\t\tradius: 'pointRadius',\n\t\trotation: 'pointRotation'\n\t},\n\n\tupdate: function(reset) {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar line = meta.dataset;\n\t\tvar points = meta.data || [];\n\t\tvar options = me.chart.options;\n\t\tvar config = me._config;\n\t\tvar showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines);\n\t\tvar i, ilen;\n\n\t\tme._xScale = me.getScaleForId(meta.xAxisID);\n\t\tme._yScale = me.getScaleForId(meta.yAxisID);\n\n\t\t// Update Line\n\t\tif (showLine) {\n\t\t\t// Compatibility: If the properties are defined with only the old name, use those values\n\t\t\tif (config.tension !== undefined && config.lineTension === undefined) {\n\t\t\t\tconfig.lineTension = config.tension;\n\t\t\t}\n\n\t\t\t// Utility\n\t\t\tline._scale = me._yScale;\n\t\t\tline._datasetIndex = me.index;\n\t\t\t// Data\n\t\t\tline._children = points;\n\t\t\t// Model\n\t\t\tline._model = me._resolveDatasetElementOptions(line);\n\n\t\t\tline.pivot();\n\t\t}\n\n\t\t// Update Points\n\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\tme.updateElement(points[i], i, reset);\n\t\t}\n\n\t\tif (showLine && line._model.tension !== 0) {\n\t\t\tme.updateBezierControlPoints();\n\t\t}\n\n\t\t// Now pivot the point for animation\n\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\tpoints[i].pivot();\n\t\t}\n\t},\n\n\tupdateElement: function(point, index, reset) {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar custom = point.custom || {};\n\t\tvar dataset = me.getDataset();\n\t\tvar datasetIndex = me.index;\n\t\tvar value = dataset.data[index];\n\t\tvar xScale = me._xScale;\n\t\tvar yScale = me._yScale;\n\t\tvar lineModel = meta.dataset._model;\n\t\tvar x, y;\n\n\t\tvar options = me._resolveDataElementOptions(point, index);\n\n\t\tx = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);\n\t\ty = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);\n\n\t\t// Utility\n\t\tpoint._xScale = xScale;\n\t\tpoint._yScale = yScale;\n\t\tpoint._options = options;\n\t\tpoint._datasetIndex = datasetIndex;\n\t\tpoint._index = index;\n\n\t\t// Desired view properties\n\t\tpoint._model = {\n\t\t\tx: x,\n\t\t\ty: y,\n\t\t\tskip: custom.skip || isNaN(x) || isNaN(y),\n\t\t\t// Appearance\n\t\t\tradius: options.radius,\n\t\t\tpointStyle: options.pointStyle,\n\t\t\trotation: options.rotation,\n\t\t\tbackgroundColor: options.backgroundColor,\n\t\t\tborderColor: options.borderColor,\n\t\t\tborderWidth: options.borderWidth,\n\t\t\ttension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0),\n\t\t\tsteppedLine: lineModel ? lineModel.steppedLine : false,\n\t\t\t// Tooltip\n\t\t\thitRadius: options.hitRadius\n\t\t};\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_resolveDatasetElementOptions: function(element) {\n\t\tvar me = this;\n\t\tvar config = me._config;\n\t\tvar custom = element.custom || {};\n\t\tvar options = me.chart.options;\n\t\tvar lineOptions = options.elements.line;\n\t\tvar values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);\n\n\t\t// The default behavior of lines is to break at null values, according\n\t\t// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158\n\t\t// This option gives lines the ability to span gaps\n\t\tvalues.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps);\n\t\tvalues.tension = valueOrDefault$6(config.lineTension, lineOptions.tension);\n\t\tvalues.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]);\n\t\tvalues.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth)));\n\n\t\treturn values;\n\t},\n\n\tcalculatePointY: function(value, index, datasetIndex) {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar yScale = me._yScale;\n\t\tvar sumPos = 0;\n\t\tvar sumNeg = 0;\n\t\tvar i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen;\n\n\t\tif (yScale.options.stacked) {\n\t\t\trightValue = +yScale.getRightValue(value);\n\t\t\tmetasets = chart._getSortedVisibleDatasetMetas();\n\t\t\tilen = metasets.length;\n\n\t\t\tfor (i = 0; i < ilen; ++i) {\n\t\t\t\tdsMeta = metasets[i];\n\t\t\t\tif (dsMeta.index === datasetIndex) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tds = chart.data.datasets[dsMeta.index];\n\t\t\t\tif (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) {\n\t\t\t\t\tstackedRightValue = +yScale.getRightValue(ds.data[index]);\n\t\t\t\t\tif (stackedRightValue < 0) {\n\t\t\t\t\t\tsumNeg += stackedRightValue || 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsumPos += stackedRightValue || 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (rightValue < 0) {\n\t\t\t\treturn yScale.getPixelForValue(sumNeg + rightValue);\n\t\t\t}\n\t\t\treturn yScale.getPixelForValue(sumPos + rightValue);\n\t\t}\n\t\treturn yScale.getPixelForValue(value);\n\t},\n\n\tupdateBezierControlPoints: function() {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar meta = me.getMeta();\n\t\tvar lineModel = meta.dataset._model;\n\t\tvar area = chart.chartArea;\n\t\tvar points = meta.data || [];\n\t\tvar i, ilen, model, controlPoints;\n\n\t\t// Only consider points that are drawn in case the spanGaps option is used\n\t\tif (lineModel.spanGaps) {\n\t\t\tpoints = points.filter(function(pt) {\n\t\t\t\treturn !pt._model.skip;\n\t\t\t});\n\t\t}\n\n\t\tfunction capControlPoint(pt, min, max) {\n\t\t\treturn Math.max(Math.min(pt, max), min);\n\t\t}\n\n\t\tif (lineModel.cubicInterpolationMode === 'monotone') {\n\t\t\thelpers$1.splineCurveMonotone(points);\n\t\t} else {\n\t\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\t\tmodel = points[i]._model;\n\t\t\t\tcontrolPoints = helpers$1.splineCurve(\n\t\t\t\t\thelpers$1.previousItem(points, i)._model,\n\t\t\t\t\tmodel,\n\t\t\t\t\thelpers$1.nextItem(points, i)._model,\n\t\t\t\t\tlineModel.tension\n\t\t\t\t);\n\t\t\t\tmodel.controlPointPreviousX = controlPoints.previous.x;\n\t\t\t\tmodel.controlPointPreviousY = controlPoints.previous.y;\n\t\t\t\tmodel.controlPointNextX = controlPoints.next.x;\n\t\t\t\tmodel.controlPointNextY = controlPoints.next.y;\n\t\t\t}\n\t\t}\n\n\t\tif (chart.options.elements.line.capBezierPoints) {\n\t\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\t\tmodel = points[i]._model;\n\t\t\t\tif (isPointInArea(model, area)) {\n\t\t\t\t\tif (i > 0 && isPointInArea(points[i - 1]._model, area)) {\n\t\t\t\t\t\tmodel.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);\n\t\t\t\t\t\tmodel.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);\n\t\t\t\t\t}\n\t\t\t\t\tif (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) {\n\t\t\t\t\t\tmodel.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);\n\t\t\t\t\t\tmodel.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tdraw: function() {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar meta = me.getMeta();\n\t\tvar points = meta.data || [];\n\t\tvar area = chart.chartArea;\n\t\tvar canvas = chart.canvas;\n\t\tvar i = 0;\n\t\tvar ilen = points.length;\n\t\tvar clip;\n\n\t\tif (me._showLine) {\n\t\t\tclip = meta.dataset._model.clip;\n\n\t\t\thelpers$1.canvas.clipArea(chart.ctx, {\n\t\t\t\tleft: clip.left === false ? 0 : area.left - clip.left,\n\t\t\t\tright: clip.right === false ? canvas.width : area.right + clip.right,\n\t\t\t\ttop: clip.top === false ? 0 : area.top - clip.top,\n\t\t\t\tbottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom\n\t\t\t});\n\n\t\t\tmeta.dataset.draw();\n\n\t\t\thelpers$1.canvas.unclipArea(chart.ctx);\n\t\t}\n\n\t\t// Draw the points\n\t\tfor (; i < ilen; ++i) {\n\t\t\tpoints[i].draw(area);\n\t\t}\n\t},\n\n\t/**\n\t * @protected\n\t */\n\tsetHoverStyle: function(point) {\n\t\tvar model = point._model;\n\t\tvar options = point._options;\n\t\tvar getHoverColor = helpers$1.getHoverColor;\n\n\t\tpoint.$previousStyle = {\n\t\t\tbackgroundColor: model.backgroundColor,\n\t\t\tborderColor: model.borderColor,\n\t\t\tborderWidth: model.borderWidth,\n\t\t\tradius: model.radius\n\t\t};\n\n\t\tmodel.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));\n\t\tmodel.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor));\n\t\tmodel.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth);\n\t\tmodel.radius = valueOrDefault$6(options.hoverRadius, options.radius);\n\t},\n});\n\nvar resolve$3 = helpers$1.options.resolve;\n\ncore_defaults._set('polarArea', {\n\tscale: {\n\t\ttype: 'radialLinear',\n\t\tangleLines: {\n\t\t\tdisplay: false\n\t\t},\n\t\tgridLines: {\n\t\t\tcircular: true\n\t\t},\n\t\tpointLabels: {\n\t\t\tdisplay: false\n\t\t},\n\t\tticks: {\n\t\t\tbeginAtZero: true\n\t\t}\n\t},\n\n\t// Boolean - Whether to animate the rotation of the chart\n\tanimation: {\n\t\tanimateRotate: true,\n\t\tanimateScale: true\n\t},\n\n\tstartAngle: -0.5 * Math.PI,\n\tlegendCallback: function(chart) {\n\t\tvar list = document.createElement('ul');\n\t\tvar data = chart.data;\n\t\tvar datasets = data.datasets;\n\t\tvar labels = data.labels;\n\t\tvar i, ilen, listItem, listItemSpan;\n\n\t\tlist.setAttribute('class', chart.id + '-legend');\n\t\tif (datasets.length) {\n\t\t\tfor (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {\n\t\t\t\tlistItem = list.appendChild(document.createElement('li'));\n\t\t\t\tlistItemSpan = listItem.appendChild(document.createElement('span'));\n\t\t\t\tlistItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];\n\t\t\t\tif (labels[i]) {\n\t\t\t\t\tlistItem.appendChild(document.createTextNode(labels[i]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn list.outerHTML;\n\t},\n\tlegend: {\n\t\tlabels: {\n\t\t\tgenerateLabels: function(chart) {\n\t\t\t\tvar data = chart.data;\n\t\t\t\tif (data.labels.length && data.datasets.length) {\n\t\t\t\t\treturn data.labels.map(function(label, i) {\n\t\t\t\t\t\tvar meta = chart.getDatasetMeta(0);\n\t\t\t\t\t\tvar style = meta.controller.getStyle(i);\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: label,\n\t\t\t\t\t\t\tfillStyle: style.backgroundColor,\n\t\t\t\t\t\t\tstrokeStyle: style.borderColor,\n\t\t\t\t\t\t\tlineWidth: style.borderWidth,\n\t\t\t\t\t\t\thidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,\n\n\t\t\t\t\t\t\t// Extra data used for toggling the correct item\n\t\t\t\t\t\t\tindex: i\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn [];\n\t\t\t}\n\t\t},\n\n\t\tonClick: function(e, legendItem) {\n\t\t\tvar index = legendItem.index;\n\t\t\tvar chart = this.chart;\n\t\t\tvar i, ilen, meta;\n\n\t\t\tfor (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {\n\t\t\t\tmeta = chart.getDatasetMeta(i);\n\t\t\t\tmeta.data[index].hidden = !meta.data[index].hidden;\n\t\t\t}\n\n\t\t\tchart.update();\n\t\t}\n\t},\n\n\t// Need to override these to give a nice default\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\treturn '';\n\t\t\t},\n\t\t\tlabel: function(item, data) {\n\t\t\t\treturn data.labels[item.index] + ': ' + item.yLabel;\n\t\t\t}\n\t\t}\n\t}\n});\n\nvar controller_polarArea = core_datasetController.extend({\n\n\tdataElementType: elements.Arc,\n\n\tlinkScales: helpers$1.noop,\n\n\t/**\n\t * @private\n\t */\n\t_dataElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderColor',\n\t\t'borderWidth',\n\t\t'borderAlign',\n\t\t'hoverBackgroundColor',\n\t\t'hoverBorderColor',\n\t\t'hoverBorderWidth',\n\t],\n\n\t/**\n\t * @private\n\t */\n\t_getIndexScaleId: function() {\n\t\treturn this.chart.scale.id;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getValueScaleId: function() {\n\t\treturn this.chart.scale.id;\n\t},\n\n\tupdate: function(reset) {\n\t\tvar me = this;\n\t\tvar dataset = me.getDataset();\n\t\tvar meta = me.getMeta();\n\t\tvar start = me.chart.options.startAngle || 0;\n\t\tvar starts = me._starts = [];\n\t\tvar angles = me._angles = [];\n\t\tvar arcs = meta.data;\n\t\tvar i, ilen, angle;\n\n\t\tme._updateRadius();\n\n\t\tmeta.count = me.countVisibleElements();\n\n\t\tfor (i = 0, ilen = dataset.data.length; i < ilen; i++) {\n\t\t\tstarts[i] = start;\n\t\t\tangle = me._computeAngle(i);\n\t\t\tangles[i] = angle;\n\t\t\tstart += angle;\n\t\t}\n\n\t\tfor (i = 0, ilen = arcs.length; i < ilen; ++i) {\n\t\t\tarcs[i]._options = me._resolveDataElementOptions(arcs[i], i);\n\t\t\tme.updateElement(arcs[i], i, reset);\n\t\t}\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_updateRadius: function() {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar chartArea = chart.chartArea;\n\t\tvar opts = chart.options;\n\t\tvar minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);\n\n\t\tchart.outerRadius = Math.max(minSize / 2, 0);\n\t\tchart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);\n\t\tchart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();\n\n\t\tme.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);\n\t\tme.innerRadius = me.outerRadius - chart.radiusLength;\n\t},\n\n\tupdateElement: function(arc, index, reset) {\n\t\tvar me = this;\n\t\tvar chart = me.chart;\n\t\tvar dataset = me.getDataset();\n\t\tvar opts = chart.options;\n\t\tvar animationOpts = opts.animation;\n\t\tvar scale = chart.scale;\n\t\tvar labels = chart.data.labels;\n\n\t\tvar centerX = scale.xCenter;\n\t\tvar centerY = scale.yCenter;\n\n\t\t// var negHalfPI = -0.5 * Math.PI;\n\t\tvar datasetStartAngle = opts.startAngle;\n\t\tvar distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);\n\t\tvar startAngle = me._starts[index];\n\t\tvar endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);\n\n\t\tvar resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);\n\t\tvar options = arc._options || {};\n\n\t\thelpers$1.extend(arc, {\n\t\t\t// Utility\n\t\t\t_datasetIndex: me.index,\n\t\t\t_index: index,\n\t\t\t_scale: scale,\n\n\t\t\t// Desired view properties\n\t\t\t_model: {\n\t\t\t\tbackgroundColor: options.backgroundColor,\n\t\t\t\tborderColor: options.borderColor,\n\t\t\t\tborderWidth: options.borderWidth,\n\t\t\t\tborderAlign: options.borderAlign,\n\t\t\t\tx: centerX,\n\t\t\t\ty: centerY,\n\t\t\t\tinnerRadius: 0,\n\t\t\t\touterRadius: reset ? resetRadius : distance,\n\t\t\t\tstartAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,\n\t\t\t\tendAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,\n\t\t\t\tlabel: helpers$1.valueAtIndexOrDefault(labels, index, labels[index])\n\t\t\t}\n\t\t});\n\n\t\tarc.pivot();\n\t},\n\n\tcountVisibleElements: function() {\n\t\tvar dataset = this.getDataset();\n\t\tvar meta = this.getMeta();\n\t\tvar count = 0;\n\n\t\thelpers$1.each(meta.data, function(element, index) {\n\t\t\tif (!isNaN(dataset.data[index]) && !element.hidden) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t});\n\n\t\treturn count;\n\t},\n\n\t/**\n\t * @protected\n\t */\n\tsetHoverStyle: function(arc) {\n\t\tvar model = arc._model;\n\t\tvar options = arc._options;\n\t\tvar getHoverColor = helpers$1.getHoverColor;\n\t\tvar valueOrDefault = helpers$1.valueOrDefault;\n\n\t\tarc.$previousStyle = {\n\t\t\tbackgroundColor: model.backgroundColor,\n\t\t\tborderColor: model.borderColor,\n\t\t\tborderWidth: model.borderWidth,\n\t\t};\n\n\t\tmodel.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));\n\t\tmodel.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));\n\t\tmodel.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_computeAngle: function(index) {\n\t\tvar me = this;\n\t\tvar count = this.getMeta().count;\n\t\tvar dataset = me.getDataset();\n\t\tvar meta = me.getMeta();\n\n\t\tif (isNaN(dataset.data[index]) || meta.data[index].hidden) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Scriptable options\n\t\tvar context = {\n\t\t\tchart: me.chart,\n\t\t\tdataIndex: index,\n\t\t\tdataset: dataset,\n\t\t\tdatasetIndex: me.index\n\t\t};\n\n\t\treturn resolve$3([\n\t\t\tme.chart.options.elements.arc.angle,\n\t\t\t(2 * Math.PI) / count\n\t\t], context, index);\n\t}\n});\n\ncore_defaults._set('pie', helpers$1.clone(core_defaults.doughnut));\ncore_defaults._set('pie', {\n\tcutoutPercentage: 0\n});\n\n// Pie charts are Doughnut chart with different defaults\nvar controller_pie = controller_doughnut;\n\nvar valueOrDefault$7 = helpers$1.valueOrDefault;\n\ncore_defaults._set('radar', {\n\tspanGaps: false,\n\tscale: {\n\t\ttype: 'radialLinear'\n\t},\n\telements: {\n\t\tline: {\n\t\t\tfill: 'start',\n\t\t\ttension: 0 // no bezier in radar\n\t\t}\n\t}\n});\n\nvar controller_radar = core_datasetController.extend({\n\tdatasetElementType: elements.Line,\n\n\tdataElementType: elements.Point,\n\n\tlinkScales: helpers$1.noop,\n\n\t/**\n\t * @private\n\t */\n\t_datasetElementOptions: [\n\t\t'backgroundColor',\n\t\t'borderWidth',\n\t\t'borderColor',\n\t\t'borderCapStyle',\n\t\t'borderDash',\n\t\t'borderDashOffset',\n\t\t'borderJoinStyle',\n\t\t'fill'\n\t],\n\n\t/**\n\t * @private\n\t */\n\t_dataElementOptions: {\n\t\tbackgroundColor: 'pointBackgroundColor',\n\t\tborderColor: 'pointBorderColor',\n\t\tborderWidth: 'pointBorderWidth',\n\t\thitRadius: 'pointHitRadius',\n\t\thoverBackgroundColor: 'pointHoverBackgroundColor',\n\t\thoverBorderColor: 'pointHoverBorderColor',\n\t\thoverBorderWidth: 'pointHoverBorderWidth',\n\t\thoverRadius: 'pointHoverRadius',\n\t\tpointStyle: 'pointStyle',\n\t\tradius: 'pointRadius',\n\t\trotation: 'pointRotation'\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getIndexScaleId: function() {\n\t\treturn this.chart.scale.id;\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_getValueScaleId: function() {\n\t\treturn this.chart.scale.id;\n\t},\n\n\tupdate: function(reset) {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar line = meta.dataset;\n\t\tvar points = meta.data || [];\n\t\tvar scale = me.chart.scale;\n\t\tvar config = me._config;\n\t\tvar i, ilen;\n\n\t\t// Compatibility: If the properties are defined with only the old name, use those values\n\t\tif (config.tension !== undefined && config.lineTension === undefined) {\n\t\t\tconfig.lineTension = config.tension;\n\t\t}\n\n\t\t// Utility\n\t\tline._scale = scale;\n\t\tline._datasetIndex = me.index;\n\t\t// Data\n\t\tline._children = points;\n\t\tline._loop = true;\n\t\t// Model\n\t\tline._model = me._resolveDatasetElementOptions(line);\n\n\t\tline.pivot();\n\n\t\t// Update Points\n\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\tme.updateElement(points[i], i, reset);\n\t\t}\n\n\t\t// Update bezier control points\n\t\tme.updateBezierControlPoints();\n\n\t\t// Now pivot the point for animation\n\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\tpoints[i].pivot();\n\t\t}\n\t},\n\n\tupdateElement: function(point, index, reset) {\n\t\tvar me = this;\n\t\tvar custom = point.custom || {};\n\t\tvar dataset = me.getDataset();\n\t\tvar scale = me.chart.scale;\n\t\tvar pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);\n\t\tvar options = me._resolveDataElementOptions(point, index);\n\t\tvar lineModel = me.getMeta().dataset._model;\n\t\tvar x = reset ? scale.xCenter : pointPosition.x;\n\t\tvar y = reset ? scale.yCenter : pointPosition.y;\n\n\t\t// Utility\n\t\tpoint._scale = scale;\n\t\tpoint._options = options;\n\t\tpoint._datasetIndex = me.index;\n\t\tpoint._index = index;\n\n\t\t// Desired view properties\n\t\tpoint._model = {\n\t\t\tx: x, // value not used in dataset scale, but we want a consistent API between scales\n\t\t\ty: y,\n\t\t\tskip: custom.skip || isNaN(x) || isNaN(y),\n\t\t\t// Appearance\n\t\t\tradius: options.radius,\n\t\t\tpointStyle: options.pointStyle,\n\t\t\trotation: options.rotation,\n\t\t\tbackgroundColor: options.backgroundColor,\n\t\t\tborderColor: options.borderColor,\n\t\t\tborderWidth: options.borderWidth,\n\t\t\ttension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0),\n\n\t\t\t// Tooltip\n\t\t\thitRadius: options.hitRadius\n\t\t};\n\t},\n\n\t/**\n\t * @private\n\t */\n\t_resolveDatasetElementOptions: function() {\n\t\tvar me = this;\n\t\tvar config = me._config;\n\t\tvar options = me.chart.options;\n\t\tvar values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);\n\n\t\tvalues.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps);\n\t\tvalues.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension);\n\n\t\treturn values;\n\t},\n\n\tupdateBezierControlPoints: function() {\n\t\tvar me = this;\n\t\tvar meta = me.getMeta();\n\t\tvar area = me.chart.chartArea;\n\t\tvar points = meta.data || [];\n\t\tvar i, ilen, model, controlPoints;\n\n\t\t// Only consider points that are drawn in case the spanGaps option is used\n\t\tif (meta.dataset._model.spanGaps) {\n\t\t\tpoints = points.filter(function(pt) {\n\t\t\t\treturn !pt._model.skip;\n\t\t\t});\n\t\t}\n\n\t\tfunction capControlPoint(pt, min, max) {\n\t\t\treturn Math.max(Math.min(pt, max), min);\n\t\t}\n\n\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\tmodel = points[i]._model;\n\t\t\tcontrolPoints = helpers$1.splineCurve(\n\t\t\t\thelpers$1.previousItem(points, i, true)._model,\n\t\t\t\tmodel,\n\t\t\t\thelpers$1.nextItem(points, i, true)._model,\n\t\t\t\tmodel.tension\n\t\t\t);\n\n\t\t\t// Prevent the bezier going outside of the bounds of the graph\n\t\t\tmodel.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);\n\t\t\tmodel.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);\n\t\t\tmodel.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);\n\t\t\tmodel.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);\n\t\t}\n\t},\n\n\tsetHoverStyle: function(point) {\n\t\tvar model = point._model;\n\t\tvar options = point._options;\n\t\tvar getHoverColor = helpers$1.getHoverColor;\n\n\t\tpoint.$previousStyle = {\n\t\t\tbackgroundColor: model.backgroundColor,\n\t\t\tborderColor: model.borderColor,\n\t\t\tborderWidth: model.borderWidth,\n\t\t\tradius: model.radius\n\t\t};\n\n\t\tmodel.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));\n\t\tmodel.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor));\n\t\tmodel.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth);\n\t\tmodel.radius = valueOrDefault$7(options.hoverRadius, options.radius);\n\t}\n});\n\ncore_defaults._set('scatter', {\n\thover: {\n\t\tmode: 'single'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\tid: 'x-axis-1', // need an ID so datasets can reference the scale\n\t\t\ttype: 'linear', // scatter should not use a category axis\n\t\t\tposition: 'bottom'\n\t\t}],\n\t\tyAxes: [{\n\t\t\tid: 'y-axis-1',\n\t\t\ttype: 'linear',\n\t\t\tposition: 'left'\n\t\t}]\n\t},\n\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\treturn ''; // doesn't make sense for scatter since data are formatted as a point\n\t\t\t},\n\t\t\tlabel: function(item) {\n\t\t\t\treturn '(' + item.xLabel + ', ' + item.yLabel + ')';\n\t\t\t}\n\t\t}\n\t}\n});\n\ncore_defaults._set('global', {\n\tdatasets: {\n\t\tscatter: {\n\t\t\tshowLine: false\n\t\t}\n\t}\n});\n\n// Scatter charts use line controllers\nvar controller_scatter = controller_line;\n\n// NOTE export a map in which the key represents the controller type, not\n// the class, and so must be CamelCase in order to be correctly retrieved\n// by the controller in core.controller.js (`controllers[meta.type]`).\n\nvar controllers = {\n\tbar: controller_bar,\n\tbubble: controller_bubble,\n\tdoughnut: controller_doughnut,\n\thorizontalBar: controller_horizontalBar,\n\tline: controller_line,\n\tpolarArea: controller_polarArea,\n\tpie: controller_pie,\n\tradar: controller_radar,\n\tscatter: controller_scatter\n};\n\n/**\n * Helper function to get relative position for an event\n * @param {Event|IEvent} event - The event to get the position for\n * @param {Chart} chart - The chart\n * @returns {object} the event position\n */\nfunction getRelativePosition(e, chart) {\n\tif (e.native) {\n\t\treturn {\n\t\t\tx: e.x,\n\t\t\ty: e.y\n\t\t};\n\t}\n\n\treturn helpers$1.getRelativePosition(e, chart);\n}\n\n/**\n * Helper function to traverse all of the visible elements in the chart\n * @param {Chart} chart - the chart\n * @param {function} handler - the callback to execute for each visible item\n */\nfunction parseVisibleItems(chart, handler) {\n\tvar metasets = chart._getSortedVisibleDatasetMetas();\n\tvar metadata, i, j, ilen, jlen, element;\n\n\tfor (i = 0, ilen = metasets.length; i < ilen; ++i) {\n\t\tmetadata = metasets[i].data;\n\t\tfor (j = 0, jlen = metadata.length; j < jlen; ++j) {\n\t\t\telement = metadata[j];\n\t\t\tif (!element._view.skip) {\n\t\t\t\thandler(element);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Helper function to get the items that intersect the event position\n * @param {ChartElement[]} items - elements to filter\n * @param {object} position - the point to be nearest to\n * @return {ChartElement[]} the nearest items\n */\nfunction getIntersectItems(chart, position) {\n\tvar elements = [];\n\n\tparseVisibleItems(chart, function(element) {\n\t\tif (element.inRange(position.x, position.y)) {\n\t\t\telements.push(element);\n\t\t}\n\t});\n\n\treturn elements;\n}\n\n/**\n * Helper function to get the items nearest to the event position considering all visible items in teh chart\n * @param {Chart} chart - the chart to look at elements from\n * @param {object} position - the point to be nearest to\n * @param {boolean} intersect - if true, only consider items that intersect the position\n * @param {function} distanceMetric - function to provide the distance between points\n * @return {ChartElement[]} the nearest items\n */\nfunction getNearestItems(chart, position, intersect, distanceMetric) {\n\tvar minDistance = Number.POSITIVE_INFINITY;\n\tvar nearestItems = [];\n\n\tparseVisibleItems(chart, function(element) {\n\t\tif (intersect && !element.inRange(position.x, position.y)) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar center = element.getCenterPoint();\n\t\tvar distance = distanceMetric(position, center);\n\t\tif (distance < minDistance) {\n\t\t\tnearestItems = [element];\n\t\t\tminDistance = distance;\n\t\t} else if (distance === minDistance) {\n\t\t\t// Can have multiple items at the same distance in which case we sort by size\n\t\t\tnearestItems.push(element);\n\t\t}\n\t});\n\n\treturn nearestItems;\n}\n\n/**\n * Get a distance metric function for two points based on the\n * axis mode setting\n * @param {string} axis - the axis mode. x|y|xy\n */\nfunction getDistanceMetricForAxis(axis) {\n\tvar useX = axis.indexOf('x') !== -1;\n\tvar useY = axis.indexOf('y') !== -1;\n\n\treturn function(pt1, pt2) {\n\t\tvar deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;\n\t\tvar deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;\n\t\treturn Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));\n\t};\n}\n\nfunction indexMode(chart, e, options) {\n\tvar position = getRelativePosition(e, chart);\n\t// Default axis for index mode is 'x' to match old behaviour\n\toptions.axis = options.axis || 'x';\n\tvar distanceMetric = getDistanceMetricForAxis(options.axis);\n\tvar items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);\n\tvar elements = [];\n\n\tif (!items.length) {\n\t\treturn [];\n\t}\n\n\tchart._getSortedVisibleDatasetMetas().forEach(function(meta) {\n\t\tvar element = meta.data[items[0]._index];\n\n\t\t// don't count items that are skipped (null data)\n\t\tif (element && !element._view.skip) {\n\t\t\telements.push(element);\n\t\t}\n\t});\n\n\treturn elements;\n}\n\n/**\n * @interface IInteractionOptions\n */\n/**\n * If true, only consider items that intersect the point\n * @name IInterfaceOptions#boolean\n * @type Boolean\n */\n\n/**\n * Contains interaction related functions\n * @namespace Chart.Interaction\n */\nvar core_interaction = {\n\t// Helper function for different modes\n\tmodes: {\n\t\tsingle: function(chart, e) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\tvar elements = [];\n\n\t\t\tparseVisibleItems(chart, function(element) {\n\t\t\t\tif (element.inRange(position.x, position.y)) {\n\t\t\t\t\telements.push(element);\n\t\t\t\t\treturn elements;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn elements.slice(0, 1);\n\t\t},\n\n\t\t/**\n\t\t * @function Chart.Interaction.modes.label\n\t\t * @deprecated since version 2.4.0\n\t\t * @todo remove at version 3\n\t\t * @private\n\t\t */\n\t\tlabel: indexMode,\n\n\t\t/**\n\t\t * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something\n\t\t * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item\n\t\t * @function Chart.Interaction.modes.index\n\t\t * @since v2.4.0\n\t\t * @param {Chart} chart - the chart we are returning items from\n\t\t * @param {Event} e - the event we are find things at\n\t\t * @param {IInteractionOptions} options - options to use during interaction\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tindex: indexMode,\n\n\t\t/**\n\t\t * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something\n\t\t * If the options.intersect is false, we find the nearest item and return the items in that dataset\n\t\t * @function Chart.Interaction.modes.dataset\n\t\t * @param {Chart} chart - the chart we are returning items from\n\t\t * @param {Event} e - the event we are find things at\n\t\t * @param {IInteractionOptions} options - options to use during interaction\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tdataset: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\toptions.axis = options.axis || 'xy';\n\t\t\tvar distanceMetric = getDistanceMetricForAxis(options.axis);\n\t\t\tvar items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);\n\n\t\t\tif (items.length > 0) {\n\t\t\t\titems = chart.getDatasetMeta(items[0]._datasetIndex).data;\n\t\t\t}\n\n\t\t\treturn items;\n\t\t},\n\n\t\t/**\n\t\t * @function Chart.Interaction.modes.x-axis\n\t\t * @deprecated since version 2.4.0. Use index mode and intersect == true\n\t\t * @todo remove at version 3\n\t\t * @private\n\t\t */\n\t\t'x-axis': function(chart, e) {\n\t\t\treturn indexMode(chart, e, {intersect: false});\n\t\t},\n\n\t\t/**\n\t\t * Point mode returns all elements that hit test based on the event position\n\t\t * of the event\n\t\t * @function Chart.Interaction.modes.intersect\n\t\t * @param {Chart} chart - the chart we are returning items from\n\t\t * @param {Event} e - the event we are find things at\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tpoint: function(chart, e) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\treturn getIntersectItems(chart, position);\n\t\t},\n\n\t\t/**\n\t\t * nearest mode returns the element closest to the point\n\t\t * @function Chart.Interaction.modes.intersect\n\t\t * @param {Chart} chart - the chart we are returning items from\n\t\t * @param {Event} e - the event we are find things at\n\t\t * @param {IInteractionOptions} options - options to use\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tnearest: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\toptions.axis = options.axis || 'xy';\n\t\t\tvar distanceMetric = getDistanceMetricForAxis(options.axis);\n\t\t\treturn getNearestItems(chart, position, options.intersect, distanceMetric);\n\t\t},\n\n\t\t/**\n\t\t * x mode returns the elements that hit-test at the current x coordinate\n\t\t * @function Chart.Interaction.modes.x\n\t\t * @param {Chart} chart - the chart we are returning items from\n\t\t * @param {Event} e - the event we are find things at\n\t\t * @param {IInteractionOptions} options - options to use\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tx: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\tvar items = [];\n\t\t\tvar intersectsItem = false;\n\n\t\t\tparseVisibleItems(chart, function(element) {\n\t\t\t\tif (element.inXRange(position.x)) {\n\t\t\t\t\titems.push(element);\n\t\t\t\t}\n\n\t\t\t\tif (element.inRange(position.x, position.y)) {\n\t\t\t\t\tintersectsItem = true;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we want to trigger on an intersect and we don't have any items\n\t\t\t// that intersect the position, return nothing\n\t\t\tif (options.intersect && !intersectsItem) {\n\t\t\t\titems = [];\n\t\t\t}\n\t\t\treturn items;\n\t\t},\n\n\t\t/**\n\t\t * y mode returns the elements that hit-test at the current y coordinate\n\t\t * @function Chart.Interaction.modes.y\n\t\t * @param {Chart} chart - the chart we are returning items from\n\t\t * @param {Event} e - the event we are find things at\n\t\t * @param {IInteractionOptions} options - options to use\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\ty: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\tvar items = [];\n\t\t\tvar intersectsItem = false;\n\n\t\t\tparseVisibleItems(chart, function(element) {\n\t\t\t\tif (element.inYRange(position.y)) {\n\t\t\t\t\titems.push(element);\n\t\t\t\t}\n\n\t\t\t\tif (element.inRange(position.x, position.y)) {\n\t\t\t\t\tintersectsItem = true;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we want to trigger on an intersect and we don't have any items\n\t\t\t// that intersect the position, return nothing\n\t\t\tif (options.intersect && !intersectsItem) {\n\t\t\t\titems = [];\n\t\t\t}\n\t\t\treturn items;\n\t\t}\n\t}\n};\n\nvar extend = helpers$1.extend;\n\nfunction filterByPosition(array, position) {\n\treturn helpers$1.where(array, function(v) {\n\t\treturn v.pos === position;\n\t});\n}\n\nfunction sortByWeight(array, reverse) {\n\treturn array.sort(function(a, b) {\n\t\tvar v0 = reverse ? b : a;\n\t\tvar v1 = reverse ? a : b;\n\t\treturn v0.weight === v1.weight ?\n\t\t\tv0.index - v1.index :\n\t\t\tv0.weight - v1.weight;\n\t});\n}\n\nfunction wrapBoxes(boxes) {\n\tvar layoutBoxes = [];\n\tvar i, ilen, box;\n\n\tfor (i = 0, ilen = (boxes || []).length; i < ilen; ++i) {\n\t\tbox = boxes[i];\n\t\tlayoutBoxes.push({\n\t\t\tindex: i,\n\t\t\tbox: box,\n\t\t\tpos: box.position,\n\t\t\thorizontal: box.isHorizontal(),\n\t\t\tweight: box.weight\n\t\t});\n\t}\n\treturn layoutBoxes;\n}\n\nfunction setLayoutDims(layouts, params) {\n\tvar i, ilen, layout;\n\tfor (i = 0, ilen = layouts.length; i < ilen; ++i) {\n\t\tlayout = layouts[i];\n\t\t// store width used instead of chartArea.w in fitBoxes\n\t\tlayout.width = layout.horizontal\n\t\t\t? layout.box.fullWidth && params.availableWidth\n\t\t\t: params.vBoxMaxWidth;\n\t\t// store height used instead of chartArea.h in fitBoxes\n\t\tlayout.height = layout.horizontal && params.hBoxMaxHeight;\n\t}\n}\n\nfunction buildLayoutBoxes(boxes) {\n\tvar layoutBoxes = wrapBoxes(boxes);\n\tvar left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);\n\tvar right = sortByWeight(filterByPosition(layoutBoxes, 'right'));\n\tvar top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);\n\tvar bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));\n\n\treturn {\n\t\tleftAndTop: left.concat(top),\n\t\trightAndBottom: right.concat(bottom),\n\t\tchartArea: filterByPosition(layoutBoxes, 'chartArea'),\n\t\tvertical: left.concat(right),\n\t\thorizontal: top.concat(bottom)\n\t};\n}\n\nfunction getCombinedMax(maxPadding, chartArea, a, b) {\n\treturn Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);\n}\n\nfunction updateDims(chartArea, params, layout) {\n\tvar box = layout.box;\n\tvar maxPadding = chartArea.maxPadding;\n\tvar newWidth, newHeight;\n\n\tif (layout.size) {\n\t\t// this layout was already counted for, lets first reduce old size\n\t\tchartArea[layout.pos] -= layout.size;\n\t}\n\tlayout.size = layout.horizontal ? box.height : box.width;\n\tchartArea[layout.pos] += layout.size;\n\n\tif (box.getPadding) {\n\t\tvar boxPadding = box.getPadding();\n\t\tmaxPadding.top = Math.max(maxPadding.top, boxPadding.top);\n\t\tmaxPadding.left = Math.max(maxPadding.left, boxPadding.left);\n\t\tmaxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);\n\t\tmaxPadding.right = Math.max(maxPadding.right, boxPadding.right);\n\t}\n\n\tnewWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right');\n\tnewHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom');\n\n\tif (newWidth !== chartArea.w || newHeight !== chartArea.h) {\n\t\tchartArea.w = newWidth;\n\t\tchartArea.h = newHeight;\n\n\t\t// return true if chart area changed in layout's direction\n\t\tvar sizes = layout.horizontal ? [newWidth, chartArea.w] : [newHeight, chartArea.h];\n\t\treturn sizes[0] !== sizes[1] && (!isNaN(sizes[0]) || !isNaN(sizes[1]));\n\t}\n}\n\nfunction handleMaxPadding(chartArea) {\n\tvar maxPadding = chartArea.maxPadding;\n\n\tfunction updatePos(pos) {\n\t\tvar change = Math.max(maxPadding[pos] - chartArea[pos], 0);\n\t\tchartArea[pos] += change;\n\t\treturn change;\n\t}\n\tchartArea.y += updatePos('top');\n\tchartArea.x += updatePos('left');\n\tupdatePos('right');\n\tupdatePos('bottom');\n}\n\nfunction getMargins(horizontal, chartArea) {\n\tvar maxPadding = chartArea.maxPadding;\n\n\tfunction marginForPositions(positions) {\n\t\tvar margin = {left: 0, top: 0, right: 0, bottom: 0};\n\t\tpositions.forEach(function(pos) {\n\t\t\tmargin[pos] = Math.max(chartArea[pos], maxPadding[pos]);\n\t\t});\n\t\treturn margin;\n\t}\n\n\treturn horizontal\n\t\t? marginForPositions(['left', 'right'])\n\t\t: marginForPositions(['top', 'bottom']);\n}\n\nfunction fitBoxes(boxes, chartArea, params) {\n\tvar refitBoxes = [];\n\tvar i, ilen, layout, box, refit, changed;\n\n\tfor (i = 0, ilen = boxes.length; i < ilen; ++i) {\n\t\tlayout = boxes[i];\n\t\tbox = layout.box;\n\n\t\tbox.update(\n\t\t\tlayout.width || chartArea.w,\n\t\t\tlayout.height || chartArea.h,\n\t\t\tgetMargins(layout.horizontal, chartArea)\n\t\t);\n\t\tif (updateDims(chartArea, params, layout)) {\n\t\t\tchanged = true;\n\t\t\tif (refitBoxes.length) {\n\t\t\t\t// Dimensions changed and there were non full width boxes before this\n\t\t\t\t// -> we have to refit those\n\t\t\t\trefit = true;\n\t\t\t}\n\t\t}\n\t\tif (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case\n\t\t\trefitBoxes.push(layout);\n\t\t}\n\t}\n\n\treturn refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed;\n}\n\nfunction placeBoxes(boxes, chartArea, params) {\n\tvar userPadding = params.padding;\n\tvar x = chartArea.x;\n\tvar y = chartArea.y;\n\tvar i, ilen, layout, box;\n\n\tfor (i = 0, ilen = boxes.length; i < ilen; ++i) {\n\t\tlayout = boxes[i];\n\t\tbox = layout.box;\n\t\tif (layout.horizontal) {\n\t\t\tbox.left = box.fullWidth ? userPadding.left : chartArea.left;\n\t\t\tbox.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w;\n\t\t\tbox.top = y;\n\t\t\tbox.bottom = y + box.height;\n\t\t\tbox.width = box.right - box.left;\n\t\t\ty = box.bottom;\n\t\t} else {\n\t\t\tbox.left = x;\n\t\t\tbox.right = x + box.width;\n\t\t\tbox.top = chartArea.top;\n\t\t\tbox.bottom = chartArea.top + chartArea.h;\n\t\t\tbox.height = box.bottom - box.top;\n\t\t\tx = box.right;\n\t\t}\n\t}\n\n\tchartArea.x = x;\n\tchartArea.y = y;\n}\n\ncore_defaults._set('global', {\n\tlayout: {\n\t\tpadding: {\n\t\t\ttop: 0,\n\t\t\tright: 0,\n\t\t\tbottom: 0,\n\t\t\tleft: 0\n\t\t}\n\t}\n});\n\n/**\n * @interface ILayoutItem\n * @prop {string} position - The position of the item in the chart layout. Possible values are\n * 'left', 'top', 'right', 'bottom', and 'chartArea'\n * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area\n * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down\n * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)\n * @prop {function} update - Takes two parameters: width and height. Returns size of item\n * @prop {function} getPadding - Returns an object with padding on the edges\n * @prop {number} width - Width of item. Must be valid after update()\n * @prop {number} height - Height of item. Must be valid after update()\n * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update\n * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update\n * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update\n * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update\n */\n\n// The layout service is very self explanatory. It's responsible for the layout within a chart.\n// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need\n// It is this service's responsibility of carrying out that layout.\nvar core_layouts = {\n\tdefaults: {},\n\n\t/**\n\t * Register a box to a chart.\n\t * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.\n\t * @param {Chart} chart - the chart to use\n\t * @param {ILayoutItem} item - the item to add to be layed out\n\t */\n\taddBox: function(chart, item) {\n\t\tif (!chart.boxes) {\n\t\t\tchart.boxes = [];\n\t\t}\n\n\t\t// initialize item with default values\n\t\titem.fullWidth = item.fullWidth || false;\n\t\titem.position = item.position || 'top';\n\t\titem.weight = item.weight || 0;\n\t\titem._layers = item._layers || function() {\n\t\t\treturn [{\n\t\t\t\tz: 0,\n\t\t\t\tdraw: function() {\n\t\t\t\t\titem.draw.apply(item, arguments);\n\t\t\t\t}\n\t\t\t}];\n\t\t};\n\n\t\tchart.boxes.push(item);\n\t},\n\n\t/**\n\t * Remove a layoutItem from a chart\n\t * @param {Chart} chart - the chart to remove the box from\n\t * @param {ILayoutItem} layoutItem - the item to remove from the layout\n\t */\n\tremoveBox: function(chart, layoutItem) {\n\t\tvar index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;\n\t\tif (index !== -1) {\n\t\t\tchart.boxes.splice(index, 1);\n\t\t}\n\t},\n\n\t/**\n\t * Sets (or updates) options on the given `item`.\n\t * @param {Chart} chart - the chart in which the item lives (or will be added to)\n\t * @param {ILayoutItem} item - the item to configure with the given options\n\t * @param {object} options - the new item options.\n\t */\n\tconfigure: function(chart, item, options) {\n\t\tvar props = ['fullWidth', 'position', 'weight'];\n\t\tvar ilen = props.length;\n\t\tvar i = 0;\n\t\tvar prop;\n\n\t\tfor (; i < ilen; ++i) {\n\t\t\tprop = props[i];\n\t\t\tif (options.hasOwnProperty(prop)) {\n\t\t\t\titem[prop] = options[prop];\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Fits boxes of the given chart into the given size by having each box measure itself\n\t * then running a fitting algorithm\n\t * @param {Chart} chart - the chart\n\t * @param {number} width - the width to fit into\n\t * @param {number} height - the height to fit into\n\t */\n\tupdate: function(chart, width, height) {\n\t\tif (!chart) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar layoutOptions = chart.options.layout || {};\n\t\tvar padding = helpers$1.options.toPadding(layoutOptions.padding);\n\n\t\tvar availableWidth = width - padding.width;\n\t\tvar availableHeight = height - padding.height;\n\t\tvar boxes = buildLayoutBoxes(chart.boxes);\n\t\tvar verticalBoxes = boxes.vertical;\n\t\tvar horizontalBoxes = boxes.horizontal;\n\n\t\t// Essentially we now have any number of boxes on each of the 4 sides.\n\t\t// Our canvas looks like the following.\n\t\t// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and\n\t\t// B1 is the bottom axis\n\t\t// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays\n\t\t// These locations are single-box locations only, when trying to register a chartArea location that is already taken,\n\t\t// an error will be thrown.\n\t\t//\n\t\t// |----------------------------------------------------|\n\t\t// | T1 (Full Width) |\n\t\t// |----------------------------------------------------|\n\t\t// | | | T2 | |\n\t\t// | |----|-------------------------------------|----|\n\t\t// | | | C1 | | C2 | |\n\t\t// | | |----| |----| |\n\t\t// | | | | |\n\t\t// | L1 | L2 | ChartArea (C0) | R1 |\n\t\t// | | | | |\n\t\t// | | |----| |----| |\n\t\t// | | | C3 | | C4 | |\n\t\t// | |----|-------------------------------------|----|\n\t\t// | | | B1 | |\n\t\t// |----------------------------------------------------|\n\t\t// | B2 (Full Width) |\n\t\t// |----------------------------------------------------|\n\t\t//\n\n\t\tvar params = Object.freeze({\n\t\t\touterWidth: width,\n\t\t\touterHeight: height,\n\t\t\tpadding: padding,\n\t\t\tavailableWidth: availableWidth,\n\t\t\tvBoxMaxWidth: availableWidth / 2 / verticalBoxes.length,\n\t\t\thBoxMaxHeight: availableHeight / 2\n\t\t});\n\t\tvar chartArea = extend({\n\t\t\tmaxPadding: extend({}, padding),\n\t\t\tw: availableWidth,\n\t\t\th: availableHeight,\n\t\t\tx: padding.left,\n\t\t\ty: padding.top\n\t\t}, padding);\n\n\t\tsetLayoutDims(verticalBoxes.concat(horizontalBoxes), params);\n\n\t\t// First fit vertical boxes\n\t\tfitBoxes(verticalBoxes, chartArea, params);\n\n\t\t// Then fit horizontal boxes\n\t\tif (fitBoxes(horizontalBoxes, chartArea, params)) {\n\t\t\t// if the area changed, re-fit vertical boxes\n\t\t\tfitBoxes(verticalBoxes, chartArea, params);\n\t\t}\n\n\t\thandleMaxPadding(chartArea);\n\n\t\t// Finally place the boxes to correct coordinates\n\t\tplaceBoxes(boxes.leftAndTop, chartArea, params);\n\n\t\t// Move to opposite side of chart\n\t\tchartArea.x += chartArea.w;\n\t\tchartArea.y += chartArea.h;\n\n\t\tplaceBoxes(boxes.rightAndBottom, chartArea, params);\n\n\t\tchart.chartArea = {\n\t\t\tleft: chartArea.left,\n\t\t\ttop: chartArea.top,\n\t\t\tright: chartArea.left + chartArea.w,\n\t\t\tbottom: chartArea.top + chartArea.h\n\t\t};\n\n\t\t// Finally update boxes in chartArea (radial scale for example)\n\t\thelpers$1.each(boxes.chartArea, function(layout) {\n\t\t\tvar box = layout.box;\n\t\t\textend(box, chart.chartArea);\n\t\t\tbox.update(chartArea.w, chartArea.h);\n\t\t});\n\t}\n};\n\n/**\n * Platform fallback implementation (minimal).\n * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939\n */\n\nvar platform_basic = {\n\tacquireContext: function(item) {\n\t\tif (item && item.canvas) {\n\t\t\t// Support for any object associated to a canvas (including a context2d)\n\t\t\titem = item.canvas;\n\t\t}\n\n\t\treturn item && item.getContext('2d') || null;\n\t}\n};\n\nvar platform_dom = \"/*\\r\\n * DOM element rendering detection\\r\\n * https://davidwalsh.name/detect-node-insertion\\r\\n */\\r\\n@keyframes chartjs-render-animation {\\r\\n\\tfrom { opacity: 0.99; }\\r\\n\\tto { opacity: 1; }\\r\\n}\\r\\n\\r\\n.chartjs-render-monitor {\\r\\n\\tanimation: chartjs-render-animation 0.001s;\\r\\n}\\r\\n\\r\\n/*\\r\\n * DOM element resizing detection\\r\\n * https://github.com/marcj/css-element-queries\\r\\n */\\r\\n.chartjs-size-monitor,\\r\\n.chartjs-size-monitor-expand,\\r\\n.chartjs-size-monitor-shrink {\\r\\n\\tposition: absolute;\\r\\n\\tdirection: ltr;\\r\\n\\tleft: 0;\\r\\n\\ttop: 0;\\r\\n\\tright: 0;\\r\\n\\tbottom: 0;\\r\\n\\toverflow: hidden;\\r\\n\\tpointer-events: none;\\r\\n\\tvisibility: hidden;\\r\\n\\tz-index: -1;\\r\\n}\\r\\n\\r\\n.chartjs-size-monitor-expand > div {\\r\\n\\tposition: absolute;\\r\\n\\twidth: 1000000px;\\r\\n\\theight: 1000000px;\\r\\n\\tleft: 0;\\r\\n\\ttop: 0;\\r\\n}\\r\\n\\r\\n.chartjs-size-monitor-shrink > div {\\r\\n\\tposition: absolute;\\r\\n\\twidth: 200%;\\r\\n\\theight: 200%;\\r\\n\\tleft: 0;\\r\\n\\ttop: 0;\\r\\n}\\r\\n\";\n\nvar platform_dom$1 = /*#__PURE__*/Object.freeze({\n__proto__: null,\n'default': platform_dom\n});\n\nvar stylesheet = getCjsExportFromNamespace(platform_dom$1);\n\nvar EXPANDO_KEY = '$chartjs';\nvar CSS_PREFIX = 'chartjs-';\nvar CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor';\nvar CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';\nvar CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';\nvar ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];\n\n/**\n * DOM event types -> Chart.js event types.\n * Note: only events with different types are mapped.\n * @see https://developer.mozilla.org/en-US/docs/Web/Events\n */\nvar EVENT_TYPES = {\n\ttouchstart: 'mousedown',\n\ttouchmove: 'mousemove',\n\ttouchend: 'mouseup',\n\tpointerenter: 'mouseenter',\n\tpointerdown: 'mousedown',\n\tpointermove: 'mousemove',\n\tpointerup: 'mouseup',\n\tpointerleave: 'mouseout',\n\tpointerout: 'mouseout'\n};\n\n/**\n * The \"used\" size is the final value of a dimension property after all calculations have\n * been performed. This method uses the computed style of `element` but returns undefined\n * if the computed style is not expressed in pixels. That can happen in some cases where\n * `element` has a size relative to its parent and this last one is not yet displayed,\n * for example because of `display: none` on a parent node.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n * @returns {number} Size in pixels or undefined if unknown.\n */\nfunction readUsedSize(element, property) {\n\tvar value = helpers$1.getStyle(element, property);\n\tvar matches = value && value.match(/^(\\d+)(\\.\\d+)?px$/);\n\treturn matches ? Number(matches[1]) : undefined;\n}\n\n/**\n * Initializes the canvas style and render size without modifying the canvas display size,\n * since responsiveness is handled by the controller.resize() method. The config is used\n * to determine the aspect ratio to apply in case no explicit height has been specified.\n */\nfunction initCanvas(canvas, config) {\n\tvar style = canvas.style;\n\n\t// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it\n\t// returns null or '' if no explicit value has been set to the canvas attribute.\n\tvar renderHeight = canvas.getAttribute('height');\n\tvar renderWidth = canvas.getAttribute('width');\n\n\t// Chart.js modifies some canvas values that we want to restore on destroy\n\tcanvas[EXPANDO_KEY] = {\n\t\tinitial: {\n\t\t\theight: renderHeight,\n\t\t\twidth: renderWidth,\n\t\t\tstyle: {\n\t\t\t\tdisplay: style.display,\n\t\t\t\theight: style.height,\n\t\t\t\twidth: style.width\n\t\t\t}\n\t\t}\n\t};\n\n\t// Force canvas to display as block to avoid extra space caused by inline\n\t// elements, which would interfere with the responsive resize process.\n\t// https://github.com/chartjs/Chart.js/issues/2538\n\tstyle.display = style.display || 'block';\n\n\tif (renderWidth === null || renderWidth === '') {\n\t\tvar displayWidth = readUsedSize(canvas, 'width');\n\t\tif (displayWidth !== undefined) {\n\t\t\tcanvas.width = displayWidth;\n\t\t}\n\t}\n\n\tif (renderHeight === null || renderHeight === '') {\n\t\tif (canvas.style.height === '') {\n\t\t\t// If no explicit render height and style height, let's apply the aspect ratio,\n\t\t\t// which one can be specified by the user but also by charts as default option\n\t\t\t// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.\n\t\t\tcanvas.height = canvas.width / (config.options.aspectRatio || 2);\n\t\t} else {\n\t\t\tvar displayHeight = readUsedSize(canvas, 'height');\n\t\t\tif (displayWidth !== undefined) {\n\t\t\t\tcanvas.height = displayHeight;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn canvas;\n}\n\n/**\n * Detects support for options object argument in addEventListener.\n * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n * @private\n */\nvar supportsEventListenerOptions = (function() {\n\tvar supports = false;\n\ttry {\n\t\tvar options = Object.defineProperty({}, 'passive', {\n\t\t\t// eslint-disable-next-line getter-return\n\t\t\tget: function() {\n\t\t\t\tsupports = true;\n\t\t\t}\n\t\t});\n\t\twindow.addEventListener('e', null, options);\n\t} catch (e) {\n\t\t// continue regardless of error\n\t}\n\treturn supports;\n}());\n\n// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.\n// https://github.com/chartjs/Chart.js/issues/4287\nvar eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;\n\nfunction addListener(node, type, listener) {\n\tnode.addEventListener(type, listener, eventListenerOptions);\n}\n\nfunction removeListener(node, type, listener) {\n\tnode.removeEventListener(type, listener, eventListenerOptions);\n}\n\nfunction createEvent(type, chart, x, y, nativeEvent) {\n\treturn {\n\t\ttype: type,\n\t\tchart: chart,\n\t\tnative: nativeEvent || null,\n\t\tx: x !== undefined ? x : null,\n\t\ty: y !== undefined ? y : null,\n\t};\n}\n\nfunction fromNativeEvent(event, chart) {\n\tvar type = EVENT_TYPES[event.type] || event.type;\n\tvar pos = helpers$1.getRelativePosition(event, chart);\n\treturn createEvent(type, chart, pos.x, pos.y, event);\n}\n\nfunction throttled(fn, thisArg) {\n\tvar ticking = false;\n\tvar args = [];\n\n\treturn function() {\n\t\targs = Array.prototype.slice.call(arguments);\n\t\tthisArg = thisArg || this;\n\n\t\tif (!ticking) {\n\t\t\tticking = true;\n\t\t\thelpers$1.requestAnimFrame.call(window, function() {\n\t\t\t\tticking = false;\n\t\t\t\tfn.apply(thisArg, args);\n\t\t\t});\n\t\t}\n\t};\n}\n\nfunction createDiv(cls) {\n\tvar el = document.createElement('div');\n\tel.className = cls || '';\n\treturn el;\n}\n\n// Implementation based on https://github.com/marcj/css-element-queries\nfunction createResizer(handler) {\n\tvar maxSize = 1000000;\n\n\t// NOTE(SB) Don't use innerHTML because it could be considered unsafe.\n\t// https://github.com/chartjs/Chart.js/issues/5902\n\tvar resizer = createDiv(CSS_SIZE_MONITOR);\n\tvar expand = createDiv(CSS_SIZE_MONITOR + '-expand');\n\tvar shrink = createDiv(CSS_SIZE_MONITOR + '-shrink');\n\n\texpand.appendChild(createDiv());\n\tshrink.appendChild(createDiv());\n\n\tresizer.appendChild(expand);\n\tresizer.appendChild(shrink);\n\tresizer._reset = function() {\n\t\texpand.scrollLeft = maxSize;\n\t\texpand.scrollTop = maxSize;\n\t\tshrink.scrollLeft = maxSize;\n\t\tshrink.scrollTop = maxSize;\n\t};\n\n\tvar onScroll = function() {\n\t\tresizer._reset();\n\t\thandler();\n\t};\n\n\taddListener(expand, 'scroll', onScroll.bind(expand, 'expand'));\n\taddListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));\n\n\treturn resizer;\n}\n\n// https://davidwalsh.name/detect-node-insertion\nfunction watchForRender(node, handler) {\n\tvar expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});\n\tvar proxy = expando.renderProxy = function(e) {\n\t\tif (e.animationName === CSS_RENDER_ANIMATION) {\n\t\t\thandler();\n\t\t}\n\t};\n\n\thelpers$1.each(ANIMATION_START_EVENTS, function(type) {\n\t\taddListener(node, type, proxy);\n\t});\n\n\t// #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class\n\t// is removed then added back immediately (same animation frame?). Accessing the\n\t// `offsetParent` property will force a reflow and re-evaluate the CSS animation.\n\t// https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics\n\t// https://github.com/chartjs/Chart.js/issues/4737\n\texpando.reflow = !!node.offsetParent;\n\n\tnode.classList.add(CSS_RENDER_MONITOR);\n}\n\nfunction unwatchForRender(node) {\n\tvar expando = node[EXPANDO_KEY] || {};\n\tvar proxy = expando.renderProxy;\n\n\tif (proxy) {\n\t\thelpers$1.each(ANIMATION_START_EVENTS, function(type) {\n\t\t\tremoveListener(node, type, proxy);\n\t\t});\n\n\t\tdelete expando.renderProxy;\n\t}\n\n\tnode.classList.remove(CSS_RENDER_MONITOR);\n}\n\nfunction addResizeListener(node, listener, chart) {\n\tvar expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});\n\n\t// Let's keep track of this added resizer and thus avoid DOM query when removing it.\n\tvar resizer = expando.resizer = createResizer(throttled(function() {\n\t\tif (expando.resizer) {\n\t\t\tvar container = chart.options.maintainAspectRatio && node.parentNode;\n\t\t\tvar w = container ? container.clientWidth : 0;\n\t\t\tlistener(createEvent('resize', chart));\n\t\t\tif (container && container.clientWidth < w && chart.canvas) {\n\t\t\t\t// If the container size shrank during chart resize, let's assume\n\t\t\t\t// scrollbar appeared. So we resize again with the scrollbar visible -\n\t\t\t\t// effectively making chart smaller and the scrollbar hidden again.\n\t\t\t\t// Because we are inside `throttled`, and currently `ticking`, scroll\n\t\t\t\t// events are ignored during this whole 2 resize process.\n\t\t\t\t// If we assumed wrong and something else happened, we are resizing\n\t\t\t\t// twice in a frame (potential performance issue)\n\t\t\t\tlistener(createEvent('resize', chart));\n\t\t\t}\n\t\t}\n\t}));\n\n\t// The resizer needs to be attached to the node parent, so we first need to be\n\t// sure that `node` is attached to the DOM before injecting the resizer element.\n\twatchForRender(node, function() {\n\t\tif (expando.resizer) {\n\t\t\tvar container = node.parentNode;\n\t\t\tif (container && container !== resizer.parentNode) {\n\t\t\t\tcontainer.insertBefore(resizer, container.firstChild);\n\t\t\t}\n\n\t\t\t// The container size might have changed, let's reset the resizer state.\n\t\t\tresizer._reset();\n\t\t}\n\t});\n}\n\nfunction removeResizeListener(node) {\n\tvar expando = node[EXPANDO_KEY] || {};\n\tvar resizer = expando.resizer;\n\n\tdelete expando.resizer;\n\tunwatchForRender(node);\n\n\tif (resizer && resizer.parentNode) {\n\t\tresizer.parentNode.removeChild(resizer);\n\t}\n}\n\n/**\n * Injects CSS styles inline if the styles are not already present.\n * @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the