From c68b51f6394dd32d0c42632db9ba38bc22b9a645 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Wed, 14 Jan 2026 14:55:12 +0100 Subject: [PATCH 01/52] git : ignore claude settings --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index c192e22..bb14856 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,10 @@ public/vendor # Content # --------------- /public/content + +# Claude settings +# --------------- +.claude +/.claude/* + + From a7d315942a2be96ab875ae96ed65c4b5ac42c09f Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 10:31:31 +0100 Subject: [PATCH 02/52] =?UTF-8?q?Refonte=20du=20syst=C3=A8me=20de=20notifi?= =?UTF-8?q?cations=20:=20passage=20aux=20notifications=20d=C3=A9riv=C3=A9e?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remplace le système de notifications stockées par un système de providers qui dérivent les notifications des données existantes (commentaires, réponses, demandes de projet, demandes de rendez-vous, validations de brief). - Ajout du NotificationCollector et de l'interface NotificationProvider - Création de 5 providers : Comment, Reply, ProjectRequest, AppointmentRequest, Content - Métadonnées de notifications stockées directement sur les entités source - Nouvelles routes mark-as-read et mark-all-read - Mise à jour du frontend pour le nouveau système - Route de migration pour les données existantes Co-Authored-By: Claude Opus 4.5 --- public/site/blueprints/pages/client-brief.yml | 11 ++ .../site/blueprints/pages/extended-brief.yml | 13 ++ public/site/blueprints/pages/project.yml | 27 +++ public/site/config/config.php | 1 + .../config/routes/migrate-notifications.php | 175 ++++++++++++++++++ .../request-optimization-appointment.php | 41 ++-- .../routes/request-project-creation.php | 32 ++-- public/site/config/routes/validate-brief.php | 29 ++- .../site/plugins/comments/routes/create.php | 8 +- .../site/plugins/comments/routes/delete.php | 4 +- public/site/plugins/comments/routes/reply.php | 7 +- public/site/plugins/notifications/index.php | 63 +++++-- .../notifications/routes/mark-all-read.php | 42 +++++ .../notifications/routes/mark-as-read.php | 46 +++++ .../src/NotificationCollector.php | 123 ++++++++++++ .../src/NotificationProvider.php | 42 +++++ .../providers/AppointmentRequestProvider.php | 111 +++++++++++ .../src/providers/CommentProvider.php | 172 +++++++++++++++++ .../src/providers/ContentProvider.php | 128 +++++++++++++ .../src/providers/ProjectRequestProvider.php | 111 +++++++++++ .../src/providers/ReplyProvider.php | 142 ++++++++++++++ public/site/templates/projects.json.php | 51 +++-- src/stores/api.js | 57 ++++++ src/stores/user.js | 92 ++++++--- src/views/Notifications.vue | 14 +- vite.config.js | 1 + 26 files changed, 1406 insertions(+), 137 deletions(-) create mode 100644 public/site/config/routes/migrate-notifications.php create mode 100644 public/site/plugins/notifications/routes/mark-all-read.php create mode 100644 public/site/plugins/notifications/routes/mark-as-read.php create mode 100644 public/site/plugins/notifications/src/NotificationCollector.php create mode 100644 public/site/plugins/notifications/src/NotificationProvider.php create mode 100644 public/site/plugins/notifications/src/providers/AppointmentRequestProvider.php create mode 100644 public/site/plugins/notifications/src/providers/CommentProvider.php create mode 100644 public/site/plugins/notifications/src/providers/ContentProvider.php create mode 100644 public/site/plugins/notifications/src/providers/ProjectRequestProvider.php create mode 100644 public/site/plugins/notifications/src/providers/ReplyProvider.php diff --git a/public/site/blueprints/pages/client-brief.yml b/public/site/blueprints/pages/client-brief.yml index 7574be8..9df9168 100644 --- a/public/site/blueprints/pages/client-brief.yml +++ b/public/site/blueprints/pages/client-brief.yml @@ -24,6 +24,17 @@ tabs: type: hidden isValidated: type: hidden + # Champs pour notification "content" (brief validé) + validatedBy: + type: hidden + validatedByName: + type: hidden + validatedByEmail: + type: hidden + validatedAt: + type: hidden + validationReadby: + type: hidden pdf: label: PDF type: files diff --git a/public/site/blueprints/pages/extended-brief.yml b/public/site/blueprints/pages/extended-brief.yml index b65d99f..477d4a1 100644 --- a/public/site/blueprints/pages/extended-brief.yml +++ b/public/site/blueprints/pages/extended-brief.yml @@ -22,6 +22,19 @@ tabs: fields: stepName: type: hidden + isValidated: + type: hidden + # Champs pour notification "content" (brief validé) + validatedBy: + type: hidden + validatedByName: + type: hidden + validatedByEmail: + type: hidden + validatedAt: + type: hidden + validationReadby: + type: hidden pdf: label: PDF type: files diff --git a/public/site/blueprints/pages/project.yml b/public/site/blueprints/pages/project.yml index 946e238..6962aa5 100644 --- a/public/site/blueprints/pages/project.yml +++ b/public/site/blueprints/pages/project.yml @@ -21,6 +21,7 @@ tabs: fields: lastCacheUpdate: type: hidden + # Champs pour project-request isClientRequest: type: hidden default: "false" @@ -30,6 +31,32 @@ tabs: disabled: true when: isClientRequest: "true" + requestAuthor: + type: hidden + requestAuthorName: + type: hidden + requestAuthorEmail: + type: hidden + requestDate: + type: hidden + requestReadby: + type: hidden + # Champs pour appointment-request (DTL) + hasOptimizationRequest: + type: hidden + default: "false" + optimizationRequestDetails: + type: hidden + optimizationAuthor: + type: hidden + optimizationAuthorName: + type: hidden + optimizationAuthorEmail: + type: hidden + optimizationDate: + type: hidden + optimizationReadby: + type: hidden currentStep: label: Étape en cours type: radio diff --git a/public/site/config/config.php b/public/site/config/config.php index 72ea0da..37f3a33 100644 --- a/public/site/config/config.php +++ b/public/site/config/config.php @@ -40,6 +40,7 @@ return [ require(__DIR__ . '/routes/validate-brief.php'), require(__DIR__ . '/routes/request-project-creation.php'), require(__DIR__ . '/routes/request-optimization-appointment.php'), + require(__DIR__ . '/routes/migrate-notifications.php'), ], 'hooks' => [ 'page.create:after' => require_once(__DIR__ . '/hooks/create-steps.php'), diff --git a/public/site/config/routes/migrate-notifications.php b/public/site/config/routes/migrate-notifications.php new file mode 100644 index 0000000..3aa033c --- /dev/null +++ b/public/site/config/routes/migrate-notifications.php @@ -0,0 +1,175 @@ + 'migrate-notifications.json', + 'method' => 'POST', + 'action' => function () { + $user = kirby()->user(); + + // Vérifier que l'utilisateur est admin + if (!$user || $user->role()->id() !== 'admin') { + return [ + 'status' => 'error', + 'message' => 'Cette action nécessite les droits administrateur.' + ]; + } + + $migrated = [ + 'comments' => 0, + 'replies' => 0, + 'project-requests' => 0, + 'appointment-requests' => 0, + 'content' => 0, + 'errors' => [] + ]; + + $projects = page('projects')->children(); + + foreach ($projects as $project) { + // Récupérer les anciennes notifications + $notifications = $project->notifications()->yaml() ?? []; + + if (empty($notifications)) { + continue; + } + + foreach ($notifications as $notification) { + try { + $type = $notification['type'] ?? 'comment'; + $id = $notification['id'] ?? null; + $readby = $notification['readby'] ?? []; + + if (empty($id) || empty($readby)) { + continue; + } + + switch ($type) { + case 'comment': + case 'comment-reply': + $fileUuid = $notification['location']['file']['uuid'] ?? null; + if (!$fileUuid) continue 2; + + $file = kirby()->file($fileUuid); + if (!$file) continue 2; + + $comments = Yaml::decode($file->comments()->value()) ?? []; + $updated = false; + + foreach ($comments as &$comment) { + // Vérifier si c'est le commentaire principal + if ($comment['id'] === $id) { + $existingReadby = $comment['readby'] ?? []; + $comment['readby'] = array_values(array_unique(array_merge($existingReadby, $readby))); + $updated = true; + $migrated['comments']++; + break; + } + + // Vérifier dans les réponses + foreach ($comment['replies'] ?? [] as &$reply) { + if ($reply['id'] === $id) { + $existingReadby = $reply['readby'] ?? []; + $reply['readby'] = array_values(array_unique(array_merge($existingReadby, $readby))); + $updated = true; + $migrated['replies']++; + break 2; + } + } + } + + if ($updated) { + $file->update(['comments' => $comments]); + } + break; + + case 'project-request': + $existingReadby = $project->requestReadby()->yaml() ?? []; + $newReadby = array_values(array_unique(array_merge($existingReadby, $readby))); + + $updateData = ['requestReadby' => $newReadby]; + + // Migrer aussi les métadonnées si elles n'existent pas encore + if ($project->requestAuthor()->isEmpty() && isset($notification['author'])) { + $updateData['requestAuthor'] = $notification['author']['uuid'] ?? ''; + $updateData['requestAuthorName'] = $notification['author']['name'] ?? ''; + $updateData['requestAuthorEmail'] = $notification['author']['email'] ?? ''; + $updateData['requestDate'] = $notification['date'] ?? ''; + } + + $project->update($updateData); + $migrated['project-requests']++; + break; + + case 'appointment-request': + $existingReadby = $project->optimizationReadby()->yaml() ?? []; + $newReadby = array_values(array_unique(array_merge($existingReadby, $readby))); + + $updateData = ['optimizationReadby' => $newReadby]; + + // Migrer aussi les métadonnées si elles n'existent pas encore + if ($project->optimizationAuthor()->isEmpty() && isset($notification['author'])) { + $updateData['optimizationAuthor'] = $notification['author']['uuid'] ?? ''; + $updateData['optimizationAuthorName'] = $notification['author']['name'] ?? ''; + $updateData['optimizationAuthorEmail'] = $notification['author']['email'] ?? ''; + $updateData['optimizationDate'] = $notification['date'] ?? ''; + } + + $project->update($updateData); + $migrated['appointment-requests']++; + break; + + case 'content': + $briefUri = $notification['location']['page']['uri'] ?? null; + if (!$briefUri) continue 2; + + $brief = page($briefUri); + if (!$brief) continue 2; + + $existingReadby = $brief->validationReadby()->yaml() ?? []; + $newReadby = array_values(array_unique(array_merge($existingReadby, $readby))); + + $updateData = ['validationReadby' => $newReadby]; + + // Migrer aussi les métadonnées si elles n'existent pas encore + if ($brief->validatedBy()->isEmpty() && isset($notification['author'])) { + $updateData['validatedBy'] = $notification['author']['uuid'] ?? ''; + $updateData['validatedByName'] = $notification['author']['name'] ?? ''; + $updateData['validatedByEmail'] = $notification['author']['email'] ?? ''; + $updateData['validatedAt'] = $notification['date'] ?? ''; + } + + $brief->update($updateData); + $migrated['content']++; + break; + } + } catch (\Throwable $th) { + $migrated['errors'][] = [ + 'project' => $project->title()->value(), + 'notification_id' => $id ?? 'unknown', + 'type' => $type ?? 'unknown', + 'error' => $th->getMessage() + ]; + } + } + } + + $total = $migrated['comments'] + $migrated['replies'] + + $migrated['project-requests'] + $migrated['appointment-requests'] + + $migrated['content']; + + return [ + 'status' => 'success', + 'message' => "Migration terminée. $total notifications migrées.", + 'details' => $migrated + ]; + } +]; diff --git a/public/site/config/routes/request-optimization-appointment.php b/public/site/config/routes/request-optimization-appointment.php index 0cc94c0..d1e4b84 100644 --- a/public/site/config/routes/request-optimization-appointment.php +++ b/public/site/config/routes/request-optimization-appointment.php @@ -7,37 +7,26 @@ return [ $json = file_get_contents('php://input'); $data = json_decode($json); - $user = kirby()->user(); + $user = kirby()->user(); $project = page($data->projectUri); + $date = new DateTime(); + $formattedDate = $date->format(DateTime::ISO8601); + try { - $newProject = $project->update([ + $project->update([ "hasOptimizationRequest" => "true", - "optimizationRequestDetails" => esc("De la part de " . kirby()->user()->name() . " (" . kirby()->user()->email() . ") : \n\n" . "Objet : " . $data->subject . "\n" . $data->details) + "optimizationRequestDetails" => esc("De la part de " . $user->name() . " (" . $user->email() . ") : \n\n" . "Objet : " . $data->subject . "\n" . $data->details), + // Métadonnées pour le système de notifications dérivées + "optimizationAuthor" => (string) $user->uuid(), + "optimizationAuthorName" => (string) $user->name(), + "optimizationAuthorEmail" => (string) $user->email(), + "optimizationDate" => $formattedDate, + "optimizationReadby" => [], ]); - } catch (\Throwable $th) { - return [ - "status" => "error", - "message" => "Can't update project " . $project->title()->value() . ". " . $th->getMessage() . " in " . $th->getFile() . " line " . $th->getLine() - ]; - } - try { - $date = new DateTime(); - $formattedDate = $date->format(DateTime::ISO8601); - - $notificationData = [ - "location" => [ - "page" => $newProject - ], - "date" => (string) $formattedDate, - "text" => nl2br("Objet : " . $data->subject . "\n" . esc($data->details)), - "author" => $user, - "id" => Str::uuid(), - "type" => "appointment-request", - ]; - - $newProject->createNotification($notificationData); + // Note: Les notifications sont maintenant dérivées. + // Plus besoin d'appeler createNotification(). return [ "status" => "success", @@ -45,7 +34,7 @@ return [ } catch (\Throwable $th) { return [ "status" => "error", - "message" => "Can't create notification. " . $th->getMessage() . " in " . $th->getFile() . " line " . $th->getLine() + "message" => "Can't update project " . $project->title()->value() . ". " . $th->getMessage() . " in " . $th->getFile() . " line " . $th->getLine() ]; } } diff --git a/public/site/config/routes/request-project-creation.php b/public/site/config/routes/request-project-creation.php index 9a474d7..1b9b378 100644 --- a/public/site/config/routes/request-project-creation.php +++ b/public/site/config/routes/request-project-creation.php @@ -11,15 +11,24 @@ return [ $client = kirby()->user()->client()->toPage()->uuid(); + $date = new DateTime(); + $formattedDate = $date->format(DateTime::ISO8601); + $projectData = [ "slug" => esc(Str::slug($data->title)), - "template" => "project", + "template" => "project", "content" => [ "title" => esc($data->title), - "requestDetails" => esc("Demande de " . kirby()->user()->name() . " (" . kirby()->user()->email() . ") : \n\n" . $data->details), + "requestDetails" => esc("Demande de " . $user->name() . " (" . $user->email() . ") : \n\n" . $data->details), "client" => [$client], "isClientRequest" => "true", - "isDTLEnabled" => esc($data->isDTLEnabled) + "isDTLEnabled" => esc($data->isDTLEnabled), + // Métadonnées pour le système de notifications dérivées + "requestAuthor" => (string) $user->uuid(), + "requestAuthorName" => (string) $user->name(), + "requestAuthorEmail" => (string) $user->email(), + "requestDate" => $formattedDate, + "requestReadby" => [], ] ]; @@ -27,21 +36,8 @@ return [ try { $newProject = $projects->createChild($projectData); - $date = new DateTime(); - $formattedDate = $date->format(DateTime::ISO8601); - - $notificationData = [ - "location" => [ - "page" => $newProject - ], - "date" => (string) $formattedDate, - "text" => nl2br(esc($data->details)), - "author" => $user, - "id" => Str::uuid(), - "type" => "project-request", - ]; - - $newProject->createNotification($notificationData); + // Note: Les notifications sont maintenant dérivées. + // Plus besoin d'appeler createNotification(). return [ "status" => "success", diff --git a/public/site/config/routes/validate-brief.php b/public/site/config/routes/validate-brief.php index f8e97f1..fb2bf86 100644 --- a/public/site/config/routes/validate-brief.php +++ b/public/site/config/routes/validate-brief.php @@ -9,27 +9,24 @@ return [ $page = page($data->briefUri); $project = $page->parent(); - - try { - $newPage = $page->update([ - 'isValidated' => 'true' - ]); + $user = kirby()->user(); + try { $timezone = new DateTimeZone('Europe/Paris'); $dateTime = new DateTime('now', $timezone); - $notification = [ - 'location' => [ - 'page' => $page, - ], - 'date' => $dateTime->format('Y-m-d\TH:i:sP'), - 'text' => "Nouveau brief", - 'author' => kirby()->user(), - 'id' => Str::uuid(), - 'type' => 'content' - ]; + $newPage = $page->update([ + 'isValidated' => 'true', + // Métadonnées pour le système de notifications dérivées + 'validatedBy' => (string) $user->uuid(), + 'validatedByName' => (string) $user->name(), + 'validatedByEmail' => (string) $user->email(), + 'validatedAt' => $dateTime->format('Y-m-d\TH:i:sP'), + 'validationReadby' => [], + ]); - $project->createNotification($notification); + // Note: Les notifications sont maintenant dérivées. + // Plus besoin d'appeler createNotification(). return [ "success" => "'" . $project->title()->value() . "' brief validated." diff --git a/public/site/plugins/comments/routes/create.php b/public/site/plugins/comments/routes/create.php index 9db31cf..5641383 100644 --- a/public/site/plugins/comments/routes/create.php +++ b/public/site/plugins/comments/routes/create.php @@ -46,6 +46,7 @@ return [ 'author' => kirby()->user(), 'id' => Str::uuid(), 'type' => 'comment', + 'readby' => [], // Pour le système de notifications dérivées ]; if (isset($data->position->pageIndex)) { @@ -62,11 +63,8 @@ return [ echo json_encode(getFileData($newFile)); - try { - $project->createNotification($commentData); - } catch (\Throwable $th) { - throw new Exception($th->getMessage() . '. line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); - } + // Note: Les notifications sont maintenant dérivées des commentaires. + // Plus besoin d'appeler createNotification(). exit; }, diff --git a/public/site/plugins/comments/routes/delete.php b/public/site/plugins/comments/routes/delete.php index 8bc9e07..5f96a96 100644 --- a/public/site/plugins/comments/routes/delete.php +++ b/public/site/plugins/comments/routes/delete.php @@ -39,8 +39,8 @@ return [ echo json_encode(getFileData($newFile)); - $project = $page->parents()->findBy('template', 'project'); - $project->deleteNotification($data->id); + // Note: Les notifications sont maintenant dérivées des commentaires. + // La suppression du commentaire supprime automatiquement la notification. exit; }, diff --git a/public/site/plugins/comments/routes/reply.php b/public/site/plugins/comments/routes/reply.php index 68aeebd..2418dfe 100644 --- a/public/site/plugins/comments/routes/reply.php +++ b/public/site/plugins/comments/routes/reply.php @@ -31,18 +31,19 @@ return [ "author" => kirby()->user(), "id" => Str::uuid(), "type" => "comment-reply", + "readby" => [], // Pour le système de notifications dérivées ]; $newReply = new Reply($replyData); $comment['replies'][] = $newReply->toArray(); } } - + $newFile = $file->update([ 'comments' => $comments ]); - $project = $page->parents()->findBy("template", "project"); - $project->createNotification($replyData); + // Note: Les notifications sont maintenant dérivées des commentaires. + // Plus besoin d'appeler createNotification(). return getFileData($newFile); } diff --git a/public/site/plugins/notifications/index.php b/public/site/plugins/notifications/index.php index 91da6f9..9b46c7e 100644 --- a/public/site/plugins/notifications/index.php +++ b/public/site/plugins/notifications/index.php @@ -1,27 +1,54 @@ "models/ProjectPage.php", -], __DIR__); +use adrienpayet\notifications\NotificationCollector; +use adrienpayet\notifications\providers\CommentProvider; +use adrienpayet\notifications\providers\ReplyProvider; +use adrienpayet\notifications\providers\ProjectRequestProvider; +use adrienpayet\notifications\providers\AppointmentRequestProvider; +use adrienpayet\notifications\providers\ContentProvider; +// Charger les classes F::loadClasses([ - // Own classes - "adrienpayet\\notifications\\Notification" => __DIR__ . "/src/Notification.php", - "adrienpayet\\notifications\\NotificationsPage" => __DIR__ . "/src/NotificationsPage.php", - - // Shared classes - "adrienpayet\\D2P\\data\\Position" => __DIR__ . "/../classes/Position.php", - "adrienpayet\\D2P\data\Author" => __DIR__ . "/../classes/Author.php", - "adrienpayet\\D2P\\data\\location\\Location" => __DIR__ . "/../classes/location/Location.php", - "adrienpayet\\D2P\\data\\location\\PageDetails" => __DIR__ . "/../classes/location/PageDetails.php", - "adrienpayet\\D2P\\data\\location\\ProjectDetails" => __DIR__ . "/../classes/location/ProjectDetails.php", - "adrienpayet\\D2P\\data\\location\\FileDetails" => __DIR__ . "/../classes/location/FileDetails.php", + // Nouvelles classes - Système de providers + "adrienpayet\\notifications\\NotificationProvider" => __DIR__ . "/src/NotificationProvider.php", + "adrienpayet\\notifications\\NotificationCollector" => __DIR__ . "/src/NotificationCollector.php", + "adrienpayet\\notifications\\providers\\CommentProvider" => __DIR__ . "/src/providers/CommentProvider.php", + "adrienpayet\\notifications\\providers\\ReplyProvider" => __DIR__ . "/src/providers/ReplyProvider.php", + "adrienpayet\\notifications\\providers\\ProjectRequestProvider" => __DIR__ . "/src/providers/ProjectRequestProvider.php", + "adrienpayet\\notifications\\providers\\AppointmentRequestProvider" => __DIR__ . "/src/providers/AppointmentRequestProvider.php", + "adrienpayet\\notifications\\providers\\ContentProvider" => __DIR__ . "/src/providers/ContentProvider.php", + + // Anciennes classes - Gardées pour rétro-compatibilité pendant migration + "adrienpayet\\notifications\\Notification" => __DIR__ . "/src/Notification.php", + "adrienpayet\\notifications\\NotificationsPage" => __DIR__ . "/src/NotificationsPage.php", + + // Classes partagées + "adrienpayet\\D2P\\data\\Position" => __DIR__ . "/../classes/Position.php", + "adrienpayet\\D2P\\data\\Author" => __DIR__ . "/../classes/Author.php", + "adrienpayet\\D2P\\data\\location\\Location" => __DIR__ . "/../classes/location/Location.php", + "adrienpayet\\D2P\\data\\location\\PageDetails" => __DIR__ . "/../classes/location/PageDetails.php", + "adrienpayet\\D2P\\data\\location\\ProjectDetails" => __DIR__ . "/../classes/location/ProjectDetails.php", + "adrienpayet\\D2P\\data\\location\\FileDetails" => __DIR__ . "/../classes/location/FileDetails.php", ]); +// Créer et configurer le collector +$collector = new NotificationCollector(); +$collector->register(new CommentProvider()); +$collector->register(new ReplyProvider()); +$collector->register(new ProjectRequestProvider()); +$collector->register(new AppointmentRequestProvider()); +$collector->register(new ContentProvider()); Kirby::plugin("adrienpayet/pdc-notifications", [ - "routes" => [ - require(__DIR__ . "/routes/readAll.php"), - require(__DIR__ . "/routes/read.php") - ], + "options" => [ + "collector" => $collector + ], + "routes" => [ + // Nouvelles routes + require(__DIR__ . "/routes/mark-as-read.php"), + require(__DIR__ . "/routes/mark-all-read.php"), + // Anciennes routes - Gardées pour rétro-compatibilité + require(__DIR__ . "/routes/readAll.php"), + require(__DIR__ . "/routes/read.php"), + ], ]); diff --git a/public/site/plugins/notifications/routes/mark-all-read.php b/public/site/plugins/notifications/routes/mark-all-read.php new file mode 100644 index 0000000..c06cc80 --- /dev/null +++ b/public/site/plugins/notifications/routes/mark-all-read.php @@ -0,0 +1,42 @@ + '(:all)mark-all-notifications-read.json', + 'method' => 'POST', + 'action' => function () { + try { + $user = kirby()->user(); + if (!$user) { + throw new Exception('User not authenticated'); + } + + $collector = kirby()->option('adrienpayet.pdc-notifications.collector'); + if (!$collector) { + throw new Exception('NotificationCollector not initialized'); + } + + // Récupérer les projets selon le rôle + if ($user->role()->name() === 'admin') { + $projects = page('projects')->children()->toArray(); + } else { + $projects = $user->projects()->toPages()->toArray(); + } + + $count = $collector->markAllAsRead($projects, $user); + + return json_encode([ + 'status' => 'success', + 'message' => "$count notifications marked as read" + ]); + } catch (\Throwable $th) { + return json_encode([ + 'status' => 'error', + 'message' => $th->getMessage() + ]); + } + } +]; diff --git a/public/site/plugins/notifications/routes/mark-as-read.php b/public/site/plugins/notifications/routes/mark-as-read.php new file mode 100644 index 0000000..736b39f --- /dev/null +++ b/public/site/plugins/notifications/routes/mark-as-read.php @@ -0,0 +1,46 @@ + '(:all)mark-notification-read.json', + 'method' => 'POST', + 'action' => function () { + $json = file_get_contents('php://input'); + $data = json_decode($json); + + if (!$data || !isset($data->type) || !isset($data->id)) { + return json_encode([ + 'status' => 'error', + 'message' => 'Missing required fields: type, id' + ]); + } + + try { + $collector = kirby()->option('adrienpayet.pdc-notifications.collector'); + + if (!$collector) { + throw new Exception('NotificationCollector not initialized'); + } + + $success = $collector->markAsRead( + $data->type, + $data->id, + (array) $data, + kirby()->user() + ); + + return json_encode([ + 'status' => $success ? 'success' : 'error', + 'message' => $success ? 'Notification marked as read' : 'Failed to mark notification as read' + ]); + } catch (\Throwable $th) { + return json_encode([ + 'status' => 'error', + 'message' => $th->getMessage() + ]); + } + } +]; diff --git a/public/site/plugins/notifications/src/NotificationCollector.php b/public/site/plugins/notifications/src/NotificationCollector.php new file mode 100644 index 0000000..113b041 --- /dev/null +++ b/public/site/plugins/notifications/src/NotificationCollector.php @@ -0,0 +1,123 @@ +providers[$provider->getType()] = $provider; + } + + /** + * Collecte toutes les notifications de tous les providers pour un projet. + * + * @param Page $project Le projet à scanner + * @param User $user L'utilisateur courant + * @return array Liste triée par date décroissante + */ + public function collect(Page $project, User $user): array + { + $all = []; + + foreach ($this->providers as $provider) { + try { + $notifications = $provider->collect($project, $user); + $all = array_merge($all, $notifications); + } catch (\Throwable $e) { + // Log l'erreur mais continue avec les autres providers + error_log("NotificationCollector: Error in {$provider->getType()}: " . $e->getMessage()); + } + } + + // Trier par date décroissante + usort($all, function ($a, $b) { + $dateA = strtotime($a['date'] ?? '0'); + $dateB = strtotime($b['date'] ?? '0'); + return $dateB - $dateA; + }); + + return $all; + } + + /** + * Marque une notification comme lue en déléguant au bon provider. + * + * @param string $type Le type de notification + * @param string $id L'identifiant de la notification + * @param array $location Informations de localisation + * @param User $user L'utilisateur qui marque comme lu + * @return bool True si succès + */ + public function markAsRead(string $type, string $id, array $location, User $user): bool + { + if (!isset($this->providers[$type])) { + error_log("NotificationCollector: Unknown notification type: $type"); + return false; + } + + try { + return $this->providers[$type]->markAsRead($id, $location, $user); + } catch (\Throwable $e) { + error_log("NotificationCollector: Error marking as read: " . $e->getMessage()); + return false; + } + } + + /** + * Marque toutes les notifications comme lues pour un utilisateur. + * + * @param Page[] $projects Liste des projets + * @param User $user L'utilisateur + * @return int Nombre de notifications marquées comme lues + */ + public function markAllAsRead(array $projects, User $user): int + { + $count = 0; + + foreach ($projects as $project) { + $notifications = $this->collect($project, $user); + + foreach ($notifications as $notification) { + if (!($notification['isRead'] ?? false)) { + $success = $this->markAsRead( + $notification['type'], + $notification['id'], + $notification, + $user + ); + if ($success) { + $count++; + } + } + } + } + + return $count; + } + + /** + * Retourne la liste des types de notifications enregistrés. + */ + public function getRegisteredTypes(): array + { + return array_keys($this->providers); + } +} diff --git a/public/site/plugins/notifications/src/NotificationProvider.php b/public/site/plugins/notifications/src/NotificationProvider.php new file mode 100644 index 0000000..cc11906 --- /dev/null +++ b/public/site/plugins/notifications/src/NotificationProvider.php @@ -0,0 +1,42 @@ +hasOptimizationRequest()->isFalse()) { + return []; + } + + // Vérifier que les champs requis existent + if ($project->optimizationAuthor()->isEmpty()) { + return []; + } + + $userUuid = (string) $user->uuid(); + $authorUuid = $project->optimizationAuthor()->value(); + + // Ne pas notifier l'auteur de sa propre demande + if ($authorUuid === $userUuid) { + return []; + } + + $readby = $project->optimizationReadby()->isNotEmpty() + ? Yaml::decode($project->optimizationReadby()->value()) + : []; + + if (!is_array($readby)) { + $readby = []; + } + + return [[ + 'id' => 'appointment-request-' . (string) $project->uuid(), + 'type' => 'appointment-request', + 'text' => $project->optimizationRequestDetails()->value() ?? '', + 'author' => [ + 'uuid' => $authorUuid, + 'name' => $project->optimizationAuthorName()->value() ?? '', + 'email' => $project->optimizationAuthorEmail()->value() ?? '', + 'role' => 'client', + ], + 'date' => $project->optimizationDate()->value() ?? '', + 'location' => [ + 'page' => [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + 'template' => 'project', + ], + 'project' => [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + ] + ], + 'readby' => $readby, + 'isRead' => in_array($userUuid, $readby), + '_projectUri' => $project->uri(), + ]]; + } + + public function markAsRead(string $id, array $location, User $user): bool + { + $projectUri = $location['_projectUri'] ?? null; + if (!$projectUri) { + return false; + } + + $project = page($projectUri); + if (!$project) { + return false; + } + + $readby = $project->optimizationReadby()->isNotEmpty() + ? Yaml::decode($project->optimizationReadby()->value()) + : []; + + if (!is_array($readby)) { + $readby = []; + } + + $userUuid = (string) $user->uuid(); + + if (in_array($userUuid, $readby)) { + return true; + } + + $readby[] = $userUuid; + + $project->update([ + 'optimizationReadby' => array_unique($readby) + ]); + + return true; + } +} diff --git a/public/site/plugins/notifications/src/providers/CommentProvider.php b/public/site/plugins/notifications/src/providers/CommentProvider.php new file mode 100644 index 0000000..fc92ce1 --- /dev/null +++ b/public/site/plugins/notifications/src/providers/CommentProvider.php @@ -0,0 +1,172 @@ +uuid(); + + // Parcourir toutes les étapes du projet + foreach ($project->children() as $step) { + // Parcourir tous les fichiers de chaque étape + foreach ($step->files() as $file) { + if ($file->comments()->isEmpty()) { + continue; + } + + $comments = Yaml::decode($file->comments()->value()); + if (!is_array($comments)) { + continue; + } + + foreach ($comments as $comment) { + // Ignorer les commentaires de type reply (gérés par ReplyProvider) + if (($comment['type'] ?? 'comment') === 'comment-reply') { + continue; + } + + // Ne pas notifier l'auteur de son propre commentaire + $authorUuid = $comment['author']['uuid'] ?? ''; + if ($authorUuid === $userUuid) { + continue; + } + + $readby = $comment['readby'] ?? []; + + $location = $comment['location'] ?? []; + // Assurer que location.project existe toujours + if (!isset($location['project'])) { + $location['project'] = [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + ]; + } + + $notifications[] = [ + 'id' => $comment['id'], + 'type' => 'comment', + 'text' => $comment['text'] ?? '', + 'author' => $comment['author'] ?? [], + 'date' => $comment['date'] ?? '', + 'location' => $location, + 'position' => $comment['position'] ?? [], + 'readby' => $readby, + 'isRead' => in_array($userUuid, $readby), + // Métadonnées pour markAsRead + '_file' => (string) $file->uuid(), + '_stepUri' => $step->uri(), + ]; + } + } + + // Parcourir aussi les sous-pages (ex: tracks dans virtual-sample) + foreach ($step->children() as $subPage) { + foreach ($subPage->files() as $file) { + if ($file->comments()->isEmpty()) { + continue; + } + + $comments = Yaml::decode($file->comments()->value()); + if (!is_array($comments)) { + continue; + } + + foreach ($comments as $comment) { + if (($comment['type'] ?? 'comment') === 'comment-reply') { + continue; + } + + $authorUuid = $comment['author']['uuid'] ?? ''; + if ($authorUuid === $userUuid) { + continue; + } + + $readby = $comment['readby'] ?? []; + + $location = $comment['location'] ?? []; + // Assurer que location.project existe toujours + if (!isset($location['project'])) { + $location['project'] = [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + ]; + } + + $notifications[] = [ + 'id' => $comment['id'], + 'type' => 'comment', + 'text' => $comment['text'] ?? '', + 'author' => $comment['author'] ?? [], + 'date' => $comment['date'] ?? '', + 'location' => $location, + 'position' => $comment['position'] ?? [], + 'readby' => $readby, + 'isRead' => in_array($userUuid, $readby), + '_file' => (string) $file->uuid(), + '_stepUri' => $subPage->uri(), + ]; + } + } + } + } + + return $notifications; + } + + public function markAsRead(string $id, array $location, User $user): bool + { + $fileUuid = $location['_file'] ?? null; + if (!$fileUuid) { + return false; + } + + // Trouver le fichier par UUID (peut être avec ou sans préfixe file://) + $fileUri = str_starts_with($fileUuid, 'file://') ? $fileUuid : 'file://' . $fileUuid; + $file = kirby()->file($fileUri); + if (!$file) { + return false; + } + + $comments = Yaml::decode($file->comments()->value()); + if (!is_array($comments)) { + return false; + } + + $userUuid = (string) $user->uuid(); + $updated = false; + + foreach ($comments as &$comment) { + if ($comment['id'] === $id) { + $comment['readby'] = $comment['readby'] ?? []; + if (!in_array($userUuid, $comment['readby'])) { + $comment['readby'][] = $userUuid; + $updated = true; + } + break; + } + } + + if ($updated) { + $file->update(['comments' => $comments]); + } + + return $updated; + } +} diff --git a/public/site/plugins/notifications/src/providers/ContentProvider.php b/public/site/plugins/notifications/src/providers/ContentProvider.php new file mode 100644 index 0000000..3f09800 --- /dev/null +++ b/public/site/plugins/notifications/src/providers/ContentProvider.php @@ -0,0 +1,128 @@ +uuid(); + + // Chercher les briefs validés (client-brief et extended-brief) + $briefTemplates = ['client-brief', 'extended-brief']; + + foreach ($project->children() as $step) { + if (!in_array($step->intendedTemplate()->name(), $briefTemplates)) { + continue; + } + + // Pas de notification si le brief n'est pas validé + if ($step->isValidated()->isFalse()) { + continue; + } + + // Vérifier que les champs requis existent + if ($step->validatedBy()->isEmpty()) { + continue; + } + + $authorUuid = $step->validatedBy()->value(); + + // Ne pas notifier l'auteur de sa propre validation + if ($authorUuid === $userUuid) { + continue; + } + + $readby = $step->validationReadby()->isNotEmpty() + ? Yaml::decode($step->validationReadby()->value()) + : []; + + if (!is_array($readby)) { + $readby = []; + } + + $stepLabel = $step->intendedTemplate()->name() === 'client-brief' + ? 'Brief client' + : 'Brief étendu'; + + $notifications[] = [ + 'id' => 'content-' . (string) $step->uuid(), + 'type' => 'content', + 'text' => "Nouveau $stepLabel validé", + 'author' => [ + 'uuid' => $authorUuid, + 'name' => $step->validatedByName()->value() ?? '', + 'email' => $step->validatedByEmail()->value() ?? '', + 'role' => 'client', + ], + 'date' => $step->validatedAt()->value() ?? '', + 'location' => [ + 'page' => [ + 'uri' => $step->uri(), + 'title' => (string) $step->title(), + 'template' => $step->intendedTemplate()->name(), + ], + 'project' => [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + ] + ], + 'readby' => $readby, + 'isRead' => in_array($userUuid, $readby), + '_briefUri' => $step->uri(), + ]; + } + + return $notifications; + } + + public function markAsRead(string $id, array $location, User $user): bool + { + $briefUri = $location['_briefUri'] ?? null; + if (!$briefUri) { + return false; + } + + $brief = page($briefUri); + if (!$brief) { + return false; + } + + $readby = $brief->validationReadby()->isNotEmpty() + ? Yaml::decode($brief->validationReadby()->value()) + : []; + + if (!is_array($readby)) { + $readby = []; + } + + $userUuid = (string) $user->uuid(); + + if (in_array($userUuid, $readby)) { + return true; + } + + $readby[] = $userUuid; + + $brief->update([ + 'validationReadby' => array_unique($readby) + ]); + + return true; + } +} diff --git a/public/site/plugins/notifications/src/providers/ProjectRequestProvider.php b/public/site/plugins/notifications/src/providers/ProjectRequestProvider.php new file mode 100644 index 0000000..cc3d186 --- /dev/null +++ b/public/site/plugins/notifications/src/providers/ProjectRequestProvider.php @@ -0,0 +1,111 @@ +isClientRequest()->isFalse()) { + return []; + } + + // Vérifier que les champs requis existent + if ($project->requestAuthor()->isEmpty()) { + return []; + } + + $userUuid = (string) $user->uuid(); + $authorUuid = $project->requestAuthor()->value(); + + // Ne pas notifier l'auteur de sa propre demande + if ($authorUuid === $userUuid) { + return []; + } + + $readby = $project->requestReadby()->isNotEmpty() + ? Yaml::decode($project->requestReadby()->value()) + : []; + + if (!is_array($readby)) { + $readby = []; + } + + return [[ + 'id' => 'project-request-' . (string) $project->uuid(), + 'type' => 'project-request', + 'text' => $project->requestDetails()->value() ?? '', + 'author' => [ + 'uuid' => $authorUuid, + 'name' => $project->requestAuthorName()->value() ?? '', + 'email' => $project->requestAuthorEmail()->value() ?? '', + 'role' => 'client', + ], + 'date' => $project->requestDate()->value() ?? '', + 'location' => [ + 'page' => [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + 'template' => 'project', + ], + 'project' => [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + ] + ], + 'readby' => $readby, + 'isRead' => in_array($userUuid, $readby), + '_projectUri' => $project->uri(), + ]]; + } + + public function markAsRead(string $id, array $location, User $user): bool + { + $projectUri = $location['_projectUri'] ?? null; + if (!$projectUri) { + return false; + } + + $project = page($projectUri); + if (!$project) { + return false; + } + + $readby = $project->requestReadby()->isNotEmpty() + ? Yaml::decode($project->requestReadby()->value()) + : []; + + if (!is_array($readby)) { + $readby = []; + } + + $userUuid = (string) $user->uuid(); + + if (in_array($userUuid, $readby)) { + return true; // Déjà lu + } + + $readby[] = $userUuid; + + $project->update([ + 'requestReadby' => array_unique($readby) + ]); + + return true; + } +} diff --git a/public/site/plugins/notifications/src/providers/ReplyProvider.php b/public/site/plugins/notifications/src/providers/ReplyProvider.php new file mode 100644 index 0000000..467b245 --- /dev/null +++ b/public/site/plugins/notifications/src/providers/ReplyProvider.php @@ -0,0 +1,142 @@ +uuid(); + + // Parcourir toutes les étapes du projet + foreach ($project->children() as $step) { + $this->collectFromPage($step, $project, $userUuid, $notifications); + + // Parcourir aussi les sous-pages (ex: tracks) + foreach ($step->children() as $subPage) { + $this->collectFromPage($subPage, $project, $userUuid, $notifications); + } + } + + return $notifications; + } + + private function collectFromPage(Page $page, Page $project, string $userUuid, array &$notifications): void + { + foreach ($page->files() as $file) { + if ($file->comments()->isEmpty()) { + continue; + } + + $comments = Yaml::decode($file->comments()->value()); + if (!is_array($comments)) { + continue; + } + + foreach ($comments as $comment) { + $replies = $comment['replies'] ?? []; + + foreach ($replies as $reply) { + // Ne pas notifier l'auteur de sa propre réponse + $authorUuid = $reply['author']['uuid'] ?? ''; + if ($authorUuid === $userUuid) { + continue; + } + + $readby = $reply['readby'] ?? []; + + $location = $reply['location'] ?? $comment['location'] ?? []; + // Assurer que location.project existe toujours + if (!isset($location['project'])) { + $location['project'] = [ + 'uri' => $project->uri(), + 'title' => (string) $project->title(), + ]; + } + + $notifications[] = [ + 'id' => $reply['id'], + 'type' => 'comment-reply', + 'text' => $reply['text'] ?? '', + 'author' => $reply['author'] ?? [], + 'date' => $reply['date'] ?? '', + 'location' => $location, + 'position' => $reply['position'] ?? $comment['position'] ?? [], + 'readby' => $readby, + 'isRead' => in_array($userUuid, $readby), + // Métadonnées pour markAsRead + '_file' => (string) $file->uuid(), + '_parentCommentId' => $comment['id'], + '_stepUri' => $page->uri(), + ]; + } + } + } + } + + public function markAsRead(string $id, array $location, User $user): bool + { + $fileUuid = $location['_file'] ?? null; + $parentCommentId = $location['_parentCommentId'] ?? null; + + if (!$fileUuid) { + return false; + } + + // Trouver le fichier par UUID (peut être avec ou sans préfixe file://) + $fileUri = str_starts_with($fileUuid, 'file://') ? $fileUuid : 'file://' . $fileUuid; + $file = kirby()->file($fileUri); + if (!$file) { + return false; + } + + $comments = Yaml::decode($file->comments()->value()); + if (!is_array($comments)) { + return false; + } + + $userUuid = (string) $user->uuid(); + $updated = false; + + foreach ($comments as &$comment) { + // Si on a l'ID du parent, l'utiliser pour cibler + if ($parentCommentId && $comment['id'] !== $parentCommentId) { + continue; + } + + $replies = &$comment['replies'] ?? []; + + foreach ($replies as &$reply) { + if ($reply['id'] === $id) { + $reply['readby'] = $reply['readby'] ?? []; + if (!in_array($userUuid, $reply['readby'])) { + $reply['readby'][] = $userUuid; + $updated = true; + } + break 2; + } + } + } + + if ($updated) { + $file->update(['comments' => $comments]); + } + + return $updated; + } +} diff --git a/public/site/templates/projects.json.php b/public/site/templates/projects.json.php index ba74b69..cf5a52a 100644 --- a/public/site/templates/projects.json.php +++ b/public/site/templates/projects.json.php @@ -7,24 +7,37 @@ if (!$kirby->user()) { ]); } -function getProjectData($project) -{ +// Récupérer le collector de notifications +$notificationCollector = $kirby->option('adrienpayet.pdc-notifications.collector'); + +function getProjectData($project, $user, $collector) +{ + // Utiliser le nouveau système de notifications dérivées + $notifications = []; + if ($collector) { + try { + $notifications = $collector->collect($project, $user); + } catch (\Throwable $e) { + error_log("Error collecting notifications for project {$project->uri()}: " . $e->getMessage()); + $notifications = []; + } + } $data = [ - 'title' => $project->title()->value(), - 'url' => $project->url(), - 'uri' => '/' . $project->uri(), - 'modified' => $project->modified('Y-MM-d'), - 'currentStep' => $project->currentStep()->value(), - 'status' => $project->status(), - 'logo' => $project->client()->toPage() ? $project->client()->toPage()->logo()->toFile()->url() : '', - 'steps' => $project->getSteps(), - 'notifications' => Yaml::decode($project->notifications()->value), - 'uuid' => (string) $project->uuid(), - 'slug' => (string) $project->slug(), - 'isDTLEnabled' => $project->isDTLEnabled()->isTrue(), - 'hasOptimizationRequest' => $project->hasOptimizationRequest()->isTrue(), - ]; + 'title' => $project->title()->value(), + 'url' => $project->url(), + 'uri' => '/' . $project->uri(), + 'modified' => $project->modified('Y-MM-d'), + 'currentStep' => $project->currentStep()->value(), + 'status' => $project->status(), + 'logo' => $project->client()->toPage() ? $project->client()->toPage()->logo()->toFile()->url() : '', + 'steps' => $project->getSteps(), + 'notifications' => $notifications, + 'uuid' => (string) $project->uuid(), + 'slug' => (string) $project->slug(), + 'isDTLEnabled' => $project->isDTLEnabled()->isTrue(), + 'hasOptimizationRequest' => $project->hasOptimizationRequest()->isTrue(), + ]; if ($project->isDTLEnabled()) { $data['designToLight'] = processDTLProposals($project); @@ -33,8 +46,12 @@ function getProjectData($project) return $data; } +$currentUser = $kirby->user(); + try { - $children = $kirby->user()->role() == 'admin' ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project))->values() : $kirby->user()->projects()->toPages()->map(fn($project) => getProjectData($project))->values(); + $children = $currentUser->role() == 'admin' + ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values() + : $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values(); } catch (\Throwable $th) { throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); $children = []; diff --git a/src/stores/api.js b/src/stores/api.js index d12707a..cbffdd8 100644 --- a/src/stores/api.js +++ b/src/stores/api.js @@ -163,6 +163,34 @@ export const useApiStore = defineStore("api", () => { } } + /** + * Marque une notification comme lue. + * @param {Object} notification - L'objet notification complet (avec type, id, _file, _projectUri, etc.) + */ + async function markNotificationRead(notification) { + const headers = { + method: "POST", + body: JSON.stringify(notification), + }; + try { + const response = await fetch("/mark-notification-read.json", headers); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + if (data.status === "error") { + throw new Error(data.message); + } + // Mettre à jour le store local + userStore.markNotificationRead(notification.id, notification.project?.uri || notification._projectUri); + return data; + } catch (error) { + console.error("Erreur lors du marquage de la notification:", error); + throw error; + } + } + + // Ancienne fonction gardée pour rétro-compatibilité async function readNotification(notificationId, projectId) { const headers = { method: "POST", @@ -215,6 +243,31 @@ export const useApiStore = defineStore("api", () => { } } + /** + * Marque toutes les notifications comme lues (nouveau système). + */ + async function markAllNotificationsRead() { + try { + const response = await fetch("/mark-all-notifications-read.json", { + method: "POST", + }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + if (data.status === "error") { + throw new Error(data.message); + } + userStore.markAllNotificationsRead(); + console.log("Toutes les notifications ont été marquées comme lues."); + return data; + } catch (error) { + console.error("Erreur lors du marquage de toutes les notifications:", error); + throw error; + } + } + + // Ancienne fonction gardée pour rétro-compatibilité async function readAllNotifications() { try { const response = await fetch("/read-all-notifications.json"); @@ -243,6 +296,10 @@ export const useApiStore = defineStore("api", () => { updateComment, deleteComment, replyComment, + // Nouvelles fonctions + markNotificationRead, + markAllNotificationsRead, + // Anciennes fonctions (rétro-compatibilité) readNotification, readAllNotifications, validateBrief, diff --git a/src/stores/user.js b/src/stores/user.js index 7beaf69..b3404df 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -11,48 +11,76 @@ export const useUserStore = defineStore('user', () => { const { projects } = storeToRefs(useProjectsStore()); + /** + * Liste des notifications agrégées depuis tous les projets. + * Les notifications sont maintenant dérivées côté backend avec isRead pré-calculé. + */ const notifications = computed(() => { - return projects.value?.flatMap((project) => { + if (!projects.value || !user.value) return []; + + return projects.value.flatMap((project) => { if (!project.notifications) return []; - return project.notifications - .filter((notification) => notification.author.uuid !== user.value.uuid) - .map((notification) => ({ - ...notification, - project: project, - isRead: notification.readby?.includes(user.value.uuid), - })); + return project.notifications.map((notification) => ({ + ...notification, + project: project, + // isRead est maintenant fourni par le backend + })); }); }); - function readNotification(notificationId, projectId) { - console.log('Read notification', notificationId, projectId); + /** + * Marque une notification comme lue dans le store local. + * @param {string} notificationId - L'ID de la notification + * @param {string} projectUri - L'URI du projet (optionnel, pour retrouver le projet) + */ + function markNotificationRead(notificationId, projectUri = null) { + if (!user.value?.uuid) return; + + projects.value = projects.value.map((project) => { + // Si projectUri fourni, cibler le bon projet + if (projectUri && project.uri !== projectUri && `/${project.uri}` !== projectUri) { + return project; + } + + return { + ...project, + notifications: (project.notifications || []).map((notification) => + notification.id === notificationId + ? { + ...notification, + isRead: true, + readby: [...new Set([...(notification.readby || []), user.value.uuid])], + } + : notification + ), + }; + }); + } + + /** + * Marque toutes les notifications comme lues dans le store local. + */ + function markAllNotificationsRead() { + if (!user.value?.uuid) return; + projects.value = projects.value.map((project) => ({ ...project, - notifications: - project.uuid === projectId || project.uri === projectId - ? project.notifications.map((notification) => - notification.id === notificationId - ? { - ...notification, - readby: [ - ...new Set([...notification.readby, user.value.uuid]), - ], - } - : notification - ) - : project.notifications, + notifications: (project.notifications || []).map((notification) => ({ + ...notification, + isRead: true, + readby: [...new Set([...(notification.readby || []), user.value.uuid])], + })), })); } + // Anciennes fonctions gardées pour rétro-compatibilité + function readNotification(notificationId, projectId) { + markNotificationRead(notificationId, projectId); + } + function readAllNotifications() { - projects.value = projects.value.map((project) => ({ - ...project, - notifications: project.notifications.map((notification) => ({ - ...notification, - readby: [...new Set([...notification.readby, user.value.uuid])], - })), - })); + markAllNotificationsRead(); } function canEditComment(comment) { @@ -63,6 +91,10 @@ export const useUserStore = defineStore('user', () => { user, isLogged, notifications, + // Nouvelles fonctions + markNotificationRead, + markAllNotificationsRead, + // Anciennes fonctions (rétro-compatibilité) readNotification, readAllNotifications, canEditComment, diff --git a/src/views/Notifications.vue b/src/views/Notifications.vue index c7db5b1..053eeba 100644 --- a/src/views/Notifications.vue +++ b/src/views/Notifications.vue @@ -119,14 +119,24 @@ function changeTab(newValue) { function readAll() { try { - api.readAllNotifications(); + api.markAllNotificationsRead(); } catch (error) { console.log('Could not read all notifications : ', error); } } // Functions -function handleNotificationClick(notification) { +async function handleNotificationClick(notification) { + // Marquer la notification comme lue + if (!notification.isRead) { + try { + await api.markNotificationRead(notification); + } catch (error) { + console.log('Could not mark notification as read:', error); + } + } + + // Naviguer vers la cible const href = notification.type === 'appointment-request' ? getHref(notification) + '?tab=designToLight' diff --git a/vite.config.js b/vite.config.js index c235442..da73536 100644 --- a/vite.config.js +++ b/vite.config.js @@ -17,6 +17,7 @@ export default defineConfig(({ mode }) => { minify: mode === 'production' ? 'esbuild' : false, }, server: { + cors: true, watch: { ignored: [ '**/node_modules/**', From 6ff59e9b07454c8ffa5f625f901a7e60d02adf65 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 10:44:30 +0100 Subject: [PATCH 03/52] =?UTF-8?q?Fix=20:=20URL=20correcte=20pour=20notific?= =?UTF-8?q?ations=20de=20brief=20valid=C3=A9=20depuis=20PDF=20+=20redirect?= =?UTF-8?q?=20briefs=20vides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème 1 : Les notifications de brief validé depuis un PDF renvoyaient vers /projects/xxx/client-brief au lieu de l'URL complète avec dialog et fileIndex. Problème 2 : Les URL /projects/xxx/client-brief pour des briefs non créés affichaient une page vide au lieu de rediriger vers le kanban. Solutions : - Stocker validationDialogUri lors de la validation du brief - Utiliser ce dialogUri dans ContentProvider et Notifications.vue - Rediriger vers le projet parent si brief vide et non validé Co-Authored-By: Claude Sonnet 4.5 --- public/site/blueprints/pages/client-brief.yml | 3 ++- .../site/blueprints/pages/extended-brief.yml | 2 ++ public/site/config/routes/validate-brief.php | 11 ++++++++-- .../src/providers/ContentProvider.php | 11 ++++++++-- src/views/Brief.vue | 21 +++++++++++++++++-- src/views/Notifications.vue | 5 +++++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/public/site/blueprints/pages/client-brief.yml b/public/site/blueprints/pages/client-brief.yml index 9df9168..d7b2100 100644 --- a/public/site/blueprints/pages/client-brief.yml +++ b/public/site/blueprints/pages/client-brief.yml @@ -24,7 +24,6 @@ tabs: type: hidden isValidated: type: hidden - # Champs pour notification "content" (brief validé) validatedBy: type: hidden validatedByName: @@ -35,6 +34,8 @@ tabs: type: hidden validationReadby: type: hidden + validationDialogUri: + type: hidden pdf: label: PDF type: files diff --git a/public/site/blueprints/pages/extended-brief.yml b/public/site/blueprints/pages/extended-brief.yml index 477d4a1..5c96ee2 100644 --- a/public/site/blueprints/pages/extended-brief.yml +++ b/public/site/blueprints/pages/extended-brief.yml @@ -35,6 +35,8 @@ tabs: type: hidden validationReadby: type: hidden + validationDialogUri: + type: hidden pdf: label: PDF type: files diff --git a/public/site/config/routes/validate-brief.php b/public/site/config/routes/validate-brief.php index fb2bf86..5b16c44 100644 --- a/public/site/config/routes/validate-brief.php +++ b/public/site/config/routes/validate-brief.php @@ -15,7 +15,7 @@ return [ $timezone = new DateTimeZone('Europe/Paris'); $dateTime = new DateTime('now', $timezone); - $newPage = $page->update([ + $updateData = [ 'isValidated' => 'true', // Métadonnées pour le système de notifications dérivées 'validatedBy' => (string) $user->uuid(), @@ -23,7 +23,14 @@ return [ 'validatedByEmail' => (string) $user->email(), 'validatedAt' => $dateTime->format('Y-m-d\TH:i:sP'), 'validationReadby' => [], - ]); + ]; + + // Si un dialogUri est fourni (validation depuis PDF), le stocker + if (isset($data->dialogUri) && !empty($data->dialogUri)) { + $updateData['validationDialogUri'] = (string) $data->dialogUri; + } + + $newPage = $page->update($updateData); // Note: Les notifications sont maintenant dérivées. // Plus besoin d'appeler createNotification(). diff --git a/public/site/plugins/notifications/src/providers/ContentProvider.php b/public/site/plugins/notifications/src/providers/ContentProvider.php index 3f09800..94698ec 100644 --- a/public/site/plugins/notifications/src/providers/ContentProvider.php +++ b/public/site/plugins/notifications/src/providers/ContentProvider.php @@ -60,10 +60,10 @@ class ContentProvider implements NotificationProvider ? 'Brief client' : 'Brief étendu'; - $notifications[] = [ + $notification = [ 'id' => 'content-' . (string) $step->uuid(), 'type' => 'content', - 'text' => "Nouveau $stepLabel validé", + 'text' => 'Nouveau ' . strtolower($stepLabel) . ' validé', 'author' => [ 'uuid' => $authorUuid, 'name' => $step->validatedByName()->value() ?? '', @@ -86,6 +86,13 @@ class ContentProvider implements NotificationProvider 'isRead' => in_array($userUuid, $readby), '_briefUri' => $step->uri(), ]; + + // Ajouter le dialogUri si présent (validation depuis PDF) + if ($step->validationDialogUri()->isNotEmpty()) { + $notification['dialogUri'] = $step->validationDialogUri()->value(); + } + + $notifications[] = $notification; } return $notifications; diff --git a/src/views/Brief.vue b/src/views/Brief.vue index acc132f..5d5963e 100644 --- a/src/views/Brief.vue +++ b/src/views/Brief.vue @@ -29,7 +29,7 @@ diff --git a/src/components/project/brief/ModeSelection.vue b/src/components/project/brief/ModeSelection.vue deleted file mode 100644 index 317ca3c..0000000 --- a/src/components/project/brief/ModeSelection.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - From 9d12ccb209823391a42534f26d3b45bc64912808 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 10:58:41 +0100 Subject: [PATCH 06/52] Fix : ne compter que les commentaires des images, pas ceux du PDF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : Dans le kanban, la carte du brief client custom (Images) affichait aussi le nombre de commentaires du PDF, alors qu'il n'y a pas de système de commentaires pour les images du brief custom. Solution : Filtrer pour ne compter que les commentaires des fichiers de type 'image', et non tous les fichiers du step. Bonus : Suppression du paramètre obsolète ?step=images dans ClientBrief.vue Co-Authored-By: Claude Sonnet 4.5 --- src/components/project/cards/ClientBrief.vue | 2 +- src/components/project/cards/Images.vue | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/project/cards/ClientBrief.vue b/src/components/project/cards/ClientBrief.vue index ca74589..27258f6 100644 --- a/src/components/project/cards/ClientBrief.vue +++ b/src/components/project/cards/ClientBrief.vue @@ -49,6 +49,6 @@ const pdf = computed(() => { }); function goToImagesBrief() { - router.push(location.pathname + "/client-brief?step=images"); + router.push(location.pathname + "/client-brief"); } diff --git a/src/components/project/cards/Images.vue b/src/components/project/cards/Images.vue index 463bfcc..c79fec9 100644 --- a/src/components/project/cards/Images.vue +++ b/src/components/project/cards/Images.vue @@ -56,8 +56,11 @@ const commentsCount = computed(() => { let count = 0; if (Array.isArray(step.files)) { + // Ne compter que les commentaires des images, pas des documents (PDFs) for (const file of step.files) { - count += file?.comments?.length || 0; + if (file.type === 'image') { + count += file?.comments?.length || 0; + } } } else { if (step.files?.dynamic) { From f614884da0708b5095373a248be4807e32654053 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 11:03:50 +0100 Subject: [PATCH 07/52] update project overview --- CLAUDE_PROJECT_OVERVIEW.md | 311 +++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 CLAUDE_PROJECT_OVERVIEW.md diff --git a/CLAUDE_PROJECT_OVERVIEW.md b/CLAUDE_PROJECT_OVERVIEW.md new file mode 100644 index 0000000..4657285 --- /dev/null +++ b/CLAUDE_PROJECT_OVERVIEW.md @@ -0,0 +1,311 @@ +# Design to Pack - Vue d'ensemble du projet + +Plateforme de gestion de projets de création de flacons de parfum pour Pochet du Courval. + +## Stack technique + +| Couche | Technologies | +|--------|-------------| +| **Backend** | Kirby CMS 4 (PHP), flat-file database | +| **Frontend** | Vue 3 + Vite 7, Pinia, Vue Router 4, PrimeVue 4.0 | +| **PDF** | @vue-pdf-viewer 2.5 | +| **3D** | Three.js (vue interactive 360) | +| **Déploiement** | GitLab CI/CD, rsync vers serveur | + +--- + +## Structure du projet + +``` +design-to-pack/ +├── src/ # App Vue.js +│ ├── assets/css/ # Styles globaux +│ ├── components/ # Composants réutilisables +│ │ ├── comments/ # Système de commentaires +│ │ ├── design-to-light/ # Feature DTL +│ │ ├── inspirations/ # Galerie inspirations +│ │ ├── notifications/ # Notifications +│ │ └── project/ # Composants projet +│ │ ├── cards/ # Cartes par type d'étape +│ │ ├── brief/ # Brief client (moodboard) +│ │ └── virtual-sample/ # Échantillon virtuel 3D +│ ├── router/ # Vue Router +│ ├── stores/ # Pinia stores +│ ├── utils/ # Utilitaires +│ ├── views/ # Pages principales +│ ├── main.js # Point d'entrée +│ └── App.vue # Composant racine +│ +├── public/ # Kirby CMS +│ ├── content/ # Données (flat-file) +│ │ ├── projects/ # Pages projets +│ │ ├── clients/ # Pages clients +│ │ ├── design-to-light/ # Page DTL +│ │ └── inspirations/ # Galerie inspirations +│ ├── site/ +│ │ ├── blueprints/ # Schémas de données +│ │ │ ├── pages/ # Blueprints des pages +│ │ │ ├── users/ # Blueprints utilisateurs +│ │ │ └── files/ # Blueprints fichiers +│ │ ├── templates/ # Templates PHP + JSON +│ │ ├── controllers/ # Contrôleurs +│ │ ├── models/ # Modèles PHP (Project, Client) +│ │ ├── plugins/ # Plugins custom +│ │ ├── snippets/ # Fragments réutilisables +│ │ └── config/ # Configuration + routes +│ └── media/ # Fichiers uploadés +│ +├── package.json +├── vite.config.js +└── .gitlab-ci.yml +``` + +--- + +## Plugins Kirby custom + +### 1. `classes/` - Classes partagées +Classes de données utilisées par comments et notifications. + +| Classe | Rôle | +|--------|------| +| `Author` | Auteur (name, email, uuid, role) | +| `Position` | Position x/y + pageIndex (marqueurs sur PDF) | +| `Location` | Localisation (page, file, parent) | +| `PageDetails` | Détails de page | +| `FileDetails` | Détails de fichier | +| `ProjectDetails` | Détails de projet | + +### 2. `comments/` - Système de commentaires +Plugin `adrienpayet/kirby4-comments` + +**Classes:** +- `BaseComment` - Classe de base +- `Comment` - Commentaire avec replies +- `Reply` - Réponse à un commentaire + +**Routes:** +| Route | Fichier | Description | +|-------|---------|-------------| +| `POST /create-comment.json` | `routes/create.php` | Créer un commentaire | +| `POST /update-comment.json` | `routes/update.php` | Modifier un commentaire | +| `POST /delete-comment.json` | `routes/delete.php` | Supprimer un commentaire | +| `POST /reply-comment.json` | `routes/reply.php` | Répondre à un commentaire | + +**Stockage:** Les commentaires sont stockés en YAML dans les métadonnées des fichiers. + +### 3. `notifications/` - Système de notifications +Plugin `adrienpayet/pdc-notifications` + +**Classes:** +- `Notification` - Notification (type, location, text, author, date, readby[]) +- `NotificationsPage` - Base pour pages avec notifications (extends Page) + +**Méthodes NotificationsPage:** +- `createNotification($data)` - Créer une notification +- `deleteNotification($id)` - Supprimer une notification +- `readNotification($id)` - Marquer comme lue (ajoute userUuid à readby) +- `readAllNotifications()` - Tout marquer comme lu + +**Routes:** +| Route | Description | +|-------|-------------| +| `POST /read-notification.json` | Marquer une notification comme lue | +| `POST /read-all-notifications.json` | Tout marquer comme lu | + +### 4. `user-projects/` - Projets autorisés par utilisateur +Plugin `adrienpayet/pdc-authorized-projects` + +**User methods:** +- `currentProjects()` - Projets actifs (listed) accessibles à l'utilisateur +- `archivedProjects()` - Projets archivés (unlisted) accessibles + +Logique: Admin = tous les projets, autres = seulement projets assignés. + +### 5. `helpers/` - Fonctions utilitaires + +| Fonction | Description | +|----------|-------------| +| `getFileData($file, $preserveQuality)` | Normalise les données fichier (thumb webp, cover, comments) | +| `getGlobalEvaluation($numberedGrade)` | Convertit note numérique en lettre A-E avec mention | +| `processDTLProposals($page)` | Traite les propositions Design to Light | +| `refreshProjectStepsCache($project, $steps)` | Rafraîchit le cache des étapes | + +### 6. `icons/` - Icônes custom panel +Plugin `adrienpayet/pochet-icons` - Icônes personnalisées pour le panel Kirby. + +### 7. `kql/` - Kirby Query Language +Plugin externe pour requêtes type GraphQL. + +### 8. `refresh-cache-button/` - Bouton refresh cache +Plugin externe ajoutant un bouton de rafraîchissement du cache dans le panel. + +--- + +## Modèles de données + +### Utilisateurs (3 rôles) + +| Rôle | Accès | +|------|-------| +| `admin` | Tous les projets, panel complet | +| `pochet` | Projets assignés uniquement, panel limité | +| `client` | Ses projets uniquement, pas de panel | + +### Projet (ProjectPage) +Hérite de `NotificationsPage`. + +**Champs principaux:** +- `title`, `status` (draft/listed/unlisted) +- `client` - Lien vers ClientPage +- `currentStep` - Étape courante +- `isDTLEnabled` - Design to Light activé + +**Étapes (children):** +1. `client-brief` - Brief client (PDF + moodboard) +2. `proposal` - Proposition commerciale (PDFs) +3. `extended-brief` - Brief étendu +4. `industrial-ideation` - Idéation industrielle (optionnel) +5. `virtual-sample` - Échantillon virtuel (pistes dynamiques + statiques) +6. `physical-sample` - Échantillon physique (médias) + +### Client (ClientPage) +- `logo`, `title` +- `projects()` - Tous les projets +- `projectsListed()` / `projectsUnlisted()` - Filtres par statut + +--- + +## Stores Pinia + +| Store | Fichier | Rôle | +|-------|---------|------| +| `api` | `stores/api.js` | Communication API (fetch, post, comments, notifications) | +| `user` | `stores/user.js` | Utilisateur courant, permissions | +| `page` | `stores/page.js` | Données de la page courante | +| `projects` | `stores/projects.js` | Liste des projets | +| `dialog` | `stores/dialog.js` | État des modales (contenu, fichier, commentaires) | +| `brief` | `stores/brief.js` | Gestion du brief client | +| `designToLight` | `stores/designToLight.js` | Feature DTL | +| `notifications` | `stores/notifications.js` | Notifications non lues | +| `virtualSample` | `stores/virtualSample.js` | État échantillon virtuel | +| `addImagesModal` | `stores/addImagesModal.js` | Modal ajout images | +| `project` | `stores/project.js` | Utilitaires projet | + +--- + +## Routes Vue + +| Path | Vue | Description | +|------|-----|-------------| +| `/` | `Home.vue` | Liste des projets | +| `/login` | `Login.vue` | Authentification | +| `/account` | `Account.vue` | Compte utilisateur | +| `/notifications` | `Notifications.vue` | Centre de notifications | +| `/inspirations` | `Inspirations.vue` | Galerie d'inspirations | +| `/design-to-light` | `DesignToLight.vue` | Feature DTL | +| `/projects/:id` | `Kanban.vue` | Détail projet (kanban) | +| `/projects/:id/client-brief` | `Brief.vue` | Brief client | +| `/projects/:id/extended-brief` | `Brief.vue` | Brief étendu | + +--- + +## API Endpoints + +### Authentification +- `POST /login.json` - Connexion (email, password) +- `GET /logout` - Déconnexion + +### Pages (JSON) +- `GET /{uri}.json` - Données page + user + +### Commentaires +- `POST /create-comment.json` +- `POST /update-comment.json` +- `POST /delete-comment.json` +- `POST /reply-comment.json` + +### Notifications +- `POST /read-notification.json` +- `POST /read-all-notifications.json` + +### Fichiers +- `POST /upload-pdf.json` +- `POST /upload-images.json` +- `POST /remove-file.json` + +### Actions +- `POST /save-page.json` +- `POST /save-file.json` +- `POST /validate-brief.json` +- `POST /toggle-favorite.json` +- `POST /request-project-creation.json` +- `POST /request-optimization-appointment.json` + +--- + +## Design to Light (DTL) + +Système d'évaluation avancée des designs de flacons. + +**Notation:** +- Note globale : A (8-10), B (6-8), C (4-6), D (2-4), E (0-2) +- Indicateurs : Design global, Bague, Épaule, Colonnes & Arêtes, Pied, Fond de Verre +- Position : Complexité, Poids + +**Propositions DTL liées à:** +- Proposition commerciale (PDF) +- Idéation industrielle (PDF) +- Échantillon virtuel - piste dynamique +- Échantillon virtuel - piste statique + +--- + +## Fichiers clés à connaître + +### Frontend +- `src/main.js` - Init app +- `src/router/router.js` - Guard + setup +- `src/router/routes.js` - Définition routes +- `src/stores/api.js` - Toute la comm API +- `src/components/Menu.vue` - Navigation latérale +- `src/components/project/DialogWrapper.vue` - Wrapper modales + +### Backend +- `public/site/config/config.php` - Routes, hooks, config +- `public/site/controllers/site.php` - Contrôleur principal +- `public/site/models/project.php` - Logique projet +- `public/site/plugins/helpers/index.php` - Fonctions utilitaires +- `public/site/blueprints/pages/project.yml` - Structure projet + +--- + +## Développement local + +```bash +# Backend +cd public +composer install +php -S localhost:8888 kirby/router.php + +# Frontend +npm install +npm run dev +``` + +## Build + +```bash +npm run build # Production +npm run build:preprod # Staging (avec sourcemaps) +``` + +--- + +## Notes importantes + +1. **Cache**: Les étapes projet sont cachées. Invalidation automatique via hooks Kirby. +2. **Permissions**: Filtrées côté serveur selon le rôle utilisateur. +3. **Commentaires**: Positionnés en % (x, y) + pageIndex pour les PDFs multi-pages. +4. **Notifications**: Stockées par projet, trackées par user UUID dans `readby[]`. +5. **Virtual Sample**: Pistes dynamiques = pages enfants, pistes statiques = fichiers. From 0250dc1487cb0d8f7775f4d4988e8abcf233680a Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 11:16:17 +0100 Subject: [PATCH 08/52] =?UTF-8?q?Fix=20:=20probl=C3=A8me=20de=20m=C3=A9moi?= =?UTF-8?q?re=20lors=20du=20chargement=20des=20projets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : projects.json.php collectait les notifications dérivées pour TOUS les projets d'un coup, ce qui causait un dépassement de mémoire (HTTP 500). Solution : - projects.json.php : Ne collecte plus les notifications (retourne []) - project.json.php : Collecte les notifications uniquement pour le projet affiché Les notifications seront chargées à la demande quand on ouvre un projet, pas lors du listing initial. Co-Authored-By: Claude Sonnet 4.5 --- public/site/templates/project.json.php | 15 ++++++++++++- public/site/templates/projects.json.php | 28 +++++++------------------ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/public/site/templates/project.json.php b/public/site/templates/project.json.php index a099586..3a31cd1 100644 --- a/public/site/templates/project.json.php +++ b/public/site/templates/project.json.php @@ -1,5 +1,18 @@ option('adrienpayet.pdc-notifications.collector'); +$notifications = []; + +if ($notificationCollector && $kirby->user()) { + try { + $notifications = $notificationCollector->collect($page, $kirby->user()); + } catch (\Throwable $e) { + error_log("Error collecting notifications for project {$page->uri()}: " . $e->getMessage()); + $notifications = []; + } +} + $project = [ 'title' => $page->title()->value(), 'url' => $page->url(), @@ -11,7 +24,7 @@ $project = [ 'steps' => $page->getSteps(), 'designToLight' => $page->isDTLEnabled()->isTrue() ? processDTLProposals($page) : null, 'hasOptimizationRequest' => $page->hasOptimizationRequest()->isTrue(), - 'notifications' => $page->notifications()->yaml(), + 'notifications' => $notifications, ]; $pageData = array_merge($genericData, $project); diff --git a/public/site/templates/projects.json.php b/public/site/templates/projects.json.php index cf5a52a..ff1d6a4 100644 --- a/public/site/templates/projects.json.php +++ b/public/site/templates/projects.json.php @@ -7,22 +7,10 @@ if (!$kirby->user()) { ]); } -// Récupérer le collector de notifications -$notificationCollector = $kirby->option('adrienpayet.pdc-notifications.collector'); - -function getProjectData($project, $user, $collector) +function getProjectData($project) { - // Utiliser le nouveau système de notifications dérivées - $notifications = []; - if ($collector) { - try { - $notifications = $collector->collect($project, $user); - } catch (\Throwable $e) { - error_log("Error collecting notifications for project {$project->uri()}: " . $e->getMessage()); - $notifications = []; - } - } - + // Les notifications ne sont plus collectées ici pour éviter les problèmes de mémoire. + // Elles seront collectées uniquement quand on affiche un projet individuel. $data = [ 'title' => $project->title()->value(), 'url' => $project->url(), @@ -32,7 +20,7 @@ function getProjectData($project, $user, $collector) 'status' => $project->status(), 'logo' => $project->client()->toPage() ? $project->client()->toPage()->logo()->toFile()->url() : '', 'steps' => $project->getSteps(), - 'notifications' => $notifications, + 'notifications' => [], // Sera collecté à la demande dans project.json.php 'uuid' => (string) $project->uuid(), 'slug' => (string) $project->slug(), 'isDTLEnabled' => $project->isDTLEnabled()->isTrue(), @@ -46,12 +34,10 @@ function getProjectData($project, $user, $collector) return $data; } -$currentUser = $kirby->user(); - try { - $children = $currentUser->role() == 'admin' - ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values() - : $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values(); + $children = $kirby->user()->role() == 'admin' + ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project))->values() + : $kirby->user()->projects()->toPages()->map(fn($project) => getProjectData($project))->values(); } catch (\Throwable $th) { throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); $children = []; From 0a980603a4a4db2a3eba0c59f2e126f3c28a7fe5 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 11:18:59 +0100 Subject: [PATCH 09/52] Ajout de collectLight() pour optimiser le chargement du listing des projets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : projects.json.php causait un dépassement mémoire en collectant toutes les notifications complètes (avec author, location, text, etc.) pour tous les projets. Solution : Nouvelle méthode collectLight() qui ne retourne que les données minimales nécessaires au frontend pour afficher les indicateurs : - id, type, isRead, date - location.project.uri (pour le filtrage) Les détails complets sont toujours chargés dans project.json.php individuel. Co-Authored-By: Claude Sonnet 4.5 --- .../src/NotificationCollector.php | 43 +++++++++++++++++++ public/site/templates/projects.json.php | 27 +++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/public/site/plugins/notifications/src/NotificationCollector.php b/public/site/plugins/notifications/src/NotificationCollector.php index 113b041..83e47d1 100644 --- a/public/site/plugins/notifications/src/NotificationCollector.php +++ b/public/site/plugins/notifications/src/NotificationCollector.php @@ -57,6 +57,49 @@ class NotificationCollector return $all; } + /** + * Collecte uniquement les données minimales des notifications (version allégée pour listing). + * Retourne seulement id, type, isRead, date pour économiser la mémoire. + * + * @param Page $project Le projet à scanner + * @param User $user L'utilisateur courant + * @return array Liste triée par date décroissante + */ + public function collectLight(Page $project, User $user): array + { + $all = []; + + foreach ($this->providers as $provider) { + try { + $notifications = $provider->collect($project, $user); + // Ne garder que les champs essentiels + foreach ($notifications as $notification) { + $all[] = [ + 'id' => $notification['id'] ?? null, + 'type' => $notification['type'] ?? null, + 'isRead' => $notification['isRead'] ?? false, + 'date' => $notification['date'] ?? null, + // Garder location.project.uri pour le frontend + 'location' => [ + 'project' => $notification['location']['project'] ?? [] + ] + ]; + } + } catch (\Throwable $e) { + error_log("NotificationCollector: Error in {$provider->getType()}: " . $e->getMessage()); + } + } + + // Trier par date décroissante + usort($all, function ($a, $b) { + $dateA = strtotime($a['date'] ?? '0'); + $dateB = strtotime($b['date'] ?? '0'); + return $dateB - $dateA; + }); + + return $all; + } + /** * Marque une notification comme lue en déléguant au bon provider. * diff --git a/public/site/templates/projects.json.php b/public/site/templates/projects.json.php index ff1d6a4..222e86a 100644 --- a/public/site/templates/projects.json.php +++ b/public/site/templates/projects.json.php @@ -7,10 +7,19 @@ if (!$kirby->user()) { ]); } -function getProjectData($project) +function getProjectData($project, $user, $collector) { - // Les notifications ne sont plus collectées ici pour éviter les problèmes de mémoire. - // Elles seront collectées uniquement quand on affiche un projet individuel. + // Utiliser collectLight() pour économiser la mémoire (seulement id, type, isRead, date) + $notifications = []; + if ($collector) { + try { + $notifications = $collector->collectLight($project, $user); + } catch (\Throwable $e) { + error_log("Error collecting light notifications for project {$project->uri()}: " . $e->getMessage()); + $notifications = []; + } + } + $data = [ 'title' => $project->title()->value(), 'url' => $project->url(), @@ -20,7 +29,7 @@ function getProjectData($project) 'status' => $project->status(), 'logo' => $project->client()->toPage() ? $project->client()->toPage()->logo()->toFile()->url() : '', 'steps' => $project->getSteps(), - 'notifications' => [], // Sera collecté à la demande dans project.json.php + 'notifications' => $notifications, 'uuid' => (string) $project->uuid(), 'slug' => (string) $project->slug(), 'isDTLEnabled' => $project->isDTLEnabled()->isTrue(), @@ -34,10 +43,14 @@ function getProjectData($project) return $data; } +// Récupérer le collector de notifications +$notificationCollector = $kirby->option('adrienpayet.pdc-notifications.collector'); +$currentUser = $kirby->user(); + try { - $children = $kirby->user()->role() == 'admin' - ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project))->values() - : $kirby->user()->projects()->toPages()->map(fn($project) => getProjectData($project))->values(); + $children = $currentUser->role() == 'admin' + ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values() + : $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values(); } catch (\Throwable $th) { throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); $children = []; From e73e25b1da50fb194a798309a48725cb3d02eb42 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 11:19:14 +0100 Subject: [PATCH 10/52] =?UTF-8?q?Ajout=20.user.ini=20:=20augmentation=20li?= =?UTF-8?q?mite=20m=C3=A9moire=20PHP=20=C3=A0=20512M?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temporaire pour gérer le chargement des notifications de tous les projets. Co-Authored-By: Claude Sonnet 4.5 --- public/.user.ini | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 public/.user.ini diff --git a/public/.user.ini b/public/.user.ini new file mode 100644 index 0000000..082ba05 --- /dev/null +++ b/public/.user.ini @@ -0,0 +1,2 @@ +; Augmentation temporaire de la limite mémoire pour le chargement des notifications +memory_limit = 512M From bb71da081b53c0487721140bf26b55ffdbf793cb Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 11:42:20 +0100 Subject: [PATCH 11/52] =?UTF-8?q?Ajout=20du=20syst=C3=A8me=20de=20cache=20?= =?UTF-8?q?pour=20les=20notifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : Les notifications étaient collectées à chaque requête sur projects.json, causant des problèmes de performance et de mémoire. Solution : Mise en cache des notifications par projet et par utilisateur - Nouvelle méthode getNotificationsLight() dans ProjectPage avec cache - Cache invalidé automatiquement via les hooks existants (page/file update) - Cache par utilisateur pour inclure le isRead spécifique Performance : Les notifications sont calculées une fois puis servies depuis le cache jusqu'à ce qu'un changement survienne (commentaire, brief, etc.) Co-Authored-By: Claude Sonnet 4.5 --- ...update--regenerate-project-steps-cache.php | 6 ++- public/site/models/project.php | 54 +++++++++++++++++-- public/site/templates/projects.json.php | 22 ++++---- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/public/site/config/hooks/page-update--regenerate-project-steps-cache.php b/public/site/config/hooks/page-update--regenerate-project-steps-cache.php index bde728d..f8d9675 100644 --- a/public/site/config/hooks/page-update--regenerate-project-steps-cache.php +++ b/public/site/config/hooks/page-update--regenerate-project-steps-cache.php @@ -1,9 +1,11 @@ template() == 'project' ? $newPage : $newPage->parents()->findBy('template', 'project'); if ($project) { - $steps = $project->rebuildStepsCache(); + $project->rebuildStepsCache(); + // Invalider aussi le cache des notifications (briefs validés, etc.) + $project->invalidateNotificationsCache(); } }; \ No newline at end of file diff --git a/public/site/models/project.php b/public/site/models/project.php index 1152945..583bfeb 100644 --- a/public/site/models/project.php +++ b/public/site/models/project.php @@ -3,18 +3,62 @@ use adrienpayet\notifications\NotificationsPage; class ProjectPage extends NotificationsPage { - public function getSteps() { + public function getSteps() { $apiCache = kirby()->cache('api'); - $stepsData = $apiCache?->get($this->slug() . '_' . 'steps'); - + $stepsData = $apiCache?->get($this->slug() . '_' . 'steps'); + if ($stepsData === null || count($stepsData) === 0) { $this->rebuildStepsCache(); }; - - $stepsData = $apiCache->get($this->slug() . '_' . 'steps'); + + $stepsData = $apiCache->get($this->slug() . '_' . 'steps'); return $stepsData; } + + /** + * Récupère les notifications pour ce projet (version allégée avec cache). + * Cache par utilisateur pour inclure le isRead. + */ + public function getNotificationsLight($user) { + if (!$user) { + return []; + } + + $apiCache = kirby()->cache('api'); + $cacheKey = $this->slug() . '_notifications_' . $user->uuid(); + $notifications = $apiCache?->get($cacheKey); + + // Si pas en cache, collecter et cacher + if ($notifications === null) { + $collector = kirby()->option('adrienpayet.pdc-notifications.collector'); + if (!$collector) { + return []; + } + + try { + $notifications = $collector->collectLight($this, $user); + $apiCache->set($cacheKey, $notifications); + } catch (\Throwable $e) { + error_log("Error caching notifications for {$this->slug()}: " . $e->getMessage()); + return []; + } + } + + return $notifications; + } + + /** + * Invalide le cache des notifications de ce projet pour tous les utilisateurs. + */ + public function invalidateNotificationsCache() { + $apiCache = kirby()->cache('api'); + // Invalider pour tous les users + foreach (kirby()->users() as $user) { + $cacheKey = $this->slug() . '_notifications_' . $user->uuid(); + $apiCache->remove($cacheKey); + } + } public function rebuildStepsCache() { // Create steps diff --git a/public/site/templates/projects.json.php b/public/site/templates/projects.json.php index 222e86a..2b2eaad 100644 --- a/public/site/templates/projects.json.php +++ b/public/site/templates/projects.json.php @@ -7,17 +7,15 @@ if (!$kirby->user()) { ]); } -function getProjectData($project, $user, $collector) +function getProjectData($project, $user) { - // Utiliser collectLight() pour économiser la mémoire (seulement id, type, isRead, date) + // Utiliser getNotificationsLight() avec cache pour optimiser les performances $notifications = []; - if ($collector) { - try { - $notifications = $collector->collectLight($project, $user); - } catch (\Throwable $e) { - error_log("Error collecting light notifications for project {$project->uri()}: " . $e->getMessage()); - $notifications = []; - } + try { + $notifications = $project->getNotificationsLight($user); + } catch (\Throwable $e) { + error_log("Error getting notifications for project {$project->uri()}: " . $e->getMessage()); + $notifications = []; } $data = [ @@ -43,14 +41,12 @@ function getProjectData($project, $user, $collector) return $data; } -// Récupérer le collector de notifications -$notificationCollector = $kirby->option('adrienpayet.pdc-notifications.collector'); $currentUser = $kirby->user(); try { $children = $currentUser->role() == 'admin' - ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values() - : $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values(); + ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser))->values() + : $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser))->values(); } catch (\Throwable $th) { throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); $children = []; From 2791bc4462913b8027fd0f26f4bce50d2c379fc9 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 11:42:40 +0100 Subject: [PATCH 12/52] Ajout invalidation cache notifications dans hook file-update Co-Authored-By: Claude Sonnet 4.5 --- .../hooks/file-update--regenerate-project-steps-cache.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/site/config/hooks/file-update--regenerate-project-steps-cache.php b/public/site/config/hooks/file-update--regenerate-project-steps-cache.php index ab09290..c30f93d 100644 --- a/public/site/config/hooks/file-update--regenerate-project-steps-cache.php +++ b/public/site/config/hooks/file-update--regenerate-project-steps-cache.php @@ -3,7 +3,9 @@ // file.update:after return function ($newFile, $oldFile) { $project = $newFile->parent()->template() == 'project' ? $newFile->parent() : $newFile->parent()->parents()->findBy('template', 'project'); - if ($project) { - $steps = $project->rebuildStepsCache(); + if ($project) { + $project->rebuildStepsCache(); + // Invalider aussi le cache des notifications (commentaires sur fichiers, etc.) + $project->invalidateNotificationsCache(); } }; \ No newline at end of file From 86db1f5a0c987a2bfdf2350065d02fd09e6cda00 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 11:55:17 +0100 Subject: [PATCH 13/52] Fix collectLight() : inclure author, text, location pour l'affichage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : collectLight() ne retournait que id/type/isRead/date, causant notification.author undefined dans le frontend. Solution : Inclure tous les champs nécessaires à l'affichage (author, text, location) mais toujours alléger en excluant les gros détails inutiles. Co-Authored-By: Claude Sonnet 4.5 --- .../src/NotificationCollector.php | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/public/site/plugins/notifications/src/NotificationCollector.php b/public/site/plugins/notifications/src/NotificationCollector.php index 83e47d1..ec1714b 100644 --- a/public/site/plugins/notifications/src/NotificationCollector.php +++ b/public/site/plugins/notifications/src/NotificationCollector.php @@ -59,7 +59,7 @@ class NotificationCollector /** * Collecte uniquement les données minimales des notifications (version allégée pour listing). - * Retourne seulement id, type, isRead, date pour économiser la mémoire. + * Retourne les champs nécessaires à l'affichage mais sans les détails lourds. * * @param Page $project Le projet à scanner * @param User $user L'utilisateur courant @@ -72,18 +72,33 @@ class NotificationCollector foreach ($this->providers as $provider) { try { $notifications = $provider->collect($project, $user); - // Ne garder que les champs essentiels + // Garder les champs nécessaires au frontend foreach ($notifications as $notification) { - $all[] = [ + $light = [ 'id' => $notification['id'] ?? null, 'type' => $notification['type'] ?? null, 'isRead' => $notification['isRead'] ?? false, 'date' => $notification['date'] ?? null, - // Garder location.project.uri pour le frontend - 'location' => [ - 'project' => $notification['location']['project'] ?? [] - ] + 'text' => $notification['text'] ?? null, + 'author' => $notification['author'] ?? null, + 'location' => $notification['location'] ?? [] ]; + + // Garder les champs optionnels s'ils existent + if (isset($notification['dialogUri'])) { + $light['dialogUri'] = $notification['dialogUri']; + } + if (isset($notification['_briefUri'])) { + $light['_briefUri'] = $notification['_briefUri']; + } + if (isset($notification['_file'])) { + $light['_file'] = $notification['_file']; + } + if (isset($notification['_projectUri'])) { + $light['_projectUri'] = $notification['_projectUri']; + } + + $all[] = $light; } } catch (\Throwable $e) { error_log("NotificationCollector: Error in {$provider->getType()}: " . $e->getMessage()); From a57b0c203a47c9a25bebc5486d769a82db158197 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 12:08:13 +0100 Subject: [PATCH 14/52] Optimisation du refresh cache avec batch processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : Le refresh cache de tous les projets timeout côté serveur à cause du trop grand nombre de projets à traiter en une seule requête. Solution : Batch processing avec indicateur de progression - Backend : traite 10 projets par batch avec offset/limit - Frontend : fait plusieurs requêtes successives et affiche la progression - Timeout réduit à 60s par batch au lieu de illimité - Bouton désactivé pendant le traitement - Ajout invalidateNotificationsCache() pour vider aussi ce cache Affichage : "15/50 (30%)" pendant le traitement, puis "Terminé (50)" Co-Authored-By: Claude Sonnet 4.5 --- .../src/components/RefreshCacheButton.vue | 92 +++++++++++++++++-- .../src/routes/refresh-cache.php | 47 +++++++--- 2 files changed, 119 insertions(+), 20 deletions(-) diff --git a/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue b/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue index 5fdb1cf..f4cf6d6 100644 --- a/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue +++ b/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue @@ -7,6 +7,7 @@ :icon="icon" :title="title" @click="refreshCache()" + :disabled="isProcessing" >{{ text }} @@ -24,6 +25,8 @@ const { pageUri, pageStatus, lastCacheUpdate } = defineProps({ const text = ref("Rafraîchir"); const icon = ref("refresh"); const theme = ref("aqua-icon"); +const isProcessing = ref(false); + const title = computed(() => { return lastCacheUpdate?.length > 0 ? "Dernière mise à jour : " + lastCacheUpdate @@ -31,25 +34,89 @@ const title = computed(() => { }); async function refreshCache() { - text.value = "En cours…"; + isProcessing.value = true; icon.value = "loader"; theme.value = "orange-icon"; + // Pour les projets multiples (batch processing) + if (pageUri === 'projects') { + await refreshAllProjects(); + } else { + await refreshSingleProject(); + } +} + +async function refreshAllProjects() { + let offset = 0; + const limit = 10; // 10 projets par batch + let hasMore = true; + let total = 0; + + try { + while (hasMore) { + const init = { + method: "POST", + "Content-Type": "application/json", + body: JSON.stringify({ + pageUri: 'projects', + offset, + limit + }), + }; + + const res = await fetch("/refresh-cache.json", init); + const json = await res.json(); + + if (json.status === "error") { + throw new Error(json.message); + } + + total = json.total; + hasMore = json.hasMore; + offset = json.nextOffset; + + // Mise à jour de la progression + const progress = Math.round((json.processed / json.total) * 100); + text.value = `${json.processed}/${json.total} (${progress}%)`; + + console.log(`Batch terminé : ${json.processed}/${json.total} projets`); + } + + // Succès + text.value = `Terminé (${total})`; + icon.value = "check"; + theme.value = "green-icon"; + + setTimeout(() => { + location.href = location.href; + }, 1500); + + } catch (error) { + console.error(error); + text.value = "Erreur"; + icon.value = "alert"; + theme.value = "red-icon"; + isProcessing.value = false; + } +} + +async function refreshSingleProject() { + text.value = "En cours…"; + const init = { method: "POST", "Content-Type": "application/json", body: JSON.stringify({ pageUri }), }; - const res = await fetch("/refresh-cache.json", init); - const json = await res.json(); + try { + const res = await fetch("/refresh-cache.json", init); + const json = await res.json(); + + if (json.status === "error") { + throw new Error(json.message); + } - if (json.status === "error") { - console.error(json); - text.value = "Erreur"; - icon.value = "alert"; - theme.value = "red-icon"; - } else { console.log(json); text.value = "Terminé"; icon.value = "check"; @@ -58,6 +125,13 @@ async function refreshCache() { setTimeout(() => { location.href = location.href; }, 1500); + + } catch (error) { + console.error(error); + text.value = "Erreur"; + icon.value = "alert"; + theme.value = "red-icon"; + isProcessing.value = false; } } diff --git a/public/site/plugins/refresh-cache-button/src/routes/refresh-cache.php b/public/site/plugins/refresh-cache-button/src/routes/refresh-cache.php index 207d34e..7f93443 100644 --- a/public/site/plugins/refresh-cache-button/src/routes/refresh-cache.php +++ b/public/site/plugins/refresh-cache-button/src/routes/refresh-cache.php @@ -1,5 +1,5 @@ '/refresh-cache.json', @@ -10,17 +10,42 @@ return [ if ($data->pageUri === 'projects') { $projects = page('projects')->children(); - foreach ($projects as $project) { - $project->rebuildStepsCache(); - $formatter = new IntlDateFormatter('fr_FR', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, 'Europe/Paris'); - $project->update([ - 'lastCacheUpdate' => $formatter->format(time()) - ]); + // Support du batch processing + $offset = isset($data->offset) ? intval($data->offset) : 0; + $limit = isset($data->limit) ? intval($data->limit) : 10; // 10 projets par batch par défaut + $total = $projects->count(); + + // Slice pour ne traiter qu'un batch + $batch = $projects->slice($offset, $limit); + $processed = 0; + + foreach ($batch as $project) { + try { + $project->rebuildStepsCache(); + $project->invalidateNotificationsCache(); + + $formatter = new IntlDateFormatter('fr_FR', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, 'Europe/Paris'); + $project->update([ + 'lastCacheUpdate' => $formatter->format(time()) + ]); + $processed++; + } catch (\Throwable $e) { + error_log("Error refreshing cache for project {$project->slug()}: " . $e->getMessage()); + } } + + $remaining = max(0, $total - ($offset + $processed)); + $hasMore = $remaining > 0; + return [ - 'satus' => 'success', - 'message' => 'Données des pages projets rafraîchies avec succès.' + 'status' => 'success', + 'message' => "Batch terminé : $processed projets traités.", + 'processed' => $offset + $processed, + 'total' => $total, + 'remaining' => $remaining, + 'hasMore' => $hasMore, + 'nextOffset' => $hasMore ? $offset + $limit : null ]; } else { try { @@ -41,7 +66,7 @@ return [ if (!$project) { return [ - 'satus' => 'error', + 'status' => 'error', 'message' => 'Impossible de rafraîchir les données de la page ' . $data->pageUri . '. Aucun projet correspondant.' ]; } @@ -55,7 +80,7 @@ return [ return [ - 'satus' => 'success', + 'status' => 'success', 'message' => 'Données de la page ' . $data->pageUri . ' rafraîchie avec succès.' ]; } From 4669f03f167fc07663afae02ffdfb149b4b3fd36 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 12:13:26 +0100 Subject: [PATCH 15/52] =?UTF-8?q?Am=C3=A9lioration=20affichage=20progressi?= =?UTF-8?q?on=20du=20refresh=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout d'une ligne de texte sous le bouton pour afficher la progression : - "Traitement : 10/50 projets (20%)" pendant le traitement - "50 projets mis à jour avec succès" à la fin - Tooltip aussi mis à jour avec la progression Le bouton affiche "En cours…" et la progression détaillée est en dessous. Co-Authored-By: Claude Sonnet 4.5 --- .../src/components/RefreshCacheButton.vue | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue b/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue index f4cf6d6..76583dc 100644 --- a/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue +++ b/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue @@ -10,6 +10,9 @@ :disabled="isProcessing" >{{ text }} +
+ {{ progressText }} +
@@ -26,8 +29,12 @@ const text = ref("Rafraîchir"); const icon = ref("refresh"); const theme = ref("aqua-icon"); const isProcessing = ref(false); +const progressText = ref(""); const title = computed(() => { + if (progressText.value) { + return progressText.value; + } return lastCacheUpdate?.length > 0 ? "Dernière mise à jour : " + lastCacheUpdate : "Mettre à jour le cache front"; @@ -52,6 +59,8 @@ async function refreshAllProjects() { let hasMore = true; let total = 0; + text.value = "En cours…"; + try { while (hasMore) { const init = { @@ -77,23 +86,25 @@ async function refreshAllProjects() { // Mise à jour de la progression const progress = Math.round((json.processed / json.total) * 100); - text.value = `${json.processed}/${json.total} (${progress}%)`; + progressText.value = `Traitement : ${json.processed}/${json.total} projets (${progress}%)`; console.log(`Batch terminé : ${json.processed}/${json.total} projets`); } // Succès - text.value = `Terminé (${total})`; + text.value = "Terminé"; + progressText.value = `${total} projets mis à jour avec succès`; icon.value = "check"; theme.value = "green-icon"; setTimeout(() => { location.href = location.href; - }, 1500); + }, 2000); } catch (error) { console.error(error); text.value = "Erreur"; + progressText.value = error.message || "Une erreur est survenue"; icon.value = "alert"; theme.value = "red-icon"; isProcessing.value = false; From 378af9ac962723abc42dc4490b9ac0ce7736e11c Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 12:18:33 +0100 Subject: [PATCH 16/52] Fix : affichage progression dans le texte du bouton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La div en dessous ne s'affichait pas dans le panel Kirby. La progression s'affiche maintenant directement dans le bouton : "En cours 0%" → "En cours 20%" → "En cours 100%" → "Terminé" Co-Authored-By: Claude Sonnet 4.5 --- .../src/components/RefreshCacheButton.vue | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue b/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue index 76583dc..f6b12f0 100644 --- a/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue +++ b/public/site/plugins/refresh-cache-button/src/components/RefreshCacheButton.vue @@ -10,9 +10,6 @@ :disabled="isProcessing" >{{ text }} -
- {{ progressText }} -
@@ -29,12 +26,8 @@ const text = ref("Rafraîchir"); const icon = ref("refresh"); const theme = ref("aqua-icon"); const isProcessing = ref(false); -const progressText = ref(""); const title = computed(() => { - if (progressText.value) { - return progressText.value; - } return lastCacheUpdate?.length > 0 ? "Dernière mise à jour : " + lastCacheUpdate : "Mettre à jour le cache front"; @@ -59,7 +52,7 @@ async function refreshAllProjects() { let hasMore = true; let total = 0; - text.value = "En cours…"; + text.value = "En cours 0%"; try { while (hasMore) { @@ -84,16 +77,15 @@ async function refreshAllProjects() { hasMore = json.hasMore; offset = json.nextOffset; - // Mise à jour de la progression + // Mise à jour de la progression dans le texte du bouton const progress = Math.round((json.processed / json.total) * 100); - progressText.value = `Traitement : ${json.processed}/${json.total} projets (${progress}%)`; + text.value = `En cours ${progress}%`; - console.log(`Batch terminé : ${json.processed}/${json.total} projets`); + console.log(`Batch terminé : ${json.processed}/${json.total} projets (${progress}%)`); } // Succès text.value = "Terminé"; - progressText.value = `${total} projets mis à jour avec succès`; icon.value = "check"; theme.value = "green-icon"; @@ -104,7 +96,6 @@ async function refreshAllProjects() { } catch (error) { console.error(error); text.value = "Erreur"; - progressText.value = error.message || "Une erreur est survenue"; icon.value = "alert"; theme.value = "red-icon"; isProcessing.value = false; From 95a8bf99cb9a53772b7beaeaf8c4b8e366c9219b Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 12:19:35 +0100 Subject: [PATCH 17/52] build plugin refresh cache --- public/site/plugins/refresh-cache-button/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/site/plugins/refresh-cache-button/index.js b/public/site/plugins/refresh-cache-button/index.js index 8fc4d42..4d99982 100644 --- a/public/site/plugins/refresh-cache-button/index.js +++ b/public/site/plugins/refresh-cache-button/index.js @@ -1 +1 @@ -(function(){"use strict";function f(n,e,a,t,r,c,s,u){var o=typeof n=="function"?n.options:n;return e&&(o.render=e,o.staticRenderFns=a,o._compiled=!0),{exports:n,options:o}}const l={__name:"RefreshCacheButton",props:{pageUri:String,pageStatus:String,lastCacheUpdate:String},setup(n){const{pageUri:e,pageStatus:a,lastCacheUpdate:t}=n,r=Vue.ref("Rafraîchir"),c=Vue.ref("refresh"),s=Vue.ref("aqua-icon"),u=Vue.computed(()=>(t==null?void 0:t.length)>0?"Dernière mise à jour : "+t:"Mettre à jour le cache front");async function o(){r.value="En cours…",c.value="loader",s.value="orange-icon";const m={method:"POST","Content-Type":"application/json",body:JSON.stringify({pageUri:e})},i=await(await fetch("/refresh-cache.json",m)).json();i.status==="error"?(console.error(i),r.value="Erreur",c.value="alert",s.value="red-icon"):(console.log(i),r.value="Terminé",c.value="check",s.value="green-icon",setTimeout(()=>{location.href=location.href},1500))}return{__sfc:!0,text:r,icon:c,theme:s,title:u,refreshCache:o}}};var h=function(){var e=this,a=e._self._c,t=e._self._setupProxy;return a("div",{attrs:{id:"refresh-cache-button"}},[e.pageStatus!=="draft"?a("k-button",{attrs:{theme:t.theme,variant:"dimmed",icon:t.icon,title:t.title},on:{click:function(r){return t.refreshCache()}}},[e._v(e._s(t.text))]):e._e()],1)},_=[],p=f(l,h,_);const d=p.exports;window.panel.plugin("adrienpayet/refresh-cache-button",{components:{"refresh-cache-button":d}})})(); +(function(){"use strict";function _(n,e,u,t,r,s,a,l){var c=typeof n=="function"?n.options:n;return e&&(c.render=e,c.staticRenderFns=u,c._compiled=!0),{exports:n,options:c}}const g={__name:"RefreshCacheButton",props:{pageUri:String,pageStatus:String,lastCacheUpdate:String},setup(n){const{pageUri:e,pageStatus:u,lastCacheUpdate:t}=n,r=Vue.ref("Rafraîchir"),s=Vue.ref("refresh"),a=Vue.ref("aqua-icon"),l=Vue.ref(!1),c=Vue.computed(()=>(t==null?void 0:t.length)>0?"Dernière mise à jour : "+t:"Mettre à jour le cache front");async function T(){l.value=!0,s.value="loader",a.value="orange-icon",e==="projects"?await d():await v()}async function d(){let f=0;const h=10;let i=!0,b=0;r.value="En cours 0%";try{for(;i;){const p={method:"POST","Content-Type":"application/json",body:JSON.stringify({pageUri:"projects",offset:f,limit:h})},o=await(await fetch("/refresh-cache.json",p)).json();if(o.status==="error")throw new Error(o.message);b=o.total,i=o.hasMore,f=o.nextOffset;const m=Math.round(o.processed/o.total*100);r.value=`En cours ${m}%`,console.log(`Batch terminé : ${o.processed}/${o.total} projets (${m}%)`)}r.value="Terminé",s.value="check",a.value="green-icon",setTimeout(()=>{location.href=location.href},2e3)}catch(p){console.error(p),r.value="Erreur",s.value="alert",a.value="red-icon",l.value=!1}}async function v(){r.value="En cours…";const f={method:"POST","Content-Type":"application/json",body:JSON.stringify({pageUri:e})};try{const i=await(await fetch("/refresh-cache.json",f)).json();if(i.status==="error")throw new Error(i.message);console.log(i),r.value="Terminé",s.value="check",a.value="green-icon",setTimeout(()=>{location.href=location.href},1500)}catch(h){console.error(h),r.value="Erreur",s.value="alert",a.value="red-icon",l.value=!1}}return{__sfc:!0,text:r,icon:s,theme:a,isProcessing:l,title:c,refreshCache:T,refreshAllProjects:d,refreshSingleProject:v}}};var j=function(){var e=this,u=e._self._c,t=e._self._setupProxy;return u("div",{attrs:{id:"refresh-cache-button"}},[e.pageStatus!=="draft"?u("k-button",{attrs:{theme:t.theme,variant:"dimmed",icon:t.icon,title:t.title,disabled:t.isProcessing},on:{click:function(r){return t.refreshCache()}}},[e._v(e._s(t.text))]):e._e()],1)},y=[],w=_(g,j,y);const S=w.exports;window.panel.plugin("adrienpayet/refresh-cache-button",{components:{"refresh-cache-button":S}})})(); From dfb8d1038be4aa0722485611d4f8ef5133f14810 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 12:29:49 +0100 Subject: [PATCH 18/52] =?UTF-8?q?Fix=20routing=20vers=20une=20piste=20sp?= =?UTF-8?q?=C3=A9cifique=20avec=20hash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : L'URL avec hash (#serumwc_lasertone_empty) n'ouvrait pas la bonne piste/variation mais toujours la première. Cause : Incohérence entre les underscores du hash et les tirets du slug backend. slugify convertit les underscores en tirets, mais les slugs Kirby peuvent varier. Solution : Comparer le hash de 3 façons : 1. Comparaison directe 2. Hash avec underscores → tirets 3. Slug avec tirets → underscores Cela gère tous les cas de figure. Co-Authored-By: Claude Sonnet 4.5 --- .../project/virtual-sample/DynamicView.vue | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/project/virtual-sample/DynamicView.vue b/src/components/project/virtual-sample/DynamicView.vue index f89552b..8273c73 100644 --- a/src/components/project/virtual-sample/DynamicView.vue +++ b/src/components/project/virtual-sample/DynamicView.vue @@ -100,8 +100,18 @@ onBeforeMount(() => { if (route?.hash && route.hash.length > 0) { const variations = tracks.value.flatMap((t) => t.variations || []); - initialVariation = - variations.find((v) => v.slug === route.hash.substring(1)) || null; + const hashValue = route.hash.substring(1); + + // Essayer de trouver la variation soit par slug direct, soit en normalisant le hash + initialVariation = variations.find((v) => { + // Comparaison directe + if (v.slug === hashValue) return true; + // Comparaison en convertissant underscores en tirets (slugify par défaut) + if (v.slug === hashValue.replace(/_/g, '-')) return true; + // Comparaison inverse : le slug du backend pourrait avoir des underscores + if (v.slug.replace(/-/g, '_') === hashValue) return true; + return false; + }) || null; } // fallback : première variation du premier track From 6b80e242b87009c2eaeaafffa2b2e21a295f8a0e Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 15 Jan 2026 13:54:36 +0100 Subject: [PATCH 19/52] Fix virtual sample routing and refactor for clarity Virtual sample variations now display correctly when loading from URL hash. Old URLs with underscores are normalized to hyphens on load. URL hash updates automatically when navigating between variations. Refactored both DynamicView and Selector components with explicit function names, removed unnecessary comments, and improved code organization. Co-Authored-By: Claude Sonnet 4.5 --- src/components/Selector.vue | 232 ++++++++++-------- .../project/virtual-sample/DynamicView.vue | 160 +++++++----- 2 files changed, 224 insertions(+), 168 deletions(-) diff --git a/src/components/Selector.vue b/src/components/Selector.vue index 32521cb..c95299d 100644 --- a/src/components/Selector.vue +++ b/src/components/Selector.vue @@ -76,112 +76,149 @@ const { items, label, isCompareModeEnabled, index } = defineProps({ // Local state const currentValue = ref(null); -const syncing = ref(false); // empêche les réactions pendant les mises à jour programmatiques +const syncing = ref(false); -// Store const { activeTracks } = storeToRefs(useDialogStore()); -// Utils -function isSame(a, b) { - if (!a || !b) return false; - if (a.slug && b.slug) return a.slug === b.slug; - return a.title === b.title; +function normalizeSlug(slug) { + return slug.replace(/_/g, '-'); } -function toVariation(v) { - if (!v) return null; - return Array.isArray(v) ? v[v.length - 1] || null : v; +function areVariationsEqual(variationA, variationB) { + if (!variationA || !variationB) return false; + + if (variationA.slug && variationB.slug) { + return normalizeSlug(variationA.slug) === normalizeSlug(variationB.slug); + } + + return variationA.title === variationB.title; } -// Initialisation : remplir le 1er select localement ET initialiser le store -onBeforeMount(() => { +function extractVariation(value) { + if (!value) return null; + return Array.isArray(value) ? value[value.length - 1] || null : value; +} + +function convertValueForCompareMode(value, shouldBeArray) { + if (shouldBeArray) { + return value && !Array.isArray(value) ? [value] : value; + } else { + return Array.isArray(value) ? value[0] || null : value; + } +} + +function findMatchingVariationsInStore(storeVariations) { + return storeVariations.filter((storeVar) => + items.some((item) => areVariationsEqual(item, storeVar)) + ); +} + +function syncCurrentValueFromStore(storeVariations) { syncing.value = true; - if (index === 0) { - currentValue.value = items[0] || null; - // si le store est vide, initialiser avec la variation du premier sélecteur - if (!activeTracks.value || activeTracks.value.length === 0) { - const v = toVariation(items[0]); - if (v) activeTracks.value = [v]; - } + const matchedVariations = findMatchingVariationsInStore(storeVariations); + + if (isCompareModeEnabled) { + currentValue.value = matchedVariations.length ? [...matchedVariations] : []; } else { - // les autres ne forcent pas le store ; leur currentValue restera à null - currentValue.value = null; + currentValue.value = matchedVariations[0] || null; } nextTick(() => (syncing.value = false)); -}); +} + +function detectVariationChanges(newValues, oldValues) { + const newList = Array.isArray(newValues) + ? newValues + : newValues + ? [newValues] + : []; + const oldList = Array.isArray(oldValues) + ? oldValues + : oldValues + ? [oldValues] + : []; + + const addedVariation = newList.find( + (n) => !oldList.some((o) => areVariationsEqual(o, n)) + ); + const removedVariation = oldList.find( + (o) => !newList.some((n) => areVariationsEqual(n, o)) + ); + + return { addedVariation, removedVariation }; +} + +function handleVariationChange(newValue, oldValue) { + if (syncing.value) return; + + const { addedVariation, removedVariation } = detectVariationChanges( + newValue, + oldValue + ); + + if ( + addedVariation && + items.some((item) => areVariationsEqual(item, addedVariation)) + ) { + updateActiveTracks(addedVariation, 'add'); + } else if ( + removedVariation && + items.some((item) => areVariationsEqual(item, removedVariation)) + ) { + updateActiveTracks(removedVariation, 'remove'); + } +} -// Quand on bascule compare mode (objet <-> tableau) watch( () => isCompareModeEnabled, - (flag) => { + (shouldBeArray) => { syncing.value = true; - if (flag) { - if (currentValue.value && !Array.isArray(currentValue.value)) { - currentValue.value = [currentValue.value]; - } - } else { - if (Array.isArray(currentValue.value)) { - currentValue.value = currentValue.value[0] || null; - } - } + currentValue.value = convertValueForCompareMode( + currentValue.value, + shouldBeArray + ); nextTick(() => (syncing.value = false)); } ); -// Détection ajout / suppression dans le MultiSelect (côté composant) -// On n'agit que si l'ajout/suppression concerne une variation appartenant à `items` -watch( - currentValue, - (newVal, oldVal) => { - if (syncing.value) return; +watch(currentValue, handleVariationChange, { deep: true }); - const newItems = Array.isArray(newVal) ? newVal : newVal ? [newVal] : []; - const oldItems = Array.isArray(oldVal) ? oldVal : oldVal ? [oldVal] : []; - - const added = newItems.find((n) => !oldItems.some((o) => isSame(o, n))); - const removed = oldItems.find((o) => !newItems.some((n) => isSame(n, o))); - - if (added && items.some((it) => isSame(it, added))) { - selectTrack(added, 'add'); - } else if (removed && items.some((it) => isSame(it, removed))) { - selectTrack(removed, 'remove'); - } - }, - { deep: true } -); - -// Quand activeTracks change elsewhere -> synchroniser l'affichage local -// Mais n'adopter que les variations qui appartiennent à ce Selector (`items`) watch( activeTracks, - (newVal) => { - syncing.value = true; - - const storeList = Array.isArray(newVal) ? newVal : []; - // ne garder que les variations du store qui sont dans `items` - const matched = storeList.filter((av) => - items.some((it) => isSame(it, av)) - ); - - if (isCompareModeEnabled) { - currentValue.value = matched.length ? [...matched] : []; - } else { - currentValue.value = matched[0] || null; - } - - nextTick(() => (syncing.value = false)); + (storeVariations) => { + const variationsList = Array.isArray(storeVariations) + ? storeVariations + : []; + syncCurrentValueFromStore(variationsList); }, - { deep: true } + { deep: true, immediate: true } ); -// Logique centrale de sélection (ajout / suppression) -// Règles : -// - mode normal -> activeTracks = [variation] -// - mode comparaison -> conserver activeTracks[0] si possible; second élément ajouté/remplacé; suppression gère le cas de la suppression de la première -function selectTrack(track, action = 'add') { - const variation = toVariation(track); +function removeVariationFromActiveTracks(variation) { + activeTracks.value = activeTracks.value.filter( + (track) => !areVariationsEqual(track, variation) + ); +} + +function addVariationToActiveTracks(variation) { + const isAlreadyPresent = activeTracks.value.some((track) => + areVariationsEqual(track, variation) + ); + + if (isAlreadyPresent) return; + + if (activeTracks.value.length === 0) { + activeTracks.value = [variation]; + } else if (activeTracks.value.length === 1) { + activeTracks.value = [activeTracks.value[0], variation]; + } else { + activeTracks.value = [activeTracks.value[0], variation]; + } +} + +function updateActiveTracks(track, action = 'add') { + const variation = extractVariation(track); if (!variation) return; if (!isCompareModeEnabled) { @@ -190,34 +227,12 @@ function selectTrack(track, action = 'add') { } if (action === 'remove') { - const wasFirst = - activeTracks.value.length && isSame(activeTracks.value[0], variation); - activeTracks.value = activeTracks.value.filter( - (t) => !isSame(t, variation) - ); - - // si on a retiré la première et qu'il reste une piste, elle devient naturellement index 0 - // pas d'action supplémentaire nécessaire ici (déjà assuré par le filter) - return; - } - - // action === 'add' - if (activeTracks.value.some((t) => isSame(t, variation))) { - // déjà présent -> ignore - return; - } - - if (activeTracks.value.length === 0) { - activeTracks.value = [variation]; - } else if (activeTracks.value.length === 1) { - activeTracks.value = [activeTracks.value[0], variation]; + removeVariationFromActiveTracks(variation); } else { - // remplacer le 2e - activeTracks.value = [activeTracks.value[0], variation]; + addVariationToActiveTracks(variation); } } -// Helpers pour affichage (inchangés) function getFrontViewUrl(item) { if (!item) return ''; if (Array.isArray(item)) { @@ -231,8 +246,8 @@ function getFrontViewUrl(item) { } function setImage() { - return getFrontViewUrl(currentValue.value) - ? '--image: url(\'' + getFrontViewUrl(currentValue.value) + '\')' + return getFrontViewUrl(currentValue.value) + ? "--image: url('" + getFrontViewUrl(currentValue.value) + "')" : undefined; } @@ -250,7 +265,8 @@ function setImage() { padding: var(--space-8) var(--space-48) var(--space-8) var(--space-16); } .selector-dropdown.has-image, -.selector-dropdown.has-image :is(#selector-select, #selector-multiselect, [role='combobox']) { +.selector-dropdown.has-image + :is(#selector-select, #selector-multiselect, [role='combobox']) { padding-left: var(--space-64); } .selector-dropdown.has-image:before { @@ -290,7 +306,9 @@ function setImage() { cursor: pointer; } [role='combobox'] p, -.selector-dropdown [data-pc-section="labelcontainer"] > [data-pc-section='label'] { +.selector-dropdown + [data-pc-section='labelcontainer'] + > [data-pc-section='label'] { max-height: 1lh; overflow: hidden; text-overflow: ellipsis; diff --git a/src/components/project/virtual-sample/DynamicView.vue b/src/components/project/virtual-sample/DynamicView.vue index 8273c73..60bcd8e 100644 --- a/src/components/project/virtual-sample/DynamicView.vue +++ b/src/components/project/virtual-sample/DynamicView.vue @@ -61,13 +61,14 @@ import { storeToRefs } from 'pinia'; import { usePageStore } from '../../../stores/page'; import { useDialogStore } from '../../../stores/dialog'; import { useVirtualSampleStore } from '../../../stores/virtualSample'; -import { useRoute } from 'vue-router'; +import { useRoute, useRouter } from 'vue-router'; import Interactive360 from './Interactive360.vue'; import SingleImage from './SingleImage.vue'; import Selector from '../../Selector.vue'; import slugify from 'slugify'; const route = useRoute(); +const router = useRouter(); const { page } = storeToRefs(usePageStore()); const { isCommentsOpen, isCommentPanelEnabled, activeTracks, openedFile } = @@ -92,51 +93,74 @@ const tracks = computed(() => { return list; }); -// ---------- INITIALISATION ---------- -// onBeforeMount : on initialise toujours activeTracks avec une VARIATION (jamais un track) -onBeforeMount(() => { - // essayer la hash en priorité - let initialVariation = null; +function normalizeSlug(slug) { + return slug.replace(/_/g, '-'); +} +function getVariationSlug(variation) { + return variation.slug || (variation.title ? slugify(variation.title) : null); +} + +function findVariationByHash(hashValue) { + const allVariations = tracks.value.flatMap((track) => track.variations || []); + const normalizedHash = normalizeSlug(hashValue); + + return allVariations.find((variation) => { + const variationSlug = getVariationSlug(variation); + if (!variationSlug) return false; + + const normalizedVariationSlug = normalizeSlug(variationSlug); + return normalizedVariationSlug === normalizedHash; + }); +} + +function getInitialVariation() { if (route?.hash && route.hash.length > 0) { - const variations = tracks.value.flatMap((t) => t.variations || []); const hashValue = route.hash.substring(1); - - // Essayer de trouver la variation soit par slug direct, soit en normalisant le hash - initialVariation = variations.find((v) => { - // Comparaison directe - if (v.slug === hashValue) return true; - // Comparaison en convertissant underscores en tirets (slugify par défaut) - if (v.slug === hashValue.replace(/_/g, '-')) return true; - // Comparaison inverse : le slug du backend pourrait avoir des underscores - if (v.slug.replace(/-/g, '_') === hashValue) return true; - return false; - }) || null; + const variationFromHash = findVariationByHash(hashValue); + if (variationFromHash) return variationFromHash; } - // fallback : première variation du premier track - if (!initialVariation) { - initialVariation = tracks.value[0]?.variations?.[0] || null; - } + return tracks.value[0]?.variations?.[0] || null; +} - if (initialVariation) { - activeTracks.value = [initialVariation]; - } else { - activeTracks.value = []; // aucun contenu disponible - } -}); +function initializeActiveTracks() { + const initialVariation = getInitialVariation(); + activeTracks.value = initialVariation ? [initialVariation] : []; +} -// scroll si hash présent -onMounted(() => { - if (route.query?.comments) isCommentsOpen.value = true; +function normalizeUrlHash() { + if (route?.hash && route.hash.includes('_')) { + const normalizedHash = normalizeSlug(route.hash); + router.replace({ ...route, hash: normalizedHash }); + } +} + +function openCommentsIfRequested() { + if (route.query?.comments) { + isCommentsOpen.value = true; + } +} + +function scrollToHashTarget() { if (!route?.hash || route.hash.length === 0) return; - const selector = route.hash.replace('#', '#track--'); - const targetBtn = document.querySelector(selector); - if (targetBtn) targetBtn.scrollIntoView(); + const selectorId = route.hash.replace('#', '#track--'); + const targetButton = document.querySelector(selectorId); + if (targetButton) { + targetButton.scrollIntoView(); + } +} + +onBeforeMount(() => { + initializeActiveTracks(); }); -// ---------- COMPUTED / WATCH ---------- +onMounted(() => { + openCommentsIfRequested(); + normalizeUrlHash(); + scrollToHashTarget(); +}); const isSingleImage = computed(() => { return ( @@ -149,38 +173,52 @@ const singleFile = computed(() => { return isSingleImage.value ? activeTracks.value[0].files[0] : null; }); -watch( - singleFile, - (newValue) => { - if (newValue) openedFile.value = newValue; - }, - { immediate: true } -); - -// gestion du mode comparaison : fermer les commentaires, etc. -watch(isCompareModeEnabled, (newValue) => { - if (newValue) { - isCommentsOpen.value = false; - isCommentPanelEnabled.value = false; - } else { - isCommentPanelEnabled.value = true; +function updateOpenedFile(file) { + if (file) { + openedFile.value = file; } +} - // quand on quitte le mode comparaison on retire l'élément secondaire si nécessaire - if (!newValue && activeTracks.value.length === 2) { +function enableCompareModeUI() { + isCommentsOpen.value = false; + isCommentPanelEnabled.value = false; +} + +function disableCompareModeUI() { + isCommentPanelEnabled.value = true; + + if (activeTracks.value.length === 2) { activeTracks.value.pop(); } +} + +function updateUrlHash(firstTrack) { + const trackSlug = getVariationSlug(firstTrack); + if (!trackSlug) return; + + const currentHash = route.hash ? route.hash.substring(1) : ''; + const normalizedTrackSlug = normalizeSlug(trackSlug); + + if (currentHash !== normalizedTrackSlug) { + router.replace({ ...route, hash: '#' + normalizedTrackSlug }); + } +} + +watch(singleFile, updateOpenedFile, { immediate: true }); + +watch(isCompareModeEnabled, (isEnabled) => { + isEnabled ? enableCompareModeUI() : disableCompareModeUI(); }); -// ---------- UTIL / helper ---------- -function getCommentsCount(track) { - if (!track || !Array.isArray(track.files)) return undefined; - let count = 0; - for (const file of track.files) { - count += file?.comments?.length || 0; - } - return count > 0 ? count : undefined; -} +watch( + activeTracks, + (tracks) => { + if (tracks && tracks.length > 0) { + updateUrlHash(tracks[0]); + } + }, + { deep: true } +); diff --git a/public/site/plugins/analytics/src/index.js b/public/site/plugins/analytics/src/index.js new file mode 100644 index 0000000..1235bac --- /dev/null +++ b/public/site/plugins/analytics/src/index.js @@ -0,0 +1,7 @@ +import AnalyticsDashboard from "./components/AnalyticsDashboard.vue"; + +window.panel.plugin("adrienpayet/analytics", { + fields: { + "analytics-dashboard": AnalyticsDashboard + } +}); diff --git a/src/stores/analytics.js b/src/stores/analytics.js new file mode 100644 index 0000000..f31b0e5 --- /dev/null +++ b/src/stores/analytics.js @@ -0,0 +1,54 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; + +export const useAnalyticsStore = defineStore('analytics', () => { + const sessionId = ref(null); + + function initSession() { + // Récupérer sessionId depuis sessionStorage ou en créer un nouveau + const storedSessionId = sessionStorage.getItem('analyticsSessionId'); + + if (storedSessionId) { + sessionId.value = storedSessionId; + } else { + sessionId.value = generateSessionId(); + sessionStorage.setItem('analyticsSessionId', sessionId.value); + } + } + + function generateSessionId() { + return `session_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`; + } + + async function trackVisit(pageUrl, pageType, pageName = null) { + if (!sessionId.value) { + initSession(); + } + + const data = { + sessionId: sessionId.value, + pageUrl, + pageType, + pageName, + }; + + try { + await fetch('/track-visit.json', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); + } catch (error) { + // Tracking silencieux : ne pas bloquer l'app si ça échoue + console.debug('Analytics tracking failed:', error); + } + } + + return { + sessionId, + initSession, + trackVisit, + }; +}); From de104dc7dd6863d006ec2d20fedb56ca0de3b82d Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 11:27:27 +0100 Subject: [PATCH 39/52] =?UTF-8?q?feat:=20filtre=20utilisateurs=20analytics?= =?UTF-8?q?,=20am=C3=A9liorations=20dashboard=20+=20autres=20modifs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Multiselect Kirby pour filtrer par utilisateur(s) - Données de test alignées sur les vrais comptes - Suppression bloc utilisateurs les plus actifs - Route get-data supporte le filtre emails - Améliorations UI filtres (layout dates + users) - Autres modifs : menu, router, dialog, deploy workflow Co-Authored-By: Claude Opus 4.6 --- .forgejo/workflows/deploy-demo.yml | 53 +++++++++ public/site/cache/index.html | 0 public/site/config/menu.php | 10 ++ public/site/plugins/analytics/index.css | 2 +- public/site/plugins/analytics/index.js | 8 +- .../plugins/analytics/routes/get-data.php | 30 ++++- .../src/components/AnalyticsDashboard.vue | 108 ++++++++++++------ src/router/router.js | 60 ++++++++++ src/stores/dialog.js | 14 +++ 9 files changed, 243 insertions(+), 42 deletions(-) create mode 100644 .forgejo/workflows/deploy-demo.yml delete mode 100644 public/site/cache/index.html diff --git a/.forgejo/workflows/deploy-demo.yml b/.forgejo/workflows/deploy-demo.yml new file mode 100644 index 0000000..ca3c523 --- /dev/null +++ b/.forgejo/workflows/deploy-demo.yml @@ -0,0 +1,53 @@ +name: Deploy Production + +on: + push: + branches: + - main + +jobs: + build-and-deploy: + name: Build and Deploy to Production + runs-on: docker + container: + image: forgejo-ci-node:latest + steps: + - name: Checkout code + run: | + git clone --depth 1 --branch main https://forge.studio-variable.com/${{ github.repository }}.git . + ls -la + + - name: Install npm dependencies + run: npm install + + - name: Build frontend (production) + run: npm run build + + - name: Install composer dependencies + run: | + cd dist + composer install --no-dev --optimize-autoloader + + - name: Deploy via rsync + env: + USERNAME: ${{ secrets.DEMO_USERNAME }} + PASSWORD: ${{ secrets.DEMO_PASSWORD }} + HOST: ${{ secrets.DEMO_HOST }} + run: | + cd dist + lftp -c " + set ftp:ssl-allow no; + open -u $USERNAME,$PASSWORD $PRODUCTION_HOST; + mirror --reverse --delete --verbose --ignore-time --parallel=10 \ + assets assets; + mirror --reverse --delete --verbose --ignore-time --parallel=10 \ + -x 'accounts/' \ + -x 'cache/' \ + -x 'sessions/' \ + site site; + mirror --reverse --delete --verbose --ignore-time --parallel=10 \ + kirby kirby; + mirror --reverse --delete --verbose --ignore-time --parallel=10 \ + vendor vendor; + put index.php -o index.php; + quit" diff --git a/public/site/cache/index.html b/public/site/cache/index.html deleted file mode 100644 index e69de29..0000000 diff --git a/public/site/config/menu.php b/public/site/config/menu.php index bce7476..dc00b7e 100644 --- a/public/site/config/menu.php +++ b/public/site/config/menu.php @@ -60,6 +60,16 @@ $menu = [ ], '-', '-', + 'analytics' => [ + 'label' => 'Analytics', + 'icon' => 'chart', + 'link' => 'pages/analytics', + 'current' => function (string $current): bool { + $path = Kirby\Cms\App::instance()->path(); + return Str::contains($path, 'pages/analytics'); + } + ], + '-', 'users', 'system' ]; diff --git a/public/site/plugins/analytics/index.css b/public/site/plugins/analytics/index.css index 4b7f131..9c68fe6 100644 --- a/public/site/plugins/analytics/index.css +++ b/public/site/plugins/analytics/index.css @@ -1 +1 @@ -.k-analytics-dashboard{padding:1.5rem 0}.k-analytics-filters{display:flex;gap:1rem;margin-bottom:1.5rem}.k-analytics-filters label{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light)}.k-analytics-filters input[type=date]{padding:.375rem .5rem;border:1px solid var(--color-border);border-radius:var(--rounded);font-size:.875rem;background:var(--color-background)}.k-analytics-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem;margin-bottom:1.5rem}.k-analytics-grid--2col{grid-template-columns:repeat(2,1fr)}.k-analytics-card{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;box-shadow:var(--shadow)}.k-analytics-card h3{font-size:.75rem;font-weight:600;color:var(--color-text-light);margin:0 0 .5rem;text-transform:uppercase;letter-spacing:.5px}.k-analytics-metric{font-size:2.5rem;font-weight:700;color:var(--color-text);line-height:1}.k-analytics-chart-container{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;margin-bottom:1.5rem;box-shadow:var(--shadow)}.k-analytics-chart-container h3{font-size:.875rem;font-weight:600;color:var(--color-text);margin:0 0 1rem}.k-analytics-chart-container canvas{max-height:300px}.k-analytics-empty{background:var(--color-background);border-radius:var(--rounded);padding:3rem;text-align:center;box-shadow:var(--shadow)}.k-analytics-empty p{margin:0;color:var(--color-text-light)}.k-analytics-list{list-style:none;margin:0;padding:0}.k-analytics-list li{display:flex;justify-content:space-between;padding:.375rem 0;border-bottom:1px solid var(--color-border);font-size:.875rem}.k-analytics-list li:last-child{border-bottom:none}.k-analytics-list-label{color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:1rem}.k-analytics-list-value{font-weight:600;color:var(--color-text);flex-shrink:0} +.k-analytics-dashboard{padding:1.5rem 0}.k-analytics-filters{display:flex;gap:1rem;margin-bottom:1.5rem}.k-analytics-filters label{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light)}.k-date-inputs-wrapper{display:flex;column-gap:1rem}.k-analytics-filters input[type=date]{padding:.375rem .5rem;border:1px solid var(--color-border);border-radius:var(--rounded);font-size:.875rem;background:var(--color-background)}.k-analytics-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem;margin-bottom:1.5rem}.k-analytics-user-filter{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light);margin-left:2rem}.k-field-name-user{min-width:15rem}.k-analytics-card{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;box-shadow:var(--shadow)}.k-analytics-card h3{font-size:.75rem;font-weight:600;color:var(--color-text-light);margin:0 0 .5rem;text-transform:uppercase;letter-spacing:.5px}.k-analytics-metric{font-size:2.5rem;font-weight:700;color:var(--color-text);line-height:1}.k-analytics-chart-container{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;margin-bottom:1.5rem;box-shadow:var(--shadow)}.k-analytics-chart-container h3{font-size:.875rem;font-weight:600;color:var(--color-text);margin:0 0 1rem}.k-analytics-chart-container canvas{max-height:300px}.k-analytics-empty{background:var(--color-background);border-radius:var(--rounded);padding:3rem;text-align:center;box-shadow:var(--shadow)}.k-analytics-empty p{margin:0;color:var(--color-text-light)}.k-analytics-list{list-style:none;margin:0;padding:0}.k-analytics-list li{display:flex;justify-content:space-between;padding:.375rem 0;border-bottom:1px solid var(--color-border);font-size:.875rem}.k-analytics-list li:last-child{border-bottom:none}.k-analytics-list-label{color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:1rem}.k-analytics-list-value{font-weight:600;color:var(--color-text);flex-shrink:0} diff --git a/public/site/plugins/analytics/index.js b/public/site/plugins/analytics/index.js index c6c00e6..bd6292a 100644 --- a/public/site/plugins/analytics/index.js +++ b/public/site/plugins/analytics/index.js @@ -3,16 +3,16 @@ var Rl=Object.defineProperty;var El=(X,N,q)=>N in X?Rl(X,N,{enumerable:!0,config * https://github.com/kurkle/color#readme * (c) 2024 Jukka Kurkela * Released under the MIT License - */function X(i){return i+.5|0}const N=(i,t,e)=>Math.max(Math.min(i,e),t);function q(i){return N(X(i*2.55),0,255)}function lt(i){return N(X(i*255),0,255)}function st(i){return N(X(i/2.55)/100,0,1)}function mi(i){return N(X(i*100),0,100)}const $={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},Le=[..."0123456789ABCDEF"],bn=i=>Le[i&15],_n=i=>Le[(i&240)>>4]+Le[i&15],Jt=i=>(i&240)>>4===(i&15),yn=i=>Jt(i.r)&&Jt(i.g)&&Jt(i.b)&&Jt(i.a);function xn(i){var t=i.length,e;return i[0]==="#"&&(t===4||t===5?e={r:255&$[i[1]]*17,g:255&$[i[2]]*17,b:255&$[i[3]]*17,a:t===5?$[i[4]]*17:255}:(t===7||t===9)&&(e={r:$[i[1]]<<4|$[i[2]],g:$[i[3]]<<4|$[i[4]],b:$[i[5]]<<4|$[i[6]],a:t===9?$[i[7]]<<4|$[i[8]]:255})),e}const vn=(i,t)=>i<255?t(i):"";function kn(i){var t=yn(i)?bn:_n;return i?"#"+t(i.r)+t(i.g)+t(i.b)+vn(i.a,t):void 0}const wn=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function bi(i,t,e){const s=t*Math.min(e,1-e),n=(o,r=(o+i/30)%12)=>e-s*Math.max(Math.min(r-3,9-r,1),-1);return[n(0),n(8),n(4)]}function Mn(i,t,e){const s=(n,o=(n+i/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[s(5),s(3),s(1)]}function Sn(i,t,e){const s=bi(i,1,.5);let n;for(t+e>1&&(n=1/(t+e),t*=n,e*=n),n=0;n<3;n++)s[n]*=1-t-e,s[n]+=t;return s}function Dn(i,t,e,s,n){return i===n?(t-e)/s+(t.5?h/(2-o-r):h/(o+r),l=Dn(e,s,n,h,o),l=l*60+.5),[l|0,c||0,a]}function Re(i,t,e,s){return(Array.isArray(t)?i(t[0],t[1],t[2]):i(t,e,s)).map(lt)}function Ee(i,t,e){return Re(bi,i,t,e)}function Pn(i,t,e){return Re(Sn,i,t,e)}function On(i,t,e){return Re(Mn,i,t,e)}function _i(i){return(i%360+360)%360}function Cn(i){const t=wn.exec(i);let e=255,s;if(!t)return;t[5]!==s&&(e=t[6]?q(+t[5]):lt(+t[5]));const n=_i(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?s=Pn(n,o,r):t[1]==="hsv"?s=On(n,o,r):s=Ee(n,o,r),{r:s[0],g:s[1],b:s[2],a:e}}function Tn(i,t){var e=Fe(i);e[0]=_i(e[0]+t),e=Ee(e),i.r=e[0],i.g=e[1],i.b=e[2]}function An(i){if(!i)return;const t=Fe(i),e=t[0],s=mi(t[1]),n=mi(t[2]);return i.a<255?`hsla(${e}, ${s}%, ${n}%, ${st(i.a)})`:`hsl(${e}, ${s}%, ${n}%)`}const yi={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},xi={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function In(){const i={},t=Object.keys(xi),e=Object.keys(yi);let s,n,o,r,a;for(s=0;s>16&255,o>>8&255,o&255]}return i}let te;function Ln(i){te||(te=In(),te.transparent=[0,0,0,0]);const t=te[i.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const Fn=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function Rn(i){const t=Fn.exec(i);let e=255,s,n,o;if(t){if(t[7]!==s){const r=+t[7];e=t[8]?q(r):N(r*255,0,255)}return s=+t[1],n=+t[3],o=+t[5],s=255&(t[2]?q(s):N(s,0,255)),n=255&(t[4]?q(n):N(n,0,255)),o=255&(t[6]?q(o):N(o,0,255)),{r:s,g:n,b:o,a:e}}}function En(i){return i&&(i.a<255?`rgba(${i.r}, ${i.g}, ${i.b}, ${st(i.a)})`:`rgb(${i.r}, ${i.g}, ${i.b})`)}const ze=i=>i<=.0031308?i*12.92:Math.pow(i,1/2.4)*1.055-.055,Pt=i=>i<=.04045?i/12.92:Math.pow((i+.055)/1.055,2.4);function zn(i,t,e){const s=Pt(st(i.r)),n=Pt(st(i.g)),o=Pt(st(i.b));return{r:lt(ze(s+e*(Pt(st(t.r))-s))),g:lt(ze(n+e*(Pt(st(t.g))-n))),b:lt(ze(o+e*(Pt(st(t.b))-o))),a:i.a+e*(t.a-i.a)}}function ee(i,t,e){if(i){let s=Fe(i);s[t]=Math.max(0,Math.min(s[t]+s[t]*e,t===0?360:1)),s=Ee(s),i.r=s[0],i.g=s[1],i.b=s[2]}}function vi(i,t){return i&&Object.assign(t||{},i)}function ki(i){var t={r:0,g:0,b:0,a:255};return Array.isArray(i)?i.length>=3&&(t={r:i[0],g:i[1],b:i[2],a:255},i.length>3&&(t.a=lt(i[3]))):(t=vi(i,{r:0,g:0,b:0,a:1}),t.a=lt(t.a)),t}function Bn(i){return i.charAt(0)==="r"?Rn(i):Cn(i)}class Ft{constructor(t){if(t instanceof Ft)return t;const e=typeof t;let s;e==="object"?s=ki(t):e==="string"&&(s=xn(t)||Ln(t)||Bn(t)),this._rgb=s,this._valid=!!s}get valid(){return this._valid}get rgb(){var t=vi(this._rgb);return t&&(t.a=st(t.a)),t}set rgb(t){this._rgb=ki(t)}rgbString(){return this._valid?En(this._rgb):void 0}hexString(){return this._valid?kn(this._rgb):void 0}hslString(){return this._valid?An(this._rgb):void 0}mix(t,e){if(t){const s=this.rgb,n=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=s.a-n.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,s.r=255&c*s.r+o*n.r+.5,s.g=255&c*s.g+o*n.g+.5,s.b=255&c*s.b+o*n.b+.5,s.a=r*s.a+(1-r)*n.a,this.rgb=s}return this}interpolate(t,e){return t&&(this._rgb=zn(this._rgb,t._rgb,e)),this}clone(){return new Ft(this.rgb)}alpha(t){return this._rgb.a=lt(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=X(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return ee(this._rgb,2,t),this}darken(t){return ee(this._rgb,2,-t),this}saturate(t){return ee(this._rgb,1,t),this}desaturate(t){return ee(this._rgb,1,-t),this}rotate(t){return Tn(this._rgb,t),this}}/*! + */function X(i){return i+.5|0}const N=(i,t,e)=>Math.max(Math.min(i,e),t);function q(i){return N(X(i*2.55),0,255)}function lt(i){return N(X(i*255),0,255)}function st(i){return N(X(i/2.55)/100,0,1)}function mi(i){return N(X(i*100),0,100)}const $={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},Le=[..."0123456789ABCDEF"],bn=i=>Le[i&15],_n=i=>Le[(i&240)>>4]+Le[i&15],Jt=i=>(i&240)>>4===(i&15),yn=i=>Jt(i.r)&&Jt(i.g)&&Jt(i.b)&&Jt(i.a);function xn(i){var t=i.length,e;return i[0]==="#"&&(t===4||t===5?e={r:255&$[i[1]]*17,g:255&$[i[2]]*17,b:255&$[i[3]]*17,a:t===5?$[i[4]]*17:255}:(t===7||t===9)&&(e={r:$[i[1]]<<4|$[i[2]],g:$[i[3]]<<4|$[i[4]],b:$[i[5]]<<4|$[i[6]],a:t===9?$[i[7]]<<4|$[i[8]]:255})),e}const vn=(i,t)=>i<255?t(i):"";function kn(i){var t=yn(i)?bn:_n;return i?"#"+t(i.r)+t(i.g)+t(i.b)+vn(i.a,t):void 0}const wn=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function bi(i,t,e){const s=t*Math.min(e,1-e),n=(o,r=(o+i/30)%12)=>e-s*Math.max(Math.min(r-3,9-r,1),-1);return[n(0),n(8),n(4)]}function Mn(i,t,e){const s=(n,o=(n+i/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[s(5),s(3),s(1)]}function Sn(i,t,e){const s=bi(i,1,.5);let n;for(t+e>1&&(n=1/(t+e),t*=n,e*=n),n=0;n<3;n++)s[n]*=1-t-e,s[n]+=t;return s}function Dn(i,t,e,s,n){return i===n?(t-e)/s+(t.5?h/(2-o-r):h/(o+r),l=Dn(e,s,n,h,o),l=l*60+.5),[l|0,c||0,a]}function Re(i,t,e,s){return(Array.isArray(t)?i(t[0],t[1],t[2]):i(t,e,s)).map(lt)}function Ee(i,t,e){return Re(bi,i,t,e)}function Pn(i,t,e){return Re(Sn,i,t,e)}function Cn(i,t,e){return Re(Mn,i,t,e)}function _i(i){return(i%360+360)%360}function On(i){const t=wn.exec(i);let e=255,s;if(!t)return;t[5]!==s&&(e=t[6]?q(+t[5]):lt(+t[5]));const n=_i(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?s=Pn(n,o,r):t[1]==="hsv"?s=Cn(n,o,r):s=Ee(n,o,r),{r:s[0],g:s[1],b:s[2],a:e}}function Tn(i,t){var e=Fe(i);e[0]=_i(e[0]+t),e=Ee(e),i.r=e[0],i.g=e[1],i.b=e[2]}function An(i){if(!i)return;const t=Fe(i),e=t[0],s=mi(t[1]),n=mi(t[2]);return i.a<255?`hsla(${e}, ${s}%, ${n}%, ${st(i.a)})`:`hsl(${e}, ${s}%, ${n}%)`}const yi={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},xi={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function In(){const i={},t=Object.keys(xi),e=Object.keys(yi);let s,n,o,r,a;for(s=0;s>16&255,o>>8&255,o&255]}return i}let te;function Ln(i){te||(te=In(),te.transparent=[0,0,0,0]);const t=te[i.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const Fn=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function Rn(i){const t=Fn.exec(i);let e=255,s,n,o;if(t){if(t[7]!==s){const r=+t[7];e=t[8]?q(r):N(r*255,0,255)}return s=+t[1],n=+t[3],o=+t[5],s=255&(t[2]?q(s):N(s,0,255)),n=255&(t[4]?q(n):N(n,0,255)),o=255&(t[6]?q(o):N(o,0,255)),{r:s,g:n,b:o,a:e}}}function En(i){return i&&(i.a<255?`rgba(${i.r}, ${i.g}, ${i.b}, ${st(i.a)})`:`rgb(${i.r}, ${i.g}, ${i.b})`)}const ze=i=>i<=.0031308?i*12.92:Math.pow(i,1/2.4)*1.055-.055,Pt=i=>i<=.04045?i/12.92:Math.pow((i+.055)/1.055,2.4);function zn(i,t,e){const s=Pt(st(i.r)),n=Pt(st(i.g)),o=Pt(st(i.b));return{r:lt(ze(s+e*(Pt(st(t.r))-s))),g:lt(ze(n+e*(Pt(st(t.g))-n))),b:lt(ze(o+e*(Pt(st(t.b))-o))),a:i.a+e*(t.a-i.a)}}function ee(i,t,e){if(i){let s=Fe(i);s[t]=Math.max(0,Math.min(s[t]+s[t]*e,t===0?360:1)),s=Ee(s),i.r=s[0],i.g=s[1],i.b=s[2]}}function vi(i,t){return i&&Object.assign(t||{},i)}function ki(i){var t={r:0,g:0,b:0,a:255};return Array.isArray(i)?i.length>=3&&(t={r:i[0],g:i[1],b:i[2],a:255},i.length>3&&(t.a=lt(i[3]))):(t=vi(i,{r:0,g:0,b:0,a:1}),t.a=lt(t.a)),t}function Bn(i){return i.charAt(0)==="r"?Rn(i):On(i)}class Ft{constructor(t){if(t instanceof Ft)return t;const e=typeof t;let s;e==="object"?s=ki(t):e==="string"&&(s=xn(t)||Ln(t)||Bn(t)),this._rgb=s,this._valid=!!s}get valid(){return this._valid}get rgb(){var t=vi(this._rgb);return t&&(t.a=st(t.a)),t}set rgb(t){this._rgb=ki(t)}rgbString(){return this._valid?En(this._rgb):void 0}hexString(){return this._valid?kn(this._rgb):void 0}hslString(){return this._valid?An(this._rgb):void 0}mix(t,e){if(t){const s=this.rgb,n=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=s.a-n.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,s.r=255&c*s.r+o*n.r+.5,s.g=255&c*s.g+o*n.g+.5,s.b=255&c*s.b+o*n.b+.5,s.a=r*s.a+(1-r)*n.a,this.rgb=s}return this}interpolate(t,e){return t&&(this._rgb=zn(this._rgb,t._rgb,e)),this}clone(){return new Ft(this.rgb)}alpha(t){return this._rgb.a=lt(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=X(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return ee(this._rgb,2,t),this}darken(t){return ee(this._rgb,2,-t),this}saturate(t){return ee(this._rgb,1,t),this}desaturate(t){return ee(this._rgb,1,-t),this}rotate(t){return Tn(this._rgb,t),this}}/*! * Chart.js v4.5.1 * https://www.chartjs.org * (c) 2025 Chart.js Contributors * Released under the MIT License - */function nt(){}const Nn=(()=>{let i=0;return()=>i++})();function T(i){return i==null}function z(i){if(Array.isArray&&Array.isArray(i))return!0;const t=Object.prototype.toString.call(i);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function D(i){return i!==null&&Object.prototype.toString.call(i)==="[object Object]"}function V(i){return(typeof i=="number"||i instanceof Number)&&isFinite(+i)}function G(i,t){return V(i)?i:t}function C(i,t){return typeof i>"u"?t:i}const Vn=(i,t)=>typeof i=="string"&&i.endsWith("%")?parseFloat(i)/100*t:+i;function A(i,t,e){if(i&&typeof i.call=="function")return i.apply(e,t)}function O(i,t,e,s){let n,o,r;if(z(i))for(o=i.length,n=0;ni,x:i=>i.x,y:i=>i.y};function Wn(i){const t=i.split("."),e=[];let s="";for(const n of t)s+=n,s.endsWith("\\")?s=s.slice(0,-1)+".":(e.push(s),s="");return e}function $n(i){const t=Wn(i);return e=>{for(const s of t){if(s==="")break;e=e&&e[s]}return e}}function ne(i,t){return(Mi[t]||(Mi[t]=$n(t)))(i)}function Be(i){return i.charAt(0).toUpperCase()+i.slice(1)}const oe=i=>typeof i<"u",ct=i=>typeof i=="function",Si=(i,t)=>{if(i.size!==t.size)return!1;for(const e of i)if(!t.has(e))return!1;return!0};function Yn(i){return i.type==="mouseup"||i.type==="click"||i.type==="contextmenu"}const j=Math.PI,Z=2*j,Un=Z+j,re=Number.POSITIVE_INFINITY,Xn=j/180,K=j/2,gt=j/4,Di=j*2/3,Pi=Math.log10,Ot=Math.sign;function zt(i,t,e){return Math.abs(i-t)n-o).pop(),t}function qn(i){return typeof i=="symbol"||typeof i=="object"&&i!==null&&!(Symbol.toPrimitive in i||"toString"in i||"valueOf"in i)}function Bt(i){return!qn(i)&&!isNaN(parseFloat(i))&&isFinite(i)}function Gn(i,t){const e=Math.round(i);return e-t<=i&&e+t>=i}function Zn(i,t,e){let s,n,o;for(s=0,n=i.length;sl&&c=Math.min(t,e)-s&&i<=Math.max(t,e)+s}function Ve(i,t,e){e=e||(r=>i[r]1;)o=n+s>>1,e(o)?n=o:s=o;return{lo:n,hi:s}}const mt=(i,t,e,s)=>Ve(i,e,s?n=>{const o=i[n][t];return oi[n][t]Ve(i,e,s=>i[s][t]>=e);function so(i,t,e){let s=0,n=i.length;for(;ss&&i[n-1]>e;)n--;return s>0||n{const s="_onData"+Be(e),n=i[e];Object.defineProperty(i,e,{configurable:!0,enumerable:!1,value(...o){const r=n.apply(this,o);return i._chartjs.listeners.forEach(a=>{typeof a[s]=="function"&&a[s](...o)}),r}})})}function Li(i,t){const e=i._chartjs;if(!e)return;const s=e.listeners,n=s.indexOf(t);n!==-1&&s.splice(n,1),!(s.length>0)&&(Ii.forEach(o=>{delete i[o]}),delete i._chartjs)}function oo(i){const t=new Set(i);return t.size===i.length?i:Array.from(t)}const Fi=(function(){return typeof window>"u"?function(i){return i()}:window.requestAnimationFrame})();function Ri(i,t){let e=[],s=!1;return function(...n){e=n,s||(s=!0,Fi.call(window,()=>{s=!1,i.apply(t,e)}))}}function ro(i,t){let e;return function(...s){return t?(clearTimeout(e),e=setTimeout(i,t,s)):i.apply(this,s),t}}const ao=i=>i==="start"?"left":i==="end"?"right":"center",Ei=(i,t,e)=>i==="start"?t:i==="end"?e:(t+e)/2;function lo(i,t,e){const s=t.length;let n=0,o=s;if(i._sorted){const{iScale:r,vScale:a,_parsed:l}=i,c=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null,h=r.axis,{min:f,max:d,minDefined:u,maxDefined:m}=r.getUserBounds();if(u){if(n=Math.min(mt(l,h,f).lo,e?s:mt(t,h,r.getPixelForValue(f)).lo),c){const g=l.slice(0,n+1).reverse().findIndex(p=>!T(p[a.axis]));n-=Math.max(0,g)}n=Y(n,0,s-1)}if(m){let g=Math.max(mt(l,r.axis,d,!0).hi+1,e?0:mt(t,h,r.getPixelForValue(d),!0).hi+1);if(c){const p=l.slice(g-1).findIndex(b=>!T(b[a.axis]));g+=Math.max(0,p)}o=Y(g,n,s)-n}else o=s-n}return{start:n,count:o}}function co(i){const{xScale:t,yScale:e,_scaleRanges:s}=i,n={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!s)return i._scaleRanges=n,!0;const o=s.xmin!==t.min||s.xmax!==t.max||s.ymin!==e.min||s.ymax!==e.max;return Object.assign(s,n),o}const ae=i=>i===0||i===1,zi=(i,t,e)=>-(Math.pow(2,10*(i-=1))*Math.sin((i-t)*Z/e)),Bi=(i,t,e)=>Math.pow(2,-10*i)*Math.sin((i-t)*Z/e)+1,Nt={linear:i=>i,easeInQuad:i=>i*i,easeOutQuad:i=>-i*(i-2),easeInOutQuad:i=>(i/=.5)<1?.5*i*i:-.5*(--i*(i-2)-1),easeInCubic:i=>i*i*i,easeOutCubic:i=>(i-=1)*i*i+1,easeInOutCubic:i=>(i/=.5)<1?.5*i*i*i:.5*((i-=2)*i*i+2),easeInQuart:i=>i*i*i*i,easeOutQuart:i=>-((i-=1)*i*i*i-1),easeInOutQuart:i=>(i/=.5)<1?.5*i*i*i*i:-.5*((i-=2)*i*i*i-2),easeInQuint:i=>i*i*i*i*i,easeOutQuint:i=>(i-=1)*i*i*i*i+1,easeInOutQuint:i=>(i/=.5)<1?.5*i*i*i*i*i:.5*((i-=2)*i*i*i*i+2),easeInSine:i=>-Math.cos(i*K)+1,easeOutSine:i=>Math.sin(i*K),easeInOutSine:i=>-.5*(Math.cos(j*i)-1),easeInExpo:i=>i===0?0:Math.pow(2,10*(i-1)),easeOutExpo:i=>i===1?1:-Math.pow(2,-10*i)+1,easeInOutExpo:i=>ae(i)?i:i<.5?.5*Math.pow(2,10*(i*2-1)):.5*(-Math.pow(2,-10*(i*2-1))+2),easeInCirc:i=>i>=1?i:-(Math.sqrt(1-i*i)-1),easeOutCirc:i=>Math.sqrt(1-(i-=1)*i),easeInOutCirc:i=>(i/=.5)<1?-.5*(Math.sqrt(1-i*i)-1):.5*(Math.sqrt(1-(i-=2)*i)+1),easeInElastic:i=>ae(i)?i:zi(i,.075,.3),easeOutElastic:i=>ae(i)?i:Bi(i,.075,.3),easeInOutElastic(i){return ae(i)?i:i<.5?.5*zi(i*2,.1125,.45):.5+.5*Bi(i*2-1,.1125,.45)},easeInBack(i){return i*i*((1.70158+1)*i-1.70158)},easeOutBack(i){return(i-=1)*i*((1.70158+1)*i+1.70158)+1},easeInOutBack(i){let t=1.70158;return(i/=.5)<1?.5*(i*i*(((t*=1.525)+1)*i-t)):.5*((i-=2)*i*(((t*=1.525)+1)*i+t)+2)},easeInBounce:i=>1-Nt.easeOutBounce(1-i),easeOutBounce(i){return i<1/2.75?7.5625*i*i:i<2/2.75?7.5625*(i-=1.5/2.75)*i+.75:i<2.5/2.75?7.5625*(i-=2.25/2.75)*i+.9375:7.5625*(i-=2.625/2.75)*i+.984375},easeInOutBounce:i=>i<.5?Nt.easeInBounce(i*2)*.5:Nt.easeOutBounce(i*2-1)*.5+.5};function je(i){if(i&&typeof i=="object"){const t=i.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Ni(i){return je(i)?i:new Ft(i)}function He(i){return je(i)?i:new Ft(i).saturate(.5).darken(.1).hexString()}const ho=["x","y","borderWidth","radius","tension"],fo=["color","borderColor","backgroundColor"];function uo(i){i.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),i.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),i.set("animations",{colors:{type:"color",properties:fo},numbers:{type:"number",properties:ho}}),i.describe("animations",{_fallback:"animation"}),i.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:t=>t|0}}}})}function go(i){i.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Vi=new Map;function po(i,t){t=t||{};const e=i+JSON.stringify(t);let s=Vi.get(e);return s||(s=new Intl.NumberFormat(i,t),Vi.set(e,s)),s}function ji(i,t,e){return po(t,e).format(i)}const mo={values(i){return z(i)?i:""+i},numeric(i,t,e){if(i===0)return"0";const s=this.chart.options.locale;let n,o=i;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(n="scientific"),o=bo(i,e)}const r=Pi(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:n,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),ji(i,s,l)}};function bo(i,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&i!==Math.floor(i)&&(e=i-Math.floor(i)),e}var Hi={formatters:mo};function _o(i){i.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width: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:Hi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),i.route("scale.ticks","color","","color"),i.route("scale.grid","color","","borderColor"),i.route("scale.border","color","","borderColor"),i.route("scale.title","color","","color"),i.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),i.describe("scales",{_fallback:"scale"}),i.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const bt=Object.create(null),We=Object.create(null);function Vt(i,t){if(!t)return i;const e=t.split(".");for(let s=0,n=e.length;ss.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=(s,n)=>He(n.backgroundColor),this.hoverBorderColor=(s,n)=>He(n.borderColor),this.hoverColor=(s,n)=>He(n.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(t),this.apply(e)}set(t,e){return $e(this,t,e)}get(t){return Vt(this,t)}describe(t,e){return $e(We,t,e)}override(t,e){return $e(bt,t,e)}route(t,e,s,n){const o=Vt(this,t),r=Vt(this,s),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[n];return D(l)?Object.assign({},c,l):C(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var R=new yo({_scriptable:i=>!i.startsWith("on"),_indexable:i=>i!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[uo,go,_o]);function xo(i){return!i||T(i.size)||T(i.family)?null:(i.style?i.style+" ":"")+(i.weight?i.weight+" ":"")+i.size+"px "+i.family}function Wi(i,t,e,s,n){let o=t[n];return o||(o=t[n]=i.measureText(n).width,e.push(n)),o>s&&(s=o),s}function _t(i,t,e){const s=i.currentDevicePixelRatio,n=e!==0?Math.max(e/2,.5):0;return Math.round((t-n)*s)/s+n}function $i(i,t){!t&&!i||(t=t||i.getContext("2d"),t.save(),t.resetTransform(),t.clearRect(0,0,i.width,i.height),t.restore())}function Ye(i,t,e,s){vo(i,t,e,s)}function vo(i,t,e,s,n){let o,r,a,l,c,h,f,d;const u=t.pointStyle,m=t.rotation,g=t.radius;let p=(m||0)*Xn;if(u&&typeof u=="object"&&(o=u.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){i.save(),i.translate(e,s),i.rotate(p),i.drawImage(u,-u.width/2,-u.height/2,u.width,u.height),i.restore();return}if(!(isNaN(g)||g<=0)){switch(i.beginPath(),u){default:i.arc(e,s,g,0,Z),i.closePath();break;case"triangle":h=g,i.moveTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Di,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Di,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),i.closePath();break;case"rectRounded":c=g*.516,l=g-c,r=Math.cos(p+gt)*l,f=Math.cos(p+gt)*l,a=Math.sin(p+gt)*l,d=Math.sin(p+gt)*l,i.arc(e-f,s-a,c,p-j,p-K),i.arc(e+d,s-r,c,p-K,p),i.arc(e+f,s+a,c,p,p+K),i.arc(e-d,s+r,c,p+K,p+j),i.closePath();break;case"rect":if(!m){l=Math.SQRT1_2*g,h=l,i.rect(e-h,s-l,2*h,2*l);break}p+=gt;case"rectRot":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+d,s-r),i.lineTo(e+f,s+a),i.lineTo(e-d,s+r),i.closePath();break;case"crossRot":p+=gt;case"cross":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r);break;case"star":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r),p+=gt,f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r);break;case"line":r=Math.cos(p)*g,a=Math.sin(p)*g,i.moveTo(e-r,s-a),i.lineTo(e+r,s+a);break;case"dash":i.moveTo(e,s),i.lineTo(e+Math.cos(p)*g,s+Math.sin(p)*g);break;case!1:i.closePath();break}i.fill(),t.borderWidth>0&&i.stroke()}}function jt(i,t,e){return e=e||.5,!t||i&&i.x>t.left-e&&i.xt.top-e&&i.y0&&o.strokeColor!=="";let l,c;for(i.save(),i.font=n.string,Mo(i,o),l=0;l+i||0;function Xi(i,t){const e={},s=D(t),n=s?Object.keys(t):t,o=D(i)?s?r=>C(i[r],i[t[r]]):r=>i[r]:()=>i;for(const r of n)e[r]=To(o(r));return e}function Ao(i){return Xi(i,{top:"y",right:"x",bottom:"y",left:"x"})}function le(i){return Xi(i,["topLeft","topRight","bottomLeft","bottomRight"])}function ht(i){const t=Ao(i);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function J(i,t){i=i||{},t=t||R.font;let e=C(i.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let s=C(i.style,t.style);s&&!(""+s).match(Oo)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:C(i.family,t.family),lineHeight:Co(C(i.lineHeight,t.lineHeight),e),size:e,style:s,weight:C(i.weight,t.weight),string:""};return n.string=xo(n),n}function ce(i,t,e,s){let n,o,r;for(n=0,o=i.length;ne&&a===0?0:a+l;return{min:r(s,-Math.abs(o)),max:r(n,o)}}function yt(i,t){return Object.assign(Object.create(i),t)}function Ke(i,t=[""],e,s,n=()=>i[0]){const o=e||i;typeof s>"u"&&(s=Qi("_fallback",i));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:i,_rootScopes:o,_fallback:s,_getTarget:n,override:a=>Ke([a,...i],t,o,s)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete i[0][l],!0},get(a,l){return qi(a,l,()=>Vo(l,t,i,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(i[0])},has(a,l){return Ji(a).includes(l)},ownKeys(a){return Ji(a)},set(a,l,c){const h=a._storage||(a._storage=n());return a[l]=h[l]=c,delete a._keys,!0}})}function Ct(i,t,e,s){const n={_cacheable:!1,_proxy:i,_context:t,_subProxy:e,_stack:new Set,_descriptors:Ki(i,s),setContext:o=>Ct(i,o,e,s),override:o=>Ct(i.override(o),t,e,s)};return new Proxy(n,{deleteProperty(o,r){return delete o[r],delete i[r],!0},get(o,r,a){return qi(o,r,()=>Fo(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(i,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(i,r)},getPrototypeOf(){return Reflect.getPrototypeOf(i)},has(o,r){return Reflect.has(i,r)},ownKeys(){return Reflect.ownKeys(i)},set(o,r,a){return i[r]=a,delete o[r],!0}})}function Ki(i,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:s=t.indexable,_allKeys:n=t.allKeys}=i;return{allKeys:n,scriptable:e,indexable:s,isScriptable:ct(e)?e:()=>e,isIndexable:ct(s)?s:()=>s}}const Lo=(i,t)=>i?i+Be(t):t,qe=(i,t)=>D(t)&&i!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function qi(i,t,e){if(Object.prototype.hasOwnProperty.call(i,t)||t==="constructor")return i[t];const s=e();return i[t]=s,s}function Fo(i,t,e){const{_proxy:s,_context:n,_subProxy:o,_descriptors:r}=i;let a=s[t];return ct(a)&&r.isScriptable(t)&&(a=Ro(t,a,i,e)),z(a)&&a.length&&(a=Eo(t,a,i,r.isIndexable)),qe(t,a)&&(a=Ct(a,n,o&&o[t],r)),a}function Ro(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_stack:a}=e;if(a.has(i))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+i);a.add(i);let l=t(o,r||s);return a.delete(i),qe(i,l)&&(l=Ge(n._scopes,n,i,l)),l}function Eo(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&s(i))return t[o.index%t.length];if(D(t[0])){const l=t,c=n._scopes.filter(h=>h!==l);t=[];for(const h of l){const f=Ge(c,n,i,h);t.push(Ct(f,o,r&&r[i],a))}}return t}function Gi(i,t,e){return ct(i)?i(t,e):i}const zo=(i,t)=>i===!0?t:typeof i=="string"?ne(t,i):void 0;function Bo(i,t,e,s,n){for(const o of t){const r=zo(e,o);if(r){i.add(r);const a=Gi(r._fallback,e,n);if(typeof a<"u"&&a!==e&&a!==s)return a}else if(r===!1&&typeof s<"u"&&e!==s)return null}return!1}function Ge(i,t,e,s){const n=t._rootScopes,o=Gi(t._fallback,e,s),r=[...i,...n],a=new Set;a.add(s);let l=Zi(a,r,e,o||e,s);return l===null||typeof o<"u"&&o!==e&&(l=Zi(a,r,o,l,s),l===null)?!1:Ke(Array.from(a),[""],n,o,()=>No(t,e,s))}function Zi(i,t,e,s,n){for(;e;)e=Bo(i,t,e,s,n);return e}function No(i,t,e){const s=i._getTarget();t in s||(s[t]={});const n=s[t];return z(n)&&D(e)?e:n||{}}function Vo(i,t,e,s){let n;for(const o of t)if(n=Qi(Lo(o,i),e),typeof n<"u")return qe(i,n)?Ge(e,s,i,n):n}function Qi(i,t){for(const e of t){if(!e)continue;const s=e[i];if(typeof s<"u")return s}}function Ji(i){let t=i._keys;return t||(t=i._keys=jo(i._scopes)),t}function jo(i){const t=new Set;for(const e of i)for(const s of Object.keys(e).filter(n=>!n.startsWith("_")))t.add(s);return Array.from(t)}const Ho=Number.EPSILON||1e-14,Tt=(i,t)=>ti==="x"?"y":"x";function Wo(i,t,e,s){const n=i.skip?t:i,o=t,r=e.skip?t:e,a=Ne(o,n),l=Ne(r,o);let c=a/(a+l),h=l/(a+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const f=s*c,d=s*h;return{previous:{x:o.x-f*(r.x-n.x),y:o.y-f*(r.y-n.y)},next:{x:o.x+d*(r.x-n.x),y:o.y+d*(r.y-n.y)}}}function $o(i,t,e){const s=i.length;let n,o,r,a,l,c=Tt(i,0);for(let h=0;h!c.skip)),t.cubicInterpolationMode==="monotone")Uo(i,n);else{let c=s?i[i.length-1]:i[0];for(o=0,r=i.length;oi.ownerDocument.defaultView.getComputedStyle(i,null);function qo(i,t){return de(i).getPropertyValue(t)}const Go=["top","right","bottom","left"];function xt(i,t,e){const s={};e=e?"-"+e:"";for(let n=0;n<4;n++){const o=Go[n];s[o]=parseFloat(i[t+"-"+o+e])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const Zo=(i,t,e)=>(i>0||t>0)&&(!e||!e.shadowRoot);function Qo(i,t){const e=i.touches,s=e&&e.length?e[0]:i,{offsetX:n,offsetY:o}=s;let r=!1,a,l;if(Zo(n,o,i.target))a=n,l=o;else{const c=t.getBoundingClientRect();a=s.clientX-c.left,l=s.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function vt(i,t){if("native"in i)return i;const{canvas:e,currentDevicePixelRatio:s}=t,n=de(e),o=n.boxSizing==="border-box",r=xt(n,"padding"),a=xt(n,"border","width"),{x:l,y:c,box:h}=Qo(i,e),f=r.left+(h&&a.left),d=r.top+(h&&a.top);let{width:u,height:m}=t;return o&&(u-=r.width+a.width,m-=r.height+a.height),{x:Math.round((l-f)/u*e.width/s),y:Math.round((c-d)/m*e.height/s)}}function Jo(i,t,e){let s,n;if(t===void 0||e===void 0){const o=i&&Qe(i);if(!o)t=i.clientWidth,e=i.clientHeight;else{const r=o.getBoundingClientRect(),a=de(o),l=xt(a,"border","width"),c=xt(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,s=fe(a.maxWidth,o,"clientWidth"),n=fe(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:s||re,maxHeight:n||re}}const ft=i=>Math.round(i*10)/10;function tr(i,t,e,s){const n=de(i),o=xt(n,"margin"),r=fe(n.maxWidth,i,"clientWidth")||re,a=fe(n.maxHeight,i,"clientHeight")||re,l=Jo(i,t,e);let{width:c,height:h}=l;if(n.boxSizing==="content-box"){const d=xt(n,"border","width"),u=xt(n,"padding");c-=u.width+d.width,h-=u.height+d.height}return c=Math.max(0,c-o.width),h=Math.max(0,s?c/s:h-o.height),c=ft(Math.min(c,r,l.maxWidth)),h=ft(Math.min(h,a,l.maxHeight)),c&&!h&&(h=ft(c/2)),(t!==void 0||e!==void 0)&&s&&l.height&&h>l.height&&(h=l.height,c=ft(Math.floor(h*s))),{width:c,height:h}}function es(i,t,e){const s=t||1,n=ft(i.height*s),o=ft(i.width*s);i.height=ft(i.height),i.width=ft(i.width);const r=i.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${i.height}px`,r.style.width=`${i.width}px`),i.currentDevicePixelRatio!==s||r.height!==n||r.width!==o?(i.currentDevicePixelRatio=s,r.height=n,r.width=o,i.ctx.setTransform(s,0,0,s,0,0),!0):!1}const er=(function(){let i=!1;try{const t={get passive(){return i=!0,!1}};Ze()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return i})();function is(i,t){const e=qo(i,t),s=e&&e.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function kt(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:i.y+e*(t.y-i.y)}}function ir(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:s==="middle"?e<.5?i.y:t.y:s==="after"?e<1?i.y:t.y:e>0?t.y:i.y}}function sr(i,t,e,s){const n={x:i.cp2x,y:i.cp2y},o={x:t.cp1x,y:t.cp1y},r=kt(i,n,e),a=kt(n,o,e),l=kt(o,t,e),c=kt(r,a,e),h=kt(a,l,e);return kt(c,h,e)}const nr=function(i,t){return{x(e){return i+i+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,s){return e-s},leftForLtr(e,s){return e-s}}},or=function(){return{x(i){return i},setWidth(i){},textAlign(i){return i},xPlus(i,t){return i+t},leftForLtr(i,t){return i}}};function Je(i,t,e){return i?nr(t,e):or()}function rr(i,t){let e,s;(t==="ltr"||t==="rtl")&&(e=i.canvas.style,s=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),i.prevTextDirection=s)}function ar(i,t){t!==void 0&&(delete i.prevTextDirection,i.canvas.style.setProperty("direction",t[0],t[1]))}function ss(i){return i==="angle"?{between:Ti,compare:to,normalize:Q}:{between:Ai,compare:(t,e)=>t-e,normalize:t=>t}}function ns({start:i,end:t,count:e,loop:s,style:n}){return{start:i%e,end:t%e,loop:s&&(t-i+1)%e===0,style:n}}function lr(i,t,e){const{property:s,start:n,end:o}=e,{between:r,normalize:a}=ss(s),l=t.length;let{start:c,end:h,loop:f}=i,d,u;if(f){for(c+=l,h+=l,d=0,u=l;dl(n,v,b)&&a(n,v)!==0,_=()=>a(o,b)===0||l(o,v,b),M=()=>g||x(),k=()=>!g||_();for(let w=h,P=h;w<=f;++w)y=t[w%r],!y.skip&&(b=c(y[s]),b!==v&&(g=l(b,n,o),p===null&&M()&&(p=a(b,n)===0?w:P),p!==null&&k()&&(m.push(ns({start:p,end:w,loop:d,count:r,style:u})),p=null),P=w,v=b));return p!==null&&m.push(ns({start:p,end:f,loop:d,count:r,style:u})),m}function rs(i,t){const e=[],s=i.segments;for(let n=0;nn&&i[o%t].skip;)o--;return o%=t,{start:n,end:o}}function hr(i,t,e,s){const n=i.length,o=[];let r=t,a=i[t],l;for(l=t+1;l<=e;++l){const c=i[l%n];c.skip||c.stop?a.skip||(s=!1,o.push({start:t%n,end:(l-1)%n,loop:s}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%n,end:r%n,loop:s}),o}function fr(i,t){const e=i.points,s=i.options.spanGaps,n=e.length;if(!n)return[];const o=!!i._loop,{start:r,end:a}=cr(e,n,o,s);if(s===!0)return as(i,[{start:r,end:a,loop:o}],e,t);const l=a{let i=0;return()=>i++})();function T(i){return i==null}function z(i){if(Array.isArray&&Array.isArray(i))return!0;const t=Object.prototype.toString.call(i);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function D(i){return i!==null&&Object.prototype.toString.call(i)==="[object Object]"}function V(i){return(typeof i=="number"||i instanceof Number)&&isFinite(+i)}function G(i,t){return V(i)?i:t}function O(i,t){return typeof i>"u"?t:i}const Vn=(i,t)=>typeof i=="string"&&i.endsWith("%")?parseFloat(i)/100*t:+i;function A(i,t,e){if(i&&typeof i.call=="function")return i.apply(e,t)}function C(i,t,e,s){let n,o,r;if(z(i))for(o=i.length,n=0;ni,x:i=>i.x,y:i=>i.y};function Wn(i){const t=i.split("."),e=[];let s="";for(const n of t)s+=n,s.endsWith("\\")?s=s.slice(0,-1)+".":(e.push(s),s="");return e}function $n(i){const t=Wn(i);return e=>{for(const s of t){if(s==="")break;e=e&&e[s]}return e}}function ne(i,t){return(Mi[t]||(Mi[t]=$n(t)))(i)}function Be(i){return i.charAt(0).toUpperCase()+i.slice(1)}const oe=i=>typeof i<"u",ct=i=>typeof i=="function",Si=(i,t)=>{if(i.size!==t.size)return!1;for(const e of i)if(!t.has(e))return!1;return!0};function Yn(i){return i.type==="mouseup"||i.type==="click"||i.type==="contextmenu"}const j=Math.PI,Z=2*j,Un=Z+j,re=Number.POSITIVE_INFINITY,Xn=j/180,K=j/2,gt=j/4,Di=j*2/3,Pi=Math.log10,Ct=Math.sign;function zt(i,t,e){return Math.abs(i-t)n-o).pop(),t}function qn(i){return typeof i=="symbol"||typeof i=="object"&&i!==null&&!(Symbol.toPrimitive in i||"toString"in i||"valueOf"in i)}function Bt(i){return!qn(i)&&!isNaN(parseFloat(i))&&isFinite(i)}function Gn(i,t){const e=Math.round(i);return e-t<=i&&e+t>=i}function Zn(i,t,e){let s,n,o;for(s=0,n=i.length;sl&&c=Math.min(t,e)-s&&i<=Math.max(t,e)+s}function Ve(i,t,e){e=e||(r=>i[r]1;)o=n+s>>1,e(o)?n=o:s=o;return{lo:n,hi:s}}const mt=(i,t,e,s)=>Ve(i,e,s?n=>{const o=i[n][t];return oi[n][t]Ve(i,e,s=>i[s][t]>=e);function so(i,t,e){let s=0,n=i.length;for(;ss&&i[n-1]>e;)n--;return s>0||n{const s="_onData"+Be(e),n=i[e];Object.defineProperty(i,e,{configurable:!0,enumerable:!1,value(...o){const r=n.apply(this,o);return i._chartjs.listeners.forEach(a=>{typeof a[s]=="function"&&a[s](...o)}),r}})})}function Li(i,t){const e=i._chartjs;if(!e)return;const s=e.listeners,n=s.indexOf(t);n!==-1&&s.splice(n,1),!(s.length>0)&&(Ii.forEach(o=>{delete i[o]}),delete i._chartjs)}function oo(i){const t=new Set(i);return t.size===i.length?i:Array.from(t)}const Fi=(function(){return typeof window>"u"?function(i){return i()}:window.requestAnimationFrame})();function Ri(i,t){let e=[],s=!1;return function(...n){e=n,s||(s=!0,Fi.call(window,()=>{s=!1,i.apply(t,e)}))}}function ro(i,t){let e;return function(...s){return t?(clearTimeout(e),e=setTimeout(i,t,s)):i.apply(this,s),t}}const ao=i=>i==="start"?"left":i==="end"?"right":"center",Ei=(i,t,e)=>i==="start"?t:i==="end"?e:(t+e)/2;function lo(i,t,e){const s=t.length;let n=0,o=s;if(i._sorted){const{iScale:r,vScale:a,_parsed:l}=i,c=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null,h=r.axis,{min:f,max:d,minDefined:u,maxDefined:m}=r.getUserBounds();if(u){if(n=Math.min(mt(l,h,f).lo,e?s:mt(t,h,r.getPixelForValue(f)).lo),c){const g=l.slice(0,n+1).reverse().findIndex(p=>!T(p[a.axis]));n-=Math.max(0,g)}n=Y(n,0,s-1)}if(m){let g=Math.max(mt(l,r.axis,d,!0).hi+1,e?0:mt(t,h,r.getPixelForValue(d),!0).hi+1);if(c){const p=l.slice(g-1).findIndex(b=>!T(b[a.axis]));g+=Math.max(0,p)}o=Y(g,n,s)-n}else o=s-n}return{start:n,count:o}}function co(i){const{xScale:t,yScale:e,_scaleRanges:s}=i,n={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!s)return i._scaleRanges=n,!0;const o=s.xmin!==t.min||s.xmax!==t.max||s.ymin!==e.min||s.ymax!==e.max;return Object.assign(s,n),o}const ae=i=>i===0||i===1,zi=(i,t,e)=>-(Math.pow(2,10*(i-=1))*Math.sin((i-t)*Z/e)),Bi=(i,t,e)=>Math.pow(2,-10*i)*Math.sin((i-t)*Z/e)+1,Nt={linear:i=>i,easeInQuad:i=>i*i,easeOutQuad:i=>-i*(i-2),easeInOutQuad:i=>(i/=.5)<1?.5*i*i:-.5*(--i*(i-2)-1),easeInCubic:i=>i*i*i,easeOutCubic:i=>(i-=1)*i*i+1,easeInOutCubic:i=>(i/=.5)<1?.5*i*i*i:.5*((i-=2)*i*i+2),easeInQuart:i=>i*i*i*i,easeOutQuart:i=>-((i-=1)*i*i*i-1),easeInOutQuart:i=>(i/=.5)<1?.5*i*i*i*i:-.5*((i-=2)*i*i*i-2),easeInQuint:i=>i*i*i*i*i,easeOutQuint:i=>(i-=1)*i*i*i*i+1,easeInOutQuint:i=>(i/=.5)<1?.5*i*i*i*i*i:.5*((i-=2)*i*i*i*i+2),easeInSine:i=>-Math.cos(i*K)+1,easeOutSine:i=>Math.sin(i*K),easeInOutSine:i=>-.5*(Math.cos(j*i)-1),easeInExpo:i=>i===0?0:Math.pow(2,10*(i-1)),easeOutExpo:i=>i===1?1:-Math.pow(2,-10*i)+1,easeInOutExpo:i=>ae(i)?i:i<.5?.5*Math.pow(2,10*(i*2-1)):.5*(-Math.pow(2,-10*(i*2-1))+2),easeInCirc:i=>i>=1?i:-(Math.sqrt(1-i*i)-1),easeOutCirc:i=>Math.sqrt(1-(i-=1)*i),easeInOutCirc:i=>(i/=.5)<1?-.5*(Math.sqrt(1-i*i)-1):.5*(Math.sqrt(1-(i-=2)*i)+1),easeInElastic:i=>ae(i)?i:zi(i,.075,.3),easeOutElastic:i=>ae(i)?i:Bi(i,.075,.3),easeInOutElastic(i){return ae(i)?i:i<.5?.5*zi(i*2,.1125,.45):.5+.5*Bi(i*2-1,.1125,.45)},easeInBack(i){return i*i*((1.70158+1)*i-1.70158)},easeOutBack(i){return(i-=1)*i*((1.70158+1)*i+1.70158)+1},easeInOutBack(i){let t=1.70158;return(i/=.5)<1?.5*(i*i*(((t*=1.525)+1)*i-t)):.5*((i-=2)*i*(((t*=1.525)+1)*i+t)+2)},easeInBounce:i=>1-Nt.easeOutBounce(1-i),easeOutBounce(i){return i<1/2.75?7.5625*i*i:i<2/2.75?7.5625*(i-=1.5/2.75)*i+.75:i<2.5/2.75?7.5625*(i-=2.25/2.75)*i+.9375:7.5625*(i-=2.625/2.75)*i+.984375},easeInOutBounce:i=>i<.5?Nt.easeInBounce(i*2)*.5:Nt.easeOutBounce(i*2-1)*.5+.5};function je(i){if(i&&typeof i=="object"){const t=i.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Ni(i){return je(i)?i:new Ft(i)}function He(i){return je(i)?i:new Ft(i).saturate(.5).darken(.1).hexString()}const ho=["x","y","borderWidth","radius","tension"],fo=["color","borderColor","backgroundColor"];function uo(i){i.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),i.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),i.set("animations",{colors:{type:"color",properties:fo},numbers:{type:"number",properties:ho}}),i.describe("animations",{_fallback:"animation"}),i.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:t=>t|0}}}})}function go(i){i.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Vi=new Map;function po(i,t){t=t||{};const e=i+JSON.stringify(t);let s=Vi.get(e);return s||(s=new Intl.NumberFormat(i,t),Vi.set(e,s)),s}function ji(i,t,e){return po(t,e).format(i)}const mo={values(i){return z(i)?i:""+i},numeric(i,t,e){if(i===0)return"0";const s=this.chart.options.locale;let n,o=i;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(n="scientific"),o=bo(i,e)}const r=Pi(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:n,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),ji(i,s,l)}};function bo(i,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&i!==Math.floor(i)&&(e=i-Math.floor(i)),e}var Hi={formatters:mo};function _o(i){i.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width: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:Hi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),i.route("scale.ticks","color","","color"),i.route("scale.grid","color","","borderColor"),i.route("scale.border","color","","borderColor"),i.route("scale.title","color","","color"),i.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),i.describe("scales",{_fallback:"scale"}),i.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const bt=Object.create(null),We=Object.create(null);function Vt(i,t){if(!t)return i;const e=t.split(".");for(let s=0,n=e.length;ss.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=(s,n)=>He(n.backgroundColor),this.hoverBorderColor=(s,n)=>He(n.borderColor),this.hoverColor=(s,n)=>He(n.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(t),this.apply(e)}set(t,e){return $e(this,t,e)}get(t){return Vt(this,t)}describe(t,e){return $e(We,t,e)}override(t,e){return $e(bt,t,e)}route(t,e,s,n){const o=Vt(this,t),r=Vt(this,s),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[n];return D(l)?Object.assign({},c,l):O(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var R=new yo({_scriptable:i=>!i.startsWith("on"),_indexable:i=>i!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[uo,go,_o]);function xo(i){return!i||T(i.size)||T(i.family)?null:(i.style?i.style+" ":"")+(i.weight?i.weight+" ":"")+i.size+"px "+i.family}function Wi(i,t,e,s,n){let o=t[n];return o||(o=t[n]=i.measureText(n).width,e.push(n)),o>s&&(s=o),s}function _t(i,t,e){const s=i.currentDevicePixelRatio,n=e!==0?Math.max(e/2,.5):0;return Math.round((t-n)*s)/s+n}function $i(i,t){!t&&!i||(t=t||i.getContext("2d"),t.save(),t.resetTransform(),t.clearRect(0,0,i.width,i.height),t.restore())}function Ye(i,t,e,s){vo(i,t,e,s)}function vo(i,t,e,s,n){let o,r,a,l,c,h,f,d;const u=t.pointStyle,m=t.rotation,g=t.radius;let p=(m||0)*Xn;if(u&&typeof u=="object"&&(o=u.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){i.save(),i.translate(e,s),i.rotate(p),i.drawImage(u,-u.width/2,-u.height/2,u.width,u.height),i.restore();return}if(!(isNaN(g)||g<=0)){switch(i.beginPath(),u){default:i.arc(e,s,g,0,Z),i.closePath();break;case"triangle":h=g,i.moveTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Di,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Di,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),i.closePath();break;case"rectRounded":c=g*.516,l=g-c,r=Math.cos(p+gt)*l,f=Math.cos(p+gt)*l,a=Math.sin(p+gt)*l,d=Math.sin(p+gt)*l,i.arc(e-f,s-a,c,p-j,p-K),i.arc(e+d,s-r,c,p-K,p),i.arc(e+f,s+a,c,p,p+K),i.arc(e-d,s+r,c,p+K,p+j),i.closePath();break;case"rect":if(!m){l=Math.SQRT1_2*g,h=l,i.rect(e-h,s-l,2*h,2*l);break}p+=gt;case"rectRot":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+d,s-r),i.lineTo(e+f,s+a),i.lineTo(e-d,s+r),i.closePath();break;case"crossRot":p+=gt;case"cross":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r);break;case"star":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r),p+=gt,f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r);break;case"line":r=Math.cos(p)*g,a=Math.sin(p)*g,i.moveTo(e-r,s-a),i.lineTo(e+r,s+a);break;case"dash":i.moveTo(e,s),i.lineTo(e+Math.cos(p)*g,s+Math.sin(p)*g);break;case!1:i.closePath();break}i.fill(),t.borderWidth>0&&i.stroke()}}function jt(i,t,e){return e=e||.5,!t||i&&i.x>t.left-e&&i.xt.top-e&&i.y0&&o.strokeColor!=="";let l,c;for(i.save(),i.font=n.string,Mo(i,o),l=0;l+i||0;function Xi(i,t){const e={},s=D(t),n=s?Object.keys(t):t,o=D(i)?s?r=>O(i[r],i[t[r]]):r=>i[r]:()=>i;for(const r of n)e[r]=To(o(r));return e}function Ao(i){return Xi(i,{top:"y",right:"x",bottom:"y",left:"x"})}function le(i){return Xi(i,["topLeft","topRight","bottomLeft","bottomRight"])}function ht(i){const t=Ao(i);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function J(i,t){i=i||{},t=t||R.font;let e=O(i.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let s=O(i.style,t.style);s&&!(""+s).match(Co)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:O(i.family,t.family),lineHeight:Oo(O(i.lineHeight,t.lineHeight),e),size:e,style:s,weight:O(i.weight,t.weight),string:""};return n.string=xo(n),n}function ce(i,t,e,s){let n,o,r;for(n=0,o=i.length;ne&&a===0?0:a+l;return{min:r(s,-Math.abs(o)),max:r(n,o)}}function yt(i,t){return Object.assign(Object.create(i),t)}function Ke(i,t=[""],e,s,n=()=>i[0]){const o=e||i;typeof s>"u"&&(s=Qi("_fallback",i));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:i,_rootScopes:o,_fallback:s,_getTarget:n,override:a=>Ke([a,...i],t,o,s)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete i[0][l],!0},get(a,l){return qi(a,l,()=>Vo(l,t,i,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(i[0])},has(a,l){return Ji(a).includes(l)},ownKeys(a){return Ji(a)},set(a,l,c){const h=a._storage||(a._storage=n());return a[l]=h[l]=c,delete a._keys,!0}})}function Ot(i,t,e,s){const n={_cacheable:!1,_proxy:i,_context:t,_subProxy:e,_stack:new Set,_descriptors:Ki(i,s),setContext:o=>Ot(i,o,e,s),override:o=>Ot(i.override(o),t,e,s)};return new Proxy(n,{deleteProperty(o,r){return delete o[r],delete i[r],!0},get(o,r,a){return qi(o,r,()=>Fo(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(i,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(i,r)},getPrototypeOf(){return Reflect.getPrototypeOf(i)},has(o,r){return Reflect.has(i,r)},ownKeys(){return Reflect.ownKeys(i)},set(o,r,a){return i[r]=a,delete o[r],!0}})}function Ki(i,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:s=t.indexable,_allKeys:n=t.allKeys}=i;return{allKeys:n,scriptable:e,indexable:s,isScriptable:ct(e)?e:()=>e,isIndexable:ct(s)?s:()=>s}}const Lo=(i,t)=>i?i+Be(t):t,qe=(i,t)=>D(t)&&i!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function qi(i,t,e){if(Object.prototype.hasOwnProperty.call(i,t)||t==="constructor")return i[t];const s=e();return i[t]=s,s}function Fo(i,t,e){const{_proxy:s,_context:n,_subProxy:o,_descriptors:r}=i;let a=s[t];return ct(a)&&r.isScriptable(t)&&(a=Ro(t,a,i,e)),z(a)&&a.length&&(a=Eo(t,a,i,r.isIndexable)),qe(t,a)&&(a=Ot(a,n,o&&o[t],r)),a}function Ro(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_stack:a}=e;if(a.has(i))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+i);a.add(i);let l=t(o,r||s);return a.delete(i),qe(i,l)&&(l=Ge(n._scopes,n,i,l)),l}function Eo(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&s(i))return t[o.index%t.length];if(D(t[0])){const l=t,c=n._scopes.filter(h=>h!==l);t=[];for(const h of l){const f=Ge(c,n,i,h);t.push(Ot(f,o,r&&r[i],a))}}return t}function Gi(i,t,e){return ct(i)?i(t,e):i}const zo=(i,t)=>i===!0?t:typeof i=="string"?ne(t,i):void 0;function Bo(i,t,e,s,n){for(const o of t){const r=zo(e,o);if(r){i.add(r);const a=Gi(r._fallback,e,n);if(typeof a<"u"&&a!==e&&a!==s)return a}else if(r===!1&&typeof s<"u"&&e!==s)return null}return!1}function Ge(i,t,e,s){const n=t._rootScopes,o=Gi(t._fallback,e,s),r=[...i,...n],a=new Set;a.add(s);let l=Zi(a,r,e,o||e,s);return l===null||typeof o<"u"&&o!==e&&(l=Zi(a,r,o,l,s),l===null)?!1:Ke(Array.from(a),[""],n,o,()=>No(t,e,s))}function Zi(i,t,e,s,n){for(;e;)e=Bo(i,t,e,s,n);return e}function No(i,t,e){const s=i._getTarget();t in s||(s[t]={});const n=s[t];return z(n)&&D(e)?e:n||{}}function Vo(i,t,e,s){let n;for(const o of t)if(n=Qi(Lo(o,i),e),typeof n<"u")return qe(i,n)?Ge(e,s,i,n):n}function Qi(i,t){for(const e of t){if(!e)continue;const s=e[i];if(typeof s<"u")return s}}function Ji(i){let t=i._keys;return t||(t=i._keys=jo(i._scopes)),t}function jo(i){const t=new Set;for(const e of i)for(const s of Object.keys(e).filter(n=>!n.startsWith("_")))t.add(s);return Array.from(t)}const Ho=Number.EPSILON||1e-14,Tt=(i,t)=>ti==="x"?"y":"x";function Wo(i,t,e,s){const n=i.skip?t:i,o=t,r=e.skip?t:e,a=Ne(o,n),l=Ne(r,o);let c=a/(a+l),h=l/(a+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const f=s*c,d=s*h;return{previous:{x:o.x-f*(r.x-n.x),y:o.y-f*(r.y-n.y)},next:{x:o.x+d*(r.x-n.x),y:o.y+d*(r.y-n.y)}}}function $o(i,t,e){const s=i.length;let n,o,r,a,l,c=Tt(i,0);for(let h=0;h!c.skip)),t.cubicInterpolationMode==="monotone")Uo(i,n);else{let c=s?i[i.length-1]:i[0];for(o=0,r=i.length;oi.ownerDocument.defaultView.getComputedStyle(i,null);function qo(i,t){return de(i).getPropertyValue(t)}const Go=["top","right","bottom","left"];function xt(i,t,e){const s={};e=e?"-"+e:"";for(let n=0;n<4;n++){const o=Go[n];s[o]=parseFloat(i[t+"-"+o+e])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const Zo=(i,t,e)=>(i>0||t>0)&&(!e||!e.shadowRoot);function Qo(i,t){const e=i.touches,s=e&&e.length?e[0]:i,{offsetX:n,offsetY:o}=s;let r=!1,a,l;if(Zo(n,o,i.target))a=n,l=o;else{const c=t.getBoundingClientRect();a=s.clientX-c.left,l=s.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function vt(i,t){if("native"in i)return i;const{canvas:e,currentDevicePixelRatio:s}=t,n=de(e),o=n.boxSizing==="border-box",r=xt(n,"padding"),a=xt(n,"border","width"),{x:l,y:c,box:h}=Qo(i,e),f=r.left+(h&&a.left),d=r.top+(h&&a.top);let{width:u,height:m}=t;return o&&(u-=r.width+a.width,m-=r.height+a.height),{x:Math.round((l-f)/u*e.width/s),y:Math.round((c-d)/m*e.height/s)}}function Jo(i,t,e){let s,n;if(t===void 0||e===void 0){const o=i&&Qe(i);if(!o)t=i.clientWidth,e=i.clientHeight;else{const r=o.getBoundingClientRect(),a=de(o),l=xt(a,"border","width"),c=xt(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,s=fe(a.maxWidth,o,"clientWidth"),n=fe(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:s||re,maxHeight:n||re}}const ft=i=>Math.round(i*10)/10;function tr(i,t,e,s){const n=de(i),o=xt(n,"margin"),r=fe(n.maxWidth,i,"clientWidth")||re,a=fe(n.maxHeight,i,"clientHeight")||re,l=Jo(i,t,e);let{width:c,height:h}=l;if(n.boxSizing==="content-box"){const d=xt(n,"border","width"),u=xt(n,"padding");c-=u.width+d.width,h-=u.height+d.height}return c=Math.max(0,c-o.width),h=Math.max(0,s?c/s:h-o.height),c=ft(Math.min(c,r,l.maxWidth)),h=ft(Math.min(h,a,l.maxHeight)),c&&!h&&(h=ft(c/2)),(t!==void 0||e!==void 0)&&s&&l.height&&h>l.height&&(h=l.height,c=ft(Math.floor(h*s))),{width:c,height:h}}function es(i,t,e){const s=t||1,n=ft(i.height*s),o=ft(i.width*s);i.height=ft(i.height),i.width=ft(i.width);const r=i.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${i.height}px`,r.style.width=`${i.width}px`),i.currentDevicePixelRatio!==s||r.height!==n||r.width!==o?(i.currentDevicePixelRatio=s,r.height=n,r.width=o,i.ctx.setTransform(s,0,0,s,0,0),!0):!1}const er=(function(){let i=!1;try{const t={get passive(){return i=!0,!1}};Ze()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return i})();function is(i,t){const e=qo(i,t),s=e&&e.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function kt(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:i.y+e*(t.y-i.y)}}function ir(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:s==="middle"?e<.5?i.y:t.y:s==="after"?e<1?i.y:t.y:e>0?t.y:i.y}}function sr(i,t,e,s){const n={x:i.cp2x,y:i.cp2y},o={x:t.cp1x,y:t.cp1y},r=kt(i,n,e),a=kt(n,o,e),l=kt(o,t,e),c=kt(r,a,e),h=kt(a,l,e);return kt(c,h,e)}const nr=function(i,t){return{x(e){return i+i+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,s){return e-s},leftForLtr(e,s){return e-s}}},or=function(){return{x(i){return i},setWidth(i){},textAlign(i){return i},xPlus(i,t){return i+t},leftForLtr(i,t){return i}}};function Je(i,t,e){return i?nr(t,e):or()}function rr(i,t){let e,s;(t==="ltr"||t==="rtl")&&(e=i.canvas.style,s=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),i.prevTextDirection=s)}function ar(i,t){t!==void 0&&(delete i.prevTextDirection,i.canvas.style.setProperty("direction",t[0],t[1]))}function ss(i){return i==="angle"?{between:Ti,compare:to,normalize:Q}:{between:Ai,compare:(t,e)=>t-e,normalize:t=>t}}function ns({start:i,end:t,count:e,loop:s,style:n}){return{start:i%e,end:t%e,loop:s&&(t-i+1)%e===0,style:n}}function lr(i,t,e){const{property:s,start:n,end:o}=e,{between:r,normalize:a}=ss(s),l=t.length;let{start:c,end:h,loop:f}=i,d,u;if(f){for(c+=l,h+=l,d=0,u=l;dl(n,v,b)&&a(n,v)!==0,_=()=>a(o,b)===0||l(o,v,b),M=()=>g||x(),k=()=>!g||_();for(let w=h,P=h;w<=f;++w)y=t[w%r],!y.skip&&(b=c(y[s]),b!==v&&(g=l(b,n,o),p===null&&M()&&(p=a(b,n)===0?w:P),p!==null&&k()&&(m.push(ns({start:p,end:w,loop:d,count:r,style:u})),p=null),P=w,v=b));return p!==null&&m.push(ns({start:p,end:f,loop:d,count:r,style:u})),m}function rs(i,t){const e=[],s=i.segments;for(let n=0;nn&&i[o%t].skip;)o--;return o%=t,{start:n,end:o}}function hr(i,t,e,s){const n=i.length,o=[];let r=t,a=i[t],l;for(l=t+1;l<=e;++l){const c=i[l%n];c.skip||c.stop?a.skip||(s=!1,o.push({start:t%n,end:(l-1)%n,loop:s}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%n,end:r%n,loop:s}),o}function fr(i,t){const e=i.points,s=i.options.spanGaps,n=e.length;if(!n)return[];const o=!!i._loop,{start:r,end:a}=cr(e,n,o,s);if(s===!0)return as(i,[{start:r,end:a,loop:o}],e,t);const l=aa({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(s-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=Fi.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((s,n)=>{if(!s.running||!s.items.length)return;const o=s.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>s.duration&&(s.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(n.draw(),this._notify(n,s,t,"progress")),o.length||(s.running=!1,this._notify(n,s,t,"complete"),s.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let s=e.get(t);return s||(s={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,s)),s}listen(t,e,s){this._getAnims(t).listeners[e].push(s)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((s,n)=>Math.max(s,n._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const s=e.items;let n=s.length-1;for(;n>=0;--n)s[n].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var ot=new pr;const hs="transparent",mr={boolean(i,t,e){return e>.5?t:i},color(i,t,e){const s=Ni(i||hs),n=s.valid&&Ni(t||hs);return n&&n.valid?n.mix(s,e).hexString():t},number(i,t,e){return i+(t-i)*e}};class br{constructor(t,e,s,n){const o=e[s];n=ce([t.to,n,o,t.from]);const r=ce([t.from,o,n]);this._active=!0,this._fn=t.fn||mr[t.type||typeof r],this._easing=Nt[t.easing]||Nt.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=s,this._from=r,this._to=n,this._promises=void 0}active(){return this._active}update(t,e,s){if(this._active){this._notify(!1);const n=this._target[this._prop],o=s-this._start,r=this._duration-o;this._start=s,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=ce([t.to,e,n,t.from]),this._from=ce([t.from,n,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,s=this._duration,n=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[n]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,s)=>{t.push({res:e,rej:s})})}_notify(t){const e=t?"res":"rej",s=this._promises||[];for(let n=0;n{const o=t[n];if(!D(o))return;const r={};for(const a of e)r[a]=o[a];(z(o.properties)&&o.properties||[n]).forEach(a=>{(a===n||!s.has(a))&&s.set(a,r)})})}_animateOptions(t,e){const s=e.options,n=yr(t,s);if(!n)return[];const o=this._createAnimations(n,s);return s.$shared&&_r(t.options.$animations,s).then(()=>{t.options=s},()=>{}),o}_createAnimations(t,e){const s=this._properties,n=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){n.push(...this._animateOptions(t,e));continue}const h=e[c];let f=o[c];const d=s.get(c);if(f)if(d&&f.active()){f.update(d,h,a);continue}else f.cancel();if(!d||!d.duration){t[c]=h;continue}o[c]=f=new br(d,t,c,h),n.push(f)}return n}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const s=this._createAnimations(t,e);if(s.length)return ot.add(this._chart,s),!0}}function _r(i,t){const e=[],s=Object.keys(t);for(let n=0;n0||!e&&o<0)return n.index}return null}function ms(i,t){const{chart:e,_cachedMeta:s}=i,n=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=s,l=o.axis,c=r.axis,h=wr(o,r,s),f=t.length;let d;for(let u=0;ue[s].axis===t).shift()}function Dr(i,t){return yt(i,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function Pr(i,t,e){return yt(i,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Ht(i,t){const e=i.controller.index,s=i.vScale&&i.vScale.axis;if(s){t=t||i._parsed;for(const n of t){const o=n._stacks;if(!o||o[s]===void 0||o[s][e]===void 0)return;delete o[s][e],o[s]._visualValues!==void 0&&o[s]._visualValues[e]!==void 0&&delete o[s]._visualValues[e]}}}const ii=i=>i==="reset"||i==="none",bs=(i,t)=>t?i:Object.assign({},i),Or=(i,t,e)=>i&&!t.hidden&&t._stacked&&{keys:us(e,!0),values:null};class Wt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,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.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=ti(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ht(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,s=this.getDataset(),n=(f,d,u,m)=>f==="x"?d:f==="r"?m:u,o=e.xAxisID=C(s.xAxisID,ei(t,"x")),r=e.yAxisID=C(s.yAxisID,ei(t,"y")),a=e.rAxisID=C(s.rAxisID,ei(t,"r")),l=e.indexAxis,c=e.iAxisID=n(l,o,r,a),h=e.vAxisID=n(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Li(this._data,this),t._stacked&&Ht(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),s=this._data;if(D(e)){const n=this._cachedMeta;this._data=kr(e,n)}else if(s!==e){if(s){Li(s,this);const n=this._cachedMeta;Ht(n),n._parsed=[]}e&&Object.isExtensible(e)&&no(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,s=this.getDataset();let n=!1;this._dataCheck();const o=e._stacked;e._stacked=ti(e.vScale,e),e.stack!==s.stack&&(n=!0,Ht(e),e.stack=s.stack),this._resyncElements(t),(n||o!==e._stacked)&&(ms(this,e._parsed),e._stacked=ti(e.vScale,e))}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),s=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(s,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:s,_data:n}=this,{iScale:o,_stacked:r}=s,a=o.axis;let l=t===0&&e===n.length?!0:s._sorted,c=t>0&&s._parsed[t-1],h,f,d;if(this._parsing===!1)s._parsed=n,s._sorted=!0,d=n;else{z(n[t])?d=this.parseArrayData(s,n,t,e):D(n[t])?d=this.parseObjectData(s,n,t,e):d=this.parsePrimitiveData(s,n,t,e);const u=()=>f[a]===null||c&&f[a]g||f=0;--d)if(!m()){this.updateRangeFromParsed(c,t,u,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,s=[];let n,o,r;for(n=0,o=e.length;n=0&&tthis.getContext(s,n,e),g=c.resolveNamedOptions(d,u,m,f);return g.$shared&&(g.$shared=l,o[r]=Object.freeze(bs(g,l))),g}_resolveAnimations(t,e,s){const n=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(n.options.animation!==!1){const h=this.chart.config,f=h.datasetAnimationScopeKeys(this._type,e),d=h.getOptionScopes(this.getDataset(),f);l=h.createResolver(d,this.getContext(t,s,e))}const c=new fs(n,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ii(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const s=this.resolveDataElementOptions(t,e),n=this._sharedOptions,o=this.getSharedOptions(s),r=this.includeOptions(e,o)||o!==n;return this.updateSharedOptions(o,e,s),{sharedOptions:o,includeOptions:r}}updateElement(t,e,s,n){ii(n)?Object.assign(t,s):this._resolveAnimations(e,n).update(t,s)}updateSharedOptions(t,e,s){t&&!ii(e)&&this._resolveAnimations(void 0,e).update(t,s)}_setStyle(t,e,s,n){t.active=n;const o=this.getStyle(e,n);this._resolveAnimations(e,s,n).update(t,{options:!n&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,s){this._setStyle(t,s,"active",!1)}setHoverStyle(t,e,s){this._setStyle(t,s,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,s=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const n=s.length,o=e.length,r=Math.min(o,n);r&&this.parse(0,r),o>n?this._insertElements(n,o-n,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;a0&&this.getParsed(e-1);for(let _=0;_=y){k.skip=!0;continue}const w=this.getParsed(_),P=T(w[u]),I=k[d]=r.getPixelForValue(w[d],_),L=k[u]=o||P?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,w,l):w[u],_);k.skip=isNaN(I)||isNaN(L)||P,k.stop=_>0&&Math.abs(w[d]-x[d])>p,g&&(k.parsed=w,k.raw=c.data[_]),f&&(k.options=h||this.resolveDataElementOptions(_,M.active?"active":n)),b||this.updateElement(M,_,k,n),x=w}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,s=e.options&&e.options.borderWidth||0,n=t.data||[];if(!n.length)return s;const o=n[0].size(this.resolveDataElementOptions(0)),r=n[n.length-1].size(this.resolveDataElementOptions(n.length-1));return Math.max(s,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}S(ge,"id","line"),S(ge,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),S(ge,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});function wt(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class si{constructor(t){S(this,"options");this.options=t||{}}static override(t){Object.assign(si.prototype,t)}init(){}formats(){return wt()}parse(){return wt()}format(){return wt()}add(){return wt()}diff(){return wt()}startOf(){return wt()}endOf(){return wt()}}var Cr={_date:si};function Tr(i,t,e,s){const{controller:n,data:o,_sorted:r}=i,a=n._cachedMeta.iScale,l=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const c=a._reversePixels?io:mt;if(s){if(n._sharedOptions){const h=o[0],f=typeof h.getRange=="function"&&h.getRange(t);if(f){const d=c(o,t,e-f),u=c(o,t,e+f);return{lo:d.lo,hi:u.hi}}}}else{const h=c(o,t,e);if(l){const{vScale:f}=n._cachedMeta,{_parsed:d}=i,u=d.slice(0,h.lo+1).reverse().findIndex(g=>!T(g[f.axis]));h.lo-=Math.max(0,u);const m=d.slice(h.hi).findIndex(g=>!T(g[f.axis]));h.hi+=Math.max(0,m)}return h}}return{lo:0,hi:o.length-1}}function pe(i,t,e,s,n){const o=i.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r]&&l[r](t[e],n)&&(o.push({element:l,datasetIndex:c,index:h}),a=a||l.inRange(t.x,t.y,n))}),s&&!a?[]:o}var Fr={modes:{index(i,t,e,s){const n=vt(t,i),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?ni(i,n,o,s,r):oi(i,n,o,!1,s,r),l=[];return a.length?(i.getSortedVisibleDatasetMetas().forEach(c=>{const h=a[0].index,f=c.data[h];f&&!f.skip&&l.push({element:f,datasetIndex:c.index,index:h})}),l):[]},dataset(i,t,e,s){const n=vt(t,i),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?ni(i,n,o,s,r):oi(i,n,o,!1,s,r);if(a.length>0){const l=a[0].datasetIndex,c=i.getDatasetMeta(l).data;a=[];for(let h=0;he.pos===t)}function xs(i,t){return i.filter(e=>ys.indexOf(e.pos)===-1&&e.box.axis===t)}function Yt(i,t){return i.sort((e,s)=>{const n=t?s:e,o=t?e:s;return n.weight===o.weight?n.index-o.index:n.weight-o.weight})}function Rr(i){const t=[];let e,s,n,o,r,a;for(e=0,s=(i||[]).length;ec.box.fullSize),!0),s=Yt($t(t,"left"),!0),n=Yt($t(t,"right")),o=Yt($t(t,"top"),!0),r=Yt($t(t,"bottom")),a=xs(t,"x"),l=xs(t,"y");return{fullSize:e,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(r).concat(a),chartArea:$t(t,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(r).concat(a)}}function vs(i,t,e,s){return Math.max(i[e],t[e])+Math.max(i[s],t[s])}function ks(i,t){i.top=Math.max(i.top,t.top),i.left=Math.max(i.left,t.left),i.bottom=Math.max(i.bottom,t.bottom),i.right=Math.max(i.right,t.right)}function Nr(i,t,e,s){const{pos:n,box:o}=e,r=i.maxPadding;if(!D(n)){e.size&&(i[n]-=e.size);const f=s[e.stack]||{size:0,count:1};f.size=Math.max(f.size,e.horizontal?o.height:o.width),e.size=f.size/f.count,i[n]+=e.size}o.getPadding&&ks(r,o.getPadding());const a=Math.max(0,t.outerWidth-vs(r,i,"left","right")),l=Math.max(0,t.outerHeight-vs(r,i,"top","bottom")),c=a!==i.w,h=l!==i.h;return i.w=a,i.h=l,e.horizontal?{same:c,other:h}:{same:h,other:c}}function Vr(i){const t=i.maxPadding;function e(s){const n=Math.max(t[s]-i[s],0);return i[s]+=n,n}i.y+=e("top"),i.x+=e("left"),e("right"),e("bottom")}function jr(i,t){const e=t.maxPadding;function s(n){const o={left:0,top:0,right:0,bottom:0};return n.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return s(i?["left","right"]:["top","bottom"])}function Ut(i,t,e,s){const n=[];let o,r,a,l,c,h;for(o=0,r=i.length,c=0;o{typeof g.beforeLayout=="function"&&g.beforeLayout()});const h=l.reduce((g,p)=>p.box.options&&p.box.options.display===!1?g:g+1,0)||1,f=Object.freeze({outerWidth:t,outerHeight:e,padding:n,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/h,hBoxMaxHeight:r/2}),d=Object.assign({},n);ks(d,ht(s));const u=Object.assign({maxPadding:d,w:o,h:r,x:n.left,y:n.top},n),m=zr(l.concat(c),f);Ut(a.fullSize,u,f,m),Ut(l,u,f,m),Ut(c,u,f,m)&&Ut(l,u,f,m),Vr(u),ws(a.leftAndTop,u,f,m),u.x+=u.w,u.y+=u.h,ws(a.rightAndBottom,u,f,m),i.chartArea={left:u.left,top:u.top,right:u.left+u.w,bottom:u.top+u.h,height:u.h,width:u.w},O(a.chartArea,g=>{const p=g.box;Object.assign(p,i.chartArea),p.update(u.w,u.h,{left:0,top:0,right:0,bottom:0})})}};class Ms{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,s){}removeEventListener(t,e,s){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,s,n){return e=Math.max(0,e||t.width),s=s||t.height,{width:e,height:Math.max(0,n?Math.floor(e/n):s)}}isAttached(t){return!0}updateConfig(t){}}class Hr extends Ms{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const _e="$chartjs",Wr={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},Ss=i=>i===null||i==="";function $r(i,t){const e=i.style,s=i.getAttribute("height"),n=i.getAttribute("width");if(i[_e]={initial:{height:s,width:n,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",Ss(n)){const o=is(i,"width");o!==void 0&&(i.width=o)}if(Ss(s))if(i.style.height==="")i.height=i.width/(t||2);else{const o=is(i,"height");o!==void 0&&(i.height=o)}return i}const Ds=er?{passive:!0}:!1;function Yr(i,t,e){i&&i.addEventListener(t,e,Ds)}function Ur(i,t,e){i&&i.canvas&&i.canvas.removeEventListener(t,e,Ds)}function Xr(i,t){const e=Wr[i.type]||i.type,{x:s,y:n}=vt(i,t);return{type:e,chart:t,native:i,x:s!==void 0?s:null,y:n!==void 0?n:null}}function ye(i,t){for(const e of i)if(e===t||e.contains(t))return!0}function Kr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ye(a.addedNodes,s),r=r&&!ye(a.removedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}function qr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ye(a.removedNodes,s),r=r&&!ye(a.addedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}const Xt=new Map;let Ps=0;function Os(){const i=window.devicePixelRatio;i!==Ps&&(Ps=i,Xt.forEach((t,e)=>{e.currentDevicePixelRatio!==i&&t()}))}function Gr(i,t){Xt.size||window.addEventListener("resize",Os),Xt.set(i,t)}function Zr(i){Xt.delete(i),Xt.size||window.removeEventListener("resize",Os)}function Qr(i,t,e){const s=i.canvas,n=s&&Qe(s);if(!n)return;const o=Ri((a,l)=>{const c=n.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,h=l.contentRect.height;c===0&&h===0||o(c,h)});return r.observe(n),Gr(i,o),r}function ri(i,t,e){e&&e.disconnect(),t==="resize"&&Zr(i)}function Jr(i,t,e){const s=i.canvas,n=Ri(o=>{i.ctx!==null&&e(Xr(o,i))},i);return Yr(s,t,n),n}class ta extends Ms{acquireContext(t,e){const s=t&&t.getContext&&t.getContext("2d");return s&&s.canvas===t?($r(t,e),s):null}releaseContext(t){const e=t.canvas;if(!e[_e])return!1;const s=e[_e].initial;["height","width"].forEach(o=>{const r=s[o];T(r)?e.removeAttribute(o):e.setAttribute(o,r)});const n=s.style||{};return Object.keys(n).forEach(o=>{e.style[o]=n[o]}),e.width=e.width,delete e[_e],!0}addEventListener(t,e,s){this.removeEventListener(t,e);const n=t.$proxies||(t.$proxies={}),r={attach:Kr,detach:qr,resize:Qr}[e]||Jr;n[e]=r(t,e,s)}removeEventListener(t,e){const s=t.$proxies||(t.$proxies={}),n=s[e];if(!n)return;({attach:ri,detach:ri,resize:ri}[e]||Ur)(t,e,n),s[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,s,n){return tr(t,e,s,n)}isAttached(t){const e=t&&Qe(t);return!!(e&&e.isConnected)}}function ea(i){return!Ze()||typeof OffscreenCanvas<"u"&&i instanceof OffscreenCanvas?Hr:ta}class Mt{constructor(){S(this,"x");S(this,"y");S(this,"active",!1);S(this,"options");S(this,"$animations")}tooltipPosition(t){const{x:e,y:s}=this.getProps(["x","y"],t);return{x:e,y:s}}hasValue(){return Bt(this.x)&&Bt(this.y)}getProps(t,e){const s=this.$animations;if(!e||!s)return this;const n={};return t.forEach(o=>{n[o]=s[o]&&s[o].active()?s[o]._to:this[o]}),n}}S(Mt,"defaults",{}),S(Mt,"defaultRoutes");function ia(i,t){const e=i.options.ticks,s=sa(i),n=Math.min(e.maxTicksLimit||s,s),o=e.major.enabled?oa(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>n)return ra(t,c,o,r/n),c;const h=na(o,t,n);if(r>0){let f,d;const u=r>1?Math.round((l-a)/(r-1)):null;for(xe(t,c,h,T(u)?0:a-u,a),f=0,d=r-1;fn)return l}return Math.max(n,1)}function oa(i){const t=[];let e,s;for(e=0,s=i.length;ei==="left"?"right":i==="right"?"left":i,Cs=(i,t,e)=>t==="top"||t==="left"?i[t]+e:i[t]-e,Ts=(i,t)=>Math.min(t||i,i);function As(i,t){const e=[],s=i.length/t,n=i.length;let o=0;for(;or+a)))return l}function ha(i,t){O(i,e=>{const s=e.gc,n=s.length/2;let o;if(n>t){for(o=0;os?s:e,s=n&&e>s?e:s,{min:G(e,G(s,e)),max:G(s,G(e,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){A(this.options.beforeUpdate,[this])}update(t,e,s){const{beginAtZero:n,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=s=Object.assign({left:0,right:0,top:0,bottom:0},s),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+s.left+s.right:this.height+s.top+s.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Io(this,o,n),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||s<=1||!this.isHorizontal()){this.labelRotation=n;return}const h=this._getLabelSizes(),f=h.widest.width,d=h.highest.height,u=Y(this.chart.width-f,0,this.maxWidth);a=t.offset?this.maxWidth/s:u/(s-1),f+6>a&&(a=u/(s-(t.offset?.5:1)),l=this.maxHeight-Kt(t.grid)-e.padding-Is(t.title,this.chart.options.font),c=Math.sqrt(f*f+d*d),r=Qn(Math.min(Math.asin(Y((h.highest.height+6)/a,-1,1)),Math.asin(Y(l/c,-1,1))-Math.asin(Y(d/c,-1,1)))),r=Math.max(n,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){A(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){A(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:s,title:n,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=Is(n,e.options.font);if(a?(t.width=this.maxWidth,t.height=Kt(o)+l):(t.height=this.maxHeight,t.width=Kt(o)+l),s.display&&this.ticks.length){const{first:c,last:h,widest:f,highest:d}=this._getLabelSizes(),u=s.padding*2,m=pt(this.labelRotation),g=Math.cos(m),p=Math.sin(m);if(a){const b=s.mirror?0:p*f.width+g*d.height;t.height=Math.min(this.maxHeight,t.height+b+u)}else{const b=s.mirror?0:g*f.width+p*d.height;t.width=Math.min(this.maxWidth,t.width+b+u)}this._calculatePadding(c,h,p,g)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,s,n){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const h=this.getPixelForTick(0)-this.left,f=this.right-this.getPixelForTick(this.ticks.length-1);let d=0,u=0;l?c?(d=n*t.width,u=s*e.height):(d=s*t.height,u=n*e.width):o==="start"?u=e.width:o==="end"?d=t.width:o!=="inner"&&(d=t.width/2,u=e.width/2),this.paddingLeft=Math.max((d-h+r)*this.width/(this.width-h),0),this.paddingRight=Math.max((u-f+r)*this.width/(this.width-f),0)}else{let h=e.height/2,f=t.height/2;o==="start"?(h=0,f=t.height):o==="end"&&(h=e.height,f=0),this.paddingTop=h+r,this.paddingBottom=f+r}}_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(){A(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,s;for(e=0,s=t.length;e({width:r[P]||0,height:a[P]||0});return{first:w(0),last:w(e-1),widest:w(M),highest:w(k),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return eo(this._alignToPixels?_t(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*n?a/s:l/n:l*n0}_computeGridLineItems(t){const e=this.axis,s=this.chart,n=this.options,{grid:o,position:r,border:a}=n,l=o.offset,c=this.isHorizontal(),f=this.ticks.length+(l?1:0),d=Kt(o),u=[],m=a.setContext(this.getContext()),g=m.display?m.width:0,p=g/2,b=function(B){return _t(s,B,g)};let y,v,x,_,M,k,w,P,I,L,E,it;if(r==="top")y=b(this.bottom),k=this.bottom-d,P=y-p,L=b(t.top)+p,it=t.bottom;else if(r==="bottom")y=b(this.top),L=t.top,it=b(t.bottom)-p,k=y+p,P=this.top+d;else if(r==="left")y=b(this.right),M=this.right-d,w=y-p,I=b(t.left)+p,E=t.right;else if(r==="right")y=b(this.left),I=t.left,E=b(t.right)-p,M=y+p,w=this.left+d;else if(e==="x"){if(r==="center")y=b((t.top+t.bottom)/2+.5);else if(D(r)){const B=Object.keys(r)[0],U=r[B];y=b(this.chart.scales[B].getPixelForValue(U))}L=t.top,it=t.bottom,k=y+p,P=k+d}else if(e==="y"){if(r==="center")y=b((t.left+t.right)/2);else if(D(r)){const B=Object.keys(r)[0],U=r[B];y=b(this.chart.scales[B].getPixelForValue(U))}M=y-p,w=M-d,I=t.left,E=t.right}const ut=C(n.ticks.maxTicksLimit,f),F=Math.max(1,Math.ceil(f/ut));for(v=0;v0&&(Dt-=St/2);break}Ie={left:Dt,top:Qt,width:St+Lt.width,height:Zt+Lt.height,color:F.backdropColor}}p.push({label:x,font:P,textOffset:E,options:{rotation:g,color:U,strokeColor:Te,strokeWidth:Ae,textAlign:It,textBaseline:it,translation:[_,M],backdrop:Ie}})}return p}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-pt(this.labelRotation))return t==="top"?"left":"right";let n="center";return e.align==="start"?n="left":e.align==="end"?n="right":e.align==="inner"&&(n="inner"),n}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:s,mirror:n,padding:o}}=this.options,r=this._getLabelSizes(),a=t+o,l=r.widest.width;let c,h;return e==="left"?n?(h=this.right+o,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h+=l)):(h=this.right-a,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h=this.left)):e==="right"?n?(h=this.left+o,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h-=l)):(h=this.left+a,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h=this.right)):c="right",{textAlign:c,x:h}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:s,top:n,width:o,height:r}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(s,n,o,r),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const n=this.ticks.findIndex(o=>o.value===t);return n>=0?e.setContext(this.getContext(n)).lineWidth:0}drawGrid(t){const e=this.options.grid,s=this.ctx,n=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,h)=>{!h.width||!h.color||(s.save(),s.lineWidth=h.width,s.strokeStyle=h.color,s.setLineDash(h.borderDash||[]),s.lineDashOffset=h.borderDashOffset,s.beginPath(),s.moveTo(l.x,l.y),s.lineTo(c.x,c.y),s.stroke(),s.restore())};if(e.display)for(o=0,r=n.length;o{this.draw(o)}}]:[{z:s,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:n,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),s=this.axis+"AxisID",n=[];let o,r;for(o=0,r=e.length;o{const s=e.split("."),n=s.pop(),o=[i].concat(s).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");R.route(o,n,l,a)})}function ba(i){return"id"in i&&"defaults"in i}class _a{constructor(){this.controllers=new ve(Wt,"datasets",!0),this.elements=new ve(Mt,"elements"),this.plugins=new ve(Object,"plugins"),this.scales=new ve(At,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,s){[...e].forEach(n=>{const o=s||this._getRegistryForType(n);s||o.isForType(n)||o===this.plugins&&n.id?this._exec(t,o,n):O(n,r=>{const a=s||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,s){const n=Be(t);A(s["before"+n],[],s),e[t](s),A(s["after"+n],[],s)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(n(e,s),t,"stop"),this._notify(n(s,e),t,"start")}}function xa(i){const t={},e=[],s=Object.keys(tt.plugins.items);for(let o=0;o1&&Ls(i[0].toLowerCase());if(s)return s}throw new Error(`Cannot determine type of '${i}' axis. Please provide 'axis' or 'position' option.`)}function Fs(i,t,e){if(e[t+"AxisID"]===i)return{axis:t}}function Pa(i,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(s=>s.xAxisID===i||s.yAxisID===i);if(e.length)return Fs(i,"x",e[0])||Fs(i,"y",e[0])}return{}}function Oa(i,t){const e=bt[i.type]||{scales:{}},s=t.scales||{},n=ai(i.type,t),o=Object.create(null);return Object.keys(s).forEach(r=>{const a=s[r];if(!D(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=li(r,a,Pa(r,i),R.scales[a.type]),c=Sa(l,n),h=e.scales||{};o[r]=Et(Object.create(null),[{axis:l},a,h[l],h[c]])}),i.data.datasets.forEach(r=>{const a=r.type||i.type,l=r.indexAxis||ai(a,t),h=(bt[a]||{}).scales||{};Object.keys(h).forEach(f=>{const d=Ma(f,l),u=r[d+"AxisID"]||d;o[u]=o[u]||Object.create(null),Et(o[u],[{axis:d},s[u],h[f]])})}),Object.keys(o).forEach(r=>{const a=o[r];Et(a,[R.scales[a.type],R.scale])}),o}function Rs(i){const t=i.options||(i.options={});t.plugins=C(t.plugins,{}),t.scales=Oa(i,t)}function Es(i){return i=i||{},i.datasets=i.datasets||[],i.labels=i.labels||[],i}function Ca(i){return i=i||{},i.data=Es(i.data),Rs(i),i}const zs=new Map,Bs=new Set;function ke(i,t){let e=zs.get(i);return e||(e=t(),zs.set(i,e),Bs.add(e)),e}const qt=(i,t,e)=>{const s=ne(t,e);s!==void 0&&i.add(s)};class Ta{constructor(t){this._config=Ca(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Es(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Rs(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return ke(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return ke(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return ke(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,s=this.type;return ke(`${s}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const s=this._scopeCache;let n=s.get(t);return(!n||e)&&(n=new Map,s.set(t,n)),n}getOptionScopes(t,e,s){const{options:n,type:o}=this,r=this._cachedScopes(t,s),a=r.get(e);if(a)return a;const l=new Set;e.forEach(h=>{t&&(l.add(t),h.forEach(f=>qt(l,t,f))),h.forEach(f=>qt(l,n,f)),h.forEach(f=>qt(l,bt[o]||{},f)),h.forEach(f=>qt(l,R,f)),h.forEach(f=>qt(l,We,f))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),Bs.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,bt[e]||{},R.datasets[e]||{},{type:e},R,We]}resolveNamedOptions(t,e,s,n=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=Ns(this._resolverCache,t,n);let l=r;if(Ia(r,e)){o.$shared=!1,s=ct(s)?s():s;const c=this.createResolver(t,s,a);l=Ct(r,s,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,s=[""],n){const{resolver:o}=Ns(this._resolverCache,t,s);return D(e)?Ct(o,e,void 0,n):o}}function Ns(i,t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));const n=e.join();let o=s.get(n);return o||(o={resolver:Ke(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},s.set(n,o)),o}const Aa=i=>D(i)&&Object.getOwnPropertyNames(i).some(t=>ct(i[t]));function Ia(i,t){const{isScriptable:e,isIndexable:s}=Ki(i);for(const n of t){const o=e(n),r=s(n),a=(r||o)&&i[n];if(o&&(ct(a)||Aa(a))||r&&z(a))return!0}return!1}var La="4.5.1";const Fa=["top","bottom","left","right","chartArea"];function Vs(i,t){return i==="top"||i==="bottom"||Fa.indexOf(i)===-1&&t==="x"}function js(i,t){return function(e,s){return e[i]===s[i]?e[t]-s[t]:e[i]-s[i]}}function Hs(i){const t=i.chart,e=t.options.animation;t.notifyPlugins("afterRender"),A(e&&e.onComplete,[i],t)}function Ra(i){const t=i.chart,e=t.options.animation;A(e&&e.onProgress,[i],t)}function Ws(i){return Ze()&&typeof i=="string"?i=document.getElementById(i):i&&i.length&&(i=i[0]),i&&i.canvas&&(i=i.canvas),i}const we={},$s=i=>{const t=Ws(i);return Object.values(we).filter(e=>e.canvas===t).pop()};function Ea(i,t,e){const s=Object.keys(i);for(const n of s){const o=+n;if(o>=t){const r=i[n];delete i[n],(e>0||o>t)&&(i[o+e]=r)}}}function za(i,t,e,s){return!e||i.type==="mouseout"?null:s?t:i}class rt{static register(...t){tt.add(...t),Ys()}static unregister(...t){tt.remove(...t),Ys()}constructor(t,e){const s=this.config=new Ta(e),n=Ws(t),o=$s(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ea(n)),this.platform.updateConfig(s);const a=this.platform.acquireContext(n,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,h=l&&l.width;if(this.id=Nn(),this.ctx=a,this.canvas=l,this.width=h,this.height=c,this._options=r,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 ya,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=ro(f=>this.update(f),r.resizeDelay||0),this._dataChanges=[],we[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}ot.listen(this,"complete",Hs),ot.listen(this,"progress",Ra),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:s,height:n,_aspectRatio:o}=this;return T(t)?e&&o?o:n?s/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return tt}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():es(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return $i(this.canvas,this.ctx),this}stop(){return ot.stop(this),this}resize(t,e){ot.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const s=this.options,n=this.canvas,o=s.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(n,t,e,o),a=s.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,es(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),A(s.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};O(e,(s,n)=>{s.id=n})}buildOrUpdateScales(){const t=this.options,e=t.scales,s=this.scales,n=Object.keys(s).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=li(r,a),c=l==="r",h=l==="x";return{options:a,dposition:c?"chartArea":h?"bottom":"left",dtype:c?"radialLinear":h?"category":"linear"}}))),O(o,r=>{const a=r.options,l=a.id,c=li(l,a),h=C(a.type,r.dtype);(a.position===void 0||Vs(a.position,c)!==Vs(r.dposition))&&(a.position=r.dposition),n[l]=!0;let f=null;if(l in s&&s[l].type===h)f=s[l];else{const d=tt.getScale(h);f=new d({id:l,type:h,ctx:this.ctx,chart:this}),s[f.id]=f}f.init(a,t)}),O(n,(r,a)=>{r||delete s[a]}),O(s,r=>{be.configure(this,r,r.options),be.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,s=t.length;if(t.sort((n,o)=>n.index-o.index),s>e){for(let n=e;ne.length&&delete this._stacks,t.forEach((s,n)=>{e.filter(o=>o===s._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let s,n;for(this._removeUnreferencedMetasets(),s=0,n=e.length;s{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const s=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),n=this._animationsDisabled=!s.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,h=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(js("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){O(this.scales,t=>{be.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),s=new Set(t.events);(!Si(e,s)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:s,start:n,count:o}of e){const r=s==="_removeElements"?-o:o;Ea(t,n,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,s=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),n=s(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;be.update(this,this.width,this.height,t);const e=this.chartArea,s=e.width<=0||e.height<=0;this._layers=[],O(this.boxes,n=>{s&&n.position==="chartArea"||(n.configure&&n.configure(),this._layers.push(...n._layers()))},this),this._layers.forEach((n,o)=>{n._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,s=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,s={meta:t,index:t.index,cancelable:!0},n=cs(this,t);this.notifyPlugins("beforeDatasetDraw",s)!==!1&&(n&&Ue(e,n),t.controller.draw(),n&&Xe(e),s.cancelable=!1,this.notifyPlugins("afterDatasetDraw",s))}isPointInArea(t){return jt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,s,n){const o=Fr.modes[e];return typeof o=="function"?o(this,t,s,n):[]}getDatasetMeta(t){const e=this.data.datasets[t],s=this._metasets;let n=s.filter(o=>o&&o._dataset===e).pop();return n||(n={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},s.push(n)),n}getContext(){return this.$context||(this.$context=yt(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const s=this.getDatasetMeta(t);return typeof s.hidden=="boolean"?!s.hidden:!e.hidden}setDatasetVisibility(t,e){const s=this.getDatasetMeta(t);s.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,s){const n=s?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,n);oe(e)?(o.data[e].hidden=!s,this.update()):(this.setDatasetVisibility(t,s),r.update(o,{visible:s}),this.update(a=>a.datasetIndex===t?n:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),ot.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},n=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};O(this.options.events,o=>s(o,n))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,s=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},n=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{n("attach",a),this.attached=!0,this.resize(),s("resize",o),s("detach",r)};r=()=>{this.attached=!1,n("resize",o),this._stop(),this._resize(0,0),s("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){O(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},O(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,s){const n=s?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+n+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!ie(s,e)&&(this._active=s,this._lastEvent=null,this._updateHoverStyles(s,e))}notifyPlugins(t,e,s){return this._plugins.notify(this,t,e,s)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,s){const n=this.options.hover,o=(l,c)=>l.filter(h=>!c.some(f=>h.datasetIndex===f.datasetIndex&&h.index===f.index)),r=o(e,t),a=s?t:o(t,e);r.length&&this.updateHoverStyle(r,n.mode,!1),a.length&&n.mode&&this.updateHoverStyle(a,n.mode,!0)}_eventHandler(t,e){const s={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},n=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",s,n)===!1)return;const o=this._handleEvent(t,e,s.inChartArea);return s.cancelable=!1,this.notifyPlugins("afterEvent",s,n),(o||s.changed)&&this.render(),this}_handleEvent(t,e,s){const{_active:n=[],options:o}=this,r=e,a=this._getActiveElements(t,n,s,r),l=Yn(t),c=za(t,this._lastEvent,s,l);s&&(this._lastEvent=null,A(o.onHover,[t,a,this],this),l&&A(o.onClick,[t,a,this],this));const h=!ie(a,n);return(h||e)&&(this._active=a,this._updateHoverStyles(a,n,e)),this._lastEvent=c,h}_getActiveElements(t,e,s,n){if(t.type==="mouseout")return[];if(!s)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,n)}}S(rt,"defaults",R),S(rt,"instances",we),S(rt,"overrides",bt),S(rt,"registry",tt),S(rt,"version",La),S(rt,"getChart",$s);function Ys(){return O(rt.instances,i=>i._plugins.invalidate())}function Us(i,t,e=t){i.lineCap=C(e.borderCapStyle,t.borderCapStyle),i.setLineDash(C(e.borderDash,t.borderDash)),i.lineDashOffset=C(e.borderDashOffset,t.borderDashOffset),i.lineJoin=C(e.borderJoinStyle,t.borderJoinStyle),i.lineWidth=C(e.borderWidth,t.borderWidth),i.strokeStyle=C(e.borderColor,t.borderColor)}function Ba(i,t,e){i.lineTo(e.x,e.y)}function Na(i){return i.stepped?ko:i.tension||i.cubicInterpolationMode==="monotone"?wo:Ba}function Xs(i,t,e={}){const s=i.length,{start:n=0,end:o=s-1}=e,{start:r,end:a}=t,l=Math.max(n,r),c=Math.min(o,a),h=na&&o>a;return{count:s,start:l,loop:t.loop,ilen:c(r+(c?a-x:x))%o,v=()=>{g!==p&&(i.lineTo(h,p),i.lineTo(h,g),i.lineTo(h,b))};for(l&&(u=n[y(0)],i.moveTo(u.x,u.y)),d=0;d<=a;++d){if(u=n[y(d)],u.skip)continue;const x=u.x,_=u.y,M=x|0;M===m?(_p&&(p=_),h=(f*h+x)/++f):(v(),i.lineTo(x,_),m=M,f=0,g=p=_),b=_}v()}function ci(i){const t=i.options,e=t.borderDash&&t.borderDash.length;return!i._decimated&&!i._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?ja:Va}function Ha(i){return i.stepped?ir:i.tension||i.cubicInterpolationMode==="monotone"?sr:kt}function Wa(i,t,e,s){let n=t._path;n||(n=t._path=new Path2D,t.path(n,e,s)&&n.closePath()),Us(i,t.options),i.stroke(n)}function $a(i,t,e,s){const{segments:n,options:o}=t,r=ci(t);for(const a of n)Us(i,o,a.style),i.beginPath(),r(i,t,a,{start:e,end:e+s-1})&&i.closePath(),i.stroke()}const Ya=typeof Path2D=="function";function Ua(i,t,e,s){Ya&&!t.options.segment?Wa(i,t,e,s):$a(i,t,e,s)}class dt extends Mt{constructor(t){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,t&&Object.assign(this,t)}updateControlPoints(t,e){const s=this.options;if((s.tension||s.cubicInterpolationMode==="monotone")&&!s.stepped&&!this._pointsUpdated){const n=s.spanGaps?this._loop:this._fullLoop;Ko(this._points,s,t,n,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=fr(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,s=t.length;return s&&e[t[s-1].end]}interpolate(t,e){const s=this.options,n=t[e],o=this.points,r=rs(this,{property:e,start:n,end:n});if(!r.length)return;const a=[],l=Ha(s);let c,h;for(c=0,h=r.length;ct!=="borderDash"&&t!=="fill"});function Ks(i,t,e,s){const n=i.options,{[e]:o}=i.getProps([e],s);return Math.abs(t-o){a=Se(r,a,n);const l=n[r],c=n[a];s!==null?(o.push({x:l.x,y:s}),o.push({x:c.x,y:s})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function Se(i,t,e){for(;t>i;t--){const s=e[t];if(!isNaN(s.x)&&!isNaN(s.y))break}return t}function qs(i,t,e,s){return i&&t?s(i[e],t[e]):i?i[e]:t?t[e]:0}function Gs(i,t){let e=[],s=!1;return z(i)?(s=!0,e=i):e=Ka(i,t),e.length?new dt({points:e,options:{tension:0},_loop:s,_fullLoop:s}):null}function Zs(i){return i&&i.fill!==!1}function qa(i,t,e){let n=i[t].fill;const o=[t];let r;if(!e)return n;for(;n!==!1&&o.indexOf(n)===-1;){if(!V(n))return n;if(r=i[n],!r)return!1;if(r.visible)return n;o.push(n),n=r.fill}return!1}function Ga(i,t,e){const s=tl(i);if(D(s))return isNaN(s.value)?!1:s;let n=parseFloat(s);return V(n)&&Math.floor(n)===n?Za(s[0],t,n,e):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function Za(i,t,e,s){return(i==="-"||i==="+")&&(e=t+e),e===t||e<0||e>=s?!1:e}function Qa(i,t){let e=null;return i==="start"?e=t.bottom:i==="end"?e=t.top:D(i)?e=t.getPixelForValue(i.value):t.getBasePixel&&(e=t.getBasePixel()),e}function Ja(i,t,e){let s;return i==="start"?s=e:i==="end"?s=t.options.reverse?t.min:t.max:D(i)?s=i.value:s=t.getBaseValue(),s}function tl(i){const t=i.options,e=t.fill;let s=C(e&&e.target,e);return s===void 0&&(s=!!t.backgroundColor),s===!1||s===null?!1:s===!0?"origin":s}function el(i){const{scale:t,index:e,line:s}=i,n=[],o=s.segments,r=s.points,a=il(t,e);a.push(Gs({x:null,y:t.bottom},s));for(let l=0;l=0;--r){const a=n[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),s&&a.fill&&fi(i.ctx,a,o))}},beforeDatasetsDraw(i,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const s=i.getSortedVisibleDatasetMetas();for(let n=s.length-1;n>=0;--n){const o=s[n].$filler;Zs(o)&&fi(i.ctx,o,i.chartArea)}},beforeDatasetDraw(i,t,e){const s=t.meta.$filler;!Zs(s)||e.drawTime!=="beforeDatasetDraw"||fi(i.ctx,s,i.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Gt={average(i){if(!i.length)return!1;let t,e,s=new Set,n=0,o=0;for(t=0,e=i.length;ta+l)/s.size,y:n/o}},nearest(i,t){if(!i.length)return!1;let e=t.x,s=t.y,n=Number.POSITIVE_INFINITY,o,r,a;for(o=0,r=i.length;oa({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(s-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=Fi.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((s,n)=>{if(!s.running||!s.items.length)return;const o=s.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>s.duration&&(s.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(n.draw(),this._notify(n,s,t,"progress")),o.length||(s.running=!1,this._notify(n,s,t,"complete"),s.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let s=e.get(t);return s||(s={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,s)),s}listen(t,e,s){this._getAnims(t).listeners[e].push(s)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((s,n)=>Math.max(s,n._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const s=e.items;let n=s.length-1;for(;n>=0;--n)s[n].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var ot=new pr;const hs="transparent",mr={boolean(i,t,e){return e>.5?t:i},color(i,t,e){const s=Ni(i||hs),n=s.valid&&Ni(t||hs);return n&&n.valid?n.mix(s,e).hexString():t},number(i,t,e){return i+(t-i)*e}};class br{constructor(t,e,s,n){const o=e[s];n=ce([t.to,n,o,t.from]);const r=ce([t.from,o,n]);this._active=!0,this._fn=t.fn||mr[t.type||typeof r],this._easing=Nt[t.easing]||Nt.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=s,this._from=r,this._to=n,this._promises=void 0}active(){return this._active}update(t,e,s){if(this._active){this._notify(!1);const n=this._target[this._prop],o=s-this._start,r=this._duration-o;this._start=s,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=ce([t.to,e,n,t.from]),this._from=ce([t.from,n,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,s=this._duration,n=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[n]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,s)=>{t.push({res:e,rej:s})})}_notify(t){const e=t?"res":"rej",s=this._promises||[];for(let n=0;n{const o=t[n];if(!D(o))return;const r={};for(const a of e)r[a]=o[a];(z(o.properties)&&o.properties||[n]).forEach(a=>{(a===n||!s.has(a))&&s.set(a,r)})})}_animateOptions(t,e){const s=e.options,n=yr(t,s);if(!n)return[];const o=this._createAnimations(n,s);return s.$shared&&_r(t.options.$animations,s).then(()=>{t.options=s},()=>{}),o}_createAnimations(t,e){const s=this._properties,n=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){n.push(...this._animateOptions(t,e));continue}const h=e[c];let f=o[c];const d=s.get(c);if(f)if(d&&f.active()){f.update(d,h,a);continue}else f.cancel();if(!d||!d.duration){t[c]=h;continue}o[c]=f=new br(d,t,c,h),n.push(f)}return n}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const s=this._createAnimations(t,e);if(s.length)return ot.add(this._chart,s),!0}}function _r(i,t){const e=[],s=Object.keys(t);for(let n=0;n0||!e&&o<0)return n.index}return null}function ms(i,t){const{chart:e,_cachedMeta:s}=i,n=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=s,l=o.axis,c=r.axis,h=wr(o,r,s),f=t.length;let d;for(let u=0;ue[s].axis===t).shift()}function Dr(i,t){return yt(i,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function Pr(i,t,e){return yt(i,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Ht(i,t){const e=i.controller.index,s=i.vScale&&i.vScale.axis;if(s){t=t||i._parsed;for(const n of t){const o=n._stacks;if(!o||o[s]===void 0||o[s][e]===void 0)return;delete o[s][e],o[s]._visualValues!==void 0&&o[s]._visualValues[e]!==void 0&&delete o[s]._visualValues[e]}}}const ii=i=>i==="reset"||i==="none",bs=(i,t)=>t?i:Object.assign({},i),Cr=(i,t,e)=>i&&!t.hidden&&t._stacked&&{keys:us(e,!0),values:null};class Wt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,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.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=ti(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ht(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,s=this.getDataset(),n=(f,d,u,m)=>f==="x"?d:f==="r"?m:u,o=e.xAxisID=O(s.xAxisID,ei(t,"x")),r=e.yAxisID=O(s.yAxisID,ei(t,"y")),a=e.rAxisID=O(s.rAxisID,ei(t,"r")),l=e.indexAxis,c=e.iAxisID=n(l,o,r,a),h=e.vAxisID=n(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Li(this._data,this),t._stacked&&Ht(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),s=this._data;if(D(e)){const n=this._cachedMeta;this._data=kr(e,n)}else if(s!==e){if(s){Li(s,this);const n=this._cachedMeta;Ht(n),n._parsed=[]}e&&Object.isExtensible(e)&&no(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,s=this.getDataset();let n=!1;this._dataCheck();const o=e._stacked;e._stacked=ti(e.vScale,e),e.stack!==s.stack&&(n=!0,Ht(e),e.stack=s.stack),this._resyncElements(t),(n||o!==e._stacked)&&(ms(this,e._parsed),e._stacked=ti(e.vScale,e))}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),s=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(s,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:s,_data:n}=this,{iScale:o,_stacked:r}=s,a=o.axis;let l=t===0&&e===n.length?!0:s._sorted,c=t>0&&s._parsed[t-1],h,f,d;if(this._parsing===!1)s._parsed=n,s._sorted=!0,d=n;else{z(n[t])?d=this.parseArrayData(s,n,t,e):D(n[t])?d=this.parseObjectData(s,n,t,e):d=this.parsePrimitiveData(s,n,t,e);const u=()=>f[a]===null||c&&f[a]g||f=0;--d)if(!m()){this.updateRangeFromParsed(c,t,u,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,s=[];let n,o,r;for(n=0,o=e.length;n=0&&tthis.getContext(s,n,e),g=c.resolveNamedOptions(d,u,m,f);return g.$shared&&(g.$shared=l,o[r]=Object.freeze(bs(g,l))),g}_resolveAnimations(t,e,s){const n=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(n.options.animation!==!1){const h=this.chart.config,f=h.datasetAnimationScopeKeys(this._type,e),d=h.getOptionScopes(this.getDataset(),f);l=h.createResolver(d,this.getContext(t,s,e))}const c=new fs(n,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ii(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const s=this.resolveDataElementOptions(t,e),n=this._sharedOptions,o=this.getSharedOptions(s),r=this.includeOptions(e,o)||o!==n;return this.updateSharedOptions(o,e,s),{sharedOptions:o,includeOptions:r}}updateElement(t,e,s,n){ii(n)?Object.assign(t,s):this._resolveAnimations(e,n).update(t,s)}updateSharedOptions(t,e,s){t&&!ii(e)&&this._resolveAnimations(void 0,e).update(t,s)}_setStyle(t,e,s,n){t.active=n;const o=this.getStyle(e,n);this._resolveAnimations(e,s,n).update(t,{options:!n&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,s){this._setStyle(t,s,"active",!1)}setHoverStyle(t,e,s){this._setStyle(t,s,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,s=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const n=s.length,o=e.length,r=Math.min(o,n);r&&this.parse(0,r),o>n?this._insertElements(n,o-n,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;a0&&this.getParsed(e-1);for(let _=0;_=y){k.skip=!0;continue}const w=this.getParsed(_),P=T(w[u]),I=k[d]=r.getPixelForValue(w[d],_),L=k[u]=o||P?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,w,l):w[u],_);k.skip=isNaN(I)||isNaN(L)||P,k.stop=_>0&&Math.abs(w[d]-x[d])>p,g&&(k.parsed=w,k.raw=c.data[_]),f&&(k.options=h||this.resolveDataElementOptions(_,M.active?"active":n)),b||this.updateElement(M,_,k,n),x=w}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,s=e.options&&e.options.borderWidth||0,n=t.data||[];if(!n.length)return s;const o=n[0].size(this.resolveDataElementOptions(0)),r=n[n.length-1].size(this.resolveDataElementOptions(n.length-1));return Math.max(s,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}S(ge,"id","line"),S(ge,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),S(ge,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});function wt(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class si{constructor(t){S(this,"options");this.options=t||{}}static override(t){Object.assign(si.prototype,t)}init(){}formats(){return wt()}parse(){return wt()}format(){return wt()}add(){return wt()}diff(){return wt()}startOf(){return wt()}endOf(){return wt()}}var Or={_date:si};function Tr(i,t,e,s){const{controller:n,data:o,_sorted:r}=i,a=n._cachedMeta.iScale,l=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const c=a._reversePixels?io:mt;if(s){if(n._sharedOptions){const h=o[0],f=typeof h.getRange=="function"&&h.getRange(t);if(f){const d=c(o,t,e-f),u=c(o,t,e+f);return{lo:d.lo,hi:u.hi}}}}else{const h=c(o,t,e);if(l){const{vScale:f}=n._cachedMeta,{_parsed:d}=i,u=d.slice(0,h.lo+1).reverse().findIndex(g=>!T(g[f.axis]));h.lo-=Math.max(0,u);const m=d.slice(h.hi).findIndex(g=>!T(g[f.axis]));h.hi+=Math.max(0,m)}return h}}return{lo:0,hi:o.length-1}}function pe(i,t,e,s,n){const o=i.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r]&&l[r](t[e],n)&&(o.push({element:l,datasetIndex:c,index:h}),a=a||l.inRange(t.x,t.y,n))}),s&&!a?[]:o}var Fr={modes:{index(i,t,e,s){const n=vt(t,i),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?ni(i,n,o,s,r):oi(i,n,o,!1,s,r),l=[];return a.length?(i.getSortedVisibleDatasetMetas().forEach(c=>{const h=a[0].index,f=c.data[h];f&&!f.skip&&l.push({element:f,datasetIndex:c.index,index:h})}),l):[]},dataset(i,t,e,s){const n=vt(t,i),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?ni(i,n,o,s,r):oi(i,n,o,!1,s,r);if(a.length>0){const l=a[0].datasetIndex,c=i.getDatasetMeta(l).data;a=[];for(let h=0;he.pos===t)}function xs(i,t){return i.filter(e=>ys.indexOf(e.pos)===-1&&e.box.axis===t)}function Yt(i,t){return i.sort((e,s)=>{const n=t?s:e,o=t?e:s;return n.weight===o.weight?n.index-o.index:n.weight-o.weight})}function Rr(i){const t=[];let e,s,n,o,r,a;for(e=0,s=(i||[]).length;ec.box.fullSize),!0),s=Yt($t(t,"left"),!0),n=Yt($t(t,"right")),o=Yt($t(t,"top"),!0),r=Yt($t(t,"bottom")),a=xs(t,"x"),l=xs(t,"y");return{fullSize:e,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(r).concat(a),chartArea:$t(t,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(r).concat(a)}}function vs(i,t,e,s){return Math.max(i[e],t[e])+Math.max(i[s],t[s])}function ks(i,t){i.top=Math.max(i.top,t.top),i.left=Math.max(i.left,t.left),i.bottom=Math.max(i.bottom,t.bottom),i.right=Math.max(i.right,t.right)}function Nr(i,t,e,s){const{pos:n,box:o}=e,r=i.maxPadding;if(!D(n)){e.size&&(i[n]-=e.size);const f=s[e.stack]||{size:0,count:1};f.size=Math.max(f.size,e.horizontal?o.height:o.width),e.size=f.size/f.count,i[n]+=e.size}o.getPadding&&ks(r,o.getPadding());const a=Math.max(0,t.outerWidth-vs(r,i,"left","right")),l=Math.max(0,t.outerHeight-vs(r,i,"top","bottom")),c=a!==i.w,h=l!==i.h;return i.w=a,i.h=l,e.horizontal?{same:c,other:h}:{same:h,other:c}}function Vr(i){const t=i.maxPadding;function e(s){const n=Math.max(t[s]-i[s],0);return i[s]+=n,n}i.y+=e("top"),i.x+=e("left"),e("right"),e("bottom")}function jr(i,t){const e=t.maxPadding;function s(n){const o={left:0,top:0,right:0,bottom:0};return n.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return s(i?["left","right"]:["top","bottom"])}function Ut(i,t,e,s){const n=[];let o,r,a,l,c,h;for(o=0,r=i.length,c=0;o{typeof g.beforeLayout=="function"&&g.beforeLayout()});const h=l.reduce((g,p)=>p.box.options&&p.box.options.display===!1?g:g+1,0)||1,f=Object.freeze({outerWidth:t,outerHeight:e,padding:n,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/h,hBoxMaxHeight:r/2}),d=Object.assign({},n);ks(d,ht(s));const u=Object.assign({maxPadding:d,w:o,h:r,x:n.left,y:n.top},n),m=zr(l.concat(c),f);Ut(a.fullSize,u,f,m),Ut(l,u,f,m),Ut(c,u,f,m)&&Ut(l,u,f,m),Vr(u),ws(a.leftAndTop,u,f,m),u.x+=u.w,u.y+=u.h,ws(a.rightAndBottom,u,f,m),i.chartArea={left:u.left,top:u.top,right:u.left+u.w,bottom:u.top+u.h,height:u.h,width:u.w},C(a.chartArea,g=>{const p=g.box;Object.assign(p,i.chartArea),p.update(u.w,u.h,{left:0,top:0,right:0,bottom:0})})}};class Ms{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,s){}removeEventListener(t,e,s){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,s,n){return e=Math.max(0,e||t.width),s=s||t.height,{width:e,height:Math.max(0,n?Math.floor(e/n):s)}}isAttached(t){return!0}updateConfig(t){}}class Hr extends Ms{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const _e="$chartjs",Wr={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},Ss=i=>i===null||i==="";function $r(i,t){const e=i.style,s=i.getAttribute("height"),n=i.getAttribute("width");if(i[_e]={initial:{height:s,width:n,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",Ss(n)){const o=is(i,"width");o!==void 0&&(i.width=o)}if(Ss(s))if(i.style.height==="")i.height=i.width/(t||2);else{const o=is(i,"height");o!==void 0&&(i.height=o)}return i}const Ds=er?{passive:!0}:!1;function Yr(i,t,e){i&&i.addEventListener(t,e,Ds)}function Ur(i,t,e){i&&i.canvas&&i.canvas.removeEventListener(t,e,Ds)}function Xr(i,t){const e=Wr[i.type]||i.type,{x:s,y:n}=vt(i,t);return{type:e,chart:t,native:i,x:s!==void 0?s:null,y:n!==void 0?n:null}}function ye(i,t){for(const e of i)if(e===t||e.contains(t))return!0}function Kr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ye(a.addedNodes,s),r=r&&!ye(a.removedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}function qr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ye(a.removedNodes,s),r=r&&!ye(a.addedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}const Xt=new Map;let Ps=0;function Cs(){const i=window.devicePixelRatio;i!==Ps&&(Ps=i,Xt.forEach((t,e)=>{e.currentDevicePixelRatio!==i&&t()}))}function Gr(i,t){Xt.size||window.addEventListener("resize",Cs),Xt.set(i,t)}function Zr(i){Xt.delete(i),Xt.size||window.removeEventListener("resize",Cs)}function Qr(i,t,e){const s=i.canvas,n=s&&Qe(s);if(!n)return;const o=Ri((a,l)=>{const c=n.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,h=l.contentRect.height;c===0&&h===0||o(c,h)});return r.observe(n),Gr(i,o),r}function ri(i,t,e){e&&e.disconnect(),t==="resize"&&Zr(i)}function Jr(i,t,e){const s=i.canvas,n=Ri(o=>{i.ctx!==null&&e(Xr(o,i))},i);return Yr(s,t,n),n}class ta extends Ms{acquireContext(t,e){const s=t&&t.getContext&&t.getContext("2d");return s&&s.canvas===t?($r(t,e),s):null}releaseContext(t){const e=t.canvas;if(!e[_e])return!1;const s=e[_e].initial;["height","width"].forEach(o=>{const r=s[o];T(r)?e.removeAttribute(o):e.setAttribute(o,r)});const n=s.style||{};return Object.keys(n).forEach(o=>{e.style[o]=n[o]}),e.width=e.width,delete e[_e],!0}addEventListener(t,e,s){this.removeEventListener(t,e);const n=t.$proxies||(t.$proxies={}),r={attach:Kr,detach:qr,resize:Qr}[e]||Jr;n[e]=r(t,e,s)}removeEventListener(t,e){const s=t.$proxies||(t.$proxies={}),n=s[e];if(!n)return;({attach:ri,detach:ri,resize:ri}[e]||Ur)(t,e,n),s[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,s,n){return tr(t,e,s,n)}isAttached(t){const e=t&&Qe(t);return!!(e&&e.isConnected)}}function ea(i){return!Ze()||typeof OffscreenCanvas<"u"&&i instanceof OffscreenCanvas?Hr:ta}class Mt{constructor(){S(this,"x");S(this,"y");S(this,"active",!1);S(this,"options");S(this,"$animations")}tooltipPosition(t){const{x:e,y:s}=this.getProps(["x","y"],t);return{x:e,y:s}}hasValue(){return Bt(this.x)&&Bt(this.y)}getProps(t,e){const s=this.$animations;if(!e||!s)return this;const n={};return t.forEach(o=>{n[o]=s[o]&&s[o].active()?s[o]._to:this[o]}),n}}S(Mt,"defaults",{}),S(Mt,"defaultRoutes");function ia(i,t){const e=i.options.ticks,s=sa(i),n=Math.min(e.maxTicksLimit||s,s),o=e.major.enabled?oa(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>n)return ra(t,c,o,r/n),c;const h=na(o,t,n);if(r>0){let f,d;const u=r>1?Math.round((l-a)/(r-1)):null;for(xe(t,c,h,T(u)?0:a-u,a),f=0,d=r-1;fn)return l}return Math.max(n,1)}function oa(i){const t=[];let e,s;for(e=0,s=i.length;ei==="left"?"right":i==="right"?"left":i,Os=(i,t,e)=>t==="top"||t==="left"?i[t]+e:i[t]-e,Ts=(i,t)=>Math.min(t||i,i);function As(i,t){const e=[],s=i.length/t,n=i.length;let o=0;for(;or+a)))return l}function ha(i,t){C(i,e=>{const s=e.gc,n=s.length/2;let o;if(n>t){for(o=0;os?s:e,s=n&&e>s?e:s,{min:G(e,G(s,e)),max:G(s,G(e,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){A(this.options.beforeUpdate,[this])}update(t,e,s){const{beginAtZero:n,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=s=Object.assign({left:0,right:0,top:0,bottom:0},s),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+s.left+s.right:this.height+s.top+s.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Io(this,o,n),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||s<=1||!this.isHorizontal()){this.labelRotation=n;return}const h=this._getLabelSizes(),f=h.widest.width,d=h.highest.height,u=Y(this.chart.width-f,0,this.maxWidth);a=t.offset?this.maxWidth/s:u/(s-1),f+6>a&&(a=u/(s-(t.offset?.5:1)),l=this.maxHeight-Kt(t.grid)-e.padding-Is(t.title,this.chart.options.font),c=Math.sqrt(f*f+d*d),r=Qn(Math.min(Math.asin(Y((h.highest.height+6)/a,-1,1)),Math.asin(Y(l/c,-1,1))-Math.asin(Y(d/c,-1,1)))),r=Math.max(n,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){A(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){A(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:s,title:n,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=Is(n,e.options.font);if(a?(t.width=this.maxWidth,t.height=Kt(o)+l):(t.height=this.maxHeight,t.width=Kt(o)+l),s.display&&this.ticks.length){const{first:c,last:h,widest:f,highest:d}=this._getLabelSizes(),u=s.padding*2,m=pt(this.labelRotation),g=Math.cos(m),p=Math.sin(m);if(a){const b=s.mirror?0:p*f.width+g*d.height;t.height=Math.min(this.maxHeight,t.height+b+u)}else{const b=s.mirror?0:g*f.width+p*d.height;t.width=Math.min(this.maxWidth,t.width+b+u)}this._calculatePadding(c,h,p,g)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,s,n){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const h=this.getPixelForTick(0)-this.left,f=this.right-this.getPixelForTick(this.ticks.length-1);let d=0,u=0;l?c?(d=n*t.width,u=s*e.height):(d=s*t.height,u=n*e.width):o==="start"?u=e.width:o==="end"?d=t.width:o!=="inner"&&(d=t.width/2,u=e.width/2),this.paddingLeft=Math.max((d-h+r)*this.width/(this.width-h),0),this.paddingRight=Math.max((u-f+r)*this.width/(this.width-f),0)}else{let h=e.height/2,f=t.height/2;o==="start"?(h=0,f=t.height):o==="end"&&(h=e.height,f=0),this.paddingTop=h+r,this.paddingBottom=f+r}}_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(){A(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,s;for(e=0,s=t.length;e({width:r[P]||0,height:a[P]||0});return{first:w(0),last:w(e-1),widest:w(M),highest:w(k),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return eo(this._alignToPixels?_t(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*n?a/s:l/n:l*n0}_computeGridLineItems(t){const e=this.axis,s=this.chart,n=this.options,{grid:o,position:r,border:a}=n,l=o.offset,c=this.isHorizontal(),f=this.ticks.length+(l?1:0),d=Kt(o),u=[],m=a.setContext(this.getContext()),g=m.display?m.width:0,p=g/2,b=function(B){return _t(s,B,g)};let y,v,x,_,M,k,w,P,I,L,E,it;if(r==="top")y=b(this.bottom),k=this.bottom-d,P=y-p,L=b(t.top)+p,it=t.bottom;else if(r==="bottom")y=b(this.top),L=t.top,it=b(t.bottom)-p,k=y+p,P=this.top+d;else if(r==="left")y=b(this.right),M=this.right-d,w=y-p,I=b(t.left)+p,E=t.right;else if(r==="right")y=b(this.left),I=t.left,E=b(t.right)-p,M=y+p,w=this.left+d;else if(e==="x"){if(r==="center")y=b((t.top+t.bottom)/2+.5);else if(D(r)){const B=Object.keys(r)[0],U=r[B];y=b(this.chart.scales[B].getPixelForValue(U))}L=t.top,it=t.bottom,k=y+p,P=k+d}else if(e==="y"){if(r==="center")y=b((t.left+t.right)/2);else if(D(r)){const B=Object.keys(r)[0],U=r[B];y=b(this.chart.scales[B].getPixelForValue(U))}M=y-p,w=M-d,I=t.left,E=t.right}const ut=O(n.ticks.maxTicksLimit,f),F=Math.max(1,Math.ceil(f/ut));for(v=0;v0&&(Dt-=St/2);break}Ie={left:Dt,top:Qt,width:St+Lt.width,height:Zt+Lt.height,color:F.backdropColor}}p.push({label:x,font:P,textOffset:E,options:{rotation:g,color:U,strokeColor:Te,strokeWidth:Ae,textAlign:It,textBaseline:it,translation:[_,M],backdrop:Ie}})}return p}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-pt(this.labelRotation))return t==="top"?"left":"right";let n="center";return e.align==="start"?n="left":e.align==="end"?n="right":e.align==="inner"&&(n="inner"),n}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:s,mirror:n,padding:o}}=this.options,r=this._getLabelSizes(),a=t+o,l=r.widest.width;let c,h;return e==="left"?n?(h=this.right+o,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h+=l)):(h=this.right-a,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h=this.left)):e==="right"?n?(h=this.left+o,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h-=l)):(h=this.left+a,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h=this.right)):c="right",{textAlign:c,x:h}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:s,top:n,width:o,height:r}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(s,n,o,r),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const n=this.ticks.findIndex(o=>o.value===t);return n>=0?e.setContext(this.getContext(n)).lineWidth:0}drawGrid(t){const e=this.options.grid,s=this.ctx,n=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,h)=>{!h.width||!h.color||(s.save(),s.lineWidth=h.width,s.strokeStyle=h.color,s.setLineDash(h.borderDash||[]),s.lineDashOffset=h.borderDashOffset,s.beginPath(),s.moveTo(l.x,l.y),s.lineTo(c.x,c.y),s.stroke(),s.restore())};if(e.display)for(o=0,r=n.length;o{this.draw(o)}}]:[{z:s,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:n,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),s=this.axis+"AxisID",n=[];let o,r;for(o=0,r=e.length;o{const s=e.split("."),n=s.pop(),o=[i].concat(s).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");R.route(o,n,l,a)})}function ba(i){return"id"in i&&"defaults"in i}class _a{constructor(){this.controllers=new ve(Wt,"datasets",!0),this.elements=new ve(Mt,"elements"),this.plugins=new ve(Object,"plugins"),this.scales=new ve(At,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,s){[...e].forEach(n=>{const o=s||this._getRegistryForType(n);s||o.isForType(n)||o===this.plugins&&n.id?this._exec(t,o,n):C(n,r=>{const a=s||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,s){const n=Be(t);A(s["before"+n],[],s),e[t](s),A(s["after"+n],[],s)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(n(e,s),t,"stop"),this._notify(n(s,e),t,"start")}}function xa(i){const t={},e=[],s=Object.keys(tt.plugins.items);for(let o=0;o1&&Ls(i[0].toLowerCase());if(s)return s}throw new Error(`Cannot determine type of '${i}' axis. Please provide 'axis' or 'position' option.`)}function Fs(i,t,e){if(e[t+"AxisID"]===i)return{axis:t}}function Pa(i,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(s=>s.xAxisID===i||s.yAxisID===i);if(e.length)return Fs(i,"x",e[0])||Fs(i,"y",e[0])}return{}}function Ca(i,t){const e=bt[i.type]||{scales:{}},s=t.scales||{},n=ai(i.type,t),o=Object.create(null);return Object.keys(s).forEach(r=>{const a=s[r];if(!D(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=li(r,a,Pa(r,i),R.scales[a.type]),c=Sa(l,n),h=e.scales||{};o[r]=Et(Object.create(null),[{axis:l},a,h[l],h[c]])}),i.data.datasets.forEach(r=>{const a=r.type||i.type,l=r.indexAxis||ai(a,t),h=(bt[a]||{}).scales||{};Object.keys(h).forEach(f=>{const d=Ma(f,l),u=r[d+"AxisID"]||d;o[u]=o[u]||Object.create(null),Et(o[u],[{axis:d},s[u],h[f]])})}),Object.keys(o).forEach(r=>{const a=o[r];Et(a,[R.scales[a.type],R.scale])}),o}function Rs(i){const t=i.options||(i.options={});t.plugins=O(t.plugins,{}),t.scales=Ca(i,t)}function Es(i){return i=i||{},i.datasets=i.datasets||[],i.labels=i.labels||[],i}function Oa(i){return i=i||{},i.data=Es(i.data),Rs(i),i}const zs=new Map,Bs=new Set;function ke(i,t){let e=zs.get(i);return e||(e=t(),zs.set(i,e),Bs.add(e)),e}const qt=(i,t,e)=>{const s=ne(t,e);s!==void 0&&i.add(s)};class Ta{constructor(t){this._config=Oa(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Es(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Rs(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return ke(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return ke(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return ke(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,s=this.type;return ke(`${s}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const s=this._scopeCache;let n=s.get(t);return(!n||e)&&(n=new Map,s.set(t,n)),n}getOptionScopes(t,e,s){const{options:n,type:o}=this,r=this._cachedScopes(t,s),a=r.get(e);if(a)return a;const l=new Set;e.forEach(h=>{t&&(l.add(t),h.forEach(f=>qt(l,t,f))),h.forEach(f=>qt(l,n,f)),h.forEach(f=>qt(l,bt[o]||{},f)),h.forEach(f=>qt(l,R,f)),h.forEach(f=>qt(l,We,f))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),Bs.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,bt[e]||{},R.datasets[e]||{},{type:e},R,We]}resolveNamedOptions(t,e,s,n=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=Ns(this._resolverCache,t,n);let l=r;if(Ia(r,e)){o.$shared=!1,s=ct(s)?s():s;const c=this.createResolver(t,s,a);l=Ot(r,s,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,s=[""],n){const{resolver:o}=Ns(this._resolverCache,t,s);return D(e)?Ot(o,e,void 0,n):o}}function Ns(i,t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));const n=e.join();let o=s.get(n);return o||(o={resolver:Ke(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},s.set(n,o)),o}const Aa=i=>D(i)&&Object.getOwnPropertyNames(i).some(t=>ct(i[t]));function Ia(i,t){const{isScriptable:e,isIndexable:s}=Ki(i);for(const n of t){const o=e(n),r=s(n),a=(r||o)&&i[n];if(o&&(ct(a)||Aa(a))||r&&z(a))return!0}return!1}var La="4.5.1";const Fa=["top","bottom","left","right","chartArea"];function Vs(i,t){return i==="top"||i==="bottom"||Fa.indexOf(i)===-1&&t==="x"}function js(i,t){return function(e,s){return e[i]===s[i]?e[t]-s[t]:e[i]-s[i]}}function Hs(i){const t=i.chart,e=t.options.animation;t.notifyPlugins("afterRender"),A(e&&e.onComplete,[i],t)}function Ra(i){const t=i.chart,e=t.options.animation;A(e&&e.onProgress,[i],t)}function Ws(i){return Ze()&&typeof i=="string"?i=document.getElementById(i):i&&i.length&&(i=i[0]),i&&i.canvas&&(i=i.canvas),i}const we={},$s=i=>{const t=Ws(i);return Object.values(we).filter(e=>e.canvas===t).pop()};function Ea(i,t,e){const s=Object.keys(i);for(const n of s){const o=+n;if(o>=t){const r=i[n];delete i[n],(e>0||o>t)&&(i[o+e]=r)}}}function za(i,t,e,s){return!e||i.type==="mouseout"?null:s?t:i}class rt{static register(...t){tt.add(...t),Ys()}static unregister(...t){tt.remove(...t),Ys()}constructor(t,e){const s=this.config=new Ta(e),n=Ws(t),o=$s(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ea(n)),this.platform.updateConfig(s);const a=this.platform.acquireContext(n,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,h=l&&l.width;if(this.id=Nn(),this.ctx=a,this.canvas=l,this.width=h,this.height=c,this._options=r,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 ya,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=ro(f=>this.update(f),r.resizeDelay||0),this._dataChanges=[],we[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}ot.listen(this,"complete",Hs),ot.listen(this,"progress",Ra),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:s,height:n,_aspectRatio:o}=this;return T(t)?e&&o?o:n?s/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return tt}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():es(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return $i(this.canvas,this.ctx),this}stop(){return ot.stop(this),this}resize(t,e){ot.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const s=this.options,n=this.canvas,o=s.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(n,t,e,o),a=s.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,es(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),A(s.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};C(e,(s,n)=>{s.id=n})}buildOrUpdateScales(){const t=this.options,e=t.scales,s=this.scales,n=Object.keys(s).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=li(r,a),c=l==="r",h=l==="x";return{options:a,dposition:c?"chartArea":h?"bottom":"left",dtype:c?"radialLinear":h?"category":"linear"}}))),C(o,r=>{const a=r.options,l=a.id,c=li(l,a),h=O(a.type,r.dtype);(a.position===void 0||Vs(a.position,c)!==Vs(r.dposition))&&(a.position=r.dposition),n[l]=!0;let f=null;if(l in s&&s[l].type===h)f=s[l];else{const d=tt.getScale(h);f=new d({id:l,type:h,ctx:this.ctx,chart:this}),s[f.id]=f}f.init(a,t)}),C(n,(r,a)=>{r||delete s[a]}),C(s,r=>{be.configure(this,r,r.options),be.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,s=t.length;if(t.sort((n,o)=>n.index-o.index),s>e){for(let n=e;ne.length&&delete this._stacks,t.forEach((s,n)=>{e.filter(o=>o===s._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let s,n;for(this._removeUnreferencedMetasets(),s=0,n=e.length;s{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const s=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),n=this._animationsDisabled=!s.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,h=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(js("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){C(this.scales,t=>{be.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),s=new Set(t.events);(!Si(e,s)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:s,start:n,count:o}of e){const r=s==="_removeElements"?-o:o;Ea(t,n,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,s=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),n=s(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;be.update(this,this.width,this.height,t);const e=this.chartArea,s=e.width<=0||e.height<=0;this._layers=[],C(this.boxes,n=>{s&&n.position==="chartArea"||(n.configure&&n.configure(),this._layers.push(...n._layers()))},this),this._layers.forEach((n,o)=>{n._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,s=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,s={meta:t,index:t.index,cancelable:!0},n=cs(this,t);this.notifyPlugins("beforeDatasetDraw",s)!==!1&&(n&&Ue(e,n),t.controller.draw(),n&&Xe(e),s.cancelable=!1,this.notifyPlugins("afterDatasetDraw",s))}isPointInArea(t){return jt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,s,n){const o=Fr.modes[e];return typeof o=="function"?o(this,t,s,n):[]}getDatasetMeta(t){const e=this.data.datasets[t],s=this._metasets;let n=s.filter(o=>o&&o._dataset===e).pop();return n||(n={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},s.push(n)),n}getContext(){return this.$context||(this.$context=yt(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const s=this.getDatasetMeta(t);return typeof s.hidden=="boolean"?!s.hidden:!e.hidden}setDatasetVisibility(t,e){const s=this.getDatasetMeta(t);s.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,s){const n=s?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,n);oe(e)?(o.data[e].hidden=!s,this.update()):(this.setDatasetVisibility(t,s),r.update(o,{visible:s}),this.update(a=>a.datasetIndex===t?n:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),ot.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},n=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};C(this.options.events,o=>s(o,n))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,s=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},n=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{n("attach",a),this.attached=!0,this.resize(),s("resize",o),s("detach",r)};r=()=>{this.attached=!1,n("resize",o),this._stop(),this._resize(0,0),s("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){C(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},C(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,s){const n=s?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+n+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!ie(s,e)&&(this._active=s,this._lastEvent=null,this._updateHoverStyles(s,e))}notifyPlugins(t,e,s){return this._plugins.notify(this,t,e,s)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,s){const n=this.options.hover,o=(l,c)=>l.filter(h=>!c.some(f=>h.datasetIndex===f.datasetIndex&&h.index===f.index)),r=o(e,t),a=s?t:o(t,e);r.length&&this.updateHoverStyle(r,n.mode,!1),a.length&&n.mode&&this.updateHoverStyle(a,n.mode,!0)}_eventHandler(t,e){const s={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},n=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",s,n)===!1)return;const o=this._handleEvent(t,e,s.inChartArea);return s.cancelable=!1,this.notifyPlugins("afterEvent",s,n),(o||s.changed)&&this.render(),this}_handleEvent(t,e,s){const{_active:n=[],options:o}=this,r=e,a=this._getActiveElements(t,n,s,r),l=Yn(t),c=za(t,this._lastEvent,s,l);s&&(this._lastEvent=null,A(o.onHover,[t,a,this],this),l&&A(o.onClick,[t,a,this],this));const h=!ie(a,n);return(h||e)&&(this._active=a,this._updateHoverStyles(a,n,e)),this._lastEvent=c,h}_getActiveElements(t,e,s,n){if(t.type==="mouseout")return[];if(!s)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,n)}}S(rt,"defaults",R),S(rt,"instances",we),S(rt,"overrides",bt),S(rt,"registry",tt),S(rt,"version",La),S(rt,"getChart",$s);function Ys(){return C(rt.instances,i=>i._plugins.invalidate())}function Us(i,t,e=t){i.lineCap=O(e.borderCapStyle,t.borderCapStyle),i.setLineDash(O(e.borderDash,t.borderDash)),i.lineDashOffset=O(e.borderDashOffset,t.borderDashOffset),i.lineJoin=O(e.borderJoinStyle,t.borderJoinStyle),i.lineWidth=O(e.borderWidth,t.borderWidth),i.strokeStyle=O(e.borderColor,t.borderColor)}function Ba(i,t,e){i.lineTo(e.x,e.y)}function Na(i){return i.stepped?ko:i.tension||i.cubicInterpolationMode==="monotone"?wo:Ba}function Xs(i,t,e={}){const s=i.length,{start:n=0,end:o=s-1}=e,{start:r,end:a}=t,l=Math.max(n,r),c=Math.min(o,a),h=na&&o>a;return{count:s,start:l,loop:t.loop,ilen:c(r+(c?a-x:x))%o,v=()=>{g!==p&&(i.lineTo(h,p),i.lineTo(h,g),i.lineTo(h,b))};for(l&&(u=n[y(0)],i.moveTo(u.x,u.y)),d=0;d<=a;++d){if(u=n[y(d)],u.skip)continue;const x=u.x,_=u.y,M=x|0;M===m?(_p&&(p=_),h=(f*h+x)/++f):(v(),i.lineTo(x,_),m=M,f=0,g=p=_),b=_}v()}function ci(i){const t=i.options,e=t.borderDash&&t.borderDash.length;return!i._decimated&&!i._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?ja:Va}function Ha(i){return i.stepped?ir:i.tension||i.cubicInterpolationMode==="monotone"?sr:kt}function Wa(i,t,e,s){let n=t._path;n||(n=t._path=new Path2D,t.path(n,e,s)&&n.closePath()),Us(i,t.options),i.stroke(n)}function $a(i,t,e,s){const{segments:n,options:o}=t,r=ci(t);for(const a of n)Us(i,o,a.style),i.beginPath(),r(i,t,a,{start:e,end:e+s-1})&&i.closePath(),i.stroke()}const Ya=typeof Path2D=="function";function Ua(i,t,e,s){Ya&&!t.options.segment?Wa(i,t,e,s):$a(i,t,e,s)}class dt extends Mt{constructor(t){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,t&&Object.assign(this,t)}updateControlPoints(t,e){const s=this.options;if((s.tension||s.cubicInterpolationMode==="monotone")&&!s.stepped&&!this._pointsUpdated){const n=s.spanGaps?this._loop:this._fullLoop;Ko(this._points,s,t,n,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=fr(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,s=t.length;return s&&e[t[s-1].end]}interpolate(t,e){const s=this.options,n=t[e],o=this.points,r=rs(this,{property:e,start:n,end:n});if(!r.length)return;const a=[],l=Ha(s);let c,h;for(c=0,h=r.length;ct!=="borderDash"&&t!=="fill"});function Ks(i,t,e,s){const n=i.options,{[e]:o}=i.getProps([e],s);return Math.abs(t-o){a=Se(r,a,n);const l=n[r],c=n[a];s!==null?(o.push({x:l.x,y:s}),o.push({x:c.x,y:s})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function Se(i,t,e){for(;t>i;t--){const s=e[t];if(!isNaN(s.x)&&!isNaN(s.y))break}return t}function qs(i,t,e,s){return i&&t?s(i[e],t[e]):i?i[e]:t?t[e]:0}function Gs(i,t){let e=[],s=!1;return z(i)?(s=!0,e=i):e=Ka(i,t),e.length?new dt({points:e,options:{tension:0},_loop:s,_fullLoop:s}):null}function Zs(i){return i&&i.fill!==!1}function qa(i,t,e){let n=i[t].fill;const o=[t];let r;if(!e)return n;for(;n!==!1&&o.indexOf(n)===-1;){if(!V(n))return n;if(r=i[n],!r)return!1;if(r.visible)return n;o.push(n),n=r.fill}return!1}function Ga(i,t,e){const s=tl(i);if(D(s))return isNaN(s.value)?!1:s;let n=parseFloat(s);return V(n)&&Math.floor(n)===n?Za(s[0],t,n,e):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function Za(i,t,e,s){return(i==="-"||i==="+")&&(e=t+e),e===t||e<0||e>=s?!1:e}function Qa(i,t){let e=null;return i==="start"?e=t.bottom:i==="end"?e=t.top:D(i)?e=t.getPixelForValue(i.value):t.getBasePixel&&(e=t.getBasePixel()),e}function Ja(i,t,e){let s;return i==="start"?s=e:i==="end"?s=t.options.reverse?t.min:t.max:D(i)?s=i.value:s=t.getBaseValue(),s}function tl(i){const t=i.options,e=t.fill;let s=O(e&&e.target,e);return s===void 0&&(s=!!t.backgroundColor),s===!1||s===null?!1:s===!0?"origin":s}function el(i){const{scale:t,index:e,line:s}=i,n=[],o=s.segments,r=s.points,a=il(t,e);a.push(Gs({x:null,y:t.bottom},s));for(let l=0;l=0;--r){const a=n[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),s&&a.fill&&fi(i.ctx,a,o))}},beforeDatasetsDraw(i,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const s=i.getSortedVisibleDatasetMetas();for(let n=s.length-1;n>=0;--n){const o=s[n].$filler;Zs(o)&&fi(i.ctx,o,i.chartArea)}},beforeDatasetDraw(i,t,e){const s=t.meta.$filler;!Zs(s)||e.drawTime!=="beforeDatasetDraw"||fi(i.ctx,s,i.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Gt={average(i){if(!i.length)return!1;let t,e,s=new Set,n=0,o=0;for(t=0,e=i.length;ta+l)/s.size,y:n/o}},nearest(i,t){if(!i.length)return!1;let e=t.x,s=t.y,n=Number.POSITIVE_INFINITY,o,r,a;for(o=0,r=i.length;o-1?i.split(` -`):i}function ul(i,t){const{element:e,datasetIndex:s,index:n}=t,o=i.getDatasetMeta(s).controller,{label:r,value:a}=o.getLabelAndValue(n);return{chart:i,label:r,parsed:o.getParsed(n),raw:i.data.datasets[s].data[n],formattedValue:a,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:e}}function sn(i,t){const e=i.chart.ctx,{body:s,footer:n,title:o}=i,{boxWidth:r,boxHeight:a}=t,l=J(t.bodyFont),c=J(t.titleFont),h=J(t.footerFont),f=o.length,d=n.length,u=s.length,m=ht(t.padding);let g=m.height,p=0,b=s.reduce((x,_)=>x+_.before.length+_.lines.length+_.after.length,0);if(b+=i.beforeBody.length+i.afterBody.length,f&&(g+=f*c.lineHeight+(f-1)*t.titleSpacing+t.titleMarginBottom),b){const x=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;g+=u*x+(b-u)*l.lineHeight+(b-1)*t.bodySpacing}d&&(g+=t.footerMarginTop+d*h.lineHeight+(d-1)*t.footerSpacing);let y=0;const v=function(x){p=Math.max(p,e.measureText(x).width+y)};return e.save(),e.font=c.string,O(i.title,v),e.font=l.string,O(i.beforeBody.concat(i.afterBody),v),y=t.displayColors?r+2+t.boxPadding:0,O(s,x=>{O(x.before,v),O(x.lines,v),O(x.after,v)}),y=0,e.font=h.string,O(i.footer,v),e.restore(),p+=m.width,{width:p,height:g}}function gl(i,t){const{y:e,height:s}=t;return ei.height-s/2?"bottom":"center"}function pl(i,t,e,s){const{x:n,width:o}=s,r=e.caretSize+e.caretPadding;if(i==="left"&&n+o+r>t.width||i==="right"&&n-o-r<0)return!0}function ml(i,t,e,s){const{x:n,width:o}=e,{width:r,chartArea:{left:a,right:l}}=i;let c="center";return s==="center"?c=n<=(a+l)/2?"left":"right":n<=o/2?c="left":n>=r-o/2&&(c="right"),pl(c,i,t,e)&&(c="center"),c}function nn(i,t,e){const s=e.yAlign||t.yAlign||gl(i,e);return{xAlign:e.xAlign||t.xAlign||ml(i,t,e,s),yAlign:s}}function bl(i,t){let{x:e,width:s}=i;return t==="right"?e-=s:t==="center"&&(e-=s/2),e}function _l(i,t,e){let{y:s,height:n}=i;return t==="top"?s+=e:t==="bottom"?s-=n+e:s-=n/2,s}function on(i,t,e,s){const{caretSize:n,caretPadding:o,cornerRadius:r}=i,{xAlign:a,yAlign:l}=e,c=n+o,{topLeft:h,topRight:f,bottomLeft:d,bottomRight:u}=le(r);let m=bl(t,a);const g=_l(t,l,c);return l==="center"?a==="left"?m+=c:a==="right"&&(m-=c):a==="left"?m-=Math.max(h,d)+n:a==="right"&&(m+=Math.max(f,u)+n),{x:Y(m,0,s.width-t.width),y:Y(g,0,s.height-t.height)}}function De(i,t,e){const s=ht(e.padding);return t==="center"?i.x+i.width/2:t==="right"?i.x+i.width-s.right:i.x+s.left}function rn(i){return et([],at(i))}function yl(i,t,e){return yt(i,{tooltip:t,tooltipItems:e,type:"tooltip"})}function an(i,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?i.override(e):i}const ln={beforeTitle:nt,title(i){if(i.length>0){const t=i[0],e=t.chart.data.labels,s=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(s>0&&t.dataIndex"u"?ln[t].call(e,s):n}class ui extends Mt{constructor(t){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=t.chart,this.options=t.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(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,s=this.options.setContext(this.getContext()),n=s.enabled&&e.options.animation&&s.animations,o=new fs(this.chart,n);return n._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=yl(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:s}=e,n=H(s,"beforeTitle",this,t),o=H(s,"title",this,t),r=H(s,"afterTitle",this,t);let a=[];return a=et(a,at(n)),a=et(a,at(o)),a=et(a,at(r)),a}getBeforeBody(t,e){return rn(H(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:s}=e,n=[];return O(t,o=>{const r={before:[],lines:[],after:[]},a=an(s,o);et(r.before,at(H(a,"beforeLabel",this,o))),et(r.lines,H(a,"label",this,o)),et(r.after,at(H(a,"afterLabel",this,o))),n.push(r)}),n}getAfterBody(t,e){return rn(H(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:s}=e,n=H(s,"beforeFooter",this,t),o=H(s,"footer",this,t),r=H(s,"afterFooter",this,t);let a=[];return a=et(a,at(n)),a=et(a,at(o)),a=et(a,at(r)),a}_createItems(t){const e=this._active,s=this.chart.data,n=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(h,f,d,s))),t.itemSort&&(a=a.sort((h,f)=>t.itemSort(h,f,s))),O(a,h=>{const f=an(t.callbacks,h);n.push(H(f,"labelColor",this,h)),o.push(H(f,"labelPointStyle",this,h)),r.push(H(f,"labelTextColor",this,h))}),this.labelColors=n,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const s=this.options.setContext(this.getContext()),n=this._active;let o,r=[];if(!n.length)this.opacity!==0&&(o={opacity:0});else{const a=Gt[s.position].call(this,n,this._eventPosition);r=this._createItems(s),this.title=this.getTitle(r,s),this.beforeBody=this.getBeforeBody(r,s),this.body=this.getBody(r,s),this.afterBody=this.getAfterBody(r,s),this.footer=this.getFooter(r,s);const l=this._size=sn(this,s),c=Object.assign({},a,l),h=nn(this.chart,s,c),f=on(s,c,h,this.chart);this.xAlign=h.xAlign,this.yAlign=h.yAlign,o={opacity:1,x:f.x,y:f.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&s.external&&s.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,s,n){const o=this.getCaretPosition(t,s,n);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,s){const{xAlign:n,yAlign:o}=this,{caretSize:r,cornerRadius:a}=s,{topLeft:l,topRight:c,bottomLeft:h,bottomRight:f}=le(a),{x:d,y:u}=t,{width:m,height:g}=e;let p,b,y,v,x,_;return o==="center"?(x=u+g/2,n==="left"?(p=d,b=p-r,v=x+r,_=x-r):(p=d+m,b=p+r,v=x-r,_=x+r),y=p):(n==="left"?b=d+Math.max(l,h)+r:n==="right"?b=d+m-Math.max(c,f)-r:b=this.caretX,o==="top"?(v=u,x=v-r,p=b-r,y=b+r):(v=u+g,x=v+r,p=b+r,y=b-r),_=v),{x1:p,x2:b,x3:y,y1:v,y2:x,y3:_}}drawTitle(t,e,s){const n=this.title,o=n.length;let r,a,l;if(o){const c=Je(s.rtl,this.x,this.width);for(t.x=De(this,s.titleAlign,s),e.textAlign=c.textAlign(s.titleAlign),e.textBaseline="middle",r=J(s.titleFont),a=s.titleSpacing,e.fillStyle=s.titleColor,e.font=r.string,l=0;ly!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,Ui(t,{x:g,y:m,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),Ui(t,{x:p,y:m+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(g,m,c,l),t.strokeRect(g,m,c,l),t.fillStyle=r.backgroundColor,t.fillRect(p,m+1,c-2,l-2))}t.fillStyle=this.labelTextColors[s]}drawBody(t,e,s){const{body:n}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:h}=s,f=J(s.bodyFont);let d=f.lineHeight,u=0;const m=Je(s.rtl,this.x,this.width),g=function(w){e.fillText(w,m.x(t.x+u),t.y+d/2),t.y+=d+o},p=m.textAlign(r);let b,y,v,x,_,M,k;for(e.textAlign=r,e.textBaseline="middle",e.font=f.string,t.x=De(this,p,s),e.fillStyle=s.bodyColor,O(this.beforeBody,g),u=a&&p!=="right"?r==="center"?c/2+h:c+2+h:0,x=0,M=n.length;x0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,s=this.$animations,n=s&&s.x,o=s&&s.y;if(n||o){const r=Gt[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=sn(this,t),l=Object.assign({},r,this._size),c=nn(e,t,l),h=on(t,l,c,e);(n._to!==h.x||o._to!==h.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,h))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let s=this.opacity;if(!s)return;this._updateAnimationTarget(e);const n={width:this.width,height:this.height},o={x:this.x,y:this.y};s=Math.abs(s)<.001?0:s;const r=ht(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=s,this.drawBackground(o,t,n,e),rr(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),ar(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const s=this._active,n=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!ie(s,n),r=this._positionChanged(n,e);(o||r)&&(this._active=n,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,s=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const n=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,s),a=this._positionChanged(r,t),l=e||!ie(r,o)||a;return l&&(this._active=r,(n.enabled||n.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,s,n){const o=this.options;if(t.type==="mouseout")return[];if(!n)return e.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const r=this.chart.getElementsAtEventForMode(t,o.mode,o,s);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:s,caretY:n,options:o}=this,r=Gt[o.position].call(this,t,e);return r!==!1&&(s!==r.x||n!==r.y)}}S(ui,"positioners",Gt);var xl={id:"tooltip",_element:ui,positioners:Gt,afterInit(i,t,e){e&&(i.tooltip=new ui({chart:i,options:e}))},beforeUpdate(i,t,e){i.tooltip&&i.tooltip.initialize(e)},reset(i,t,e){i.tooltip&&i.tooltip.initialize(e)},afterDraw(i){const t=i.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(i.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(i.ctx),i.notifyPlugins("afterTooltipDraw",e)}},afterEvent(i,t){if(i.tooltip){const e=t.replay;i.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.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:(i,t)=>t.bodyFont.size,boxWidth:(i,t)=>t.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:ln},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:i=>i!=="filter"&&i!=="itemSort"&&i!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const vl=(i,t,e,s)=>(typeof t=="string"?(e=i.push(t)-1,s.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function kl(i,t,e,s){const n=i.indexOf(t);if(n===-1)return vl(i,t,e,s);const o=i.lastIndexOf(t);return n!==o?e:n}const wl=(i,t)=>i===null?null:Y(Math.round(i),0,t);function cn(i){const t=this.getLabels();return i>=0&&ie.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}S(gi,"id","category"),S(gi,"defaults",{ticks:{callback:cn}});function Ml(i,t){const e=[],{bounds:n,step:o,min:r,max:a,precision:l,count:c,maxTicks:h,maxDigits:f,includeBounds:d}=i,u=o||1,m=h-1,{min:g,max:p}=t,b=!T(r),y=!T(a),v=!T(c),x=(p-g)/(f+1);let _=Oi((p-g)/m/u)*u,M,k,w,P;if(_<1e-14&&!b&&!y)return[{value:g},{value:p}];P=Math.ceil(p/_)-Math.floor(g/_),P>m&&(_=Oi(P*_/m/u)*u),T(l)||(M=Math.pow(10,l),_=Math.ceil(_*M)/M),n==="ticks"?(k=Math.floor(g/_)*_,w=Math.ceil(p/_)*_):(k=g,w=p),b&&y&&o&&Gn((a-r)/o,_/1e3)?(P=Math.round(Math.min((a-r)/_,h)),_=(a-r)/P,k=r,w=a):v?(k=b?r:k,w=y?a:w,P=c-1,_=(w-k)/P):(P=(w-k)/_,zt(P,Math.round(P),_/1e3)?P=Math.round(P):P=Math.ceil(P));const I=Math.max(Ci(_),Ci(k));M=Math.pow(10,T(l)?I:l),k=Math.round(k*M)/M,w=Math.round(w*M)/M;let L=0;for(b&&(d&&k!==r?(e.push({value:r}),ka)break;e.push({value:E})}return y&&d&&w!==a?e.length&&zt(e[e.length-1].value,a,hn(a,x,i))?e[e.length-1].value=a:e.push({value:a}):(!y||w===a)&&e.push({value:w}),e}function hn(i,t,{horizontal:e,minRotation:s}){const n=pt(s),o=(e?Math.sin(n):Math.cos(n))||.001,r=.75*t*(""+i).length;return Math.min(t/o,r)}class Sl extends At{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return T(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:s}=this.getUserBounds();let{min:n,max:o}=this;const r=l=>n=e?n:l,a=l=>o=s?o:l;if(t){const l=Ot(n),c=Ot(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(n===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(n-l)}this.min=n,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:s}=t,n;return s?(n=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let s=this.getTickLimit();s=Math.max(2,s);const n={maxTicks:s,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=Ml(n,o);return t.bounds==="ticks"&&Zn(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,s=this.max;if(super.configure(),this.options.offset&&t.length){const n=(s-e)/Math.max(t.length-1,1)/2;e-=n,s+=n}this._startValue=e,this._endValue=s,this._valueRange=s-e}getLabelForValue(t){return ji(t,this.chart.options.locale,this.options.ticks.format)}}class pi extends Sl{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=V(t)?t:0,this.max=V(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,s=pt(this.options.ticks.minRotation),n=(t?Math.sin(s):Math.cos(s))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/n))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}S(pi,"id","linear"),S(pi,"defaults",{ticks:{callback:Hi.formatters.numeric}});const Pe={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}},W=Object.keys(Pe);function fn(i,t){return i-t}function dn(i,t){if(T(t))return null;const e=i._adapter,{parser:s,round:n,isoWeekday:o}=i._parseOpts;let r=t;return typeof s=="function"&&(r=s(r)),V(r)||(r=typeof s=="string"?e.parse(r,s):e.parse(r)),r===null?null:(n&&(r=n==="week"&&(Bt(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,n)),+r)}function un(i,t,e,s){const n=W.length;for(let o=W.indexOf(i);o=W.indexOf(e);o--){const r=W[o];if(Pe[r].common&&i._adapter.diff(n,s,r)>=t-1)return r}return W[e?W.indexOf(e):0]}function Pl(i){for(let t=W.indexOf(i)+1,e=W.length;t=t?e[s]:e[n];i[o]=!0}}function Ol(i,t,e,s){const n=i._adapter,o=+n.startOf(t[0].value,s),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+n.add(a,1,s))l=e[a],l>=0&&(t[l].major=!0);return t}function pn(i,t,e){const s=[],n={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,s=0,n,o;this.options.offset&&t.length&&(n=this.getDecimalForValue(t[0]),t.length===1?e=1-n:e=(this.getDecimalForValue(t[1])-n)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?s=o:s=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=Y(e,0,r),s=Y(s,0,r),this._offsets={start:e,end:s,factor:1/(e+1+s)}}_generate(){const t=this._adapter,e=this.min,s=this.max,n=this.options,o=n.time,r=o.unit||un(o.minUnit,e,s,this._getLabelCapacity(e)),a=C(n.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=Bt(l)||l===!0,h={};let f=e,d,u;if(c&&(f=+t.startOf(f,"isoWeek",l)),f=+t.startOf(f,c?"day":r),t.diff(s,e,r)>1e5*a)throw new Error(e+" and "+s+" are too far apart with stepSize of "+a+" "+r);const m=n.ticks.source==="data"&&this.getDataTimestamps();for(d=f,u=0;d+g)}getLabelForValue(t){const e=this._adapter,s=this.options.time;return s.tooltipFormat?e.format(t,s.tooltipFormat):e.format(t,s.displayFormats.datetime)}format(t,e){const n=this.options.time.displayFormats,o=this._unit,r=e||n[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,s,n){const o=this.options,r=o.ticks.callback;if(r)return A(r,[t,e,s],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,h=l&&a[l],f=c&&a[c],d=s[e],u=c&&f&&d&&d.major;return this._adapter.format(t,n||(u?f:h))}generateTickLabels(t){let e,s,n;for(e=0,s=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,s;if(t.length)return t;const n=this.getMatchingVisibleMetas();if(this._normalized&&n.length)return this._cache.data=n[0].controller.getAllParsedValues(this);for(e=0,s=n.length;e=i[s].pos&&t<=i[n].pos&&({lo:s,hi:n}=mt(i,"pos",t)),{pos:o,time:a}=i[s],{pos:r,time:l}=i[n]):(t>=i[s].time&&t<=i[n].time&&({lo:s,hi:n}=mt(i,"time",t)),{time:o,pos:a}=i[s],{time:r,pos:l}=i[n]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class mn extends Oe{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Ce(e,this.min),this._tableRange=Ce(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:s}=this,n=[],o=[];let r,a,l,c,h;for(r=0,a=t.length;r=e&&c<=s&&n.push(c);if(n.length<2)return[{time:e,pos:0},{time:s,pos:1}];for(r=0,a=n.length;rn-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),s=this.getLabelTimestamps();return e.length&&s.length?t=this.normalize(e.concat(s)):t=e.length?e:s,t=this._cache.all=t,t}getDecimalForValue(t){return(Ce(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,s=this.getDecimalForPixel(t)/e.factor-e.end;return Ce(this._table,s*this._tableRange+this._minPos,!0)}}S(mn,"id","timeseries"),S(mn,"defaults",Oe.defaults);function Cl(i,t,e,s,n,o,r,a){var l=typeof i=="function"?i.options:i;return t&&(l.render=t,l.staticRenderFns=e,l._compiled=!0),{exports:i,options:l}}rt.register(ge,dt,Me,pi,gi,dl,xl);const Tl={props:{analyticsData:{type:Object,default:()=>({})}},data(){return{startDate:"",endDate:"",data:this.analyticsData||{},chart:null}},computed:{hasData(){return this.data&&this.data.totalVisits>0},pagesPerSession(){var i;return(i=this.data)!=null&&i.uniqueSessions?(this.data.totalVisits/this.data.uniqueSessions).toFixed(1):"0"}},watch:{analyticsData(i){this.data=i||{},this.renderChart()}},mounted(){this.setDefaultDates(),this.fetchData()},beforeUnmount(){this.destroyChart()},methods:{setDefaultDates(){const i=new Date,t=new Date(i);t.setDate(t.getDate()-30),this.endDate=i.toISOString().split("T")[0],this.startDate=t.toISOString().split("T")[0]},async fetchData(){const i=new URLSearchParams;this.startDate&&i.set("startDate",this.startDate),this.endDate&&i.set("endDate",this.endDate);try{const e=await(await fetch(`/analytics-data.json?${i}`)).json();e.status==="success"&&(this.data=e.data),this.renderChart()}catch(t){console.error("Analytics fetch error:",t),this.renderChart()}},destroyChart(){this.chart&&(this.chart.destroy(),this.chart=null)},formatDateFR(i){const[t,e,s]=i.split("-");return`${s}/${e}/${t}`},renderChart(){var n;this.destroyChart();const i=this.$refs.chartCanvas;if(!i||!((n=this.data)!=null&&n.visitsByDay))return;const t=Object.keys(this.data.visitsByDay).map(o=>this.formatDateFR(o)),e=Object.values(this.data.visitsByDay);if(!t.length)return;const s=Math.max(...e);this.chart=new rt(i,{type:"line",data:{labels:t,datasets:[{label:"Visites",data:e,borderColor:"#4271ae",backgroundColor:"rgba(66, 113, 174, 0.1)",fill:!0,tension:.3,pointRadius:3,pointHoverRadius:5}]},options:{responsive:!0,maintainAspectRatio:!1,plugins:{tooltip:{mode:"index",intersect:!1}},scales:{y:{beginAtZero:!0,max:s+3,ticks:{precision:0}}}}})}}};var Al=function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-analytics-dashboard"},[e("div",{staticClass:"k-analytics-filters"},[e("label",[t._v(" Du "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.startDate,expression:"startDate"}],attrs:{type:"date"},domProps:{value:t.startDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.startDate=s.target.value)}}})]),e("label",[t._v(" Au "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.endDate,expression:"endDate"}],attrs:{type:"date"},domProps:{value:t.endDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.endDate=s.target.value)}}})])]),t.hasData?[e("div",{staticClass:"k-analytics-grid"},[e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Sessions uniques")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.uniqueSessions))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages vues")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.totalVisits))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages / session")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.pagesPerSession))])])]),e("div",{staticClass:"k-analytics-chart-container"},[e("h3",[t._v("Visites par jour")]),e("canvas",{ref:"chartCanvas"})]),e("div",{staticClass:"k-analytics-grid k-analytics-grid--2col"},[t.data.visitsByPage&&Object.keys(t.data.visitsByPage).length?e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages les plus visitées")]),e("ul",{staticClass:"k-analytics-list"},t._l(t.data.visitsByPage,function(s,n){return e("li",{key:n},[e("span",{staticClass:"k-analytics-list-label"},[t._v(t._s(n))]),e("span",{staticClass:"k-analytics-list-value"},[t._v(t._s(s))])])}),0)]):t._e(),t.data.visitsByUser&&Object.keys(t.data.visitsByUser).length?e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Utilisateurs les plus actifs")]),e("ul",{staticClass:"k-analytics-list"},t._l(t.data.visitsByUser,function(s,n){return e("li",{key:n},[e("span",{staticClass:"k-analytics-list-label"},[t._v(t._s(n))]),e("span",{staticClass:"k-analytics-list-value"},[t._v(t._s(s))])])}),0)]):t._e()])]:e("div",{staticClass:"k-analytics-empty"},[e("p",[t._v("Aucune donnée à afficher")])])],2)},Il=[],Ll=Cl(Tl,Al,Il);const Fl=Ll.exports;window.panel.plugin("adrienpayet/analytics",{fields:{"analytics-dashboard":Fl}})})(); +`):i}function ul(i,t){const{element:e,datasetIndex:s,index:n}=t,o=i.getDatasetMeta(s).controller,{label:r,value:a}=o.getLabelAndValue(n);return{chart:i,label:r,parsed:o.getParsed(n),raw:i.data.datasets[s].data[n],formattedValue:a,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:e}}function sn(i,t){const e=i.chart.ctx,{body:s,footer:n,title:o}=i,{boxWidth:r,boxHeight:a}=t,l=J(t.bodyFont),c=J(t.titleFont),h=J(t.footerFont),f=o.length,d=n.length,u=s.length,m=ht(t.padding);let g=m.height,p=0,b=s.reduce((x,_)=>x+_.before.length+_.lines.length+_.after.length,0);if(b+=i.beforeBody.length+i.afterBody.length,f&&(g+=f*c.lineHeight+(f-1)*t.titleSpacing+t.titleMarginBottom),b){const x=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;g+=u*x+(b-u)*l.lineHeight+(b-1)*t.bodySpacing}d&&(g+=t.footerMarginTop+d*h.lineHeight+(d-1)*t.footerSpacing);let y=0;const v=function(x){p=Math.max(p,e.measureText(x).width+y)};return e.save(),e.font=c.string,C(i.title,v),e.font=l.string,C(i.beforeBody.concat(i.afterBody),v),y=t.displayColors?r+2+t.boxPadding:0,C(s,x=>{C(x.before,v),C(x.lines,v),C(x.after,v)}),y=0,e.font=h.string,C(i.footer,v),e.restore(),p+=m.width,{width:p,height:g}}function gl(i,t){const{y:e,height:s}=t;return ei.height-s/2?"bottom":"center"}function pl(i,t,e,s){const{x:n,width:o}=s,r=e.caretSize+e.caretPadding;if(i==="left"&&n+o+r>t.width||i==="right"&&n-o-r<0)return!0}function ml(i,t,e,s){const{x:n,width:o}=e,{width:r,chartArea:{left:a,right:l}}=i;let c="center";return s==="center"?c=n<=(a+l)/2?"left":"right":n<=o/2?c="left":n>=r-o/2&&(c="right"),pl(c,i,t,e)&&(c="center"),c}function nn(i,t,e){const s=e.yAlign||t.yAlign||gl(i,e);return{xAlign:e.xAlign||t.xAlign||ml(i,t,e,s),yAlign:s}}function bl(i,t){let{x:e,width:s}=i;return t==="right"?e-=s:t==="center"&&(e-=s/2),e}function _l(i,t,e){let{y:s,height:n}=i;return t==="top"?s+=e:t==="bottom"?s-=n+e:s-=n/2,s}function on(i,t,e,s){const{caretSize:n,caretPadding:o,cornerRadius:r}=i,{xAlign:a,yAlign:l}=e,c=n+o,{topLeft:h,topRight:f,bottomLeft:d,bottomRight:u}=le(r);let m=bl(t,a);const g=_l(t,l,c);return l==="center"?a==="left"?m+=c:a==="right"&&(m-=c):a==="left"?m-=Math.max(h,d)+n:a==="right"&&(m+=Math.max(f,u)+n),{x:Y(m,0,s.width-t.width),y:Y(g,0,s.height-t.height)}}function De(i,t,e){const s=ht(e.padding);return t==="center"?i.x+i.width/2:t==="right"?i.x+i.width-s.right:i.x+s.left}function rn(i){return et([],at(i))}function yl(i,t,e){return yt(i,{tooltip:t,tooltipItems:e,type:"tooltip"})}function an(i,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?i.override(e):i}const ln={beforeTitle:nt,title(i){if(i.length>0){const t=i[0],e=t.chart.data.labels,s=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(s>0&&t.dataIndex"u"?ln[t].call(e,s):n}class ui extends Mt{constructor(t){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=t.chart,this.options=t.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(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,s=this.options.setContext(this.getContext()),n=s.enabled&&e.options.animation&&s.animations,o=new fs(this.chart,n);return n._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=yl(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:s}=e,n=H(s,"beforeTitle",this,t),o=H(s,"title",this,t),r=H(s,"afterTitle",this,t);let a=[];return a=et(a,at(n)),a=et(a,at(o)),a=et(a,at(r)),a}getBeforeBody(t,e){return rn(H(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:s}=e,n=[];return C(t,o=>{const r={before:[],lines:[],after:[]},a=an(s,o);et(r.before,at(H(a,"beforeLabel",this,o))),et(r.lines,H(a,"label",this,o)),et(r.after,at(H(a,"afterLabel",this,o))),n.push(r)}),n}getAfterBody(t,e){return rn(H(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:s}=e,n=H(s,"beforeFooter",this,t),o=H(s,"footer",this,t),r=H(s,"afterFooter",this,t);let a=[];return a=et(a,at(n)),a=et(a,at(o)),a=et(a,at(r)),a}_createItems(t){const e=this._active,s=this.chart.data,n=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(h,f,d,s))),t.itemSort&&(a=a.sort((h,f)=>t.itemSort(h,f,s))),C(a,h=>{const f=an(t.callbacks,h);n.push(H(f,"labelColor",this,h)),o.push(H(f,"labelPointStyle",this,h)),r.push(H(f,"labelTextColor",this,h))}),this.labelColors=n,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const s=this.options.setContext(this.getContext()),n=this._active;let o,r=[];if(!n.length)this.opacity!==0&&(o={opacity:0});else{const a=Gt[s.position].call(this,n,this._eventPosition);r=this._createItems(s),this.title=this.getTitle(r,s),this.beforeBody=this.getBeforeBody(r,s),this.body=this.getBody(r,s),this.afterBody=this.getAfterBody(r,s),this.footer=this.getFooter(r,s);const l=this._size=sn(this,s),c=Object.assign({},a,l),h=nn(this.chart,s,c),f=on(s,c,h,this.chart);this.xAlign=h.xAlign,this.yAlign=h.yAlign,o={opacity:1,x:f.x,y:f.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&s.external&&s.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,s,n){const o=this.getCaretPosition(t,s,n);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,s){const{xAlign:n,yAlign:o}=this,{caretSize:r,cornerRadius:a}=s,{topLeft:l,topRight:c,bottomLeft:h,bottomRight:f}=le(a),{x:d,y:u}=t,{width:m,height:g}=e;let p,b,y,v,x,_;return o==="center"?(x=u+g/2,n==="left"?(p=d,b=p-r,v=x+r,_=x-r):(p=d+m,b=p+r,v=x-r,_=x+r),y=p):(n==="left"?b=d+Math.max(l,h)+r:n==="right"?b=d+m-Math.max(c,f)-r:b=this.caretX,o==="top"?(v=u,x=v-r,p=b-r,y=b+r):(v=u+g,x=v+r,p=b+r,y=b-r),_=v),{x1:p,x2:b,x3:y,y1:v,y2:x,y3:_}}drawTitle(t,e,s){const n=this.title,o=n.length;let r,a,l;if(o){const c=Je(s.rtl,this.x,this.width);for(t.x=De(this,s.titleAlign,s),e.textAlign=c.textAlign(s.titleAlign),e.textBaseline="middle",r=J(s.titleFont),a=s.titleSpacing,e.fillStyle=s.titleColor,e.font=r.string,l=0;ly!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,Ui(t,{x:g,y:m,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),Ui(t,{x:p,y:m+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(g,m,c,l),t.strokeRect(g,m,c,l),t.fillStyle=r.backgroundColor,t.fillRect(p,m+1,c-2,l-2))}t.fillStyle=this.labelTextColors[s]}drawBody(t,e,s){const{body:n}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:h}=s,f=J(s.bodyFont);let d=f.lineHeight,u=0;const m=Je(s.rtl,this.x,this.width),g=function(w){e.fillText(w,m.x(t.x+u),t.y+d/2),t.y+=d+o},p=m.textAlign(r);let b,y,v,x,_,M,k;for(e.textAlign=r,e.textBaseline="middle",e.font=f.string,t.x=De(this,p,s),e.fillStyle=s.bodyColor,C(this.beforeBody,g),u=a&&p!=="right"?r==="center"?c/2+h:c+2+h:0,x=0,M=n.length;x0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,s=this.$animations,n=s&&s.x,o=s&&s.y;if(n||o){const r=Gt[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=sn(this,t),l=Object.assign({},r,this._size),c=nn(e,t,l),h=on(t,l,c,e);(n._to!==h.x||o._to!==h.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,h))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let s=this.opacity;if(!s)return;this._updateAnimationTarget(e);const n={width:this.width,height:this.height},o={x:this.x,y:this.y};s=Math.abs(s)<.001?0:s;const r=ht(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=s,this.drawBackground(o,t,n,e),rr(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),ar(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const s=this._active,n=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!ie(s,n),r=this._positionChanged(n,e);(o||r)&&(this._active=n,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,s=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const n=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,s),a=this._positionChanged(r,t),l=e||!ie(r,o)||a;return l&&(this._active=r,(n.enabled||n.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,s,n){const o=this.options;if(t.type==="mouseout")return[];if(!n)return e.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const r=this.chart.getElementsAtEventForMode(t,o.mode,o,s);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:s,caretY:n,options:o}=this,r=Gt[o.position].call(this,t,e);return r!==!1&&(s!==r.x||n!==r.y)}}S(ui,"positioners",Gt);var xl={id:"tooltip",_element:ui,positioners:Gt,afterInit(i,t,e){e&&(i.tooltip=new ui({chart:i,options:e}))},beforeUpdate(i,t,e){i.tooltip&&i.tooltip.initialize(e)},reset(i,t,e){i.tooltip&&i.tooltip.initialize(e)},afterDraw(i){const t=i.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(i.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(i.ctx),i.notifyPlugins("afterTooltipDraw",e)}},afterEvent(i,t){if(i.tooltip){const e=t.replay;i.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.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:(i,t)=>t.bodyFont.size,boxWidth:(i,t)=>t.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:ln},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:i=>i!=="filter"&&i!=="itemSort"&&i!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const vl=(i,t,e,s)=>(typeof t=="string"?(e=i.push(t)-1,s.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function kl(i,t,e,s){const n=i.indexOf(t);if(n===-1)return vl(i,t,e,s);const o=i.lastIndexOf(t);return n!==o?e:n}const wl=(i,t)=>i===null?null:Y(Math.round(i),0,t);function cn(i){const t=this.getLabels();return i>=0&&ie.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}S(gi,"id","category"),S(gi,"defaults",{ticks:{callback:cn}});function Ml(i,t){const e=[],{bounds:n,step:o,min:r,max:a,precision:l,count:c,maxTicks:h,maxDigits:f,includeBounds:d}=i,u=o||1,m=h-1,{min:g,max:p}=t,b=!T(r),y=!T(a),v=!T(c),x=(p-g)/(f+1);let _=Ci((p-g)/m/u)*u,M,k,w,P;if(_<1e-14&&!b&&!y)return[{value:g},{value:p}];P=Math.ceil(p/_)-Math.floor(g/_),P>m&&(_=Ci(P*_/m/u)*u),T(l)||(M=Math.pow(10,l),_=Math.ceil(_*M)/M),n==="ticks"?(k=Math.floor(g/_)*_,w=Math.ceil(p/_)*_):(k=g,w=p),b&&y&&o&&Gn((a-r)/o,_/1e3)?(P=Math.round(Math.min((a-r)/_,h)),_=(a-r)/P,k=r,w=a):v?(k=b?r:k,w=y?a:w,P=c-1,_=(w-k)/P):(P=(w-k)/_,zt(P,Math.round(P),_/1e3)?P=Math.round(P):P=Math.ceil(P));const I=Math.max(Oi(_),Oi(k));M=Math.pow(10,T(l)?I:l),k=Math.round(k*M)/M,w=Math.round(w*M)/M;let L=0;for(b&&(d&&k!==r?(e.push({value:r}),ka)break;e.push({value:E})}return y&&d&&w!==a?e.length&&zt(e[e.length-1].value,a,hn(a,x,i))?e[e.length-1].value=a:e.push({value:a}):(!y||w===a)&&e.push({value:w}),e}function hn(i,t,{horizontal:e,minRotation:s}){const n=pt(s),o=(e?Math.sin(n):Math.cos(n))||.001,r=.75*t*(""+i).length;return Math.min(t/o,r)}class Sl extends At{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return T(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:s}=this.getUserBounds();let{min:n,max:o}=this;const r=l=>n=e?n:l,a=l=>o=s?o:l;if(t){const l=Ct(n),c=Ct(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(n===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(n-l)}this.min=n,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:s}=t,n;return s?(n=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let s=this.getTickLimit();s=Math.max(2,s);const n={maxTicks:s,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=Ml(n,o);return t.bounds==="ticks"&&Zn(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,s=this.max;if(super.configure(),this.options.offset&&t.length){const n=(s-e)/Math.max(t.length-1,1)/2;e-=n,s+=n}this._startValue=e,this._endValue=s,this._valueRange=s-e}getLabelForValue(t){return ji(t,this.chart.options.locale,this.options.ticks.format)}}class pi extends Sl{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=V(t)?t:0,this.max=V(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,s=pt(this.options.ticks.minRotation),n=(t?Math.sin(s):Math.cos(s))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/n))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}S(pi,"id","linear"),S(pi,"defaults",{ticks:{callback:Hi.formatters.numeric}});const Pe={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}},W=Object.keys(Pe);function fn(i,t){return i-t}function dn(i,t){if(T(t))return null;const e=i._adapter,{parser:s,round:n,isoWeekday:o}=i._parseOpts;let r=t;return typeof s=="function"&&(r=s(r)),V(r)||(r=typeof s=="string"?e.parse(r,s):e.parse(r)),r===null?null:(n&&(r=n==="week"&&(Bt(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,n)),+r)}function un(i,t,e,s){const n=W.length;for(let o=W.indexOf(i);o=W.indexOf(e);o--){const r=W[o];if(Pe[r].common&&i._adapter.diff(n,s,r)>=t-1)return r}return W[e?W.indexOf(e):0]}function Pl(i){for(let t=W.indexOf(i)+1,e=W.length;t=t?e[s]:e[n];i[o]=!0}}function Cl(i,t,e,s){const n=i._adapter,o=+n.startOf(t[0].value,s),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+n.add(a,1,s))l=e[a],l>=0&&(t[l].major=!0);return t}function pn(i,t,e){const s=[],n={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,s=0,n,o;this.options.offset&&t.length&&(n=this.getDecimalForValue(t[0]),t.length===1?e=1-n:e=(this.getDecimalForValue(t[1])-n)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?s=o:s=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=Y(e,0,r),s=Y(s,0,r),this._offsets={start:e,end:s,factor:1/(e+1+s)}}_generate(){const t=this._adapter,e=this.min,s=this.max,n=this.options,o=n.time,r=o.unit||un(o.minUnit,e,s,this._getLabelCapacity(e)),a=O(n.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=Bt(l)||l===!0,h={};let f=e,d,u;if(c&&(f=+t.startOf(f,"isoWeek",l)),f=+t.startOf(f,c?"day":r),t.diff(s,e,r)>1e5*a)throw new Error(e+" and "+s+" are too far apart with stepSize of "+a+" "+r);const m=n.ticks.source==="data"&&this.getDataTimestamps();for(d=f,u=0;d+g)}getLabelForValue(t){const e=this._adapter,s=this.options.time;return s.tooltipFormat?e.format(t,s.tooltipFormat):e.format(t,s.displayFormats.datetime)}format(t,e){const n=this.options.time.displayFormats,o=this._unit,r=e||n[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,s,n){const o=this.options,r=o.ticks.callback;if(r)return A(r,[t,e,s],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,h=l&&a[l],f=c&&a[c],d=s[e],u=c&&f&&d&&d.major;return this._adapter.format(t,n||(u?f:h))}generateTickLabels(t){let e,s,n;for(e=0,s=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,s;if(t.length)return t;const n=this.getMatchingVisibleMetas();if(this._normalized&&n.length)return this._cache.data=n[0].controller.getAllParsedValues(this);for(e=0,s=n.length;e=i[s].pos&&t<=i[n].pos&&({lo:s,hi:n}=mt(i,"pos",t)),{pos:o,time:a}=i[s],{pos:r,time:l}=i[n]):(t>=i[s].time&&t<=i[n].time&&({lo:s,hi:n}=mt(i,"time",t)),{time:o,pos:a}=i[s],{time:r,pos:l}=i[n]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class mn extends Ce{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Oe(e,this.min),this._tableRange=Oe(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:s}=this,n=[],o=[];let r,a,l,c,h;for(r=0,a=t.length;r=e&&c<=s&&n.push(c);if(n.length<2)return[{time:e,pos:0},{time:s,pos:1}];for(r=0,a=n.length;rn-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),s=this.getLabelTimestamps();return e.length&&s.length?t=this.normalize(e.concat(s)):t=e.length?e:s,t=this._cache.all=t,t}getDecimalForValue(t){return(Oe(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,s=this.getDecimalForPixel(t)/e.factor-e.end;return Oe(this._table,s*this._tableRange+this._minPos,!0)}}S(mn,"id","timeseries"),S(mn,"defaults",Ce.defaults);function Ol(i,t,e,s,n,o,r,a){var l=typeof i=="function"?i.options:i;return t&&(l.render=t,l.staticRenderFns=e,l._compiled=!0),{exports:i,options:l}}rt.register(ge,dt,Me,pi,gi,dl,xl);const Tl={props:{analyticsData:{type:Object,default:()=>({})}},data(){return{startDate:"",endDate:"",data:this.analyticsData||{},chart:null,users:[],selectedEmails:[]}},computed:{hasData(){return this.data&&this.data.totalVisits>0},pagesPerSession(){var i;return(i=this.data)!=null&&i.uniqueSessions?(this.data.totalVisits/this.data.uniqueSessions).toFixed(1):"0"},userOptions(){return this.users.map(i=>({value:i.email,text:i.label}))}},watch:{analyticsData(i){this.data=i||{},this.renderChart()}},mounted(){this.setDefaultDates(),this.fetchData()},beforeUnmount(){this.destroyChart()},methods:{setDefaultDates(){const i=new Date,t=new Date(i);t.setDate(t.getDate()-30),this.endDate=i.toISOString().split("T")[0],this.startDate=t.toISOString().split("T")[0]},onUserSelectionChange(i){this.selectedEmails=i,this.fetchData()},async fetchData(){const i=new URLSearchParams;this.startDate&&i.set("startDate",this.startDate),this.endDate&&i.set("endDate",this.endDate),this.selectedEmails.length&&i.set("emails",this.selectedEmails.join(","));try{const e=await(await fetch(`/analytics-data.json?${i}`)).json();console.log(e.data),e.status==="success"&&(this.data=e.data),e.users&&!this.users.length&&(this.users=e.users),this.renderChart()}catch(t){console.error("Analytics fetch error:",t),this.renderChart()}},destroyChart(){this.chart&&(this.chart.destroy(),this.chart=null)},formatDateFR(i){const[t,e,s]=i.split("-");return`${s}/${e}/${t}`},renderChart(){var n;this.destroyChart();const i=this.$refs.chartCanvas;if(!i||!((n=this.data)!=null&&n.visitsByDay))return;const t=Object.keys(this.data.visitsByDay).map(o=>this.formatDateFR(o)),e=Object.values(this.data.visitsByDay);if(!t.length)return;const s=Math.max(...e);this.chart=new rt(i,{type:"line",data:{labels:t,datasets:[{label:"Visites",data:e,borderColor:"#4271ae",backgroundColor:"rgba(66, 113, 174, 0.1)",fill:!0,tension:.3,pointRadius:3,pointHoverRadius:5}]},options:{responsive:!0,maintainAspectRatio:!1,plugins:{tooltip:{mode:"index",intersect:!1}},scales:{y:{beginAtZero:!0,max:s+3,ticks:{precision:0}}}}})}}};var Al=function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-analytics-dashboard"},[e("div",{staticClass:"k-analytics-filters"},[e("div",{staticClass:"k-analytics-date-filter"},[t._m(0),e("div",{staticClass:"k-date-inputs-wrapper"},[e("label",[t._v(" Du "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.startDate,expression:"startDate"}],attrs:{type:"date"},domProps:{value:t.startDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.startDate=s.target.value)}}})]),e("label",[t._v(" Au "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.endDate,expression:"endDate"}],attrs:{type:"date"},domProps:{value:t.endDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.endDate=s.target.value)}}})])])]),e("div",{staticClass:"k-analytics-user-filter"},[e("k-multiselect-field",{attrs:{options:t.userOptions,value:t.selectedEmails,label:"Filtrer par utilisateur(s)",search:"true",name:"user"},on:{input:t.onUserSelectionChange}})],1)]),t.hasData?[e("div",{staticClass:"k-analytics-grid"},[e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Sessions uniques")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.uniqueSessions))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages vues")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.totalVisits))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages / session")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.pagesPerSession))])])]),e("div",{staticClass:"k-analytics-chart-container"},[e("h3",[t._v("Visites par jour")]),e("canvas",{ref:"chartCanvas"})]),t.data.visitsByPage&&Object.keys(t.data.visitsByPage).length?e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages les plus visitées")]),e("ul",{staticClass:"k-analytics-list"},t._l(t.data.visitsByPage,function(s,n){return e("li",{key:n},[e("span",{staticClass:"k-analytics-list-label"},[t._v(t._s(n))]),e("span",{staticClass:"k-analytics-list-value"},[t._v(t._s(s))])])}),0)]):t._e()]:e("div",{staticClass:"k-analytics-empty"},[e("p",[t._v("Aucune donnée à afficher")])])],2)},Il=[function(){var i=this,t=i._self._c;return t("header",{staticClass:"k-field-header"},[t("label",{staticClass:"k-label k-field-label",attrs:{title:"Filtrer par dates"}},[t("span",{staticClass:"k-label-text"},[i._v("Filtrer par dates ")])])])}],Ll=Ol(Tl,Al,Il);const Fl=Ll.exports;window.panel.plugin("adrienpayet/analytics",{fields:{"analytics-dashboard":Fl}})})(); diff --git a/public/site/plugins/analytics/routes/get-data.php b/public/site/plugins/analytics/routes/get-data.php index 3663023..4a5a4a7 100644 --- a/public/site/plugins/analytics/routes/get-data.php +++ b/public/site/plugins/analytics/routes/get-data.php @@ -27,7 +27,6 @@ return [ $request = $kirby->request(); $filters = []; - // Récupérer les filtres depuis query params if ($startDate = $request->query()->get('startDate')) { $filters['startDate'] = $startDate; } @@ -40,11 +39,38 @@ return [ $filters['project'] = $project; } + if (!empty($_GET['emails'])) { + $filters['emails'] = explode(',', $_GET['emails']); + } + $data = $analyticsPage->getAnalyticsData($filters); + $users = []; + foreach ($kirby->users() as $u) { + $email = (string) $u->email(); + $name = $u->name()->isNotEmpty() ? (string) $u->name() : $email; + $label = $name; + + $clientField = $u->content()->get('client'); + if ($clientField && $clientField->isNotEmpty()) { + $clientPage = $clientField->toPage(); + if ($clientPage) { + $label .= ' (' . $clientPage->title() . ')'; + } + } + + $users[] = [ + 'email' => $email, + 'label' => $label, + ]; + } + + usort($users, fn($a, $b) => strcasecmp($a['label'], $b['label'])); + return [ 'status' => 'success', - 'data' => $data + 'data' => $data, + 'users' => $users ]; } ]; diff --git a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue index 5700cdd..09b0290 100644 --- a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue +++ b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue @@ -1,14 +1,33 @@ @@ -102,6 +107,8 @@ export default { endDate: '', data: this.analyticsData || {}, chart: null, + users: [], + selectedEmails: [], }; }, @@ -113,6 +120,9 @@ export default { if (!this.data?.uniqueSessions) return '0'; return (this.data.totalVisits / this.data.uniqueSessions).toFixed(1); }, + userOptions() { + return this.users.map((u) => ({ value: u.email, text: u.label })); + }, }, watch: { @@ -141,19 +151,33 @@ export default { this.startDate = thirtyDaysAgo.toISOString().split('T')[0]; }, + onUserSelectionChange(emails) { + this.selectedEmails = emails; + this.fetchData(); + }, + async fetchData() { const params = new URLSearchParams(); if (this.startDate) params.set('startDate', this.startDate); if (this.endDate) params.set('endDate', this.endDate); + if (this.selectedEmails.length) { + params.set('emails', this.selectedEmails.join(',')); + } try { const response = await fetch(`/analytics-data.json?${params}`); const json = await response.json(); + console.log(json.data); + if (json.status === 'success') { this.data = json.data; } + if (json.users && !this.users.length) { + this.users = json.users; + } + this.renderChart(); } catch (e) { console.error('Analytics fetch error:', e); @@ -247,6 +271,11 @@ export default { color: var(--color-text-light); } +.k-date-inputs-wrapper { + display: flex; + column-gap: 1rem; +} + .k-analytics-filters input[type='date'] { padding: 0.375rem 0.5rem; border: 1px solid var(--color-border); @@ -262,8 +291,17 @@ export default { margin-bottom: 1.5rem; } -.k-analytics-grid--2col { - grid-template-columns: repeat(2, 1fr); +.k-analytics-user-filter { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--color-text-light); + margin-left: 2rem; +} + +.k-field-name-user { + min-width: 15rem; } .k-analytics-card { diff --git a/src/router/router.js b/src/router/router.js index aa6c4d1..91706bf 100644 --- a/src/router/router.js +++ b/src/router/router.js @@ -4,6 +4,7 @@ import { useApiStore } from '../stores/api'; import { usePageStore } from '../stores/page'; import { useUserStore } from '../stores/user'; import { useLocaleStore } from '../stores/locale'; +import { useAnalyticsStore } from '../stores/analytics'; const router = createRouter({ history: createWebHistory(), @@ -41,6 +42,65 @@ router.beforeEach(async (to, from, next) => { } }); +router.afterEach((to) => { + const userStore = useUserStore(); + const pageStore = usePageStore(); + + if (userStore.user) { + const analytics = useAnalyticsStore(); + analytics.initSession(); + + const { pageType, pageName } = getPageInfo(to, pageStore.page); + analytics.trackVisit(to.path, pageType, pageName); + } +}); + +function getPageInfo(route, page) { + const path = route.path; + + if (path === '/' || path === '/en') { + return { pageType: 'home', pageName: 'Accueil' }; + } + + if (path.includes('/login')) { + return { pageType: 'login', pageName: 'Connexion' }; + } + + if (path.includes('/account')) { + return { pageType: 'account', pageName: 'Compte' }; + } + + if (path.includes('/notifications')) { + return { pageType: 'notifications', pageName: 'Notifications' }; + } + + if (path.includes('/reunions')) { + return { pageType: 'reunions', pageName: 'Réunions' }; + } + + if (path.includes('/inspirations')) { + return { pageType: 'inspirations', pageName: 'Inspirations' }; + } + + if (path.includes('/design-to-light')) { + return { pageType: 'design-to-light', pageName: 'Design to Light' }; + } + + if (path.includes('/client-brief')) { + return { pageType: 'client-brief', pageName: page?.title || 'Brief Client' }; + } + + if (path.includes('/extended-brief')) { + return { pageType: 'extended-brief', pageName: page?.title || 'Brief Étendu' }; + } + + if (path.includes('/projects/')) { + return { pageType: 'project', pageName: page?.title || 'Projet' }; + } + + return { pageType: 'unknown', pageName: path }; +} + export function setI18nLocale(i18n) { router.afterEach(() => { const localeStore = useLocaleStore(); diff --git a/src/stores/dialog.js b/src/stores/dialog.js index f35d979..38f4e21 100644 --- a/src/stores/dialog.js +++ b/src/stores/dialog.js @@ -1,6 +1,7 @@ import { defineStore } from 'pinia'; import { ref, computed, watch } from 'vue'; import { useRoute } from 'vue-router'; +import { useAnalyticsStore } from './analytics'; export const useDialogStore = defineStore('dialog', () => { const content = ref(null); @@ -148,6 +149,19 @@ export const useDialogStore = defineStore('dialog', () => { } } + // Analytics tracking pour ouverture de fichiers + watch(openedFile, (newFile) => { + if (newFile) { + const analytics = useAnalyticsStore(); + const currentPath = route.path; + analytics.trackVisit( + `${currentPath}#file-${newFile.uuid}`, + 'modal-file', + newFile.name || newFile.filename + ); + } + }); + return { content, activeTracks, From 084bb46379fe8ef9d87c2807e03ac4347c1d905c Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 11:53:50 +0100 Subject: [PATCH 40/52] =?UTF-8?q?feat:=20filtre=20par=20page,=20multi-cour?= =?UTF-8?q?bes=20chart,=20tri=20alphab=C3=A9tique=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout filtre multiselect par page(s) dans le dashboard analytics - Chart multi-courbes par page quand aucun filtre page sélectionné - Options de pages indépendantes du filtre page (persistent à la sélection) - Options de pages dépendantes du filtre utilisateur - Tracking modal-file avec format "Projet / Label" dans dialog.js - Tri alphabétique des options du filtre par page - Fix user list 500 error (email string, client field check) Co-Authored-By: Claude Opus 4.6 --- .../analytics/classes/AnalyticsStore.php | 63 +++++---- public/site/plugins/analytics/index.css | 2 +- public/site/plugins/analytics/index.js | 10 +- .../plugins/analytics/routes/get-data.php | 4 + .../src/components/AnalyticsDashboard.vue | 127 ++++++++++++++---- src/stores/dialog.js | 6 +- 6 files changed, 157 insertions(+), 55 deletions(-) diff --git a/public/site/plugins/analytics/classes/AnalyticsStore.php b/public/site/plugins/analytics/classes/AnalyticsStore.php index 53ca29a..9bdc184 100644 --- a/public/site/plugins/analytics/classes/AnalyticsStore.php +++ b/public/site/plugins/analytics/classes/AnalyticsStore.php @@ -95,15 +95,39 @@ class AnalyticsStore }); } + // Filtrer par nom de page + if (!empty($filters['pageNames'])) { + $pageNames = $filters['pageNames']; + $visits = array_filter($visits, function($visit) use ($pageNames) { + $name = $visit->pageName ?: $visit->pageUrl; + return in_array($name, $pageNames); + }); + } + return array_values($visits); } public static function getAggregatedData(array $filters = []): array { self::ensureFileExists(); - $visits = self::getVisits($filters); - // Visites par jour + // Visits sans filtre pageNames → pour construire la liste des pages (options) + $filtersWithoutPages = $filters; + unset($filtersWithoutPages['pageNames']); + $allVisits = self::getVisits($filtersWithoutPages); + + $visitsByPage = []; + foreach ($allVisits as $visit) { + $page = $visit->pageName ?: $visit->pageUrl; + $visitsByPage[$page] = ($visitsByPage[$page] ?? 0) + 1; + } + arsort($visitsByPage); + + // Visits avec tous les filtres → pour les stats affichées + $visits = !empty($filters['pageNames']) + ? self::getVisits($filters) + : $allVisits; + $visitsByDay = []; foreach ($visits as $visit) { $day = date('Y-m-d', strtotime($visit->timestamp)); @@ -111,38 +135,27 @@ class AnalyticsStore } ksort($visitsByDay); - // Visites par page - $visitsByPage = []; - foreach ($visits as $visit) { - $page = $visit->pageName ?: $visit->pageUrl; - $visitsByPage[$page] = ($visitsByPage[$page] ?? 0) + 1; - } - arsort($visitsByPage); - - // Visites par utilisateur - $visitsByUser = []; - foreach ($visits as $visit) { - $visitsByUser[$visit->email] = ($visitsByUser[$visit->email] ?? 0) + 1; - } - arsort($visitsByUser); - - // Nombre de sessions uniques $uniqueSessions = count(array_unique(array_map(fn($v) => $v->sessionId, $visits))); - // Visites par type de page - $visitsByType = []; + // Visites par jour par page (top 10 pages) + $topPages = array_keys(array_slice($visitsByPage, 0, 10, true)); + $visitsByDayByPage = []; foreach ($visits as $visit) { - $visitsByType[$visit->pageType] = ($visitsByType[$visit->pageType] ?? 0) + 1; + $page = $visit->pageName ?: $visit->pageUrl; + if (!in_array($page, $topPages)) continue; + $day = date('Y-m-d', strtotime($visit->timestamp)); + $visitsByDayByPage[$page][$day] = ($visitsByDayByPage[$page][$day] ?? 0) + 1; + } + foreach ($visitsByDayByPage as &$days) { + ksort($days); } - arsort($visitsByType); return [ 'totalVisits' => count($visits), 'uniqueSessions' => $uniqueSessions, 'visitsByDay' => $visitsByDay, - 'visitsByPage' => array_slice($visitsByPage, 0, 10), - 'visitsByUser' => $visitsByUser, - 'visitsByType' => $visitsByType, + 'visitsByPage' => array_slice($visitsByPage, 0, 10, true), + 'visitsByDayByPage' => $visitsByDayByPage, ]; } } diff --git a/public/site/plugins/analytics/index.css b/public/site/plugins/analytics/index.css index 9c68fe6..a8e09f1 100644 --- a/public/site/plugins/analytics/index.css +++ b/public/site/plugins/analytics/index.css @@ -1 +1 @@ -.k-analytics-dashboard{padding:1.5rem 0}.k-analytics-filters{display:flex;gap:1rem;margin-bottom:1.5rem}.k-analytics-filters label{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light)}.k-date-inputs-wrapper{display:flex;column-gap:1rem}.k-analytics-filters input[type=date]{padding:.375rem .5rem;border:1px solid var(--color-border);border-radius:var(--rounded);font-size:.875rem;background:var(--color-background)}.k-analytics-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem;margin-bottom:1.5rem}.k-analytics-user-filter{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light);margin-left:2rem}.k-field-name-user{min-width:15rem}.k-analytics-card{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;box-shadow:var(--shadow)}.k-analytics-card h3{font-size:.75rem;font-weight:600;color:var(--color-text-light);margin:0 0 .5rem;text-transform:uppercase;letter-spacing:.5px}.k-analytics-metric{font-size:2.5rem;font-weight:700;color:var(--color-text);line-height:1}.k-analytics-chart-container{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;margin-bottom:1.5rem;box-shadow:var(--shadow)}.k-analytics-chart-container h3{font-size:.875rem;font-weight:600;color:var(--color-text);margin:0 0 1rem}.k-analytics-chart-container canvas{max-height:300px}.k-analytics-empty{background:var(--color-background);border-radius:var(--rounded);padding:3rem;text-align:center;box-shadow:var(--shadow)}.k-analytics-empty p{margin:0;color:var(--color-text-light)}.k-analytics-list{list-style:none;margin:0;padding:0}.k-analytics-list li{display:flex;justify-content:space-between;padding:.375rem 0;border-bottom:1px solid var(--color-border);font-size:.875rem}.k-analytics-list li:last-child{border-bottom:none}.k-analytics-list-label{color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:1rem}.k-analytics-list-value{font-weight:600;color:var(--color-text);flex-shrink:0} +.k-analytics-dashboard{padding:1.5rem 0}.k-analytics-filters{display:flex;gap:1rem;margin-bottom:1.5rem}.k-analytics-filters label{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light)}.k-date-inputs-wrapper{display:flex;column-gap:1rem}.k-analytics-filters input[type=date]{padding:.375rem .5rem;border:1px solid var(--color-border);border-radius:var(--rounded);font-size:.875rem;background:var(--color-background)}.k-analytics-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem;margin-bottom:1.5rem}.k-analytics-user-filter{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light);margin-left:2rem}.k-field-name-user,.k-field-name-page{min-width:15rem}.k-analytics-page-filter{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light);margin-left:2rem}.k-analytics-card{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;box-shadow:var(--shadow)}.k-analytics-card h3{font-size:.75rem;font-weight:600;color:var(--color-text-light);margin:0 0 .5rem;text-transform:uppercase;letter-spacing:.5px}.k-analytics-metric{font-size:2.5rem;font-weight:700;color:var(--color-text);line-height:1}.k-analytics-chart-container{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;margin-bottom:1.5rem;box-shadow:var(--shadow)}.k-analytics-chart-container h3{font-size:.875rem;font-weight:600;color:var(--color-text);margin:0 0 1rem}.k-analytics-chart-container canvas{max-height:300px}.k-analytics-empty{background:var(--color-background);border-radius:var(--rounded);padding:3rem;text-align:center;box-shadow:var(--shadow)}.k-analytics-empty p{margin:0;color:var(--color-text-light)}.k-analytics-list{list-style:none;margin:0;padding:0}.k-analytics-list li{display:flex;justify-content:space-between;padding:.375rem 0;border-bottom:1px solid var(--color-border);font-size:.875rem}.k-analytics-list li:last-child{border-bottom:none}.k-analytics-list-label{color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:1rem}.k-analytics-list-value{font-weight:600;color:var(--color-text);flex-shrink:0} diff --git a/public/site/plugins/analytics/index.js b/public/site/plugins/analytics/index.js index bd6292a..cceb074 100644 --- a/public/site/plugins/analytics/index.js +++ b/public/site/plugins/analytics/index.js @@ -1,18 +1,18 @@ -var Rl=Object.defineProperty;var El=(X,N,q)=>N in X?Rl(X,N,{enumerable:!0,configurable:!0,writable:!0,value:q}):X[N]=q;var S=(X,N,q)=>El(X,typeof N!="symbol"?N+"":N,q);(function(){"use strict";/*! +var Ul=Object.defineProperty;var Xl=(G,H,tt)=>H in G?Ul(G,H,{enumerable:!0,configurable:!0,writable:!0,value:tt}):G[H]=tt;var D=(G,H,tt)=>Xl(G,typeof H!="symbol"?H+"":H,tt);(function(){"use strict";/*! * @kurkle/color v0.3.4 * https://github.com/kurkle/color#readme * (c) 2024 Jukka Kurkela * Released under the MIT License - */function X(i){return i+.5|0}const N=(i,t,e)=>Math.max(Math.min(i,e),t);function q(i){return N(X(i*2.55),0,255)}function lt(i){return N(X(i*255),0,255)}function st(i){return N(X(i/2.55)/100,0,1)}function mi(i){return N(X(i*100),0,100)}const $={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},Le=[..."0123456789ABCDEF"],bn=i=>Le[i&15],_n=i=>Le[(i&240)>>4]+Le[i&15],Jt=i=>(i&240)>>4===(i&15),yn=i=>Jt(i.r)&&Jt(i.g)&&Jt(i.b)&&Jt(i.a);function xn(i){var t=i.length,e;return i[0]==="#"&&(t===4||t===5?e={r:255&$[i[1]]*17,g:255&$[i[2]]*17,b:255&$[i[3]]*17,a:t===5?$[i[4]]*17:255}:(t===7||t===9)&&(e={r:$[i[1]]<<4|$[i[2]],g:$[i[3]]<<4|$[i[4]],b:$[i[5]]<<4|$[i[6]],a:t===9?$[i[7]]<<4|$[i[8]]:255})),e}const vn=(i,t)=>i<255?t(i):"";function kn(i){var t=yn(i)?bn:_n;return i?"#"+t(i.r)+t(i.g)+t(i.b)+vn(i.a,t):void 0}const wn=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function bi(i,t,e){const s=t*Math.min(e,1-e),n=(o,r=(o+i/30)%12)=>e-s*Math.max(Math.min(r-3,9-r,1),-1);return[n(0),n(8),n(4)]}function Mn(i,t,e){const s=(n,o=(n+i/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[s(5),s(3),s(1)]}function Sn(i,t,e){const s=bi(i,1,.5);let n;for(t+e>1&&(n=1/(t+e),t*=n,e*=n),n=0;n<3;n++)s[n]*=1-t-e,s[n]+=t;return s}function Dn(i,t,e,s,n){return i===n?(t-e)/s+(t.5?h/(2-o-r):h/(o+r),l=Dn(e,s,n,h,o),l=l*60+.5),[l|0,c||0,a]}function Re(i,t,e,s){return(Array.isArray(t)?i(t[0],t[1],t[2]):i(t,e,s)).map(lt)}function Ee(i,t,e){return Re(bi,i,t,e)}function Pn(i,t,e){return Re(Sn,i,t,e)}function Cn(i,t,e){return Re(Mn,i,t,e)}function _i(i){return(i%360+360)%360}function On(i){const t=wn.exec(i);let e=255,s;if(!t)return;t[5]!==s&&(e=t[6]?q(+t[5]):lt(+t[5]));const n=_i(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?s=Pn(n,o,r):t[1]==="hsv"?s=Cn(n,o,r):s=Ee(n,o,r),{r:s[0],g:s[1],b:s[2],a:e}}function Tn(i,t){var e=Fe(i);e[0]=_i(e[0]+t),e=Ee(e),i.r=e[0],i.g=e[1],i.b=e[2]}function An(i){if(!i)return;const t=Fe(i),e=t[0],s=mi(t[1]),n=mi(t[2]);return i.a<255?`hsla(${e}, ${s}%, ${n}%, ${st(i.a)})`:`hsl(${e}, ${s}%, ${n}%)`}const yi={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},xi={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function In(){const i={},t=Object.keys(xi),e=Object.keys(yi);let s,n,o,r,a;for(s=0;s>16&255,o>>8&255,o&255]}return i}let te;function Ln(i){te||(te=In(),te.transparent=[0,0,0,0]);const t=te[i.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const Fn=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function Rn(i){const t=Fn.exec(i);let e=255,s,n,o;if(t){if(t[7]!==s){const r=+t[7];e=t[8]?q(r):N(r*255,0,255)}return s=+t[1],n=+t[3],o=+t[5],s=255&(t[2]?q(s):N(s,0,255)),n=255&(t[4]?q(n):N(n,0,255)),o=255&(t[6]?q(o):N(o,0,255)),{r:s,g:n,b:o,a:e}}}function En(i){return i&&(i.a<255?`rgba(${i.r}, ${i.g}, ${i.b}, ${st(i.a)})`:`rgb(${i.r}, ${i.g}, ${i.b})`)}const ze=i=>i<=.0031308?i*12.92:Math.pow(i,1/2.4)*1.055-.055,Pt=i=>i<=.04045?i/12.92:Math.pow((i+.055)/1.055,2.4);function zn(i,t,e){const s=Pt(st(i.r)),n=Pt(st(i.g)),o=Pt(st(i.b));return{r:lt(ze(s+e*(Pt(st(t.r))-s))),g:lt(ze(n+e*(Pt(st(t.g))-n))),b:lt(ze(o+e*(Pt(st(t.b))-o))),a:i.a+e*(t.a-i.a)}}function ee(i,t,e){if(i){let s=Fe(i);s[t]=Math.max(0,Math.min(s[t]+s[t]*e,t===0?360:1)),s=Ee(s),i.r=s[0],i.g=s[1],i.b=s[2]}}function vi(i,t){return i&&Object.assign(t||{},i)}function ki(i){var t={r:0,g:0,b:0,a:255};return Array.isArray(i)?i.length>=3&&(t={r:i[0],g:i[1],b:i[2],a:255},i.length>3&&(t.a=lt(i[3]))):(t=vi(i,{r:0,g:0,b:0,a:1}),t.a=lt(t.a)),t}function Bn(i){return i.charAt(0)==="r"?Rn(i):On(i)}class Ft{constructor(t){if(t instanceof Ft)return t;const e=typeof t;let s;e==="object"?s=ki(t):e==="string"&&(s=xn(t)||Ln(t)||Bn(t)),this._rgb=s,this._valid=!!s}get valid(){return this._valid}get rgb(){var t=vi(this._rgb);return t&&(t.a=st(t.a)),t}set rgb(t){this._rgb=ki(t)}rgbString(){return this._valid?En(this._rgb):void 0}hexString(){return this._valid?kn(this._rgb):void 0}hslString(){return this._valid?An(this._rgb):void 0}mix(t,e){if(t){const s=this.rgb,n=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=s.a-n.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,s.r=255&c*s.r+o*n.r+.5,s.g=255&c*s.g+o*n.g+.5,s.b=255&c*s.b+o*n.b+.5,s.a=r*s.a+(1-r)*n.a,this.rgb=s}return this}interpolate(t,e){return t&&(this._rgb=zn(this._rgb,t._rgb,e)),this}clone(){return new Ft(this.rgb)}alpha(t){return this._rgb.a=lt(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=X(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return ee(this._rgb,2,t),this}darken(t){return ee(this._rgb,2,-t),this}saturate(t){return ee(this._rgb,1,t),this}desaturate(t){return ee(this._rgb,1,-t),this}rotate(t){return Tn(this._rgb,t),this}}/*! + */function G(i){return i+.5|0}const H=(i,t,e)=>Math.max(Math.min(i,e),t);function tt(i){return H(G(i*2.55),0,255)}function dt(i){return H(G(i*255),0,255)}function at(i){return H(G(i/2.55)/100,0,1)}function yi(i){return H(G(i*100),0,100)}const X={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},He=[..."0123456789ABCDEF"],Mn=i=>He[i&15],Dn=i=>He[(i&240)>>4]+He[i&15],ne=i=>(i&240)>>4===(i&15),Pn=i=>ne(i.r)&&ne(i.g)&&ne(i.b)&&ne(i.a);function Cn(i){var t=i.length,e;return i[0]==="#"&&(t===4||t===5?e={r:255&X[i[1]]*17,g:255&X[i[2]]*17,b:255&X[i[3]]*17,a:t===5?X[i[4]]*17:255}:(t===7||t===9)&&(e={r:X[i[1]]<<4|X[i[2]],g:X[i[3]]<<4|X[i[4]],b:X[i[5]]<<4|X[i[6]],a:t===9?X[i[7]]<<4|X[i[8]]:255})),e}const On=(i,t)=>i<255?t(i):"";function Tn(i){var t=Pn(i)?Mn:Dn;return i?"#"+t(i.r)+t(i.g)+t(i.b)+On(i.a,t):void 0}const An=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function vi(i,t,e){const s=t*Math.min(e,1-e),n=(o,a=(o+i/30)%12)=>e-s*Math.max(Math.min(a-3,9-a,1),-1);return[n(0),n(8),n(4)]}function Ln(i,t,e){const s=(n,o=(n+i/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[s(5),s(3),s(1)]}function In(i,t,e){const s=vi(i,1,.5);let n;for(t+e>1&&(n=1/(t+e),t*=n,e*=n),n=0;n<3;n++)s[n]*=1-t-e,s[n]+=t;return s}function Fn(i,t,e,s,n){return i===n?(t-e)/s+(t.5?h/(2-o-a):h/(o+a),l=Fn(e,s,n,h,o),l=l*60+.5),[l|0,c||0,r]}function Ne(i,t,e,s){return(Array.isArray(t)?i(t[0],t[1],t[2]):i(t,e,s)).map(dt)}function Ve(i,t,e){return Ne(vi,i,t,e)}function Rn(i,t,e){return Ne(In,i,t,e)}function zn(i,t,e){return Ne(Ln,i,t,e)}function ki(i){return(i%360+360)%360}function En(i){const t=An.exec(i);let e=255,s;if(!t)return;t[5]!==s&&(e=t[6]?tt(+t[5]):dt(+t[5]));const n=ki(+t[2]),o=+t[3]/100,a=+t[4]/100;return t[1]==="hwb"?s=Rn(n,o,a):t[1]==="hsv"?s=zn(n,o,a):s=Ve(n,o,a),{r:s[0],g:s[1],b:s[2],a:e}}function Bn(i,t){var e=We(i);e[0]=ki(e[0]+t),e=Ve(e),i.r=e[0],i.g=e[1],i.b=e[2]}function Hn(i){if(!i)return;const t=We(i),e=t[0],s=yi(t[1]),n=yi(t[2]);return i.a<255?`hsla(${e}, ${s}%, ${n}%, ${at(i.a)})`:`hsl(${e}, ${s}%, ${n}%)`}const wi={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Si={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function Wn(){const i={},t=Object.keys(Si),e=Object.keys(wi);let s,n,o,a,r;for(s=0;s>16&255,o>>8&255,o&255]}return i}let oe;function Nn(i){oe||(oe=Wn(),oe.transparent=[0,0,0,0]);const t=oe[i.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const Vn=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function jn(i){const t=Vn.exec(i);let e=255,s,n,o;if(t){if(t[7]!==s){const a=+t[7];e=t[8]?tt(a):H(a*255,0,255)}return s=+t[1],n=+t[3],o=+t[5],s=255&(t[2]?tt(s):H(s,0,255)),n=255&(t[4]?tt(n):H(n,0,255)),o=255&(t[6]?tt(o):H(o,0,255)),{r:s,g:n,b:o,a:e}}}function $n(i){return i&&(i.a<255?`rgba(${i.r}, ${i.g}, ${i.b}, ${at(i.a)})`:`rgb(${i.r}, ${i.g}, ${i.b})`)}const je=i=>i<=.0031308?i*12.92:Math.pow(i,1/2.4)*1.055-.055,Ot=i=>i<=.04045?i/12.92:Math.pow((i+.055)/1.055,2.4);function Yn(i,t,e){const s=Ot(at(i.r)),n=Ot(at(i.g)),o=Ot(at(i.b));return{r:dt(je(s+e*(Ot(at(t.r))-s))),g:dt(je(n+e*(Ot(at(t.g))-n))),b:dt(je(o+e*(Ot(at(t.b))-o))),a:i.a+e*(t.a-i.a)}}function ae(i,t,e){if(i){let s=We(i);s[t]=Math.max(0,Math.min(s[t]+s[t]*e,t===0?360:1)),s=Ve(s),i.r=s[0],i.g=s[1],i.b=s[2]}}function Mi(i,t){return i&&Object.assign(t||{},i)}function Di(i){var t={r:0,g:0,b:0,a:255};return Array.isArray(i)?i.length>=3&&(t={r:i[0],g:i[1],b:i[2],a:255},i.length>3&&(t.a=dt(i[3]))):(t=Mi(i,{r:0,g:0,b:0,a:1}),t.a=dt(t.a)),t}function Un(i){return i.charAt(0)==="r"?jn(i):En(i)}class Bt{constructor(t){if(t instanceof Bt)return t;const e=typeof t;let s;e==="object"?s=Di(t):e==="string"&&(s=Cn(t)||Nn(t)||Un(t)),this._rgb=s,this._valid=!!s}get valid(){return this._valid}get rgb(){var t=Mi(this._rgb);return t&&(t.a=at(t.a)),t}set rgb(t){this._rgb=Di(t)}rgbString(){return this._valid?$n(this._rgb):void 0}hexString(){return this._valid?Tn(this._rgb):void 0}hslString(){return this._valid?Hn(this._rgb):void 0}mix(t,e){if(t){const s=this.rgb,n=t.rgb;let o;const a=e===o?.5:e,r=2*a-1,l=s.a-n.a,c=((r*l===-1?r:(r+l)/(1+r*l))+1)/2;o=1-c,s.r=255&c*s.r+o*n.r+.5,s.g=255&c*s.g+o*n.g+.5,s.b=255&c*s.b+o*n.b+.5,s.a=a*s.a+(1-a)*n.a,this.rgb=s}return this}interpolate(t,e){return t&&(this._rgb=Yn(this._rgb,t._rgb,e)),this}clone(){return new Bt(this.rgb)}alpha(t){return this._rgb.a=dt(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=G(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return ae(this._rgb,2,t),this}darken(t){return ae(this._rgb,2,-t),this}saturate(t){return ae(this._rgb,1,t),this}desaturate(t){return ae(this._rgb,1,-t),this}rotate(t){return Bn(this._rgb,t),this}}/*! * Chart.js v4.5.1 * https://www.chartjs.org * (c) 2025 Chart.js Contributors * Released under the MIT License - */function nt(){}const Nn=(()=>{let i=0;return()=>i++})();function T(i){return i==null}function z(i){if(Array.isArray&&Array.isArray(i))return!0;const t=Object.prototype.toString.call(i);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function D(i){return i!==null&&Object.prototype.toString.call(i)==="[object Object]"}function V(i){return(typeof i=="number"||i instanceof Number)&&isFinite(+i)}function G(i,t){return V(i)?i:t}function O(i,t){return typeof i>"u"?t:i}const Vn=(i,t)=>typeof i=="string"&&i.endsWith("%")?parseFloat(i)/100*t:+i;function A(i,t,e){if(i&&typeof i.call=="function")return i.apply(e,t)}function C(i,t,e,s){let n,o,r;if(z(i))for(o=i.length,n=0;ni,x:i=>i.x,y:i=>i.y};function Wn(i){const t=i.split("."),e=[];let s="";for(const n of t)s+=n,s.endsWith("\\")?s=s.slice(0,-1)+".":(e.push(s),s="");return e}function $n(i){const t=Wn(i);return e=>{for(const s of t){if(s==="")break;e=e&&e[s]}return e}}function ne(i,t){return(Mi[t]||(Mi[t]=$n(t)))(i)}function Be(i){return i.charAt(0).toUpperCase()+i.slice(1)}const oe=i=>typeof i<"u",ct=i=>typeof i=="function",Si=(i,t)=>{if(i.size!==t.size)return!1;for(const e of i)if(!t.has(e))return!1;return!0};function Yn(i){return i.type==="mouseup"||i.type==="click"||i.type==="contextmenu"}const j=Math.PI,Z=2*j,Un=Z+j,re=Number.POSITIVE_INFINITY,Xn=j/180,K=j/2,gt=j/4,Di=j*2/3,Pi=Math.log10,Ct=Math.sign;function zt(i,t,e){return Math.abs(i-t)n-o).pop(),t}function qn(i){return typeof i=="symbol"||typeof i=="object"&&i!==null&&!(Symbol.toPrimitive in i||"toString"in i||"valueOf"in i)}function Bt(i){return!qn(i)&&!isNaN(parseFloat(i))&&isFinite(i)}function Gn(i,t){const e=Math.round(i);return e-t<=i&&e+t>=i}function Zn(i,t,e){let s,n,o;for(s=0,n=i.length;sl&&c=Math.min(t,e)-s&&i<=Math.max(t,e)+s}function Ve(i,t,e){e=e||(r=>i[r]1;)o=n+s>>1,e(o)?n=o:s=o;return{lo:n,hi:s}}const mt=(i,t,e,s)=>Ve(i,e,s?n=>{const o=i[n][t];return oi[n][t]Ve(i,e,s=>i[s][t]>=e);function so(i,t,e){let s=0,n=i.length;for(;ss&&i[n-1]>e;)n--;return s>0||n{const s="_onData"+Be(e),n=i[e];Object.defineProperty(i,e,{configurable:!0,enumerable:!1,value(...o){const r=n.apply(this,o);return i._chartjs.listeners.forEach(a=>{typeof a[s]=="function"&&a[s](...o)}),r}})})}function Li(i,t){const e=i._chartjs;if(!e)return;const s=e.listeners,n=s.indexOf(t);n!==-1&&s.splice(n,1),!(s.length>0)&&(Ii.forEach(o=>{delete i[o]}),delete i._chartjs)}function oo(i){const t=new Set(i);return t.size===i.length?i:Array.from(t)}const Fi=(function(){return typeof window>"u"?function(i){return i()}:window.requestAnimationFrame})();function Ri(i,t){let e=[],s=!1;return function(...n){e=n,s||(s=!0,Fi.call(window,()=>{s=!1,i.apply(t,e)}))}}function ro(i,t){let e;return function(...s){return t?(clearTimeout(e),e=setTimeout(i,t,s)):i.apply(this,s),t}}const ao=i=>i==="start"?"left":i==="end"?"right":"center",Ei=(i,t,e)=>i==="start"?t:i==="end"?e:(t+e)/2;function lo(i,t,e){const s=t.length;let n=0,o=s;if(i._sorted){const{iScale:r,vScale:a,_parsed:l}=i,c=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null,h=r.axis,{min:f,max:d,minDefined:u,maxDefined:m}=r.getUserBounds();if(u){if(n=Math.min(mt(l,h,f).lo,e?s:mt(t,h,r.getPixelForValue(f)).lo),c){const g=l.slice(0,n+1).reverse().findIndex(p=>!T(p[a.axis]));n-=Math.max(0,g)}n=Y(n,0,s-1)}if(m){let g=Math.max(mt(l,r.axis,d,!0).hi+1,e?0:mt(t,h,r.getPixelForValue(d),!0).hi+1);if(c){const p=l.slice(g-1).findIndex(b=>!T(b[a.axis]));g+=Math.max(0,p)}o=Y(g,n,s)-n}else o=s-n}return{start:n,count:o}}function co(i){const{xScale:t,yScale:e,_scaleRanges:s}=i,n={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!s)return i._scaleRanges=n,!0;const o=s.xmin!==t.min||s.xmax!==t.max||s.ymin!==e.min||s.ymax!==e.max;return Object.assign(s,n),o}const ae=i=>i===0||i===1,zi=(i,t,e)=>-(Math.pow(2,10*(i-=1))*Math.sin((i-t)*Z/e)),Bi=(i,t,e)=>Math.pow(2,-10*i)*Math.sin((i-t)*Z/e)+1,Nt={linear:i=>i,easeInQuad:i=>i*i,easeOutQuad:i=>-i*(i-2),easeInOutQuad:i=>(i/=.5)<1?.5*i*i:-.5*(--i*(i-2)-1),easeInCubic:i=>i*i*i,easeOutCubic:i=>(i-=1)*i*i+1,easeInOutCubic:i=>(i/=.5)<1?.5*i*i*i:.5*((i-=2)*i*i+2),easeInQuart:i=>i*i*i*i,easeOutQuart:i=>-((i-=1)*i*i*i-1),easeInOutQuart:i=>(i/=.5)<1?.5*i*i*i*i:-.5*((i-=2)*i*i*i-2),easeInQuint:i=>i*i*i*i*i,easeOutQuint:i=>(i-=1)*i*i*i*i+1,easeInOutQuint:i=>(i/=.5)<1?.5*i*i*i*i*i:.5*((i-=2)*i*i*i*i+2),easeInSine:i=>-Math.cos(i*K)+1,easeOutSine:i=>Math.sin(i*K),easeInOutSine:i=>-.5*(Math.cos(j*i)-1),easeInExpo:i=>i===0?0:Math.pow(2,10*(i-1)),easeOutExpo:i=>i===1?1:-Math.pow(2,-10*i)+1,easeInOutExpo:i=>ae(i)?i:i<.5?.5*Math.pow(2,10*(i*2-1)):.5*(-Math.pow(2,-10*(i*2-1))+2),easeInCirc:i=>i>=1?i:-(Math.sqrt(1-i*i)-1),easeOutCirc:i=>Math.sqrt(1-(i-=1)*i),easeInOutCirc:i=>(i/=.5)<1?-.5*(Math.sqrt(1-i*i)-1):.5*(Math.sqrt(1-(i-=2)*i)+1),easeInElastic:i=>ae(i)?i:zi(i,.075,.3),easeOutElastic:i=>ae(i)?i:Bi(i,.075,.3),easeInOutElastic(i){return ae(i)?i:i<.5?.5*zi(i*2,.1125,.45):.5+.5*Bi(i*2-1,.1125,.45)},easeInBack(i){return i*i*((1.70158+1)*i-1.70158)},easeOutBack(i){return(i-=1)*i*((1.70158+1)*i+1.70158)+1},easeInOutBack(i){let t=1.70158;return(i/=.5)<1?.5*(i*i*(((t*=1.525)+1)*i-t)):.5*((i-=2)*i*(((t*=1.525)+1)*i+t)+2)},easeInBounce:i=>1-Nt.easeOutBounce(1-i),easeOutBounce(i){return i<1/2.75?7.5625*i*i:i<2/2.75?7.5625*(i-=1.5/2.75)*i+.75:i<2.5/2.75?7.5625*(i-=2.25/2.75)*i+.9375:7.5625*(i-=2.625/2.75)*i+.984375},easeInOutBounce:i=>i<.5?Nt.easeInBounce(i*2)*.5:Nt.easeOutBounce(i*2-1)*.5+.5};function je(i){if(i&&typeof i=="object"){const t=i.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Ni(i){return je(i)?i:new Ft(i)}function He(i){return je(i)?i:new Ft(i).saturate(.5).darken(.1).hexString()}const ho=["x","y","borderWidth","radius","tension"],fo=["color","borderColor","backgroundColor"];function uo(i){i.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),i.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),i.set("animations",{colors:{type:"color",properties:fo},numbers:{type:"number",properties:ho}}),i.describe("animations",{_fallback:"animation"}),i.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:t=>t|0}}}})}function go(i){i.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Vi=new Map;function po(i,t){t=t||{};const e=i+JSON.stringify(t);let s=Vi.get(e);return s||(s=new Intl.NumberFormat(i,t),Vi.set(e,s)),s}function ji(i,t,e){return po(t,e).format(i)}const mo={values(i){return z(i)?i:""+i},numeric(i,t,e){if(i===0)return"0";const s=this.chart.options.locale;let n,o=i;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(n="scientific"),o=bo(i,e)}const r=Pi(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:n,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),ji(i,s,l)}};function bo(i,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&i!==Math.floor(i)&&(e=i-Math.floor(i)),e}var Hi={formatters:mo};function _o(i){i.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width: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:Hi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),i.route("scale.ticks","color","","color"),i.route("scale.grid","color","","borderColor"),i.route("scale.border","color","","borderColor"),i.route("scale.title","color","","color"),i.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),i.describe("scales",{_fallback:"scale"}),i.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const bt=Object.create(null),We=Object.create(null);function Vt(i,t){if(!t)return i;const e=t.split(".");for(let s=0,n=e.length;ss.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=(s,n)=>He(n.backgroundColor),this.hoverBorderColor=(s,n)=>He(n.borderColor),this.hoverColor=(s,n)=>He(n.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(t),this.apply(e)}set(t,e){return $e(this,t,e)}get(t){return Vt(this,t)}describe(t,e){return $e(We,t,e)}override(t,e){return $e(bt,t,e)}route(t,e,s,n){const o=Vt(this,t),r=Vt(this,s),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[n];return D(l)?Object.assign({},c,l):O(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var R=new yo({_scriptable:i=>!i.startsWith("on"),_indexable:i=>i!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[uo,go,_o]);function xo(i){return!i||T(i.size)||T(i.family)?null:(i.style?i.style+" ":"")+(i.weight?i.weight+" ":"")+i.size+"px "+i.family}function Wi(i,t,e,s,n){let o=t[n];return o||(o=t[n]=i.measureText(n).width,e.push(n)),o>s&&(s=o),s}function _t(i,t,e){const s=i.currentDevicePixelRatio,n=e!==0?Math.max(e/2,.5):0;return Math.round((t-n)*s)/s+n}function $i(i,t){!t&&!i||(t=t||i.getContext("2d"),t.save(),t.resetTransform(),t.clearRect(0,0,i.width,i.height),t.restore())}function Ye(i,t,e,s){vo(i,t,e,s)}function vo(i,t,e,s,n){let o,r,a,l,c,h,f,d;const u=t.pointStyle,m=t.rotation,g=t.radius;let p=(m||0)*Xn;if(u&&typeof u=="object"&&(o=u.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){i.save(),i.translate(e,s),i.rotate(p),i.drawImage(u,-u.width/2,-u.height/2,u.width,u.height),i.restore();return}if(!(isNaN(g)||g<=0)){switch(i.beginPath(),u){default:i.arc(e,s,g,0,Z),i.closePath();break;case"triangle":h=g,i.moveTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Di,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Di,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),i.closePath();break;case"rectRounded":c=g*.516,l=g-c,r=Math.cos(p+gt)*l,f=Math.cos(p+gt)*l,a=Math.sin(p+gt)*l,d=Math.sin(p+gt)*l,i.arc(e-f,s-a,c,p-j,p-K),i.arc(e+d,s-r,c,p-K,p),i.arc(e+f,s+a,c,p,p+K),i.arc(e-d,s+r,c,p+K,p+j),i.closePath();break;case"rect":if(!m){l=Math.SQRT1_2*g,h=l,i.rect(e-h,s-l,2*h,2*l);break}p+=gt;case"rectRot":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+d,s-r),i.lineTo(e+f,s+a),i.lineTo(e-d,s+r),i.closePath();break;case"crossRot":p+=gt;case"cross":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r);break;case"star":f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r),p+=gt,f=Math.cos(p)*g,r=Math.cos(p)*g,a=Math.sin(p)*g,d=Math.sin(p)*g,i.moveTo(e-f,s-a),i.lineTo(e+f,s+a),i.moveTo(e+d,s-r),i.lineTo(e-d,s+r);break;case"line":r=Math.cos(p)*g,a=Math.sin(p)*g,i.moveTo(e-r,s-a),i.lineTo(e+r,s+a);break;case"dash":i.moveTo(e,s),i.lineTo(e+Math.cos(p)*g,s+Math.sin(p)*g);break;case!1:i.closePath();break}i.fill(),t.borderWidth>0&&i.stroke()}}function jt(i,t,e){return e=e||.5,!t||i&&i.x>t.left-e&&i.xt.top-e&&i.y0&&o.strokeColor!=="";let l,c;for(i.save(),i.font=n.string,Mo(i,o),l=0;l+i||0;function Xi(i,t){const e={},s=D(t),n=s?Object.keys(t):t,o=D(i)?s?r=>O(i[r],i[t[r]]):r=>i[r]:()=>i;for(const r of n)e[r]=To(o(r));return e}function Ao(i){return Xi(i,{top:"y",right:"x",bottom:"y",left:"x"})}function le(i){return Xi(i,["topLeft","topRight","bottomLeft","bottomRight"])}function ht(i){const t=Ao(i);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function J(i,t){i=i||{},t=t||R.font;let e=O(i.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let s=O(i.style,t.style);s&&!(""+s).match(Co)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:O(i.family,t.family),lineHeight:Oo(O(i.lineHeight,t.lineHeight),e),size:e,style:s,weight:O(i.weight,t.weight),string:""};return n.string=xo(n),n}function ce(i,t,e,s){let n,o,r;for(n=0,o=i.length;ne&&a===0?0:a+l;return{min:r(s,-Math.abs(o)),max:r(n,o)}}function yt(i,t){return Object.assign(Object.create(i),t)}function Ke(i,t=[""],e,s,n=()=>i[0]){const o=e||i;typeof s>"u"&&(s=Qi("_fallback",i));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:i,_rootScopes:o,_fallback:s,_getTarget:n,override:a=>Ke([a,...i],t,o,s)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete i[0][l],!0},get(a,l){return qi(a,l,()=>Vo(l,t,i,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(i[0])},has(a,l){return Ji(a).includes(l)},ownKeys(a){return Ji(a)},set(a,l,c){const h=a._storage||(a._storage=n());return a[l]=h[l]=c,delete a._keys,!0}})}function Ot(i,t,e,s){const n={_cacheable:!1,_proxy:i,_context:t,_subProxy:e,_stack:new Set,_descriptors:Ki(i,s),setContext:o=>Ot(i,o,e,s),override:o=>Ot(i.override(o),t,e,s)};return new Proxy(n,{deleteProperty(o,r){return delete o[r],delete i[r],!0},get(o,r,a){return qi(o,r,()=>Fo(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(i,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(i,r)},getPrototypeOf(){return Reflect.getPrototypeOf(i)},has(o,r){return Reflect.has(i,r)},ownKeys(){return Reflect.ownKeys(i)},set(o,r,a){return i[r]=a,delete o[r],!0}})}function Ki(i,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:s=t.indexable,_allKeys:n=t.allKeys}=i;return{allKeys:n,scriptable:e,indexable:s,isScriptable:ct(e)?e:()=>e,isIndexable:ct(s)?s:()=>s}}const Lo=(i,t)=>i?i+Be(t):t,qe=(i,t)=>D(t)&&i!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function qi(i,t,e){if(Object.prototype.hasOwnProperty.call(i,t)||t==="constructor")return i[t];const s=e();return i[t]=s,s}function Fo(i,t,e){const{_proxy:s,_context:n,_subProxy:o,_descriptors:r}=i;let a=s[t];return ct(a)&&r.isScriptable(t)&&(a=Ro(t,a,i,e)),z(a)&&a.length&&(a=Eo(t,a,i,r.isIndexable)),qe(t,a)&&(a=Ot(a,n,o&&o[t],r)),a}function Ro(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_stack:a}=e;if(a.has(i))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+i);a.add(i);let l=t(o,r||s);return a.delete(i),qe(i,l)&&(l=Ge(n._scopes,n,i,l)),l}function Eo(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&s(i))return t[o.index%t.length];if(D(t[0])){const l=t,c=n._scopes.filter(h=>h!==l);t=[];for(const h of l){const f=Ge(c,n,i,h);t.push(Ot(f,o,r&&r[i],a))}}return t}function Gi(i,t,e){return ct(i)?i(t,e):i}const zo=(i,t)=>i===!0?t:typeof i=="string"?ne(t,i):void 0;function Bo(i,t,e,s,n){for(const o of t){const r=zo(e,o);if(r){i.add(r);const a=Gi(r._fallback,e,n);if(typeof a<"u"&&a!==e&&a!==s)return a}else if(r===!1&&typeof s<"u"&&e!==s)return null}return!1}function Ge(i,t,e,s){const n=t._rootScopes,o=Gi(t._fallback,e,s),r=[...i,...n],a=new Set;a.add(s);let l=Zi(a,r,e,o||e,s);return l===null||typeof o<"u"&&o!==e&&(l=Zi(a,r,o,l,s),l===null)?!1:Ke(Array.from(a),[""],n,o,()=>No(t,e,s))}function Zi(i,t,e,s,n){for(;e;)e=Bo(i,t,e,s,n);return e}function No(i,t,e){const s=i._getTarget();t in s||(s[t]={});const n=s[t];return z(n)&&D(e)?e:n||{}}function Vo(i,t,e,s){let n;for(const o of t)if(n=Qi(Lo(o,i),e),typeof n<"u")return qe(i,n)?Ge(e,s,i,n):n}function Qi(i,t){for(const e of t){if(!e)continue;const s=e[i];if(typeof s<"u")return s}}function Ji(i){let t=i._keys;return t||(t=i._keys=jo(i._scopes)),t}function jo(i){const t=new Set;for(const e of i)for(const s of Object.keys(e).filter(n=>!n.startsWith("_")))t.add(s);return Array.from(t)}const Ho=Number.EPSILON||1e-14,Tt=(i,t)=>ti==="x"?"y":"x";function Wo(i,t,e,s){const n=i.skip?t:i,o=t,r=e.skip?t:e,a=Ne(o,n),l=Ne(r,o);let c=a/(a+l),h=l/(a+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const f=s*c,d=s*h;return{previous:{x:o.x-f*(r.x-n.x),y:o.y-f*(r.y-n.y)},next:{x:o.x+d*(r.x-n.x),y:o.y+d*(r.y-n.y)}}}function $o(i,t,e){const s=i.length;let n,o,r,a,l,c=Tt(i,0);for(let h=0;h!c.skip)),t.cubicInterpolationMode==="monotone")Uo(i,n);else{let c=s?i[i.length-1]:i[0];for(o=0,r=i.length;oi.ownerDocument.defaultView.getComputedStyle(i,null);function qo(i,t){return de(i).getPropertyValue(t)}const Go=["top","right","bottom","left"];function xt(i,t,e){const s={};e=e?"-"+e:"";for(let n=0;n<4;n++){const o=Go[n];s[o]=parseFloat(i[t+"-"+o+e])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const Zo=(i,t,e)=>(i>0||t>0)&&(!e||!e.shadowRoot);function Qo(i,t){const e=i.touches,s=e&&e.length?e[0]:i,{offsetX:n,offsetY:o}=s;let r=!1,a,l;if(Zo(n,o,i.target))a=n,l=o;else{const c=t.getBoundingClientRect();a=s.clientX-c.left,l=s.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function vt(i,t){if("native"in i)return i;const{canvas:e,currentDevicePixelRatio:s}=t,n=de(e),o=n.boxSizing==="border-box",r=xt(n,"padding"),a=xt(n,"border","width"),{x:l,y:c,box:h}=Qo(i,e),f=r.left+(h&&a.left),d=r.top+(h&&a.top);let{width:u,height:m}=t;return o&&(u-=r.width+a.width,m-=r.height+a.height),{x:Math.round((l-f)/u*e.width/s),y:Math.round((c-d)/m*e.height/s)}}function Jo(i,t,e){let s,n;if(t===void 0||e===void 0){const o=i&&Qe(i);if(!o)t=i.clientWidth,e=i.clientHeight;else{const r=o.getBoundingClientRect(),a=de(o),l=xt(a,"border","width"),c=xt(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,s=fe(a.maxWidth,o,"clientWidth"),n=fe(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:s||re,maxHeight:n||re}}const ft=i=>Math.round(i*10)/10;function tr(i,t,e,s){const n=de(i),o=xt(n,"margin"),r=fe(n.maxWidth,i,"clientWidth")||re,a=fe(n.maxHeight,i,"clientHeight")||re,l=Jo(i,t,e);let{width:c,height:h}=l;if(n.boxSizing==="content-box"){const d=xt(n,"border","width"),u=xt(n,"padding");c-=u.width+d.width,h-=u.height+d.height}return c=Math.max(0,c-o.width),h=Math.max(0,s?c/s:h-o.height),c=ft(Math.min(c,r,l.maxWidth)),h=ft(Math.min(h,a,l.maxHeight)),c&&!h&&(h=ft(c/2)),(t!==void 0||e!==void 0)&&s&&l.height&&h>l.height&&(h=l.height,c=ft(Math.floor(h*s))),{width:c,height:h}}function es(i,t,e){const s=t||1,n=ft(i.height*s),o=ft(i.width*s);i.height=ft(i.height),i.width=ft(i.width);const r=i.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${i.height}px`,r.style.width=`${i.width}px`),i.currentDevicePixelRatio!==s||r.height!==n||r.width!==o?(i.currentDevicePixelRatio=s,r.height=n,r.width=o,i.ctx.setTransform(s,0,0,s,0,0),!0):!1}const er=(function(){let i=!1;try{const t={get passive(){return i=!0,!1}};Ze()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return i})();function is(i,t){const e=qo(i,t),s=e&&e.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function kt(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:i.y+e*(t.y-i.y)}}function ir(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:s==="middle"?e<.5?i.y:t.y:s==="after"?e<1?i.y:t.y:e>0?t.y:i.y}}function sr(i,t,e,s){const n={x:i.cp2x,y:i.cp2y},o={x:t.cp1x,y:t.cp1y},r=kt(i,n,e),a=kt(n,o,e),l=kt(o,t,e),c=kt(r,a,e),h=kt(a,l,e);return kt(c,h,e)}const nr=function(i,t){return{x(e){return i+i+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,s){return e-s},leftForLtr(e,s){return e-s}}},or=function(){return{x(i){return i},setWidth(i){},textAlign(i){return i},xPlus(i,t){return i+t},leftForLtr(i,t){return i}}};function Je(i,t,e){return i?nr(t,e):or()}function rr(i,t){let e,s;(t==="ltr"||t==="rtl")&&(e=i.canvas.style,s=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),i.prevTextDirection=s)}function ar(i,t){t!==void 0&&(delete i.prevTextDirection,i.canvas.style.setProperty("direction",t[0],t[1]))}function ss(i){return i==="angle"?{between:Ti,compare:to,normalize:Q}:{between:Ai,compare:(t,e)=>t-e,normalize:t=>t}}function ns({start:i,end:t,count:e,loop:s,style:n}){return{start:i%e,end:t%e,loop:s&&(t-i+1)%e===0,style:n}}function lr(i,t,e){const{property:s,start:n,end:o}=e,{between:r,normalize:a}=ss(s),l=t.length;let{start:c,end:h,loop:f}=i,d,u;if(f){for(c+=l,h+=l,d=0,u=l;dl(n,v,b)&&a(n,v)!==0,_=()=>a(o,b)===0||l(o,v,b),M=()=>g||x(),k=()=>!g||_();for(let w=h,P=h;w<=f;++w)y=t[w%r],!y.skip&&(b=c(y[s]),b!==v&&(g=l(b,n,o),p===null&&M()&&(p=a(b,n)===0?w:P),p!==null&&k()&&(m.push(ns({start:p,end:w,loop:d,count:r,style:u})),p=null),P=w,v=b));return p!==null&&m.push(ns({start:p,end:f,loop:d,count:r,style:u})),m}function rs(i,t){const e=[],s=i.segments;for(let n=0;nn&&i[o%t].skip;)o--;return o%=t,{start:n,end:o}}function hr(i,t,e,s){const n=i.length,o=[];let r=t,a=i[t],l;for(l=t+1;l<=e;++l){const c=i[l%n];c.skip||c.stop?a.skip||(s=!1,o.push({start:t%n,end:(l-1)%n,loop:s}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%n,end:r%n,loop:s}),o}function fr(i,t){const e=i.points,s=i.options.spanGaps,n=e.length;if(!n)return[];const o=!!i._loop,{start:r,end:a}=cr(e,n,o,s);if(s===!0)return as(i,[{start:r,end:a,loop:o}],e,t);const l=a{let i=0;return()=>i++})();function I(i){return i==null}function E(i){if(Array.isArray&&Array.isArray(i))return!0;const t=Object.prototype.toString.call(i);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function O(i){return i!==null&&Object.prototype.toString.call(i)==="[object Object]"}function W(i){return(typeof i=="number"||i instanceof Number)&&isFinite(+i)}function et(i,t){return W(i)?i:t}function C(i,t){return typeof i>"u"?t:i}const Kn=(i,t)=>typeof i=="string"&&i.endsWith("%")?parseFloat(i)/100*t:+i;function F(i,t,e){if(i&&typeof i.call=="function")return i.apply(e,t)}function L(i,t,e,s){let n,o,a;if(E(i))for(o=i.length,n=0;ni,x:i=>i.x,y:i=>i.y};function Zn(i){const t=i.split("."),e=[];let s="";for(const n of t)s+=n,s.endsWith("\\")?s=s.slice(0,-1)+".":(e.push(s),s="");return e}function Qn(i){const t=Zn(i);return e=>{for(const s of t){if(s==="")break;e=e&&e[s]}return e}}function ce(i,t){return(Ci[t]||(Ci[t]=Qn(t)))(i)}function $e(i){return i.charAt(0).toUpperCase()+i.slice(1)}const he=i=>typeof i<"u",ft=i=>typeof i=="function",Oi=(i,t)=>{if(i.size!==t.size)return!1;for(const e of i)if(!t.has(e))return!1;return!0};function Jn(i){return i.type==="mouseup"||i.type==="click"||i.type==="contextmenu"}const N=Math.PI,Z=2*N,to=Z+N,de=Number.POSITIVE_INFINITY,eo=N/180,Q=N/2,bt=N/4,Ti=N*2/3,Ai=Math.log10,Tt=Math.sign;function Nt(i,t,e){return Math.abs(i-t)n-o).pop(),t}function so(i){return typeof i=="symbol"||typeof i=="object"&&i!==null&&!(Symbol.toPrimitive in i||"toString"in i||"valueOf"in i)}function Vt(i){return!so(i)&&!isNaN(parseFloat(i))&&isFinite(i)}function no(i,t){const e=Math.round(i);return e-t<=i&&e+t>=i}function oo(i,t,e){let s,n,o;for(s=0,n=i.length;sl&&c=Math.min(t,e)-s&&i<=Math.max(t,e)+s}function Ue(i,t,e){e=e||(a=>i[a]1;)o=n+s>>1,e(o)?n=o:s=o;return{lo:n,hi:s}}const xt=(i,t,e,s)=>Ue(i,e,s?n=>{const o=i[n][t];return oi[n][t]Ue(i,e,s=>i[s][t]>=e);function fo(i,t,e){let s=0,n=i.length;for(;ss&&i[n-1]>e;)n--;return s>0||n{const s="_onData"+$e(e),n=i[e];Object.defineProperty(i,e,{configurable:!0,enumerable:!1,value(...o){const a=n.apply(this,o);return i._chartjs.listeners.forEach(r=>{typeof r[s]=="function"&&r[s](...o)}),a}})})}function zi(i,t){const e=i._chartjs;if(!e)return;const s=e.listeners,n=s.indexOf(t);n!==-1&&s.splice(n,1),!(s.length>0)&&(Ri.forEach(o=>{delete i[o]}),delete i._chartjs)}function go(i){const t=new Set(i);return t.size===i.length?i:Array.from(t)}const Ei=(function(){return typeof window>"u"?function(i){return i()}:window.requestAnimationFrame})();function Bi(i,t){let e=[],s=!1;return function(...n){e=n,s||(s=!0,Ei.call(window,()=>{s=!1,i.apply(t,e)}))}}function po(i,t){let e;return function(...s){return t?(clearTimeout(e),e=setTimeout(i,t,s)):i.apply(this,s),t}}const Hi=i=>i==="start"?"left":i==="end"?"right":"center",U=(i,t,e)=>i==="start"?t:i==="end"?e:(t+e)/2,mo=(i,t,e,s)=>i===(s?"left":"right")?e:i==="center"?(t+e)/2:t;function bo(i,t,e){const s=t.length;let n=0,o=s;if(i._sorted){const{iScale:a,vScale:r,_parsed:l}=i,c=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null,h=a.axis,{min:d,max:f,minDefined:u,maxDefined:m}=a.getUserBounds();if(u){if(n=Math.min(xt(l,h,d).lo,e?s:xt(t,h,a.getPixelForValue(d)).lo),c){const g=l.slice(0,n+1).reverse().findIndex(p=>!I(p[r.axis]));n-=Math.max(0,g)}n=K(n,0,s-1)}if(m){let g=Math.max(xt(l,a.axis,f,!0).hi+1,e?0:xt(t,h,a.getPixelForValue(f),!0).hi+1);if(c){const p=l.slice(g-1).findIndex(b=>!I(b[r.axis]));g+=Math.max(0,p)}o=K(g,n,s)-n}else o=s-n}return{start:n,count:o}}function _o(i){const{xScale:t,yScale:e,_scaleRanges:s}=i,n={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!s)return i._scaleRanges=n,!0;const o=s.xmin!==t.min||s.xmax!==t.max||s.ymin!==e.min||s.ymax!==e.max;return Object.assign(s,n),o}const fe=i=>i===0||i===1,Wi=(i,t,e)=>-(Math.pow(2,10*(i-=1))*Math.sin((i-t)*Z/e)),Ni=(i,t,e)=>Math.pow(2,-10*i)*Math.sin((i-t)*Z/e)+1,jt={linear:i=>i,easeInQuad:i=>i*i,easeOutQuad:i=>-i*(i-2),easeInOutQuad:i=>(i/=.5)<1?.5*i*i:-.5*(--i*(i-2)-1),easeInCubic:i=>i*i*i,easeOutCubic:i=>(i-=1)*i*i+1,easeInOutCubic:i=>(i/=.5)<1?.5*i*i*i:.5*((i-=2)*i*i+2),easeInQuart:i=>i*i*i*i,easeOutQuart:i=>-((i-=1)*i*i*i-1),easeInOutQuart:i=>(i/=.5)<1?.5*i*i*i*i:-.5*((i-=2)*i*i*i-2),easeInQuint:i=>i*i*i*i*i,easeOutQuint:i=>(i-=1)*i*i*i*i+1,easeInOutQuint:i=>(i/=.5)<1?.5*i*i*i*i*i:.5*((i-=2)*i*i*i*i+2),easeInSine:i=>-Math.cos(i*Q)+1,easeOutSine:i=>Math.sin(i*Q),easeInOutSine:i=>-.5*(Math.cos(N*i)-1),easeInExpo:i=>i===0?0:Math.pow(2,10*(i-1)),easeOutExpo:i=>i===1?1:-Math.pow(2,-10*i)+1,easeInOutExpo:i=>fe(i)?i:i<.5?.5*Math.pow(2,10*(i*2-1)):.5*(-Math.pow(2,-10*(i*2-1))+2),easeInCirc:i=>i>=1?i:-(Math.sqrt(1-i*i)-1),easeOutCirc:i=>Math.sqrt(1-(i-=1)*i),easeInOutCirc:i=>(i/=.5)<1?-.5*(Math.sqrt(1-i*i)-1):.5*(Math.sqrt(1-(i-=2)*i)+1),easeInElastic:i=>fe(i)?i:Wi(i,.075,.3),easeOutElastic:i=>fe(i)?i:Ni(i,.075,.3),easeInOutElastic(i){return fe(i)?i:i<.5?.5*Wi(i*2,.1125,.45):.5+.5*Ni(i*2-1,.1125,.45)},easeInBack(i){return i*i*((1.70158+1)*i-1.70158)},easeOutBack(i){return(i-=1)*i*((1.70158+1)*i+1.70158)+1},easeInOutBack(i){let t=1.70158;return(i/=.5)<1?.5*(i*i*(((t*=1.525)+1)*i-t)):.5*((i-=2)*i*(((t*=1.525)+1)*i+t)+2)},easeInBounce:i=>1-jt.easeOutBounce(1-i),easeOutBounce(i){return i<1/2.75?7.5625*i*i:i<2/2.75?7.5625*(i-=1.5/2.75)*i+.75:i<2.5/2.75?7.5625*(i-=2.25/2.75)*i+.9375:7.5625*(i-=2.625/2.75)*i+.984375},easeInOutBounce:i=>i<.5?jt.easeInBounce(i*2)*.5:jt.easeOutBounce(i*2-1)*.5+.5};function Xe(i){if(i&&typeof i=="object"){const t=i.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Vi(i){return Xe(i)?i:new Bt(i)}function Ke(i){return Xe(i)?i:new Bt(i).saturate(.5).darken(.1).hexString()}const xo=["x","y","borderWidth","radius","tension"],yo=["color","borderColor","backgroundColor"];function vo(i){i.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),i.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),i.set("animations",{colors:{type:"color",properties:yo},numbers:{type:"number",properties:xo}}),i.describe("animations",{_fallback:"animation"}),i.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:t=>t|0}}}})}function ko(i){i.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const ji=new Map;function wo(i,t){t=t||{};const e=i+JSON.stringify(t);let s=ji.get(e);return s||(s=new Intl.NumberFormat(i,t),ji.set(e,s)),s}function $i(i,t,e){return wo(t,e).format(i)}const So={values(i){return E(i)?i:""+i},numeric(i,t,e){if(i===0)return"0";const s=this.chart.options.locale;let n,o=i;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(n="scientific"),o=Mo(i,e)}const a=Ai(Math.abs(o)),r=isNaN(a)?1:Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),$i(i,s,l)}};function Mo(i,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&i!==Math.floor(i)&&(e=i-Math.floor(i)),e}var Yi={formatters:So};function Do(i){i.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width: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:Yi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),i.route("scale.ticks","color","","color"),i.route("scale.grid","color","","borderColor"),i.route("scale.border","color","","borderColor"),i.route("scale.title","color","","color"),i.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),i.describe("scales",{_fallback:"scale"}),i.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const yt=Object.create(null),qe=Object.create(null);function $t(i,t){if(!t)return i;const e=t.split(".");for(let s=0,n=e.length;ss.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=(s,n)=>Ke(n.backgroundColor),this.hoverBorderColor=(s,n)=>Ke(n.borderColor),this.hoverColor=(s,n)=>Ke(n.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(t),this.apply(e)}set(t,e){return Ge(this,t,e)}get(t){return $t(this,t)}describe(t,e){return Ge(qe,t,e)}override(t,e){return Ge(yt,t,e)}route(t,e,s,n){const o=$t(this,t),a=$t(this,s),r="_"+e;Object.defineProperties(o,{[r]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[r],c=a[n];return O(l)?Object.assign({},c,l):C(l,c)},set(l){this[r]=l}}})}apply(t){t.forEach(e=>e(this))}}var R=new Po({_scriptable:i=>!i.startsWith("on"),_indexable:i=>i!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[vo,ko,Do]);function Co(i){return!i||I(i.size)||I(i.family)?null:(i.style?i.style+" ":"")+(i.weight?i.weight+" ":"")+i.size+"px "+i.family}function Ui(i,t,e,s,n){let o=t[n];return o||(o=t[n]=i.measureText(n).width,e.push(n)),o>s&&(s=o),s}function vt(i,t,e){const s=i.currentDevicePixelRatio,n=e!==0?Math.max(e/2,.5):0;return Math.round((t-n)*s)/s+n}function Xi(i,t){!t&&!i||(t=t||i.getContext("2d"),t.save(),t.resetTransform(),t.clearRect(0,0,i.width,i.height),t.restore())}function Ze(i,t,e,s){Ki(i,t,e,s,null)}function Ki(i,t,e,s,n){let o,a,r,l,c,h,d,f;const u=t.pointStyle,m=t.rotation,g=t.radius;let p=(m||0)*eo;if(u&&typeof u=="object"&&(o=u.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){i.save(),i.translate(e,s),i.rotate(p),i.drawImage(u,-u.width/2,-u.height/2,u.width,u.height),i.restore();return}if(!(isNaN(g)||g<=0)){switch(i.beginPath(),u){default:n?i.ellipse(e,s,n/2,g,0,0,Z):i.arc(e,s,g,0,Z),i.closePath();break;case"triangle":h=n?n/2:g,i.moveTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Ti,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Ti,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),i.closePath();break;case"rectRounded":c=g*.516,l=g-c,a=Math.cos(p+bt)*l,d=Math.cos(p+bt)*(n?n/2-c:l),r=Math.sin(p+bt)*l,f=Math.sin(p+bt)*(n?n/2-c:l),i.arc(e-d,s-r,c,p-N,p-Q),i.arc(e+f,s-a,c,p-Q,p),i.arc(e+d,s+r,c,p,p+Q),i.arc(e-f,s+a,c,p+Q,p+N),i.closePath();break;case"rect":if(!m){l=Math.SQRT1_2*g,h=n?n/2:l,i.rect(e-h,s-l,2*h,2*l);break}p+=bt;case"rectRot":d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+f,s-a),i.lineTo(e+d,s+r),i.lineTo(e-f,s+a),i.closePath();break;case"crossRot":p+=bt;case"cross":d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+d,s+r),i.moveTo(e+f,s-a),i.lineTo(e-f,s+a);break;case"star":d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+d,s+r),i.moveTo(e+f,s-a),i.lineTo(e-f,s+a),p+=bt,d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+d,s+r),i.moveTo(e+f,s-a),i.lineTo(e-f,s+a);break;case"line":a=n?n/2:Math.cos(p)*g,r=Math.sin(p)*g,i.moveTo(e-a,s-r),i.lineTo(e+a,s+r);break;case"dash":i.moveTo(e,s),i.lineTo(e+Math.cos(p)*(n?n/2:g),s+Math.sin(p)*g);break;case!1:i.closePath();break}i.fill(),t.borderWidth>0&&i.stroke()}}function Yt(i,t,e){return e=e||.5,!t||i&&i.x>t.left-e&&i.xt.top-e&&i.y0&&o.strokeColor!=="";let l,c;for(i.save(),i.font=n.string,Ao(i,o),l=0;l+i||0;function qi(i,t){const e={},s=O(t),n=s?Object.keys(t):t,o=O(i)?s?a=>C(i[a],i[t[a]]):a=>i[a]:()=>i;for(const a of n)e[a]=Eo(o(a));return e}function Bo(i){return qi(i,{top:"y",right:"x",bottom:"y",left:"x"})}function Ut(i){return qi(i,["topLeft","topRight","bottomLeft","bottomRight"])}function J(i){const t=Bo(i);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function V(i,t){i=i||{},t=t||R.font;let e=C(i.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let s=C(i.style,t.style);s&&!(""+s).match(Ro)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:C(i.family,t.family),lineHeight:zo(C(i.lineHeight,t.lineHeight),e),size:e,style:s,weight:C(i.weight,t.weight),string:""};return n.string=Co(n),n}function me(i,t,e,s){let n,o,a;for(n=0,o=i.length;ne&&r===0?0:r+l;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function kt(i,t){return Object.assign(Object.create(i),t)}function Je(i,t=[""],e,s,n=()=>i[0]){const o=e||i;typeof s>"u"&&(s=ts("_fallback",i));const a={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:i,_rootScopes:o,_fallback:s,_getTarget:n,override:r=>Je([r,...i],t,o,s)};return new Proxy(a,{deleteProperty(r,l){return delete r[l],delete r._keys,delete i[0][l],!0},get(r,l){return Zi(r,l,()=>Xo(l,t,i,r))},getOwnPropertyDescriptor(r,l){return Reflect.getOwnPropertyDescriptor(r._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(i[0])},has(r,l){return es(r).includes(l)},ownKeys(r){return es(r)},set(r,l,c){const h=r._storage||(r._storage=n());return r[l]=h[l]=c,delete r._keys,!0}})}function Lt(i,t,e,s){const n={_cacheable:!1,_proxy:i,_context:t,_subProxy:e,_stack:new Set,_descriptors:Gi(i,s),setContext:o=>Lt(i,o,e,s),override:o=>Lt(i.override(o),t,e,s)};return new Proxy(n,{deleteProperty(o,a){return delete o[a],delete i[a],!0},get(o,a,r){return Zi(o,a,()=>No(o,a,r))},getOwnPropertyDescriptor(o,a){return o._descriptors.allKeys?Reflect.has(i,a)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(i,a)},getPrototypeOf(){return Reflect.getPrototypeOf(i)},has(o,a){return Reflect.has(i,a)},ownKeys(){return Reflect.ownKeys(i)},set(o,a,r){return i[a]=r,delete o[a],!0}})}function Gi(i,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:s=t.indexable,_allKeys:n=t.allKeys}=i;return{allKeys:n,scriptable:e,indexable:s,isScriptable:ft(e)?e:()=>e,isIndexable:ft(s)?s:()=>s}}const Wo=(i,t)=>i?i+$e(t):t,ti=(i,t)=>O(t)&&i!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function Zi(i,t,e){if(Object.prototype.hasOwnProperty.call(i,t)||t==="constructor")return i[t];const s=e();return i[t]=s,s}function No(i,t,e){const{_proxy:s,_context:n,_subProxy:o,_descriptors:a}=i;let r=s[t];return ft(r)&&a.isScriptable(t)&&(r=Vo(t,r,i,e)),E(r)&&r.length&&(r=jo(t,r,i,a.isIndexable)),ti(t,r)&&(r=Lt(r,n,o&&o[t],a)),r}function Vo(i,t,e,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=e;if(r.has(i))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+i);r.add(i);let l=t(o,a||s);return r.delete(i),ti(i,l)&&(l=ei(n._scopes,n,i,l)),l}function jo(i,t,e,s){const{_proxy:n,_context:o,_subProxy:a,_descriptors:r}=e;if(typeof o.index<"u"&&s(i))return t[o.index%t.length];if(O(t[0])){const l=t,c=n._scopes.filter(h=>h!==l);t=[];for(const h of l){const d=ei(c,n,i,h);t.push(Lt(d,o,a&&a[i],r))}}return t}function Qi(i,t,e){return ft(i)?i(t,e):i}const $o=(i,t)=>i===!0?t:typeof i=="string"?ce(t,i):void 0;function Yo(i,t,e,s,n){for(const o of t){const a=$o(e,o);if(a){i.add(a);const r=Qi(a._fallback,e,n);if(typeof r<"u"&&r!==e&&r!==s)return r}else if(a===!1&&typeof s<"u"&&e!==s)return null}return!1}function ei(i,t,e,s){const n=t._rootScopes,o=Qi(t._fallback,e,s),a=[...i,...n],r=new Set;r.add(s);let l=Ji(r,a,e,o||e,s);return l===null||typeof o<"u"&&o!==e&&(l=Ji(r,a,o,l,s),l===null)?!1:Je(Array.from(r),[""],n,o,()=>Uo(t,e,s))}function Ji(i,t,e,s,n){for(;e;)e=Yo(i,t,e,s,n);return e}function Uo(i,t,e){const s=i._getTarget();t in s||(s[t]={});const n=s[t];return E(n)&&O(e)?e:n||{}}function Xo(i,t,e,s){let n;for(const o of t)if(n=ts(Wo(o,i),e),typeof n<"u")return ti(i,n)?ei(e,s,i,n):n}function ts(i,t){for(const e of t){if(!e)continue;const s=e[i];if(typeof s<"u")return s}}function es(i){let t=i._keys;return t||(t=i._keys=Ko(i._scopes)),t}function Ko(i){const t=new Set;for(const e of i)for(const s of Object.keys(e).filter(n=>!n.startsWith("_")))t.add(s);return Array.from(t)}const qo=Number.EPSILON||1e-14,It=(i,t)=>ti==="x"?"y":"x";function Go(i,t,e,s){const n=i.skip?t:i,o=t,a=e.skip?t:e,r=Ye(o,n),l=Ye(a,o);let c=r/(r+l),h=l/(r+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const d=s*c,f=s*h;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+f*(a.x-n.x),y:o.y+f*(a.y-n.y)}}}function Zo(i,t,e){const s=i.length;let n,o,a,r,l,c=It(i,0);for(let h=0;h!c.skip)),t.cubicInterpolationMode==="monotone")Jo(i,n);else{let c=s?i[i.length-1]:i[0];for(o=0,a=i.length;oi.ownerDocument.defaultView.getComputedStyle(i,null);function ia(i,t){return xe(i).getPropertyValue(t)}const sa=["top","right","bottom","left"];function wt(i,t,e){const s={};e=e?"-"+e:"";for(let n=0;n<4;n++){const o=sa[n];s[o]=parseFloat(i[t+"-"+o+e])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const na=(i,t,e)=>(i>0||t>0)&&(!e||!e.shadowRoot);function oa(i,t){const e=i.touches,s=e&&e.length?e[0]:i,{offsetX:n,offsetY:o}=s;let a=!1,r,l;if(na(n,o,i.target))r=n,l=o;else{const c=t.getBoundingClientRect();r=s.clientX-c.left,l=s.clientY-c.top,a=!0}return{x:r,y:l,box:a}}function St(i,t){if("native"in i)return i;const{canvas:e,currentDevicePixelRatio:s}=t,n=xe(e),o=n.boxSizing==="border-box",a=wt(n,"padding"),r=wt(n,"border","width"),{x:l,y:c,box:h}=oa(i,e),d=a.left+(h&&r.left),f=a.top+(h&&r.top);let{width:u,height:m}=t;return o&&(u-=a.width+r.width,m-=a.height+r.height),{x:Math.round((l-d)/u*e.width/s),y:Math.round((c-f)/m*e.height/s)}}function aa(i,t,e){let s,n;if(t===void 0||e===void 0){const o=i&&si(i);if(!o)t=i.clientWidth,e=i.clientHeight;else{const a=o.getBoundingClientRect(),r=xe(o),l=wt(r,"border","width"),c=wt(r,"padding");t=a.width-c.width-l.width,e=a.height-c.height-l.height,s=_e(r.maxWidth,o,"clientWidth"),n=_e(r.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:s||de,maxHeight:n||de}}const ut=i=>Math.round(i*10)/10;function ra(i,t,e,s){const n=xe(i),o=wt(n,"margin"),a=_e(n.maxWidth,i,"clientWidth")||de,r=_e(n.maxHeight,i,"clientHeight")||de,l=aa(i,t,e);let{width:c,height:h}=l;if(n.boxSizing==="content-box"){const f=wt(n,"border","width"),u=wt(n,"padding");c-=u.width+f.width,h-=u.height+f.height}return c=Math.max(0,c-o.width),h=Math.max(0,s?c/s:h-o.height),c=ut(Math.min(c,a,l.maxWidth)),h=ut(Math.min(h,r,l.maxHeight)),c&&!h&&(h=ut(c/2)),(t!==void 0||e!==void 0)&&s&&l.height&&h>l.height&&(h=l.height,c=ut(Math.floor(h*s))),{width:c,height:h}}function ss(i,t,e){const s=t||1,n=ut(i.height*s),o=ut(i.width*s);i.height=ut(i.height),i.width=ut(i.width);const a=i.canvas;return a.style&&(e||!a.style.height&&!a.style.width)&&(a.style.height=`${i.height}px`,a.style.width=`${i.width}px`),i.currentDevicePixelRatio!==s||a.height!==n||a.width!==o?(i.currentDevicePixelRatio=s,a.height=n,a.width=o,i.ctx.setTransform(s,0,0,s,0,0),!0):!1}const la=(function(){let i=!1;try{const t={get passive(){return i=!0,!1}};ii()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return i})();function ns(i,t){const e=ia(i,t),s=e&&e.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function Mt(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:i.y+e*(t.y-i.y)}}function ca(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:s==="middle"?e<.5?i.y:t.y:s==="after"?e<1?i.y:t.y:e>0?t.y:i.y}}function ha(i,t,e,s){const n={x:i.cp2x,y:i.cp2y},o={x:t.cp1x,y:t.cp1y},a=Mt(i,n,e),r=Mt(n,o,e),l=Mt(o,t,e),c=Mt(a,r,e),h=Mt(r,l,e);return Mt(c,h,e)}const da=function(i,t){return{x(e){return i+i+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,s){return e-s},leftForLtr(e,s){return e-s}}},fa=function(){return{x(i){return i},setWidth(i){},textAlign(i){return i},xPlus(i,t){return i+t},leftForLtr(i,t){return i}}};function Ft(i,t,e){return i?da(t,e):fa()}function os(i,t){let e,s;(t==="ltr"||t==="rtl")&&(e=i.canvas.style,s=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),i.prevTextDirection=s)}function as(i,t){t!==void 0&&(delete i.prevTextDirection,i.canvas.style.setProperty("direction",t[0],t[1]))}function rs(i){return i==="angle"?{between:Fi,compare:lo,normalize:it}:{between:At,compare:(t,e)=>t-e,normalize:t=>t}}function ls({start:i,end:t,count:e,loop:s,style:n}){return{start:i%e,end:t%e,loop:s&&(t-i+1)%e===0,style:n}}function ua(i,t,e){const{property:s,start:n,end:o}=e,{between:a,normalize:r}=rs(s),l=t.length;let{start:c,end:h,loop:d}=i,f,u;if(d){for(c+=l,h+=l,f=0,u=l;fl(n,v,b)&&r(n,v)!==0,_=()=>r(o,b)===0||l(o,v,b),S=()=>g||k(),w=()=>!g||_();for(let y=h,M=h;y<=d;++y)x=t[y%a],!x.skip&&(b=c(x[s]),b!==v&&(g=l(b,n,o),p===null&&S()&&(p=r(b,n)===0?y:M),p!==null&&w()&&(m.push(ls({start:p,end:y,loop:f,count:a,style:u})),p=null),M=y,v=b));return p!==null&&m.push(ls({start:p,end:d,loop:f,count:a,style:u})),m}function hs(i,t){const e=[],s=i.segments;for(let n=0;nn&&i[o%t].skip;)o--;return o%=t,{start:n,end:o}}function pa(i,t,e,s){const n=i.length,o=[];let a=t,r=i[t],l;for(l=t+1;l<=e;++l){const c=i[l%n];c.skip||c.stop?r.skip||(s=!1,o.push({start:t%n,end:(l-1)%n,loop:s}),t=a=c.stop?l:null):(a=l,r.skip&&(t=l)),r=c}return a!==null&&o.push({start:t%n,end:a%n,loop:s}),o}function ma(i,t){const e=i.points,s=i.options.spanGaps,n=e.length;if(!n)return[];const o=!!i._loop,{start:a,end:r}=ga(e,n,o,s);if(s===!0)return ds(i,[{start:a,end:r,loop:o}],e,t);const l=ra({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(s-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=Fi.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((s,n)=>{if(!s.running||!s.items.length)return;const o=s.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>s.duration&&(s.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(n.draw(),this._notify(n,s,t,"progress")),o.length||(s.running=!1,this._notify(n,s,t,"complete"),s.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let s=e.get(t);return s||(s={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,s)),s}listen(t,e,s){this._getAnims(t).listeners[e].push(s)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((s,n)=>Math.max(s,n._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const s=e.items;let n=s.length-1;for(;n>=0;--n)s[n].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var ot=new pr;const hs="transparent",mr={boolean(i,t,e){return e>.5?t:i},color(i,t,e){const s=Ni(i||hs),n=s.valid&&Ni(t||hs);return n&&n.valid?n.mix(s,e).hexString():t},number(i,t,e){return i+(t-i)*e}};class br{constructor(t,e,s,n){const o=e[s];n=ce([t.to,n,o,t.from]);const r=ce([t.from,o,n]);this._active=!0,this._fn=t.fn||mr[t.type||typeof r],this._easing=Nt[t.easing]||Nt.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=s,this._from=r,this._to=n,this._promises=void 0}active(){return this._active}update(t,e,s){if(this._active){this._notify(!1);const n=this._target[this._prop],o=s-this._start,r=this._duration-o;this._start=s,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=ce([t.to,e,n,t.from]),this._from=ce([t.from,n,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,s=this._duration,n=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[n]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,s)=>{t.push({res:e,rej:s})})}_notify(t){const e=t?"res":"rej",s=this._promises||[];for(let n=0;n{const o=t[n];if(!D(o))return;const r={};for(const a of e)r[a]=o[a];(z(o.properties)&&o.properties||[n]).forEach(a=>{(a===n||!s.has(a))&&s.set(a,r)})})}_animateOptions(t,e){const s=e.options,n=yr(t,s);if(!n)return[];const o=this._createAnimations(n,s);return s.$shared&&_r(t.options.$animations,s).then(()=>{t.options=s},()=>{}),o}_createAnimations(t,e){const s=this._properties,n=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){n.push(...this._animateOptions(t,e));continue}const h=e[c];let f=o[c];const d=s.get(c);if(f)if(d&&f.active()){f.update(d,h,a);continue}else f.cancel();if(!d||!d.duration){t[c]=h;continue}o[c]=f=new br(d,t,c,h),n.push(f)}return n}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const s=this._createAnimations(t,e);if(s.length)return ot.add(this._chart,s),!0}}function _r(i,t){const e=[],s=Object.keys(t);for(let n=0;n0||!e&&o<0)return n.index}return null}function ms(i,t){const{chart:e,_cachedMeta:s}=i,n=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=s,l=o.axis,c=r.axis,h=wr(o,r,s),f=t.length;let d;for(let u=0;ue[s].axis===t).shift()}function Dr(i,t){return yt(i,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function Pr(i,t,e){return yt(i,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Ht(i,t){const e=i.controller.index,s=i.vScale&&i.vScale.axis;if(s){t=t||i._parsed;for(const n of t){const o=n._stacks;if(!o||o[s]===void 0||o[s][e]===void 0)return;delete o[s][e],o[s]._visualValues!==void 0&&o[s]._visualValues[e]!==void 0&&delete o[s]._visualValues[e]}}}const ii=i=>i==="reset"||i==="none",bs=(i,t)=>t?i:Object.assign({},i),Cr=(i,t,e)=>i&&!t.hidden&&t._stacked&&{keys:us(e,!0),values:null};class Wt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,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.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=ti(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ht(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,s=this.getDataset(),n=(f,d,u,m)=>f==="x"?d:f==="r"?m:u,o=e.xAxisID=O(s.xAxisID,ei(t,"x")),r=e.yAxisID=O(s.yAxisID,ei(t,"y")),a=e.rAxisID=O(s.rAxisID,ei(t,"r")),l=e.indexAxis,c=e.iAxisID=n(l,o,r,a),h=e.vAxisID=n(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Li(this._data,this),t._stacked&&Ht(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),s=this._data;if(D(e)){const n=this._cachedMeta;this._data=kr(e,n)}else if(s!==e){if(s){Li(s,this);const n=this._cachedMeta;Ht(n),n._parsed=[]}e&&Object.isExtensible(e)&&no(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,s=this.getDataset();let n=!1;this._dataCheck();const o=e._stacked;e._stacked=ti(e.vScale,e),e.stack!==s.stack&&(n=!0,Ht(e),e.stack=s.stack),this._resyncElements(t),(n||o!==e._stacked)&&(ms(this,e._parsed),e._stacked=ti(e.vScale,e))}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),s=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(s,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:s,_data:n}=this,{iScale:o,_stacked:r}=s,a=o.axis;let l=t===0&&e===n.length?!0:s._sorted,c=t>0&&s._parsed[t-1],h,f,d;if(this._parsing===!1)s._parsed=n,s._sorted=!0,d=n;else{z(n[t])?d=this.parseArrayData(s,n,t,e):D(n[t])?d=this.parseObjectData(s,n,t,e):d=this.parsePrimitiveData(s,n,t,e);const u=()=>f[a]===null||c&&f[a]g||f=0;--d)if(!m()){this.updateRangeFromParsed(c,t,u,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,s=[];let n,o,r;for(n=0,o=e.length;n=0&&tthis.getContext(s,n,e),g=c.resolveNamedOptions(d,u,m,f);return g.$shared&&(g.$shared=l,o[r]=Object.freeze(bs(g,l))),g}_resolveAnimations(t,e,s){const n=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(n.options.animation!==!1){const h=this.chart.config,f=h.datasetAnimationScopeKeys(this._type,e),d=h.getOptionScopes(this.getDataset(),f);l=h.createResolver(d,this.getContext(t,s,e))}const c=new fs(n,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ii(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const s=this.resolveDataElementOptions(t,e),n=this._sharedOptions,o=this.getSharedOptions(s),r=this.includeOptions(e,o)||o!==n;return this.updateSharedOptions(o,e,s),{sharedOptions:o,includeOptions:r}}updateElement(t,e,s,n){ii(n)?Object.assign(t,s):this._resolveAnimations(e,n).update(t,s)}updateSharedOptions(t,e,s){t&&!ii(e)&&this._resolveAnimations(void 0,e).update(t,s)}_setStyle(t,e,s,n){t.active=n;const o=this.getStyle(e,n);this._resolveAnimations(e,s,n).update(t,{options:!n&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,s){this._setStyle(t,s,"active",!1)}setHoverStyle(t,e,s){this._setStyle(t,s,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,s=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const n=s.length,o=e.length,r=Math.min(o,n);r&&this.parse(0,r),o>n?this._insertElements(n,o-n,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;a0&&this.getParsed(e-1);for(let _=0;_=y){k.skip=!0;continue}const w=this.getParsed(_),P=T(w[u]),I=k[d]=r.getPixelForValue(w[d],_),L=k[u]=o||P?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,w,l):w[u],_);k.skip=isNaN(I)||isNaN(L)||P,k.stop=_>0&&Math.abs(w[d]-x[d])>p,g&&(k.parsed=w,k.raw=c.data[_]),f&&(k.options=h||this.resolveDataElementOptions(_,M.active?"active":n)),b||this.updateElement(M,_,k,n),x=w}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,s=e.options&&e.options.borderWidth||0,n=t.data||[];if(!n.length)return s;const o=n[0].size(this.resolveDataElementOptions(0)),r=n[n.length-1].size(this.resolveDataElementOptions(n.length-1));return Math.max(s,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}S(ge,"id","line"),S(ge,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),S(ge,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});function wt(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class si{constructor(t){S(this,"options");this.options=t||{}}static override(t){Object.assign(si.prototype,t)}init(){}formats(){return wt()}parse(){return wt()}format(){return wt()}add(){return wt()}diff(){return wt()}startOf(){return wt()}endOf(){return wt()}}var Or={_date:si};function Tr(i,t,e,s){const{controller:n,data:o,_sorted:r}=i,a=n._cachedMeta.iScale,l=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const c=a._reversePixels?io:mt;if(s){if(n._sharedOptions){const h=o[0],f=typeof h.getRange=="function"&&h.getRange(t);if(f){const d=c(o,t,e-f),u=c(o,t,e+f);return{lo:d.lo,hi:u.hi}}}}else{const h=c(o,t,e);if(l){const{vScale:f}=n._cachedMeta,{_parsed:d}=i,u=d.slice(0,h.lo+1).reverse().findIndex(g=>!T(g[f.axis]));h.lo-=Math.max(0,u);const m=d.slice(h.hi).findIndex(g=>!T(g[f.axis]));h.hi+=Math.max(0,m)}return h}}return{lo:0,hi:o.length-1}}function pe(i,t,e,s,n){const o=i.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r]&&l[r](t[e],n)&&(o.push({element:l,datasetIndex:c,index:h}),a=a||l.inRange(t.x,t.y,n))}),s&&!a?[]:o}var Fr={modes:{index(i,t,e,s){const n=vt(t,i),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?ni(i,n,o,s,r):oi(i,n,o,!1,s,r),l=[];return a.length?(i.getSortedVisibleDatasetMetas().forEach(c=>{const h=a[0].index,f=c.data[h];f&&!f.skip&&l.push({element:f,datasetIndex:c.index,index:h})}),l):[]},dataset(i,t,e,s){const n=vt(t,i),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?ni(i,n,o,s,r):oi(i,n,o,!1,s,r);if(a.length>0){const l=a[0].datasetIndex,c=i.getDatasetMeta(l).data;a=[];for(let h=0;he.pos===t)}function xs(i,t){return i.filter(e=>ys.indexOf(e.pos)===-1&&e.box.axis===t)}function Yt(i,t){return i.sort((e,s)=>{const n=t?s:e,o=t?e:s;return n.weight===o.weight?n.index-o.index:n.weight-o.weight})}function Rr(i){const t=[];let e,s,n,o,r,a;for(e=0,s=(i||[]).length;ec.box.fullSize),!0),s=Yt($t(t,"left"),!0),n=Yt($t(t,"right")),o=Yt($t(t,"top"),!0),r=Yt($t(t,"bottom")),a=xs(t,"x"),l=xs(t,"y");return{fullSize:e,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(r).concat(a),chartArea:$t(t,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(r).concat(a)}}function vs(i,t,e,s){return Math.max(i[e],t[e])+Math.max(i[s],t[s])}function ks(i,t){i.top=Math.max(i.top,t.top),i.left=Math.max(i.left,t.left),i.bottom=Math.max(i.bottom,t.bottom),i.right=Math.max(i.right,t.right)}function Nr(i,t,e,s){const{pos:n,box:o}=e,r=i.maxPadding;if(!D(n)){e.size&&(i[n]-=e.size);const f=s[e.stack]||{size:0,count:1};f.size=Math.max(f.size,e.horizontal?o.height:o.width),e.size=f.size/f.count,i[n]+=e.size}o.getPadding&&ks(r,o.getPadding());const a=Math.max(0,t.outerWidth-vs(r,i,"left","right")),l=Math.max(0,t.outerHeight-vs(r,i,"top","bottom")),c=a!==i.w,h=l!==i.h;return i.w=a,i.h=l,e.horizontal?{same:c,other:h}:{same:h,other:c}}function Vr(i){const t=i.maxPadding;function e(s){const n=Math.max(t[s]-i[s],0);return i[s]+=n,n}i.y+=e("top"),i.x+=e("left"),e("right"),e("bottom")}function jr(i,t){const e=t.maxPadding;function s(n){const o={left:0,top:0,right:0,bottom:0};return n.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return s(i?["left","right"]:["top","bottom"])}function Ut(i,t,e,s){const n=[];let o,r,a,l,c,h;for(o=0,r=i.length,c=0;o{typeof g.beforeLayout=="function"&&g.beforeLayout()});const h=l.reduce((g,p)=>p.box.options&&p.box.options.display===!1?g:g+1,0)||1,f=Object.freeze({outerWidth:t,outerHeight:e,padding:n,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/h,hBoxMaxHeight:r/2}),d=Object.assign({},n);ks(d,ht(s));const u=Object.assign({maxPadding:d,w:o,h:r,x:n.left,y:n.top},n),m=zr(l.concat(c),f);Ut(a.fullSize,u,f,m),Ut(l,u,f,m),Ut(c,u,f,m)&&Ut(l,u,f,m),Vr(u),ws(a.leftAndTop,u,f,m),u.x+=u.w,u.y+=u.h,ws(a.rightAndBottom,u,f,m),i.chartArea={left:u.left,top:u.top,right:u.left+u.w,bottom:u.top+u.h,height:u.h,width:u.w},C(a.chartArea,g=>{const p=g.box;Object.assign(p,i.chartArea),p.update(u.w,u.h,{left:0,top:0,right:0,bottom:0})})}};class Ms{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,s){}removeEventListener(t,e,s){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,s,n){return e=Math.max(0,e||t.width),s=s||t.height,{width:e,height:Math.max(0,n?Math.floor(e/n):s)}}isAttached(t){return!0}updateConfig(t){}}class Hr extends Ms{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const _e="$chartjs",Wr={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},Ss=i=>i===null||i==="";function $r(i,t){const e=i.style,s=i.getAttribute("height"),n=i.getAttribute("width");if(i[_e]={initial:{height:s,width:n,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",Ss(n)){const o=is(i,"width");o!==void 0&&(i.width=o)}if(Ss(s))if(i.style.height==="")i.height=i.width/(t||2);else{const o=is(i,"height");o!==void 0&&(i.height=o)}return i}const Ds=er?{passive:!0}:!1;function Yr(i,t,e){i&&i.addEventListener(t,e,Ds)}function Ur(i,t,e){i&&i.canvas&&i.canvas.removeEventListener(t,e,Ds)}function Xr(i,t){const e=Wr[i.type]||i.type,{x:s,y:n}=vt(i,t);return{type:e,chart:t,native:i,x:s!==void 0?s:null,y:n!==void 0?n:null}}function ye(i,t){for(const e of i)if(e===t||e.contains(t))return!0}function Kr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ye(a.addedNodes,s),r=r&&!ye(a.removedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}function qr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ye(a.removedNodes,s),r=r&&!ye(a.addedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}const Xt=new Map;let Ps=0;function Cs(){const i=window.devicePixelRatio;i!==Ps&&(Ps=i,Xt.forEach((t,e)=>{e.currentDevicePixelRatio!==i&&t()}))}function Gr(i,t){Xt.size||window.addEventListener("resize",Cs),Xt.set(i,t)}function Zr(i){Xt.delete(i),Xt.size||window.removeEventListener("resize",Cs)}function Qr(i,t,e){const s=i.canvas,n=s&&Qe(s);if(!n)return;const o=Ri((a,l)=>{const c=n.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,h=l.contentRect.height;c===0&&h===0||o(c,h)});return r.observe(n),Gr(i,o),r}function ri(i,t,e){e&&e.disconnect(),t==="resize"&&Zr(i)}function Jr(i,t,e){const s=i.canvas,n=Ri(o=>{i.ctx!==null&&e(Xr(o,i))},i);return Yr(s,t,n),n}class ta extends Ms{acquireContext(t,e){const s=t&&t.getContext&&t.getContext("2d");return s&&s.canvas===t?($r(t,e),s):null}releaseContext(t){const e=t.canvas;if(!e[_e])return!1;const s=e[_e].initial;["height","width"].forEach(o=>{const r=s[o];T(r)?e.removeAttribute(o):e.setAttribute(o,r)});const n=s.style||{};return Object.keys(n).forEach(o=>{e.style[o]=n[o]}),e.width=e.width,delete e[_e],!0}addEventListener(t,e,s){this.removeEventListener(t,e);const n=t.$proxies||(t.$proxies={}),r={attach:Kr,detach:qr,resize:Qr}[e]||Jr;n[e]=r(t,e,s)}removeEventListener(t,e){const s=t.$proxies||(t.$proxies={}),n=s[e];if(!n)return;({attach:ri,detach:ri,resize:ri}[e]||Ur)(t,e,n),s[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,s,n){return tr(t,e,s,n)}isAttached(t){const e=t&&Qe(t);return!!(e&&e.isConnected)}}function ea(i){return!Ze()||typeof OffscreenCanvas<"u"&&i instanceof OffscreenCanvas?Hr:ta}class Mt{constructor(){S(this,"x");S(this,"y");S(this,"active",!1);S(this,"options");S(this,"$animations")}tooltipPosition(t){const{x:e,y:s}=this.getProps(["x","y"],t);return{x:e,y:s}}hasValue(){return Bt(this.x)&&Bt(this.y)}getProps(t,e){const s=this.$animations;if(!e||!s)return this;const n={};return t.forEach(o=>{n[o]=s[o]&&s[o].active()?s[o]._to:this[o]}),n}}S(Mt,"defaults",{}),S(Mt,"defaultRoutes");function ia(i,t){const e=i.options.ticks,s=sa(i),n=Math.min(e.maxTicksLimit||s,s),o=e.major.enabled?oa(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>n)return ra(t,c,o,r/n),c;const h=na(o,t,n);if(r>0){let f,d;const u=r>1?Math.round((l-a)/(r-1)):null;for(xe(t,c,h,T(u)?0:a-u,a),f=0,d=r-1;fn)return l}return Math.max(n,1)}function oa(i){const t=[];let e,s;for(e=0,s=i.length;ei==="left"?"right":i==="right"?"left":i,Os=(i,t,e)=>t==="top"||t==="left"?i[t]+e:i[t]-e,Ts=(i,t)=>Math.min(t||i,i);function As(i,t){const e=[],s=i.length/t,n=i.length;let o=0;for(;or+a)))return l}function ha(i,t){C(i,e=>{const s=e.gc,n=s.length/2;let o;if(n>t){for(o=0;os?s:e,s=n&&e>s?e:s,{min:G(e,G(s,e)),max:G(s,G(e,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){A(this.options.beforeUpdate,[this])}update(t,e,s){const{beginAtZero:n,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=s=Object.assign({left:0,right:0,top:0,bottom:0},s),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+s.left+s.right:this.height+s.top+s.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Io(this,o,n),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||s<=1||!this.isHorizontal()){this.labelRotation=n;return}const h=this._getLabelSizes(),f=h.widest.width,d=h.highest.height,u=Y(this.chart.width-f,0,this.maxWidth);a=t.offset?this.maxWidth/s:u/(s-1),f+6>a&&(a=u/(s-(t.offset?.5:1)),l=this.maxHeight-Kt(t.grid)-e.padding-Is(t.title,this.chart.options.font),c=Math.sqrt(f*f+d*d),r=Qn(Math.min(Math.asin(Y((h.highest.height+6)/a,-1,1)),Math.asin(Y(l/c,-1,1))-Math.asin(Y(d/c,-1,1)))),r=Math.max(n,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){A(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){A(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:s,title:n,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=Is(n,e.options.font);if(a?(t.width=this.maxWidth,t.height=Kt(o)+l):(t.height=this.maxHeight,t.width=Kt(o)+l),s.display&&this.ticks.length){const{first:c,last:h,widest:f,highest:d}=this._getLabelSizes(),u=s.padding*2,m=pt(this.labelRotation),g=Math.cos(m),p=Math.sin(m);if(a){const b=s.mirror?0:p*f.width+g*d.height;t.height=Math.min(this.maxHeight,t.height+b+u)}else{const b=s.mirror?0:g*f.width+p*d.height;t.width=Math.min(this.maxWidth,t.width+b+u)}this._calculatePadding(c,h,p,g)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,s,n){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const h=this.getPixelForTick(0)-this.left,f=this.right-this.getPixelForTick(this.ticks.length-1);let d=0,u=0;l?c?(d=n*t.width,u=s*e.height):(d=s*t.height,u=n*e.width):o==="start"?u=e.width:o==="end"?d=t.width:o!=="inner"&&(d=t.width/2,u=e.width/2),this.paddingLeft=Math.max((d-h+r)*this.width/(this.width-h),0),this.paddingRight=Math.max((u-f+r)*this.width/(this.width-f),0)}else{let h=e.height/2,f=t.height/2;o==="start"?(h=0,f=t.height):o==="end"&&(h=e.height,f=0),this.paddingTop=h+r,this.paddingBottom=f+r}}_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(){A(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,s;for(e=0,s=t.length;e({width:r[P]||0,height:a[P]||0});return{first:w(0),last:w(e-1),widest:w(M),highest:w(k),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return eo(this._alignToPixels?_t(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*n?a/s:l/n:l*n0}_computeGridLineItems(t){const e=this.axis,s=this.chart,n=this.options,{grid:o,position:r,border:a}=n,l=o.offset,c=this.isHorizontal(),f=this.ticks.length+(l?1:0),d=Kt(o),u=[],m=a.setContext(this.getContext()),g=m.display?m.width:0,p=g/2,b=function(B){return _t(s,B,g)};let y,v,x,_,M,k,w,P,I,L,E,it;if(r==="top")y=b(this.bottom),k=this.bottom-d,P=y-p,L=b(t.top)+p,it=t.bottom;else if(r==="bottom")y=b(this.top),L=t.top,it=b(t.bottom)-p,k=y+p,P=this.top+d;else if(r==="left")y=b(this.right),M=this.right-d,w=y-p,I=b(t.left)+p,E=t.right;else if(r==="right")y=b(this.left),I=t.left,E=b(t.right)-p,M=y+p,w=this.left+d;else if(e==="x"){if(r==="center")y=b((t.top+t.bottom)/2+.5);else if(D(r)){const B=Object.keys(r)[0],U=r[B];y=b(this.chart.scales[B].getPixelForValue(U))}L=t.top,it=t.bottom,k=y+p,P=k+d}else if(e==="y"){if(r==="center")y=b((t.left+t.right)/2);else if(D(r)){const B=Object.keys(r)[0],U=r[B];y=b(this.chart.scales[B].getPixelForValue(U))}M=y-p,w=M-d,I=t.left,E=t.right}const ut=O(n.ticks.maxTicksLimit,f),F=Math.max(1,Math.ceil(f/ut));for(v=0;v0&&(Dt-=St/2);break}Ie={left:Dt,top:Qt,width:St+Lt.width,height:Zt+Lt.height,color:F.backdropColor}}p.push({label:x,font:P,textOffset:E,options:{rotation:g,color:U,strokeColor:Te,strokeWidth:Ae,textAlign:It,textBaseline:it,translation:[_,M],backdrop:Ie}})}return p}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-pt(this.labelRotation))return t==="top"?"left":"right";let n="center";return e.align==="start"?n="left":e.align==="end"?n="right":e.align==="inner"&&(n="inner"),n}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:s,mirror:n,padding:o}}=this.options,r=this._getLabelSizes(),a=t+o,l=r.widest.width;let c,h;return e==="left"?n?(h=this.right+o,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h+=l)):(h=this.right-a,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h=this.left)):e==="right"?n?(h=this.left+o,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h-=l)):(h=this.left+a,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h=this.right)):c="right",{textAlign:c,x:h}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:s,top:n,width:o,height:r}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(s,n,o,r),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const n=this.ticks.findIndex(o=>o.value===t);return n>=0?e.setContext(this.getContext(n)).lineWidth:0}drawGrid(t){const e=this.options.grid,s=this.ctx,n=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,h)=>{!h.width||!h.color||(s.save(),s.lineWidth=h.width,s.strokeStyle=h.color,s.setLineDash(h.borderDash||[]),s.lineDashOffset=h.borderDashOffset,s.beginPath(),s.moveTo(l.x,l.y),s.lineTo(c.x,c.y),s.stroke(),s.restore())};if(e.display)for(o=0,r=n.length;o{this.draw(o)}}]:[{z:s,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:n,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),s=this.axis+"AxisID",n=[];let o,r;for(o=0,r=e.length;o{const s=e.split("."),n=s.pop(),o=[i].concat(s).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");R.route(o,n,l,a)})}function ba(i){return"id"in i&&"defaults"in i}class _a{constructor(){this.controllers=new ve(Wt,"datasets",!0),this.elements=new ve(Mt,"elements"),this.plugins=new ve(Object,"plugins"),this.scales=new ve(At,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,s){[...e].forEach(n=>{const o=s||this._getRegistryForType(n);s||o.isForType(n)||o===this.plugins&&n.id?this._exec(t,o,n):C(n,r=>{const a=s||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,s){const n=Be(t);A(s["before"+n],[],s),e[t](s),A(s["after"+n],[],s)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(n(e,s),t,"stop"),this._notify(n(s,e),t,"start")}}function xa(i){const t={},e=[],s=Object.keys(tt.plugins.items);for(let o=0;o1&&Ls(i[0].toLowerCase());if(s)return s}throw new Error(`Cannot determine type of '${i}' axis. Please provide 'axis' or 'position' option.`)}function Fs(i,t,e){if(e[t+"AxisID"]===i)return{axis:t}}function Pa(i,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(s=>s.xAxisID===i||s.yAxisID===i);if(e.length)return Fs(i,"x",e[0])||Fs(i,"y",e[0])}return{}}function Ca(i,t){const e=bt[i.type]||{scales:{}},s=t.scales||{},n=ai(i.type,t),o=Object.create(null);return Object.keys(s).forEach(r=>{const a=s[r];if(!D(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=li(r,a,Pa(r,i),R.scales[a.type]),c=Sa(l,n),h=e.scales||{};o[r]=Et(Object.create(null),[{axis:l},a,h[l],h[c]])}),i.data.datasets.forEach(r=>{const a=r.type||i.type,l=r.indexAxis||ai(a,t),h=(bt[a]||{}).scales||{};Object.keys(h).forEach(f=>{const d=Ma(f,l),u=r[d+"AxisID"]||d;o[u]=o[u]||Object.create(null),Et(o[u],[{axis:d},s[u],h[f]])})}),Object.keys(o).forEach(r=>{const a=o[r];Et(a,[R.scales[a.type],R.scale])}),o}function Rs(i){const t=i.options||(i.options={});t.plugins=O(t.plugins,{}),t.scales=Ca(i,t)}function Es(i){return i=i||{},i.datasets=i.datasets||[],i.labels=i.labels||[],i}function Oa(i){return i=i||{},i.data=Es(i.data),Rs(i),i}const zs=new Map,Bs=new Set;function ke(i,t){let e=zs.get(i);return e||(e=t(),zs.set(i,e),Bs.add(e)),e}const qt=(i,t,e)=>{const s=ne(t,e);s!==void 0&&i.add(s)};class Ta{constructor(t){this._config=Oa(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Es(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Rs(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return ke(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return ke(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return ke(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,s=this.type;return ke(`${s}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const s=this._scopeCache;let n=s.get(t);return(!n||e)&&(n=new Map,s.set(t,n)),n}getOptionScopes(t,e,s){const{options:n,type:o}=this,r=this._cachedScopes(t,s),a=r.get(e);if(a)return a;const l=new Set;e.forEach(h=>{t&&(l.add(t),h.forEach(f=>qt(l,t,f))),h.forEach(f=>qt(l,n,f)),h.forEach(f=>qt(l,bt[o]||{},f)),h.forEach(f=>qt(l,R,f)),h.forEach(f=>qt(l,We,f))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),Bs.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,bt[e]||{},R.datasets[e]||{},{type:e},R,We]}resolveNamedOptions(t,e,s,n=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=Ns(this._resolverCache,t,n);let l=r;if(Ia(r,e)){o.$shared=!1,s=ct(s)?s():s;const c=this.createResolver(t,s,a);l=Ot(r,s,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,s=[""],n){const{resolver:o}=Ns(this._resolverCache,t,s);return D(e)?Ot(o,e,void 0,n):o}}function Ns(i,t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));const n=e.join();let o=s.get(n);return o||(o={resolver:Ke(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},s.set(n,o)),o}const Aa=i=>D(i)&&Object.getOwnPropertyNames(i).some(t=>ct(i[t]));function Ia(i,t){const{isScriptable:e,isIndexable:s}=Ki(i);for(const n of t){const o=e(n),r=s(n),a=(r||o)&&i[n];if(o&&(ct(a)||Aa(a))||r&&z(a))return!0}return!1}var La="4.5.1";const Fa=["top","bottom","left","right","chartArea"];function Vs(i,t){return i==="top"||i==="bottom"||Fa.indexOf(i)===-1&&t==="x"}function js(i,t){return function(e,s){return e[i]===s[i]?e[t]-s[t]:e[i]-s[i]}}function Hs(i){const t=i.chart,e=t.options.animation;t.notifyPlugins("afterRender"),A(e&&e.onComplete,[i],t)}function Ra(i){const t=i.chart,e=t.options.animation;A(e&&e.onProgress,[i],t)}function Ws(i){return Ze()&&typeof i=="string"?i=document.getElementById(i):i&&i.length&&(i=i[0]),i&&i.canvas&&(i=i.canvas),i}const we={},$s=i=>{const t=Ws(i);return Object.values(we).filter(e=>e.canvas===t).pop()};function Ea(i,t,e){const s=Object.keys(i);for(const n of s){const o=+n;if(o>=t){const r=i[n];delete i[n],(e>0||o>t)&&(i[o+e]=r)}}}function za(i,t,e,s){return!e||i.type==="mouseout"?null:s?t:i}class rt{static register(...t){tt.add(...t),Ys()}static unregister(...t){tt.remove(...t),Ys()}constructor(t,e){const s=this.config=new Ta(e),n=Ws(t),o=$s(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ea(n)),this.platform.updateConfig(s);const a=this.platform.acquireContext(n,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,h=l&&l.width;if(this.id=Nn(),this.ctx=a,this.canvas=l,this.width=h,this.height=c,this._options=r,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 ya,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=ro(f=>this.update(f),r.resizeDelay||0),this._dataChanges=[],we[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}ot.listen(this,"complete",Hs),ot.listen(this,"progress",Ra),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:s,height:n,_aspectRatio:o}=this;return T(t)?e&&o?o:n?s/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return tt}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():es(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return $i(this.canvas,this.ctx),this}stop(){return ot.stop(this),this}resize(t,e){ot.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const s=this.options,n=this.canvas,o=s.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(n,t,e,o),a=s.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,es(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),A(s.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};C(e,(s,n)=>{s.id=n})}buildOrUpdateScales(){const t=this.options,e=t.scales,s=this.scales,n=Object.keys(s).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=li(r,a),c=l==="r",h=l==="x";return{options:a,dposition:c?"chartArea":h?"bottom":"left",dtype:c?"radialLinear":h?"category":"linear"}}))),C(o,r=>{const a=r.options,l=a.id,c=li(l,a),h=O(a.type,r.dtype);(a.position===void 0||Vs(a.position,c)!==Vs(r.dposition))&&(a.position=r.dposition),n[l]=!0;let f=null;if(l in s&&s[l].type===h)f=s[l];else{const d=tt.getScale(h);f=new d({id:l,type:h,ctx:this.ctx,chart:this}),s[f.id]=f}f.init(a,t)}),C(n,(r,a)=>{r||delete s[a]}),C(s,r=>{be.configure(this,r,r.options),be.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,s=t.length;if(t.sort((n,o)=>n.index-o.index),s>e){for(let n=e;ne.length&&delete this._stacks,t.forEach((s,n)=>{e.filter(o=>o===s._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let s,n;for(this._removeUnreferencedMetasets(),s=0,n=e.length;s{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const s=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),n=this._animationsDisabled=!s.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,h=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(js("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){C(this.scales,t=>{be.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),s=new Set(t.events);(!Si(e,s)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:s,start:n,count:o}of e){const r=s==="_removeElements"?-o:o;Ea(t,n,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,s=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),n=s(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;be.update(this,this.width,this.height,t);const e=this.chartArea,s=e.width<=0||e.height<=0;this._layers=[],C(this.boxes,n=>{s&&n.position==="chartArea"||(n.configure&&n.configure(),this._layers.push(...n._layers()))},this),this._layers.forEach((n,o)=>{n._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,s=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,s={meta:t,index:t.index,cancelable:!0},n=cs(this,t);this.notifyPlugins("beforeDatasetDraw",s)!==!1&&(n&&Ue(e,n),t.controller.draw(),n&&Xe(e),s.cancelable=!1,this.notifyPlugins("afterDatasetDraw",s))}isPointInArea(t){return jt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,s,n){const o=Fr.modes[e];return typeof o=="function"?o(this,t,s,n):[]}getDatasetMeta(t){const e=this.data.datasets[t],s=this._metasets;let n=s.filter(o=>o&&o._dataset===e).pop();return n||(n={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},s.push(n)),n}getContext(){return this.$context||(this.$context=yt(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const s=this.getDatasetMeta(t);return typeof s.hidden=="boolean"?!s.hidden:!e.hidden}setDatasetVisibility(t,e){const s=this.getDatasetMeta(t);s.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,s){const n=s?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,n);oe(e)?(o.data[e].hidden=!s,this.update()):(this.setDatasetVisibility(t,s),r.update(o,{visible:s}),this.update(a=>a.datasetIndex===t?n:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),ot.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},n=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};C(this.options.events,o=>s(o,n))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,s=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},n=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{n("attach",a),this.attached=!0,this.resize(),s("resize",o),s("detach",r)};r=()=>{this.attached=!1,n("resize",o),this._stop(),this._resize(0,0),s("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){C(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},C(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,s){const n=s?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+n+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!ie(s,e)&&(this._active=s,this._lastEvent=null,this._updateHoverStyles(s,e))}notifyPlugins(t,e,s){return this._plugins.notify(this,t,e,s)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,s){const n=this.options.hover,o=(l,c)=>l.filter(h=>!c.some(f=>h.datasetIndex===f.datasetIndex&&h.index===f.index)),r=o(e,t),a=s?t:o(t,e);r.length&&this.updateHoverStyle(r,n.mode,!1),a.length&&n.mode&&this.updateHoverStyle(a,n.mode,!0)}_eventHandler(t,e){const s={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},n=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",s,n)===!1)return;const o=this._handleEvent(t,e,s.inChartArea);return s.cancelable=!1,this.notifyPlugins("afterEvent",s,n),(o||s.changed)&&this.render(),this}_handleEvent(t,e,s){const{_active:n=[],options:o}=this,r=e,a=this._getActiveElements(t,n,s,r),l=Yn(t),c=za(t,this._lastEvent,s,l);s&&(this._lastEvent=null,A(o.onHover,[t,a,this],this),l&&A(o.onClick,[t,a,this],this));const h=!ie(a,n);return(h||e)&&(this._active=a,this._updateHoverStyles(a,n,e)),this._lastEvent=c,h}_getActiveElements(t,e,s,n){if(t.type==="mouseout")return[];if(!s)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,n)}}S(rt,"defaults",R),S(rt,"instances",we),S(rt,"overrides",bt),S(rt,"registry",tt),S(rt,"version",La),S(rt,"getChart",$s);function Ys(){return C(rt.instances,i=>i._plugins.invalidate())}function Us(i,t,e=t){i.lineCap=O(e.borderCapStyle,t.borderCapStyle),i.setLineDash(O(e.borderDash,t.borderDash)),i.lineDashOffset=O(e.borderDashOffset,t.borderDashOffset),i.lineJoin=O(e.borderJoinStyle,t.borderJoinStyle),i.lineWidth=O(e.borderWidth,t.borderWidth),i.strokeStyle=O(e.borderColor,t.borderColor)}function Ba(i,t,e){i.lineTo(e.x,e.y)}function Na(i){return i.stepped?ko:i.tension||i.cubicInterpolationMode==="monotone"?wo:Ba}function Xs(i,t,e={}){const s=i.length,{start:n=0,end:o=s-1}=e,{start:r,end:a}=t,l=Math.max(n,r),c=Math.min(o,a),h=na&&o>a;return{count:s,start:l,loop:t.loop,ilen:c(r+(c?a-x:x))%o,v=()=>{g!==p&&(i.lineTo(h,p),i.lineTo(h,g),i.lineTo(h,b))};for(l&&(u=n[y(0)],i.moveTo(u.x,u.y)),d=0;d<=a;++d){if(u=n[y(d)],u.skip)continue;const x=u.x,_=u.y,M=x|0;M===m?(_p&&(p=_),h=(f*h+x)/++f):(v(),i.lineTo(x,_),m=M,f=0,g=p=_),b=_}v()}function ci(i){const t=i.options,e=t.borderDash&&t.borderDash.length;return!i._decimated&&!i._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?ja:Va}function Ha(i){return i.stepped?ir:i.tension||i.cubicInterpolationMode==="monotone"?sr:kt}function Wa(i,t,e,s){let n=t._path;n||(n=t._path=new Path2D,t.path(n,e,s)&&n.closePath()),Us(i,t.options),i.stroke(n)}function $a(i,t,e,s){const{segments:n,options:o}=t,r=ci(t);for(const a of n)Us(i,o,a.style),i.beginPath(),r(i,t,a,{start:e,end:e+s-1})&&i.closePath(),i.stroke()}const Ya=typeof Path2D=="function";function Ua(i,t,e,s){Ya&&!t.options.segment?Wa(i,t,e,s):$a(i,t,e,s)}class dt extends Mt{constructor(t){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,t&&Object.assign(this,t)}updateControlPoints(t,e){const s=this.options;if((s.tension||s.cubicInterpolationMode==="monotone")&&!s.stepped&&!this._pointsUpdated){const n=s.spanGaps?this._loop:this._fullLoop;Ko(this._points,s,t,n,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=fr(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,s=t.length;return s&&e[t[s-1].end]}interpolate(t,e){const s=this.options,n=t[e],o=this.points,r=rs(this,{property:e,start:n,end:n});if(!r.length)return;const a=[],l=Ha(s);let c,h;for(c=0,h=r.length;ct!=="borderDash"&&t!=="fill"});function Ks(i,t,e,s){const n=i.options,{[e]:o}=i.getProps([e],s);return Math.abs(t-o){a=Se(r,a,n);const l=n[r],c=n[a];s!==null?(o.push({x:l.x,y:s}),o.push({x:c.x,y:s})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function Se(i,t,e){for(;t>i;t--){const s=e[t];if(!isNaN(s.x)&&!isNaN(s.y))break}return t}function qs(i,t,e,s){return i&&t?s(i[e],t[e]):i?i[e]:t?t[e]:0}function Gs(i,t){let e=[],s=!1;return z(i)?(s=!0,e=i):e=Ka(i,t),e.length?new dt({points:e,options:{tension:0},_loop:s,_fullLoop:s}):null}function Zs(i){return i&&i.fill!==!1}function qa(i,t,e){let n=i[t].fill;const o=[t];let r;if(!e)return n;for(;n!==!1&&o.indexOf(n)===-1;){if(!V(n))return n;if(r=i[n],!r)return!1;if(r.visible)return n;o.push(n),n=r.fill}return!1}function Ga(i,t,e){const s=tl(i);if(D(s))return isNaN(s.value)?!1:s;let n=parseFloat(s);return V(n)&&Math.floor(n)===n?Za(s[0],t,n,e):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function Za(i,t,e,s){return(i==="-"||i==="+")&&(e=t+e),e===t||e<0||e>=s?!1:e}function Qa(i,t){let e=null;return i==="start"?e=t.bottom:i==="end"?e=t.top:D(i)?e=t.getPixelForValue(i.value):t.getBasePixel&&(e=t.getBasePixel()),e}function Ja(i,t,e){let s;return i==="start"?s=e:i==="end"?s=t.options.reverse?t.min:t.max:D(i)?s=i.value:s=t.getBaseValue(),s}function tl(i){const t=i.options,e=t.fill;let s=O(e&&e.target,e);return s===void 0&&(s=!!t.backgroundColor),s===!1||s===null?!1:s===!0?"origin":s}function el(i){const{scale:t,index:e,line:s}=i,n=[],o=s.segments,r=s.points,a=il(t,e);a.push(Gs({x:null,y:t.bottom},s));for(let l=0;l=0;--r){const a=n[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),s&&a.fill&&fi(i.ctx,a,o))}},beforeDatasetsDraw(i,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const s=i.getSortedVisibleDatasetMetas();for(let n=s.length-1;n>=0;--n){const o=s[n].$filler;Zs(o)&&fi(i.ctx,o,i.chartArea)}},beforeDatasetDraw(i,t,e){const s=t.meta.$filler;!Zs(s)||e.drawTime!=="beforeDatasetDraw"||fi(i.ctx,s,i.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Gt={average(i){if(!i.length)return!1;let t,e,s=new Set,n=0,o=0;for(t=0,e=i.length;ta+l)/s.size,y:n/o}},nearest(i,t){if(!i.length)return!1;let e=t.x,s=t.y,n=Number.POSITIVE_INFINITY,o,r,a;for(o=0,r=i.length;or({chart:t,initial:e.initial,numSteps:a,currentStep:Math.min(s-e.start,a)}))}_refresh(){this._request||(this._running=!0,this._request=Ei.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((s,n)=>{if(!s.running||!s.items.length)return;const o=s.items;let a=o.length-1,r=!1,l;for(;a>=0;--a)l=o[a],l._active?(l._total>s.duration&&(s.duration=l._total),l.tick(t),r=!0):(o[a]=o[o.length-1],o.pop());r&&(n.draw(),this._notify(n,s,t,"progress")),o.length||(s.running=!1,this._notify(n,s,t,"complete"),s.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let s=e.get(t);return s||(s={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,s)),s}listen(t,e,s){this._getAnims(t).listeners[e].push(s)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((s,n)=>Math.max(s,n._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const s=e.items;let n=s.length-1;for(;n>=0;--n)s[n].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var lt=new ya;const gs="transparent",va={boolean(i,t,e){return e>.5?t:i},color(i,t,e){const s=Vi(i||gs),n=s.valid&&Vi(t||gs);return n&&n.valid?n.mix(s,e).hexString():t},number(i,t,e){return i+(t-i)*e}};class ka{constructor(t,e,s,n){const o=e[s];n=me([t.to,n,o,t.from]);const a=me([t.from,o,n]);this._active=!0,this._fn=t.fn||va[t.type||typeof a],this._easing=jt[t.easing]||jt.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=s,this._from=a,this._to=n,this._promises=void 0}active(){return this._active}update(t,e,s){if(this._active){this._notify(!1);const n=this._target[this._prop],o=s-this._start,a=this._duration-o;this._start=s,this._duration=Math.floor(Math.max(a,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=me([t.to,e,n,t.from]),this._from=me([t.from,n,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,s=this._duration,n=this._prop,o=this._from,a=this._loop,r=this._to;let l;if(this._active=o!==r&&(a||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[n]=this._fn(o,r,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,s)=>{t.push({res:e,rej:s})})}_notify(t){const e=t?"res":"rej",s=this._promises||[];for(let n=0;n{const o=t[n];if(!O(o))return;const a={};for(const r of e)a[r]=o[r];(E(o.properties)&&o.properties||[n]).forEach(r=>{(r===n||!s.has(r))&&s.set(r,a)})})}_animateOptions(t,e){const s=e.options,n=Sa(t,s);if(!n)return[];const o=this._createAnimations(n,s);return s.$shared&&wa(t.options.$animations,s).then(()=>{t.options=s},()=>{}),o}_createAnimations(t,e){const s=this._properties,n=[],o=t.$animations||(t.$animations={}),a=Object.keys(e),r=Date.now();let l;for(l=a.length-1;l>=0;--l){const c=a[l];if(c.charAt(0)==="$")continue;if(c==="options"){n.push(...this._animateOptions(t,e));continue}const h=e[c];let d=o[c];const f=s.get(c);if(d)if(f&&d.active()){d.update(f,h,r);continue}else d.cancel();if(!f||!f.duration){t[c]=h;continue}o[c]=d=new ka(f,t,c,h),n.push(d)}return n}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const s=this._createAnimations(t,e);if(s.length)return lt.add(this._chart,s),!0}}function wa(i,t){const e=[],s=Object.keys(t);for(let n=0;n0||!e&&o<0)return n.index}return null}function ys(i,t){const{chart:e,_cachedMeta:s}=i,n=e._stacks||(e._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,c=a.axis,h=Ca(o,a,s),d=t.length;let f;for(let u=0;ue[s].axis===t).shift()}function Aa(i,t){return kt(i,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function La(i,t,e){return kt(i,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Xt(i,t){const e=i.controller.index,s=i.vScale&&i.vScale.axis;if(s){t=t||i._parsed;for(const n of t){const o=n._stacks;if(!o||o[s]===void 0||o[s][e]===void 0)return;delete o[s][e],o[s]._visualValues!==void 0&&o[s]._visualValues[e]!==void 0&&delete o[s]._visualValues[e]}}}const ai=i=>i==="reset"||i==="none",vs=(i,t)=>t?i:Object.assign({},i),Ia=(i,t,e)=>i&&!t.hidden&&t._stacked&&{keys:bs(e,!0),values:null};class Kt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,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.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=ni(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Xt(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,s=this.getDataset(),n=(d,f,u,m)=>d==="x"?f:d==="r"?m:u,o=e.xAxisID=C(s.xAxisID,oi(t,"x")),a=e.yAxisID=C(s.yAxisID,oi(t,"y")),r=e.rAxisID=C(s.rAxisID,oi(t,"r")),l=e.indexAxis,c=e.iAxisID=n(l,o,a,r),h=e.vAxisID=n(l,a,o,r);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(a),e.rScale=this.getScaleForId(r),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&zi(this._data,this),t._stacked&&Xt(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),s=this._data;if(O(e)){const n=this._cachedMeta;this._data=Pa(e,n)}else if(s!==e){if(s){zi(s,this);const n=this._cachedMeta;Xt(n),n._parsed=[]}e&&Object.isExtensible(e)&&uo(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,s=this.getDataset();let n=!1;this._dataCheck();const o=e._stacked;e._stacked=ni(e.vScale,e),e.stack!==s.stack&&(n=!0,Xt(e),e.stack=s.stack),this._resyncElements(t),(n||o!==e._stacked)&&(ys(this,e._parsed),e._stacked=ni(e.vScale,e))}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),s=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(s,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:s,_data:n}=this,{iScale:o,_stacked:a}=s,r=o.axis;let l=t===0&&e===n.length?!0:s._sorted,c=t>0&&s._parsed[t-1],h,d,f;if(this._parsing===!1)s._parsed=n,s._sorted=!0,f=n;else{E(n[t])?f=this.parseArrayData(s,n,t,e):O(n[t])?f=this.parseObjectData(s,n,t,e):f=this.parsePrimitiveData(s,n,t,e);const u=()=>d[r]===null||c&&d[r]g||d=0;--f)if(!m()){this.updateRangeFromParsed(c,t,u,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,s=[];let n,o,a;for(n=0,o=e.length;n=0&&tthis.getContext(s,n,e),g=c.resolveNamedOptions(f,u,m,d);return g.$shared&&(g.$shared=l,o[a]=Object.freeze(vs(g,l))),g}_resolveAnimations(t,e,s){const n=this.chart,o=this._cachedDataOpts,a=`animation-${e}`,r=o[a];if(r)return r;let l;if(n.options.animation!==!1){const h=this.chart.config,d=h.datasetAnimationScopeKeys(this._type,e),f=h.getOptionScopes(this.getDataset(),d);l=h.createResolver(f,this.getContext(t,s,e))}const c=new ps(n,l&&l.animations);return l&&l._cacheable&&(o[a]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ai(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const s=this.resolveDataElementOptions(t,e),n=this._sharedOptions,o=this.getSharedOptions(s),a=this.includeOptions(e,o)||o!==n;return this.updateSharedOptions(o,e,s),{sharedOptions:o,includeOptions:a}}updateElement(t,e,s,n){ai(n)?Object.assign(t,s):this._resolveAnimations(e,n).update(t,s)}updateSharedOptions(t,e,s){t&&!ai(e)&&this._resolveAnimations(void 0,e).update(t,s)}_setStyle(t,e,s,n){t.active=n;const o=this.getStyle(e,n);this._resolveAnimations(e,s,n).update(t,{options:!n&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,s){this._setStyle(t,s,"active",!1)}setHoverStyle(t,e,s){this._setStyle(t,s,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,s=this._cachedMeta.data;for(const[r,l,c]of this._syncList)this[r](l,c);this._syncList=[];const n=s.length,o=e.length,a=Math.min(o,n);a&&this.parse(0,a),o>n?this._insertElements(n,o-n,t):o{for(c.length+=e,r=c.length-1;r>=a;r--)c[r]=c[r-e]};for(l(o),r=t;r0&&this.getParsed(e-1);for(let _=0;_=x){w.skip=!0;continue}const y=this.getParsed(_),M=I(y[u]),T=w[f]=a.getPixelForValue(y[f],_),P=w[u]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,y,l):y[u],_);w.skip=isNaN(T)||isNaN(P)||M,w.stop=_>0&&Math.abs(y[f]-k[f])>p,g&&(w.parsed=y,w.raw=c.data[_]),d&&(w.options=h||this.resolveDataElementOptions(_,S.active?"active":n)),b||this.updateElement(S,_,w,n),k=y}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,s=e.options&&e.options.borderWidth||0,n=t.data||[];if(!n.length)return s;const o=n[0].size(this.resolveDataElementOptions(0)),a=n[n.length-1].size(this.resolveDataElementOptions(n.length-1));return Math.max(s,o,a)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}D(ve,"id","line"),D(ve,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),D(ve,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});function Dt(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class ri{constructor(t){D(this,"options");this.options=t||{}}static override(t){Object.assign(ri.prototype,t)}init(){}formats(){return Dt()}parse(){return Dt()}format(){return Dt()}add(){return Dt()}diff(){return Dt()}startOf(){return Dt()}endOf(){return Dt()}}var Fa={_date:ri};function Ra(i,t,e,s){const{controller:n,data:o,_sorted:a}=i,r=n._cachedMeta.iScale,l=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null;if(r&&t===r.axis&&t!=="r"&&a&&o.length){const c=r._reversePixels?ho:xt;if(s){if(n._sharedOptions){const h=o[0],d=typeof h.getRange=="function"&&h.getRange(t);if(d){const f=c(o,t,e-d),u=c(o,t,e+d);return{lo:f.lo,hi:u.hi}}}}else{const h=c(o,t,e);if(l){const{vScale:d}=n._cachedMeta,{_parsed:f}=i,u=f.slice(0,h.lo+1).reverse().findIndex(g=>!I(g[d.axis]));h.lo-=Math.max(0,u);const m=f.slice(h.hi).findIndex(g=>!I(g[d.axis]));h.hi+=Math.max(0,m)}return h}}return{lo:0,hi:o.length-1}}function ke(i,t,e,s,n){const o=i.getSortedVisibleDatasetMetas(),a=e[t];for(let r=0,l=o.length;r{l[a]&&l[a](t[e],n)&&(o.push({element:l,datasetIndex:c,index:h}),r=r||l.inRange(t.x,t.y,n))}),s&&!r?[]:o}var Ha={modes:{index(i,t,e,s){const n=St(t,i),o=e.axis||"x",a=e.includeInvisible||!1,r=e.intersect?li(i,n,o,s,a):ci(i,n,o,!1,s,a),l=[];return r.length?(i.getSortedVisibleDatasetMetas().forEach(c=>{const h=r[0].index,d=c.data[h];d&&!d.skip&&l.push({element:d,datasetIndex:c.index,index:h})}),l):[]},dataset(i,t,e,s){const n=St(t,i),o=e.axis||"xy",a=e.includeInvisible||!1;let r=e.intersect?li(i,n,o,s,a):ci(i,n,o,!1,s,a);if(r.length>0){const l=r[0].datasetIndex,c=i.getDatasetMeta(l).data;r=[];for(let h=0;he.pos===t)}function Ss(i,t){return i.filter(e=>ws.indexOf(e.pos)===-1&&e.box.axis===t)}function Gt(i,t){return i.sort((e,s)=>{const n=t?s:e,o=t?e:s;return n.weight===o.weight?n.index-o.index:n.weight-o.weight})}function Wa(i){const t=[];let e,s,n,o,a,r;for(e=0,s=(i||[]).length;ec.box.fullSize),!0),s=Gt(qt(t,"left"),!0),n=Gt(qt(t,"right")),o=Gt(qt(t,"top"),!0),a=Gt(qt(t,"bottom")),r=Ss(t,"x"),l=Ss(t,"y");return{fullSize:e,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:qt(t,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}function Ms(i,t,e,s){return Math.max(i[e],t[e])+Math.max(i[s],t[s])}function Ds(i,t){i.top=Math.max(i.top,t.top),i.left=Math.max(i.left,t.left),i.bottom=Math.max(i.bottom,t.bottom),i.right=Math.max(i.right,t.right)}function $a(i,t,e,s){const{pos:n,box:o}=e,a=i.maxPadding;if(!O(n)){e.size&&(i[n]-=e.size);const d=s[e.stack]||{size:0,count:1};d.size=Math.max(d.size,e.horizontal?o.height:o.width),e.size=d.size/d.count,i[n]+=e.size}o.getPadding&&Ds(a,o.getPadding());const r=Math.max(0,t.outerWidth-Ms(a,i,"left","right")),l=Math.max(0,t.outerHeight-Ms(a,i,"top","bottom")),c=r!==i.w,h=l!==i.h;return i.w=r,i.h=l,e.horizontal?{same:c,other:h}:{same:h,other:c}}function Ya(i){const t=i.maxPadding;function e(s){const n=Math.max(t[s]-i[s],0);return i[s]+=n,n}i.y+=e("top"),i.x+=e("left"),e("right"),e("bottom")}function Ua(i,t){const e=t.maxPadding;function s(n){const o={left:0,top:0,right:0,bottom:0};return n.forEach(a=>{o[a]=Math.max(t[a],e[a])}),o}return s(i?["left","right"]:["top","bottom"])}function Zt(i,t,e,s){const n=[];let o,a,r,l,c,h;for(o=0,a=i.length,c=0;o{typeof g.beforeLayout=="function"&&g.beforeLayout()});const h=l.reduce((g,p)=>p.box.options&&p.box.options.display===!1?g:g+1,0)||1,d=Object.freeze({outerWidth:t,outerHeight:e,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/h,hBoxMaxHeight:a/2}),f=Object.assign({},n);Ds(f,J(s));const u=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),m=Va(l.concat(c),d);Zt(r.fullSize,u,d,m),Zt(l,u,d,m),Zt(c,u,d,m)&&Zt(l,u,d,m),Ya(u),Ps(r.leftAndTop,u,d,m),u.x+=u.w,u.y+=u.h,Ps(r.rightAndBottom,u,d,m),i.chartArea={left:u.left,top:u.top,right:u.left+u.w,bottom:u.top+u.h,height:u.h,width:u.w},L(r.chartArea,g=>{const p=g.box;Object.assign(p,i.chartArea),p.update(u.w,u.h,{left:0,top:0,right:0,bottom:0})})}};class Cs{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,s){}removeEventListener(t,e,s){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,s,n){return e=Math.max(0,e||t.width),s=s||t.height,{width:e,height:Math.max(0,n?Math.floor(e/n):s)}}isAttached(t){return!0}updateConfig(t){}}class Xa extends Cs{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const Se="$chartjs",Ka={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},Os=i=>i===null||i==="";function qa(i,t){const e=i.style,s=i.getAttribute("height"),n=i.getAttribute("width");if(i[Se]={initial:{height:s,width:n,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",Os(n)){const o=ns(i,"width");o!==void 0&&(i.width=o)}if(Os(s))if(i.style.height==="")i.height=i.width/(t||2);else{const o=ns(i,"height");o!==void 0&&(i.height=o)}return i}const Ts=la?{passive:!0}:!1;function Ga(i,t,e){i&&i.addEventListener(t,e,Ts)}function Za(i,t,e){i&&i.canvas&&i.canvas.removeEventListener(t,e,Ts)}function Qa(i,t){const e=Ka[i.type]||i.type,{x:s,y:n}=St(i,t);return{type:e,chart:t,native:i,x:s!==void 0?s:null,y:n!==void 0?n:null}}function Me(i,t){for(const e of i)if(e===t||e.contains(t))return!0}function Ja(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let a=!1;for(const r of o)a=a||Me(r.addedNodes,s),a=a&&!Me(r.removedNodes,s);a&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}function tr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let a=!1;for(const r of o)a=a||Me(r.removedNodes,s),a=a&&!Me(r.addedNodes,s);a&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}const Qt=new Map;let As=0;function Ls(){const i=window.devicePixelRatio;i!==As&&(As=i,Qt.forEach((t,e)=>{e.currentDevicePixelRatio!==i&&t()}))}function er(i,t){Qt.size||window.addEventListener("resize",Ls),Qt.set(i,t)}function ir(i){Qt.delete(i),Qt.size||window.removeEventListener("resize",Ls)}function sr(i,t,e){const s=i.canvas,n=s&&si(s);if(!n)return;const o=Bi((r,l)=>{const c=n.clientWidth;e(r,l),c{const l=r[0],c=l.contentRect.width,h=l.contentRect.height;c===0&&h===0||o(c,h)});return a.observe(n),er(i,o),a}function hi(i,t,e){e&&e.disconnect(),t==="resize"&&ir(i)}function nr(i,t,e){const s=i.canvas,n=Bi(o=>{i.ctx!==null&&e(Qa(o,i))},i);return Ga(s,t,n),n}class or extends Cs{acquireContext(t,e){const s=t&&t.getContext&&t.getContext("2d");return s&&s.canvas===t?(qa(t,e),s):null}releaseContext(t){const e=t.canvas;if(!e[Se])return!1;const s=e[Se].initial;["height","width"].forEach(o=>{const a=s[o];I(a)?e.removeAttribute(o):e.setAttribute(o,a)});const n=s.style||{};return Object.keys(n).forEach(o=>{e.style[o]=n[o]}),e.width=e.width,delete e[Se],!0}addEventListener(t,e,s){this.removeEventListener(t,e);const n=t.$proxies||(t.$proxies={}),a={attach:Ja,detach:tr,resize:sr}[e]||nr;n[e]=a(t,e,s)}removeEventListener(t,e){const s=t.$proxies||(t.$proxies={}),n=s[e];if(!n)return;({attach:hi,detach:hi,resize:hi}[e]||Za)(t,e,n),s[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,s,n){return ra(t,e,s,n)}isAttached(t){const e=t&&si(t);return!!(e&&e.isConnected)}}function ar(i){return!ii()||typeof OffscreenCanvas<"u"&&i instanceof OffscreenCanvas?Xa:or}class pt{constructor(){D(this,"x");D(this,"y");D(this,"active",!1);D(this,"options");D(this,"$animations")}tooltipPosition(t){const{x:e,y:s}=this.getProps(["x","y"],t);return{x:e,y:s}}hasValue(){return Vt(this.x)&&Vt(this.y)}getProps(t,e){const s=this.$animations;if(!e||!s)return this;const n={};return t.forEach(o=>{n[o]=s[o]&&s[o].active()?s[o]._to:this[o]}),n}}D(pt,"defaults",{}),D(pt,"defaultRoutes");function rr(i,t){const e=i.options.ticks,s=lr(i),n=Math.min(e.maxTicksLimit||s,s),o=e.major.enabled?hr(t):[],a=o.length,r=o[0],l=o[a-1],c=[];if(a>n)return dr(t,c,o,a/n),c;const h=cr(o,t,n);if(a>0){let d,f;const u=a>1?Math.round((l-r)/(a-1)):null;for(De(t,c,h,I(u)?0:r-u,r),d=0,f=a-1;dn)return l}return Math.max(n,1)}function hr(i){const t=[];let e,s;for(e=0,s=i.length;ei==="left"?"right":i==="right"?"left":i,Is=(i,t,e)=>t==="top"||t==="left"?i[t]+e:i[t]-e,Fs=(i,t)=>Math.min(t||i,i);function Rs(i,t){const e=[],s=i.length/t,n=i.length;let o=0;for(;oa+r)))return l}function pr(i,t){L(i,e=>{const s=e.gc,n=s.length/2;let o;if(n>t){for(o=0;os?s:e,s=n&&e>s?e:s,{min:et(e,et(s,e)),max:et(s,et(e,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){F(this.options.beforeUpdate,[this])}update(t,e,s){const{beginAtZero:n,grace:o,ticks:a}=this.options,r=a.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=s=Object.assign({left:0,right:0,top:0,bottom:0},s),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+s.left+s.right:this.height+s.top+s.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Ho(this,o,n),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=r=o||s<=1||!this.isHorizontal()){this.labelRotation=n;return}const h=this._getLabelSizes(),d=h.widest.width,f=h.highest.height,u=K(this.chart.width-d,0,this.maxWidth);r=t.offset?this.maxWidth/s:u/(s-1),d+6>r&&(r=u/(s-(t.offset?.5:1)),l=this.maxHeight-Jt(t.grid)-e.padding-zs(t.title,this.chart.options.font),c=Math.sqrt(d*d+f*f),a=ao(Math.min(Math.asin(K((h.highest.height+6)/r,-1,1)),Math.asin(K(l/c,-1,1))-Math.asin(K(f/c,-1,1)))),a=Math.max(n,Math.min(o,a))),this.labelRotation=a}afterCalculateLabelRotation(){F(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){F(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:s,title:n,grid:o}}=this,a=this._isVisible(),r=this.isHorizontal();if(a){const l=zs(n,e.options.font);if(r?(t.width=this.maxWidth,t.height=Jt(o)+l):(t.height=this.maxHeight,t.width=Jt(o)+l),s.display&&this.ticks.length){const{first:c,last:h,widest:d,highest:f}=this._getLabelSizes(),u=s.padding*2,m=_t(this.labelRotation),g=Math.cos(m),p=Math.sin(m);if(r){const b=s.mirror?0:p*d.width+g*f.height;t.height=Math.min(this.maxHeight,t.height+b+u)}else{const b=s.mirror?0:g*d.width+p*f.height;t.width=Math.min(this.maxWidth,t.width+b+u)}this._calculatePadding(c,h,p,g)}}this._handleMargins(),r?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,s,n){const{ticks:{align:o,padding:a},position:r}=this.options,l=this.labelRotation!==0,c=r!=="top"&&this.axis==="x";if(this.isHorizontal()){const h=this.getPixelForTick(0)-this.left,d=this.right-this.getPixelForTick(this.ticks.length-1);let f=0,u=0;l?c?(f=n*t.width,u=s*e.height):(f=s*t.height,u=n*e.width):o==="start"?u=e.width:o==="end"?f=t.width:o!=="inner"&&(f=t.width/2,u=e.width/2),this.paddingLeft=Math.max((f-h+a)*this.width/(this.width-h),0),this.paddingRight=Math.max((u-d+a)*this.width/(this.width-d),0)}else{let h=e.height/2,d=t.height/2;o==="start"?(h=0,d=t.height):o==="end"&&(h=e.height,d=0),this.paddingTop=h+a,this.paddingBottom=d+a}}_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(){F(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,s;for(e=0,s=t.length;e({width:a[M]||0,height:r[M]||0});return{first:y(0),last:y(e-1),widest:y(S),highest:y(w),widths:a,heights:r}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return co(this._alignToPixels?vt(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&tr*n?r/s:l/n:l*n0}_computeGridLineItems(t){const e=this.axis,s=this.chart,n=this.options,{grid:o,position:a,border:r}=n,l=o.offset,c=this.isHorizontal(),d=this.ticks.length+(l?1:0),f=Jt(o),u=[],m=r.setContext(this.getContext()),g=m.display?m.width:0,p=g/2,b=function(B){return vt(s,B,g)};let x,v,k,_,S,w,y,M,T,P,A,Y;if(a==="top")x=b(this.bottom),w=this.bottom-f,M=x-p,P=b(t.top)+p,Y=t.bottom;else if(a==="bottom")x=b(this.top),P=t.top,Y=b(t.bottom)-p,w=x+p,M=this.top+f;else if(a==="left")x=b(this.right),S=this.right-f,y=x-p,T=b(t.left)+p,A=t.right;else if(a==="right")x=b(this.left),T=t.left,A=b(t.right)-p,S=x+p,y=this.left+f;else if(e==="x"){if(a==="center")x=b((t.top+t.bottom)/2+.5);else if(O(a)){const B=Object.keys(a)[0],q=a[B];x=b(this.chart.scales[B].getPixelForValue(q))}P=t.top,Y=t.bottom,w=x+p,M=w+f}else if(e==="y"){if(a==="center")x=b((t.left+t.right)/2);else if(O(a)){const B=Object.keys(a)[0],q=a[B];x=b(this.chart.scales[B].getPixelForValue(q))}S=x-p,y=S-f,T=t.left,A=t.right}const ot=C(n.ticks.maxTicksLimit,d),z=Math.max(1,Math.ceil(d/ot));for(v=0;v0&&(Ct-=Pt/2);break}Be={left:Ct,top:se,width:Pt+Et.width,height:ie+Et.height,color:z.backdropColor}}p.push({label:k,font:M,textOffset:A,options:{rotation:g,color:q,strokeColor:ze,strokeWidth:Ee,textAlign:zt,textBaseline:Y,translation:[_,S],backdrop:Be}})}return p}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-_t(this.labelRotation))return t==="top"?"left":"right";let n="center";return e.align==="start"?n="left":e.align==="end"?n="right":e.align==="inner"&&(n="inner"),n}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:s,mirror:n,padding:o}}=this.options,a=this._getLabelSizes(),r=t+o,l=a.widest.width;let c,h;return e==="left"?n?(h=this.right+o,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h+=l)):(h=this.right-r,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h=this.left)):e==="right"?n?(h=this.left+o,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h-=l)):(h=this.left+r,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h=this.right)):c="right",{textAlign:c,x:h}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:s,top:n,width:o,height:a}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(s,n,o,a),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const n=this.ticks.findIndex(o=>o.value===t);return n>=0?e.setContext(this.getContext(n)).lineWidth:0}drawGrid(t){const e=this.options.grid,s=this.ctx,n=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,a;const r=(l,c,h)=>{!h.width||!h.color||(s.save(),s.lineWidth=h.width,s.strokeStyle=h.color,s.setLineDash(h.borderDash||[]),s.lineDashOffset=h.borderDashOffset,s.beginPath(),s.moveTo(l.x,l.y),s.lineTo(c.x,c.y),s.stroke(),s.restore())};if(e.display)for(o=0,a=n.length;o{this.draw(o)}}]:[{z:s,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:n,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),s=this.axis+"AxisID",n=[];let o,a;for(o=0,a=e.length;o{const s=e.split("."),n=s.pop(),o=[i].concat(s).join("."),a=t[e].split("."),r=a.pop(),l=a.join(".");R.route(o,n,l,r)})}function kr(i){return"id"in i&&"defaults"in i}class wr{constructor(){this.controllers=new Pe(Kt,"datasets",!0),this.elements=new Pe(pt,"elements"),this.plugins=new Pe(Object,"plugins"),this.scales=new Pe(Rt,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,s){[...e].forEach(n=>{const o=s||this._getRegistryForType(n);s||o.isForType(n)||o===this.plugins&&n.id?this._exec(t,o,n):L(n,a=>{const r=s||this._getRegistryForType(a);this._exec(t,r,a)})})}_exec(t,e,s){const n=$e(t);F(s["before"+n],[],s),e[t](s),F(s["after"+n],[],s)}_getRegistryForType(t){for(let e=0;eo.filter(r=>!a.some(l=>r.plugin.id===l.plugin.id));this._notify(n(e,s),t,"stop"),this._notify(n(s,e),t,"start")}}function Mr(i){const t={},e=[],s=Object.keys(st.plugins.items);for(let o=0;o1&&Es(i[0].toLowerCase());if(s)return s}throw new Error(`Cannot determine type of '${i}' axis. Please provide 'axis' or 'position' option.`)}function Bs(i,t,e){if(e[t+"AxisID"]===i)return{axis:t}}function Lr(i,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(s=>s.xAxisID===i||s.yAxisID===i);if(e.length)return Bs(i,"x",e[0])||Bs(i,"y",e[0])}return{}}function Ir(i,t){const e=yt[i.type]||{scales:{}},s=t.scales||{},n=di(i.type,t),o=Object.create(null);return Object.keys(s).forEach(a=>{const r=s[a];if(!O(r))return console.error(`Invalid scale configuration for scale: ${a}`);if(r._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${a}`);const l=fi(a,r,Lr(a,i),R.scales[r.type]),c=Tr(l,n),h=e.scales||{};o[a]=Wt(Object.create(null),[{axis:l},r,h[l],h[c]])}),i.data.datasets.forEach(a=>{const r=a.type||i.type,l=a.indexAxis||di(r,t),h=(yt[r]||{}).scales||{};Object.keys(h).forEach(d=>{const f=Or(d,l),u=a[f+"AxisID"]||f;o[u]=o[u]||Object.create(null),Wt(o[u],[{axis:f},s[u],h[d]])})}),Object.keys(o).forEach(a=>{const r=o[a];Wt(r,[R.scales[r.type],R.scale])}),o}function Hs(i){const t=i.options||(i.options={});t.plugins=C(t.plugins,{}),t.scales=Ir(i,t)}function Ws(i){return i=i||{},i.datasets=i.datasets||[],i.labels=i.labels||[],i}function Fr(i){return i=i||{},i.data=Ws(i.data),Hs(i),i}const Ns=new Map,Vs=new Set;function Ce(i,t){let e=Ns.get(i);return e||(e=t(),Ns.set(i,e),Vs.add(e)),e}const te=(i,t,e)=>{const s=ce(t,e);s!==void 0&&i.add(s)};class Rr{constructor(t){this._config=Fr(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Ws(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Hs(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Ce(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return Ce(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return Ce(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,s=this.type;return Ce(`${s}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const s=this._scopeCache;let n=s.get(t);return(!n||e)&&(n=new Map,s.set(t,n)),n}getOptionScopes(t,e,s){const{options:n,type:o}=this,a=this._cachedScopes(t,s),r=a.get(e);if(r)return r;const l=new Set;e.forEach(h=>{t&&(l.add(t),h.forEach(d=>te(l,t,d))),h.forEach(d=>te(l,n,d)),h.forEach(d=>te(l,yt[o]||{},d)),h.forEach(d=>te(l,R,d)),h.forEach(d=>te(l,qe,d))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),Vs.has(e)&&a.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,yt[e]||{},R.datasets[e]||{},{type:e},R,qe]}resolveNamedOptions(t,e,s,n=[""]){const o={$shared:!0},{resolver:a,subPrefixes:r}=js(this._resolverCache,t,n);let l=a;if(Er(a,e)){o.$shared=!1,s=ft(s)?s():s;const c=this.createResolver(t,s,r);l=Lt(a,s,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,s=[""],n){const{resolver:o}=js(this._resolverCache,t,s);return O(e)?Lt(o,e,void 0,n):o}}function js(i,t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));const n=e.join();let o=s.get(n);return o||(o={resolver:Je(t,e),subPrefixes:e.filter(r=>!r.toLowerCase().includes("hover"))},s.set(n,o)),o}const zr=i=>O(i)&&Object.getOwnPropertyNames(i).some(t=>ft(i[t]));function Er(i,t){const{isScriptable:e,isIndexable:s}=Gi(i);for(const n of t){const o=e(n),a=s(n),r=(a||o)&&i[n];if(o&&(ft(r)||zr(r))||a&&E(r))return!0}return!1}var Br="4.5.1";const Hr=["top","bottom","left","right","chartArea"];function $s(i,t){return i==="top"||i==="bottom"||Hr.indexOf(i)===-1&&t==="x"}function Ys(i,t){return function(e,s){return e[i]===s[i]?e[t]-s[t]:e[i]-s[i]}}function Us(i){const t=i.chart,e=t.options.animation;t.notifyPlugins("afterRender"),F(e&&e.onComplete,[i],t)}function Wr(i){const t=i.chart,e=t.options.animation;F(e&&e.onProgress,[i],t)}function Xs(i){return ii()&&typeof i=="string"?i=document.getElementById(i):i&&i.length&&(i=i[0]),i&&i.canvas&&(i=i.canvas),i}const Oe={},Ks=i=>{const t=Xs(i);return Object.values(Oe).filter(e=>e.canvas===t).pop()};function Nr(i,t,e){const s=Object.keys(i);for(const n of s){const o=+n;if(o>=t){const a=i[n];delete i[n],(e>0||o>t)&&(i[o+e]=a)}}}function Vr(i,t,e,s){return!e||i.type==="mouseout"?null:s?t:i}class ct{static register(...t){st.add(...t),qs()}static unregister(...t){st.remove(...t),qs()}constructor(t,e){const s=this.config=new Rr(e),n=Xs(t),o=Ks(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ar(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,c=l&&l.height,h=l&&l.width;if(this.id=Xn(),this.ctx=r,this.canvas=l,this.width=h,this.height=c,this._options=a,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 Sr,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=po(d=>this.update(d),a.resizeDelay||0),this._dataChanges=[],Oe[this.id]=this,!r||!l){console.error("Failed to create chart: can't acquire context from the given item");return}lt.listen(this,"complete",Us),lt.listen(this,"progress",Wr),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:s,height:n,_aspectRatio:o}=this;return I(t)?e&&o?o:n?s/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return st}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ss(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Xi(this.canvas,this.ctx),this}stop(){return lt.stop(this),this}resize(t,e){lt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const s=this.options,n=this.canvas,o=s.maintainAspectRatio&&this.aspectRatio,a=this.platform.getMaximumSize(n,t,e,o),r=s.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=a.width,this.height=a.height,this._aspectRatio=this.aspectRatio,ss(this,r,!0)&&(this.notifyPlugins("resize",{size:a}),F(s.onResize,[this,a],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};L(e,(s,n)=>{s.id=n})}buildOrUpdateScales(){const t=this.options,e=t.scales,s=this.scales,n=Object.keys(s).reduce((a,r)=>(a[r]=!1,a),{});let o=[];e&&(o=o.concat(Object.keys(e).map(a=>{const r=e[a],l=fi(a,r),c=l==="r",h=l==="x";return{options:r,dposition:c?"chartArea":h?"bottom":"left",dtype:c?"radialLinear":h?"category":"linear"}}))),L(o,a=>{const r=a.options,l=r.id,c=fi(l,r),h=C(r.type,a.dtype);(r.position===void 0||$s(r.position,c)!==$s(a.dposition))&&(r.position=a.dposition),n[l]=!0;let d=null;if(l in s&&s[l].type===h)d=s[l];else{const f=st.getScale(h);d=new f({id:l,type:h,ctx:this.ctx,chart:this}),s[d.id]=d}d.init(r,t)}),L(n,(a,r)=>{a||delete s[r]}),L(s,a=>{gt.configure(this,a,a.options),gt.addBox(this,a)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,s=t.length;if(t.sort((n,o)=>n.index-o.index),s>e){for(let n=e;ne.length&&delete this._stacks,t.forEach((s,n)=>{e.filter(o=>o===s._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let s,n;for(this._removeUnreferencedMetasets(),s=0,n=e.length;s{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const s=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),n=this._animationsDisabled=!s.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let a=0;for(let c=0,h=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(Ys("z","_idx"));const{_active:r,_lastEvent:l}=this;l?this._eventHandler(l,!0):r.length&&this._updateHoverStyles(r,r,!0),this.render()}_updateScales(){L(this.scales,t=>{gt.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),s=new Set(t.events);(!Oi(e,s)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:s,start:n,count:o}of e){const a=s==="_removeElements"?-o:o;Nr(t,n,a)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,s=o=>new Set(t.filter(a=>a[0]===o).map((a,r)=>r+","+a.splice(1).join(","))),n=s(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;gt.update(this,this.width,this.height,t);const e=this.chartArea,s=e.width<=0||e.height<=0;this._layers=[],L(this.boxes,n=>{s&&n.position==="chartArea"||(n.configure&&n.configure(),this._layers.push(...n._layers()))},this),this._layers.forEach((n,o)=>{n._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,s=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,s={meta:t,index:t.index,cancelable:!0},n=us(this,t);this.notifyPlugins("beforeDatasetDraw",s)!==!1&&(n&&ue(e,n),t.controller.draw(),n&&ge(e),s.cancelable=!1,this.notifyPlugins("afterDatasetDraw",s))}isPointInArea(t){return Yt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,s,n){const o=Ha.modes[e];return typeof o=="function"?o(this,t,s,n):[]}getDatasetMeta(t){const e=this.data.datasets[t],s=this._metasets;let n=s.filter(o=>o&&o._dataset===e).pop();return n||(n={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},s.push(n)),n}getContext(){return this.$context||(this.$context=kt(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const s=this.getDatasetMeta(t);return typeof s.hidden=="boolean"?!s.hidden:!e.hidden}setDatasetVisibility(t,e){const s=this.getDatasetMeta(t);s.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,s){const n=s?"show":"hide",o=this.getDatasetMeta(t),a=o.controller._resolveAnimations(void 0,n);he(e)?(o.data[e].hidden=!s,this.update()):(this.setDatasetVisibility(t,s),a.update(o,{visible:s}),this.update(r=>r.datasetIndex===t?n:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),lt.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,a),t[o]=a},n=(o,a,r)=>{o.offsetX=a,o.offsetY=r,this._eventHandler(o)};L(this.options.events,o=>s(o,n))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,s=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},n=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let a;const r=()=>{n("attach",r),this.attached=!0,this.resize(),s("resize",o),s("detach",a)};a=()=>{this.attached=!1,n("resize",o),this._stop(),this._resize(0,0),s("attach",r)},e.isAttached(this.canvas)?r():a()}unbindEvents(){L(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},L(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,s){const n=s?"set":"remove";let o,a,r,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+n+"DatasetHoverStyle"]()),r=0,l=t.length;r{const r=this.getDatasetMeta(o);if(!r)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:r.data[a],index:a}});!re(s,e)&&(this._active=s,this._lastEvent=null,this._updateHoverStyles(s,e))}notifyPlugins(t,e,s){return this._plugins.notify(this,t,e,s)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,s){const n=this.options.hover,o=(l,c)=>l.filter(h=>!c.some(d=>h.datasetIndex===d.datasetIndex&&h.index===d.index)),a=o(e,t),r=s?t:o(t,e);a.length&&this.updateHoverStyle(a,n.mode,!1),r.length&&n.mode&&this.updateHoverStyle(r,n.mode,!0)}_eventHandler(t,e){const s={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},n=a=>(a.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",s,n)===!1)return;const o=this._handleEvent(t,e,s.inChartArea);return s.cancelable=!1,this.notifyPlugins("afterEvent",s,n),(o||s.changed)&&this.render(),this}_handleEvent(t,e,s){const{_active:n=[],options:o}=this,a=e,r=this._getActiveElements(t,n,s,a),l=Jn(t),c=Vr(t,this._lastEvent,s,l);s&&(this._lastEvent=null,F(o.onHover,[t,r,this],this),l&&F(o.onClick,[t,r,this],this));const h=!re(r,n);return(h||e)&&(this._active=r,this._updateHoverStyles(r,n,e)),this._lastEvent=c,h}_getActiveElements(t,e,s,n){if(t.type==="mouseout")return[];if(!s)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,n)}}D(ct,"defaults",R),D(ct,"instances",Oe),D(ct,"overrides",yt),D(ct,"registry",st),D(ct,"version",Br),D(ct,"getChart",Ks);function qs(){return L(ct.instances,i=>i._plugins.invalidate())}function Gs(i,t,e=t){i.lineCap=C(e.borderCapStyle,t.borderCapStyle),i.setLineDash(C(e.borderDash,t.borderDash)),i.lineDashOffset=C(e.borderDashOffset,t.borderDashOffset),i.lineJoin=C(e.borderJoinStyle,t.borderJoinStyle),i.lineWidth=C(e.borderWidth,t.borderWidth),i.strokeStyle=C(e.borderColor,t.borderColor)}function jr(i,t,e){i.lineTo(e.x,e.y)}function $r(i){return i.stepped?Oo:i.tension||i.cubicInterpolationMode==="monotone"?To:jr}function Zs(i,t,e={}){const s=i.length,{start:n=0,end:o=s-1}=e,{start:a,end:r}=t,l=Math.max(n,a),c=Math.min(o,r),h=nr&&o>r;return{count:s,start:l,loop:t.loop,ilen:c(a+(c?r-k:k))%o,v=()=>{g!==p&&(i.lineTo(h,p),i.lineTo(h,g),i.lineTo(h,b))};for(l&&(u=n[x(0)],i.moveTo(u.x,u.y)),f=0;f<=r;++f){if(u=n[x(f)],u.skip)continue;const k=u.x,_=u.y,S=k|0;S===m?(_p&&(p=_),h=(d*h+k)/++d):(v(),i.lineTo(k,_),m=S,d=0,g=p=_),b=_}v()}function ui(i){const t=i.options,e=t.borderDash&&t.borderDash.length;return!i._decimated&&!i._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?Ur:Yr}function Xr(i){return i.stepped?ca:i.tension||i.cubicInterpolationMode==="monotone"?ha:Mt}function Kr(i,t,e,s){let n=t._path;n||(n=t._path=new Path2D,t.path(n,e,s)&&n.closePath()),Gs(i,t.options),i.stroke(n)}function qr(i,t,e,s){const{segments:n,options:o}=t,a=ui(t);for(const r of n)Gs(i,o,r.style),i.beginPath(),a(i,t,r,{start:e,end:e+s-1})&&i.closePath(),i.stroke()}const Gr=typeof Path2D=="function";function Zr(i,t,e,s){Gr&&!t.options.segment?Kr(i,t,e,s):qr(i,t,e,s)}class mt extends pt{constructor(t){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,t&&Object.assign(this,t)}updateControlPoints(t,e){const s=this.options;if((s.tension||s.cubicInterpolationMode==="monotone")&&!s.stepped&&!this._pointsUpdated){const n=s.spanGaps?this._loop:this._fullLoop;ea(this._points,s,t,n,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=ma(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,s=t.length;return s&&e[t[s-1].end]}interpolate(t,e){const s=this.options,n=t[e],o=this.points,a=hs(this,{property:e,start:n,end:n});if(!a.length)return;const r=[],l=Xr(s);let c,h;for(c=0,h=a.length;ct!=="borderDash"&&t!=="fill"});function Qs(i,t,e,s){const n=i.options,{[e]:o}=i.getProps([e],s);return Math.abs(t-o){r=Ae(a,r,n);const l=n[a],c=n[r];s!==null?(o.push({x:l.x,y:s}),o.push({x:c.x,y:s})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function Ae(i,t,e){for(;t>i;t--){const s=e[t];if(!isNaN(s.x)&&!isNaN(s.y))break}return t}function Js(i,t,e,s){return i&&t?s(i[e],t[e]):i?i[e]:t?t[e]:0}function tn(i,t){let e=[],s=!1;return E(i)?(s=!0,e=i):e=Jr(i,t),e.length?new mt({points:e,options:{tension:0},_loop:s,_fullLoop:s}):null}function en(i){return i&&i.fill!==!1}function tl(i,t,e){let n=i[t].fill;const o=[t];let a;if(!e)return n;for(;n!==!1&&o.indexOf(n)===-1;){if(!W(n))return n;if(a=i[n],!a)return!1;if(a.visible)return n;o.push(n),n=a.fill}return!1}function el(i,t,e){const s=ol(i);if(O(s))return isNaN(s.value)?!1:s;let n=parseFloat(s);return W(n)&&Math.floor(n)===n?il(s[0],t,n,e):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function il(i,t,e,s){return(i==="-"||i==="+")&&(e=t+e),e===t||e<0||e>=s?!1:e}function sl(i,t){let e=null;return i==="start"?e=t.bottom:i==="end"?e=t.top:O(i)?e=t.getPixelForValue(i.value):t.getBasePixel&&(e=t.getBasePixel()),e}function nl(i,t,e){let s;return i==="start"?s=e:i==="end"?s=t.options.reverse?t.min:t.max:O(i)?s=i.value:s=t.getBaseValue(),s}function ol(i){const t=i.options,e=t.fill;let s=C(e&&e.target,e);return s===void 0&&(s=!!t.backgroundColor),s===!1||s===null?!1:s===!0?"origin":s}function al(i){const{scale:t,index:e,line:s}=i,n=[],o=s.segments,a=s.points,r=rl(t,e);r.push(tn({x:null,y:t.bottom},s));for(let l=0;l=0;--a){const r=n[a].$filler;r&&(r.line.updateControlPoints(o,r.axis),s&&r.fill&&pi(i.ctx,r,o))}},beforeDatasetsDraw(i,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const s=i.getSortedVisibleDatasetMetas();for(let n=s.length-1;n>=0;--n){const o=s[n].$filler;en(o)&&pi(i.ctx,o,i.chartArea)}},beforeDatasetDraw(i,t,e){const s=t.meta.$filler;!en(s)||e.drawTime!=="beforeDatasetDraw"||pi(i.ctx,s,i.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const rn=(i,t)=>{let{boxHeight:e=t,boxWidth:s=t}=i;return i.usePointStyle&&(e=Math.min(e,t),s=i.pointStyleWidth||Math.min(s,t)),{boxWidth:s,boxHeight:e,itemHeight:Math.max(t,e)}},_l=(i,t)=>i!==null&&t!==null&&i.datasetIndex===t.datasetIndex&&i.index===t.index;class ln extends pt{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.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(t,e,s){this.maxWidth=t,this.maxHeight=e,this._margins=s,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 t=this.options.labels||{};let e=F(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter(s=>t.filter(s,this.chart.data))),t.sort&&(e=e.sort((s,n)=>t.sort(s,n,this.chart.data))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display){this.width=this.height=0;return}const s=t.labels,n=V(s.font),o=n.size,a=this._computeTitleHeight(),{boxWidth:r,itemHeight:l}=rn(s,o);let c,h;e.font=n.string,this.isHorizontal()?(c=this.maxWidth,h=this._fitRows(a,o,r,l)+10):(h=this.maxHeight,c=this._fitCols(a,n,r,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,s,n){const{ctx:o,maxWidth:a,options:{labels:{padding:r}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],h=n+r;let d=t;o.textAlign="left",o.textBaseline="middle";let f=-1,u=-h;return this.legendItems.forEach((m,g)=>{const p=s+e/2+o.measureText(m.text).width;(g===0||c[c.length-1]+p+2*r>a)&&(d+=h,c[c.length-(g>0?0:1)]=0,u+=h,f++),l[g]={left:0,top:u,row:f,width:p,height:n},c[c.length-1]+=p+r}),d}_fitCols(t,e,s,n){const{ctx:o,maxHeight:a,options:{labels:{padding:r}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],h=a-t;let d=r,f=0,u=0,m=0,g=0;return this.legendItems.forEach((p,b)=>{const{itemWidth:x,itemHeight:v}=xl(s,e,o,p,n);b>0&&u+v+2*r>h&&(d+=f+r,c.push({width:f,height:u}),m+=f+r,g++,f=u=0),l[b]={left:m,top:u,col:g,width:x,height:v},f=Math.max(f,x),u+=v+r}),d+=f,c.push({width:f,height:u}),d}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:s,labels:{padding:n},rtl:o}}=this,a=Ft(o,this.left,this.width);if(this.isHorizontal()){let r=0,l=U(s,this.left+n,this.right-this.lineWidths[r]);for(const c of e)r!==c.row&&(r=c.row,l=U(s,this.left+n,this.right-this.lineWidths[r])),c.top+=this.top+t+n,c.left=a.leftForLtr(a.x(l),c.width),l+=c.width+n}else{let r=0,l=U(s,this.top+t+n,this.bottom-this.columnSizes[r].height);for(const c of e)c.col!==r&&(r=c.col,l=U(s,this.top+t+n,this.bottom-this.columnSizes[r].height)),c.top=l,c.left+=this.left+n,c.left=a.leftForLtr(a.x(c.left),c.width),l+=c.height+n}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;ue(t,this),this._draw(),ge(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:s,ctx:n}=this,{align:o,labels:a}=t,r=R.color,l=Ft(t.rtl,this.left,this.width),c=V(a.font),{padding:h}=a,d=c.size,f=d/2;let u;this.drawTitle(),n.textAlign=l.textAlign("left"),n.textBaseline="middle",n.lineWidth=.5,n.font=c.string;const{boxWidth:m,boxHeight:g,itemHeight:p}=rn(a,d),b=function(S,w,y){if(isNaN(m)||m<=0||isNaN(g)||g<0)return;n.save();const M=C(y.lineWidth,1);if(n.fillStyle=C(y.fillStyle,r),n.lineCap=C(y.lineCap,"butt"),n.lineDashOffset=C(y.lineDashOffset,0),n.lineJoin=C(y.lineJoin,"miter"),n.lineWidth=M,n.strokeStyle=C(y.strokeStyle,r),n.setLineDash(C(y.lineDash,[])),a.usePointStyle){const T={radius:g*Math.SQRT2/2,pointStyle:y.pointStyle,rotation:y.rotation,borderWidth:M},P=l.xPlus(S,m/2),A=w+f;Ki(n,T,P,A,a.pointStyleWidth&&m)}else{const T=w+Math.max((d-g)/2,0),P=l.leftForLtr(S,m),A=Ut(y.borderRadius);n.beginPath(),Object.values(A).some(Y=>Y!==0)?Qe(n,{x:P,y:T,w:m,h:g,radius:A}):n.rect(P,T,m,g),n.fill(),M!==0&&n.stroke()}n.restore()},x=function(S,w,y){pe(n,y.text,S,w+p/2,c,{strikethrough:y.hidden,textAlign:l.textAlign(y.textAlign)})},v=this.isHorizontal(),k=this._computeTitleHeight();v?u={x:U(o,this.left+h,this.right-s[0]),y:this.top+h+k,line:0}:u={x:this.left+h,y:U(o,this.top+k+h,this.bottom-e[0].height),line:0},os(this.ctx,t.textDirection);const _=p+h;this.legendItems.forEach((S,w)=>{n.strokeStyle=S.fontColor,n.fillStyle=S.fontColor;const y=n.measureText(S.text).width,M=l.textAlign(S.textAlign||(S.textAlign=a.textAlign)),T=m+f+y;let P=u.x,A=u.y;l.setWidth(this.width),v?w>0&&P+T+h>this.right&&(A=u.y+=_,u.line++,P=u.x=U(o,this.left+h,this.right-s[u.line])):w>0&&A+_>this.bottom&&(P=u.x=P+e[u.line].width+h,u.line++,A=u.y=U(o,this.top+k+h,this.bottom-e[u.line].height));const Y=l.x(P);if(b(Y,A,S),P=mo(M,P+m+f,v?P+T:this.right,t.rtl),x(l.x(P),A,S),v)u.x+=T+h;else if(typeof S.text!="string"){const ot=c.lineHeight;u.y+=cn(S,ot)+h}else u.y+=_}),as(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,s=V(e.font),n=J(e.padding);if(!e.display)return;const o=Ft(t.rtl,this.left,this.width),a=this.ctx,r=e.position,l=s.size/2,c=n.top+l;let h,d=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),h=this.top+c,d=U(t.align,d,this.right-f);else{const m=this.columnSizes.reduce((g,p)=>Math.max(g,p.height),0);h=c+U(t.align,this.top,this.bottom-m-t.labels.padding-this._computeTitleHeight())}const u=U(r,d,d+f);a.textAlign=o.textAlign(Hi(r)),a.textBaseline="middle",a.strokeStyle=e.color,a.fillStyle=e.color,a.font=s.string,pe(a,e.text,u,h,s)}_computeTitleHeight(){const t=this.options.title,e=V(t.font),s=J(t.padding);return t.display?e.lineHeight+s.height:0}_getLegendItemAt(t,e){let s,n,o;if(At(t,this.left,this.right)&&At(e,this.top,this.bottom)){for(o=this.legendHitBoxes,s=0;so.length>a.length?o:a)),t+e.size/2+s.measureText(n).width}function vl(i,t,e){let s=i;return typeof t.text!="string"&&(s=cn(t,e)),s}function cn(i,t){const e=i.text?i.text.length:0;return t*e}function kl(i,t){return!!((i==="mousemove"||i==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(i==="click"||i==="mouseup"))}var wl={id:"legend",_element:ln,start(i,t,e){const s=i.legend=new ln({ctx:i.ctx,options:e,chart:i});gt.configure(i,s,e),gt.addBox(i,s)},stop(i){gt.removeBox(i,i.legend),delete i.legend},beforeUpdate(i,t,e){const s=i.legend;gt.configure(i,s,e),s.options=e},afterUpdate(i){const t=i.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(i,t){t.replay||i.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(i,t,e){const s=t.datasetIndex,n=e.chart;n.isDatasetVisible(s)?(n.hide(s),t.hidden=!0):(n.show(s),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:i=>i.chart.options.color,boxWidth:40,padding:10,generateLabels(i){const t=i.data.datasets,{labels:{usePointStyle:e,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=i.legend.options;return i._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(e?0:void 0),h=J(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:c.borderColor,pointStyle:s||c.pointStyle,rotation:c.rotation,textAlign:n||c.textAlign,borderRadius:a&&(r||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:i=>i.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:i=>!i.startsWith("on"),labels:{_scriptable:i=>!["generateLabels","filter","sort"].includes(i)}}};const ee={average(i){if(!i.length)return!1;let t,e,s=new Set,n=0,o=0;for(t=0,e=i.length;tr+l)/s.size,y:n/o}},nearest(i,t){if(!i.length)return!1;let e=t.x,s=t.y,n=Number.POSITIVE_INFINITY,o,a,r;for(o=0,a=i.length;o-1?i.split(` -`):i}function ul(i,t){const{element:e,datasetIndex:s,index:n}=t,o=i.getDatasetMeta(s).controller,{label:r,value:a}=o.getLabelAndValue(n);return{chart:i,label:r,parsed:o.getParsed(n),raw:i.data.datasets[s].data[n],formattedValue:a,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:e}}function sn(i,t){const e=i.chart.ctx,{body:s,footer:n,title:o}=i,{boxWidth:r,boxHeight:a}=t,l=J(t.bodyFont),c=J(t.titleFont),h=J(t.footerFont),f=o.length,d=n.length,u=s.length,m=ht(t.padding);let g=m.height,p=0,b=s.reduce((x,_)=>x+_.before.length+_.lines.length+_.after.length,0);if(b+=i.beforeBody.length+i.afterBody.length,f&&(g+=f*c.lineHeight+(f-1)*t.titleSpacing+t.titleMarginBottom),b){const x=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;g+=u*x+(b-u)*l.lineHeight+(b-1)*t.bodySpacing}d&&(g+=t.footerMarginTop+d*h.lineHeight+(d-1)*t.footerSpacing);let y=0;const v=function(x){p=Math.max(p,e.measureText(x).width+y)};return e.save(),e.font=c.string,C(i.title,v),e.font=l.string,C(i.beforeBody.concat(i.afterBody),v),y=t.displayColors?r+2+t.boxPadding:0,C(s,x=>{C(x.before,v),C(x.lines,v),C(x.after,v)}),y=0,e.font=h.string,C(i.footer,v),e.restore(),p+=m.width,{width:p,height:g}}function gl(i,t){const{y:e,height:s}=t;return ei.height-s/2?"bottom":"center"}function pl(i,t,e,s){const{x:n,width:o}=s,r=e.caretSize+e.caretPadding;if(i==="left"&&n+o+r>t.width||i==="right"&&n-o-r<0)return!0}function ml(i,t,e,s){const{x:n,width:o}=e,{width:r,chartArea:{left:a,right:l}}=i;let c="center";return s==="center"?c=n<=(a+l)/2?"left":"right":n<=o/2?c="left":n>=r-o/2&&(c="right"),pl(c,i,t,e)&&(c="center"),c}function nn(i,t,e){const s=e.yAlign||t.yAlign||gl(i,e);return{xAlign:e.xAlign||t.xAlign||ml(i,t,e,s),yAlign:s}}function bl(i,t){let{x:e,width:s}=i;return t==="right"?e-=s:t==="center"&&(e-=s/2),e}function _l(i,t,e){let{y:s,height:n}=i;return t==="top"?s+=e:t==="bottom"?s-=n+e:s-=n/2,s}function on(i,t,e,s){const{caretSize:n,caretPadding:o,cornerRadius:r}=i,{xAlign:a,yAlign:l}=e,c=n+o,{topLeft:h,topRight:f,bottomLeft:d,bottomRight:u}=le(r);let m=bl(t,a);const g=_l(t,l,c);return l==="center"?a==="left"?m+=c:a==="right"&&(m-=c):a==="left"?m-=Math.max(h,d)+n:a==="right"&&(m+=Math.max(f,u)+n),{x:Y(m,0,s.width-t.width),y:Y(g,0,s.height-t.height)}}function De(i,t,e){const s=ht(e.padding);return t==="center"?i.x+i.width/2:t==="right"?i.x+i.width-s.right:i.x+s.left}function rn(i){return et([],at(i))}function yl(i,t,e){return yt(i,{tooltip:t,tooltipItems:e,type:"tooltip"})}function an(i,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?i.override(e):i}const ln={beforeTitle:nt,title(i){if(i.length>0){const t=i[0],e=t.chart.data.labels,s=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(s>0&&t.dataIndex"u"?ln[t].call(e,s):n}class ui extends Mt{constructor(t){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=t.chart,this.options=t.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(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,s=this.options.setContext(this.getContext()),n=s.enabled&&e.options.animation&&s.animations,o=new fs(this.chart,n);return n._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=yl(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:s}=e,n=H(s,"beforeTitle",this,t),o=H(s,"title",this,t),r=H(s,"afterTitle",this,t);let a=[];return a=et(a,at(n)),a=et(a,at(o)),a=et(a,at(r)),a}getBeforeBody(t,e){return rn(H(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:s}=e,n=[];return C(t,o=>{const r={before:[],lines:[],after:[]},a=an(s,o);et(r.before,at(H(a,"beforeLabel",this,o))),et(r.lines,H(a,"label",this,o)),et(r.after,at(H(a,"afterLabel",this,o))),n.push(r)}),n}getAfterBody(t,e){return rn(H(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:s}=e,n=H(s,"beforeFooter",this,t),o=H(s,"footer",this,t),r=H(s,"afterFooter",this,t);let a=[];return a=et(a,at(n)),a=et(a,at(o)),a=et(a,at(r)),a}_createItems(t){const e=this._active,s=this.chart.data,n=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(h,f,d,s))),t.itemSort&&(a=a.sort((h,f)=>t.itemSort(h,f,s))),C(a,h=>{const f=an(t.callbacks,h);n.push(H(f,"labelColor",this,h)),o.push(H(f,"labelPointStyle",this,h)),r.push(H(f,"labelTextColor",this,h))}),this.labelColors=n,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const s=this.options.setContext(this.getContext()),n=this._active;let o,r=[];if(!n.length)this.opacity!==0&&(o={opacity:0});else{const a=Gt[s.position].call(this,n,this._eventPosition);r=this._createItems(s),this.title=this.getTitle(r,s),this.beforeBody=this.getBeforeBody(r,s),this.body=this.getBody(r,s),this.afterBody=this.getAfterBody(r,s),this.footer=this.getFooter(r,s);const l=this._size=sn(this,s),c=Object.assign({},a,l),h=nn(this.chart,s,c),f=on(s,c,h,this.chart);this.xAlign=h.xAlign,this.yAlign=h.yAlign,o={opacity:1,x:f.x,y:f.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&s.external&&s.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,s,n){const o=this.getCaretPosition(t,s,n);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,s){const{xAlign:n,yAlign:o}=this,{caretSize:r,cornerRadius:a}=s,{topLeft:l,topRight:c,bottomLeft:h,bottomRight:f}=le(a),{x:d,y:u}=t,{width:m,height:g}=e;let p,b,y,v,x,_;return o==="center"?(x=u+g/2,n==="left"?(p=d,b=p-r,v=x+r,_=x-r):(p=d+m,b=p+r,v=x-r,_=x+r),y=p):(n==="left"?b=d+Math.max(l,h)+r:n==="right"?b=d+m-Math.max(c,f)-r:b=this.caretX,o==="top"?(v=u,x=v-r,p=b-r,y=b+r):(v=u+g,x=v+r,p=b+r,y=b-r),_=v),{x1:p,x2:b,x3:y,y1:v,y2:x,y3:_}}drawTitle(t,e,s){const n=this.title,o=n.length;let r,a,l;if(o){const c=Je(s.rtl,this.x,this.width);for(t.x=De(this,s.titleAlign,s),e.textAlign=c.textAlign(s.titleAlign),e.textBaseline="middle",r=J(s.titleFont),a=s.titleSpacing,e.fillStyle=s.titleColor,e.font=r.string,l=0;ly!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,Ui(t,{x:g,y:m,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),Ui(t,{x:p,y:m+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(g,m,c,l),t.strokeRect(g,m,c,l),t.fillStyle=r.backgroundColor,t.fillRect(p,m+1,c-2,l-2))}t.fillStyle=this.labelTextColors[s]}drawBody(t,e,s){const{body:n}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:h}=s,f=J(s.bodyFont);let d=f.lineHeight,u=0;const m=Je(s.rtl,this.x,this.width),g=function(w){e.fillText(w,m.x(t.x+u),t.y+d/2),t.y+=d+o},p=m.textAlign(r);let b,y,v,x,_,M,k;for(e.textAlign=r,e.textBaseline="middle",e.font=f.string,t.x=De(this,p,s),e.fillStyle=s.bodyColor,C(this.beforeBody,g),u=a&&p!=="right"?r==="center"?c/2+h:c+2+h:0,x=0,M=n.length;x0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,s=this.$animations,n=s&&s.x,o=s&&s.y;if(n||o){const r=Gt[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=sn(this,t),l=Object.assign({},r,this._size),c=nn(e,t,l),h=on(t,l,c,e);(n._to!==h.x||o._to!==h.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,h))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let s=this.opacity;if(!s)return;this._updateAnimationTarget(e);const n={width:this.width,height:this.height},o={x:this.x,y:this.y};s=Math.abs(s)<.001?0:s;const r=ht(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=s,this.drawBackground(o,t,n,e),rr(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),ar(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const s=this._active,n=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!ie(s,n),r=this._positionChanged(n,e);(o||r)&&(this._active=n,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,s=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const n=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,s),a=this._positionChanged(r,t),l=e||!ie(r,o)||a;return l&&(this._active=r,(n.enabled||n.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,s,n){const o=this.options;if(t.type==="mouseout")return[];if(!n)return e.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const r=this.chart.getElementsAtEventForMode(t,o.mode,o,s);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:s,caretY:n,options:o}=this,r=Gt[o.position].call(this,t,e);return r!==!1&&(s!==r.x||n!==r.y)}}S(ui,"positioners",Gt);var xl={id:"tooltip",_element:ui,positioners:Gt,afterInit(i,t,e){e&&(i.tooltip=new ui({chart:i,options:e}))},beforeUpdate(i,t,e){i.tooltip&&i.tooltip.initialize(e)},reset(i,t,e){i.tooltip&&i.tooltip.initialize(e)},afterDraw(i){const t=i.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(i.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(i.ctx),i.notifyPlugins("afterTooltipDraw",e)}},afterEvent(i,t){if(i.tooltip){const e=t.replay;i.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.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:(i,t)=>t.bodyFont.size,boxWidth:(i,t)=>t.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:ln},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:i=>i!=="filter"&&i!=="itemSort"&&i!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const vl=(i,t,e,s)=>(typeof t=="string"?(e=i.push(t)-1,s.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function kl(i,t,e,s){const n=i.indexOf(t);if(n===-1)return vl(i,t,e,s);const o=i.lastIndexOf(t);return n!==o?e:n}const wl=(i,t)=>i===null?null:Y(Math.round(i),0,t);function cn(i){const t=this.getLabels();return i>=0&&ie.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}S(gi,"id","category"),S(gi,"defaults",{ticks:{callback:cn}});function Ml(i,t){const e=[],{bounds:n,step:o,min:r,max:a,precision:l,count:c,maxTicks:h,maxDigits:f,includeBounds:d}=i,u=o||1,m=h-1,{min:g,max:p}=t,b=!T(r),y=!T(a),v=!T(c),x=(p-g)/(f+1);let _=Ci((p-g)/m/u)*u,M,k,w,P;if(_<1e-14&&!b&&!y)return[{value:g},{value:p}];P=Math.ceil(p/_)-Math.floor(g/_),P>m&&(_=Ci(P*_/m/u)*u),T(l)||(M=Math.pow(10,l),_=Math.ceil(_*M)/M),n==="ticks"?(k=Math.floor(g/_)*_,w=Math.ceil(p/_)*_):(k=g,w=p),b&&y&&o&&Gn((a-r)/o,_/1e3)?(P=Math.round(Math.min((a-r)/_,h)),_=(a-r)/P,k=r,w=a):v?(k=b?r:k,w=y?a:w,P=c-1,_=(w-k)/P):(P=(w-k)/_,zt(P,Math.round(P),_/1e3)?P=Math.round(P):P=Math.ceil(P));const I=Math.max(Oi(_),Oi(k));M=Math.pow(10,T(l)?I:l),k=Math.round(k*M)/M,w=Math.round(w*M)/M;let L=0;for(b&&(d&&k!==r?(e.push({value:r}),ka)break;e.push({value:E})}return y&&d&&w!==a?e.length&&zt(e[e.length-1].value,a,hn(a,x,i))?e[e.length-1].value=a:e.push({value:a}):(!y||w===a)&&e.push({value:w}),e}function hn(i,t,{horizontal:e,minRotation:s}){const n=pt(s),o=(e?Math.sin(n):Math.cos(n))||.001,r=.75*t*(""+i).length;return Math.min(t/o,r)}class Sl extends At{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return T(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:s}=this.getUserBounds();let{min:n,max:o}=this;const r=l=>n=e?n:l,a=l=>o=s?o:l;if(t){const l=Ct(n),c=Ct(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(n===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(n-l)}this.min=n,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:s}=t,n;return s?(n=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let s=this.getTickLimit();s=Math.max(2,s);const n={maxTicks:s,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=Ml(n,o);return t.bounds==="ticks"&&Zn(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,s=this.max;if(super.configure(),this.options.offset&&t.length){const n=(s-e)/Math.max(t.length-1,1)/2;e-=n,s+=n}this._startValue=e,this._endValue=s,this._valueRange=s-e}getLabelForValue(t){return ji(t,this.chart.options.locale,this.options.ticks.format)}}class pi extends Sl{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=V(t)?t:0,this.max=V(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,s=pt(this.options.ticks.minRotation),n=(t?Math.sin(s):Math.cos(s))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/n))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}S(pi,"id","linear"),S(pi,"defaults",{ticks:{callback:Hi.formatters.numeric}});const Pe={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}},W=Object.keys(Pe);function fn(i,t){return i-t}function dn(i,t){if(T(t))return null;const e=i._adapter,{parser:s,round:n,isoWeekday:o}=i._parseOpts;let r=t;return typeof s=="function"&&(r=s(r)),V(r)||(r=typeof s=="string"?e.parse(r,s):e.parse(r)),r===null?null:(n&&(r=n==="week"&&(Bt(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,n)),+r)}function un(i,t,e,s){const n=W.length;for(let o=W.indexOf(i);o=W.indexOf(e);o--){const r=W[o];if(Pe[r].common&&i._adapter.diff(n,s,r)>=t-1)return r}return W[e?W.indexOf(e):0]}function Pl(i){for(let t=W.indexOf(i)+1,e=W.length;t=t?e[s]:e[n];i[o]=!0}}function Cl(i,t,e,s){const n=i._adapter,o=+n.startOf(t[0].value,s),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+n.add(a,1,s))l=e[a],l>=0&&(t[l].major=!0);return t}function pn(i,t,e){const s=[],n={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,s=0,n,o;this.options.offset&&t.length&&(n=this.getDecimalForValue(t[0]),t.length===1?e=1-n:e=(this.getDecimalForValue(t[1])-n)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?s=o:s=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=Y(e,0,r),s=Y(s,0,r),this._offsets={start:e,end:s,factor:1/(e+1+s)}}_generate(){const t=this._adapter,e=this.min,s=this.max,n=this.options,o=n.time,r=o.unit||un(o.minUnit,e,s,this._getLabelCapacity(e)),a=O(n.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=Bt(l)||l===!0,h={};let f=e,d,u;if(c&&(f=+t.startOf(f,"isoWeek",l)),f=+t.startOf(f,c?"day":r),t.diff(s,e,r)>1e5*a)throw new Error(e+" and "+s+" are too far apart with stepSize of "+a+" "+r);const m=n.ticks.source==="data"&&this.getDataTimestamps();for(d=f,u=0;d+g)}getLabelForValue(t){const e=this._adapter,s=this.options.time;return s.tooltipFormat?e.format(t,s.tooltipFormat):e.format(t,s.displayFormats.datetime)}format(t,e){const n=this.options.time.displayFormats,o=this._unit,r=e||n[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,s,n){const o=this.options,r=o.ticks.callback;if(r)return A(r,[t,e,s],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,h=l&&a[l],f=c&&a[c],d=s[e],u=c&&f&&d&&d.major;return this._adapter.format(t,n||(u?f:h))}generateTickLabels(t){let e,s,n;for(e=0,s=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,s;if(t.length)return t;const n=this.getMatchingVisibleMetas();if(this._normalized&&n.length)return this._cache.data=n[0].controller.getAllParsedValues(this);for(e=0,s=n.length;e=i[s].pos&&t<=i[n].pos&&({lo:s,hi:n}=mt(i,"pos",t)),{pos:o,time:a}=i[s],{pos:r,time:l}=i[n]):(t>=i[s].time&&t<=i[n].time&&({lo:s,hi:n}=mt(i,"time",t)),{time:o,pos:a}=i[s],{time:r,pos:l}=i[n]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class mn extends Ce{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Oe(e,this.min),this._tableRange=Oe(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:s}=this,n=[],o=[];let r,a,l,c,h;for(r=0,a=t.length;r=e&&c<=s&&n.push(c);if(n.length<2)return[{time:e,pos:0},{time:s,pos:1}];for(r=0,a=n.length;rn-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),s=this.getLabelTimestamps();return e.length&&s.length?t=this.normalize(e.concat(s)):t=e.length?e:s,t=this._cache.all=t,t}getDecimalForValue(t){return(Oe(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,s=this.getDecimalForPixel(t)/e.factor-e.end;return Oe(this._table,s*this._tableRange+this._minPos,!0)}}S(mn,"id","timeseries"),S(mn,"defaults",Ce.defaults);function Ol(i,t,e,s,n,o,r,a){var l=typeof i=="function"?i.options:i;return t&&(l.render=t,l.staticRenderFns=e,l._compiled=!0),{exports:i,options:l}}rt.register(ge,dt,Me,pi,gi,dl,xl);const Tl={props:{analyticsData:{type:Object,default:()=>({})}},data(){return{startDate:"",endDate:"",data:this.analyticsData||{},chart:null,users:[],selectedEmails:[]}},computed:{hasData(){return this.data&&this.data.totalVisits>0},pagesPerSession(){var i;return(i=this.data)!=null&&i.uniqueSessions?(this.data.totalVisits/this.data.uniqueSessions).toFixed(1):"0"},userOptions(){return this.users.map(i=>({value:i.email,text:i.label}))}},watch:{analyticsData(i){this.data=i||{},this.renderChart()}},mounted(){this.setDefaultDates(),this.fetchData()},beforeUnmount(){this.destroyChart()},methods:{setDefaultDates(){const i=new Date,t=new Date(i);t.setDate(t.getDate()-30),this.endDate=i.toISOString().split("T")[0],this.startDate=t.toISOString().split("T")[0]},onUserSelectionChange(i){this.selectedEmails=i,this.fetchData()},async fetchData(){const i=new URLSearchParams;this.startDate&&i.set("startDate",this.startDate),this.endDate&&i.set("endDate",this.endDate),this.selectedEmails.length&&i.set("emails",this.selectedEmails.join(","));try{const e=await(await fetch(`/analytics-data.json?${i}`)).json();console.log(e.data),e.status==="success"&&(this.data=e.data),e.users&&!this.users.length&&(this.users=e.users),this.renderChart()}catch(t){console.error("Analytics fetch error:",t),this.renderChart()}},destroyChart(){this.chart&&(this.chart.destroy(),this.chart=null)},formatDateFR(i){const[t,e,s]=i.split("-");return`${s}/${e}/${t}`},renderChart(){var n;this.destroyChart();const i=this.$refs.chartCanvas;if(!i||!((n=this.data)!=null&&n.visitsByDay))return;const t=Object.keys(this.data.visitsByDay).map(o=>this.formatDateFR(o)),e=Object.values(this.data.visitsByDay);if(!t.length)return;const s=Math.max(...e);this.chart=new rt(i,{type:"line",data:{labels:t,datasets:[{label:"Visites",data:e,borderColor:"#4271ae",backgroundColor:"rgba(66, 113, 174, 0.1)",fill:!0,tension:.3,pointRadius:3,pointHoverRadius:5}]},options:{responsive:!0,maintainAspectRatio:!1,plugins:{tooltip:{mode:"index",intersect:!1}},scales:{y:{beginAtZero:!0,max:s+3,ticks:{precision:0}}}}})}}};var Al=function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-analytics-dashboard"},[e("div",{staticClass:"k-analytics-filters"},[e("div",{staticClass:"k-analytics-date-filter"},[t._m(0),e("div",{staticClass:"k-date-inputs-wrapper"},[e("label",[t._v(" Du "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.startDate,expression:"startDate"}],attrs:{type:"date"},domProps:{value:t.startDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.startDate=s.target.value)}}})]),e("label",[t._v(" Au "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.endDate,expression:"endDate"}],attrs:{type:"date"},domProps:{value:t.endDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.endDate=s.target.value)}}})])])]),e("div",{staticClass:"k-analytics-user-filter"},[e("k-multiselect-field",{attrs:{options:t.userOptions,value:t.selectedEmails,label:"Filtrer par utilisateur(s)",search:"true",name:"user"},on:{input:t.onUserSelectionChange}})],1)]),t.hasData?[e("div",{staticClass:"k-analytics-grid"},[e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Sessions uniques")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.uniqueSessions))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages vues")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.totalVisits))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages / session")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.pagesPerSession))])])]),e("div",{staticClass:"k-analytics-chart-container"},[e("h3",[t._v("Visites par jour")]),e("canvas",{ref:"chartCanvas"})]),t.data.visitsByPage&&Object.keys(t.data.visitsByPage).length?e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages les plus visitées")]),e("ul",{staticClass:"k-analytics-list"},t._l(t.data.visitsByPage,function(s,n){return e("li",{key:n},[e("span",{staticClass:"k-analytics-list-label"},[t._v(t._s(n))]),e("span",{staticClass:"k-analytics-list-value"},[t._v(t._s(s))])])}),0)]):t._e()]:e("div",{staticClass:"k-analytics-empty"},[e("p",[t._v("Aucune donnée à afficher")])])],2)},Il=[function(){var i=this,t=i._self._c;return t("header",{staticClass:"k-field-header"},[t("label",{staticClass:"k-label k-field-label",attrs:{title:"Filtrer par dates"}},[t("span",{staticClass:"k-label-text"},[i._v("Filtrer par dates ")])])])}],Ll=Ol(Tl,Al,Il);const Fl=Ll.exports;window.panel.plugin("adrienpayet/analytics",{fields:{"analytics-dashboard":Fl}})})(); +`):i}function Sl(i,t){const{element:e,datasetIndex:s,index:n}=t,o=i.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:i,label:a,parsed:o.getParsed(n),raw:i.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:e}}function hn(i,t){const e=i.chart.ctx,{body:s,footer:n,title:o}=i,{boxWidth:a,boxHeight:r}=t,l=V(t.bodyFont),c=V(t.titleFont),h=V(t.footerFont),d=o.length,f=n.length,u=s.length,m=J(t.padding);let g=m.height,p=0,b=s.reduce((k,_)=>k+_.before.length+_.lines.length+_.after.length,0);if(b+=i.beforeBody.length+i.afterBody.length,d&&(g+=d*c.lineHeight+(d-1)*t.titleSpacing+t.titleMarginBottom),b){const k=t.displayColors?Math.max(r,l.lineHeight):l.lineHeight;g+=u*k+(b-u)*l.lineHeight+(b-1)*t.bodySpacing}f&&(g+=t.footerMarginTop+f*h.lineHeight+(f-1)*t.footerSpacing);let x=0;const v=function(k){p=Math.max(p,e.measureText(k).width+x)};return e.save(),e.font=c.string,L(i.title,v),e.font=l.string,L(i.beforeBody.concat(i.afterBody),v),x=t.displayColors?a+2+t.boxPadding:0,L(s,k=>{L(k.before,v),L(k.lines,v),L(k.after,v)}),x=0,e.font=h.string,L(i.footer,v),e.restore(),p+=m.width,{width:p,height:g}}function Ml(i,t){const{y:e,height:s}=t;return ei.height-s/2?"bottom":"center"}function Dl(i,t,e,s){const{x:n,width:o}=s,a=e.caretSize+e.caretPadding;if(i==="left"&&n+o+a>t.width||i==="right"&&n-o-a<0)return!0}function Pl(i,t,e,s){const{x:n,width:o}=e,{width:a,chartArea:{left:r,right:l}}=i;let c="center";return s==="center"?c=n<=(r+l)/2?"left":"right":n<=o/2?c="left":n>=a-o/2&&(c="right"),Dl(c,i,t,e)&&(c="center"),c}function dn(i,t,e){const s=e.yAlign||t.yAlign||Ml(i,e);return{xAlign:e.xAlign||t.xAlign||Pl(i,t,e,s),yAlign:s}}function Cl(i,t){let{x:e,width:s}=i;return t==="right"?e-=s:t==="center"&&(e-=s/2),e}function Ol(i,t,e){let{y:s,height:n}=i;return t==="top"?s+=e:t==="bottom"?s-=n+e:s-=n/2,s}function fn(i,t,e,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=i,{xAlign:r,yAlign:l}=e,c=n+o,{topLeft:h,topRight:d,bottomLeft:f,bottomRight:u}=Ut(a);let m=Cl(t,r);const g=Ol(t,l,c);return l==="center"?r==="left"?m+=c:r==="right"&&(m-=c):r==="left"?m-=Math.max(h,f)+n:r==="right"&&(m+=Math.max(d,u)+n),{x:K(m,0,s.width-t.width),y:K(g,0,s.height-t.height)}}function Le(i,t,e){const s=J(e.padding);return t==="center"?i.x+i.width/2:t==="right"?i.x+i.width-s.right:i.x+s.left}function un(i){return nt([],ht(i))}function Tl(i,t,e){return kt(i,{tooltip:t,tooltipItems:e,type:"tooltip"})}function gn(i,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?i.override(e):i}const pn={beforeTitle:rt,title(i){if(i.length>0){const t=i[0],e=t.chart.data.labels,s=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(s>0&&t.dataIndex"u"?pn[t].call(e,s):n}class bi extends pt{constructor(t){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=t.chart,this.options=t.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(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,s=this.options.setContext(this.getContext()),n=s.enabled&&e.options.animation&&s.animations,o=new ps(this.chart,n);return n._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=Tl(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:s}=e,n=j(s,"beforeTitle",this,t),o=j(s,"title",this,t),a=j(s,"afterTitle",this,t);let r=[];return r=nt(r,ht(n)),r=nt(r,ht(o)),r=nt(r,ht(a)),r}getBeforeBody(t,e){return un(j(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:s}=e,n=[];return L(t,o=>{const a={before:[],lines:[],after:[]},r=gn(s,o);nt(a.before,ht(j(r,"beforeLabel",this,o))),nt(a.lines,j(r,"label",this,o)),nt(a.after,ht(j(r,"afterLabel",this,o))),n.push(a)}),n}getAfterBody(t,e){return un(j(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:s}=e,n=j(s,"beforeFooter",this,t),o=j(s,"footer",this,t),a=j(s,"afterFooter",this,t);let r=[];return r=nt(r,ht(n)),r=nt(r,ht(o)),r=nt(r,ht(a)),r}_createItems(t){const e=this._active,s=this.chart.data,n=[],o=[],a=[];let r=[],l,c;for(l=0,c=e.length;lt.filter(h,d,f,s))),t.itemSort&&(r=r.sort((h,d)=>t.itemSort(h,d,s))),L(r,h=>{const d=gn(t.callbacks,h);n.push(j(d,"labelColor",this,h)),o.push(j(d,"labelPointStyle",this,h)),a.push(j(d,"labelTextColor",this,h))}),this.labelColors=n,this.labelPointStyles=o,this.labelTextColors=a,this.dataPoints=r,r}update(t,e){const s=this.options.setContext(this.getContext()),n=this._active;let o,a=[];if(!n.length)this.opacity!==0&&(o={opacity:0});else{const r=ee[s.position].call(this,n,this._eventPosition);a=this._createItems(s),this.title=this.getTitle(a,s),this.beforeBody=this.getBeforeBody(a,s),this.body=this.getBody(a,s),this.afterBody=this.getAfterBody(a,s),this.footer=this.getFooter(a,s);const l=this._size=hn(this,s),c=Object.assign({},r,l),h=dn(this.chart,s,c),d=fn(s,c,h,this.chart);this.xAlign=h.xAlign,this.yAlign=h.yAlign,o={opacity:1,x:d.x,y:d.y,width:l.width,height:l.height,caretX:r.x,caretY:r.y}}this._tooltipItems=a,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&s.external&&s.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,s,n){const o=this.getCaretPosition(t,s,n);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,s){const{xAlign:n,yAlign:o}=this,{caretSize:a,cornerRadius:r}=s,{topLeft:l,topRight:c,bottomLeft:h,bottomRight:d}=Ut(r),{x:f,y:u}=t,{width:m,height:g}=e;let p,b,x,v,k,_;return o==="center"?(k=u+g/2,n==="left"?(p=f,b=p-a,v=k+a,_=k-a):(p=f+m,b=p+a,v=k-a,_=k+a),x=p):(n==="left"?b=f+Math.max(l,h)+a:n==="right"?b=f+m-Math.max(c,d)-a:b=this.caretX,o==="top"?(v=u,k=v-a,p=b-a,x=b+a):(v=u+g,k=v+a,p=b+a,x=b-a),_=v),{x1:p,x2:b,x3:x,y1:v,y2:k,y3:_}}drawTitle(t,e,s){const n=this.title,o=n.length;let a,r,l;if(o){const c=Ft(s.rtl,this.x,this.width);for(t.x=Le(this,s.titleAlign,s),e.textAlign=c.textAlign(s.titleAlign),e.textBaseline="middle",a=V(s.titleFont),r=s.titleSpacing,e.fillStyle=s.titleColor,e.font=a.string,l=0;lx!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,Qe(t,{x:g,y:m,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),Qe(t,{x:p,y:m+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(g,m,c,l),t.strokeRect(g,m,c,l),t.fillStyle=a.backgroundColor,t.fillRect(p,m+1,c-2,l-2))}t.fillStyle=this.labelTextColors[s]}drawBody(t,e,s){const{body:n}=this,{bodySpacing:o,bodyAlign:a,displayColors:r,boxHeight:l,boxWidth:c,boxPadding:h}=s,d=V(s.bodyFont);let f=d.lineHeight,u=0;const m=Ft(s.rtl,this.x,this.width),g=function(y){e.fillText(y,m.x(t.x+u),t.y+f/2),t.y+=f+o},p=m.textAlign(a);let b,x,v,k,_,S,w;for(e.textAlign=a,e.textBaseline="middle",e.font=d.string,t.x=Le(this,p,s),e.fillStyle=s.bodyColor,L(this.beforeBody,g),u=r&&p!=="right"?a==="center"?c/2+h:c+2+h:0,k=0,S=n.length;k0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,s=this.$animations,n=s&&s.x,o=s&&s.y;if(n||o){const a=ee[t.position].call(this,this._active,this._eventPosition);if(!a)return;const r=this._size=hn(this,t),l=Object.assign({},a,this._size),c=dn(e,t,l),h=fn(t,l,c,e);(n._to!==h.x||o._to!==h.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=r.width,this.height=r.height,this.caretX=a.x,this.caretY=a.y,this._resolveAnimations().update(this,h))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let s=this.opacity;if(!s)return;this._updateAnimationTarget(e);const n={width:this.width,height:this.height},o={x:this.x,y:this.y};s=Math.abs(s)<.001?0:s;const a=J(e.padding),r=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&r&&(t.save(),t.globalAlpha=s,this.drawBackground(o,t,n,e),os(t,e.textDirection),o.y+=a.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),as(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const s=this._active,n=t.map(({datasetIndex:r,index:l})=>{const c=this.chart.getDatasetMeta(r);if(!c)throw new Error("Cannot find a dataset at index "+r);return{datasetIndex:r,element:c.data[l],index:l}}),o=!re(s,n),a=this._positionChanged(n,e);(o||a)&&(this._active=n,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,s=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const n=this.options,o=this._active||[],a=this._getActiveElements(t,o,e,s),r=this._positionChanged(a,t),l=e||!re(a,o)||r;return l&&(this._active=a,(n.enabled||n.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,s,n){const o=this.options;if(t.type==="mouseout")return[];if(!n)return e.filter(r=>this.chart.data.datasets[r.datasetIndex]&&this.chart.getDatasetMeta(r.datasetIndex).controller.getParsed(r.index)!==void 0);const a=this.chart.getElementsAtEventForMode(t,o.mode,o,s);return o.reverse&&a.reverse(),a}_positionChanged(t,e){const{caretX:s,caretY:n,options:o}=this,a=ee[o.position].call(this,t,e);return a!==!1&&(s!==a.x||n!==a.y)}}D(bi,"positioners",ee);var Al={id:"tooltip",_element:bi,positioners:ee,afterInit(i,t,e){e&&(i.tooltip=new bi({chart:i,options:e}))},beforeUpdate(i,t,e){i.tooltip&&i.tooltip.initialize(e)},reset(i,t,e){i.tooltip&&i.tooltip.initialize(e)},afterDraw(i){const t=i.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(i.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(i.ctx),i.notifyPlugins("afterTooltipDraw",e)}},afterEvent(i,t){if(i.tooltip){const e=t.replay;i.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.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:(i,t)=>t.bodyFont.size,boxWidth:(i,t)=>t.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:pn},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:i=>i!=="filter"&&i!=="itemSort"&&i!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const Ll=(i,t,e,s)=>(typeof t=="string"?(e=i.push(t)-1,s.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function Il(i,t,e,s){const n=i.indexOf(t);if(n===-1)return Ll(i,t,e,s);const o=i.lastIndexOf(t);return n!==o?e:n}const Fl=(i,t)=>i===null?null:K(Math.round(i),0,t);function mn(i){const t=this.getLabels();return i>=0&&ie.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}D(_i,"id","category"),D(_i,"defaults",{ticks:{callback:mn}});function Rl(i,t){const e=[],{bounds:n,step:o,min:a,max:r,precision:l,count:c,maxTicks:h,maxDigits:d,includeBounds:f}=i,u=o||1,m=h-1,{min:g,max:p}=t,b=!I(a),x=!I(r),v=!I(c),k=(p-g)/(d+1);let _=Li((p-g)/m/u)*u,S,w,y,M;if(_<1e-14&&!b&&!x)return[{value:g},{value:p}];M=Math.ceil(p/_)-Math.floor(g/_),M>m&&(_=Li(M*_/m/u)*u),I(l)||(S=Math.pow(10,l),_=Math.ceil(_*S)/S),n==="ticks"?(w=Math.floor(g/_)*_,y=Math.ceil(p/_)*_):(w=g,y=p),b&&x&&o&&no((r-a)/o,_/1e3)?(M=Math.round(Math.min((r-a)/_,h)),_=(r-a)/M,w=a,y=r):v?(w=b?a:w,y=x?r:y,M=c-1,_=(y-w)/M):(M=(y-w)/_,Nt(M,Math.round(M),_/1e3)?M=Math.round(M):M=Math.ceil(M));const T=Math.max(Ii(_),Ii(w));S=Math.pow(10,I(l)?T:l),w=Math.round(w*S)/S,y=Math.round(y*S)/S;let P=0;for(b&&(f&&w!==a?(e.push({value:a}),wr)break;e.push({value:A})}return x&&f&&y!==r?e.length&&Nt(e[e.length-1].value,r,bn(r,k,i))?e[e.length-1].value=r:e.push({value:r}):(!x||y===r)&&e.push({value:y}),e}function bn(i,t,{horizontal:e,minRotation:s}){const n=_t(s),o=(e?Math.sin(n):Math.cos(n))||.001,a=.75*t*(""+i).length;return Math.min(t/o,a)}class zl extends Rt{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return I(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:s}=this.getUserBounds();let{min:n,max:o}=this;const a=l=>n=e?n:l,r=l=>o=s?o:l;if(t){const l=Tt(n),c=Tt(o);l<0&&c<0?r(0):l>0&&c>0&&a(0)}if(n===o){let l=o===0?1:Math.abs(o*.05);r(o+l),t||a(n-l)}this.min=n,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:s}=t,n;return s?(n=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let s=this.getTickLimit();s=Math.max(2,s);const n={maxTicks:s,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,a=Rl(n,o);return t.bounds==="ticks"&&oo(a,this,"value"),t.reverse?(a.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),a}configure(){const t=this.ticks;let e=this.min,s=this.max;if(super.configure(),this.options.offset&&t.length){const n=(s-e)/Math.max(t.length-1,1)/2;e-=n,s+=n}this._startValue=e,this._endValue=s,this._valueRange=s-e}getLabelForValue(t){return $i(t,this.chart.options.locale,this.options.ticks.format)}}class xi extends zl{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=W(t)?t:0,this.max=W(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,s=_t(this.options.ticks.minRotation),n=(t?Math.sin(s):Math.cos(s))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/n))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}D(xi,"id","linear"),D(xi,"defaults",{ticks:{callback:Yi.formatters.numeric}});const Ie={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}},$=Object.keys(Ie);function _n(i,t){return i-t}function xn(i,t){if(I(t))return null;const e=i._adapter,{parser:s,round:n,isoWeekday:o}=i._parseOpts;let a=t;return typeof s=="function"&&(a=s(a)),W(a)||(a=typeof s=="string"?e.parse(a,s):e.parse(a)),a===null?null:(n&&(a=n==="week"&&(Vt(o)||o===!0)?e.startOf(a,"isoWeek",o):e.startOf(a,n)),+a)}function yn(i,t,e,s){const n=$.length;for(let o=$.indexOf(i);o=$.indexOf(e);o--){const a=$[o];if(Ie[a].common&&i._adapter.diff(n,s,a)>=t-1)return a}return $[e?$.indexOf(e):0]}function Bl(i){for(let t=$.indexOf(i)+1,e=$.length;t=t?e[s]:e[n];i[o]=!0}}function Hl(i,t,e,s){const n=i._adapter,o=+n.startOf(t[0].value,s),a=t[t.length-1].value;let r,l;for(r=o;r<=a;r=+n.add(r,1,s))l=e[r],l>=0&&(t[l].major=!0);return t}function kn(i,t,e){const s=[],n={},o=t.length;let a,r;for(a=0;a+t.value))}initOffsets(t=[]){let e=0,s=0,n,o;this.options.offset&&t.length&&(n=this.getDecimalForValue(t[0]),t.length===1?e=1-n:e=(this.getDecimalForValue(t[1])-n)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?s=o:s=(o-this.getDecimalForValue(t[t.length-2]))/2);const a=t.length<3?.5:.25;e=K(e,0,a),s=K(s,0,a),this._offsets={start:e,end:s,factor:1/(e+1+s)}}_generate(){const t=this._adapter,e=this.min,s=this.max,n=this.options,o=n.time,a=o.unit||yn(o.minUnit,e,s,this._getLabelCapacity(e)),r=C(n.ticks.stepSize,1),l=a==="week"?o.isoWeekday:!1,c=Vt(l)||l===!0,h={};let d=e,f,u;if(c&&(d=+t.startOf(d,"isoWeek",l)),d=+t.startOf(d,c?"day":a),t.diff(s,e,a)>1e5*r)throw new Error(e+" and "+s+" are too far apart with stepSize of "+r+" "+a);const m=n.ticks.source==="data"&&this.getDataTimestamps();for(f=d,u=0;f+g)}getLabelForValue(t){const e=this._adapter,s=this.options.time;return s.tooltipFormat?e.format(t,s.tooltipFormat):e.format(t,s.displayFormats.datetime)}format(t,e){const n=this.options.time.displayFormats,o=this._unit,a=e||n[o];return this._adapter.format(t,a)}_tickFormatFunction(t,e,s,n){const o=this.options,a=o.ticks.callback;if(a)return F(a,[t,e,s],this);const r=o.time.displayFormats,l=this._unit,c=this._majorUnit,h=l&&r[l],d=c&&r[c],f=s[e],u=c&&d&&f&&f.major;return this._adapter.format(t,n||(u?d:h))}generateTickLabels(t){let e,s,n;for(e=0,s=t.length;e0?r:1}getDataTimestamps(){let t=this._cache.data||[],e,s;if(t.length)return t;const n=this.getMatchingVisibleMetas();if(this._normalized&&n.length)return this._cache.data=n[0].controller.getAllParsedValues(this);for(e=0,s=n.length;e=i[s].pos&&t<=i[n].pos&&({lo:s,hi:n}=xt(i,"pos",t)),{pos:o,time:r}=i[s],{pos:a,time:l}=i[n]):(t>=i[s].time&&t<=i[n].time&&({lo:s,hi:n}=xt(i,"time",t)),{time:o,pos:r}=i[s],{time:a,pos:l}=i[n]);const c=a-o;return c?r+(l-r)*(t-o)/c:r}class wn extends Fe{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Re(e,this.min),this._tableRange=Re(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:s}=this,n=[],o=[];let a,r,l,c,h;for(a=0,r=t.length;a=e&&c<=s&&n.push(c);if(n.length<2)return[{time:e,pos:0},{time:s,pos:1}];for(a=0,r=n.length;an-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),s=this.getLabelTimestamps();return e.length&&s.length?t=this.normalize(e.concat(s)):t=e.length?e:s,t=this._cache.all=t,t}getDecimalForValue(t){return(Re(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,s=this.getDecimalForPixel(t)/e.factor-e.end;return Re(this._table,s*this._tableRange+this._minPos,!0)}}D(wn,"id","timeseries"),D(wn,"defaults",Fe.defaults);function Wl(i,t,e,s,n,o,a,r){var l=typeof i=="function"?i.options:i;return t&&(l.render=t,l.staticRenderFns=e,l._compiled=!0),{exports:i,options:l}}ct.register(ve,mt,Te,xi,_i,bl,Al,wl);const Sn=["#4271ae","#c82829","#8959a8","#4d9a0f","#f5871f","#3e999f","#718c00","#a3685a","#525252","#eab700"],Nl={props:{analyticsData:{type:Object,default:()=>({})}},data(){return{startDate:"",endDate:"",data:this.analyticsData||{},chart:null,users:[],selectedEmails:[],selectedPages:[]}},computed:{hasData(){return this.data&&this.data.totalVisits>0},pagesPerSession(){var i;return(i=this.data)!=null&&i.uniqueSessions?(this.data.totalVisits/this.data.uniqueSessions).toFixed(1):"0"},userOptions(){return this.users.map(i=>({value:i.email,text:i.label}))},pageOptions(){var i;return(i=this.data)!=null&&i.visitsByPage?Object.keys(this.data.visitsByPage).map(t=>({value:t,text:t})).sort((t,e)=>t.text.localeCompare(e.text)):[]}},watch:{analyticsData(i){this.data=i||{},this.renderChart()}},mounted(){this.setDefaultDates(),this.fetchData()},beforeUnmount(){this.destroyChart()},methods:{setDefaultDates(){const i=new Date,t=new Date(i);t.setDate(t.getDate()-30),this.endDate=i.toISOString().split("T")[0],this.startDate=t.toISOString().split("T")[0]},onUserSelectionChange(i){this.selectedEmails=i,this.fetchData()},onPageSelectionChange(i){this.selectedPages=i,this.fetchData()},async fetchData(){const i=new URLSearchParams;this.startDate&&i.set("startDate",this.startDate),this.endDate&&i.set("endDate",this.endDate),this.selectedEmails.length&&i.set("emails",this.selectedEmails.join(",")),this.selectedPages.length&&i.set("pageNames",this.selectedPages.join(","));try{const e=await(await fetch(`/analytics-data.json?${i}`)).json();console.log(e.data),e.status==="success"&&(this.data=e.data),e.users&&!this.users.length&&(this.users=e.users),this.renderChart()}catch(t){console.error("Analytics fetch error:",t),this.renderChart()}},destroyChart(){this.chart&&(this.chart.destroy(),this.chart=null)},formatDateFR(i){const[t,e,s]=i.split("-");return`${s}/${e}/${t}`},renderChart(){var a;this.destroyChart();const i=this.$refs.chartCanvas;if(!i||!((a=this.data)!=null&&a.visitsByDay))return;const t=Object.keys(this.data.visitsByDay);if(!t.length)return;const e=t.map(r=>this.formatDateFR(r)),s=this.selectedPages.length===0&&this.data.visitsByDayByPage;let n,o;if(s)n=Object.keys(this.data.visitsByDayByPage).map((l,c)=>{const h=Sn[c%Sn.length],d=t.map(f=>{var u;return((u=this.data.visitsByDayByPage[l])==null?void 0:u[f])||0});return{label:l,data:d,borderColor:h,backgroundColor:"transparent",fill:!1,tension:.3,pointRadius:2,pointHoverRadius:4,borderWidth:2}}),o=Math.max(...n.flatMap(l=>l.data));else{const r=Object.values(this.data.visitsByDay);n=[{label:"Visites",data:r,borderColor:"#4271ae",backgroundColor:"rgba(66, 113, 174, 0.1)",fill:!0,tension:.3,pointRadius:3,pointHoverRadius:5}],o=Math.max(...r)}this.chart=new ct(i,{type:"line",data:{labels:e,datasets:n},options:{responsive:!0,maintainAspectRatio:!1,plugins:{tooltip:{mode:"index",intersect:!1},legend:{display:s,position:"bottom",labels:{boxWidth:12,padding:12}}},scales:{y:{beginAtZero:!0,max:o+3,ticks:{precision:0}}}}})}}};var Vl=function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-analytics-dashboard"},[e("div",{staticClass:"k-analytics-filters"},[e("div",{staticClass:"k-analytics-date-filter"},[t._m(0),e("div",{staticClass:"k-date-inputs-wrapper"},[e("label",[t._v(" Du "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.startDate,expression:"startDate"}],attrs:{type:"date"},domProps:{value:t.startDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.startDate=s.target.value)}}})]),e("label",[t._v(" Au "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.endDate,expression:"endDate"}],attrs:{type:"date"},domProps:{value:t.endDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.endDate=s.target.value)}}})])])]),e("div",{staticClass:"k-analytics-user-filter"},[e("k-multiselect-field",{attrs:{options:t.userOptions,value:t.selectedEmails,label:"Filtrer par utilisateur(s)",search:"true",name:"user"},on:{input:t.onUserSelectionChange}})],1),e("div",{staticClass:"k-analytics-page-filter"},[e("k-multiselect-field",{attrs:{options:t.pageOptions,value:t.selectedPages,label:"Filtrer par page(s)",search:"true",name:"page"},on:{input:t.onPageSelectionChange}})],1)]),t.hasData?[e("div",{staticClass:"k-analytics-grid"},[e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Sessions uniques")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.uniqueSessions))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages vues")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.totalVisits))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages / session")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.pagesPerSession))])])]),e("div",{staticClass:"k-analytics-chart-container"},[e("h3",[t._v("Visites par jour")]),e("canvas",{ref:"chartCanvas"})]),t.data.visitsByPage&&Object.keys(t.data.visitsByPage).length?e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages les plus visitées")]),e("ul",{staticClass:"k-analytics-list"},t._l(t.data.visitsByPage,function(s,n){return e("li",{key:n},[e("span",{staticClass:"k-analytics-list-label"},[t._v(t._s(n))]),e("span",{staticClass:"k-analytics-list-value"},[t._v(t._s(s))])])}),0)]):t._e()]:e("div",{staticClass:"k-analytics-empty"},[e("p",[t._v("Aucune donnée à afficher")])])],2)},jl=[function(){var i=this,t=i._self._c;return t("header",{staticClass:"k-field-header"},[t("label",{staticClass:"k-label k-field-label",attrs:{title:"Filtrer par dates"}},[t("span",{staticClass:"k-label-text"},[i._v("Filtrer par dates ")])])])}],$l=Wl(Nl,Vl,jl);const Yl=$l.exports;window.panel.plugin("adrienpayet/analytics",{fields:{"analytics-dashboard":Yl}})})(); diff --git a/public/site/plugins/analytics/routes/get-data.php b/public/site/plugins/analytics/routes/get-data.php index 4a5a4a7..f6fe2a7 100644 --- a/public/site/plugins/analytics/routes/get-data.php +++ b/public/site/plugins/analytics/routes/get-data.php @@ -43,6 +43,10 @@ return [ $filters['emails'] = explode(',', $_GET['emails']); } + if (!empty($_GET['pageNames'])) { + $filters['pageNames'] = explode(',', $_GET['pageNames']); + } + $data = $analyticsPage->getAnalyticsData($filters); $users = []; diff --git a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue index 09b0290..ea276b1 100644 --- a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue +++ b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue @@ -28,6 +28,16 @@ @input="onUserSelectionChange" /> +
+ +
@@ -81,6 +91,7 @@ import { CategoryScale, Filler, Tooltip, + Legend, } from 'chart.js'; Chart.register( @@ -90,9 +101,23 @@ Chart.register( LinearScale, CategoryScale, Filler, - Tooltip + Tooltip, + Legend ); +const CHART_COLORS = [ + '#4271ae', + '#c82829', + '#8959a8', + '#4d9a0f', + '#f5871f', + '#3e999f', + '#718c00', + '#a3685a', + '#525252', + '#eab700', +]; + export default { props: { analyticsData: { @@ -109,6 +134,7 @@ export default { chart: null, users: [], selectedEmails: [], + selectedPages: [], }; }, @@ -123,6 +149,12 @@ export default { userOptions() { return this.users.map((u) => ({ value: u.email, text: u.label })); }, + pageOptions() { + if (!this.data?.visitsByPage) return []; + return Object.keys(this.data.visitsByPage) + .map((p) => ({ value: p, text: p })) + .sort((a, b) => a.text.localeCompare(b.text)); + }, }, watch: { @@ -156,6 +188,11 @@ export default { this.fetchData(); }, + onPageSelectionChange(pages) { + this.selectedPages = pages; + this.fetchData(); + }, + async fetchData() { const params = new URLSearchParams(); if (this.startDate) params.set('startDate', this.startDate); @@ -163,6 +200,9 @@ export default { if (this.selectedEmails.length) { params.set('emails', this.selectedEmails.join(',')); } + if (this.selectedPages.length) { + params.set('pageNames', this.selectedPages.join(',')); + } try { const response = await fetch(`/analytics-data.json?${params}`); @@ -203,32 +243,58 @@ export default { const canvas = this.$refs.chartCanvas; if (!canvas || !this.data?.visitsByDay) return; - const labels = Object.keys(this.data.visitsByDay).map((d) => - this.formatDateFR(d) - ); - const values = Object.values(this.data.visitsByDay); + const allDays = Object.keys(this.data.visitsByDay); + if (!allDays.length) return; - if (!labels.length) return; + const labels = allDays.map((d) => this.formatDateFR(d)); + const showPerPage = + this.selectedPages.length === 0 && this.data.visitsByDayByPage; - const maxValue = Math.max(...values); + let datasets; + let maxValue; + + if (showPerPage) { + const pages = Object.keys(this.data.visitsByDayByPage); + datasets = pages.map((page, i) => { + const color = CHART_COLORS[i % CHART_COLORS.length]; + const values = allDays.map( + (day) => this.data.visitsByDayByPage[page]?.[day] || 0 + ); + return { + label: page, + data: values, + borderColor: color, + backgroundColor: 'transparent', + fill: false, + tension: 0.3, + pointRadius: 2, + pointHoverRadius: 4, + borderWidth: 2, + }; + }); + maxValue = Math.max( + ...datasets.flatMap((ds) => ds.data) + ); + } else { + const values = Object.values(this.data.visitsByDay); + datasets = [ + { + label: 'Visites', + data: values, + borderColor: '#4271ae', + backgroundColor: 'rgba(66, 113, 174, 0.1)', + fill: true, + tension: 0.3, + pointRadius: 3, + pointHoverRadius: 5, + }, + ]; + maxValue = Math.max(...values); + } this.chart = new Chart(canvas, { type: 'line', - data: { - labels, - datasets: [ - { - label: 'Visites', - data: values, - borderColor: '#4271ae', - backgroundColor: 'rgba(66, 113, 174, 0.1)', - fill: true, - tension: 0.3, - pointRadius: 3, - pointHoverRadius: 5, - }, - ], - }, + data: { labels, datasets }, options: { responsive: true, maintainAspectRatio: false, @@ -237,6 +303,11 @@ export default { mode: 'index', intersect: false, }, + legend: { + display: showPerPage, + position: 'bottom', + labels: { boxWidth: 12, padding: 12 }, + }, }, scales: { y: { @@ -300,10 +371,20 @@ export default { margin-left: 2rem; } -.k-field-name-user { +.k-field-name-user, +.k-field-name-page { min-width: 15rem; } +.k-analytics-page-filter { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--color-text-light); + margin-left: 2rem; +} + .k-analytics-card { background: var(--color-background); border-radius: var(--rounded); diff --git a/src/stores/dialog.js b/src/stores/dialog.js index 38f4e21..df9f238 100644 --- a/src/stores/dialog.js +++ b/src/stores/dialog.js @@ -2,6 +2,7 @@ import { defineStore } from 'pinia'; import { ref, computed, watch } from 'vue'; import { useRoute } from 'vue-router'; import { useAnalyticsStore } from './analytics'; +import { usePageStore } from './page'; export const useDialogStore = defineStore('dialog', () => { const content = ref(null); @@ -153,11 +154,14 @@ export const useDialogStore = defineStore('dialog', () => { watch(openedFile, (newFile) => { if (newFile) { const analytics = useAnalyticsStore(); + const pageStore = usePageStore(); const currentPath = route.path; + const projectTitle = pageStore.page?.title || 'Projet'; + const fileLabel = newFile.label?.length ? newFile.label : (newFile.name || newFile.filename); analytics.trackVisit( `${currentPath}#file-${newFile.uuid}`, 'modal-file', - newFile.name || newFile.filename + `${projectTitle} / ${fileLabel}` ); } }); From f0652c12cdb57588aed54cd2635408500da338b6 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 13:21:22 +0100 Subject: [PATCH 41/52] =?UTF-8?q?feat:=20toggle=20moyenne=20/=20pages=20le?= =?UTF-8?q?s=20plus=20visit=C3=A9es=20sur=20le=20chart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout toggle Kirby natif au-dessus du graphique - Moyenne : courbe agrégée unique pour toutes les pages - Pages les + visitées : multi-courbes pour le top 10 - Toggle masqué quand un filtre par page est actif - PHP : visitsByDayByPage inclut toutes les pages Co-Authored-By: Claude Opus 4.6 --- .../analytics/classes/AnalyticsStore.php | 4 +- public/site/plugins/analytics/index.css | 134 +- public/site/plugins/analytics/index.js | 11721 +++++++++++++++- .../src/components/AnalyticsDashboard.vue | 43 +- 4 files changed, 11872 insertions(+), 30 deletions(-) diff --git a/public/site/plugins/analytics/classes/AnalyticsStore.php b/public/site/plugins/analytics/classes/AnalyticsStore.php index 9bdc184..552891e 100644 --- a/public/site/plugins/analytics/classes/AnalyticsStore.php +++ b/public/site/plugins/analytics/classes/AnalyticsStore.php @@ -137,12 +137,10 @@ class AnalyticsStore $uniqueSessions = count(array_unique(array_map(fn($v) => $v->sessionId, $visits))); - // Visites par jour par page (top 10 pages) - $topPages = array_keys(array_slice($visitsByPage, 0, 10, true)); + // Visites par jour par page (toutes les pages) $visitsByDayByPage = []; foreach ($visits as $visit) { $page = $visit->pageName ?: $visit->pageUrl; - if (!in_array($page, $topPages)) continue; $day = date('Y-m-d', strtotime($visit->timestamp)); $visitsByDayByPage[$page][$day] = ($visitsByDayByPage[$page][$day] ?? 0) + 1; } diff --git a/public/site/plugins/analytics/index.css b/public/site/plugins/analytics/index.css index a8e09f1..a0dd05a 100644 --- a/public/site/plugins/analytics/index.css +++ b/public/site/plugins/analytics/index.css @@ -1 +1,133 @@ -.k-analytics-dashboard{padding:1.5rem 0}.k-analytics-filters{display:flex;gap:1rem;margin-bottom:1.5rem}.k-analytics-filters label{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light)}.k-date-inputs-wrapper{display:flex;column-gap:1rem}.k-analytics-filters input[type=date]{padding:.375rem .5rem;border:1px solid var(--color-border);border-radius:var(--rounded);font-size:.875rem;background:var(--color-background)}.k-analytics-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem;margin-bottom:1.5rem}.k-analytics-user-filter{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light);margin-left:2rem}.k-field-name-user,.k-field-name-page{min-width:15rem}.k-analytics-page-filter{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--color-text-light);margin-left:2rem}.k-analytics-card{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;box-shadow:var(--shadow)}.k-analytics-card h3{font-size:.75rem;font-weight:600;color:var(--color-text-light);margin:0 0 .5rem;text-transform:uppercase;letter-spacing:.5px}.k-analytics-metric{font-size:2.5rem;font-weight:700;color:var(--color-text);line-height:1}.k-analytics-chart-container{background:var(--color-background);border-radius:var(--rounded);padding:1.5rem;margin-bottom:1.5rem;box-shadow:var(--shadow)}.k-analytics-chart-container h3{font-size:.875rem;font-weight:600;color:var(--color-text);margin:0 0 1rem}.k-analytics-chart-container canvas{max-height:300px}.k-analytics-empty{background:var(--color-background);border-radius:var(--rounded);padding:3rem;text-align:center;box-shadow:var(--shadow)}.k-analytics-empty p{margin:0;color:var(--color-text-light)}.k-analytics-list{list-style:none;margin:0;padding:0}.k-analytics-list li{display:flex;justify-content:space-between;padding:.375rem 0;border-bottom:1px solid var(--color-border);font-size:.875rem}.k-analytics-list li:last-child{border-bottom:none}.k-analytics-list-label{color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:1rem}.k-analytics-list-value{font-weight:600;color:var(--color-text);flex-shrink:0} + +.k-analytics-dashboard { + padding: 1.5rem 0; +} +.k-analytics-filters { + display: flex; + gap: 1rem; + margin-bottom: 1.5rem; +} +.k-analytics-filters label { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--color-text-light); +} +.k-date-inputs-wrapper { + display: flex; + column-gap: 1rem; +} +.k-analytics-filters input[type='date'] { + padding: 0.375rem 0.5rem; + border: 1px solid var(--color-border); + border-radius: var(--rounded); + font-size: 0.875rem; + background: var(--color-background); +} +.k-analytics-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + margin-bottom: 1.5rem; +} +.k-analytics-user-filter { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--color-text-light); + margin-left: 2rem; +} +.k-field-name-user, +.k-field-name-page { + min-width: 15rem; +} +.k-analytics-page-filter { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--color-text-light); + margin-left: 2rem; +} +.k-analytics-card { + background: var(--color-background); + border-radius: var(--rounded); + padding: 1.5rem; + box-shadow: var(--shadow); +} +.k-analytics-card h3 { + font-size: 0.75rem; + font-weight: 600; + color: var(--color-text-light); + margin: 0 0 0.5rem 0; + text-transform: uppercase; + letter-spacing: 0.5px; +} +.k-analytics-metric { + font-size: 2.5rem; + font-weight: 700; + color: var(--color-text); + line-height: 1; +} +.k-analytics-chart-container { + background: var(--color-background); + border-radius: var(--rounded); + padding: 1.5rem; + margin-bottom: 1.5rem; + box-shadow: var(--shadow); +} +.k-analytics-chart-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} +.k-analytics-chart-header h3 { + font-size: 0.875rem; + font-weight: 600; + color: var(--color-text); + margin: 0; +} +.k-analytics-chart-container canvas { + max-height: 300px; +} +.k-analytics-empty { + background: var(--color-background); + border-radius: var(--rounded); + padding: 3rem; + text-align: center; + box-shadow: var(--shadow); +} +.k-analytics-empty p { + margin: 0; + color: var(--color-text-light); +} +.k-analytics-list { + list-style: none; + margin: 0; + padding: 0; +} +.k-analytics-list li { + display: flex; + justify-content: space-between; + padding: 0.375rem 0; + border-bottom: 1px solid var(--color-border); + font-size: 0.875rem; +} +.k-analytics-list li:last-child { + border-bottom: none; +} +.k-analytics-list-label { + color: var(--color-text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: 1rem; +} +.k-analytics-list-value { + font-weight: 600; + color: var(--color-text); + flex-shrink: 0; +} diff --git a/public/site/plugins/analytics/index.js b/public/site/plugins/analytics/index.js index cceb074..e07aa94 100644 --- a/public/site/plugins/analytics/index.js +++ b/public/site/plugins/analytics/index.js @@ -1,18 +1,11703 @@ -var Ul=Object.defineProperty;var Xl=(G,H,tt)=>H in G?Ul(G,H,{enumerable:!0,configurable:!0,writable:!0,value:tt}):G[H]=tt;var D=(G,H,tt)=>Xl(G,typeof H!="symbol"?H+"":H,tt);(function(){"use strict";/*! - * @kurkle/color v0.3.4 - * https://github.com/kurkle/color#readme - * (c) 2024 Jukka Kurkela - * Released under the MIT License - */function G(i){return i+.5|0}const H=(i,t,e)=>Math.max(Math.min(i,e),t);function tt(i){return H(G(i*2.55),0,255)}function dt(i){return H(G(i*255),0,255)}function at(i){return H(G(i/2.55)/100,0,1)}function yi(i){return H(G(i*100),0,100)}const X={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},He=[..."0123456789ABCDEF"],Mn=i=>He[i&15],Dn=i=>He[(i&240)>>4]+He[i&15],ne=i=>(i&240)>>4===(i&15),Pn=i=>ne(i.r)&&ne(i.g)&&ne(i.b)&&ne(i.a);function Cn(i){var t=i.length,e;return i[0]==="#"&&(t===4||t===5?e={r:255&X[i[1]]*17,g:255&X[i[2]]*17,b:255&X[i[3]]*17,a:t===5?X[i[4]]*17:255}:(t===7||t===9)&&(e={r:X[i[1]]<<4|X[i[2]],g:X[i[3]]<<4|X[i[4]],b:X[i[5]]<<4|X[i[6]],a:t===9?X[i[7]]<<4|X[i[8]]:255})),e}const On=(i,t)=>i<255?t(i):"";function Tn(i){var t=Pn(i)?Mn:Dn;return i?"#"+t(i.r)+t(i.g)+t(i.b)+On(i.a,t):void 0}const An=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function vi(i,t,e){const s=t*Math.min(e,1-e),n=(o,a=(o+i/30)%12)=>e-s*Math.max(Math.min(a-3,9-a,1),-1);return[n(0),n(8),n(4)]}function Ln(i,t,e){const s=(n,o=(n+i/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[s(5),s(3),s(1)]}function In(i,t,e){const s=vi(i,1,.5);let n;for(t+e>1&&(n=1/(t+e),t*=n,e*=n),n=0;n<3;n++)s[n]*=1-t-e,s[n]+=t;return s}function Fn(i,t,e,s,n){return i===n?(t-e)/s+(t.5?h/(2-o-a):h/(o+a),l=Fn(e,s,n,h,o),l=l*60+.5),[l|0,c||0,r]}function Ne(i,t,e,s){return(Array.isArray(t)?i(t[0],t[1],t[2]):i(t,e,s)).map(dt)}function Ve(i,t,e){return Ne(vi,i,t,e)}function Rn(i,t,e){return Ne(In,i,t,e)}function zn(i,t,e){return Ne(Ln,i,t,e)}function ki(i){return(i%360+360)%360}function En(i){const t=An.exec(i);let e=255,s;if(!t)return;t[5]!==s&&(e=t[6]?tt(+t[5]):dt(+t[5]));const n=ki(+t[2]),o=+t[3]/100,a=+t[4]/100;return t[1]==="hwb"?s=Rn(n,o,a):t[1]==="hsv"?s=zn(n,o,a):s=Ve(n,o,a),{r:s[0],g:s[1],b:s[2],a:e}}function Bn(i,t){var e=We(i);e[0]=ki(e[0]+t),e=Ve(e),i.r=e[0],i.g=e[1],i.b=e[2]}function Hn(i){if(!i)return;const t=We(i),e=t[0],s=yi(t[1]),n=yi(t[2]);return i.a<255?`hsla(${e}, ${s}%, ${n}%, ${at(i.a)})`:`hsl(${e}, ${s}%, ${n}%)`}const wi={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Si={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function Wn(){const i={},t=Object.keys(Si),e=Object.keys(wi);let s,n,o,a,r;for(s=0;s>16&255,o>>8&255,o&255]}return i}let oe;function Nn(i){oe||(oe=Wn(),oe.transparent=[0,0,0,0]);const t=oe[i.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const Vn=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function jn(i){const t=Vn.exec(i);let e=255,s,n,o;if(t){if(t[7]!==s){const a=+t[7];e=t[8]?tt(a):H(a*255,0,255)}return s=+t[1],n=+t[3],o=+t[5],s=255&(t[2]?tt(s):H(s,0,255)),n=255&(t[4]?tt(n):H(n,0,255)),o=255&(t[6]?tt(o):H(o,0,255)),{r:s,g:n,b:o,a:e}}}function $n(i){return i&&(i.a<255?`rgba(${i.r}, ${i.g}, ${i.b}, ${at(i.a)})`:`rgb(${i.r}, ${i.g}, ${i.b})`)}const je=i=>i<=.0031308?i*12.92:Math.pow(i,1/2.4)*1.055-.055,Ot=i=>i<=.04045?i/12.92:Math.pow((i+.055)/1.055,2.4);function Yn(i,t,e){const s=Ot(at(i.r)),n=Ot(at(i.g)),o=Ot(at(i.b));return{r:dt(je(s+e*(Ot(at(t.r))-s))),g:dt(je(n+e*(Ot(at(t.g))-n))),b:dt(je(o+e*(Ot(at(t.b))-o))),a:i.a+e*(t.a-i.a)}}function ae(i,t,e){if(i){let s=We(i);s[t]=Math.max(0,Math.min(s[t]+s[t]*e,t===0?360:1)),s=Ve(s),i.r=s[0],i.g=s[1],i.b=s[2]}}function Mi(i,t){return i&&Object.assign(t||{},i)}function Di(i){var t={r:0,g:0,b:0,a:255};return Array.isArray(i)?i.length>=3&&(t={r:i[0],g:i[1],b:i[2],a:255},i.length>3&&(t.a=dt(i[3]))):(t=Mi(i,{r:0,g:0,b:0,a:1}),t.a=dt(t.a)),t}function Un(i){return i.charAt(0)==="r"?jn(i):En(i)}class Bt{constructor(t){if(t instanceof Bt)return t;const e=typeof t;let s;e==="object"?s=Di(t):e==="string"&&(s=Cn(t)||Nn(t)||Un(t)),this._rgb=s,this._valid=!!s}get valid(){return this._valid}get rgb(){var t=Mi(this._rgb);return t&&(t.a=at(t.a)),t}set rgb(t){this._rgb=Di(t)}rgbString(){return this._valid?$n(this._rgb):void 0}hexString(){return this._valid?Tn(this._rgb):void 0}hslString(){return this._valid?Hn(this._rgb):void 0}mix(t,e){if(t){const s=this.rgb,n=t.rgb;let o;const a=e===o?.5:e,r=2*a-1,l=s.a-n.a,c=((r*l===-1?r:(r+l)/(1+r*l))+1)/2;o=1-c,s.r=255&c*s.r+o*n.r+.5,s.g=255&c*s.g+o*n.g+.5,s.b=255&c*s.b+o*n.b+.5,s.a=a*s.a+(1-a)*n.a,this.rgb=s}return this}interpolate(t,e){return t&&(this._rgb=Yn(this._rgb,t._rgb,e)),this}clone(){return new Bt(this.rgb)}alpha(t){return this._rgb.a=dt(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=G(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return ae(this._rgb,2,t),this}darken(t){return ae(this._rgb,2,-t),this}saturate(t){return ae(this._rgb,1,t),this}desaturate(t){return ae(this._rgb,1,-t),this}rotate(t){return Bn(this._rgb,t),this}}/*! - * Chart.js v4.5.1 - * https://www.chartjs.org - * (c) 2025 Chart.js Contributors - * Released under the MIT License - */function rt(){}const Xn=(()=>{let i=0;return()=>i++})();function I(i){return i==null}function E(i){if(Array.isArray&&Array.isArray(i))return!0;const t=Object.prototype.toString.call(i);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function O(i){return i!==null&&Object.prototype.toString.call(i)==="[object Object]"}function W(i){return(typeof i=="number"||i instanceof Number)&&isFinite(+i)}function et(i,t){return W(i)?i:t}function C(i,t){return typeof i>"u"?t:i}const Kn=(i,t)=>typeof i=="string"&&i.endsWith("%")?parseFloat(i)/100*t:+i;function F(i,t,e){if(i&&typeof i.call=="function")return i.apply(e,t)}function L(i,t,e,s){let n,o,a;if(E(i))for(o=i.length,n=0;ni,x:i=>i.x,y:i=>i.y};function Zn(i){const t=i.split("."),e=[];let s="";for(const n of t)s+=n,s.endsWith("\\")?s=s.slice(0,-1)+".":(e.push(s),s="");return e}function Qn(i){const t=Zn(i);return e=>{for(const s of t){if(s==="")break;e=e&&e[s]}return e}}function ce(i,t){return(Ci[t]||(Ci[t]=Qn(t)))(i)}function $e(i){return i.charAt(0).toUpperCase()+i.slice(1)}const he=i=>typeof i<"u",ft=i=>typeof i=="function",Oi=(i,t)=>{if(i.size!==t.size)return!1;for(const e of i)if(!t.has(e))return!1;return!0};function Jn(i){return i.type==="mouseup"||i.type==="click"||i.type==="contextmenu"}const N=Math.PI,Z=2*N,to=Z+N,de=Number.POSITIVE_INFINITY,eo=N/180,Q=N/2,bt=N/4,Ti=N*2/3,Ai=Math.log10,Tt=Math.sign;function Nt(i,t,e){return Math.abs(i-t)n-o).pop(),t}function so(i){return typeof i=="symbol"||typeof i=="object"&&i!==null&&!(Symbol.toPrimitive in i||"toString"in i||"valueOf"in i)}function Vt(i){return!so(i)&&!isNaN(parseFloat(i))&&isFinite(i)}function no(i,t){const e=Math.round(i);return e-t<=i&&e+t>=i}function oo(i,t,e){let s,n,o;for(s=0,n=i.length;sl&&c=Math.min(t,e)-s&&i<=Math.max(t,e)+s}function Ue(i,t,e){e=e||(a=>i[a]1;)o=n+s>>1,e(o)?n=o:s=o;return{lo:n,hi:s}}const xt=(i,t,e,s)=>Ue(i,e,s?n=>{const o=i[n][t];return oi[n][t]Ue(i,e,s=>i[s][t]>=e);function fo(i,t,e){let s=0,n=i.length;for(;ss&&i[n-1]>e;)n--;return s>0||n{const s="_onData"+$e(e),n=i[e];Object.defineProperty(i,e,{configurable:!0,enumerable:!1,value(...o){const a=n.apply(this,o);return i._chartjs.listeners.forEach(r=>{typeof r[s]=="function"&&r[s](...o)}),a}})})}function zi(i,t){const e=i._chartjs;if(!e)return;const s=e.listeners,n=s.indexOf(t);n!==-1&&s.splice(n,1),!(s.length>0)&&(Ri.forEach(o=>{delete i[o]}),delete i._chartjs)}function go(i){const t=new Set(i);return t.size===i.length?i:Array.from(t)}const Ei=(function(){return typeof window>"u"?function(i){return i()}:window.requestAnimationFrame})();function Bi(i,t){let e=[],s=!1;return function(...n){e=n,s||(s=!0,Ei.call(window,()=>{s=!1,i.apply(t,e)}))}}function po(i,t){let e;return function(...s){return t?(clearTimeout(e),e=setTimeout(i,t,s)):i.apply(this,s),t}}const Hi=i=>i==="start"?"left":i==="end"?"right":"center",U=(i,t,e)=>i==="start"?t:i==="end"?e:(t+e)/2,mo=(i,t,e,s)=>i===(s?"left":"right")?e:i==="center"?(t+e)/2:t;function bo(i,t,e){const s=t.length;let n=0,o=s;if(i._sorted){const{iScale:a,vScale:r,_parsed:l}=i,c=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null,h=a.axis,{min:d,max:f,minDefined:u,maxDefined:m}=a.getUserBounds();if(u){if(n=Math.min(xt(l,h,d).lo,e?s:xt(t,h,a.getPixelForValue(d)).lo),c){const g=l.slice(0,n+1).reverse().findIndex(p=>!I(p[r.axis]));n-=Math.max(0,g)}n=K(n,0,s-1)}if(m){let g=Math.max(xt(l,a.axis,f,!0).hi+1,e?0:xt(t,h,a.getPixelForValue(f),!0).hi+1);if(c){const p=l.slice(g-1).findIndex(b=>!I(b[r.axis]));g+=Math.max(0,p)}o=K(g,n,s)-n}else o=s-n}return{start:n,count:o}}function _o(i){const{xScale:t,yScale:e,_scaleRanges:s}=i,n={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!s)return i._scaleRanges=n,!0;const o=s.xmin!==t.min||s.xmax!==t.max||s.ymin!==e.min||s.ymax!==e.max;return Object.assign(s,n),o}const fe=i=>i===0||i===1,Wi=(i,t,e)=>-(Math.pow(2,10*(i-=1))*Math.sin((i-t)*Z/e)),Ni=(i,t,e)=>Math.pow(2,-10*i)*Math.sin((i-t)*Z/e)+1,jt={linear:i=>i,easeInQuad:i=>i*i,easeOutQuad:i=>-i*(i-2),easeInOutQuad:i=>(i/=.5)<1?.5*i*i:-.5*(--i*(i-2)-1),easeInCubic:i=>i*i*i,easeOutCubic:i=>(i-=1)*i*i+1,easeInOutCubic:i=>(i/=.5)<1?.5*i*i*i:.5*((i-=2)*i*i+2),easeInQuart:i=>i*i*i*i,easeOutQuart:i=>-((i-=1)*i*i*i-1),easeInOutQuart:i=>(i/=.5)<1?.5*i*i*i*i:-.5*((i-=2)*i*i*i-2),easeInQuint:i=>i*i*i*i*i,easeOutQuint:i=>(i-=1)*i*i*i*i+1,easeInOutQuint:i=>(i/=.5)<1?.5*i*i*i*i*i:.5*((i-=2)*i*i*i*i+2),easeInSine:i=>-Math.cos(i*Q)+1,easeOutSine:i=>Math.sin(i*Q),easeInOutSine:i=>-.5*(Math.cos(N*i)-1),easeInExpo:i=>i===0?0:Math.pow(2,10*(i-1)),easeOutExpo:i=>i===1?1:-Math.pow(2,-10*i)+1,easeInOutExpo:i=>fe(i)?i:i<.5?.5*Math.pow(2,10*(i*2-1)):.5*(-Math.pow(2,-10*(i*2-1))+2),easeInCirc:i=>i>=1?i:-(Math.sqrt(1-i*i)-1),easeOutCirc:i=>Math.sqrt(1-(i-=1)*i),easeInOutCirc:i=>(i/=.5)<1?-.5*(Math.sqrt(1-i*i)-1):.5*(Math.sqrt(1-(i-=2)*i)+1),easeInElastic:i=>fe(i)?i:Wi(i,.075,.3),easeOutElastic:i=>fe(i)?i:Ni(i,.075,.3),easeInOutElastic(i){return fe(i)?i:i<.5?.5*Wi(i*2,.1125,.45):.5+.5*Ni(i*2-1,.1125,.45)},easeInBack(i){return i*i*((1.70158+1)*i-1.70158)},easeOutBack(i){return(i-=1)*i*((1.70158+1)*i+1.70158)+1},easeInOutBack(i){let t=1.70158;return(i/=.5)<1?.5*(i*i*(((t*=1.525)+1)*i-t)):.5*((i-=2)*i*(((t*=1.525)+1)*i+t)+2)},easeInBounce:i=>1-jt.easeOutBounce(1-i),easeOutBounce(i){return i<1/2.75?7.5625*i*i:i<2/2.75?7.5625*(i-=1.5/2.75)*i+.75:i<2.5/2.75?7.5625*(i-=2.25/2.75)*i+.9375:7.5625*(i-=2.625/2.75)*i+.984375},easeInOutBounce:i=>i<.5?jt.easeInBounce(i*2)*.5:jt.easeOutBounce(i*2-1)*.5+.5};function Xe(i){if(i&&typeof i=="object"){const t=i.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Vi(i){return Xe(i)?i:new Bt(i)}function Ke(i){return Xe(i)?i:new Bt(i).saturate(.5).darken(.1).hexString()}const xo=["x","y","borderWidth","radius","tension"],yo=["color","borderColor","backgroundColor"];function vo(i){i.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),i.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),i.set("animations",{colors:{type:"color",properties:yo},numbers:{type:"number",properties:xo}}),i.describe("animations",{_fallback:"animation"}),i.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:t=>t|0}}}})}function ko(i){i.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const ji=new Map;function wo(i,t){t=t||{};const e=i+JSON.stringify(t);let s=ji.get(e);return s||(s=new Intl.NumberFormat(i,t),ji.set(e,s)),s}function $i(i,t,e){return wo(t,e).format(i)}const So={values(i){return E(i)?i:""+i},numeric(i,t,e){if(i===0)return"0";const s=this.chart.options.locale;let n,o=i;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(n="scientific"),o=Mo(i,e)}const a=Ai(Math.abs(o)),r=isNaN(a)?1:Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),$i(i,s,l)}};function Mo(i,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&i!==Math.floor(i)&&(e=i-Math.floor(i)),e}var Yi={formatters:So};function Do(i){i.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width: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:Yi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),i.route("scale.ticks","color","","color"),i.route("scale.grid","color","","borderColor"),i.route("scale.border","color","","borderColor"),i.route("scale.title","color","","color"),i.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),i.describe("scales",{_fallback:"scale"}),i.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const yt=Object.create(null),qe=Object.create(null);function $t(i,t){if(!t)return i;const e=t.split(".");for(let s=0,n=e.length;ss.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=(s,n)=>Ke(n.backgroundColor),this.hoverBorderColor=(s,n)=>Ke(n.borderColor),this.hoverColor=(s,n)=>Ke(n.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(t),this.apply(e)}set(t,e){return Ge(this,t,e)}get(t){return $t(this,t)}describe(t,e){return Ge(qe,t,e)}override(t,e){return Ge(yt,t,e)}route(t,e,s,n){const o=$t(this,t),a=$t(this,s),r="_"+e;Object.defineProperties(o,{[r]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[r],c=a[n];return O(l)?Object.assign({},c,l):C(l,c)},set(l){this[r]=l}}})}apply(t){t.forEach(e=>e(this))}}var R=new Po({_scriptable:i=>!i.startsWith("on"),_indexable:i=>i!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[vo,ko,Do]);function Co(i){return!i||I(i.size)||I(i.family)?null:(i.style?i.style+" ":"")+(i.weight?i.weight+" ":"")+i.size+"px "+i.family}function Ui(i,t,e,s,n){let o=t[n];return o||(o=t[n]=i.measureText(n).width,e.push(n)),o>s&&(s=o),s}function vt(i,t,e){const s=i.currentDevicePixelRatio,n=e!==0?Math.max(e/2,.5):0;return Math.round((t-n)*s)/s+n}function Xi(i,t){!t&&!i||(t=t||i.getContext("2d"),t.save(),t.resetTransform(),t.clearRect(0,0,i.width,i.height),t.restore())}function Ze(i,t,e,s){Ki(i,t,e,s,null)}function Ki(i,t,e,s,n){let o,a,r,l,c,h,d,f;const u=t.pointStyle,m=t.rotation,g=t.radius;let p=(m||0)*eo;if(u&&typeof u=="object"&&(o=u.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){i.save(),i.translate(e,s),i.rotate(p),i.drawImage(u,-u.width/2,-u.height/2,u.width,u.height),i.restore();return}if(!(isNaN(g)||g<=0)){switch(i.beginPath(),u){default:n?i.ellipse(e,s,n/2,g,0,0,Z):i.arc(e,s,g,0,Z),i.closePath();break;case"triangle":h=n?n/2:g,i.moveTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Ti,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),p+=Ti,i.lineTo(e+Math.sin(p)*h,s-Math.cos(p)*g),i.closePath();break;case"rectRounded":c=g*.516,l=g-c,a=Math.cos(p+bt)*l,d=Math.cos(p+bt)*(n?n/2-c:l),r=Math.sin(p+bt)*l,f=Math.sin(p+bt)*(n?n/2-c:l),i.arc(e-d,s-r,c,p-N,p-Q),i.arc(e+f,s-a,c,p-Q,p),i.arc(e+d,s+r,c,p,p+Q),i.arc(e-f,s+a,c,p+Q,p+N),i.closePath();break;case"rect":if(!m){l=Math.SQRT1_2*g,h=n?n/2:l,i.rect(e-h,s-l,2*h,2*l);break}p+=bt;case"rectRot":d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+f,s-a),i.lineTo(e+d,s+r),i.lineTo(e-f,s+a),i.closePath();break;case"crossRot":p+=bt;case"cross":d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+d,s+r),i.moveTo(e+f,s-a),i.lineTo(e-f,s+a);break;case"star":d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+d,s+r),i.moveTo(e+f,s-a),i.lineTo(e-f,s+a),p+=bt,d=Math.cos(p)*(n?n/2:g),a=Math.cos(p)*g,r=Math.sin(p)*g,f=Math.sin(p)*(n?n/2:g),i.moveTo(e-d,s-r),i.lineTo(e+d,s+r),i.moveTo(e+f,s-a),i.lineTo(e-f,s+a);break;case"line":a=n?n/2:Math.cos(p)*g,r=Math.sin(p)*g,i.moveTo(e-a,s-r),i.lineTo(e+a,s+r);break;case"dash":i.moveTo(e,s),i.lineTo(e+Math.cos(p)*(n?n/2:g),s+Math.sin(p)*g);break;case!1:i.closePath();break}i.fill(),t.borderWidth>0&&i.stroke()}}function Yt(i,t,e){return e=e||.5,!t||i&&i.x>t.left-e&&i.xt.top-e&&i.y0&&o.strokeColor!=="";let l,c;for(i.save(),i.font=n.string,Ao(i,o),l=0;l+i||0;function qi(i,t){const e={},s=O(t),n=s?Object.keys(t):t,o=O(i)?s?a=>C(i[a],i[t[a]]):a=>i[a]:()=>i;for(const a of n)e[a]=Eo(o(a));return e}function Bo(i){return qi(i,{top:"y",right:"x",bottom:"y",left:"x"})}function Ut(i){return qi(i,["topLeft","topRight","bottomLeft","bottomRight"])}function J(i){const t=Bo(i);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function V(i,t){i=i||{},t=t||R.font;let e=C(i.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let s=C(i.style,t.style);s&&!(""+s).match(Ro)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:C(i.family,t.family),lineHeight:zo(C(i.lineHeight,t.lineHeight),e),size:e,style:s,weight:C(i.weight,t.weight),string:""};return n.string=Co(n),n}function me(i,t,e,s){let n,o,a;for(n=0,o=i.length;ne&&r===0?0:r+l;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function kt(i,t){return Object.assign(Object.create(i),t)}function Je(i,t=[""],e,s,n=()=>i[0]){const o=e||i;typeof s>"u"&&(s=ts("_fallback",i));const a={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:i,_rootScopes:o,_fallback:s,_getTarget:n,override:r=>Je([r,...i],t,o,s)};return new Proxy(a,{deleteProperty(r,l){return delete r[l],delete r._keys,delete i[0][l],!0},get(r,l){return Zi(r,l,()=>Xo(l,t,i,r))},getOwnPropertyDescriptor(r,l){return Reflect.getOwnPropertyDescriptor(r._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(i[0])},has(r,l){return es(r).includes(l)},ownKeys(r){return es(r)},set(r,l,c){const h=r._storage||(r._storage=n());return r[l]=h[l]=c,delete r._keys,!0}})}function Lt(i,t,e,s){const n={_cacheable:!1,_proxy:i,_context:t,_subProxy:e,_stack:new Set,_descriptors:Gi(i,s),setContext:o=>Lt(i,o,e,s),override:o=>Lt(i.override(o),t,e,s)};return new Proxy(n,{deleteProperty(o,a){return delete o[a],delete i[a],!0},get(o,a,r){return Zi(o,a,()=>No(o,a,r))},getOwnPropertyDescriptor(o,a){return o._descriptors.allKeys?Reflect.has(i,a)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(i,a)},getPrototypeOf(){return Reflect.getPrototypeOf(i)},has(o,a){return Reflect.has(i,a)},ownKeys(){return Reflect.ownKeys(i)},set(o,a,r){return i[a]=r,delete o[a],!0}})}function Gi(i,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:s=t.indexable,_allKeys:n=t.allKeys}=i;return{allKeys:n,scriptable:e,indexable:s,isScriptable:ft(e)?e:()=>e,isIndexable:ft(s)?s:()=>s}}const Wo=(i,t)=>i?i+$e(t):t,ti=(i,t)=>O(t)&&i!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function Zi(i,t,e){if(Object.prototype.hasOwnProperty.call(i,t)||t==="constructor")return i[t];const s=e();return i[t]=s,s}function No(i,t,e){const{_proxy:s,_context:n,_subProxy:o,_descriptors:a}=i;let r=s[t];return ft(r)&&a.isScriptable(t)&&(r=Vo(t,r,i,e)),E(r)&&r.length&&(r=jo(t,r,i,a.isIndexable)),ti(t,r)&&(r=Lt(r,n,o&&o[t],a)),r}function Vo(i,t,e,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=e;if(r.has(i))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+i);r.add(i);let l=t(o,a||s);return r.delete(i),ti(i,l)&&(l=ei(n._scopes,n,i,l)),l}function jo(i,t,e,s){const{_proxy:n,_context:o,_subProxy:a,_descriptors:r}=e;if(typeof o.index<"u"&&s(i))return t[o.index%t.length];if(O(t[0])){const l=t,c=n._scopes.filter(h=>h!==l);t=[];for(const h of l){const d=ei(c,n,i,h);t.push(Lt(d,o,a&&a[i],r))}}return t}function Qi(i,t,e){return ft(i)?i(t,e):i}const $o=(i,t)=>i===!0?t:typeof i=="string"?ce(t,i):void 0;function Yo(i,t,e,s,n){for(const o of t){const a=$o(e,o);if(a){i.add(a);const r=Qi(a._fallback,e,n);if(typeof r<"u"&&r!==e&&r!==s)return r}else if(a===!1&&typeof s<"u"&&e!==s)return null}return!1}function ei(i,t,e,s){const n=t._rootScopes,o=Qi(t._fallback,e,s),a=[...i,...n],r=new Set;r.add(s);let l=Ji(r,a,e,o||e,s);return l===null||typeof o<"u"&&o!==e&&(l=Ji(r,a,o,l,s),l===null)?!1:Je(Array.from(r),[""],n,o,()=>Uo(t,e,s))}function Ji(i,t,e,s,n){for(;e;)e=Yo(i,t,e,s,n);return e}function Uo(i,t,e){const s=i._getTarget();t in s||(s[t]={});const n=s[t];return E(n)&&O(e)?e:n||{}}function Xo(i,t,e,s){let n;for(const o of t)if(n=ts(Wo(o,i),e),typeof n<"u")return ti(i,n)?ei(e,s,i,n):n}function ts(i,t){for(const e of t){if(!e)continue;const s=e[i];if(typeof s<"u")return s}}function es(i){let t=i._keys;return t||(t=i._keys=Ko(i._scopes)),t}function Ko(i){const t=new Set;for(const e of i)for(const s of Object.keys(e).filter(n=>!n.startsWith("_")))t.add(s);return Array.from(t)}const qo=Number.EPSILON||1e-14,It=(i,t)=>ti==="x"?"y":"x";function Go(i,t,e,s){const n=i.skip?t:i,o=t,a=e.skip?t:e,r=Ye(o,n),l=Ye(a,o);let c=r/(r+l),h=l/(r+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const d=s*c,f=s*h;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+f*(a.x-n.x),y:o.y+f*(a.y-n.y)}}}function Zo(i,t,e){const s=i.length;let n,o,a,r,l,c=It(i,0);for(let h=0;h!c.skip)),t.cubicInterpolationMode==="monotone")Jo(i,n);else{let c=s?i[i.length-1]:i[0];for(o=0,a=i.length;oi.ownerDocument.defaultView.getComputedStyle(i,null);function ia(i,t){return xe(i).getPropertyValue(t)}const sa=["top","right","bottom","left"];function wt(i,t,e){const s={};e=e?"-"+e:"";for(let n=0;n<4;n++){const o=sa[n];s[o]=parseFloat(i[t+"-"+o+e])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const na=(i,t,e)=>(i>0||t>0)&&(!e||!e.shadowRoot);function oa(i,t){const e=i.touches,s=e&&e.length?e[0]:i,{offsetX:n,offsetY:o}=s;let a=!1,r,l;if(na(n,o,i.target))r=n,l=o;else{const c=t.getBoundingClientRect();r=s.clientX-c.left,l=s.clientY-c.top,a=!0}return{x:r,y:l,box:a}}function St(i,t){if("native"in i)return i;const{canvas:e,currentDevicePixelRatio:s}=t,n=xe(e),o=n.boxSizing==="border-box",a=wt(n,"padding"),r=wt(n,"border","width"),{x:l,y:c,box:h}=oa(i,e),d=a.left+(h&&r.left),f=a.top+(h&&r.top);let{width:u,height:m}=t;return o&&(u-=a.width+r.width,m-=a.height+r.height),{x:Math.round((l-d)/u*e.width/s),y:Math.round((c-f)/m*e.height/s)}}function aa(i,t,e){let s,n;if(t===void 0||e===void 0){const o=i&&si(i);if(!o)t=i.clientWidth,e=i.clientHeight;else{const a=o.getBoundingClientRect(),r=xe(o),l=wt(r,"border","width"),c=wt(r,"padding");t=a.width-c.width-l.width,e=a.height-c.height-l.height,s=_e(r.maxWidth,o,"clientWidth"),n=_e(r.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:s||de,maxHeight:n||de}}const ut=i=>Math.round(i*10)/10;function ra(i,t,e,s){const n=xe(i),o=wt(n,"margin"),a=_e(n.maxWidth,i,"clientWidth")||de,r=_e(n.maxHeight,i,"clientHeight")||de,l=aa(i,t,e);let{width:c,height:h}=l;if(n.boxSizing==="content-box"){const f=wt(n,"border","width"),u=wt(n,"padding");c-=u.width+f.width,h-=u.height+f.height}return c=Math.max(0,c-o.width),h=Math.max(0,s?c/s:h-o.height),c=ut(Math.min(c,a,l.maxWidth)),h=ut(Math.min(h,r,l.maxHeight)),c&&!h&&(h=ut(c/2)),(t!==void 0||e!==void 0)&&s&&l.height&&h>l.height&&(h=l.height,c=ut(Math.floor(h*s))),{width:c,height:h}}function ss(i,t,e){const s=t||1,n=ut(i.height*s),o=ut(i.width*s);i.height=ut(i.height),i.width=ut(i.width);const a=i.canvas;return a.style&&(e||!a.style.height&&!a.style.width)&&(a.style.height=`${i.height}px`,a.style.width=`${i.width}px`),i.currentDevicePixelRatio!==s||a.height!==n||a.width!==o?(i.currentDevicePixelRatio=s,a.height=n,a.width=o,i.ctx.setTransform(s,0,0,s,0,0),!0):!1}const la=(function(){let i=!1;try{const t={get passive(){return i=!0,!1}};ii()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return i})();function ns(i,t){const e=ia(i,t),s=e&&e.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function Mt(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:i.y+e*(t.y-i.y)}}function ca(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:s==="middle"?e<.5?i.y:t.y:s==="after"?e<1?i.y:t.y:e>0?t.y:i.y}}function ha(i,t,e,s){const n={x:i.cp2x,y:i.cp2y},o={x:t.cp1x,y:t.cp1y},a=Mt(i,n,e),r=Mt(n,o,e),l=Mt(o,t,e),c=Mt(a,r,e),h=Mt(r,l,e);return Mt(c,h,e)}const da=function(i,t){return{x(e){return i+i+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,s){return e-s},leftForLtr(e,s){return e-s}}},fa=function(){return{x(i){return i},setWidth(i){},textAlign(i){return i},xPlus(i,t){return i+t},leftForLtr(i,t){return i}}};function Ft(i,t,e){return i?da(t,e):fa()}function os(i,t){let e,s;(t==="ltr"||t==="rtl")&&(e=i.canvas.style,s=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),i.prevTextDirection=s)}function as(i,t){t!==void 0&&(delete i.prevTextDirection,i.canvas.style.setProperty("direction",t[0],t[1]))}function rs(i){return i==="angle"?{between:Fi,compare:lo,normalize:it}:{between:At,compare:(t,e)=>t-e,normalize:t=>t}}function ls({start:i,end:t,count:e,loop:s,style:n}){return{start:i%e,end:t%e,loop:s&&(t-i+1)%e===0,style:n}}function ua(i,t,e){const{property:s,start:n,end:o}=e,{between:a,normalize:r}=rs(s),l=t.length;let{start:c,end:h,loop:d}=i,f,u;if(d){for(c+=l,h+=l,f=0,u=l;fl(n,v,b)&&r(n,v)!==0,_=()=>r(o,b)===0||l(o,v,b),S=()=>g||k(),w=()=>!g||_();for(let y=h,M=h;y<=d;++y)x=t[y%a],!x.skip&&(b=c(x[s]),b!==v&&(g=l(b,n,o),p===null&&S()&&(p=r(b,n)===0?y:M),p!==null&&w()&&(m.push(ls({start:p,end:y,loop:f,count:a,style:u})),p=null),M=y,v=b));return p!==null&&m.push(ls({start:p,end:d,loop:f,count:a,style:u})),m}function hs(i,t){const e=[],s=i.segments;for(let n=0;nn&&i[o%t].skip;)o--;return o%=t,{start:n,end:o}}function pa(i,t,e,s){const n=i.length,o=[];let a=t,r=i[t],l;for(l=t+1;l<=e;++l){const c=i[l%n];c.skip||c.stop?r.skip||(s=!1,o.push({start:t%n,end:(l-1)%n,loop:s}),t=a=c.stop?l:null):(a=l,r.skip&&(t=l)),r=c}return a!==null&&o.push({start:t%n,end:a%n,loop:s}),o}function ma(i,t){const e=i.points,s=i.options.spanGaps,n=e.length;if(!n)return[];const o=!!i._loop,{start:a,end:r}=ga(e,n,o,s);if(s===!0)return ds(i,[{start:a,end:r,loop:o}],e,t);const l=rr({chart:t,initial:e.initial,numSteps:a,currentStep:Math.min(s-e.start,a)}))}_refresh(){this._request||(this._running=!0,this._request=Ei.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((s,n)=>{if(!s.running||!s.items.length)return;const o=s.items;let a=o.length-1,r=!1,l;for(;a>=0;--a)l=o[a],l._active?(l._total>s.duration&&(s.duration=l._total),l.tick(t),r=!0):(o[a]=o[o.length-1],o.pop());r&&(n.draw(),this._notify(n,s,t,"progress")),o.length||(s.running=!1,this._notify(n,s,t,"complete"),s.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let s=e.get(t);return s||(s={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,s)),s}listen(t,e,s){this._getAnims(t).listeners[e].push(s)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((s,n)=>Math.max(s,n._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const s=e.items;let n=s.length-1;for(;n>=0;--n)s[n].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var lt=new ya;const gs="transparent",va={boolean(i,t,e){return e>.5?t:i},color(i,t,e){const s=Vi(i||gs),n=s.valid&&Vi(t||gs);return n&&n.valid?n.mix(s,e).hexString():t},number(i,t,e){return i+(t-i)*e}};class ka{constructor(t,e,s,n){const o=e[s];n=me([t.to,n,o,t.from]);const a=me([t.from,o,n]);this._active=!0,this._fn=t.fn||va[t.type||typeof a],this._easing=jt[t.easing]||jt.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=s,this._from=a,this._to=n,this._promises=void 0}active(){return this._active}update(t,e,s){if(this._active){this._notify(!1);const n=this._target[this._prop],o=s-this._start,a=this._duration-o;this._start=s,this._duration=Math.floor(Math.max(a,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=me([t.to,e,n,t.from]),this._from=me([t.from,n,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,s=this._duration,n=this._prop,o=this._from,a=this._loop,r=this._to;let l;if(this._active=o!==r&&(a||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[n]=this._fn(o,r,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,s)=>{t.push({res:e,rej:s})})}_notify(t){const e=t?"res":"rej",s=this._promises||[];for(let n=0;n{const o=t[n];if(!O(o))return;const a={};for(const r of e)a[r]=o[r];(E(o.properties)&&o.properties||[n]).forEach(r=>{(r===n||!s.has(r))&&s.set(r,a)})})}_animateOptions(t,e){const s=e.options,n=Sa(t,s);if(!n)return[];const o=this._createAnimations(n,s);return s.$shared&&wa(t.options.$animations,s).then(()=>{t.options=s},()=>{}),o}_createAnimations(t,e){const s=this._properties,n=[],o=t.$animations||(t.$animations={}),a=Object.keys(e),r=Date.now();let l;for(l=a.length-1;l>=0;--l){const c=a[l];if(c.charAt(0)==="$")continue;if(c==="options"){n.push(...this._animateOptions(t,e));continue}const h=e[c];let d=o[c];const f=s.get(c);if(d)if(f&&d.active()){d.update(f,h,r);continue}else d.cancel();if(!f||!f.duration){t[c]=h;continue}o[c]=d=new ka(f,t,c,h),n.push(d)}return n}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const s=this._createAnimations(t,e);if(s.length)return lt.add(this._chart,s),!0}}function wa(i,t){const e=[],s=Object.keys(t);for(let n=0;n0||!e&&o<0)return n.index}return null}function ys(i,t){const{chart:e,_cachedMeta:s}=i,n=e._stacks||(e._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,c=a.axis,h=Ca(o,a,s),d=t.length;let f;for(let u=0;ue[s].axis===t).shift()}function Aa(i,t){return kt(i,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function La(i,t,e){return kt(i,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Xt(i,t){const e=i.controller.index,s=i.vScale&&i.vScale.axis;if(s){t=t||i._parsed;for(const n of t){const o=n._stacks;if(!o||o[s]===void 0||o[s][e]===void 0)return;delete o[s][e],o[s]._visualValues!==void 0&&o[s]._visualValues[e]!==void 0&&delete o[s]._visualValues[e]}}}const ai=i=>i==="reset"||i==="none",vs=(i,t)=>t?i:Object.assign({},i),Ia=(i,t,e)=>i&&!t.hidden&&t._stacked&&{keys:bs(e,!0),values:null};class Kt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,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.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=ni(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Xt(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,s=this.getDataset(),n=(d,f,u,m)=>d==="x"?f:d==="r"?m:u,o=e.xAxisID=C(s.xAxisID,oi(t,"x")),a=e.yAxisID=C(s.yAxisID,oi(t,"y")),r=e.rAxisID=C(s.rAxisID,oi(t,"r")),l=e.indexAxis,c=e.iAxisID=n(l,o,a,r),h=e.vAxisID=n(l,a,o,r);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(a),e.rScale=this.getScaleForId(r),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&zi(this._data,this),t._stacked&&Xt(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),s=this._data;if(O(e)){const n=this._cachedMeta;this._data=Pa(e,n)}else if(s!==e){if(s){zi(s,this);const n=this._cachedMeta;Xt(n),n._parsed=[]}e&&Object.isExtensible(e)&&uo(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,s=this.getDataset();let n=!1;this._dataCheck();const o=e._stacked;e._stacked=ni(e.vScale,e),e.stack!==s.stack&&(n=!0,Xt(e),e.stack=s.stack),this._resyncElements(t),(n||o!==e._stacked)&&(ys(this,e._parsed),e._stacked=ni(e.vScale,e))}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),s=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(s,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:s,_data:n}=this,{iScale:o,_stacked:a}=s,r=o.axis;let l=t===0&&e===n.length?!0:s._sorted,c=t>0&&s._parsed[t-1],h,d,f;if(this._parsing===!1)s._parsed=n,s._sorted=!0,f=n;else{E(n[t])?f=this.parseArrayData(s,n,t,e):O(n[t])?f=this.parseObjectData(s,n,t,e):f=this.parsePrimitiveData(s,n,t,e);const u=()=>d[r]===null||c&&d[r]g||d=0;--f)if(!m()){this.updateRangeFromParsed(c,t,u,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,s=[];let n,o,a;for(n=0,o=e.length;n=0&&tthis.getContext(s,n,e),g=c.resolveNamedOptions(f,u,m,d);return g.$shared&&(g.$shared=l,o[a]=Object.freeze(vs(g,l))),g}_resolveAnimations(t,e,s){const n=this.chart,o=this._cachedDataOpts,a=`animation-${e}`,r=o[a];if(r)return r;let l;if(n.options.animation!==!1){const h=this.chart.config,d=h.datasetAnimationScopeKeys(this._type,e),f=h.getOptionScopes(this.getDataset(),d);l=h.createResolver(f,this.getContext(t,s,e))}const c=new ps(n,l&&l.animations);return l&&l._cacheable&&(o[a]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ai(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const s=this.resolveDataElementOptions(t,e),n=this._sharedOptions,o=this.getSharedOptions(s),a=this.includeOptions(e,o)||o!==n;return this.updateSharedOptions(o,e,s),{sharedOptions:o,includeOptions:a}}updateElement(t,e,s,n){ai(n)?Object.assign(t,s):this._resolveAnimations(e,n).update(t,s)}updateSharedOptions(t,e,s){t&&!ai(e)&&this._resolveAnimations(void 0,e).update(t,s)}_setStyle(t,e,s,n){t.active=n;const o=this.getStyle(e,n);this._resolveAnimations(e,s,n).update(t,{options:!n&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,s){this._setStyle(t,s,"active",!1)}setHoverStyle(t,e,s){this._setStyle(t,s,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,s=this._cachedMeta.data;for(const[r,l,c]of this._syncList)this[r](l,c);this._syncList=[];const n=s.length,o=e.length,a=Math.min(o,n);a&&this.parse(0,a),o>n?this._insertElements(n,o-n,t):o{for(c.length+=e,r=c.length-1;r>=a;r--)c[r]=c[r-e]};for(l(o),r=t;r0&&this.getParsed(e-1);for(let _=0;_=x){w.skip=!0;continue}const y=this.getParsed(_),M=I(y[u]),T=w[f]=a.getPixelForValue(y[f],_),P=w[u]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,y,l):y[u],_);w.skip=isNaN(T)||isNaN(P)||M,w.stop=_>0&&Math.abs(y[f]-k[f])>p,g&&(w.parsed=y,w.raw=c.data[_]),d&&(w.options=h||this.resolveDataElementOptions(_,S.active?"active":n)),b||this.updateElement(S,_,w,n),k=y}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,s=e.options&&e.options.borderWidth||0,n=t.data||[];if(!n.length)return s;const o=n[0].size(this.resolveDataElementOptions(0)),a=n[n.length-1].size(this.resolveDataElementOptions(n.length-1));return Math.max(s,o,a)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}D(ve,"id","line"),D(ve,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),D(ve,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});function Dt(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class ri{constructor(t){D(this,"options");this.options=t||{}}static override(t){Object.assign(ri.prototype,t)}init(){}formats(){return Dt()}parse(){return Dt()}format(){return Dt()}add(){return Dt()}diff(){return Dt()}startOf(){return Dt()}endOf(){return Dt()}}var Fa={_date:ri};function Ra(i,t,e,s){const{controller:n,data:o,_sorted:a}=i,r=n._cachedMeta.iScale,l=i.dataset&&i.dataset.options?i.dataset.options.spanGaps:null;if(r&&t===r.axis&&t!=="r"&&a&&o.length){const c=r._reversePixels?ho:xt;if(s){if(n._sharedOptions){const h=o[0],d=typeof h.getRange=="function"&&h.getRange(t);if(d){const f=c(o,t,e-d),u=c(o,t,e+d);return{lo:f.lo,hi:u.hi}}}}else{const h=c(o,t,e);if(l){const{vScale:d}=n._cachedMeta,{_parsed:f}=i,u=f.slice(0,h.lo+1).reverse().findIndex(g=>!I(g[d.axis]));h.lo-=Math.max(0,u);const m=f.slice(h.hi).findIndex(g=>!I(g[d.axis]));h.hi+=Math.max(0,m)}return h}}return{lo:0,hi:o.length-1}}function ke(i,t,e,s,n){const o=i.getSortedVisibleDatasetMetas(),a=e[t];for(let r=0,l=o.length;r{l[a]&&l[a](t[e],n)&&(o.push({element:l,datasetIndex:c,index:h}),r=r||l.inRange(t.x,t.y,n))}),s&&!r?[]:o}var Ha={modes:{index(i,t,e,s){const n=St(t,i),o=e.axis||"x",a=e.includeInvisible||!1,r=e.intersect?li(i,n,o,s,a):ci(i,n,o,!1,s,a),l=[];return r.length?(i.getSortedVisibleDatasetMetas().forEach(c=>{const h=r[0].index,d=c.data[h];d&&!d.skip&&l.push({element:d,datasetIndex:c.index,index:h})}),l):[]},dataset(i,t,e,s){const n=St(t,i),o=e.axis||"xy",a=e.includeInvisible||!1;let r=e.intersect?li(i,n,o,s,a):ci(i,n,o,!1,s,a);if(r.length>0){const l=r[0].datasetIndex,c=i.getDatasetMeta(l).data;r=[];for(let h=0;he.pos===t)}function Ss(i,t){return i.filter(e=>ws.indexOf(e.pos)===-1&&e.box.axis===t)}function Gt(i,t){return i.sort((e,s)=>{const n=t?s:e,o=t?e:s;return n.weight===o.weight?n.index-o.index:n.weight-o.weight})}function Wa(i){const t=[];let e,s,n,o,a,r;for(e=0,s=(i||[]).length;ec.box.fullSize),!0),s=Gt(qt(t,"left"),!0),n=Gt(qt(t,"right")),o=Gt(qt(t,"top"),!0),a=Gt(qt(t,"bottom")),r=Ss(t,"x"),l=Ss(t,"y");return{fullSize:e,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:qt(t,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}function Ms(i,t,e,s){return Math.max(i[e],t[e])+Math.max(i[s],t[s])}function Ds(i,t){i.top=Math.max(i.top,t.top),i.left=Math.max(i.left,t.left),i.bottom=Math.max(i.bottom,t.bottom),i.right=Math.max(i.right,t.right)}function $a(i,t,e,s){const{pos:n,box:o}=e,a=i.maxPadding;if(!O(n)){e.size&&(i[n]-=e.size);const d=s[e.stack]||{size:0,count:1};d.size=Math.max(d.size,e.horizontal?o.height:o.width),e.size=d.size/d.count,i[n]+=e.size}o.getPadding&&Ds(a,o.getPadding());const r=Math.max(0,t.outerWidth-Ms(a,i,"left","right")),l=Math.max(0,t.outerHeight-Ms(a,i,"top","bottom")),c=r!==i.w,h=l!==i.h;return i.w=r,i.h=l,e.horizontal?{same:c,other:h}:{same:h,other:c}}function Ya(i){const t=i.maxPadding;function e(s){const n=Math.max(t[s]-i[s],0);return i[s]+=n,n}i.y+=e("top"),i.x+=e("left"),e("right"),e("bottom")}function Ua(i,t){const e=t.maxPadding;function s(n){const o={left:0,top:0,right:0,bottom:0};return n.forEach(a=>{o[a]=Math.max(t[a],e[a])}),o}return s(i?["left","right"]:["top","bottom"])}function Zt(i,t,e,s){const n=[];let o,a,r,l,c,h;for(o=0,a=i.length,c=0;o{typeof g.beforeLayout=="function"&&g.beforeLayout()});const h=l.reduce((g,p)=>p.box.options&&p.box.options.display===!1?g:g+1,0)||1,d=Object.freeze({outerWidth:t,outerHeight:e,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/h,hBoxMaxHeight:a/2}),f=Object.assign({},n);Ds(f,J(s));const u=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),m=Va(l.concat(c),d);Zt(r.fullSize,u,d,m),Zt(l,u,d,m),Zt(c,u,d,m)&&Zt(l,u,d,m),Ya(u),Ps(r.leftAndTop,u,d,m),u.x+=u.w,u.y+=u.h,Ps(r.rightAndBottom,u,d,m),i.chartArea={left:u.left,top:u.top,right:u.left+u.w,bottom:u.top+u.h,height:u.h,width:u.w},L(r.chartArea,g=>{const p=g.box;Object.assign(p,i.chartArea),p.update(u.w,u.h,{left:0,top:0,right:0,bottom:0})})}};class Cs{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,s){}removeEventListener(t,e,s){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,s,n){return e=Math.max(0,e||t.width),s=s||t.height,{width:e,height:Math.max(0,n?Math.floor(e/n):s)}}isAttached(t){return!0}updateConfig(t){}}class Xa extends Cs{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const Se="$chartjs",Ka={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},Os=i=>i===null||i==="";function qa(i,t){const e=i.style,s=i.getAttribute("height"),n=i.getAttribute("width");if(i[Se]={initial:{height:s,width:n,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",Os(n)){const o=ns(i,"width");o!==void 0&&(i.width=o)}if(Os(s))if(i.style.height==="")i.height=i.width/(t||2);else{const o=ns(i,"height");o!==void 0&&(i.height=o)}return i}const Ts=la?{passive:!0}:!1;function Ga(i,t,e){i&&i.addEventListener(t,e,Ts)}function Za(i,t,e){i&&i.canvas&&i.canvas.removeEventListener(t,e,Ts)}function Qa(i,t){const e=Ka[i.type]||i.type,{x:s,y:n}=St(i,t);return{type:e,chart:t,native:i,x:s!==void 0?s:null,y:n!==void 0?n:null}}function Me(i,t){for(const e of i)if(e===t||e.contains(t))return!0}function Ja(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let a=!1;for(const r of o)a=a||Me(r.addedNodes,s),a=a&&!Me(r.removedNodes,s);a&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}function tr(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let a=!1;for(const r of o)a=a||Me(r.removedNodes,s),a=a&&!Me(r.addedNodes,s);a&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}const Qt=new Map;let As=0;function Ls(){const i=window.devicePixelRatio;i!==As&&(As=i,Qt.forEach((t,e)=>{e.currentDevicePixelRatio!==i&&t()}))}function er(i,t){Qt.size||window.addEventListener("resize",Ls),Qt.set(i,t)}function ir(i){Qt.delete(i),Qt.size||window.removeEventListener("resize",Ls)}function sr(i,t,e){const s=i.canvas,n=s&&si(s);if(!n)return;const o=Bi((r,l)=>{const c=n.clientWidth;e(r,l),c{const l=r[0],c=l.contentRect.width,h=l.contentRect.height;c===0&&h===0||o(c,h)});return a.observe(n),er(i,o),a}function hi(i,t,e){e&&e.disconnect(),t==="resize"&&ir(i)}function nr(i,t,e){const s=i.canvas,n=Bi(o=>{i.ctx!==null&&e(Qa(o,i))},i);return Ga(s,t,n),n}class or extends Cs{acquireContext(t,e){const s=t&&t.getContext&&t.getContext("2d");return s&&s.canvas===t?(qa(t,e),s):null}releaseContext(t){const e=t.canvas;if(!e[Se])return!1;const s=e[Se].initial;["height","width"].forEach(o=>{const a=s[o];I(a)?e.removeAttribute(o):e.setAttribute(o,a)});const n=s.style||{};return Object.keys(n).forEach(o=>{e.style[o]=n[o]}),e.width=e.width,delete e[Se],!0}addEventListener(t,e,s){this.removeEventListener(t,e);const n=t.$proxies||(t.$proxies={}),a={attach:Ja,detach:tr,resize:sr}[e]||nr;n[e]=a(t,e,s)}removeEventListener(t,e){const s=t.$proxies||(t.$proxies={}),n=s[e];if(!n)return;({attach:hi,detach:hi,resize:hi}[e]||Za)(t,e,n),s[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,s,n){return ra(t,e,s,n)}isAttached(t){const e=t&&si(t);return!!(e&&e.isConnected)}}function ar(i){return!ii()||typeof OffscreenCanvas<"u"&&i instanceof OffscreenCanvas?Xa:or}class pt{constructor(){D(this,"x");D(this,"y");D(this,"active",!1);D(this,"options");D(this,"$animations")}tooltipPosition(t){const{x:e,y:s}=this.getProps(["x","y"],t);return{x:e,y:s}}hasValue(){return Vt(this.x)&&Vt(this.y)}getProps(t,e){const s=this.$animations;if(!e||!s)return this;const n={};return t.forEach(o=>{n[o]=s[o]&&s[o].active()?s[o]._to:this[o]}),n}}D(pt,"defaults",{}),D(pt,"defaultRoutes");function rr(i,t){const e=i.options.ticks,s=lr(i),n=Math.min(e.maxTicksLimit||s,s),o=e.major.enabled?hr(t):[],a=o.length,r=o[0],l=o[a-1],c=[];if(a>n)return dr(t,c,o,a/n),c;const h=cr(o,t,n);if(a>0){let d,f;const u=a>1?Math.round((l-r)/(a-1)):null;for(De(t,c,h,I(u)?0:r-u,r),d=0,f=a-1;dn)return l}return Math.max(n,1)}function hr(i){const t=[];let e,s;for(e=0,s=i.length;ei==="left"?"right":i==="right"?"left":i,Is=(i,t,e)=>t==="top"||t==="left"?i[t]+e:i[t]-e,Fs=(i,t)=>Math.min(t||i,i);function Rs(i,t){const e=[],s=i.length/t,n=i.length;let o=0;for(;oa+r)))return l}function pr(i,t){L(i,e=>{const s=e.gc,n=s.length/2;let o;if(n>t){for(o=0;os?s:e,s=n&&e>s?e:s,{min:et(e,et(s,e)),max:et(s,et(e,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){F(this.options.beforeUpdate,[this])}update(t,e,s){const{beginAtZero:n,grace:o,ticks:a}=this.options,r=a.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=s=Object.assign({left:0,right:0,top:0,bottom:0},s),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+s.left+s.right:this.height+s.top+s.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Ho(this,o,n),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=r=o||s<=1||!this.isHorizontal()){this.labelRotation=n;return}const h=this._getLabelSizes(),d=h.widest.width,f=h.highest.height,u=K(this.chart.width-d,0,this.maxWidth);r=t.offset?this.maxWidth/s:u/(s-1),d+6>r&&(r=u/(s-(t.offset?.5:1)),l=this.maxHeight-Jt(t.grid)-e.padding-zs(t.title,this.chart.options.font),c=Math.sqrt(d*d+f*f),a=ao(Math.min(Math.asin(K((h.highest.height+6)/r,-1,1)),Math.asin(K(l/c,-1,1))-Math.asin(K(f/c,-1,1)))),a=Math.max(n,Math.min(o,a))),this.labelRotation=a}afterCalculateLabelRotation(){F(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){F(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:s,title:n,grid:o}}=this,a=this._isVisible(),r=this.isHorizontal();if(a){const l=zs(n,e.options.font);if(r?(t.width=this.maxWidth,t.height=Jt(o)+l):(t.height=this.maxHeight,t.width=Jt(o)+l),s.display&&this.ticks.length){const{first:c,last:h,widest:d,highest:f}=this._getLabelSizes(),u=s.padding*2,m=_t(this.labelRotation),g=Math.cos(m),p=Math.sin(m);if(r){const b=s.mirror?0:p*d.width+g*f.height;t.height=Math.min(this.maxHeight,t.height+b+u)}else{const b=s.mirror?0:g*d.width+p*f.height;t.width=Math.min(this.maxWidth,t.width+b+u)}this._calculatePadding(c,h,p,g)}}this._handleMargins(),r?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,s,n){const{ticks:{align:o,padding:a},position:r}=this.options,l=this.labelRotation!==0,c=r!=="top"&&this.axis==="x";if(this.isHorizontal()){const h=this.getPixelForTick(0)-this.left,d=this.right-this.getPixelForTick(this.ticks.length-1);let f=0,u=0;l?c?(f=n*t.width,u=s*e.height):(f=s*t.height,u=n*e.width):o==="start"?u=e.width:o==="end"?f=t.width:o!=="inner"&&(f=t.width/2,u=e.width/2),this.paddingLeft=Math.max((f-h+a)*this.width/(this.width-h),0),this.paddingRight=Math.max((u-d+a)*this.width/(this.width-d),0)}else{let h=e.height/2,d=t.height/2;o==="start"?(h=0,d=t.height):o==="end"&&(h=e.height,d=0),this.paddingTop=h+a,this.paddingBottom=d+a}}_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(){F(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,s;for(e=0,s=t.length;e({width:a[M]||0,height:r[M]||0});return{first:y(0),last:y(e-1),widest:y(S),highest:y(w),widths:a,heights:r}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return co(this._alignToPixels?vt(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&tr*n?r/s:l/n:l*n0}_computeGridLineItems(t){const e=this.axis,s=this.chart,n=this.options,{grid:o,position:a,border:r}=n,l=o.offset,c=this.isHorizontal(),d=this.ticks.length+(l?1:0),f=Jt(o),u=[],m=r.setContext(this.getContext()),g=m.display?m.width:0,p=g/2,b=function(B){return vt(s,B,g)};let x,v,k,_,S,w,y,M,T,P,A,Y;if(a==="top")x=b(this.bottom),w=this.bottom-f,M=x-p,P=b(t.top)+p,Y=t.bottom;else if(a==="bottom")x=b(this.top),P=t.top,Y=b(t.bottom)-p,w=x+p,M=this.top+f;else if(a==="left")x=b(this.right),S=this.right-f,y=x-p,T=b(t.left)+p,A=t.right;else if(a==="right")x=b(this.left),T=t.left,A=b(t.right)-p,S=x+p,y=this.left+f;else if(e==="x"){if(a==="center")x=b((t.top+t.bottom)/2+.5);else if(O(a)){const B=Object.keys(a)[0],q=a[B];x=b(this.chart.scales[B].getPixelForValue(q))}P=t.top,Y=t.bottom,w=x+p,M=w+f}else if(e==="y"){if(a==="center")x=b((t.left+t.right)/2);else if(O(a)){const B=Object.keys(a)[0],q=a[B];x=b(this.chart.scales[B].getPixelForValue(q))}S=x-p,y=S-f,T=t.left,A=t.right}const ot=C(n.ticks.maxTicksLimit,d),z=Math.max(1,Math.ceil(d/ot));for(v=0;v0&&(Ct-=Pt/2);break}Be={left:Ct,top:se,width:Pt+Et.width,height:ie+Et.height,color:z.backdropColor}}p.push({label:k,font:M,textOffset:A,options:{rotation:g,color:q,strokeColor:ze,strokeWidth:Ee,textAlign:zt,textBaseline:Y,translation:[_,S],backdrop:Be}})}return p}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-_t(this.labelRotation))return t==="top"?"left":"right";let n="center";return e.align==="start"?n="left":e.align==="end"?n="right":e.align==="inner"&&(n="inner"),n}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:s,mirror:n,padding:o}}=this.options,a=this._getLabelSizes(),r=t+o,l=a.widest.width;let c,h;return e==="left"?n?(h=this.right+o,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h+=l)):(h=this.right-r,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h=this.left)):e==="right"?n?(h=this.left+o,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h-=l)):(h=this.left+r,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h=this.right)):c="right",{textAlign:c,x:h}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:s,top:n,width:o,height:a}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(s,n,o,a),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const n=this.ticks.findIndex(o=>o.value===t);return n>=0?e.setContext(this.getContext(n)).lineWidth:0}drawGrid(t){const e=this.options.grid,s=this.ctx,n=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,a;const r=(l,c,h)=>{!h.width||!h.color||(s.save(),s.lineWidth=h.width,s.strokeStyle=h.color,s.setLineDash(h.borderDash||[]),s.lineDashOffset=h.borderDashOffset,s.beginPath(),s.moveTo(l.x,l.y),s.lineTo(c.x,c.y),s.stroke(),s.restore())};if(e.display)for(o=0,a=n.length;o{this.draw(o)}}]:[{z:s,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:n,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),s=this.axis+"AxisID",n=[];let o,a;for(o=0,a=e.length;o{const s=e.split("."),n=s.pop(),o=[i].concat(s).join("."),a=t[e].split("."),r=a.pop(),l=a.join(".");R.route(o,n,l,r)})}function kr(i){return"id"in i&&"defaults"in i}class wr{constructor(){this.controllers=new Pe(Kt,"datasets",!0),this.elements=new Pe(pt,"elements"),this.plugins=new Pe(Object,"plugins"),this.scales=new Pe(Rt,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,s){[...e].forEach(n=>{const o=s||this._getRegistryForType(n);s||o.isForType(n)||o===this.plugins&&n.id?this._exec(t,o,n):L(n,a=>{const r=s||this._getRegistryForType(a);this._exec(t,r,a)})})}_exec(t,e,s){const n=$e(t);F(s["before"+n],[],s),e[t](s),F(s["after"+n],[],s)}_getRegistryForType(t){for(let e=0;eo.filter(r=>!a.some(l=>r.plugin.id===l.plugin.id));this._notify(n(e,s),t,"stop"),this._notify(n(s,e),t,"start")}}function Mr(i){const t={},e=[],s=Object.keys(st.plugins.items);for(let o=0;o1&&Es(i[0].toLowerCase());if(s)return s}throw new Error(`Cannot determine type of '${i}' axis. Please provide 'axis' or 'position' option.`)}function Bs(i,t,e){if(e[t+"AxisID"]===i)return{axis:t}}function Lr(i,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(s=>s.xAxisID===i||s.yAxisID===i);if(e.length)return Bs(i,"x",e[0])||Bs(i,"y",e[0])}return{}}function Ir(i,t){const e=yt[i.type]||{scales:{}},s=t.scales||{},n=di(i.type,t),o=Object.create(null);return Object.keys(s).forEach(a=>{const r=s[a];if(!O(r))return console.error(`Invalid scale configuration for scale: ${a}`);if(r._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${a}`);const l=fi(a,r,Lr(a,i),R.scales[r.type]),c=Tr(l,n),h=e.scales||{};o[a]=Wt(Object.create(null),[{axis:l},r,h[l],h[c]])}),i.data.datasets.forEach(a=>{const r=a.type||i.type,l=a.indexAxis||di(r,t),h=(yt[r]||{}).scales||{};Object.keys(h).forEach(d=>{const f=Or(d,l),u=a[f+"AxisID"]||f;o[u]=o[u]||Object.create(null),Wt(o[u],[{axis:f},s[u],h[d]])})}),Object.keys(o).forEach(a=>{const r=o[a];Wt(r,[R.scales[r.type],R.scale])}),o}function Hs(i){const t=i.options||(i.options={});t.plugins=C(t.plugins,{}),t.scales=Ir(i,t)}function Ws(i){return i=i||{},i.datasets=i.datasets||[],i.labels=i.labels||[],i}function Fr(i){return i=i||{},i.data=Ws(i.data),Hs(i),i}const Ns=new Map,Vs=new Set;function Ce(i,t){let e=Ns.get(i);return e||(e=t(),Ns.set(i,e),Vs.add(e)),e}const te=(i,t,e)=>{const s=ce(t,e);s!==void 0&&i.add(s)};class Rr{constructor(t){this._config=Fr(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Ws(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Hs(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Ce(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return Ce(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return Ce(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,s=this.type;return Ce(`${s}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const s=this._scopeCache;let n=s.get(t);return(!n||e)&&(n=new Map,s.set(t,n)),n}getOptionScopes(t,e,s){const{options:n,type:o}=this,a=this._cachedScopes(t,s),r=a.get(e);if(r)return r;const l=new Set;e.forEach(h=>{t&&(l.add(t),h.forEach(d=>te(l,t,d))),h.forEach(d=>te(l,n,d)),h.forEach(d=>te(l,yt[o]||{},d)),h.forEach(d=>te(l,R,d)),h.forEach(d=>te(l,qe,d))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),Vs.has(e)&&a.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,yt[e]||{},R.datasets[e]||{},{type:e},R,qe]}resolveNamedOptions(t,e,s,n=[""]){const o={$shared:!0},{resolver:a,subPrefixes:r}=js(this._resolverCache,t,n);let l=a;if(Er(a,e)){o.$shared=!1,s=ft(s)?s():s;const c=this.createResolver(t,s,r);l=Lt(a,s,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,s=[""],n){const{resolver:o}=js(this._resolverCache,t,s);return O(e)?Lt(o,e,void 0,n):o}}function js(i,t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));const n=e.join();let o=s.get(n);return o||(o={resolver:Je(t,e),subPrefixes:e.filter(r=>!r.toLowerCase().includes("hover"))},s.set(n,o)),o}const zr=i=>O(i)&&Object.getOwnPropertyNames(i).some(t=>ft(i[t]));function Er(i,t){const{isScriptable:e,isIndexable:s}=Gi(i);for(const n of t){const o=e(n),a=s(n),r=(a||o)&&i[n];if(o&&(ft(r)||zr(r))||a&&E(r))return!0}return!1}var Br="4.5.1";const Hr=["top","bottom","left","right","chartArea"];function $s(i,t){return i==="top"||i==="bottom"||Hr.indexOf(i)===-1&&t==="x"}function Ys(i,t){return function(e,s){return e[i]===s[i]?e[t]-s[t]:e[i]-s[i]}}function Us(i){const t=i.chart,e=t.options.animation;t.notifyPlugins("afterRender"),F(e&&e.onComplete,[i],t)}function Wr(i){const t=i.chart,e=t.options.animation;F(e&&e.onProgress,[i],t)}function Xs(i){return ii()&&typeof i=="string"?i=document.getElementById(i):i&&i.length&&(i=i[0]),i&&i.canvas&&(i=i.canvas),i}const Oe={},Ks=i=>{const t=Xs(i);return Object.values(Oe).filter(e=>e.canvas===t).pop()};function Nr(i,t,e){const s=Object.keys(i);for(const n of s){const o=+n;if(o>=t){const a=i[n];delete i[n],(e>0||o>t)&&(i[o+e]=a)}}}function Vr(i,t,e,s){return!e||i.type==="mouseout"?null:s?t:i}class ct{static register(...t){st.add(...t),qs()}static unregister(...t){st.remove(...t),qs()}constructor(t,e){const s=this.config=new Rr(e),n=Xs(t),o=Ks(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ar(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,c=l&&l.height,h=l&&l.width;if(this.id=Xn(),this.ctx=r,this.canvas=l,this.width=h,this.height=c,this._options=a,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 Sr,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=po(d=>this.update(d),a.resizeDelay||0),this._dataChanges=[],Oe[this.id]=this,!r||!l){console.error("Failed to create chart: can't acquire context from the given item");return}lt.listen(this,"complete",Us),lt.listen(this,"progress",Wr),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:s,height:n,_aspectRatio:o}=this;return I(t)?e&&o?o:n?s/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return st}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ss(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Xi(this.canvas,this.ctx),this}stop(){return lt.stop(this),this}resize(t,e){lt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const s=this.options,n=this.canvas,o=s.maintainAspectRatio&&this.aspectRatio,a=this.platform.getMaximumSize(n,t,e,o),r=s.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=a.width,this.height=a.height,this._aspectRatio=this.aspectRatio,ss(this,r,!0)&&(this.notifyPlugins("resize",{size:a}),F(s.onResize,[this,a],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};L(e,(s,n)=>{s.id=n})}buildOrUpdateScales(){const t=this.options,e=t.scales,s=this.scales,n=Object.keys(s).reduce((a,r)=>(a[r]=!1,a),{});let o=[];e&&(o=o.concat(Object.keys(e).map(a=>{const r=e[a],l=fi(a,r),c=l==="r",h=l==="x";return{options:r,dposition:c?"chartArea":h?"bottom":"left",dtype:c?"radialLinear":h?"category":"linear"}}))),L(o,a=>{const r=a.options,l=r.id,c=fi(l,r),h=C(r.type,a.dtype);(r.position===void 0||$s(r.position,c)!==$s(a.dposition))&&(r.position=a.dposition),n[l]=!0;let d=null;if(l in s&&s[l].type===h)d=s[l];else{const f=st.getScale(h);d=new f({id:l,type:h,ctx:this.ctx,chart:this}),s[d.id]=d}d.init(r,t)}),L(n,(a,r)=>{a||delete s[r]}),L(s,a=>{gt.configure(this,a,a.options),gt.addBox(this,a)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,s=t.length;if(t.sort((n,o)=>n.index-o.index),s>e){for(let n=e;ne.length&&delete this._stacks,t.forEach((s,n)=>{e.filter(o=>o===s._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let s,n;for(this._removeUnreferencedMetasets(),s=0,n=e.length;s{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const s=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),n=this._animationsDisabled=!s.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let a=0;for(let c=0,h=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(Ys("z","_idx"));const{_active:r,_lastEvent:l}=this;l?this._eventHandler(l,!0):r.length&&this._updateHoverStyles(r,r,!0),this.render()}_updateScales(){L(this.scales,t=>{gt.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),s=new Set(t.events);(!Oi(e,s)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:s,start:n,count:o}of e){const a=s==="_removeElements"?-o:o;Nr(t,n,a)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,s=o=>new Set(t.filter(a=>a[0]===o).map((a,r)=>r+","+a.splice(1).join(","))),n=s(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;gt.update(this,this.width,this.height,t);const e=this.chartArea,s=e.width<=0||e.height<=0;this._layers=[],L(this.boxes,n=>{s&&n.position==="chartArea"||(n.configure&&n.configure(),this._layers.push(...n._layers()))},this),this._layers.forEach((n,o)=>{n._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,s=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,s={meta:t,index:t.index,cancelable:!0},n=us(this,t);this.notifyPlugins("beforeDatasetDraw",s)!==!1&&(n&&ue(e,n),t.controller.draw(),n&&ge(e),s.cancelable=!1,this.notifyPlugins("afterDatasetDraw",s))}isPointInArea(t){return Yt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,s,n){const o=Ha.modes[e];return typeof o=="function"?o(this,t,s,n):[]}getDatasetMeta(t){const e=this.data.datasets[t],s=this._metasets;let n=s.filter(o=>o&&o._dataset===e).pop();return n||(n={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},s.push(n)),n}getContext(){return this.$context||(this.$context=kt(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const s=this.getDatasetMeta(t);return typeof s.hidden=="boolean"?!s.hidden:!e.hidden}setDatasetVisibility(t,e){const s=this.getDatasetMeta(t);s.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,s){const n=s?"show":"hide",o=this.getDatasetMeta(t),a=o.controller._resolveAnimations(void 0,n);he(e)?(o.data[e].hidden=!s,this.update()):(this.setDatasetVisibility(t,s),a.update(o,{visible:s}),this.update(r=>r.datasetIndex===t?n:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),lt.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,a),t[o]=a},n=(o,a,r)=>{o.offsetX=a,o.offsetY=r,this._eventHandler(o)};L(this.options.events,o=>s(o,n))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,s=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},n=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let a;const r=()=>{n("attach",r),this.attached=!0,this.resize(),s("resize",o),s("detach",a)};a=()=>{this.attached=!1,n("resize",o),this._stop(),this._resize(0,0),s("attach",r)},e.isAttached(this.canvas)?r():a()}unbindEvents(){L(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},L(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,s){const n=s?"set":"remove";let o,a,r,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+n+"DatasetHoverStyle"]()),r=0,l=t.length;r{const r=this.getDatasetMeta(o);if(!r)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:r.data[a],index:a}});!re(s,e)&&(this._active=s,this._lastEvent=null,this._updateHoverStyles(s,e))}notifyPlugins(t,e,s){return this._plugins.notify(this,t,e,s)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,s){const n=this.options.hover,o=(l,c)=>l.filter(h=>!c.some(d=>h.datasetIndex===d.datasetIndex&&h.index===d.index)),a=o(e,t),r=s?t:o(t,e);a.length&&this.updateHoverStyle(a,n.mode,!1),r.length&&n.mode&&this.updateHoverStyle(r,n.mode,!0)}_eventHandler(t,e){const s={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},n=a=>(a.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",s,n)===!1)return;const o=this._handleEvent(t,e,s.inChartArea);return s.cancelable=!1,this.notifyPlugins("afterEvent",s,n),(o||s.changed)&&this.render(),this}_handleEvent(t,e,s){const{_active:n=[],options:o}=this,a=e,r=this._getActiveElements(t,n,s,a),l=Jn(t),c=Vr(t,this._lastEvent,s,l);s&&(this._lastEvent=null,F(o.onHover,[t,r,this],this),l&&F(o.onClick,[t,r,this],this));const h=!re(r,n);return(h||e)&&(this._active=r,this._updateHoverStyles(r,n,e)),this._lastEvent=c,h}_getActiveElements(t,e,s,n){if(t.type==="mouseout")return[];if(!s)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,n)}}D(ct,"defaults",R),D(ct,"instances",Oe),D(ct,"overrides",yt),D(ct,"registry",st),D(ct,"version",Br),D(ct,"getChart",Ks);function qs(){return L(ct.instances,i=>i._plugins.invalidate())}function Gs(i,t,e=t){i.lineCap=C(e.borderCapStyle,t.borderCapStyle),i.setLineDash(C(e.borderDash,t.borderDash)),i.lineDashOffset=C(e.borderDashOffset,t.borderDashOffset),i.lineJoin=C(e.borderJoinStyle,t.borderJoinStyle),i.lineWidth=C(e.borderWidth,t.borderWidth),i.strokeStyle=C(e.borderColor,t.borderColor)}function jr(i,t,e){i.lineTo(e.x,e.y)}function $r(i){return i.stepped?Oo:i.tension||i.cubicInterpolationMode==="monotone"?To:jr}function Zs(i,t,e={}){const s=i.length,{start:n=0,end:o=s-1}=e,{start:a,end:r}=t,l=Math.max(n,a),c=Math.min(o,r),h=nr&&o>r;return{count:s,start:l,loop:t.loop,ilen:c(a+(c?r-k:k))%o,v=()=>{g!==p&&(i.lineTo(h,p),i.lineTo(h,g),i.lineTo(h,b))};for(l&&(u=n[x(0)],i.moveTo(u.x,u.y)),f=0;f<=r;++f){if(u=n[x(f)],u.skip)continue;const k=u.x,_=u.y,S=k|0;S===m?(_p&&(p=_),h=(d*h+k)/++d):(v(),i.lineTo(k,_),m=S,d=0,g=p=_),b=_}v()}function ui(i){const t=i.options,e=t.borderDash&&t.borderDash.length;return!i._decimated&&!i._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?Ur:Yr}function Xr(i){return i.stepped?ca:i.tension||i.cubicInterpolationMode==="monotone"?ha:Mt}function Kr(i,t,e,s){let n=t._path;n||(n=t._path=new Path2D,t.path(n,e,s)&&n.closePath()),Gs(i,t.options),i.stroke(n)}function qr(i,t,e,s){const{segments:n,options:o}=t,a=ui(t);for(const r of n)Gs(i,o,r.style),i.beginPath(),a(i,t,r,{start:e,end:e+s-1})&&i.closePath(),i.stroke()}const Gr=typeof Path2D=="function";function Zr(i,t,e,s){Gr&&!t.options.segment?Kr(i,t,e,s):qr(i,t,e,s)}class mt extends pt{constructor(t){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,t&&Object.assign(this,t)}updateControlPoints(t,e){const s=this.options;if((s.tension||s.cubicInterpolationMode==="monotone")&&!s.stepped&&!this._pointsUpdated){const n=s.spanGaps?this._loop:this._fullLoop;ea(this._points,s,t,n,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=ma(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,s=t.length;return s&&e[t[s-1].end]}interpolate(t,e){const s=this.options,n=t[e],o=this.points,a=hs(this,{property:e,start:n,end:n});if(!a.length)return;const r=[],l=Xr(s);let c,h;for(c=0,h=a.length;ct!=="borderDash"&&t!=="fill"});function Qs(i,t,e,s){const n=i.options,{[e]:o}=i.getProps([e],s);return Math.abs(t-o){r=Ae(a,r,n);const l=n[a],c=n[r];s!==null?(o.push({x:l.x,y:s}),o.push({x:c.x,y:s})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function Ae(i,t,e){for(;t>i;t--){const s=e[t];if(!isNaN(s.x)&&!isNaN(s.y))break}return t}function Js(i,t,e,s){return i&&t?s(i[e],t[e]):i?i[e]:t?t[e]:0}function tn(i,t){let e=[],s=!1;return E(i)?(s=!0,e=i):e=Jr(i,t),e.length?new mt({points:e,options:{tension:0},_loop:s,_fullLoop:s}):null}function en(i){return i&&i.fill!==!1}function tl(i,t,e){let n=i[t].fill;const o=[t];let a;if(!e)return n;for(;n!==!1&&o.indexOf(n)===-1;){if(!W(n))return n;if(a=i[n],!a)return!1;if(a.visible)return n;o.push(n),n=a.fill}return!1}function el(i,t,e){const s=ol(i);if(O(s))return isNaN(s.value)?!1:s;let n=parseFloat(s);return W(n)&&Math.floor(n)===n?il(s[0],t,n,e):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function il(i,t,e,s){return(i==="-"||i==="+")&&(e=t+e),e===t||e<0||e>=s?!1:e}function sl(i,t){let e=null;return i==="start"?e=t.bottom:i==="end"?e=t.top:O(i)?e=t.getPixelForValue(i.value):t.getBasePixel&&(e=t.getBasePixel()),e}function nl(i,t,e){let s;return i==="start"?s=e:i==="end"?s=t.options.reverse?t.min:t.max:O(i)?s=i.value:s=t.getBaseValue(),s}function ol(i){const t=i.options,e=t.fill;let s=C(e&&e.target,e);return s===void 0&&(s=!!t.backgroundColor),s===!1||s===null?!1:s===!0?"origin":s}function al(i){const{scale:t,index:e,line:s}=i,n=[],o=s.segments,a=s.points,r=rl(t,e);r.push(tn({x:null,y:t.bottom},s));for(let l=0;l=0;--a){const r=n[a].$filler;r&&(r.line.updateControlPoints(o,r.axis),s&&r.fill&&pi(i.ctx,r,o))}},beforeDatasetsDraw(i,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const s=i.getSortedVisibleDatasetMetas();for(let n=s.length-1;n>=0;--n){const o=s[n].$filler;en(o)&&pi(i.ctx,o,i.chartArea)}},beforeDatasetDraw(i,t,e){const s=t.meta.$filler;!en(s)||e.drawTime!=="beforeDatasetDraw"||pi(i.ctx,s,i.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const rn=(i,t)=>{let{boxHeight:e=t,boxWidth:s=t}=i;return i.usePointStyle&&(e=Math.min(e,t),s=i.pointStyleWidth||Math.min(s,t)),{boxWidth:s,boxHeight:e,itemHeight:Math.max(t,e)}},_l=(i,t)=>i!==null&&t!==null&&i.datasetIndex===t.datasetIndex&&i.index===t.index;class ln extends pt{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.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(t,e,s){this.maxWidth=t,this.maxHeight=e,this._margins=s,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 t=this.options.labels||{};let e=F(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter(s=>t.filter(s,this.chart.data))),t.sort&&(e=e.sort((s,n)=>t.sort(s,n,this.chart.data))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display){this.width=this.height=0;return}const s=t.labels,n=V(s.font),o=n.size,a=this._computeTitleHeight(),{boxWidth:r,itemHeight:l}=rn(s,o);let c,h;e.font=n.string,this.isHorizontal()?(c=this.maxWidth,h=this._fitRows(a,o,r,l)+10):(h=this.maxHeight,c=this._fitCols(a,n,r,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,s,n){const{ctx:o,maxWidth:a,options:{labels:{padding:r}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],h=n+r;let d=t;o.textAlign="left",o.textBaseline="middle";let f=-1,u=-h;return this.legendItems.forEach((m,g)=>{const p=s+e/2+o.measureText(m.text).width;(g===0||c[c.length-1]+p+2*r>a)&&(d+=h,c[c.length-(g>0?0:1)]=0,u+=h,f++),l[g]={left:0,top:u,row:f,width:p,height:n},c[c.length-1]+=p+r}),d}_fitCols(t,e,s,n){const{ctx:o,maxHeight:a,options:{labels:{padding:r}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],h=a-t;let d=r,f=0,u=0,m=0,g=0;return this.legendItems.forEach((p,b)=>{const{itemWidth:x,itemHeight:v}=xl(s,e,o,p,n);b>0&&u+v+2*r>h&&(d+=f+r,c.push({width:f,height:u}),m+=f+r,g++,f=u=0),l[b]={left:m,top:u,col:g,width:x,height:v},f=Math.max(f,x),u+=v+r}),d+=f,c.push({width:f,height:u}),d}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:s,labels:{padding:n},rtl:o}}=this,a=Ft(o,this.left,this.width);if(this.isHorizontal()){let r=0,l=U(s,this.left+n,this.right-this.lineWidths[r]);for(const c of e)r!==c.row&&(r=c.row,l=U(s,this.left+n,this.right-this.lineWidths[r])),c.top+=this.top+t+n,c.left=a.leftForLtr(a.x(l),c.width),l+=c.width+n}else{let r=0,l=U(s,this.top+t+n,this.bottom-this.columnSizes[r].height);for(const c of e)c.col!==r&&(r=c.col,l=U(s,this.top+t+n,this.bottom-this.columnSizes[r].height)),c.top=l,c.left+=this.left+n,c.left=a.leftForLtr(a.x(c.left),c.width),l+=c.height+n}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;ue(t,this),this._draw(),ge(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:s,ctx:n}=this,{align:o,labels:a}=t,r=R.color,l=Ft(t.rtl,this.left,this.width),c=V(a.font),{padding:h}=a,d=c.size,f=d/2;let u;this.drawTitle(),n.textAlign=l.textAlign("left"),n.textBaseline="middle",n.lineWidth=.5,n.font=c.string;const{boxWidth:m,boxHeight:g,itemHeight:p}=rn(a,d),b=function(S,w,y){if(isNaN(m)||m<=0||isNaN(g)||g<0)return;n.save();const M=C(y.lineWidth,1);if(n.fillStyle=C(y.fillStyle,r),n.lineCap=C(y.lineCap,"butt"),n.lineDashOffset=C(y.lineDashOffset,0),n.lineJoin=C(y.lineJoin,"miter"),n.lineWidth=M,n.strokeStyle=C(y.strokeStyle,r),n.setLineDash(C(y.lineDash,[])),a.usePointStyle){const T={radius:g*Math.SQRT2/2,pointStyle:y.pointStyle,rotation:y.rotation,borderWidth:M},P=l.xPlus(S,m/2),A=w+f;Ki(n,T,P,A,a.pointStyleWidth&&m)}else{const T=w+Math.max((d-g)/2,0),P=l.leftForLtr(S,m),A=Ut(y.borderRadius);n.beginPath(),Object.values(A).some(Y=>Y!==0)?Qe(n,{x:P,y:T,w:m,h:g,radius:A}):n.rect(P,T,m,g),n.fill(),M!==0&&n.stroke()}n.restore()},x=function(S,w,y){pe(n,y.text,S,w+p/2,c,{strikethrough:y.hidden,textAlign:l.textAlign(y.textAlign)})},v=this.isHorizontal(),k=this._computeTitleHeight();v?u={x:U(o,this.left+h,this.right-s[0]),y:this.top+h+k,line:0}:u={x:this.left+h,y:U(o,this.top+k+h,this.bottom-e[0].height),line:0},os(this.ctx,t.textDirection);const _=p+h;this.legendItems.forEach((S,w)=>{n.strokeStyle=S.fontColor,n.fillStyle=S.fontColor;const y=n.measureText(S.text).width,M=l.textAlign(S.textAlign||(S.textAlign=a.textAlign)),T=m+f+y;let P=u.x,A=u.y;l.setWidth(this.width),v?w>0&&P+T+h>this.right&&(A=u.y+=_,u.line++,P=u.x=U(o,this.left+h,this.right-s[u.line])):w>0&&A+_>this.bottom&&(P=u.x=P+e[u.line].width+h,u.line++,A=u.y=U(o,this.top+k+h,this.bottom-e[u.line].height));const Y=l.x(P);if(b(Y,A,S),P=mo(M,P+m+f,v?P+T:this.right,t.rtl),x(l.x(P),A,S),v)u.x+=T+h;else if(typeof S.text!="string"){const ot=c.lineHeight;u.y+=cn(S,ot)+h}else u.y+=_}),as(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,s=V(e.font),n=J(e.padding);if(!e.display)return;const o=Ft(t.rtl,this.left,this.width),a=this.ctx,r=e.position,l=s.size/2,c=n.top+l;let h,d=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),h=this.top+c,d=U(t.align,d,this.right-f);else{const m=this.columnSizes.reduce((g,p)=>Math.max(g,p.height),0);h=c+U(t.align,this.top,this.bottom-m-t.labels.padding-this._computeTitleHeight())}const u=U(r,d,d+f);a.textAlign=o.textAlign(Hi(r)),a.textBaseline="middle",a.strokeStyle=e.color,a.fillStyle=e.color,a.font=s.string,pe(a,e.text,u,h,s)}_computeTitleHeight(){const t=this.options.title,e=V(t.font),s=J(t.padding);return t.display?e.lineHeight+s.height:0}_getLegendItemAt(t,e){let s,n,o;if(At(t,this.left,this.right)&&At(e,this.top,this.bottom)){for(o=this.legendHitBoxes,s=0;so.length>a.length?o:a)),t+e.size/2+s.measureText(n).width}function vl(i,t,e){let s=i;return typeof t.text!="string"&&(s=cn(t,e)),s}function cn(i,t){const e=i.text?i.text.length:0;return t*e}function kl(i,t){return!!((i==="mousemove"||i==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(i==="click"||i==="mouseup"))}var wl={id:"legend",_element:ln,start(i,t,e){const s=i.legend=new ln({ctx:i.ctx,options:e,chart:i});gt.configure(i,s,e),gt.addBox(i,s)},stop(i){gt.removeBox(i,i.legend),delete i.legend},beforeUpdate(i,t,e){const s=i.legend;gt.configure(i,s,e),s.options=e},afterUpdate(i){const t=i.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(i,t){t.replay||i.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(i,t,e){const s=t.datasetIndex,n=e.chart;n.isDatasetVisible(s)?(n.hide(s),t.hidden=!0):(n.show(s),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:i=>i.chart.options.color,boxWidth:40,padding:10,generateLabels(i){const t=i.data.datasets,{labels:{usePointStyle:e,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=i.legend.options;return i._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(e?0:void 0),h=J(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:c.borderColor,pointStyle:s||c.pointStyle,rotation:c.rotation,textAlign:n||c.textAlign,borderRadius:a&&(r||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:i=>i.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:i=>!i.startsWith("on"),labels:{_scriptable:i=>!["generateLabels","filter","sort"].includes(i)}}};const ee={average(i){if(!i.length)return!1;let t,e,s=new Set,n=0,o=0;for(t=0,e=i.length;tr+l)/s.size,y:n/o}},nearest(i,t){if(!i.length)return!1;let e=t.x,s=t.y,n=Number.POSITIVE_INFINITY,o,a,r;for(o=0,a=i.length;o-1?i.split(` -`):i}function Sl(i,t){const{element:e,datasetIndex:s,index:n}=t,o=i.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:i,label:a,parsed:o.getParsed(n),raw:i.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:e}}function hn(i,t){const e=i.chart.ctx,{body:s,footer:n,title:o}=i,{boxWidth:a,boxHeight:r}=t,l=V(t.bodyFont),c=V(t.titleFont),h=V(t.footerFont),d=o.length,f=n.length,u=s.length,m=J(t.padding);let g=m.height,p=0,b=s.reduce((k,_)=>k+_.before.length+_.lines.length+_.after.length,0);if(b+=i.beforeBody.length+i.afterBody.length,d&&(g+=d*c.lineHeight+(d-1)*t.titleSpacing+t.titleMarginBottom),b){const k=t.displayColors?Math.max(r,l.lineHeight):l.lineHeight;g+=u*k+(b-u)*l.lineHeight+(b-1)*t.bodySpacing}f&&(g+=t.footerMarginTop+f*h.lineHeight+(f-1)*t.footerSpacing);let x=0;const v=function(k){p=Math.max(p,e.measureText(k).width+x)};return e.save(),e.font=c.string,L(i.title,v),e.font=l.string,L(i.beforeBody.concat(i.afterBody),v),x=t.displayColors?a+2+t.boxPadding:0,L(s,k=>{L(k.before,v),L(k.lines,v),L(k.after,v)}),x=0,e.font=h.string,L(i.footer,v),e.restore(),p+=m.width,{width:p,height:g}}function Ml(i,t){const{y:e,height:s}=t;return ei.height-s/2?"bottom":"center"}function Dl(i,t,e,s){const{x:n,width:o}=s,a=e.caretSize+e.caretPadding;if(i==="left"&&n+o+a>t.width||i==="right"&&n-o-a<0)return!0}function Pl(i,t,e,s){const{x:n,width:o}=e,{width:a,chartArea:{left:r,right:l}}=i;let c="center";return s==="center"?c=n<=(r+l)/2?"left":"right":n<=o/2?c="left":n>=a-o/2&&(c="right"),Dl(c,i,t,e)&&(c="center"),c}function dn(i,t,e){const s=e.yAlign||t.yAlign||Ml(i,e);return{xAlign:e.xAlign||t.xAlign||Pl(i,t,e,s),yAlign:s}}function Cl(i,t){let{x:e,width:s}=i;return t==="right"?e-=s:t==="center"&&(e-=s/2),e}function Ol(i,t,e){let{y:s,height:n}=i;return t==="top"?s+=e:t==="bottom"?s-=n+e:s-=n/2,s}function fn(i,t,e,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=i,{xAlign:r,yAlign:l}=e,c=n+o,{topLeft:h,topRight:d,bottomLeft:f,bottomRight:u}=Ut(a);let m=Cl(t,r);const g=Ol(t,l,c);return l==="center"?r==="left"?m+=c:r==="right"&&(m-=c):r==="left"?m-=Math.max(h,f)+n:r==="right"&&(m+=Math.max(d,u)+n),{x:K(m,0,s.width-t.width),y:K(g,0,s.height-t.height)}}function Le(i,t,e){const s=J(e.padding);return t==="center"?i.x+i.width/2:t==="right"?i.x+i.width-s.right:i.x+s.left}function un(i){return nt([],ht(i))}function Tl(i,t,e){return kt(i,{tooltip:t,tooltipItems:e,type:"tooltip"})}function gn(i,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?i.override(e):i}const pn={beforeTitle:rt,title(i){if(i.length>0){const t=i[0],e=t.chart.data.labels,s=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(s>0&&t.dataIndex"u"?pn[t].call(e,s):n}class bi extends pt{constructor(t){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=t.chart,this.options=t.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(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,s=this.options.setContext(this.getContext()),n=s.enabled&&e.options.animation&&s.animations,o=new ps(this.chart,n);return n._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=Tl(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:s}=e,n=j(s,"beforeTitle",this,t),o=j(s,"title",this,t),a=j(s,"afterTitle",this,t);let r=[];return r=nt(r,ht(n)),r=nt(r,ht(o)),r=nt(r,ht(a)),r}getBeforeBody(t,e){return un(j(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:s}=e,n=[];return L(t,o=>{const a={before:[],lines:[],after:[]},r=gn(s,o);nt(a.before,ht(j(r,"beforeLabel",this,o))),nt(a.lines,j(r,"label",this,o)),nt(a.after,ht(j(r,"afterLabel",this,o))),n.push(a)}),n}getAfterBody(t,e){return un(j(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:s}=e,n=j(s,"beforeFooter",this,t),o=j(s,"footer",this,t),a=j(s,"afterFooter",this,t);let r=[];return r=nt(r,ht(n)),r=nt(r,ht(o)),r=nt(r,ht(a)),r}_createItems(t){const e=this._active,s=this.chart.data,n=[],o=[],a=[];let r=[],l,c;for(l=0,c=e.length;lt.filter(h,d,f,s))),t.itemSort&&(r=r.sort((h,d)=>t.itemSort(h,d,s))),L(r,h=>{const d=gn(t.callbacks,h);n.push(j(d,"labelColor",this,h)),o.push(j(d,"labelPointStyle",this,h)),a.push(j(d,"labelTextColor",this,h))}),this.labelColors=n,this.labelPointStyles=o,this.labelTextColors=a,this.dataPoints=r,r}update(t,e){const s=this.options.setContext(this.getContext()),n=this._active;let o,a=[];if(!n.length)this.opacity!==0&&(o={opacity:0});else{const r=ee[s.position].call(this,n,this._eventPosition);a=this._createItems(s),this.title=this.getTitle(a,s),this.beforeBody=this.getBeforeBody(a,s),this.body=this.getBody(a,s),this.afterBody=this.getAfterBody(a,s),this.footer=this.getFooter(a,s);const l=this._size=hn(this,s),c=Object.assign({},r,l),h=dn(this.chart,s,c),d=fn(s,c,h,this.chart);this.xAlign=h.xAlign,this.yAlign=h.yAlign,o={opacity:1,x:d.x,y:d.y,width:l.width,height:l.height,caretX:r.x,caretY:r.y}}this._tooltipItems=a,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&s.external&&s.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,s,n){const o=this.getCaretPosition(t,s,n);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,s){const{xAlign:n,yAlign:o}=this,{caretSize:a,cornerRadius:r}=s,{topLeft:l,topRight:c,bottomLeft:h,bottomRight:d}=Ut(r),{x:f,y:u}=t,{width:m,height:g}=e;let p,b,x,v,k,_;return o==="center"?(k=u+g/2,n==="left"?(p=f,b=p-a,v=k+a,_=k-a):(p=f+m,b=p+a,v=k-a,_=k+a),x=p):(n==="left"?b=f+Math.max(l,h)+a:n==="right"?b=f+m-Math.max(c,d)-a:b=this.caretX,o==="top"?(v=u,k=v-a,p=b-a,x=b+a):(v=u+g,k=v+a,p=b+a,x=b-a),_=v),{x1:p,x2:b,x3:x,y1:v,y2:k,y3:_}}drawTitle(t,e,s){const n=this.title,o=n.length;let a,r,l;if(o){const c=Ft(s.rtl,this.x,this.width);for(t.x=Le(this,s.titleAlign,s),e.textAlign=c.textAlign(s.titleAlign),e.textBaseline="middle",a=V(s.titleFont),r=s.titleSpacing,e.fillStyle=s.titleColor,e.font=a.string,l=0;lx!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,Qe(t,{x:g,y:m,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),Qe(t,{x:p,y:m+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(g,m,c,l),t.strokeRect(g,m,c,l),t.fillStyle=a.backgroundColor,t.fillRect(p,m+1,c-2,l-2))}t.fillStyle=this.labelTextColors[s]}drawBody(t,e,s){const{body:n}=this,{bodySpacing:o,bodyAlign:a,displayColors:r,boxHeight:l,boxWidth:c,boxPadding:h}=s,d=V(s.bodyFont);let f=d.lineHeight,u=0;const m=Ft(s.rtl,this.x,this.width),g=function(y){e.fillText(y,m.x(t.x+u),t.y+f/2),t.y+=f+o},p=m.textAlign(a);let b,x,v,k,_,S,w;for(e.textAlign=a,e.textBaseline="middle",e.font=d.string,t.x=Le(this,p,s),e.fillStyle=s.bodyColor,L(this.beforeBody,g),u=r&&p!=="right"?a==="center"?c/2+h:c+2+h:0,k=0,S=n.length;k0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,s=this.$animations,n=s&&s.x,o=s&&s.y;if(n||o){const a=ee[t.position].call(this,this._active,this._eventPosition);if(!a)return;const r=this._size=hn(this,t),l=Object.assign({},a,this._size),c=dn(e,t,l),h=fn(t,l,c,e);(n._to!==h.x||o._to!==h.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=r.width,this.height=r.height,this.caretX=a.x,this.caretY=a.y,this._resolveAnimations().update(this,h))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let s=this.opacity;if(!s)return;this._updateAnimationTarget(e);const n={width:this.width,height:this.height},o={x:this.x,y:this.y};s=Math.abs(s)<.001?0:s;const a=J(e.padding),r=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&r&&(t.save(),t.globalAlpha=s,this.drawBackground(o,t,n,e),os(t,e.textDirection),o.y+=a.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),as(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const s=this._active,n=t.map(({datasetIndex:r,index:l})=>{const c=this.chart.getDatasetMeta(r);if(!c)throw new Error("Cannot find a dataset at index "+r);return{datasetIndex:r,element:c.data[l],index:l}}),o=!re(s,n),a=this._positionChanged(n,e);(o||a)&&(this._active=n,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,s=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const n=this.options,o=this._active||[],a=this._getActiveElements(t,o,e,s),r=this._positionChanged(a,t),l=e||!re(a,o)||r;return l&&(this._active=a,(n.enabled||n.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,s,n){const o=this.options;if(t.type==="mouseout")return[];if(!n)return e.filter(r=>this.chart.data.datasets[r.datasetIndex]&&this.chart.getDatasetMeta(r.datasetIndex).controller.getParsed(r.index)!==void 0);const a=this.chart.getElementsAtEventForMode(t,o.mode,o,s);return o.reverse&&a.reverse(),a}_positionChanged(t,e){const{caretX:s,caretY:n,options:o}=this,a=ee[o.position].call(this,t,e);return a!==!1&&(s!==a.x||n!==a.y)}}D(bi,"positioners",ee);var Al={id:"tooltip",_element:bi,positioners:ee,afterInit(i,t,e){e&&(i.tooltip=new bi({chart:i,options:e}))},beforeUpdate(i,t,e){i.tooltip&&i.tooltip.initialize(e)},reset(i,t,e){i.tooltip&&i.tooltip.initialize(e)},afterDraw(i){const t=i.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(i.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(i.ctx),i.notifyPlugins("afterTooltipDraw",e)}},afterEvent(i,t){if(i.tooltip){const e=t.replay;i.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.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:(i,t)=>t.bodyFont.size,boxWidth:(i,t)=>t.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:pn},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:i=>i!=="filter"&&i!=="itemSort"&&i!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const Ll=(i,t,e,s)=>(typeof t=="string"?(e=i.push(t)-1,s.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function Il(i,t,e,s){const n=i.indexOf(t);if(n===-1)return Ll(i,t,e,s);const o=i.lastIndexOf(t);return n!==o?e:n}const Fl=(i,t)=>i===null?null:K(Math.round(i),0,t);function mn(i){const t=this.getLabels();return i>=0&&ie.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}D(_i,"id","category"),D(_i,"defaults",{ticks:{callback:mn}});function Rl(i,t){const e=[],{bounds:n,step:o,min:a,max:r,precision:l,count:c,maxTicks:h,maxDigits:d,includeBounds:f}=i,u=o||1,m=h-1,{min:g,max:p}=t,b=!I(a),x=!I(r),v=!I(c),k=(p-g)/(d+1);let _=Li((p-g)/m/u)*u,S,w,y,M;if(_<1e-14&&!b&&!x)return[{value:g},{value:p}];M=Math.ceil(p/_)-Math.floor(g/_),M>m&&(_=Li(M*_/m/u)*u),I(l)||(S=Math.pow(10,l),_=Math.ceil(_*S)/S),n==="ticks"?(w=Math.floor(g/_)*_,y=Math.ceil(p/_)*_):(w=g,y=p),b&&x&&o&&no((r-a)/o,_/1e3)?(M=Math.round(Math.min((r-a)/_,h)),_=(r-a)/M,w=a,y=r):v?(w=b?a:w,y=x?r:y,M=c-1,_=(y-w)/M):(M=(y-w)/_,Nt(M,Math.round(M),_/1e3)?M=Math.round(M):M=Math.ceil(M));const T=Math.max(Ii(_),Ii(w));S=Math.pow(10,I(l)?T:l),w=Math.round(w*S)/S,y=Math.round(y*S)/S;let P=0;for(b&&(f&&w!==a?(e.push({value:a}),wr)break;e.push({value:A})}return x&&f&&y!==r?e.length&&Nt(e[e.length-1].value,r,bn(r,k,i))?e[e.length-1].value=r:e.push({value:r}):(!x||y===r)&&e.push({value:y}),e}function bn(i,t,{horizontal:e,minRotation:s}){const n=_t(s),o=(e?Math.sin(n):Math.cos(n))||.001,a=.75*t*(""+i).length;return Math.min(t/o,a)}class zl extends Rt{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return I(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:s}=this.getUserBounds();let{min:n,max:o}=this;const a=l=>n=e?n:l,r=l=>o=s?o:l;if(t){const l=Tt(n),c=Tt(o);l<0&&c<0?r(0):l>0&&c>0&&a(0)}if(n===o){let l=o===0?1:Math.abs(o*.05);r(o+l),t||a(n-l)}this.min=n,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:s}=t,n;return s?(n=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let s=this.getTickLimit();s=Math.max(2,s);const n={maxTicks:s,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,a=Rl(n,o);return t.bounds==="ticks"&&oo(a,this,"value"),t.reverse?(a.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),a}configure(){const t=this.ticks;let e=this.min,s=this.max;if(super.configure(),this.options.offset&&t.length){const n=(s-e)/Math.max(t.length-1,1)/2;e-=n,s+=n}this._startValue=e,this._endValue=s,this._valueRange=s-e}getLabelForValue(t){return $i(t,this.chart.options.locale,this.options.ticks.format)}}class xi extends zl{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=W(t)?t:0,this.max=W(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,s=_t(this.options.ticks.minRotation),n=(t?Math.sin(s):Math.cos(s))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/n))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}D(xi,"id","linear"),D(xi,"defaults",{ticks:{callback:Yi.formatters.numeric}});const Ie={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}},$=Object.keys(Ie);function _n(i,t){return i-t}function xn(i,t){if(I(t))return null;const e=i._adapter,{parser:s,round:n,isoWeekday:o}=i._parseOpts;let a=t;return typeof s=="function"&&(a=s(a)),W(a)||(a=typeof s=="string"?e.parse(a,s):e.parse(a)),a===null?null:(n&&(a=n==="week"&&(Vt(o)||o===!0)?e.startOf(a,"isoWeek",o):e.startOf(a,n)),+a)}function yn(i,t,e,s){const n=$.length;for(let o=$.indexOf(i);o=$.indexOf(e);o--){const a=$[o];if(Ie[a].common&&i._adapter.diff(n,s,a)>=t-1)return a}return $[e?$.indexOf(e):0]}function Bl(i){for(let t=$.indexOf(i)+1,e=$.length;t=t?e[s]:e[n];i[o]=!0}}function Hl(i,t,e,s){const n=i._adapter,o=+n.startOf(t[0].value,s),a=t[t.length-1].value;let r,l;for(r=o;r<=a;r=+n.add(r,1,s))l=e[r],l>=0&&(t[l].major=!0);return t}function kn(i,t,e){const s=[],n={},o=t.length;let a,r;for(a=0;a+t.value))}initOffsets(t=[]){let e=0,s=0,n,o;this.options.offset&&t.length&&(n=this.getDecimalForValue(t[0]),t.length===1?e=1-n:e=(this.getDecimalForValue(t[1])-n)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?s=o:s=(o-this.getDecimalForValue(t[t.length-2]))/2);const a=t.length<3?.5:.25;e=K(e,0,a),s=K(s,0,a),this._offsets={start:e,end:s,factor:1/(e+1+s)}}_generate(){const t=this._adapter,e=this.min,s=this.max,n=this.options,o=n.time,a=o.unit||yn(o.minUnit,e,s,this._getLabelCapacity(e)),r=C(n.ticks.stepSize,1),l=a==="week"?o.isoWeekday:!1,c=Vt(l)||l===!0,h={};let d=e,f,u;if(c&&(d=+t.startOf(d,"isoWeek",l)),d=+t.startOf(d,c?"day":a),t.diff(s,e,a)>1e5*r)throw new Error(e+" and "+s+" are too far apart with stepSize of "+r+" "+a);const m=n.ticks.source==="data"&&this.getDataTimestamps();for(f=d,u=0;f+g)}getLabelForValue(t){const e=this._adapter,s=this.options.time;return s.tooltipFormat?e.format(t,s.tooltipFormat):e.format(t,s.displayFormats.datetime)}format(t,e){const n=this.options.time.displayFormats,o=this._unit,a=e||n[o];return this._adapter.format(t,a)}_tickFormatFunction(t,e,s,n){const o=this.options,a=o.ticks.callback;if(a)return F(a,[t,e,s],this);const r=o.time.displayFormats,l=this._unit,c=this._majorUnit,h=l&&r[l],d=c&&r[c],f=s[e],u=c&&d&&f&&f.major;return this._adapter.format(t,n||(u?d:h))}generateTickLabels(t){let e,s,n;for(e=0,s=t.length;e0?r:1}getDataTimestamps(){let t=this._cache.data||[],e,s;if(t.length)return t;const n=this.getMatchingVisibleMetas();if(this._normalized&&n.length)return this._cache.data=n[0].controller.getAllParsedValues(this);for(e=0,s=n.length;e=i[s].pos&&t<=i[n].pos&&({lo:s,hi:n}=xt(i,"pos",t)),{pos:o,time:r}=i[s],{pos:a,time:l}=i[n]):(t>=i[s].time&&t<=i[n].time&&({lo:s,hi:n}=xt(i,"time",t)),{time:o,pos:r}=i[s],{time:a,pos:l}=i[n]);const c=a-o;return c?r+(l-r)*(t-o)/c:r}class wn extends Fe{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Re(e,this.min),this._tableRange=Re(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:s}=this,n=[],o=[];let a,r,l,c,h;for(a=0,r=t.length;a=e&&c<=s&&n.push(c);if(n.length<2)return[{time:e,pos:0},{time:s,pos:1}];for(a=0,r=n.length;an-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),s=this.getLabelTimestamps();return e.length&&s.length?t=this.normalize(e.concat(s)):t=e.length?e:s,t=this._cache.all=t,t}getDecimalForValue(t){return(Re(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,s=this.getDecimalForPixel(t)/e.factor-e.end;return Re(this._table,s*this._tableRange+this._minPos,!0)}}D(wn,"id","timeseries"),D(wn,"defaults",Fe.defaults);function Wl(i,t,e,s,n,o,a,r){var l=typeof i=="function"?i.options:i;return t&&(l.render=t,l.staticRenderFns=e,l._compiled=!0),{exports:i,options:l}}ct.register(ve,mt,Te,xi,_i,bl,Al,wl);const Sn=["#4271ae","#c82829","#8959a8","#4d9a0f","#f5871f","#3e999f","#718c00","#a3685a","#525252","#eab700"],Nl={props:{analyticsData:{type:Object,default:()=>({})}},data(){return{startDate:"",endDate:"",data:this.analyticsData||{},chart:null,users:[],selectedEmails:[],selectedPages:[]}},computed:{hasData(){return this.data&&this.data.totalVisits>0},pagesPerSession(){var i;return(i=this.data)!=null&&i.uniqueSessions?(this.data.totalVisits/this.data.uniqueSessions).toFixed(1):"0"},userOptions(){return this.users.map(i=>({value:i.email,text:i.label}))},pageOptions(){var i;return(i=this.data)!=null&&i.visitsByPage?Object.keys(this.data.visitsByPage).map(t=>({value:t,text:t})).sort((t,e)=>t.text.localeCompare(e.text)):[]}},watch:{analyticsData(i){this.data=i||{},this.renderChart()}},mounted(){this.setDefaultDates(),this.fetchData()},beforeUnmount(){this.destroyChart()},methods:{setDefaultDates(){const i=new Date,t=new Date(i);t.setDate(t.getDate()-30),this.endDate=i.toISOString().split("T")[0],this.startDate=t.toISOString().split("T")[0]},onUserSelectionChange(i){this.selectedEmails=i,this.fetchData()},onPageSelectionChange(i){this.selectedPages=i,this.fetchData()},async fetchData(){const i=new URLSearchParams;this.startDate&&i.set("startDate",this.startDate),this.endDate&&i.set("endDate",this.endDate),this.selectedEmails.length&&i.set("emails",this.selectedEmails.join(",")),this.selectedPages.length&&i.set("pageNames",this.selectedPages.join(","));try{const e=await(await fetch(`/analytics-data.json?${i}`)).json();console.log(e.data),e.status==="success"&&(this.data=e.data),e.users&&!this.users.length&&(this.users=e.users),this.renderChart()}catch(t){console.error("Analytics fetch error:",t),this.renderChart()}},destroyChart(){this.chart&&(this.chart.destroy(),this.chart=null)},formatDateFR(i){const[t,e,s]=i.split("-");return`${s}/${e}/${t}`},renderChart(){var a;this.destroyChart();const i=this.$refs.chartCanvas;if(!i||!((a=this.data)!=null&&a.visitsByDay))return;const t=Object.keys(this.data.visitsByDay);if(!t.length)return;const e=t.map(r=>this.formatDateFR(r)),s=this.selectedPages.length===0&&this.data.visitsByDayByPage;let n,o;if(s)n=Object.keys(this.data.visitsByDayByPage).map((l,c)=>{const h=Sn[c%Sn.length],d=t.map(f=>{var u;return((u=this.data.visitsByDayByPage[l])==null?void 0:u[f])||0});return{label:l,data:d,borderColor:h,backgroundColor:"transparent",fill:!1,tension:.3,pointRadius:2,pointHoverRadius:4,borderWidth:2}}),o=Math.max(...n.flatMap(l=>l.data));else{const r=Object.values(this.data.visitsByDay);n=[{label:"Visites",data:r,borderColor:"#4271ae",backgroundColor:"rgba(66, 113, 174, 0.1)",fill:!0,tension:.3,pointRadius:3,pointHoverRadius:5}],o=Math.max(...r)}this.chart=new ct(i,{type:"line",data:{labels:e,datasets:n},options:{responsive:!0,maintainAspectRatio:!1,plugins:{tooltip:{mode:"index",intersect:!1},legend:{display:s,position:"bottom",labels:{boxWidth:12,padding:12}}},scales:{y:{beginAtZero:!0,max:o+3,ticks:{precision:0}}}}})}}};var Vl=function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-analytics-dashboard"},[e("div",{staticClass:"k-analytics-filters"},[e("div",{staticClass:"k-analytics-date-filter"},[t._m(0),e("div",{staticClass:"k-date-inputs-wrapper"},[e("label",[t._v(" Du "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.startDate,expression:"startDate"}],attrs:{type:"date"},domProps:{value:t.startDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.startDate=s.target.value)}}})]),e("label",[t._v(" Au "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.endDate,expression:"endDate"}],attrs:{type:"date"},domProps:{value:t.endDate},on:{change:t.fetchData,input:function(s){s.target.composing||(t.endDate=s.target.value)}}})])])]),e("div",{staticClass:"k-analytics-user-filter"},[e("k-multiselect-field",{attrs:{options:t.userOptions,value:t.selectedEmails,label:"Filtrer par utilisateur(s)",search:"true",name:"user"},on:{input:t.onUserSelectionChange}})],1),e("div",{staticClass:"k-analytics-page-filter"},[e("k-multiselect-field",{attrs:{options:t.pageOptions,value:t.selectedPages,label:"Filtrer par page(s)",search:"true",name:"page"},on:{input:t.onPageSelectionChange}})],1)]),t.hasData?[e("div",{staticClass:"k-analytics-grid"},[e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Sessions uniques")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.uniqueSessions))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages vues")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.data.totalVisits))])]),e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages / session")]),e("div",{staticClass:"k-analytics-metric"},[t._v(t._s(t.pagesPerSession))])])]),e("div",{staticClass:"k-analytics-chart-container"},[e("h3",[t._v("Visites par jour")]),e("canvas",{ref:"chartCanvas"})]),t.data.visitsByPage&&Object.keys(t.data.visitsByPage).length?e("div",{staticClass:"k-analytics-card"},[e("h3",[t._v("Pages les plus visitées")]),e("ul",{staticClass:"k-analytics-list"},t._l(t.data.visitsByPage,function(s,n){return e("li",{key:n},[e("span",{staticClass:"k-analytics-list-label"},[t._v(t._s(n))]),e("span",{staticClass:"k-analytics-list-value"},[t._v(t._s(s))])])}),0)]):t._e()]:e("div",{staticClass:"k-analytics-empty"},[e("p",[t._v("Aucune donnée à afficher")])])],2)},jl=[function(){var i=this,t=i._self._c;return t("header",{staticClass:"k-field-header"},[t("label",{staticClass:"k-label k-field-label",attrs:{title:"Filtrer par dates"}},[t("span",{staticClass:"k-label-text"},[i._v("Filtrer par dates ")])])])}],$l=Wl(Nl,Vl,jl);const Yl=$l.exports;window.panel.plugin("adrienpayet/analytics",{fields:{"analytics-dashboard":Yl}})})(); +var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); +(function() { + "use strict"; + /*! + * @kurkle/color v0.3.4 + * https://github.com/kurkle/color#readme + * (c) 2024 Jukka Kurkela + * Released under the MIT License + */ + function round(v) { + return v + 0.5 | 0; + } + const lim = (v, l, h) => Math.max(Math.min(v, h), l); + function p2b(v) { + return lim(round(v * 2.55), 0, 255); + } + function n2b(v) { + return lim(round(v * 255), 0, 255); + } + function b2n(v) { + return lim(round(v / 2.55) / 100, 0, 1); + } + function n2p(v) { + return lim(round(v * 100), 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 }; + const hex = [..."0123456789ABCDEF"]; + const h1 = (b) => hex[b & 15]; + const h2 = (b) => hex[(b & 240) >> 4] + hex[b & 15]; + const eq = (b) => (b & 240) >> 4 === (b & 15); + const isShort = (v) => eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a); + function hexParse(str) { + var len = str.length; + var ret; + if (str[0] === "#") { + if (len === 4 || len === 5) { + ret = { + r: 255 & map$1[str[1]] * 17, + g: 255 & map$1[str[2]] * 17, + b: 255 & map$1[str[3]] * 17, + a: len === 5 ? map$1[str[4]] * 17 : 255 + }; + } else if (len === 7 || len === 9) { + 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: len === 9 ? map$1[str[7]] << 4 | map$1[str[8]] : 255 + }; + } + } + return ret; + } + const alpha = (a, f) => a < 255 ? f(a) : ""; + function hexString(v) { + var f = isShort(v) ? h1 : h2; + return v ? "#" + f(v.r) + f(v.g) + f(v.b) + alpha(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); + const f = (n, k = (n + h / 30) % 12) => 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 = (n, k = (n + h / 60) % 6) => 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, 0.5); + let i; + if (w + b > 1) { + i = 1 / (w + b); + w *= i; + b *= i; + } + for (i = 0; i < 3; i++) { + rgb[i] *= 1 - w - b; + rgb[i] += w; + } + return rgb; + } + function hueValue(r, g, b, d, max) { + if (r === max) { + return (g - b) / d + (g < b ? 6 : 0); + } + if (g === max) { + return (b - r) / d + 2; + } + return (r - g) / d + 4; + } + function rgb2hsl(v) { + const range = 255; + const r = v.r / range; + const g = v.g / range; + const b = v.b / range; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const l = (max + min) / 2; + let h, s, d; + if (max !== min) { + d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + h = hueValue(r, g, b, d, max); + h = h * 60 + 0.5; + } + return [h | 0, s || 0, l]; + } + function calln(f, a, b, c) { + return (Array.isArray(a) ? f(a[0], a[1], a[2]) : f(a, b, c)).map(n2b); + } + function hsl2rgb(h, s, l) { + return calln(hsl2rgbn, h, s, l); + } + function hwb2rgb(h, w, b) { + return calln(hwb2rgbn, h, w, b); + } + function hsv2rgb(h, s, v) { + return calln(hsv2rgbn, h, s, v); + } + function hue(h) { + return (h % 360 + 360) % 360; + } + function hueParse(str) { + const m = HUE_RE.exec(str); + let a = 255; + let v; + if (!m) { + return; + } + if (m[5] !== v) { + a = m[6] ? p2b(+m[5]) : n2b(+m[5]); + } + const h = hue(+m[2]); + const p1 = +m[3] / 100; + const p2 = +m[4] / 100; + if (m[1] === "hwb") { + v = hwb2rgb(h, p1, p2); + } else if (m[1] === "hsv") { + v = hsv2rgb(h, p1, p2); + } else { + v = hsl2rgb(h, p1, p2); + } + return { + r: v[0], + g: v[1], + b: v[2], + a + }; + } + function rotate(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]; + } + function hslString(v) { + if (!v) { + return; + } + const a = rgb2hsl(v); + const h = a[0]; + const s = n2p(a[1]); + const l = n2p(a[2]); + return v.a < 255 ? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})` : `hsl(${h}, ${s}%, ${l}%)`; + } + const map = { + x: "dark", + Z: "light", + Y: "re", + X: "blu", + W: "gr", + V: "medium", + U: "slate", + A: "ee", + T: "ol", + S: "or", + B: "ra", + C: "lateg", + D: "ights", + R: "in", + Q: "turquois", + E: "hi", + P: "ro", + O: "al", + N: "le", + M: "de", + L: "yello", + F: "en", + K: "ch", + G: "arks", + H: "ea", + I: "ightg", + J: "wh" + }; + const names$1 = { + OiceXe: "f0f8ff", + antiquewEte: "faebd7", + aqua: "ffff", + aquamarRe: "7fffd4", + azuY: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "0", + blanKedOmond: "ffebcd", + Xe: "ff", + XeviTet: "8a2be2", + bPwn: "a52a2a", + burlywood: "deb887", + caMtXe: "5f9ea0", + KartYuse: "7fff00", + KocTate: "d2691e", + cSO: "ff7f50", + cSnflowerXe: "6495ed", + cSnsilk: "fff8dc", + crimson: "dc143c", + cyan: "ffff", + xXe: "8b", + xcyan: "8b8b", + xgTMnPd: "b8860b", + xWay: "a9a9a9", + xgYF: "6400", + xgYy: "a9a9a9", + xkhaki: "bdb76b", + xmagFta: "8b008b", + xTivegYF: "556b2f", + xSange: "ff8c00", + xScEd: "9932cc", + xYd: "8b0000", + xsOmon: "e9967a", + xsHgYF: "8fbc8f", + xUXe: "483d8b", + xUWay: "2f4f4f", + xUgYy: "2f4f4f", + xQe: "ced1", + xviTet: "9400d3", + dAppRk: "ff1493", + dApskyXe: "bfff", + dimWay: "696969", + dimgYy: "696969", + dodgerXe: "1e90ff", + fiYbrick: "b22222", + flSOwEte: "fffaf0", + foYstWAn: "228b22", + fuKsia: "ff00ff", + gaRsbSo: "dcdcdc", + ghostwEte: "f8f8ff", + gTd: "ffd700", + gTMnPd: "daa520", + Way: "808080", + gYF: "8000", + gYFLw: "adff2f", + gYy: "808080", + honeyMw: "f0fff0", + hotpRk: "ff69b4", + RdianYd: "cd5c5c", + Rdigo: "4b0082", + ivSy: "fffff0", + khaki: "f0e68c", + lavFMr: "e6e6fa", + lavFMrXsh: "fff0f5", + lawngYF: "7cfc00", + NmoncEffon: "fffacd", + ZXe: "add8e6", + ZcSO: "f08080", + Zcyan: "e0ffff", + ZgTMnPdLw: "fafad2", + ZWay: "d3d3d3", + ZgYF: "90ee90", + ZgYy: "d3d3d3", + ZpRk: "ffb6c1", + ZsOmon: "ffa07a", + ZsHgYF: "20b2aa", + ZskyXe: "87cefa", + ZUWay: "778899", + ZUgYy: "778899", + ZstAlXe: "b0c4de", + ZLw: "ffffe0", + lime: "ff00", + limegYF: "32cd32", + lRF: "faf0e6", + magFta: "ff00ff", + maPon: "800000", + VaquamarRe: "66cdaa", + VXe: "cd", + VScEd: "ba55d3", + VpurpN: "9370db", + VsHgYF: "3cb371", + VUXe: "7b68ee", + VsprRggYF: "fa9a", + VQe: "48d1cc", + VviTetYd: "c71585", + midnightXe: "191970", + mRtcYam: "f5fffa", + mistyPse: "ffe4e1", + moccasR: "ffe4b5", + navajowEte: "ffdead", + navy: "80", + Tdlace: "fdf5e6", + Tive: "808000", + TivedBb: "6b8e23", + Sange: "ffa500", + SangeYd: "ff4500", + ScEd: "da70d6", + pOegTMnPd: "eee8aa", + pOegYF: "98fb98", + pOeQe: "afeeee", + pOeviTetYd: "db7093", + papayawEp: "ffefd5", + pHKpuff: "ffdab9", + peru: "cd853f", + pRk: "ffc0cb", + plum: "dda0dd", + powMrXe: "b0e0e6", + purpN: "800080", + YbeccapurpN: "663399", + Yd: "ff0000", + Psybrown: "bc8f8f", + PyOXe: "4169e1", + saddNbPwn: "8b4513", + sOmon: "fa8072", + sandybPwn: "f4a460", + sHgYF: "2e8b57", + sHshell: "fff5ee", + siFna: "a0522d", + silver: "c0c0c0", + skyXe: "87ceeb", + UXe: "6a5acd", + UWay: "708090", + UgYy: "708090", + snow: "fffafa", + sprRggYF: "ff7f", + stAlXe: "4682b4", + tan: "d2b48c", + teO: "8080", + tEstN: "d8bfd8", + tomato: "ff6347", + Qe: "40e0d0", + viTet: "ee82ee", + JHt: "f5deb3", + wEte: "ffffff", + wEtesmoke: "f5f5f5", + Lw: "ffff00", + LwgYF: "9acd32" + }; + function unpack() { + const unpacked = {}; + const keys = Object.keys(names$1); + const tkeys = Object.keys(map); + let i, j, k, ok, nk; + for (i = 0; i < keys.length; i++) { + ok = nk = keys[i]; + for (j = 0; j < tkeys.length; j++) { + k = tkeys[j]; + nk = nk.replace(k, map[k]); + } + k = parseInt(names$1[ok], 16); + unpacked[nk] = [k >> 16 & 255, k >> 8 & 255, k & 255]; + } + return unpacked; + } + let names; + function nameParse(str) { + if (!names) { + names = unpack(); + names.transparent = [0, 0, 0, 0]; + } + const a = names[str.toLowerCase()]; + return a && { + r: a[0], + g: a[1], + b: a[2], + a: a.length === 4 ? a[3] : 255 + }; + } + const RGB_RE = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/; + function rgbParse(str) { + const m = RGB_RE.exec(str); + let a = 255; + let r, g, b; + if (!m) { + return; + } + if (m[7] !== r) { + const v = +m[7]; + a = m[8] ? p2b(v) : lim(v * 255, 0, 255); + } + 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)); + return { + r, + g, + b, + a + }; + } + function rgbString(v) { + return v && (v.a < 255 ? `rgba(${v.r}, ${v.g}, ${v.b}, ${b2n(v.a)})` : `rgb(${v.r}, ${v.g}, ${v.b})`); + } + const to = (v) => v <= 31308e-7 ? v * 12.92 : Math.pow(v, 1 / 2.4) * 1.055 - 0.055; + const from = (v) => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); + function interpolate$1(rgb1, rgb2, t) { + const r = from(b2n(rgb1.r)); + const g = from(b2n(rgb1.g)); + const 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) + }; + } + function modHSL(v, i, ratio) { + if (v) { + let tmp = rgb2hsl(v); + tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 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 }; + if (Array.isArray(input)) { + if (input.length >= 3) { + v = { r: input[0], g: input[1], b: input[2], a: 255 }; + if (input.length > 3) { + v.a = n2b(input[3]); + } + } + } else { + v = clone$1(input, { r: 0, g: 0, b: 0, a: 1 }); + v.a = n2b(v.a); + } + return v; + } + function functionParse(str) { + if (str.charAt(0) === "r") { + return rgbParse(str); + } + return hueParse(str); + } + class Color { + constructor(input) { + if (input instanceof Color) { + return input; + } + const type = typeof input; + let v; + if (type === "object") { + v = fromObject(input); + } else if (type === "string") { + v = hexParse(input) || nameParse(input) || functionParse(input); + } + this._rgb = v; + this._valid = !!v; + } + get valid() { + return this._valid; + } + get rgb() { + var v = clone$1(this._rgb); + if (v) { + v.a = b2n(v.a); + } + return v; + } + set rgb(obj) { + this._rgb = fromObject(obj); + } + rgbString() { + return this._valid ? rgbString(this._rgb) : void 0; + } + hexString() { + return this._valid ? hexString(this._rgb) : void 0; + } + hslString() { + return this._valid ? hslString(this._rgb) : void 0; + } + mix(color2, weight) { + if (color2) { + const c1 = this.rgb; + const c2 = color2.rgb; + let w2; + const p = weight === w2 ? 0.5 : weight; + const w = 2 * p - 1; + const a = c1.a - c2.a; + const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2; + w2 = 1 - w1; + c1.r = 255 & w1 * c1.r + w2 * c2.r + 0.5; + c1.g = 255 & w1 * c1.g + w2 * c2.g + 0.5; + c1.b = 255 & w1 * c1.b + w2 * c2.b + 0.5; + c1.a = p * c1.a + (1 - p) * c2.a; + this.rgb = c1; + } + return this; + } + interpolate(color2, t) { + if (color2) { + this._rgb = interpolate$1(this._rgb, color2._rgb, t); + } + return this; + } + clone() { + return new Color(this.rgb); + } + alpha(a) { + this._rgb.a = n2b(a); + return this; + } + clearer(ratio) { + const rgb = this._rgb; + rgb.a *= 1 - ratio; + return this; + } + greyscale() { + const rgb = this._rgb; + const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11); + rgb.r = rgb.g = rgb.b = val; + return this; + } + opaquer(ratio) { + const rgb = this._rgb; + rgb.a *= 1 + ratio; + return this; + } + negate() { + const v = this._rgb; + v.r = 255 - v.r; + v.g = 255 - v.g; + v.b = 255 - v.b; + return this; + } + lighten(ratio) { + modHSL(this._rgb, 2, ratio); + return this; + } + darken(ratio) { + modHSL(this._rgb, 2, -ratio); + return this; + } + saturate(ratio) { + modHSL(this._rgb, 1, ratio); + return this; + } + desaturate(ratio) { + modHSL(this._rgb, 1, -ratio); + return this; + } + rotate(deg) { + rotate(this._rgb, deg); + return this; + } + } + /*! + * Chart.js v4.5.1 + * https://www.chartjs.org + * (c) 2025 Chart.js Contributors + * Released under the MIT License + */ + function noop() { + } + const uid = /* @__PURE__ */ (() => { + let id = 0; + return () => id++; + })(); + function isNullOrUndef(value) { + return value === null || value === void 0; + } + function isArray(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + const type = Object.prototype.toString.call(value); + if (type.slice(0, 7) === "[object" && type.slice(-6) === "Array]") { + return true; + } + return false; + } + function isObject(value) { + return value !== null && Object.prototype.toString.call(value) === "[object Object]"; + } + function isNumberFinite(value) { + return (typeof value === "number" || value instanceof Number) && isFinite(+value); + } + function finiteOrDefault(value, defaultValue) { + return isNumberFinite(value) ? value : defaultValue; + } + function valueOrDefault(value, defaultValue) { + return typeof value === "undefined" ? defaultValue : value; + } + const toDimension = (value, dimension) => typeof value === "string" && value.endsWith("%") ? parseFloat(value) / 100 * dimension : +value; + function callback(fn, args, thisArg) { + if (fn && typeof fn.call === "function") { + return fn.apply(thisArg, args); + } + } + function each(loopable, fn, thisArg, reverse) { + let i, len, keys; + if (isArray(loopable)) { + len = loopable.length; + { + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[i], i); + } + } + } else if (isObject(loopable)) { + keys = Object.keys(loopable); + len = keys.length; + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[keys[i]], keys[i]); + } + } + } + function _elementsEqual(a0, a1) { + let i, ilen, v0, v1; + if (!a0 || !a1 || a0.length !== a1.length) { + return false; + } + for (i = 0, ilen = a0.length; i < ilen; ++i) { + v0 = a0[i]; + v1 = a1[i]; + if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) { + return false; + } + } + return true; + } + function clone(source) { + if (isArray(source)) { + return source.map(clone); + } + if (isObject(source)) { + const target = /* @__PURE__ */ Object.create(null); + const keys = Object.keys(source); + const klen = keys.length; + let k = 0; + for (; k < klen; ++k) { + target[keys[k]] = clone(source[keys[k]]); + } + return target; + } + return source; + } + function isValidKey(key) { + return [ + "__proto__", + "prototype", + "constructor" + ].indexOf(key) === -1; + } + function _merger(key, target, source, options) { + if (!isValidKey(key)) { + return; + } + const tval = target[key]; + const sval = source[key]; + if (isObject(tval) && isObject(sval)) { + merge(tval, sval, options); + } else { + target[key] = clone(sval); + } + } + function merge(target, source, options) { + const sources = isArray(source) ? source : [ + source + ]; + const ilen = sources.length; + if (!isObject(target)) { + return target; + } + options = options || {}; + const merger = options.merger || _merger; + let current; + for (let i = 0; i < ilen; ++i) { + current = sources[i]; + if (!isObject(current)) { + continue; + } + const keys = Object.keys(current); + for (let k = 0, klen = keys.length; k < klen; ++k) { + merger(keys[k], target, current, options); + } + } + return target; + } + function mergeIf(target, source) { + return merge(target, source, { + merger: _mergerIf + }); + } + function _mergerIf(key, target, source) { + if (!isValidKey(key)) { + return; + } + const tval = target[key]; + const sval = source[key]; + if (isObject(tval) && isObject(sval)) { + mergeIf(tval, sval); + } else if (!Object.prototype.hasOwnProperty.call(target, key)) { + target[key] = clone(sval); + } + } + const keyResolvers = { + // Chart.helpers.core resolveObjectKey should resolve empty key to root object + "": (v) => v, + // default resolvers + x: (o) => o.x, + y: (o) => o.y + }; + function _splitKey(key) { + const parts = key.split("."); + const keys = []; + let tmp = ""; + for (const part of parts) { + tmp += part; + if (tmp.endsWith("\\")) { + tmp = tmp.slice(0, -1) + "."; + } else { + keys.push(tmp); + tmp = ""; + } + } + return keys; + } + function _getKeyResolver(key) { + const keys = _splitKey(key); + return (obj) => { + for (const k of keys) { + if (k === "") { + break; + } + obj = obj && obj[k]; + } + return obj; + }; + } + function resolveObjectKey(obj, key) { + const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key)); + return resolver(obj); + } + function _capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + const defined = (value) => typeof value !== "undefined"; + const isFunction = (value) => typeof value === "function"; + const setsEqual = (a, b) => { + if (a.size !== b.size) { + return false; + } + for (const item of a) { + if (!b.has(item)) { + return false; + } + } + return true; + }; + function _isClickEvent(e) { + return e.type === "mouseup" || e.type === "click" || e.type === "contextmenu"; + } + const PI = Math.PI; + const TAU = 2 * PI; + const PITAU = TAU + PI; + const INFINITY = Number.POSITIVE_INFINITY; + const RAD_PER_DEG = PI / 180; + const HALF_PI = PI / 2; + const QUARTER_PI = PI / 4; + const TWO_THIRDS_PI = PI * 2 / 3; + const log10 = Math.log10; + const sign = Math.sign; + function almostEquals(x, y, epsilon) { + return Math.abs(x - y) < epsilon; + } + function niceNum(range) { + const roundedRange = Math.round(range); + range = almostEquals(range, roundedRange, range / 1e3) ? roundedRange : range; + const niceRange = Math.pow(10, Math.floor(log10(range))); + const fraction = range / niceRange; + const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10; + return niceFraction * niceRange; + } + function _factorize(value) { + const result = []; + const sqrt = Math.sqrt(value); + let i; + for (i = 1; i < sqrt; i++) { + if (value % i === 0) { + result.push(i); + result.push(value / i); + } + } + if (sqrt === (sqrt | 0)) { + result.push(sqrt); + } + result.sort((a, b) => a - b).pop(); + return result; + } + function isNonPrimitive(n) { + return typeof n === "symbol" || typeof n === "object" && n !== null && !(Symbol.toPrimitive in n || "toString" in n || "valueOf" in n); + } + function isNumber(n) { + return !isNonPrimitive(n) && !isNaN(parseFloat(n)) && isFinite(n); + } + function almostWhole(x, epsilon) { + const rounded = Math.round(x); + return rounded - epsilon <= x && rounded + epsilon >= x; + } + function _setMinAndMaxByKey(array, target, property) { + let i, ilen, value; + for (i = 0, ilen = array.length; i < ilen; i++) { + value = array[i][property]; + if (!isNaN(value)) { + target.min = Math.min(target.min, value); + target.max = Math.max(target.max, value); + } + } + } + function toRadians(degrees) { + return degrees * (PI / 180); + } + function toDegrees(radians) { + return radians * (180 / PI); + } + function _decimalPlaces(x) { + if (!isNumberFinite(x)) { + return; + } + let e = 1; + let p = 0; + while (Math.round(x * e) / e !== x) { + e *= 10; + p++; + } + return p; + } + function getAngleFromPoint(centrePoint, anglePoint) { + const distanceFromXCenter = anglePoint.x - centrePoint.x; + const distanceFromYCenter = anglePoint.y - centrePoint.y; + const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); + let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); + if (angle < -0.5 * PI) { + angle += TAU; + } + return { + angle, + distance: radialDistanceFromCenter + }; + } + function distanceBetweenPoints(pt1, pt2) { + return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); + } + function _angleDiff(a, b) { + return (a - b + PITAU) % TAU - PI; + } + function _normalizeAngle(a) { + return (a % TAU + TAU) % TAU; + } + function _angleBetween(angle, start, end, sameAngleIsFullCircle) { + const a = _normalizeAngle(angle); + const s = _normalizeAngle(start); + const e = _normalizeAngle(end); + const angleToStart = _normalizeAngle(s - a); + const angleToEnd = _normalizeAngle(e - a); + const startToAngle = _normalizeAngle(a - s); + const endToAngle = _normalizeAngle(a - e); + return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle; + } + function _limitValue(value, min, max) { + return Math.max(min, Math.min(max, value)); + } + function _int16Range(value) { + return _limitValue(value, -32768, 32767); + } + function _isBetween(value, start, end, epsilon = 1e-6) { + return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon; + } + function _lookup(table, value, cmp) { + cmp = cmp || ((index2) => table[index2] < value); + let hi = table.length - 1; + let lo = 0; + let mid; + while (hi - lo > 1) { + mid = lo + hi >> 1; + if (cmp(mid)) { + lo = mid; + } else { + hi = mid; + } + } + return { + lo, + hi + }; + } + const _lookupByKey = (table, key, value, last) => _lookup(table, value, last ? (index2) => { + const ti = table[index2][key]; + return ti < value || ti === value && table[index2 + 1][key] === value; + } : (index2) => table[index2][key] < value); + const _rlookupByKey = (table, key, value) => _lookup(table, value, (index2) => table[index2][key] >= value); + function _filterBetween(values, min, max) { + let start = 0; + let end = values.length; + while (start < end && values[start] < min) { + start++; + } + while (end > start && values[end - 1] > max) { + end--; + } + return start > 0 || end < values.length ? values.slice(start, end) : values; + } + const arrayEvents = [ + "push", + "pop", + "shift", + "splice", + "unshift" + ]; + function listenArrayEvents(array, listener) { + if (array._chartjs) { + array._chartjs.listeners.push(listener); + return; + } + Object.defineProperty(array, "_chartjs", { + configurable: true, + enumerable: false, + value: { + listeners: [ + listener + ] + } + }); + arrayEvents.forEach((key) => { + const method = "_onData" + _capitalize(key); + const base = array[key]; + Object.defineProperty(array, key, { + configurable: true, + enumerable: false, + value(...args) { + const res = base.apply(this, args); + array._chartjs.listeners.forEach((object) => { + if (typeof object[method] === "function") { + object[method](...args); + } + }); + return res; + } + }); + }); + } + function unlistenArrayEvents(array, listener) { + const stub = array._chartjs; + if (!stub) { + return; + } + const listeners = stub.listeners; + const index2 = listeners.indexOf(listener); + if (index2 !== -1) { + listeners.splice(index2, 1); + } + if (listeners.length > 0) { + return; + } + arrayEvents.forEach((key) => { + delete array[key]; + }); + delete array._chartjs; + } + function _arrayUnique(items) { + const set2 = new Set(items); + if (set2.size === items.length) { + return items; + } + return Array.from(set2); + } + const requestAnimFrame = (function() { + if (typeof window === "undefined") { + return function(callback2) { + return callback2(); + }; + } + return window.requestAnimationFrame; + })(); + function throttled(fn, thisArg) { + let argsToUse = []; + let ticking = false; + return function(...args) { + argsToUse = args; + if (!ticking) { + ticking = true; + requestAnimFrame.call(window, () => { + ticking = false; + fn.apply(thisArg, argsToUse); + }); + } + }; + } + function debounce(fn, delay) { + let timeout; + return function(...args) { + if (delay) { + clearTimeout(timeout); + timeout = setTimeout(fn, delay, args); + } else { + fn.apply(this, args); + } + return delay; + }; + } + const _toLeftRightCenter = (align) => align === "start" ? "left" : align === "end" ? "right" : "center"; + const _alignStartEnd = (align, start, end) => align === "start" ? start : align === "end" ? end : (start + end) / 2; + const _textX = (align, left, right, rtl) => { + const check = rtl ? "left" : "right"; + return align === check ? right : align === "center" ? (left + right) / 2 : left; + }; + function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) { + const pointCount = points.length; + let start = 0; + let count = pointCount; + if (meta._sorted) { + const { iScale, vScale, _parsed } = meta; + const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null; + const axis = iScale.axis; + const { min, max, minDefined, maxDefined } = iScale.getUserBounds(); + if (minDefined) { + start = Math.min( + // @ts-expect-error Need to type _parsed + _lookupByKey(_parsed, axis, min).lo, + // @ts-expect-error Need to fix types on _lookupByKey + animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo + ); + if (spanGaps) { + const distanceToDefinedLo = _parsed.slice(0, start + 1).reverse().findIndex((point) => !isNullOrUndef(point[vScale.axis])); + start -= Math.max(0, distanceToDefinedLo); + } + start = _limitValue(start, 0, pointCount - 1); + } + if (maxDefined) { + let end = Math.max( + // @ts-expect-error Need to type _parsed + _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, + // @ts-expect-error Need to fix types on _lookupByKey + animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1 + ); + if (spanGaps) { + const distanceToDefinedHi = _parsed.slice(end - 1).findIndex((point) => !isNullOrUndef(point[vScale.axis])); + end += Math.max(0, distanceToDefinedHi); + } + count = _limitValue(end, start, pointCount) - start; + } else { + count = pointCount - start; + } + } + return { + start, + count + }; + } + function _scaleRangesChanged(meta) { + const { xScale, yScale, _scaleRanges } = meta; + const newRanges = { + xmin: xScale.min, + xmax: xScale.max, + ymin: yScale.min, + ymax: yScale.max + }; + if (!_scaleRanges) { + meta._scaleRanges = newRanges; + return true; + } + const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max; + Object.assign(_scaleRanges, newRanges); + return changed; + } + const atEdge = (t) => t === 0 || t === 1; + const elasticIn = (t, s, p) => -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p)); + const elasticOut = (t, s, p) => Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1; + const effects = { + linear: (t) => t, + easeInQuad: (t) => t * t, + easeOutQuad: (t) => -t * (t - 2), + easeInOutQuad: (t) => (t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1), + easeInCubic: (t) => t * t * t, + easeOutCubic: (t) => (t -= 1) * t * t + 1, + easeInOutCubic: (t) => (t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2), + easeInQuart: (t) => t * t * t * t, + easeOutQuart: (t) => -((t -= 1) * t * t * t - 1), + easeInOutQuart: (t) => (t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.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 /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2), + easeInSine: (t) => -Math.cos(t * HALF_PI) + 1, + easeOutSine: (t) => Math.sin(t * HALF_PI), + easeInOutSine: (t) => -0.5 * (Math.cos(PI * t) - 1), + easeInExpo: (t) => t === 0 ? 0 : Math.pow(2, 10 * (t - 1)), + easeOutExpo: (t) => t === 1 ? 1 : -Math.pow(2, -10 * t) + 1, + easeInOutExpo: (t) => atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2), + easeInCirc: (t) => t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1), + easeOutCirc: (t) => Math.sqrt(1 - (t -= 1) * t), + easeInOutCirc: (t) => (t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1), + easeInElastic: (t) => atEdge(t) ? t : elasticIn(t, 0.075, 0.3), + easeOutElastic: (t) => atEdge(t) ? t : elasticOut(t, 0.075, 0.3), + easeInOutElastic(t) { + const s = 0.1125; + const p = 0.45; + return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p); + }, + 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; + if ((t /= 0.5) < 1) { + return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2); + }, + easeInBounce: (t) => 1 - effects.easeOutBounce(1 - t), + easeOutBounce(t) { + const m = 7.5625; + const d = 2.75; + if (t < 1 / d) { + return m * t * t; + } + if (t < 2 / d) { + return m * (t -= 1.5 / d) * t + 0.75; + } + if (t < 2.5 / d) { + return m * (t -= 2.25 / d) * t + 0.9375; + } + return m * (t -= 2.625 / d) * t + 0.984375; + }, + easeInOutBounce: (t) => t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5 + }; + function isPatternOrGradient(value) { + if (value && typeof value === "object") { + const type = value.toString(); + return type === "[object CanvasPattern]" || type === "[object CanvasGradient]"; + } + return false; + } + function color(value) { + return isPatternOrGradient(value) ? value : new Color(value); + } + function getHoverColor(value) { + return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString(); + } + const numbers = [ + "x", + "y", + "borderWidth", + "radius", + "tension" + ]; + const colors = [ + "color", + "borderColor", + "backgroundColor" + ]; + function applyAnimationsDefaults(defaults2) { + defaults2.set("animation", { + delay: void 0, + duration: 1e3, + easing: "easeOutQuart", + fn: void 0, + from: void 0, + loop: void 0, + to: void 0, + type: void 0 + }); + defaults2.describe("animation", { + _fallback: false, + _indexable: false, + _scriptable: (name) => name !== "onProgress" && name !== "onComplete" && name !== "fn" + }); + defaults2.set("animations", { + colors: { + type: "color", + properties: colors + }, + numbers: { + type: "number", + properties: numbers + } + }); + defaults2.describe("animations", { + _fallback: "animation" + }); + defaults2.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) => v | 0 + } + } + } + }); + } + function applyLayoutsDefaults(defaults2) { + defaults2.set("layout", { + autoPadding: true, + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }); + } + const intlCache = /* @__PURE__ */ new Map(); + function getNumberFormat(locale, options) { + options = options || {}; + const cacheKey = locale + JSON.stringify(options); + let formatter = intlCache.get(cacheKey); + if (!formatter) { + formatter = new Intl.NumberFormat(locale, options); + intlCache.set(cacheKey, formatter); + } + return formatter; + } + function formatNumber(num, locale, options) { + return getNumberFormat(locale, options).format(num); + } + const formatters = { + values(value) { + return isArray(value) ? value : "" + value; + }, + numeric(tickValue, index2, ticks) { + if (tickValue === 0) { + return "0"; + } + const locale = this.chart.options.locale; + let notation; + let delta = tickValue; + if (ticks.length > 1) { + const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value)); + if (maxTick < 1e-4 || maxTick > 1e15) { + notation = "scientific"; + } + delta = calculateDelta(tickValue, ticks); + } + const logDelta = log10(Math.abs(delta)); + const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0); + const options = { + notation, + minimumFractionDigits: numDecimal, + maximumFractionDigits: numDecimal + }; + Object.assign(options, this.options.ticks.format); + return formatNumber(tickValue, locale, options); + } + }; + function calculateDelta(tickValue, ticks) { + let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value; + if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) { + delta = tickValue - Math.floor(tickValue); + } + return delta; + } + var Ticks = { + formatters + }; + function applyScaleDefaults(defaults2) { + defaults2.set("scale", { + display: true, + offset: false, + reverse: false, + beginAtZero: false, + bounds: "ticks", + clip: true, + grace: 0, + grid: { + display: true, + lineWidth: 1, + drawOnChartArea: true, + drawTicks: true, + tickLength: 8, + tickWidth: (_ctx, options) => options.lineWidth, + tickColor: (_ctx, options) => options.color, + offset: false + }, + border: { + display: true, + dash: [], + dashOffset: 0, + width: 1 + }, + title: { + display: false, + text: "", + padding: { + top: 4, + bottom: 4 + } + }, + ticks: { + minRotation: 0, + maxRotation: 50, + mirror: false, + textStrokeWidth: 0, + textStrokeColor: "", + padding: 3, + display: true, + autoSkip: true, + autoSkipPadding: 3, + labelOffset: 0, + callback: Ticks.formatters.values, + minor: {}, + major: {}, + align: "center", + crossAlign: "near", + showLabelBackdrop: false, + backdropColor: "rgba(255, 255, 255, 0.75)", + backdropPadding: 2 + } + }); + defaults2.route("scale.ticks", "color", "", "color"); + defaults2.route("scale.grid", "color", "", "borderColor"); + defaults2.route("scale.border", "color", "", "borderColor"); + defaults2.route("scale.title", "color", "", "color"); + defaults2.describe("scale", { + _fallback: false, + _scriptable: (name) => !name.startsWith("before") && !name.startsWith("after") && name !== "callback" && name !== "parser", + _indexable: (name) => name !== "borderDash" && name !== "tickBorderDash" && name !== "dash" + }); + defaults2.describe("scales", { + _fallback: "scale" + }); + defaults2.describe("scale.ticks", { + _scriptable: (name) => name !== "backdropPadding" && name !== "callback", + _indexable: (name) => name !== "backdropPadding" + }); + } + const overrides = /* @__PURE__ */ Object.create(null); + const descriptors = /* @__PURE__ */ Object.create(null); + function getScope$1(node, key) { + if (!key) { + return node; + } + const keys = key.split("."); + for (let i = 0, n = keys.length; i < n; ++i) { + const k = keys[i]; + node = node[k] || (node[k] = /* @__PURE__ */ Object.create(null)); + } + return node; + } + function set(root, scope, values) { + if (typeof scope === "string") { + return merge(getScope$1(root, scope), values); + } + return merge(getScope$1(root, ""), scope); + } + class Defaults { + constructor(_descriptors2, _appliers) { + this.animation = void 0; + this.backgroundColor = "rgba(0,0,0,0.1)"; + this.borderColor = "rgba(0,0,0,0.1)"; + this.color = "#666"; + this.datasets = {}; + this.devicePixelRatio = (context) => context.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: true, + includeInvisible: false + }; + this.maintainAspectRatio = true; + this.onHover = null; + this.onClick = null; + this.parsing = true; + this.plugins = {}; + this.responsive = true; + this.scale = void 0; + this.scales = {}; + this.showLine = true; + this.drawActiveElementsOnTop = true; + this.describe(_descriptors2); + this.apply(_appliers); + } + 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); + const targetScopeObject = getScope$1(this, targetScope); + const privateName = "_" + name; + Object.defineProperties(scopeObject, { + [privateName]: { + value: scopeObject[name], + writable: true + }, + [name]: { + enumerable: true, + get() { + const local = this[privateName]; + const target = targetScopeObject[targetName]; + if (isObject(local)) { + return Object.assign({}, target, local); + } + return valueOrDefault(local, target); + }, + set(value) { + this[privateName] = value; + } + } + }); + } + apply(appliers) { + appliers.forEach((apply) => apply(this)); + } + } + var defaults = /* @__PURE__ */ new Defaults({ + _scriptable: (name) => !name.startsWith("on"), + _indexable: (name) => name !== "events", + hover: { + _fallback: "interaction" + }, + interaction: { + _scriptable: false, + _indexable: false + } + }, [ + applyAnimationsDefaults, + applyLayoutsDefaults, + applyScaleDefaults + ]); + function toFontString(font) { + if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) { + return null; + } + return (font.style ? font.style + " " : "") + (font.weight ? font.weight + " " : "") + font.size + "px " + font.family; + } + function _measureText(ctx, data, gc, longest, string) { + let textWidth = data[string]; + if (!textWidth) { + textWidth = data[string] = ctx.measureText(string).width; + gc.push(string); + } + if (textWidth > longest) { + longest = textWidth; + } + return longest; + } + function _alignPixel(chart, pixel, width) { + const devicePixelRatio = chart.currentDevicePixelRatio; + const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0; + return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth; + } + function clearCanvas(canvas, ctx) { + if (!ctx && !canvas) { + return; + } + ctx = ctx || canvas.getContext("2d"); + ctx.save(); + ctx.resetTransform(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + } + function drawPoint(ctx, options, x, y) { + drawPointLegend(ctx, options, x, y, null); + } + function drawPointLegend(ctx, options, x, y, w) { + let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW; + const style = options.pointStyle; + const rotation = options.rotation; + const radius = options.radius; + let rad = (rotation || 0) * RAD_PER_DEG; + if (style && typeof style === "object") { + type = style.toString(); + if (type === "[object HTMLImageElement]" || type === "[object HTMLCanvasElement]") { + ctx.save(); + ctx.translate(x, y); + ctx.rotate(rad); + ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); + ctx.restore(); + return; + } + } + if (isNaN(radius) || radius <= 0) { + return; + } + ctx.beginPath(); + switch (style) { + // Default includes circle + default: + if (w) { + ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU); + } else { + ctx.arc(x, y, radius, 0, TAU); + } + ctx.closePath(); + break; + case "triangle": + width = w ? w / 2 : radius; + ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case "rectRounded": + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size); + yOffset = Math.sin(rad + QUARTER_PI) * size; + yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size); + ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case "rect": + if (!rotation) { + size = Math.SQRT1_2 * radius; + width = w ? w / 2 : size; + ctx.rect(x - width, y - size, 2 * width, 2 * size); + break; + } + rad += QUARTER_PI; + /* falls through */ + case "rectRot": + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + ctx.closePath(); + break; + case "crossRot": + rad += QUARTER_PI; + /* falls through */ + case "cross": + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + break; + case "star": + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + rad += QUARTER_PI; + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + break; + case "line": + xOffset = w ? w / 2 : Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + break; + case "dash": + ctx.moveTo(x, y); + ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius); + break; + case false: + ctx.closePath(); + break; + } + ctx.fill(); + if (options.borderWidth > 0) { + ctx.stroke(); + } + } + function _isPointInArea(point, area, margin) { + margin = margin || 0.5; + return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin; + } + function clipArea(ctx, area) { + ctx.save(); + ctx.beginPath(); + ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); + ctx.clip(); + } + function unclipArea(ctx) { + ctx.restore(); + } + function _steppedLineTo(ctx, previous, target, flip, mode) { + if (!previous) { + return ctx.lineTo(target.x, target.y); + } + if (mode === "middle") { + const midpoint = (previous.x + target.x) / 2; + ctx.lineTo(midpoint, previous.y); + ctx.lineTo(midpoint, target.y); + } else if (mode === "after" !== !!flip) { + ctx.lineTo(previous.x, target.y); + } else { + ctx.lineTo(target.x, previous.y); + } + ctx.lineTo(target.x, target.y); + } + function _bezierCurveTo(ctx, previous, target, flip) { + if (!previous) { + return ctx.lineTo(target.x, target.y); + } + ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y); + } + function setRenderOpts(ctx, opts) { + if (opts.translation) { + ctx.translate(opts.translation[0], opts.translation[1]); + } + if (!isNullOrUndef(opts.rotation)) { + ctx.rotate(opts.rotation); + } + if (opts.color) { + ctx.fillStyle = opts.color; + } + if (opts.textAlign) { + ctx.textAlign = opts.textAlign; + } + if (opts.textBaseline) { + ctx.textBaseline = opts.textBaseline; + } + } + function decorateText(ctx, x, y, line, opts) { + if (opts.strikethrough || opts.underline) { + const metrics = ctx.measureText(line); + const left = x - metrics.actualBoundingBoxLeft; + const right = x + metrics.actualBoundingBoxRight; + const top = y - metrics.actualBoundingBoxAscent; + const bottom = y + metrics.actualBoundingBoxDescent; + const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom; + ctx.strokeStyle = ctx.fillStyle; + ctx.beginPath(); + ctx.lineWidth = opts.decorationWidth || 2; + ctx.moveTo(left, yDecoration); + ctx.lineTo(right, yDecoration); + ctx.stroke(); + } + } + function drawBackdrop(ctx, opts) { + const oldColor = ctx.fillStyle; + ctx.fillStyle = opts.color; + ctx.fillRect(opts.left, opts.top, opts.width, opts.height); + ctx.fillStyle = oldColor; + } + function renderText(ctx, text, x, y, font, opts = {}) { + const lines = isArray(text) ? text : [ + text + ]; + const stroke = opts.strokeWidth > 0 && opts.strokeColor !== ""; + let i, line; + ctx.save(); + ctx.font = font.string; + setRenderOpts(ctx, opts); + for (i = 0; i < lines.length; ++i) { + line = lines[i]; + if (opts.backdrop) { + drawBackdrop(ctx, opts.backdrop); + } + if (stroke) { + if (opts.strokeColor) { + ctx.strokeStyle = opts.strokeColor; + } + if (!isNullOrUndef(opts.strokeWidth)) { + ctx.lineWidth = opts.strokeWidth; + } + ctx.strokeText(line, x, y, opts.maxWidth); + } + ctx.fillText(line, x, y, opts.maxWidth); + decorateText(ctx, x, y, line, opts); + y += Number(font.lineHeight); + } + ctx.restore(); + } + function addRoundedRectPath(ctx, rect) { + const { x, y, w, h, radius } = rect; + ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true); + ctx.lineTo(x, y + h - radius.bottomLeft); + ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true); + ctx.lineTo(x + w - radius.bottomRight, y + h); + ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true); + ctx.lineTo(x + w, y + radius.topRight); + ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true); + ctx.lineTo(x + radius.topLeft, y); + } + const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/; + const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/; + function toLineHeight(value, size) { + const matches = ("" + value).match(LINE_HEIGHT); + if (!matches || matches[1] === "normal") { + return size * 1.2; + } + value = +matches[2]; + switch (matches[3]) { + case "px": + return value; + case "%": + value /= 100; + break; + } + return size * value; + } + const numberOrZero = (v) => +v || 0; + function _readValueToProps(value, props) { + const ret = {}; + const objProps = isObject(props); + const keys = objProps ? Object.keys(props) : props; + const read = isObject(value) ? objProps ? (prop) => valueOrDefault(value[prop], value[props[prop]]) : (prop) => value[prop] : () => value; + for (const prop of keys) { + ret[prop] = numberOrZero(read(prop)); + } + 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); + obj.width = obj.left + obj.right; + obj.height = obj.top + obj.bottom; + return obj; + } + function toFont(options, fallback) { + options = options || {}; + fallback = fallback || defaults.font; + let size = valueOrDefault(options.size, fallback.size); + if (typeof size === "string") { + size = parseInt(size, 10); + } + let style = valueOrDefault(options.style, fallback.style); + if (style && !("" + style).match(FONT_STYLE)) { + console.warn('Invalid font style specified: "' + style + '"'); + style = void 0; + } + const font = { + family: valueOrDefault(options.family, fallback.family), + lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size), + size, + style, + weight: valueOrDefault(options.weight, fallback.weight), + string: "" + }; + font.string = toFontString(font); + return font; + } + function resolve(inputs, context, index2, info) { + let i, ilen, value; + for (i = 0, ilen = inputs.length; i < ilen; ++i) { + value = inputs[i]; + if (value === void 0) { + continue; + } + if (value !== void 0) { + return value; + } + } + } + function _addGrace(minmax, grace, beginAtZero) { + const { min, max } = minmax; + const change = toDimension(grace, (max - min) / 2); + const keepZero = (value, add) => beginAtZero && value === 0 ? 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 _createResolver(scopes, prefixes = [ + "" + ], rootScopes, fallback, getTarget = () => scopes[0]) { + const finalRootScopes = rootScopes || scopes; + if (typeof fallback === "undefined") { + fallback = _resolve("_fallback", scopes); + } + const cache = { + [Symbol.toStringTag]: "Object", + _cacheable: true, + _scopes: scopes, + _rootScopes: finalRootScopes, + _fallback: fallback, + _getTarget: getTarget, + override: (scope) => _createResolver([ + scope, + ...scopes + ], prefixes, finalRootScopes, fallback) + }; + return new Proxy(cache, { + /** + * A trap for the delete operator. + */ + deleteProperty(target, prop) { + delete target[prop]; + delete target._keys; + delete scopes[0][prop]; + return true; + }, + /** + * A trap for getting property values. + */ + get(target, prop) { + return _cached(target, prop, () => _resolveWithPrefixes(prop, prefixes, scopes, target)); + }, + /** + * A trap for Object.getOwnPropertyDescriptor. + * Also used by Object.hasOwnProperty. + */ + getOwnPropertyDescriptor(target, prop) { + return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop); + }, + /** + * A trap for Object.getPrototypeOf. + */ + getPrototypeOf() { + return Reflect.getPrototypeOf(scopes[0]); + }, + /** + * A trap for the in operator. + */ + has(target, prop) { + return getKeysFromAllScopes(target).includes(prop); + }, + /** + * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols. + */ + ownKeys(target) { + return getKeysFromAllScopes(target); + }, + /** + * A trap for setting property values. + */ + set(target, prop, value) { + const storage = target._storage || (target._storage = getTarget()); + target[prop] = storage[prop] = value; + delete target._keys; + return true; + } + }); + } + function _attachContext(proxy, context, subProxy, descriptorDefaults) { + const cache = { + _cacheable: false, + _proxy: proxy, + _context: context, + _subProxy: subProxy, + _stack: /* @__PURE__ */ 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, { + /** + * A trap for the delete operator. + */ + deleteProperty(target, prop) { + delete target[prop]; + delete proxy[prop]; + return true; + }, + /** + * A trap for getting property values. + */ + get(target, prop, receiver) { + return _cached(target, prop, () => _resolveWithContext(target, prop, receiver)); + }, + /** + * A trap for Object.getOwnPropertyDescriptor. + * Also used by Object.hasOwnProperty. + */ + getOwnPropertyDescriptor(target, prop) { + return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? { + enumerable: true, + configurable: true + } : void 0 : Reflect.getOwnPropertyDescriptor(proxy, prop); + }, + /** + * A trap for Object.getPrototypeOf. + */ + getPrototypeOf() { + return Reflect.getPrototypeOf(proxy); + }, + /** + * A trap for the in operator. + */ + has(target, prop) { + return Reflect.has(proxy, prop); + }, + /** + * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols. + */ + ownKeys() { + return Reflect.ownKeys(proxy); + }, + /** + * A trap for setting property values. + */ + set(target, prop, value) { + proxy[prop] = value; + delete target[prop]; + return true; + } + }); + } + function _descriptors(proxy, defaults2 = { + scriptable: true, + indexable: true + }) { + const { _scriptable = defaults2.scriptable, _indexable = defaults2.indexable, _allKeys = defaults2.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; + const needsSubResolver = (prop, value) => isObject(value) && prop !== "adapters" && (Object.getPrototypeOf(value) === null || value.constructor === Object); + function _cached(target, prop, resolve2) { + if (Object.prototype.hasOwnProperty.call(target, prop) || prop === "constructor") { + return target[prop]; + } + const value = resolve2(); + target[prop] = value; + return value; + } + function _resolveWithContext(target, prop, receiver) { + const { _proxy, _context, _subProxy, _descriptors: descriptors2 } = target; + let value = _proxy[prop]; + if (isFunction(value) && descriptors2.isScriptable(prop)) { + value = _resolveScriptable(prop, value, target, receiver); + } + if (isArray(value) && value.length) { + value = _resolveArray(prop, value, target, descriptors2.isIndexable); + } + if (needsSubResolver(prop, value)) { + value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors2); + } + return value; + } + function _resolveScriptable(prop, getValue, target, receiver) { + const { _proxy, _context, _subProxy, _stack } = target; + if (_stack.has(prop)) { + throw new Error("Recursion detected: " + Array.from(_stack).join("->") + "->" + prop); + } + _stack.add(prop); + let value = getValue(_context, _subProxy || receiver); + _stack.delete(prop); + if (needsSubResolver(prop, value)) { + value = createSubResolver(_proxy._scopes, _proxy, prop, value); + } + return value; + } + function _resolveArray(prop, value, target, isIndexable) { + const { _proxy, _context, _subProxy, _descriptors: descriptors2 } = target; + if (typeof _context.index !== "undefined" && isIndexable(prop)) { + return value[_context.index % value.length]; + } else if (isObject(value[0])) { + const arr = value; + const 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], descriptors2)); + } + } + return value; + } + function resolveFallback(fallback, prop, value) { + return isFunction(fallback) ? fallback(prop, value) : fallback; + } + const getScope = (key, parent) => key === true ? parent : typeof key === "string" ? resolveObjectKey(parent, key) : void 0; + function addScopes(set2, parentScopes, key, parentFallback, value) { + for (const parent of parentScopes) { + const scope = getScope(key, parent); + if (scope) { + set2.add(scope); + const fallback = resolveFallback(scope._fallback, key, value); + if (typeof fallback !== "undefined" && fallback !== key && fallback !== parentFallback) { + return fallback; + } + } else if (scope === false && typeof parentFallback !== "undefined" && key !== parentFallback) { + return null; + } + } + return false; + } + function createSubResolver(parentScopes, resolver, prop, value) { + const rootScopes = resolver._rootScopes; + const fallback = resolveFallback(resolver._fallback, prop, value); + const allScopes = [ + ...parentScopes, + ...rootScopes + ]; + const set2 = /* @__PURE__ */ new Set(); + set2.add(value); + let key = addScopesFromKey(set2, allScopes, prop, fallback || prop, value); + if (key === null) { + return false; + } + if (typeof fallback !== "undefined" && fallback !== prop) { + key = addScopesFromKey(set2, allScopes, fallback, key, value); + if (key === null) { + return false; + } + } + return _createResolver(Array.from(set2), [ + "" + ], rootScopes, fallback, () => subGetTarget(resolver, prop, value)); + } + function addScopesFromKey(set2, allScopes, key, fallback, item) { + while (key) { + key = addScopes(set2, allScopes, key, fallback, item); + } + return key; + } + function subGetTarget(resolver, prop, value) { + const parent = resolver._getTarget(); + if (!(prop in parent)) { + parent[prop] = {}; + } + const target = parent[prop]; + if (isArray(target) && isObject(value)) { + return value; + } + return target || {}; + } + function _resolveWithPrefixes(prop, prefixes, scopes, proxy) { + let value; + for (const prefix of prefixes) { + value = _resolve(readKey(prefix, prop), scopes); + if (typeof value !== "undefined") { + 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 (typeof value !== "undefined") { + return value; + } + } + } + function getKeysFromAllScopes(target) { + let keys = target._keys; + if (!keys) { + keys = target._keys = resolveKeysFromAllScopes(target._scopes); + } + return keys; + } + function resolveKeysFromAllScopes(scopes) { + const set2 = /* @__PURE__ */ new Set(); + for (const scope of scopes) { + for (const key of Object.keys(scope).filter((k) => !k.startsWith("_"))) { + set2.add(key); + } + } + return Array.from(set2); + } + const EPSILON = Number.EPSILON || 1e-14; + const getPoint = (points, i) => i < points.length && !points[i].skip && points[i]; + const getValueAxis = (indexAxis) => indexAxis === "x" ? "y" : "x"; + function splineCurve(firstPoint, middlePoint, afterPoint, t) { + const previous = firstPoint.skip ? middlePoint : firstPoint; + const current = middlePoint; + const next = afterPoint.skip ? middlePoint : afterPoint; + const d01 = distanceBetweenPoints(current, previous); + const d12 = distanceBetweenPoints(next, current); + let s01 = d01 / (d01 + d12); + let s12 = d12 / (d01 + d12); + s01 = isNaN(s01) ? 0 : s01; + s12 = isNaN(s12) ? 0 : s12; + const fa = t * s01; + const 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; + let pointAfter = getPoint(points, 0); + for (let i = 0; i < pointsLen - 1; ++i) { + pointCurrent = pointAfter; + pointAfter = getPoint(points, i + 1); + if (!pointCurrent || !pointAfter) { + continue; + } + if (almostEquals(deltaK[i], 0, EPSILON)) { + mK[i] = mK[i + 1] = 0; + continue; + } + alphaK = mK[i] / deltaK[i]; + betaK = mK[i + 1] / deltaK[i]; + squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); + if (squaredMagnitude <= 9) { + continue; + } + tauK = 3 / Math.sqrt(squaredMagnitude); + mK[i] = alphaK * tauK * deltaK[i]; + mK[i + 1] = betaK * tauK * deltaK[i]; + } + } + function monotoneCompute(points, mK, indexAxis = "x") { + const valueAxis = getValueAxis(indexAxis); + const pointsLen = points.length; + let delta, pointBefore, pointCurrent; + let pointAfter = getPoint(points, 0); + for (let i = 0; i < pointsLen; ++i) { + pointBefore = pointCurrent; + pointCurrent = pointAfter; + pointAfter = getPoint(points, i + 1); + if (!pointCurrent) { + continue; + } + const iPixel = pointCurrent[indexAxis]; + const vPixel = pointCurrent[valueAxis]; + if (pointBefore) { + delta = (iPixel - pointBefore[indexAxis]) / 3; + pointCurrent[`cp1${indexAxis}`] = iPixel - delta; + pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i]; + } + if (pointAfter) { + delta = (pointAfter[indexAxis] - iPixel) / 3; + pointCurrent[`cp2${indexAxis}`] = iPixel + delta; + pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i]; + } + } + } + function splineCurveMonotone(points, indexAxis = "x") { + const valueAxis = getValueAxis(indexAxis); + const pointsLen = points.length; + const deltaK = Array(pointsLen).fill(0); + const mK = Array(pointsLen); + let i, pointBefore, pointCurrent; + let pointAfter = getPoint(points, 0); + for (i = 0; i < pointsLen; ++i) { + pointBefore = pointCurrent; + pointCurrent = pointAfter; + pointAfter = getPoint(points, i + 1); + if (!pointCurrent) { + continue; + } + if (pointAfter) { + const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis]; + deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0; + } + mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2; + } + monotoneAdjust(points, deltaK, mK); + monotoneCompute(points, mK, indexAxis); + } + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + function capBezierPoints(points, area) { + let i, ilen, point, inArea, inAreaPrev; + let inAreaNext = _isPointInArea(points[0], area); + for (i = 0, ilen = points.length; i < ilen; ++i) { + inAreaPrev = inArea; + inArea = inAreaNext; + inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area); + if (!inArea) { + continue; + } + point = points[i]; + if (inAreaPrev) { + point.cp1x = capControlPoint(point.cp1x, area.left, area.right); + point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom); + } + if (inAreaNext) { + point.cp2x = capControlPoint(point.cp2x, area.left, area.right); + point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom); + } + } + } + function _updateBezierControlPoints(points, options, area, loop, indexAxis) { + let i, ilen, point, controlPoints; + if (options.spanGaps) { + points = points.filter((pt) => !pt.skip); + } + if (options.cubicInterpolationMode === "monotone") { + splineCurveMonotone(points, indexAxis); + } else { + let prev = loop ? points[points.length - 1] : points[0]; + for (i = 0, ilen = points.length; i < ilen; ++i) { + point = points[i]; + controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension); + point.cp1x = controlPoints.previous.x; + point.cp1y = controlPoints.previous.y; + point.cp2x = controlPoints.next.x; + point.cp2y = controlPoints.next.y; + prev = point; + } + } + if (options.capBezierPoints) { + capBezierPoints(points, area); + } + } + function _isDomSupported() { + return typeof window !== "undefined" && typeof document !== "undefined"; + } + function _getParentNode(domNode) { + let parent = domNode.parentNode; + if (parent && parent.toString() === "[object ShadowRoot]") { + parent = parent.host; + } + return parent; + } + function parseMaxStyle(styleValue, node, parentProperty) { + let valueInPixels; + if (typeof styleValue === "string") { + valueInPixels = parseInt(styleValue, 10); + if (styleValue.indexOf("%") !== -1) { + valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; + } + } else { + valueInPixels = styleValue; + } + return valueInPixels; + } + const getComputedStyle = (element) => element.ownerDocument.defaultView.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; + } + result.width = result.left + result.right; + result.height = result.top + result.bottom; + return result; + } + const useOffsetPos = (x, y, target) => (x > 0 || y > 0) && (!target || !target.shadowRoot); + function getCanvasPosition(e, canvas) { + const touches = e.touches; + const source = touches && touches.length ? touches[0] : e; + const { offsetX, offsetY } = source; + let box = false; + let x, y; + if (useOffsetPos(offsetX, offsetY, e.target)) { + x = offsetX; + y = offsetY; + } else { + const rect = canvas.getBoundingClientRect(); + x = source.clientX - rect.left; + y = source.clientY - rect.top; + box = true; + } + return { + x, + y, + box + }; + } + function getRelativePosition(event, chart) { + if ("native" in event) { + return event; + } + const { canvas, currentDevicePixelRatio } = chart; + const style = getComputedStyle(canvas); + const borderBox = style.boxSizing === "border-box"; + const paddings = getPositionedStyle(style, "padding"); + const borders = getPositionedStyle(style, "border", "width"); + const { x, y, box } = getCanvasPosition(event, canvas); + const xOffset = paddings.left + (box && borders.left); + const yOffset = paddings.top + (box && borders.top); + let { width, height } = chart; + if (borderBox) { + width -= paddings.width + borders.width; + height -= paddings.height + borders.height; + } + return { + x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio), + y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio) + }; + } + function getContainerSize(canvas, width, height) { + let maxWidth, maxHeight; + if (width === void 0 || height === void 0) { + const container = canvas && _getParentNode(canvas); + if (!container) { + width = canvas.clientWidth; + height = canvas.clientHeight; + } else { + const rect = container.getBoundingClientRect(); + const containerStyle = getComputedStyle(container); + const containerBorder = getPositionedStyle(containerStyle, "border", "width"); + const 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"); + } + } + return { + width, + height, + maxWidth: maxWidth || INFINITY, + maxHeight: maxHeight || INFINITY + }; + } + const round1 = (v) => Math.round(v * 10) / 10; + function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) { + const style = getComputedStyle(canvas); + const margins = getPositionedStyle(style, "margin"); + const maxWidth = parseMaxStyle(style.maxWidth, canvas, "clientWidth") || INFINITY; + const maxHeight = parseMaxStyle(style.maxHeight, canvas, "clientHeight") || INFINITY; + const containerSize = getContainerSize(canvas, bbWidth, bbHeight); + let { width, height } = containerSize; + if (style.boxSizing === "content-box") { + const borders = getPositionedStyle(style, "border", "width"); + const paddings = getPositionedStyle(style, "padding"); + width -= paddings.width + borders.width; + height -= paddings.height + borders.height; + } + width = Math.max(0, width - margins.width); + height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height); + width = round1(Math.min(width, maxWidth, containerSize.maxWidth)); + height = round1(Math.min(height, maxHeight, containerSize.maxHeight)); + if (width && !height) { + height = round1(width / 2); + } + const maintainHeight = bbWidth !== void 0 || bbHeight !== void 0; + if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) { + height = containerSize.height; + width = round1(Math.floor(height * aspectRatio)); + } + return { + width, + height + }; + } + function retinaScale(chart, forceRatio, forceStyle) { + const pixelRatio = forceRatio || 1; + const deviceHeight = round1(chart.height * pixelRatio); + const deviceWidth = round1(chart.width * pixelRatio); + chart.height = round1(chart.height); + chart.width = round1(chart.width); + const canvas = chart.canvas; + if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) { + canvas.style.height = `${chart.height}px`; + canvas.style.width = `${chart.width}px`; + } + if (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); + return true; + } + return false; + } + const supportsEventListenerOptions = (function() { + let passiveSupported = false; + try { + const options = { + get passive() { + passiveSupported = true; + return false; + } + }; + if (_isDomSupported()) { + window.addEventListener("test", null, options); + window.removeEventListener("test", null, options); + } + } catch (e) { + } + return passiveSupported; + })(); + function readUsedSize(element, property) { + const value = getStyle(element, property); + const matches = value && value.match(/^(\d+)(\.\d+)?px$/); + return matches ? +matches[1] : void 0; + } + 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: mode === "middle" ? t < 0.5 ? p1.y : p2.y : mode === "after" ? 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 + }; + const cp2 = { + x: p2.cp1x, + y: p2.cp1y + }; + const a = _pointInLine(p1, cp1, t); + const b = _pointInLine(cp1, cp2, t); + const c = _pointInLine(cp2, p2, t); + const d = _pointInLine(a, b, t); + const e = _pointInLine(b, c, t); + return _pointInLine(d, e, t); + } + const getRightToLeftAdapter = function(rectX, width) { + return { + x(x) { + return rectX + rectX + width - x; + }, + setWidth(w) { + width = w; + }, + textAlign(align) { + if (align === "center") { + return align; + } + return align === "right" ? "left" : "right"; + }, + xPlus(x, value) { + return x - value; + }, + leftForLtr(x, itemWidth) { + return x - itemWidth; + } + }; + }; + const getLeftToRightAdapter = function() { + return { + x(x) { + return x; + }, + setWidth(w) { + }, + textAlign(align) { + return align; + }, + xPlus(x, value) { + return x + value; + }, + leftForLtr(x, _itemWidth) { + return x; + } + }; + }; + function getRtlAdapter(rtl, rectX, width) { + return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter(); + } + function overrideTextDirection(ctx, direction) { + let style, original; + if (direction === "ltr" || direction === "rtl") { + style = ctx.canvas.style; + original = [ + style.getPropertyValue("direction"), + style.getPropertyPriority("direction") + ]; + style.setProperty("direction", direction, "important"); + ctx.prevTextDirection = original; + } + } + function restoreTextDirection(ctx, original) { + if (original !== void 0) { + delete ctx.prevTextDirection; + ctx.canvas.style.setProperty("direction", original[0], original[1]); + } + } + function propertyFn(property) { + if (property === "angle") { + return { + between: _angleBetween, + compare: _angleDiff, + normalize: _normalizeAngle + }; + } + return { + between: _isBetween, + compare: (a, b) => a - b, + normalize: (x) => x + }; + } + function normalizeSegment({ start, end, count, loop, style }) { + return { + start: start % count, + end: end % count, + loop: loop && (end - start + 1) % count === 0, + style + }; + } + function getSegment(segment, points, bounds) { + const { property, start: startBound, end: endBound } = bounds; + const { between, normalize } = propertyFn(property); + const count = points.length; + let { start, end, loop } = segment; + let i, ilen; + if (loop) { + start += count; + end += count; + for (i = 0, ilen = count; i < ilen; ++i) { + if (!between(normalize(points[start % count][property]), startBound, endBound)) { + break; + } + start--; + end--; + } + start %= count; + end %= count; + } + if (end < start) { + end += count; + } + return { + start, + end, + loop, + style: segment.style + }; + } + function _boundSegment(segment, points, bounds) { + if (!bounds) { + return [ + segment + ]; + } + const { property, start: startBound, end: endBound } = bounds; + const count = points.length; + const { compare, between, normalize } = propertyFn(property); + const { start, end, loop, style } = getSegment(segment, points, bounds); + const result = []; + let inside = false; + let subStart = null; + let value, point, prevValue; + const startIsBefore = () => between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0; + const endIsBefore = () => compare(endBound, value) === 0 || between(endBound, prevValue, value); + const shouldStart = () => inside || startIsBefore(); + const shouldStop = () => !inside || endIsBefore(); + for (let i = start, prev = start; i <= end; ++i) { + point = points[i % count]; + if (point.skip) { + continue; + } + value = normalize(point[property]); + if (value === prevValue) { + continue; + } + inside = between(value, startBound, endBound); + if (subStart === null && shouldStart()) { + subStart = compare(value, startBound) === 0 ? i : prev; + } + if (subStart !== null && shouldStop()) { + result.push(normalizeSegment({ + start: subStart, + end: i, + loop, + count, + style + })); + subStart = null; + } + prev = i; + prevValue = value; + } + if (subStart !== null) { + result.push(normalizeSegment({ + start: subStart, + end, + loop, + count, + style + })); + } + return result; + } + function _boundSegments(line, bounds) { + const result = []; + const segments = line.segments; + for (let i = 0; i < segments.length; i++) { + const sub = _boundSegment(segments[i], line.points, bounds); + if (sub.length) { + result.push(...sub); + } + } + return result; + } + function findStartAndEnd(points, count, loop, spanGaps) { + let start = 0; + let end = count - 1; + if (loop && !spanGaps) { + while (start < count && !points[start].skip) { + start++; + } + } + while (start < count && points[start].skip) { + start++; + } + start %= count; + if (loop) { + end += start; + } + while (end > start && points[end % count].skip) { + end--; + } + end %= count; + return { + start, + end + }; + } + function solidSegments(points, start, max, loop) { + const count = points.length; + const result = []; + let last = start; + let prev = points[start]; + let end; + for (end = start + 1; end <= max; ++end) { + const cur = points[end % count]; + if (cur.skip || cur.stop) { + if (!prev.skip) { + loop = false; + result.push({ + start: start % count, + end: (end - 1) % count, + loop + }); + start = last = cur.stop ? end : null; + } + } else { + last = end; + if (prev.skip) { + start = end; + } + } + prev = cur; + } + if (last !== null) { + result.push({ + start: start % count, + end: last % count, + loop + }); + } + return result; + } + function _computeSegments(line, segmentOptions) { + const points = line.points; + const spanGaps = line.options.spanGaps; + const count = points.length; + if (!count) { + return []; + } + const loop = !!line._loop; + const { start, end } = findStartAndEnd(points, count, loop, spanGaps); + if (spanGaps === true) { + return splitByStyles(line, [ + { + start, + end, + loop + } + ], points, segmentOptions); + } + const max = end < start ? end + count : end; + const completeLoop = !!line._fullLoop && start === 0 && end === count - 1; + return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions); + } + function splitByStyles(line, segments, points, segmentOptions) { + if (!segmentOptions || !segmentOptions.setContext || !points) { + return segments; + } + return doSplitByStyles(line, segments, points, segmentOptions); + } + function doSplitByStyles(line, segments, points, segmentOptions) { + const chartContext = line._chart.getContext(); + const baseStyle = readStyle(line.options); + const { _datasetIndex: datasetIndex, options: { spanGaps } } = line; + const count = points.length; + const result = []; + let prevStyle = baseStyle; + let start = segments[0].start; + let i = start; + function addStyle(s, e, l, st) { + const dir = spanGaps ? -1 : 1; + if (s === e) { + return; + } + s += count; + while (points[s % count].skip) { + s -= dir; + } + while (points[e % count].skip) { + e += dir; + } + if (s % count !== e % count) { + result.push({ + start: s % count, + end: e % count, + loop: l, + style: st + }); + prevStyle = st; + start = e % count; + } + } + for (const segment of segments) { + start = spanGaps ? start : segment.start; + let prev = points[start % count]; + let style; + for (i = start + 1; i <= segment.end; i++) { + const pt = points[i % count]; + style = readStyle(segmentOptions.setContext(createContext(chartContext, { + type: "segment", + p0: prev, + p1: pt, + p0DataIndex: (i - 1) % count, + p1DataIndex: i % count, + datasetIndex + }))); + if (styleChanged(style, prevStyle)) { + addStyle(start, i - 1, segment.loop, prevStyle); + } + prev = pt; + prevStyle = style; + } + if (start < i - 1) { + addStyle(start, i - 1, segment.loop, prevStyle); + } + } + return result; + } + function readStyle(options) { + return { + backgroundColor: options.backgroundColor, + borderCapStyle: options.borderCapStyle, + borderDash: options.borderDash, + borderDashOffset: options.borderDashOffset, + borderJoinStyle: options.borderJoinStyle, + borderWidth: options.borderWidth, + borderColor: options.borderColor + }; + } + function styleChanged(style, prevStyle) { + if (!prevStyle) { + return false; + } + const cache = []; + const replacer = function(key, value) { + if (!isPatternOrGradient(value)) { + return value; + } + if (!cache.includes(value)) { + cache.push(value); + } + return cache.indexOf(value); + }; + return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer); + } + function getSizeForArea(scale, chartArea, field) { + return scale.options.clip ? scale[field] : chartArea[field]; + } + function getDatasetArea(meta, chartArea) { + const { xScale, yScale } = meta; + if (xScale && yScale) { + return { + left: getSizeForArea(xScale, chartArea, "left"), + right: getSizeForArea(xScale, chartArea, "right"), + top: getSizeForArea(yScale, chartArea, "top"), + bottom: getSizeForArea(yScale, chartArea, "bottom") + }; + } + return chartArea; + } + function getDatasetClipArea(chart, meta) { + const clip = meta._clip; + if (clip.disabled) { + return false; + } + const area = getDatasetArea(meta, chart.chartArea); + return { + left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left), + right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right), + top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top), + bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom) + }; + } + /*! + * Chart.js v4.5.1 + * https://www.chartjs.org + * (c) 2025 Chart.js Contributors + * Released under the MIT License + */ + class Animator { + constructor() { + this._request = null; + this._charts = /* @__PURE__ */ new Map(); + this._running = false; + this._lastDate = void 0; + } + _notify(chart, anims, date, type) { + const callbacks = anims.listeners[type]; + const numSteps = anims.duration; + callbacks.forEach((fn) => fn({ + chart, + initial: anims.initial, + numSteps, + currentStep: Math.min(date - anims.start, numSteps) + })); + } + _refresh() { + if (this._request) { + return; + } + this._running = true; + this._request = requestAnimFrame.call(window, () => { + this._update(); + this._request = null; + if (this._running) { + this._refresh(); + } + }); + } + _update(date = Date.now()) { + let remaining = 0; + this._charts.forEach((anims, chart) => { + if (!anims.running || !anims.items.length) { + return; + } + const items = anims.items; + let i = items.length - 1; + let draw2 = false; + let item; + for (; i >= 0; --i) { + item = items[i]; + if (item._active) { + if (item._total > anims.duration) { + anims.duration = item._total; + } + item.tick(date); + draw2 = true; + } else { + items[i] = items[items.length - 1]; + items.pop(); + } + } + if (draw2) { + chart.draw(); + this._notify(chart, anims, date, "progress"); + } + if (!items.length) { + anims.running = false; + this._notify(chart, anims, date, "complete"); + anims.initial = false; + } + remaining += items.length; + }); + this._lastDate = date; + if (remaining === 0) { + this._running = false; + } + } + _getAnims(chart) { + const charts = this._charts; + let anims = charts.get(chart); + if (!anims) { + anims = { + running: false, + initial: true, + items: [], + listeners: { + complete: [], + progress: [] + } + }; + charts.set(chart, anims); + } + return anims; + } + listen(chart, event, cb) { + this._getAnims(chart).listeners[event].push(cb); + } + add(chart, items) { + if (!items || !items.length) { + return; + } + this._getAnims(chart).items.push(...items); + } + has(chart) { + return this._getAnims(chart).items.length > 0; + } + start(chart) { + const anims = this._charts.get(chart); + if (!anims) { + return; + } + anims.running = true; + 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 false; + } + const anims = this._charts.get(chart); + if (!anims || !anims.running || !anims.items.length) { + return false; + } + return true; + } + 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); + } + } + var animator = /* @__PURE__ */ new Animator(); + const transparent = "transparent"; + const interpolators = { + boolean(from2, to2, factor) { + return factor > 0.5 ? to2 : from2; + }, + color(from2, to2, factor) { + const c0 = color(from2 || transparent); + const c1 = c0.valid && color(to2 || transparent); + return c1 && c1.valid ? c1.mix(c0, factor).hexString() : to2; + }, + number(from2, to2, factor) { + return from2 + (to2 - from2) * factor; + } + }; + class Animation { + constructor(cfg, target, prop, to2) { + const currentValue = target[prop]; + to2 = resolve([ + cfg.to, + to2, + currentValue, + cfg.from + ]); + const from2 = resolve([ + cfg.from, + currentValue, + to2 + ]); + this._active = true; + this._fn = cfg.fn || interpolators[cfg.type || typeof from2]; + 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 = from2; + this._to = to2; + this._promises = void 0; + } + active() { + return this._active; + } + update(cfg, to2, date) { + if (this._active) { + this._notify(false); + const currentValue = this._target[this._prop]; + const elapsed = date - this._start; + const 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, + to2, + currentValue, + cfg.from + ]); + this._from = resolve([ + cfg.from, + currentValue, + to2 + ]); + } + } + cancel() { + if (this._active) { + this.tick(Date.now()); + this._active = false; + this._notify(false); + } + } + tick(date) { + const elapsed = date - this._start; + const duration = this._duration; + const prop = this._prop; + const from2 = this._from; + const loop = this._loop; + const to2 = this._to; + let factor; + this._active = from2 !== to2 && (loop || elapsed < duration); + if (!this._active) { + this._target[prop] = to2; + this._notify(true); + return; + } + if (elapsed < 0) { + this._target[prop] = from2; + return; + } + factor = elapsed / duration % 2; + factor = loop && factor > 1 ? 2 - factor : factor; + factor = this._easing(Math.min(1, Math.max(0, factor))); + this._target[prop] = this._fn(from2, to2, factor); + } + wait() { + const promises = this._promises || (this._promises = []); + return new Promise((res, rej) => { + promises.push({ + res, + rej + }); + }); + } + _notify(resolved) { + const method = resolved ? "res" : "rej"; + const promises = this._promises || []; + for (let i = 0; i < promises.length; i++) { + promises[i][method](); + } + } + } + class Animations { + constructor(chart, config) { + this._chart = chart; + this._properties = /* @__PURE__ */ new Map(); + this.configure(config); + } + configure(config) { + if (!isObject(config)) { + return; + } + const animationOptions = Object.keys(defaults.animation); + 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) => { + if (prop === key || !animatedProps.has(prop)) { + animatedProps.set(prop, resolved); + } + }); + }); + } + _animateOptions(target, values) { + const newOptions = values.options; + const options = resolveTargetOptions(target, newOptions); + if (!options) { + return []; + } + const animations = this._createAnimations(options, newOptions); + if (newOptions.$shared) { + awaitAll(target.options.$animations, newOptions).then(() => { + target.options = newOptions; + }, () => { + }); + } + return animations; + } + _createAnimations(target, values) { + const animatedProps = this._properties; + const animations = []; + const running = target.$animations || (target.$animations = {}); + const props = Object.keys(values); + const date = Date.now(); + let i; + for (i = props.length - 1; i >= 0; --i) { + const prop = props[i]; + if (prop.charAt(0) === "$") { + continue; + } + if (prop === "options") { + 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; + } else { + animation.cancel(); + } + } + if (!cfg || !cfg.duration) { + target[prop] = value; + continue; + } + running[prop] = animation = new Animation(cfg, target, prop, value); + animations.push(animation); + } + return animations; + } + update(target, values) { + if (this._properties.size === 0) { + Object.assign(target, values); + return; + } + const animations = this._createAnimations(target, values); + if (animations.length) { + animator.add(this._chart, animations); + return true; + } + } + } + function awaitAll(animations, properties) { + const running = []; + const keys = Object.keys(properties); + for (let i = 0; i < keys.length; i++) { + const anim = animations[keys[i]]; + if (anim && anim.active()) { + running.push(anim.wait()); + } + } + return Promise.all(running); + } + function resolveTargetOptions(target, newOptions) { + if (!newOptions) { + return; + } + let options = target.options; + if (!options) { + target.options = newOptions; + return; + } + if (options.$shared) { + target.options = options = Object.assign({}, options, { + $shared: false, + $animations: {} + }); + } + return options; + } + function scaleClip(scale, allowedOverflow) { + const opts = scale && scale.options || {}; + const reverse = opts.reverse; + const min = opts.min === void 0 ? allowedOverflow : 0; + const max = opts.max === void 0 ? allowedOverflow : 0; + return { + start: reverse ? max : min, + end: reverse ? min : max + }; + } + function defaultClip(xScale, yScale, allowedOverflow) { + if (allowedOverflow === false) { + return false; + } + const x = scaleClip(xScale, allowedOverflow); + const y = scaleClip(yScale, allowedOverflow); + return { + top: y.end, + right: x.end, + bottom: y.start, + left: x.start + }; + } + function toClip(value) { + let t, r, b, l; + if (isObject(value)) { + t = value.top; + r = value.right; + b = value.bottom; + l = value.left; + } else { + t = r = b = l = value; + } + return { + top: t, + right: r, + bottom: b, + left: l, + disabled: value === false + }; + } + function getSortedDatasetIndices(chart, filterVisible) { + const keys = []; + const metasets = chart._getSortedDatasetMetas(filterVisible); + let i, ilen; + for (i = 0, ilen = metasets.length; i < ilen; ++i) { + keys.push(metasets[i].index); + } + return keys; + } + function applyStack(stack, value, dsIndex, options = {}) { + const keys = stack.keys; + const singleMode = options.mode === "single"; + let i, ilen, datasetIndex, otherValue; + if (value === null) { + return; + } + let found = false; + for (i = 0, ilen = keys.length; i < ilen; ++i) { + datasetIndex = +keys[i]; + if (datasetIndex === dsIndex) { + found = true; + if (options.all) { + continue; + } + break; + } + otherValue = stack.values[datasetIndex]; + if (isNumberFinite(otherValue) && (singleMode || value === 0 || sign(value) === sign(otherValue))) { + value += otherValue; + } + } + if (!found && !options.all) { + return 0; + } + return value; + } + function convertObjectDataToArray(data, meta) { + const { iScale, vScale } = meta; + const iAxisKey = iScale.axis === "x" ? "x" : "y"; + const vAxisKey = vScale.axis === "x" ? "x" : "y"; + const keys = Object.keys(data); + const adata = new Array(keys.length); + let i, ilen, key; + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + adata[i] = { + [iAxisKey]: key, + [vAxisKey]: data[key] + }; + } + return adata; + } + function isStacked(scale, meta) { + const stacked = scale && scale.options.stacked; + return stacked || stacked === void 0 && meta.stack !== void 0; + } + function getStackKey(indexScale, valueScale, meta) { + return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`; + } + function getUserBounds(scale) { + const { min, max, minDefined, maxDefined } = scale.getUserBounds(); + return { + min: minDefined ? min : Number.NEGATIVE_INFINITY, + max: maxDefined ? max : Number.POSITIVE_INFINITY + }; + } + function getOrCreateStack(stacks, stackKey, indexValue) { + const subStack = stacks[stackKey] || (stacks[stackKey] = {}); + return subStack[indexValue] || (subStack[indexValue] = {}); + } + function getLastIndexInStack(stack, vScale, positive, type) { + for (const meta of vScale.getMatchingVisibleMetas(type).reverse()) { + const value = stack[meta.index]; + if (positive && value > 0 || !positive && value < 0) { + return meta.index; + } + } + return null; + } + function updateStacks(controller, parsed) { + const { chart, _cachedMeta: meta } = controller; + const stacks = chart._stacks || (chart._stacks = {}); + const { iScale, vScale, index: datasetIndex } = meta; + const iAxis = iScale.axis; + const vAxis = vScale.axis; + const key = getStackKey(iScale, vScale, meta); + const ilen = parsed.length; + let stack; + for (let i = 0; i < ilen; ++i) { + const item = parsed[i]; + const { [iAxis]: index2, [vAxis]: value } = item; + const itemStacks = item._stacks || (item._stacks = {}); + stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index2); + stack[datasetIndex] = value; + stack._top = getLastIndexInStack(stack, vScale, true, meta.type); + stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type); + const visualValues = stack._visualValues || (stack._visualValues = {}); + visualValues[datasetIndex] = value; + } + } + function getFirstScaleId(chart, axis) { + const scales = chart.scales; + return Object.keys(scales).filter((key) => scales[key].axis === axis).shift(); + } + function createDatasetContext(parent, index2) { + return createContext(parent, { + active: false, + dataset: void 0, + datasetIndex: index2, + index: index2, + mode: "default", + type: "dataset" + }); + } + function createDataContext(parent, index2, element) { + return createContext(parent, { + active: false, + dataIndex: index2, + parsed: void 0, + raw: void 0, + element, + index: index2, + mode: "default", + type: "data" + }); + } + function clearStacks(meta, items) { + const datasetIndex = meta.controller.index; + const axis = meta.vScale && meta.vScale.axis; + if (!axis) { + return; + } + items = items || meta._parsed; + for (const parsed of items) { + const stacks = parsed._stacks; + if (!stacks || stacks[axis] === void 0 || stacks[axis][datasetIndex] === void 0) { + return; + } + delete stacks[axis][datasetIndex]; + if (stacks[axis]._visualValues !== void 0 && stacks[axis]._visualValues[datasetIndex] !== void 0) { + delete stacks[axis]._visualValues[datasetIndex]; + } + } + } + const isDirectUpdateMode = (mode) => mode === "reset" || mode === "none"; + const cloneIfNotShared = (cached, shared) => shared ? cached : Object.assign({}, cached); + const createStack = (canStack, meta, chart) => canStack && !meta.hidden && meta._stacked && { + keys: getSortedDatasetIndices(chart, true), + values: null + }; + 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 = false; + this._data = void 0; + this._objectData = void 0; + this._sharedOptions = void 0; + this._drawStart = void 0; + this._drawCount = void 0; + this.enableOptionSharing = false; + this.supportsDecimation = false; + this.$context = void 0; + this._syncList = []; + this.datasetElementType = new.target.datasetElementType; + this.dataElementType = new.target.dataElementType; + this.initialize(); + } + initialize() { + const meta = this._cachedMeta; + this.configure(); + this.linkScales(); + meta._stacked = isStacked(meta.vScale, meta); + this.addElements(); + if (this.options.fill && !this.chart.isPluginEnabled("filler")) { + console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options"); + } + } + updateIndex(datasetIndex) { + if (this.index !== datasetIndex) { + clearStacks(this._cachedMeta); + } + this.index = datasetIndex; + } + linkScales() { + const chart = this.chart; + const meta = this._cachedMeta; + const dataset = this.getDataset(); + const chooseId = (axis, x, y, r) => axis === "x" ? x : axis === "r" ? r : y; + const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, "x")); + const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, "y")); + const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, "r")); + const indexAxis = meta.indexAxis; + const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid); + const 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; + if (this._data) { + unlistenArrayEvents(this._data, this); + } + if (meta._stacked) { + clearStacks(meta); + } + } + _dataCheck() { + const dataset = this.getDataset(); + const data = dataset.data || (dataset.data = []); + const _data = this._data; + if (isObject(data)) { + const meta = this._cachedMeta; + this._data = convertObjectDataToArray(data, meta); + } else if (_data !== data) { + if (_data) { + unlistenArrayEvents(_data, this); + const meta = this._cachedMeta; + clearStacks(meta); + meta._parsed = []; + } + if (data && Object.isExtensible(data)) { + listenArrayEvents(data, this); + } + this._syncList = []; + this._data = data; + } + } + addElements() { + const meta = this._cachedMeta; + this._dataCheck(); + if (this.datasetElementType) { + meta.dataset = new this.datasetElementType(); + } + } + buildOrUpdateElements(resetNewElements) { + const meta = this._cachedMeta; + const dataset = this.getDataset(); + let stackChanged = false; + this._dataCheck(); + const oldStacked = meta._stacked; + meta._stacked = isStacked(meta.vScale, meta); + if (meta.stack !== dataset.stack) { + stackChanged = true; + clearStacks(meta); + meta.stack = dataset.stack; + } + this._resyncElements(resetNewElements); + if (stackChanged || oldStacked !== meta._stacked) { + updateStacks(this, meta._parsed); + meta._stacked = isStacked(meta.vScale, meta); + } + } + configure() { + const config = this.chart.config; + const scopeKeys = config.datasetScopeKeys(this._type); + const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true); + this.options = config.createResolver(scopes, this.getContext()); + this._parsing = this.options.parsing; + this._cachedDataOpts = {}; + } + parse(start, count) { + const { _cachedMeta: meta, _data: data } = this; + const { iScale, _stacked } = meta; + const iAxis = iScale.axis; + let sorted = start === 0 && count === data.length ? true : meta._sorted; + let prev = start > 0 && meta._parsed[start - 1]; + let i, cur, parsed; + if (this._parsing === false) { + meta._parsed = data; + meta._sorted = true; + parsed = data; + } else { + if (isArray(data[start])) { + parsed = this.parseArrayData(meta, data, start, count); + } else if (isObject(data[start])) { + parsed = this.parseObjectData(meta, data, start, count); + } else { + parsed = this.parsePrimitiveData(meta, data, start, count); + } + const isNotInOrderComparedToPrev = () => cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis]; + for (i = 0; i < count; ++i) { + meta._parsed[i + start] = cur = parsed[i]; + if (sorted) { + if (isNotInOrderComparedToPrev()) { + sorted = false; + } + prev = cur; + } + } + meta._sorted = sorted; + } + if (_stacked) { + updateStacks(this, parsed); + } + } + parsePrimitiveData(meta, data, start, count) { + const { iScale, vScale } = meta; + const iAxis = iScale.axis; + const vAxis = vScale.axis; + const labels = iScale.getLabels(); + const singleScale = iScale === vScale; + const parsed = new Array(count); + let i, ilen, index2; + for (i = 0, ilen = count; i < ilen; ++i) { + index2 = i + start; + parsed[i] = { + [iAxis]: singleScale || iScale.parse(labels[index2], index2), + [vAxis]: vScale.parse(data[index2], index2) + }; + } + return parsed; + } + parseArrayData(meta, data, start, count) { + const { xScale, yScale } = meta; + const parsed = new Array(count); + let i, ilen, index2, item; + for (i = 0, ilen = count; i < ilen; ++i) { + index2 = i + start; + item = data[index2]; + parsed[i] = { + x: xScale.parse(item[0], index2), + y: yScale.parse(item[1], index2) + }; + } + return parsed; + } + parseObjectData(meta, data, start, count) { + const { xScale, yScale } = meta; + const { xAxisKey = "x", yAxisKey = "y" } = this._parsing; + const parsed = new Array(count); + let i, ilen, index2, item; + for (i = 0, ilen = count; i < ilen; ++i) { + index2 = i + start; + item = data[index2]; + parsed[i] = { + x: xScale.parse(resolveObjectKey(item, xAxisKey), index2), + y: yScale.parse(resolveObjectKey(item, yAxisKey), index2) + }; + } + return parsed; + } + getParsed(index2) { + return this._cachedMeta._parsed[index2]; + } + getDataElement(index2) { + return this._cachedMeta.data[index2]; + } + applyStack(scale, parsed, mode) { + const chart = this.chart; + const meta = this._cachedMeta; + const value = parsed[scale.axis]; + const stack = { + keys: getSortedDatasetIndices(chart, true), + values: parsed._stacks[scale.axis]._visualValues + }; + return applyStack(stack, value, meta.index, { + mode + }); + } + updateRangeFromParsed(range, scale, parsed, stack) { + const parsedValue = parsed[scale.axis]; + let value = parsedValue === null ? NaN : parsedValue; + const values = stack && parsed._stacks[scale.axis]; + if (stack && values) { + stack.values = values; + value = applyStack(stack, parsedValue, this._cachedMeta.index); + } + range.min = Math.min(range.min, value); + range.max = Math.max(range.max, value); + } + getMinMax(scale, canStack) { + const meta = this._cachedMeta; + const _parsed = meta._parsed; + const sorted = meta._sorted && scale === meta.iScale; + const ilen = _parsed.length; + const otherScale = this._getOtherScale(scale); + const stack = createStack(canStack, meta, this.chart); + const range = { + min: Number.POSITIVE_INFINITY, + max: Number.NEGATIVE_INFINITY + }; + const { min: otherMin, max: otherMax } = getUserBounds(otherScale); + let i, parsed; + function _skip() { + parsed = _parsed[i]; + const otherValue = parsed[otherScale.axis]; + return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue; + } + for (i = 0; i < ilen; ++i) { + if (_skip()) { + continue; + } + this.updateRangeFromParsed(range, scale, parsed, stack); + if (sorted) { + break; + } + } + if (sorted) { + for (i = ilen - 1; i >= 0; --i) { + if (_skip()) { + continue; + } + this.updateRangeFromParsed(range, scale, parsed, stack); + break; + } + } + return range; + } + getAllParsedValues(scale) { + const parsed = this._cachedMeta._parsed; + const values = []; + let i, ilen, value; + for (i = 0, ilen = parsed.length; i < ilen; ++i) { + value = parsed[i][scale.axis]; + if (isNumberFinite(value)) { + values.push(value); + } + } + return values; + } + getMaxOverflow() { + return false; + } + getLabelAndValue(index2) { + const meta = this._cachedMeta; + const iScale = meta.iScale; + const vScale = meta.vScale; + const parsed = this.getParsed(index2); + return { + label: iScale ? "" + iScale.getLabelForValue(parsed[iScale.axis]) : "", + value: vScale ? "" + vScale.getLabelForValue(parsed[vScale.axis]) : "" + }; + } + _update(mode) { + const meta = this._cachedMeta; + this.update(mode || "default"); + meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow()))); + } + update(mode) { + } + draw() { + const ctx = this._ctx; + const chart = this.chart; + const meta = this._cachedMeta; + const elements = meta.data || []; + const area = chart.chartArea; + const active = []; + const start = this._drawStart || 0; + const count = this._drawCount || elements.length - start; + const drawActiveElementsOnTop = this.options.drawActiveElementsOnTop; + let i; + if (meta.dataset) { + meta.dataset.draw(ctx, area, start, count); + } + for (i = start; i < start + count; ++i) { + const element = elements[i]; + if (element.hidden) { + continue; + } + if (element.active && drawActiveElementsOnTop) { + active.push(element); + } else { + element.draw(ctx, area); + } + } + for (i = 0; i < active.length; ++i) { + active[i].draw(ctx, area); + } + } + getStyle(index2, active) { + const mode = active ? "active" : "default"; + return index2 === void 0 && this._cachedMeta.dataset ? this.resolveDatasetElementOptions(mode) : this.resolveDataElementOptions(index2 || 0, mode); + } + getContext(index2, active, mode) { + const dataset = this.getDataset(); + let context; + if (index2 >= 0 && index2 < this._cachedMeta.data.length) { + const element = this._cachedMeta.data[index2]; + context = element.$context || (element.$context = createDataContext(this.getContext(), index2, element)); + context.parsed = this.getParsed(index2); + context.raw = dataset.data[index2]; + context.index = context.dataIndex = index2; + } else { + context = this.$context || (this.$context = createDatasetContext(this.chart.getContext(), this.index)); + context.dataset = dataset; + context.index = context.datasetIndex = this.index; + } + context.active = !!active; + context.mode = mode; + return context; + } + resolveDatasetElementOptions(mode) { + return this._resolveElementOptions(this.datasetElementType.id, mode); + } + resolveDataElementOptions(index2, mode) { + return this._resolveElementOptions(this.dataElementType.id, mode, index2); + } + _resolveElementOptions(elementType, mode = "default", index2) { + const active = mode === "active"; + const cache = this._cachedDataOpts; + const cacheKey = elementType + "-" + mode; + const cached = cache[cacheKey]; + const sharing = this.enableOptionSharing && defined(index2); + if (cached) { + return cloneIfNotShared(cached, sharing); + } + const config = this.chart.config; + const scopeKeys = config.datasetElementScopeKeys(this._type, elementType); + const prefixes = active ? [ + `${elementType}Hover`, + "hover", + elementType, + "" + ] : [ + elementType, + "" + ]; + const scopes = config.getOptionScopes(this.getDataset(), scopeKeys); + const names2 = Object.keys(defaults.elements[elementType]); + const context = () => this.getContext(index2, active, mode); + const values = config.resolveNamedOptions(scopes, names2, context, prefixes); + if (values.$shared) { + values.$shared = sharing; + cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing)); + } + return values; + } + _resolveAnimations(index2, transition, active) { + const chart = this.chart; + const cache = this._cachedDataOpts; + const cacheKey = `animation-${transition}`; + const cached = cache[cacheKey]; + if (cached) { + return cached; + } + let options; + if (chart.options.animation !== false) { + const config = this.chart.config; + const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition); + const scopes = config.getOptionScopes(this.getDataset(), scopeKeys); + options = config.createResolver(scopes, this.getContext(index2, active, transition)); + } + const animations = new Animations(chart, options && options.animations); + if (options && options._cacheable) { + cache[cacheKey] = Object.freeze(animations); + } + return animations; + } + getSharedOptions(options) { + if (!options.$shared) { + return; + } + return this._sharedOptions || (this._sharedOptions = Object.assign({}, options)); + } + includeOptions(mode, sharedOptions) { + return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled; + } + _getSharedOptions(start, mode) { + const firstOpts = this.resolveDataElementOptions(start, mode); + const previouslySharedOptions = this._sharedOptions; + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions; + this.updateSharedOptions(sharedOptions, mode, firstOpts); + return { + sharedOptions, + includeOptions + }; + } + updateElement(element, index2, properties, mode) { + if (isDirectUpdateMode(mode)) { + Object.assign(element, properties); + } else { + this._resolveAnimations(index2, mode).update(element, properties); + } + } + updateSharedOptions(sharedOptions, mode, newOptions) { + if (sharedOptions && !isDirectUpdateMode(mode)) { + this._resolveAnimations(void 0, mode).update(sharedOptions, newOptions); + } + } + _setStyle(element, index2, mode, active) { + element.active = active; + const options = this.getStyle(index2, active); + this._resolveAnimations(index2, mode, active).update(element, { + options: !active && this.getSharedOptions(options) || options + }); + } + removeHoverStyle(element, datasetIndex, index2) { + this._setStyle(element, index2, "active", false); + } + setHoverStyle(element, datasetIndex, index2) { + this._setStyle(element, index2, "active", true); + } + _removeDatasetHoverStyle() { + const element = this._cachedMeta.dataset; + if (element) { + this._setStyle(element, void 0, "active", false); + } + } + _setDatasetHoverStyle() { + const element = this._cachedMeta.dataset; + if (element) { + this._setStyle(element, void 0, "active", true); + } + } + _resyncElements(resetNewElements) { + const data = this._data; + const elements = this._cachedMeta.data; + for (const [method, arg1, arg2] of this._syncList) { + this[method](arg1, arg2); + } + this._syncList = []; + const numMeta = elements.length; + const numData = data.length; + const count = Math.min(numData, numMeta); + if (count) { + this.parse(0, count); + } + if (numData > numMeta) { + this._insertElements(numMeta, numData - numMeta, resetNewElements); + } else if (numData < numMeta) { + this._removeElements(numData, numMeta - numData); + } + } + _insertElements(start, count, resetNewElements = true) { + const meta = this._cachedMeta; + const data = meta.data; + const end = start + count; + let i; + const move = (arr) => { + arr.length += count; + for (i = arr.length - 1; i >= end; i--) { + arr[i] = arr[i - count]; + } + }; + move(data); + for (i = start; i < end; ++i) { + data[i] = new this.dataElementType(); + } + if (this._parsing) { + move(meta._parsed); + } + this.parse(start, count); + if (resetNewElements) { + this.updateElements(data, start, count, "reset"); + } + } + updateElements(element, start, count, mode) { + } + _removeElements(start, count) { + const meta = this._cachedMeta; + if (this._parsing) { + const removed = meta._parsed.splice(start, count); + if (meta._stacked) { + clearStacks(meta, removed); + } + } + meta.data.splice(start, count); + } + _sync(args) { + if (this._parsing) { + this._syncList.push(args); + } else { + const [method, arg1, arg2] = args; + this[method](arg1, arg2); + } + this.chart._dataChanges.push([ + this.index, + ...args + ]); + } + _onDataPush() { + const count = arguments.length; + this._sync([ + "_insertElements", + this.getDataset().data.length - count, + count + ]); + } + _onDataPop() { + this._sync([ + "_removeElements", + this._cachedMeta.data.length - 1, + 1 + ]); + } + _onDataShift() { + this._sync([ + "_removeElements", + 0, + 1 + ]); + } + _onDataSplice(start, count) { + if (count) { + this._sync([ + "_removeElements", + start, + count + ]); + } + const newCount = arguments.length - 2; + if (newCount) { + this._sync([ + "_insertElements", + start, + newCount + ]); + } + } + _onDataUnshift() { + this._sync([ + "_insertElements", + 0, + arguments.length + ]); + } + } + __publicField(DatasetController, "defaults", {}); + __publicField(DatasetController, "datasetElementType", null); + __publicField(DatasetController, "dataElementType", null); + class LineController extends DatasetController { + initialize() { + this.enableOptionSharing = true; + this.supportsDecimation = true; + super.initialize(); + } + update(mode) { + const meta = this._cachedMeta; + const { dataset: line, data: points = [], _dataset } = meta; + const animationsDisabled = this.chart._animationsDisabled; + let { start, count } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled); + this._drawStart = start; + this._drawCount = count; + if (_scaleRangesChanged(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); + if (!this.options.showLine) { + options.borderWidth = 0; + } + options.segment = this.options.segment; + this.updateElement(line, void 0, { + animated: !animationsDisabled, + options + }, mode); + this.updateElements(points, start, count, mode); + } + updateElements(points, start, count, mode) { + const reset = mode === "reset"; + const { iScale, vScale, _stacked, _dataset } = this._cachedMeta; + const { sharedOptions, includeOptions } = this._getSharedOptions(start, mode); + const iAxis = iScale.axis; + const vAxis = vScale.axis; + const { spanGaps, segment } = this.options; + const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY; + const directUpdate = this.chart._animationsDisabled || reset || mode === "none"; + const end = start + count; + const pointsCount = points.length; + let prevParsed = start > 0 && this.getParsed(start - 1); + for (let i = 0; i < pointsCount; ++i) { + const point = points[i]; + const properties = directUpdate ? point : {}; + if (i < start || i >= end) { + properties.skip = true; + continue; + } + const parsed = this.getParsed(i); + const nullData = isNullOrUndef(parsed[vAxis]); + const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i); + const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i); + properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData; + properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength; + if (segment) { + properties.parsed = parsed; + properties.raw = _dataset.data[i]; + } + if (includeOptions) { + properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? "active" : mode); + } + if (!directUpdate) { + this.updateElement(point, i, properties, mode); + } + prevParsed = parsed; + } + } + getMaxOverflow() { + const meta = this._cachedMeta; + const dataset = meta.dataset; + const border = dataset.options && dataset.options.borderWidth || 0; + const data = meta.data || []; + if (!data.length) { + return border; + } + const firstPoint = data[0].size(this.resolveDataElementOptions(0)); + const 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(); + } + } + __publicField(LineController, "id", "line"); + __publicField(LineController, "defaults", { + datasetElementType: "line", + dataElementType: "point", + showLine: true, + spanGaps: false + }); + __publicField(LineController, "overrides", { + scales: { + _index_: { + type: "category" + }, + _value_: { + type: "linear" + } + } + }); + function abstract() { + throw new Error("This method is not implemented: Check that a complete date adapter is provided."); + } + class DateAdapterBase { + constructor(options) { + __publicField(this, "options"); + this.options = options || {}; + } + /** + * Override default date adapter methods. + * Accepts type parameter to define options type. + * @example + * Chart._adapters._date.override<{myAdapterOption: string}>({ + * init() { + * console.log(this.options.myAdapterOption); + * } + * }) + */ + static override(members) { + Object.assign(DateAdapterBase.prototype, members); + } + // eslint-disable-next-line @typescript-eslint/no-empty-function + init() { + } + formats() { + return abstract(); + } + parse() { + return abstract(); + } + format() { + return abstract(); + } + add() { + return abstract(); + } + diff() { + return abstract(); + } + startOf() { + return abstract(); + } + endOf() { + return abstract(); + } + } + var adapters = { + _date: DateAdapterBase + }; + function binarySearch(metaset, axis, value, intersect) { + const { controller, data, _sorted } = metaset; + const iScale = controller._cachedMeta.iScale; + const spanGaps = metaset.dataset ? metaset.dataset.options ? metaset.dataset.options.spanGaps : null : null; + if (iScale && axis === iScale.axis && axis !== "r" && _sorted && data.length) { + const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey; + if (!intersect) { + const result = lookupMethod(data, axis, value); + if (spanGaps) { + const { vScale } = controller._cachedMeta; + const { _parsed } = metaset; + const distanceToDefinedLo = _parsed.slice(0, result.lo + 1).reverse().findIndex((point) => !isNullOrUndef(point[vScale.axis])); + result.lo -= Math.max(0, distanceToDefinedLo); + const distanceToDefinedHi = _parsed.slice(result.hi).findIndex((point) => !isNullOrUndef(point[vScale.axis])); + result.hi += Math.max(0, distanceToDefinedHi); + } + return result; + } else if (controller._sharedOptions) { + const el = data[0]; + const range = typeof el.getRange === "function" && el.getRange(axis); + if (range) { + const start = lookupMethod(data, axis, value - range); + const end = lookupMethod(data, axis, value + range); + return { + lo: start.lo, + hi: end.hi + }; + } + } + } + return { + lo: 0, + hi: data.length - 1 + }; + } + function evaluateInteractionItems(chart, axis, position, handler, intersect) { + const metasets = chart.getSortedVisibleDatasetMetas(); + const value = position[axis]; + for (let i = 0, ilen = metasets.length; i < ilen; ++i) { + const { index: index2, data } = metasets[i]; + const { lo, hi } = binarySearch(metasets[i], axis, value, intersect); + for (let j = lo; j <= hi; ++j) { + const element = data[j]; + if (!element.skip) { + handler(element, index2, j); + } + } + } + } + function getDistanceMetricForAxis(axis) { + const useX = axis.indexOf("x") !== -1; + const useY = axis.indexOf("y") !== -1; + return function(pt1, pt2) { + const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; + const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + }; + } + function getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) { + const items = []; + if (!includeInvisible && !chart.isPointInArea(position)) { + return items; + } + const evaluationFunc = function(element, datasetIndex, index2) { + if (!includeInvisible && !_isPointInArea(element, chart.chartArea, 0)) { + return; + } + if (element.inRange(position.x, position.y, useFinalPosition)) { + items.push({ + element, + datasetIndex, + index: index2 + }); + } + }; + evaluateInteractionItems(chart, axis, position, evaluationFunc, true); + return items; + } + function getNearestRadialItems(chart, position, axis, useFinalPosition) { + let items = []; + function evaluationFunc(element, datasetIndex, index2) { + const { startAngle, endAngle } = element.getProps([ + "startAngle", + "endAngle" + ], useFinalPosition); + const { angle } = getAngleFromPoint(element, { + x: position.x, + y: position.y + }); + if (_angleBetween(angle, startAngle, endAngle)) { + items.push({ + element, + datasetIndex, + index: index2 + }); + } + } + evaluateInteractionItems(chart, axis, position, evaluationFunc); + return items; + } + function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) { + let items = []; + const distanceMetric = getDistanceMetricForAxis(axis); + let minDistance = Number.POSITIVE_INFINITY; + function evaluationFunc(element, datasetIndex, index2) { + const inRange = element.inRange(position.x, position.y, useFinalPosition); + if (intersect && !inRange) { + return; + } + const center = element.getCenterPoint(useFinalPosition); + const pointInArea = !!includeInvisible || chart.isPointInArea(center); + if (!pointInArea && !inRange) { + return; + } + const distance = distanceMetric(position, center); + if (distance < minDistance) { + items = [ + { + element, + datasetIndex, + index: index2 + } + ]; + minDistance = distance; + } else if (distance === minDistance) { + items.push({ + element, + datasetIndex, + index: index2 + }); + } + } + evaluateInteractionItems(chart, axis, position, evaluationFunc); + return items; + } + function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) { + if (!includeInvisible && !chart.isPointInArea(position)) { + return []; + } + return axis === "r" && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible); + } + function getAxisItems(chart, position, axis, intersect, useFinalPosition) { + const items = []; + const rangeMethod = axis === "x" ? "inXRange" : "inYRange"; + let intersectsItem = false; + evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index2) => { + if (element[rangeMethod] && element[rangeMethod](position[axis], useFinalPosition)) { + items.push({ + element, + datasetIndex, + index: index2 + }); + intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition); + } + }); + if (intersect && !intersectsItem) { + return []; + } + return items; + } + var Interaction = { + modes: { + index(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || "x"; + const includeInvisible = options.includeInvisible || false; + const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible); + const elements = []; + if (!items.length) { + return []; + } + chart.getSortedVisibleDatasetMetas().forEach((meta) => { + const index2 = items[0].index; + const element = meta.data[index2]; + if (element && !element.skip) { + elements.push({ + element, + datasetIndex: meta.index, + index: index2 + }); + } + }); + return elements; + }, + dataset(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || "xy"; + const includeInvisible = options.includeInvisible || false; + let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible); + if (items.length > 0) { + const datasetIndex = items[0].datasetIndex; + const data = chart.getDatasetMeta(datasetIndex).data; + items = []; + for (let i = 0; i < data.length; ++i) { + items.push({ + element: data[i], + datasetIndex, + index: i + }); + } + } + return items; + }, + point(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || "xy"; + const includeInvisible = options.includeInvisible || false; + return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible); + }, + nearest(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || "xy"; + const includeInvisible = options.includeInvisible || false; + return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible); + }, + x(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + return getAxisItems(chart, position, "x", options.intersect, useFinalPosition); + }, + y(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + return getAxisItems(chart, position, "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) => STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis); + } + function sortByWeight(array, reverse) { + return array.sort((a, b) => { + const v0 = reverse ? b : a; + const v1 = reverse ? a : b; + return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight; + }); + } + function wrapBoxes(boxes) { + const layoutBoxes = []; + let i, ilen, box, pos, stack, stackWeight; + for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) { + box = boxes[i]; + ({ position: pos, options: { stack, stackWeight = 1 } } = box); + layoutBoxes.push({ + index: i, + box, + pos, + horizontal: box.isHorizontal(), + weight: box.weight, + stack: stack && pos + stack, + stackWeight + }); + } + return layoutBoxes; + } + function buildStacks(layouts2) { + const stacks = {}; + for (const wrap of layouts2) { + const { stack, pos, 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; + } + function setLayoutDims(layouts2, params) { + const stacks = buildStacks(layouts2); + const { vBoxMaxWidth, hBoxMaxHeight } = params; + let i, ilen, layout; + for (i = 0, ilen = layouts2.length; i < ilen; ++i) { + layout = layouts2[i]; + const { fullSize } = layout.box; + const stack = stacks[layout.stack]; + const factor = stack && layout.stackWeight / stack.weight; + if (layout.horizontal) { + layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth; + layout.height = hBoxMaxHeight; + } else { + layout.width = vBoxMaxWidth; + layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight; + } + } + return stacks; + } + function buildLayoutBoxes(boxes) { + const layoutBoxes = wrapBoxes(boxes); + const fullSize = sortByWeight(layoutBoxes.filter((wrap) => wrap.box.fullSize), true); + const left = sortByWeight(filterByPosition(layoutBoxes, "left"), true); + const right = sortByWeight(filterByPosition(layoutBoxes, "right")); + const top = sortByWeight(filterByPosition(layoutBoxes, "top"), true); + const bottom = sortByWeight(filterByPosition(layoutBoxes, "bottom")); + const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, "x"); + const centerVertical = filterDynamicPositionByAxis(layoutBoxes, "y"); + return { + 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) + }; + } + function getCombinedMax(maxPadding, chartArea, a, b) { + return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]); + } + function updateMaxPadding(maxPadding, boxPadding) { + 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); + } + function updateDims(chartArea, params, layout, stacks) { + const { pos, box } = layout; + const maxPadding = chartArea.maxPadding; + if (!isObject(pos)) { + if (layout.size) { + chartArea[pos] -= layout.size; + } + const stack = stacks[layout.stack] || { + size: 0, + count: 1 + }; + stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width); + layout.size = stack.size / stack.count; + chartArea[pos] += layout.size; + } + if (box.getPadding) { + updateMaxPadding(maxPadding, box.getPadding()); + } + const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, "left", "right")); + const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, "top", "bottom")); + const widthChanged = newWidth !== chartArea.w; + const heightChanged = newHeight !== chartArea.h; + chartArea.w = newWidth; + chartArea.h = newHeight; + return layout.horizontal ? { + same: widthChanged, + other: heightChanged + } : { + same: heightChanged, + other: widthChanged + }; + } + function handleMaxPadding(chartArea) { + const maxPadding = chartArea.maxPadding; + function updatePos(pos) { + const change = Math.max(maxPadding[pos] - chartArea[pos], 0); + chartArea[pos] += change; + return change; + } + chartArea.y += updatePos("top"); + chartArea.x += updatePos("left"); + updatePos("right"); + updatePos("bottom"); + } + function getMargins(horizontal, chartArea) { + const maxPadding = chartArea.maxPadding; + function marginForPositions(positions2) { + const margin = { + left: 0, + top: 0, + right: 0, + bottom: 0 + }; + positions2.forEach((pos) => { + margin[pos] = Math.max(chartArea[pos], maxPadding[pos]); + }); + return margin; + } + return horizontal ? marginForPositions([ + "left", + "right" + ]) : marginForPositions([ + "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; i < ilen; ++i) { + layout = boxes[i]; + box = layout.box; + box.update(layout.width || chartArea.w, layout.height || chartArea.h, getMargins(layout.horizontal, chartArea)); + const { same, other } = updateDims(chartArea, params, layout, stacks); + refit |= same && refitBoxes.length; + changed = changed || other; + if (!box.fullSize) { + refitBoxes.push(layout); + } + } + return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed; + } + function setBoxDims(box, left, top, width, height) { + box.top = top; + box.left = left; + box.right = left + width; + box.bottom = top + height; + box.width = width; + box.height = height; + } + function placeBoxes(boxes, chartArea, params, stacks) { + const userPadding = params.padding; + let { x, y } = chartArea; + for (const layout of boxes) { + const box = layout.box; + const stack = stacks[layout.stack] || { + placed: 0, + weight: 1 + }; + const weight = layout.stackWeight / stack.weight || 1; + if (layout.horizontal) { + const width = chartArea.w * weight; + const height = stack.size || box.height; + if (defined(stack.start)) { + y = stack.start; + } + if (box.fullSize) { + setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height); + } else { + setBoxDims(box, chartArea.left + stack.placed, y, width, height); + } + stack.start = y; + stack.placed += width; + y = box.bottom; + } else { + const height = chartArea.h * weight; + const width = stack.size || box.width; + if (defined(stack.start)) { + x = stack.start; + } + if (box.fullSize) { + setBoxDims(box, x, userPadding.top, width, params.outerHeight - userPadding.bottom - userPadding.top); + } else { + setBoxDims(box, x, chartArea.top + stack.placed, width, height); + } + stack.start = x; + stack.placed += height; + x = box.right; + } + } + chartArea.x = x; + chartArea.y = y; + } + var layouts = { + addBox(chart, item) { + if (!chart.boxes) { + chart.boxes = []; + } + item.fullSize = item.fullSize || false; + item.position = item.position || "top"; + item.weight = item.weight || 0; + item._layers = item._layers || function() { + return [ + { + z: 0, + draw(chartArea) { + item.draw(chartArea); + } + } + ]; + }; + chart.boxes.push(item); + }, + removeBox(chart, layoutItem) { + const index2 = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; + if (index2 !== -1) { + chart.boxes.splice(index2, 1); + } + }, + configure(chart, item, options) { + item.fullSize = options.fullSize; + item.position = options.position; + item.weight = options.weight; + }, + update(chart, width, height, minPadding) { + if (!chart) { + return; + } + const padding = toPadding(chart.options.layout.padding); + const availableWidth = Math.max(width - padding.width, 0); + const availableHeight = Math.max(height - padding.height, 0); + const boxes = buildLayoutBoxes(chart.boxes); + const verticalBoxes = boxes.vertical; + const horizontalBoxes = boxes.horizontal; + each(chart.boxes, (box) => { + if (typeof box.beforeLayout === "function") { + box.beforeLayout(); + } + }); + const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap) => wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1; + const params = Object.freeze({ + outerWidth: width, + outerHeight: height, + padding, + availableWidth, + availableHeight, + vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount, + hBoxMaxHeight: availableHeight / 2 + }); + const maxPadding = Object.assign({}, padding); + updateMaxPadding(maxPadding, toPadding(minPadding)); + const chartArea = Object.assign({ + maxPadding, + w: availableWidth, + h: availableHeight, + x: padding.left, + y: padding.top + }, padding); + const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params); + fitBoxes(boxes.fullSize, chartArea, params, stacks); + fitBoxes(verticalBoxes, chartArea, params, stacks); + if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) { + fitBoxes(verticalBoxes, chartArea, params, stacks); + } + handleMaxPadding(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 false; + } + addEventListener(chart, type, listener) { + } + removeEventListener(chart, type, listener) { + } + getDevicePixelRatio() { + return 1; + } + getMaximumSize(element, width, height, aspectRatio) { + width = Math.max(0, width || element.width); + height = height || element.height; + return { + width, + height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height) + }; + } + isAttached(canvas) { + return true; + } + updateConfig(config) { + } + } + class BasicPlatform extends BasePlatform { + acquireContext(item) { + return item && item.getContext && item.getContext("2d") || null; + } + updateConfig(config) { + config.options.animation = false; + } + } + const EXPANDO_KEY = "$chartjs"; + const EVENT_TYPES = { + touchstart: "mousedown", + touchmove: "mousemove", + touchend: "mouseup", + pointerenter: "mouseenter", + pointerdown: "mousedown", + pointermove: "mousemove", + pointerup: "mouseup", + pointerleave: "mouseout", + pointerout: "mouseout" + }; + const isNullOrEmpty = (value) => value === null || value === ""; + function initCanvas(canvas, aspectRatio) { + const style = canvas.style; + const renderHeight = canvas.getAttribute("height"); + const renderWidth = canvas.getAttribute("width"); + canvas[EXPANDO_KEY] = { + 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"; + if (isNullOrEmpty(renderWidth)) { + const displayWidth = readUsedSize(canvas, "width"); + if (displayWidth !== void 0) { + canvas.width = displayWidth; + } + } + if (isNullOrEmpty(renderHeight)) { + if (canvas.style.height === "") { + canvas.height = canvas.width / (aspectRatio || 2); + } else { + const displayHeight = readUsedSize(canvas, "height"); + if (displayHeight !== void 0) { + canvas.height = displayHeight; + } + } + } + return canvas; + } + const eventListenerOptions = supportsEventListenerOptions ? { + passive: true + } : false; + function addListener(node, type, listener) { + if (node) { + node.addEventListener(type, listener, eventListenerOptions); + } + } + function removeListener(chart, type, listener) { + if (chart && chart.canvas) { + chart.canvas.removeEventListener(type, listener, eventListenerOptions); + } + } + function fromNativeEvent(event, chart) { + const type = EVENT_TYPES[event.type] || event.type; + const { x, y } = getRelativePosition(event, chart); + return { + type, + chart, + native: event, + x: x !== void 0 ? x : null, + y: y !== void 0 ? y : null + }; + } + function nodeListContains(nodeList, canvas) { + for (const node of nodeList) { + if (node === canvas || node.contains(canvas)) { + return true; + } + } + } + function createAttachObserver(chart, type, listener) { + const canvas = chart.canvas; + const observer = new MutationObserver((entries) => { + let trigger = false; + for (const entry of entries) { + trigger = trigger || nodeListContains(entry.addedNodes, canvas); + trigger = trigger && !nodeListContains(entry.removedNodes, canvas); + } + if (trigger) { + listener(); + } + }); + observer.observe(document, { + childList: true, + subtree: true + }); + return observer; + } + function createDetachObserver(chart, type, listener) { + const canvas = chart.canvas; + const observer = new MutationObserver((entries) => { + let trigger = false; + for (const entry of entries) { + trigger = trigger || nodeListContains(entry.removedNodes, canvas); + trigger = trigger && !nodeListContains(entry.addedNodes, canvas); + } + if (trigger) { + listener(); + } + }); + observer.observe(document, { + childList: true, + subtree: true + }); + return observer; + } + const drpListeningCharts = /* @__PURE__ */ new Map(); + let oldDevicePixelRatio = 0; + function onWindowResize() { + const dpr = window.devicePixelRatio; + if (dpr === oldDevicePixelRatio) { + return; + } + oldDevicePixelRatio = dpr; + drpListeningCharts.forEach((resize, chart) => { + if (chart.currentDevicePixelRatio !== dpr) { + resize(); + } + }); + } + function listenDevicePixelRatioChanges(chart, resize) { + if (!drpListeningCharts.size) { + window.addEventListener("resize", onWindowResize); + } + drpListeningCharts.set(chart, resize); + } + function unlistenDevicePixelRatioChanges(chart) { + drpListeningCharts.delete(chart); + if (!drpListeningCharts.size) { + window.removeEventListener("resize", onWindowResize); + } + } + function createResizeObserver(chart, type, listener) { + const canvas = chart.canvas; + const container = canvas && _getParentNode(canvas); + if (!container) { + return; + } + const resize = throttled((width, height) => { + const w = container.clientWidth; + listener(width, height); + if (w < container.clientWidth) { + listener(); + } + }, window); + const observer = new ResizeObserver((entries) => { + const entry = entries[0]; + const width = entry.contentRect.width; + const height = entry.contentRect.height; + if (width === 0 && height === 0) { + return; + } + resize(width, height); + }); + observer.observe(container); + listenDevicePixelRatioChanges(chart, resize); + return observer; + } + function releaseObserver(chart, type, observer) { + if (observer) { + observer.disconnect(); + } + if (type === "resize") { + unlistenDevicePixelRatioChanges(chart); + } + } + function createProxyAndListen(chart, type, listener) { + const canvas = chart.canvas; + const proxy = throttled((event) => { + if (chart.ctx !== null) { + listener(fromNativeEvent(event, chart)); + } + }, chart); + addListener(canvas, type, proxy); + return proxy; + } + class DomPlatform extends BasePlatform { + acquireContext(canvas, aspectRatio) { + const context = canvas && canvas.getContext && canvas.getContext("2d"); + if (context && context.canvas === canvas) { + initCanvas(canvas, aspectRatio); + return context; + } + return null; + } + releaseContext(context) { + const canvas = context.canvas; + if (!canvas[EXPANDO_KEY]) { + return false; + } + const initial = canvas[EXPANDO_KEY].initial; + [ + "height", + "width" + ].forEach((prop) => { + const value = initial[prop]; + if (isNullOrUndef(value)) { + canvas.removeAttribute(prop); + } else { + canvas.setAttribute(prop, value); + } + }); + const style = initial.style || {}; + Object.keys(style).forEach((key) => { + canvas.style[key] = style[key]; + }); + canvas.width = canvas.width; + delete canvas[EXPANDO_KEY]; + return true; + } + addEventListener(chart, type, listener) { + this.removeEventListener(chart, type); + const proxies = chart.$proxies || (chart.$proxies = {}); + const handlers = { + attach: createAttachObserver, + detach: createDetachObserver, + resize: createResizeObserver + }; + const handler = handlers[type] || createProxyAndListen; + proxies[type] = handler(chart, type, listener); + } + removeEventListener(chart, type) { + const proxies = chart.$proxies || (chart.$proxies = {}); + const proxy = proxies[type]; + if (!proxy) { + return; + } + const handlers = { + attach: releaseObserver, + detach: releaseObserver, + resize: releaseObserver + }; + const handler = handlers[type] || removeListener; + handler(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 = canvas && _getParentNode(canvas); + return !!(container && container.isConnected); + } + } + function _detectPlatform(canvas) { + if (!_isDomSupported() || typeof OffscreenCanvas !== "undefined" && canvas instanceof OffscreenCanvas) { + return BasicPlatform; + } + return DomPlatform; + } + class Element { + constructor() { + __publicField(this, "x"); + __publicField(this, "y"); + __publicField(this, "active", false); + __publicField(this, "options"); + __publicField(this, "$animations"); + } + tooltipPosition(useFinalPosition) { + const { x, y } = this.getProps([ + "x", + "y" + ], useFinalPosition); + return { + x, + y + }; + } + hasValue() { + return isNumber(this.x) && isNumber(this.y); + } + getProps(props, final) { + const anims = this.$animations; + if (!final || !anims) { + return this; + } + const ret = {}; + props.forEach((prop) => { + ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop]; + }); + return ret; + } + } + __publicField(Element, "defaults", {}); + __publicField(Element, "defaultRoutes"); + function autoSkip(scale, ticks) { + const tickOpts = scale.options.ticks; + const determinedMaxTicks = determineMaxTicks(scale); + const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks); + const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : []; + const numMajorIndices = majorIndices.length; + const first = majorIndices[0]; + const last = majorIndices[numMajorIndices - 1]; + const newTicks = []; + if (numMajorIndices > ticksLimit) { + skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit); + return newTicks; + } + const spacing = calculateSpacing(majorIndices, ticks, ticksLimit); + if (numMajorIndices > 0) { + let i, ilen; + const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null; + skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first); + for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) { + skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]); + } + skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing); + return newTicks; + } + skip(ticks, newTicks, spacing); + return newTicks; + } + function determineMaxTicks(scale) { + const offset = scale.options.offset; + const tickLength = scale._tickSize(); + const maxScale = scale._length / tickLength + (offset ? 0 : 1); + const maxChart = scale._maxLength / tickLength; + return Math.floor(Math.min(maxScale, maxChart)); + } + function calculateSpacing(majorIndices, ticks, ticksLimit) { + const evenMajorSpacing = getEvenSpacing(majorIndices); + const spacing = ticks.length / ticksLimit; + if (!evenMajorSpacing) { + return Math.max(spacing, 1); + } + const factors = _factorize(evenMajorSpacing); + for (let i = 0, ilen = factors.length - 1; i < ilen; i++) { + const factor = factors[i]; + if (factor > spacing) { + return factor; + } + } + return Math.max(spacing, 1); + } + function getMajorIndices(ticks) { + const result = []; + let i, ilen; + for (i = 0, ilen = ticks.length; i < ilen; i++) { + if (ticks[i].major) { + result.push(i); + } + } + return result; + } + function skipMajors(ticks, newTicks, majorIndices, spacing) { + let count = 0; + let next = majorIndices[0]; + let i; + spacing = Math.ceil(spacing); + for (i = 0; i < ticks.length; i++) { + if (i === next) { + newTicks.push(ticks[i]); + count++; + next = majorIndices[count * spacing]; + } + } + } + function skip(ticks, newTicks, spacing, majorStart, majorEnd) { + const start = valueOrDefault(majorStart, 0); + const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length); + let count = 0; + let length, i, next; + spacing = Math.ceil(spacing); + if (majorEnd) { + length = majorEnd - majorStart; + spacing = length / Math.floor(length / spacing); + } + next = start; + while (next < 0) { + count++; + next = Math.round(start + count * spacing); + } + for (i = Math.max(start, 0); i < end; i++) { + if (i === next) { + newTicks.push(ticks[i]); + count++; + next = Math.round(start + count * spacing); + } + } + } + function getEvenSpacing(arr) { + const len = arr.length; + let i, diff; + if (len < 2) { + return false; + } + for (diff = arr[0], i = 1; i < len; ++i) { + if (arr[i] - arr[i - 1] !== diff) { + return false; + } + } + return diff; + } + const reverseAlign = (align) => align === "left" ? "right" : align === "right" ? "left" : align; + const offsetFromEdge = (scale, edge, offset) => edge === "top" || edge === "left" ? scale[edge] + offset : scale[edge] - offset; + const getTicksLimit = (ticksLength, maxTicksLimit) => Math.min(maxTicksLimit || ticksLength, ticksLength); + function sample(arr, numItems) { + const result = []; + const increment = arr.length / numItems; + const len = arr.length; + let i = 0; + for (; i < len; i += increment) { + result.push(arr[Math.floor(i)]); + } + return result; + } + function getPixelForGridLine(scale, index2, offsetGridLines) { + const length = scale.ticks.length; + const validIndex2 = Math.min(index2, length - 1); + const start = scale._startPixel; + const end = scale._endPixel; + const epsilon = 1e-6; + let lineValue = scale.getPixelForTick(validIndex2); + let offset; + if (offsetGridLines) { + if (length === 1) { + offset = Math.max(lineValue - start, end - lineValue); + } else if (index2 === 0) { + offset = (scale.getPixelForTick(1) - lineValue) / 2; + } else { + offset = (lineValue - scale.getPixelForTick(validIndex2 - 1)) / 2; + } + lineValue += validIndex2 < index2 ? offset : -offset; + if (lineValue < start - epsilon || lineValue > end + epsilon) { + return; + } + } + return lineValue; + } + function garbageCollect(caches, length) { + each(caches, (cache) => { + const gc = cache.gc; + const gcLen = gc.length / 2; + let i; + if (gcLen > length) { + for (i = 0; i < gcLen; ++i) { + delete cache.data[gc[i]]; + } + gc.splice(0, gcLen); + } + }); + } + function getTickMarkLength(options) { + return options.drawTicks ? options.tickLength : 0; + } + function getTitleHeight(options, fallback) { + if (!options.display) { + return 0; + } + const font = toFont(options.font, fallback); + const padding = toPadding(options.padding); + const lines = isArray(options.text) ? options.text.length : 1; + return lines * font.lineHeight + padding.height; + } + function createScaleContext(parent, scale) { + return createContext(parent, { + scale, + type: "scale" + }); + } + function createTickContext(parent, index2, tick) { + return createContext(parent, { + tick, + index: index2, + type: "tick" + }); + } + function titleAlign(align, position, reverse) { + let ret = _toLeftRightCenter(align); + if (reverse && position !== "right" || !reverse && position === "right") { + ret = reverseAlign(ret); + } + return ret; + } + function titleArgs(scale, offset, position, align) { + const { top, left, bottom, right, chart } = scale; + const { chartArea, scales } = chart; + let rotation = 0; + let maxWidth, titleX, titleY; + const height = bottom - top; + const width = right - left; + if (scale.isHorizontal()) { + titleX = _alignStartEnd(align, left, right); + if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const value = position[positionAxisID]; + titleY = scales[positionAxisID].getPixelForValue(value) + height - offset; + } else if (position === "center") { + titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset; + } else { + titleY = offsetFromEdge(scale, position, offset); + } + maxWidth = right - left; + } else { + if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const value = position[positionAxisID]; + titleX = scales[positionAxisID].getPixelForValue(value) - width + offset; + } else if (position === "center") { + titleX = (chartArea.left + chartArea.right) / 2 - width + offset; + } else { + titleX = offsetFromEdge(scale, position, offset); + } + titleY = _alignStartEnd(align, bottom, top); + rotation = position === "left" ? -HALF_PI : HALF_PI; + } + return { + titleX, + titleY, + maxWidth, + rotation + }; + } + 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 = false; + 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 = false; + 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, index2) { + return raw; + } + getUserBounds() { + let { _userMin, _userMax, _suggestedMin, _suggestedMax } = this; + _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY); + _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY); + _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY); + _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY); + return { + min: finiteOrDefault(_userMin, _suggestedMin), + max: finiteOrDefault(_userMax, _suggestedMax), + minDefined: isNumberFinite(_userMin), + maxDefined: isNumberFinite(_userMax) + }; + } + getMinMax(canStack) { + let { min, max, minDefined, maxDefined } = this.getUserBounds(); + let range; + if (minDefined && maxDefined) { + return { + min, + max + }; + } + const metas = this.getMatchingVisibleMetas(); + for (let i = 0, ilen = metas.length; i < ilen; ++i) { + range = metas[i].controller.getMinMax(this, canStack); + if (!minDefined) { + min = Math.min(min, range.min); + } + if (!maxDefined) { + max = Math.max(max, range.max); + } + } + min = maxDefined && min > max ? max : min; + max = minDefined && min > max ? min : max; + return { + 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 || []; + } + getLabelItems(chartArea = this.chart.chartArea) { + const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea)); + return items; + } + beforeLayout() { + this._cache = {}; + this._dataLimitsCached = false; + } + beforeUpdate() { + callback(this.options.beforeUpdate, [ + this + ]); + } + update(maxWidth, maxHeight, margins) { + const { beginAtZero, grace, ticks: tickOpts } = this.options; + const 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; + if (!this._dataLimitsCached) { + this.beforeDataLimits(); + this.determineDataLimits(); + this.afterDataLimits(); + this._range = _addGrace(this, grace, beginAtZero); + this._dataLimitsCached = true; + } + this.beforeBuildTicks(); + this.ticks = this.buildTicks() || []; + this.afterBuildTicks(); + const samplingEnabled = sampleSize < this.ticks.length; + this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks); + this.configure(); + this.beforeCalculateLabelRotation(); + this.calculateLabelRotation(); + this.afterCalculateLabelRotation(); + if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === "auto")) { + this.ticks = autoSkip(this, this.ticks); + this._labelSizes = null; + this.afterAutoSkip(); + } + if (samplingEnabled) { + this._convertTicksToLabels(this.ticks); + } + this.beforeFit(); + this.fit(); + this.afterFit(); + this.afterUpdate(); + } + configure() { + let reversePixels = this.options.reverse; + let startPixel, endPixel; + if (this.isHorizontal()) { + startPixel = this.left; + endPixel = this.right; + } else { + startPixel = this.top; + endPixel = this.bottom; + reversePixels = !reversePixels; + } + this._startPixel = startPixel; + this._endPixel = endPixel; + this._reversePixels = reversePixels; + this._length = endPixel - startPixel; + this._alignToPixels = this.options.alignToPixels; + } + afterUpdate() { + callback(this.options.afterUpdate, [ + this + ]); + } + beforeSetDimensions() { + callback(this.options.beforeSetDimensions, [ + this + ]); + } + setDimensions() { + if (this.isHorizontal()) { + this.width = this.maxWidth; + this.left = 0; + this.right = this.width; + } else { + this.height = this.maxHeight; + this.top = 0; + this.bottom = this.height; + } + this.paddingLeft = 0; + this.paddingTop = 0; + this.paddingRight = 0; + this.paddingBottom = 0; + } + afterSetDimensions() { + callback(this.options.afterSetDimensions, [ + this + ]); + } + _callHooks(name) { + this.chart.notifyPlugins(name, this.getContext()); + callback(this.options[name], [ + this + ]); + } + beforeDataLimits() { + this._callHooks("beforeDataLimits"); + } + determineDataLimits() { + } + afterDataLimits() { + this._callHooks("afterDataLimits"); + } + beforeBuildTicks() { + this._callHooks("beforeBuildTicks"); + } + buildTicks() { + return []; + } + afterBuildTicks() { + this._callHooks("afterBuildTicks"); + } + beforeTickToLabelConversion() { + callback(this.options.beforeTickToLabelConversion, [ + this + ]); + } + generateTickLabels(ticks) { + const tickOpts = this.options.ticks; + let i, ilen, tick; + for (i = 0, ilen = ticks.length; i < ilen; i++) { + tick = ticks[i]; + tick.label = callback(tickOpts.callback, [ + tick.value, + i, + ticks + ], this); + } + } + afterTickToLabelConversion() { + callback(this.options.afterTickToLabelConversion, [ + this + ]); + } + beforeCalculateLabelRotation() { + callback(this.options.beforeCalculateLabelRotation, [ + this + ]); + } + calculateLabelRotation() { + const options = this.options; + const tickOpts = options.ticks; + const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit); + const minRotation = tickOpts.minRotation || 0; + const maxRotation = tickOpts.maxRotation; + let labelRotation = minRotation; + let tickWidth, maxHeight, maxLabelDiagonal; + if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) { + this.labelRotation = minRotation; + return; + } + const labelSizes = this._getLabelSizes(); + const maxLabelWidth = labelSizes.widest.width; + const maxLabelHeight = labelSizes.highest.height; + const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth); + tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1); + if (maxLabelWidth + 6 > tickWidth) { + tickWidth = maxWidth / (numTicks - (options.offset ? 0.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 + }; + const { chart, options: { ticks: tickOpts, title: titleOpts, grid: gridOpts } } = this; + const display = this._isVisible(); + const isHorizontal = this.isHorizontal(); + if (display) { + const titleHeight = getTitleHeight(titleOpts, chart.options.font); + if (isHorizontal) { + minSize.width = this.maxWidth; + minSize.height = getTickMarkLength(gridOpts) + titleHeight; + } else { + minSize.height = this.maxHeight; + minSize.width = getTickMarkLength(gridOpts) + titleHeight; + } + if (tickOpts.display && this.ticks.length) { + const { first, last, widest, highest } = this._getLabelSizes(); + const tickPadding = tickOpts.padding * 2; + const angleRadians = toRadians(this.labelRotation); + const cos = Math.cos(angleRadians); + const 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(); + if (isHorizontal) { + this.width = this._length = chart.width - this._margins.left - this._margins.right; + this.height = minSize.height; + } else { + this.width = minSize.width; + this.height = this._length = chart.height - this._margins.top - this._margins.bottom; + } + } + _calculatePadding(first, last, sin, cos) { + const { ticks: { align, padding }, position } = this.options; + const isRotated = this.labelRotation !== 0; + const labelsBelowTicks = position !== "top" && this.axis === "x"; + if (this.isHorizontal()) { + const offsetLeft = this.getPixelForTick(0) - this.left; + const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1); + let paddingLeft = 0; + let paddingRight = 0; + if (isRotated) { + if (labelsBelowTicks) { + paddingLeft = cos * first.width; + paddingRight = sin * last.height; + } else { + paddingLeft = sin * first.height; + paddingRight = cos * last.width; + } + } else if (align === "start") { + paddingRight = last.width; + } else if (align === "end") { + paddingLeft = first.width; + } else if (align !== "inner") { + 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; + let paddingBottom = first.height / 2; + if (align === "start") { + paddingTop = 0; + paddingBottom = first.height; + } else if (align === "end") { + paddingTop = last.height; + paddingBottom = 0; + } + this.paddingTop = paddingTop + padding; + this.paddingBottom = paddingBottom + padding; + } + } + _handleMargins() { + if (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, position } = this.options; + return position === "top" || position === "bottom" || axis === "x"; + } + isFullSize() { + return this.options.fullSize; + } + _convertTicksToLabels(ticks) { + this.beforeTickToLabelConversion(); + this.generateTickLabels(ticks); + let i, ilen; + for (i = 0, ilen = ticks.length; i < ilen; i++) { + if (isNullOrUndef(ticks[i].label)) { + ticks.splice(i, 1); + ilen--; + i--; + } + } + this.afterTickToLabelConversion(); + } + _getLabelSizes() { + let labelSizes = this._labelSizes; + if (!labelSizes) { + const sampleSize = this.options.ticks.sampleSize; + let ticks = this.ticks; + if (sampleSize < ticks.length) { + ticks = sample(ticks, sampleSize); + } + this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit); + } + return labelSizes; + } + _computeLabelSizes(ticks, length, maxTicksLimit) { + const { ctx, _longestTextCache: caches } = this; + const widths = []; + const heights = []; + const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit)); + let widestLabelSize = 0; + let highestLabelSize = 0; + let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel; + for (i = 0; i < length; i += increment) { + label = ticks[i].label; + tickFont = this._resolveTickFontOptions(i); + ctx.font = fontString = tickFont.string; + cache = caches[fontString] = caches[fontString] || { + data: {}, + gc: [] + }; + lineHeight = tickFont.lineHeight; + width = height = 0; + if (!isNullOrUndef(label) && !isArray(label)) { + width = _measureText(ctx, cache.data, cache.gc, width, label); + height = lineHeight; + } else if (isArray(label)) { + for (j = 0, jlen = label.length; j < jlen; ++j) { + nestedLabel = label[j]; + if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) { + width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel); + height += lineHeight; + } + } + } + widths.push(width); + heights.push(height); + widestLabelSize = Math.max(width, widestLabelSize); + highestLabelSize = Math.max(height, highestLabelSize); + } + garbageCollect(caches, length); + const widest = widths.indexOf(widestLabelSize); + const highest = heights.indexOf(highestLabelSize); + const valueAt = (idx) => ({ + width: widths[idx] || 0, + height: heights[idx] || 0 + }); + return { + first: valueAt(0), + last: valueAt(length - 1), + widest: valueAt(widest), + highest: valueAt(highest), + widths, + heights + }; + } + getLabelForValue(value) { + return value; + } + getPixelForValue(value, index2) { + return NaN; + } + getValueForPixel(pixel) { + } + getPixelForTick(index2) { + const ticks = this.ticks; + if (index2 < 0 || index2 > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index2].value); + } + getPixelForDecimal(decimal) { + if (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, max } = this; + return min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0; + } + getContext(index2) { + const ticks = this.ticks || []; + if (index2 >= 0 && index2 < ticks.length) { + const tick = ticks[index2]; + return tick.$context || (tick.$context = createTickContext(this.getContext(), index2, tick)); + } + return this.$context || (this.$context = createScaleContext(this.chart.getContext(), this)); + } + _tickSize() { + const optionTicks = this.options.ticks; + const rot = toRadians(this.labelRotation); + const cos = Math.abs(Math.cos(rot)); + const sin = Math.abs(Math.sin(rot)); + const labelSizes = this._getLabelSizes(); + const padding = optionTicks.autoSkipPadding || 0; + const w = labelSizes ? labelSizes.widest.width + padding : 0; + const h = labelSizes ? labelSizes.highest.height + padding : 0; + return this.isHorizontal() ? h * cos > w * sin ? w / cos : h / sin : h * sin < w * cos ? h / cos : w / sin; + } + _isVisible() { + const display = this.options.display; + if (display !== "auto") { + return !!display; + } + return this.getMatchingVisibleMetas().length > 0; + } + _computeGridLineItems(chartArea) { + const axis = this.axis; + const chart = this.chart; + const options = this.options; + const { grid, position, border } = options; + const offset = grid.offset; + const isHorizontal = this.isHorizontal(); + const ticks = this.ticks; + const ticksLength = ticks.length + (offset ? 1 : 0); + const tl = getTickMarkLength(grid); + const items = []; + const borderOpts = border.setContext(this.getContext()); + const axisWidth = borderOpts.display ? borderOpts.width : 0; + const axisHalfWidth = axisWidth / 2; + const alignBorderValue = function(pixel) { + return _alignPixel(chart, pixel, axisWidth); + }; + let borderValue, i, lineValue, alignedLineValue; + let tx1, ty1, tx2, ty2, x1, y1, x2, y2; + if (position === "top") { + borderValue = alignBorderValue(this.bottom); + ty1 = this.bottom - tl; + ty2 = borderValue - axisHalfWidth; + y1 = alignBorderValue(chartArea.top) + axisHalfWidth; + y2 = chartArea.bottom; + } else if (position === "bottom") { + borderValue = alignBorderValue(this.top); + y1 = chartArea.top; + y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth; + ty1 = borderValue + axisHalfWidth; + ty2 = this.top + tl; + } else if (position === "left") { + borderValue = alignBorderValue(this.right); + tx1 = this.right - tl; + tx2 = borderValue - axisHalfWidth; + x1 = alignBorderValue(chartArea.left) + axisHalfWidth; + x2 = chartArea.right; + } else if (position === "right") { + borderValue = alignBorderValue(this.left); + x1 = chartArea.left; + x2 = alignBorderValue(chartArea.right) - axisHalfWidth; + tx1 = borderValue + axisHalfWidth; + tx2 = this.left + tl; + } else if (axis === "x") { + if (position === "center") { + borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5); + } else if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const 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 (axis === "y") { + if (position === "center") { + borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2); + } else if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const 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); + const step = Math.max(1, Math.ceil(ticksLength / limit)); + for (i = 0; i < ticksLength; i += step) { + const context = this.getContext(i); + const optsAtIndex = grid.setContext(context); + const optsAtIndexBorder = border.setContext(context); + const lineWidth = optsAtIndex.lineWidth; + const lineColor = optsAtIndex.color; + const borderDash = optsAtIndexBorder.dash || []; + const borderDashOffset = optsAtIndexBorder.dashOffset; + const tickWidth = optsAtIndex.tickWidth; + const tickColor = optsAtIndex.tickColor; + const tickBorderDash = optsAtIndex.tickBorderDash || []; + const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset; + lineValue = getPixelForGridLine(this, i, offset); + if (lineValue === void 0) { + continue; + } + alignedLineValue = _alignPixel(chart, lineValue, lineWidth); + if (isHorizontal) { + tx1 = tx2 = x1 = x2 = alignedLineValue; + } else { + ty1 = ty2 = y1 = y2 = alignedLineValue; + } + items.push({ + tx1, + ty1, + tx2, + ty2, + x1, + y1, + x2, + y2, + width: lineWidth, + color: lineColor, + borderDash, + borderDashOffset, + tickWidth, + tickColor, + tickBorderDash, + tickBorderDashOffset + }); + } + this._ticksLength = ticksLength; + this._borderValue = borderValue; + return items; + } + _computeLabelItems(chartArea) { + const axis = this.axis; + const options = this.options; + const { position, ticks: optionTicks } = options; + const isHorizontal = this.isHorizontal(); + const ticks = this.ticks; + const { align, crossAlign, padding, mirror } = optionTicks; + const tl = getTickMarkLength(options.grid); + const tickAndPadding = tl + padding; + const hTickAndPadding = mirror ? -padding : tickAndPadding; + const rotation = -toRadians(this.labelRotation); + const items = []; + let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset; + let textBaseline = "middle"; + if (position === "top") { + y = this.bottom - hTickAndPadding; + textAlign = this._getXAxisLabelAlignment(); + } else if (position === "bottom") { + y = this.top + hTickAndPadding; + textAlign = this._getXAxisLabelAlignment(); + } else if (position === "left") { + const ret = this._getYAxisLabelAlignment(tl); + textAlign = ret.textAlign; + x = ret.x; + } else if (position === "right") { + const ret = this._getYAxisLabelAlignment(tl); + textAlign = ret.textAlign; + x = ret.x; + } else if (axis === "x") { + if (position === "center") { + y = (chartArea.top + chartArea.bottom) / 2 + tickAndPadding; + } else if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const value = position[positionAxisID]; + y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding; + } + textAlign = this._getXAxisLabelAlignment(); + } else if (axis === "y") { + if (position === "center") { + x = (chartArea.left + chartArea.right) / 2 - tickAndPadding; + } else if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const value = position[positionAxisID]; + x = this.chart.scales[positionAxisID].getPixelForValue(value); + } + textAlign = this._getYAxisLabelAlignment(tl).textAlign; + } + if (axis === "y") { + if (align === "start") { + textBaseline = "top"; + } else if (align === "end") { + textBaseline = "bottom"; + } + } + const labelSizes = this._getLabelSizes(); + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + tick = ticks[i]; + label = tick.label; + const optsAtIndex = optionTicks.setContext(this.getContext(i)); + pixel = this.getPixelForTick(i) + optionTicks.labelOffset; + font = this._resolveTickFontOptions(i); + lineHeight = font.lineHeight; + lineCount = isArray(label) ? label.length : 1; + const halfCount = lineCount / 2; + const color2 = optsAtIndex.color; + const strokeColor = optsAtIndex.textStrokeColor; + const strokeWidth = optsAtIndex.textStrokeWidth; + let tickTextAlign = textAlign; + if (isHorizontal) { + x = pixel; + if (textAlign === "inner") { + if (i === ilen - 1) { + tickTextAlign = !this.options.reverse ? "right" : "left"; + } else if (i === 0) { + tickTextAlign = !this.options.reverse ? "left" : "right"; + } else { + tickTextAlign = "center"; + } + } + if (position === "top") { + if (crossAlign === "near" || rotation !== 0) { + textOffset = -lineCount * lineHeight + lineHeight / 2; + } else if (crossAlign === "center") { + textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight; + } else { + textOffset = -labelSizes.highest.height + lineHeight / 2; + } + } else { + if (crossAlign === "near" || rotation !== 0) { + textOffset = lineHeight / 2; + } else if (crossAlign === "center") { + textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight; + } else { + textOffset = labelSizes.highest.height - lineCount * lineHeight; + } + } + if (mirror) { + textOffset *= -1; + } + if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) { + x += lineHeight / 2 * Math.sin(rotation); + } + } else { + y = pixel; + textOffset = (1 - lineCount) * lineHeight / 2; + } + let backdrop; + if (optsAtIndex.showLabelBackdrop) { + const labelPadding = toPadding(optsAtIndex.backdropPadding); + const height = labelSizes.heights[i]; + const width = labelSizes.widths[i]; + let top = textOffset - labelPadding.top; + let left = 0 - labelPadding.left; + switch (textBaseline) { + case "middle": + top -= height / 2; + break; + case "bottom": + top -= height; + break; + } + switch (textAlign) { + case "center": + left -= width / 2; + break; + case "right": + left -= width; + break; + case "inner": + if (i === ilen - 1) { + left -= width; + } else if (i > 0) { + left -= width / 2; + } + break; + } + backdrop = { + left, + top, + width: width + labelPadding.width, + height: height + labelPadding.height, + color: optsAtIndex.backdropColor + }; + } + items.push({ + label, + font, + textOffset, + options: { + rotation, + color: color2, + strokeColor, + strokeWidth, + textAlign: tickTextAlign, + textBaseline, + translation: [ + x, + y + ], + backdrop + } + }); + } + return items; + } + _getXAxisLabelAlignment() { + const { position, ticks } = this.options; + const rotation = -toRadians(this.labelRotation); + if (rotation) { + return position === "top" ? "left" : "right"; + } + let align = "center"; + if (ticks.align === "start") { + align = "left"; + } else if (ticks.align === "end") { + align = "right"; + } else if (ticks.align === "inner") { + align = "inner"; + } + return align; + } + _getYAxisLabelAlignment(tl) { + const { position, ticks: { crossAlign, mirror, padding } } = this.options; + const labelSizes = this._getLabelSizes(); + const tickAndPadding = tl + padding; + const widest = labelSizes.widest.width; + let textAlign; + let x; + if (position === "left") { + if (mirror) { + x = this.right + padding; + if (crossAlign === "near") { + textAlign = "left"; + } else if (crossAlign === "center") { + textAlign = "center"; + x += widest / 2; + } else { + textAlign = "right"; + x += widest; + } + } else { + x = this.right - tickAndPadding; + if (crossAlign === "near") { + textAlign = "right"; + } else if (crossAlign === "center") { + textAlign = "center"; + x -= widest / 2; + } else { + textAlign = "left"; + x = this.left; + } + } + } else if (position === "right") { + if (mirror) { + x = this.left + padding; + if (crossAlign === "near") { + textAlign = "right"; + } else if (crossAlign === "center") { + textAlign = "center"; + x -= widest / 2; + } else { + textAlign = "left"; + x -= widest; + } + } else { + x = this.left + tickAndPadding; + if (crossAlign === "near") { + textAlign = "left"; + } else if (crossAlign === "center") { + textAlign = "center"; + x += widest / 2; + } else { + textAlign = "right"; + x = this.right; + } + } + } else { + textAlign = "right"; + } + return { + textAlign, + x + }; + } + _computeLabelArea() { + if (this.options.ticks.mirror) { + return; + } + const chart = this.chart; + const position = this.options.position; + if (position === "left" || position === "right") { + return { + top: 0, + left: this.left, + bottom: chart.height, + right: this.right + }; + } + if (position === "top" || position === "bottom") { + return { + top: this.top, + left: 0, + bottom: this.bottom, + right: chart.width + }; + } + } + drawBackground() { + const { ctx, options: { backgroundColor }, left, top, width, height } = this; + if (backgroundColor) { + ctx.save(); + ctx.fillStyle = backgroundColor; + ctx.fillRect(left, top, width, height); + ctx.restore(); + } + } + getLineWidthForValue(value) { + const grid = this.options.grid; + if (!this._isVisible() || !grid.display) { + return 0; + } + const ticks = this.ticks; + const index2 = ticks.findIndex((t) => t.value === value); + if (index2 >= 0) { + const opts = grid.setContext(this.getContext(index2)); + return opts.lineWidth; + } + return 0; + } + drawGrid(chartArea) { + const grid = this.options.grid; + const ctx = this.ctx; + const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea)); + let i, ilen; + const drawLine = (p1, p2, style) => { + if (!style.width || !style.color) { + return; + } + 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 < ilen; ++i) { + const item = items[i]; + if (grid.drawOnChartArea) { + drawLine({ + x: item.x1, + y: item.y1 + }, { + x: item.x2, + y: item.y2 + }, item); + } + if (grid.drawTicks) { + drawLine({ + x: item.tx1, + y: item.ty1 + }, { + x: item.tx2, + y: item.ty2 + }, { + color: item.tickColor, + width: item.tickWidth, + borderDash: item.tickBorderDash, + borderDashOffset: item.tickBorderDashOffset + }); + } + } + } + } + drawBorder() { + const { chart, ctx, options: { border, grid } } = this; + const borderOpts = border.setContext(this.getContext()); + const axisWidth = border.display ? borderOpts.width : 0; + if (!axisWidth) { + return; + } + const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth; + const borderValue = this._borderValue; + let x1, x2, y1, y2; + if (this.isHorizontal()) { + x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2; + x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2; + y1 = y2 = borderValue; + } else { + y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2; + y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2; + x1 = x2 = borderValue; + } + ctx.save(); + ctx.lineWidth = borderOpts.width; + ctx.strokeStyle = borderOpts.color; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + ctx.restore(); + } + drawLabels(chartArea) { + const optionTicks = this.options.ticks; + if (!optionTicks.display) { + return; + } + const ctx = this.ctx; + const area = this._computeLabelArea(); + if (area) { + clipArea(ctx, area); + } + const items = this.getLabelItems(chartArea); + for (const item of items) { + const renderTextOptions = item.options; + const tickFont = item.font; + const label = item.label; + const y = item.textOffset; + renderText(ctx, label, 0, y, tickFont, renderTextOptions); + } + if (area) { + unclipArea(ctx); + } + } + drawTitle() { + const { ctx, options: { position, title, reverse } } = this; + if (!title.display) { + return; + } + const font = toFont(title.font); + const padding = toPadding(title.padding); + const align = title.align; + let offset = font.lineHeight / 2; + if (position === "bottom" || position === "center" || isObject(position)) { + offset += padding.bottom; + if (isArray(title.text)) { + offset += font.lineHeight * (title.text.length - 1); + } + } else { + offset += padding.top; + } + const { titleX, titleY, maxWidth, rotation } = titleArgs(this, offset, position, align); + renderText(ctx, title.text, 0, 0, font, { + color: title.color, + maxWidth, + rotation, + textAlign: titleAlign(align, position, reverse), + textBaseline: "middle", + translation: [ + titleX, + titleY + ] + }); + } + draw(chartArea) { + if (!this._isVisible()) { + return; + } + this.drawBackground(); + this.drawGrid(chartArea); + this.drawBorder(); + this.drawTitle(); + this.drawLabels(chartArea); + } + _layers() { + const opts = this.options; + const tz = opts.ticks && opts.ticks.z || 0; + const gz = valueOrDefault(opts.grid && opts.grid.z, -1); + const bz = valueOrDefault(opts.border && opts.border.z, 0); + if (!this._isVisible() || this.draw !== Scale.prototype.draw) { + return [ + { + z: tz, + draw: (chartArea) => { + this.draw(chartArea); + } + } + ]; + } + return [ + { + z: gz, + draw: (chartArea) => { + this.drawBackground(); + this.drawGrid(chartArea); + this.drawTitle(); + } + }, + { + z: bz, + draw: () => { + this.drawBorder(); + } + }, + { + z: tz, + draw: (chartArea) => { + this.drawLabels(chartArea); + } + } + ]; + } + getMatchingVisibleMetas(type) { + const metas = this.chart.getSortedVisibleDatasetMetas(); + const axisID = this.axis + "AxisID"; + const result = []; + let i, ilen; + for (i = 0, ilen = metas.length; i < ilen; ++i) { + const meta = metas[i]; + if (meta[axisID] === this.id && (!type || meta.type === type)) { + result.push(meta); + } + } + return result; + } + _resolveTickFontOptions(index2) { + const opts = this.options.ticks.setContext(this.getContext(index2)); + return toFont(opts.font); + } + _maxDigits() { + const fontSize = this._resolveTickFontOptions(0).lineHeight; + return (this.isHorizontal() ? this.width : this.height) / fontSize; + } + } + class TypedRegistry { + constructor(type, scope, override) { + this.type = type; + this.scope = scope; + this.override = override; + this.items = /* @__PURE__ */ Object.create(null); + } + isForType(type) { + return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype); + } + register(item) { + const proto = Object.getPrototypeOf(item); + let parentScope; + if (isIChartComponent(proto)) { + parentScope = this.register(proto); + } + const items = this.items; + const id = item.id; + const scope = this.scope + "." + id; + if (!id) { + throw new Error("class does not have id: " + item); + } + if (id in items) { + return scope; + } + items[id] = item; + registerDefaults(item, scope, parentScope); + if (this.override) { + defaults.override(item.id, item.overrides); + } + return scope; + } + get(id) { + return this.items[id]; + } + unregister(item) { + const items = this.items; + const id = item.id; + const scope = this.scope; + if (id in items) { + delete items[id]; + } + if (scope && id in defaults[scope]) { + delete defaults[scope][id]; + if (this.override) { + delete overrides[id]; + } + } + } + } + function registerDefaults(item, scope, parentScope) { + const itemDefaults = merge(/* @__PURE__ */ Object.create(null), [ + parentScope ? defaults.get(parentScope) : {}, + defaults.get(scope), + item.defaults + ]); + defaults.set(scope, itemDefaults); + if (item.defaultRoutes) { + routeDefaults(scope, item.defaultRoutes); + } + if (item.descriptors) { + defaults.describe(scope, item.descriptors); + } + } + function routeDefaults(scope, routes) { + Object.keys(routes).forEach((property) => { + const propertyParts = property.split("."); + const sourceName = propertyParts.pop(); + const sourceScope = [ + scope + ].concat(propertyParts).join("."); + const parts = routes[property].split("."); + const targetName = parts.pop(); + const targetScope = parts.join("."); + defaults.route(sourceScope, sourceName, targetScope, targetName); + }); + } + function isIChartComponent(proto) { + return "id" in proto && "defaults" in proto; + } + class Registry { + constructor() { + this.controllers = new TypedRegistry(DatasetController, "datasets", true); + 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(...args) { + this._each("register", args); + } + remove(...args) { + this._each("unregister", args); + } + addControllers(...args) { + this._each("register", args, this.controllers); + } + addElements(...args) { + this._each("register", args, this.elements); + } + addPlugins(...args) { + this._each("register", args, this.plugins); + } + addScales(...args) { + 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(...args) { + this._each("unregister", args, this.controllers); + } + removeElements(...args) { + this._each("unregister", args, this.elements); + } + removePlugins(...args) { + this._each("unregister", args, this.plugins); + } + removeScales(...args) { + this._each("unregister", args, this.scales); + } + _each(method, args, typedRegistry) { + [ + ...args + ].forEach((arg) => { + const reg = typedRegistry || this._getRegistryForType(arg); + if (typedRegistry || reg.isForType(arg) || reg === this.plugins && arg.id) { + this._exec(method, reg, arg); + } else { + each(arg, (item) => { + const itemReg = typedRegistry || this._getRegistryForType(item); + this._exec(method, itemReg, item); + }); + } + }); + } + _exec(method, registry2, component) { + const camelMethod = _capitalize(method); + callback(component["before" + camelMethod], [], component); + registry2[method](component); + callback(component["after" + camelMethod], [], component); + } + _getRegistryForType(type) { + for (let i = 0; i < this._typedRegistries.length; i++) { + const reg = this._typedRegistries[i]; + if (reg.isForType(type)) { + return reg; + } + } + return this.plugins; + } + _get(id, typedRegistry, type) { + const item = typedRegistry.get(id); + if (item === void 0) { + throw new Error('"' + id + '" is not a registered ' + type + "."); + } + return item; + } + } + var registry = /* @__PURE__ */ new Registry(); + class PluginService { + constructor() { + this._init = void 0; + } + notify(chart, hook, args, filter) { + if (hook === "beforeInit") { + this._init = this._createDescriptors(chart, true); + this._notify(this._init, chart, "install"); + } + if (this._init === void 0) { + return; + } + const descriptors2 = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart); + const result = this._notify(descriptors2, chart, hook, args); + if (hook === "afterDestroy") { + this._notify(descriptors2, chart, "stop"); + this._notify(this._init, chart, "uninstall"); + this._init = void 0; + } + return result; + } + _notify(descriptors2, chart, hook, args) { + args = args || {}; + for (const descriptor of descriptors2) { + const plugin = descriptor.plugin; + const method = plugin[hook]; + const params = [ + chart, + args, + descriptor.options + ]; + if (callback(method, params, plugin) === false && args.cancelable) { + return false; + } + } + return true; + } + invalidate() { + if (!isNullOrUndef(this._cache)) { + this._oldCache = this._cache; + this._cache = void 0; + } + } + _descriptors(chart) { + if (this._cache) { + return this._cache; + } + const descriptors2 = this._cache = this._createDescriptors(chart); + this._notifyStateChanges(chart); + return descriptors2; + } + _createDescriptors(chart, all) { + const config = chart && chart.config; + const options = valueOrDefault(config.options && config.options.plugins, {}); + const plugins = allPlugins(config); + return options === false && !all ? [] : createDescriptors(chart, plugins, options, all); + } + _notifyStateChanges(chart) { + const previousDescriptors = this._oldCache || []; + const descriptors2 = this._cache; + const diff = (a, b) => a.filter((x) => !b.some((y) => x.plugin.id === y.plugin.id)); + this._notify(diff(previousDescriptors, descriptors2), chart, "stop"); + this._notify(diff(descriptors2, previousDescriptors), chart, "start"); + } + } + function allPlugins(config) { + const localIds = {}; + const plugins = []; + const keys = Object.keys(registry.plugins.items); + for (let i = 0; i < keys.length; i++) { + plugins.push(registry.getPlugin(keys[i])); + } + const local = config.plugins || []; + for (let i = 0; i < local.length; i++) { + const plugin = local[i]; + if (plugins.indexOf(plugin) === -1) { + plugins.push(plugin); + localIds[plugin.id] = true; + } + } + return { + plugins, + localIds + }; + } + function getOpts(options, all) { + if (!all && options === false) { + return null; + } + if (options === true) { + return {}; + } + return options; + } + function createDescriptors(chart, { plugins, localIds }, options, all) { + const result = []; + const context = chart.getContext(); + for (const plugin of plugins) { + const id = plugin.id; + const opts = getOpts(options[id], all); + if (opts === null) { + continue; + } + result.push({ + plugin, + options: pluginOpts(chart.config, { + plugin, + local: localIds[id] + }, opts, context) + }); + } + return result; + } + function pluginOpts(config, { plugin, local }, opts, context) { + const keys = config.pluginScopeKeys(plugin); + const scopes = config.getOptionScopes(opts, keys); + if (local && plugin.defaults) { + scopes.push(plugin.defaults); + } + return config.createResolver(scopes, context, [ + "" + ], { + scriptable: false, + indexable: false, + allKeys: true + }); + } + function getIndexAxis(type, options) { + const datasetDefaults = defaults.datasets[type] || {}; + const datasetOptions = (options.datasets || {})[type] || {}; + return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || "x"; + } + function getAxisFromDefaultScaleID(id, indexAxis) { + let axis = id; + if (id === "_index_") { + axis = indexAxis; + } else if (id === "_value_") { + axis = indexAxis === "x" ? "y" : "x"; + } + return axis; + } + function getDefaultScaleIDFromAxis(axis, indexAxis) { + return axis === indexAxis ? "_index_" : "_value_"; + } + function idMatchesAxis(id) { + if (id === "x" || id === "y" || id === "r") { + return id; + } + } + function axisFromPosition(position) { + if (position === "top" || position === "bottom") { + return "x"; + } + if (position === "left" || position === "right") { + return "y"; + } + } + function determineAxis(id, ...scaleOptions) { + if (idMatchesAxis(id)) { + return id; + } + for (const opts of scaleOptions) { + const axis = opts.axis || axisFromPosition(opts.position) || id.length > 1 && idMatchesAxis(id[0].toLowerCase()); + if (axis) { + return axis; + } + } + throw new Error(`Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.`); + } + function getAxisFromDataset(id, axis, dataset) { + if (dataset[axis + "AxisID"] === id) { + return { + axis + }; + } + } + function retrieveAxisFromDatasets(id, config) { + if (config.data && config.data.datasets) { + const boundDs = config.data.datasets.filter((d) => d.xAxisID === id || d.yAxisID === id); + if (boundDs.length) { + return getAxisFromDataset(id, "x", boundDs[0]) || getAxisFromDataset(id, "y", boundDs[0]); + } + } + return {}; + } + function mergeScaleConfig(config, options) { + const chartDefaults = overrides[config.type] || { + scales: {} + }; + const configScales = options.scales || {}; + const chartIndexAxis = getIndexAxis(config.type, options); + const scales = /* @__PURE__ */ Object.create(null); + Object.keys(configScales).forEach((id) => { + const scaleConf = configScales[id]; + if (!isObject(scaleConf)) { + return console.error(`Invalid scale configuration for scale: ${id}`); + } + if (scaleConf._proxy) { + return console.warn(`Ignoring resolver passed as options for scale: ${id}`); + } + const axis = determineAxis(id, scaleConf, retrieveAxisFromDatasets(id, config), defaults.scales[scaleConf.type]); + const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis); + const defaultScaleOptions = chartDefaults.scales || {}; + scales[id] = mergeIf(/* @__PURE__ */ Object.create(null), [ + { + axis + }, + scaleConf, + defaultScaleOptions[axis], + defaultScaleOptions[defaultId] + ]); + }); + config.data.datasets.forEach((dataset) => { + const type = dataset.type || config.type; + const indexAxis = dataset.indexAxis || getIndexAxis(type, options); + const datasetDefaults = overrides[type] || {}; + const defaultScaleOptions = datasetDefaults.scales || {}; + Object.keys(defaultScaleOptions).forEach((defaultID) => { + const axis = getAxisFromDefaultScaleID(defaultID, indexAxis); + const id = dataset[axis + "AxisID"] || axis; + scales[id] = scales[id] || /* @__PURE__ */ Object.create(null); + mergeIf(scales[id], [ + { + axis + }, + configScales[id], + defaultScaleOptions[defaultID] + ]); + }); + }); + Object.keys(scales).forEach((key) => { + const scale = scales[key]; + mergeIf(scale, [ + defaults.scales[scale.type], + defaults.scale + ]); + }); + return scales; + } + function initOptions(config) { + const options = config.options || (config.options = {}); + options.plugins = valueOrDefault(options.plugins, {}); + options.scales = mergeScaleConfig(config, options); + } + function initData(data) { + data = data || {}; + data.datasets = data.datasets || []; + data.labels = data.labels || []; + return data; + } + function initConfig(config) { + config = config || {}; + config.data = initData(config.data); + initOptions(config); + return config; + } + const keyCache = /* @__PURE__ */ new Map(); + const keysCached = /* @__PURE__ */ new Set(); + function cachedKeys(cacheKey, generate) { + let keys = keyCache.get(cacheKey); + if (!keys) { + keys = generate(); + keyCache.set(cacheKey, keys); + keysCached.add(keys); + } + return keys; + } + const addIfFound = (set2, obj, key) => { + const opts = resolveObjectKey(obj, key); + if (opts !== void 0) { + set2.add(opts); + } + }; + class Config { + constructor(config) { + this._config = initConfig(config); + this._scopeCache = /* @__PURE__ */ new Map(); + this._resolverCache = /* @__PURE__ */ 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.${datasetType}`, + "" + ] + ]); + } + datasetAnimationScopeKeys(datasetType, transition) { + return cachedKeys(`${datasetType}.transition.${transition}`, () => [ + [ + `datasets.${datasetType}.transitions.${transition}`, + `transitions.${transition}` + ], + [ + `datasets.${datasetType}`, + "" + ] + ]); + } + datasetElementScopeKeys(datasetType, elementType) { + return cachedKeys(`${datasetType}-${elementType}`, () => [ + [ + `datasets.${datasetType}.elements.${elementType}`, + `datasets.${datasetType}`, + `elements.${elementType}`, + "" + ] + ]); + } + pluginScopeKeys(plugin) { + const id = plugin.id; + const type = this.type; + return cachedKeys(`${type}-plugin-${id}`, () => [ + [ + `plugins.${id}`, + ...plugin.additionalOptionScopes || [] + ] + ]); + } + _cachedScopes(mainScope, resetCache) { + const _scopeCache = this._scopeCache; + let cache = _scopeCache.get(mainScope); + if (!cache || resetCache) { + cache = /* @__PURE__ */ new Map(); + _scopeCache.set(mainScope, cache); + } + return cache; + } + getOptionScopes(mainScope, keyLists, resetCache) { + const { options, type } = this; + const cache = this._cachedScopes(mainScope, resetCache); + const cached = cache.get(keyLists); + if (cached) { + return cached; + } + const scopes = /* @__PURE__ */ new Set(); + keyLists.forEach((keys) => { + if (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); + if (array.length === 0) { + array.push(/* @__PURE__ */ Object.create(null)); + } + if (keysCached.has(keyLists)) { + cache.set(keyLists, array); + } + return array; + } + chartOptionScopes() { + const { options, type } = this; + return [ + options, + overrides[type] || {}, + defaults.datasets[type] || {}, + { + type + }, + defaults, + descriptors + ]; + } + resolveNamedOptions(scopes, names2, context, prefixes = [ + "" + ]) { + const result = { + $shared: true + }; + const { resolver, subPrefixes } = getResolver(this._resolverCache, scopes, prefixes); + let options = resolver; + if (needContext(resolver, names2)) { + result.$shared = false; + context = isFunction(context) ? context() : context; + const subResolver = this.createResolver(scopes, context, subPrefixes); + options = _attachContext(resolver, context, subResolver); + } + for (const prop of names2) { + result[prop] = options[prop]; + } + return result; + } + createResolver(scopes, context, prefixes = [ + "" + ], descriptorDefaults) { + const { 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); + if (!cache) { + cache = /* @__PURE__ */ new Map(); + resolverCache.set(scopes, cache); + } + const cacheKey = prefixes.join(); + let cached = cache.get(cacheKey); + if (!cached) { + const resolver = _createResolver(scopes, prefixes); + cached = { + resolver, + subPrefixes: prefixes.filter((p) => !p.toLowerCase().includes("hover")) + }; + cache.set(cacheKey, cached); + } + return cached; + } + const hasFunction = (value) => isObject(value) && Object.getOwnPropertyNames(value).some((key) => isFunction(value[key])); + function needContext(proxy, names2) { + const { isScriptable, isIndexable } = _descriptors(proxy); + for (const prop of names2) { + const scriptable = isScriptable(prop); + const indexable = isIndexable(prop); + const value = (indexable || scriptable) && proxy[prop]; + if (scriptable && (isFunction(value) || hasFunction(value)) || indexable && isArray(value)) { + return true; + } + } + return false; + } + var version = "4.5.1"; + const KNOWN_POSITIONS = [ + "top", + "bottom", + "left", + "right", + "chartArea" + ]; + function positionIsHorizontal(position, axis) { + return position === "top" || position === "bottom" || KNOWN_POSITIONS.indexOf(position) === -1 && axis === "x"; + } + 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; + const animationOptions = chart.options.animation; + chart.notifyPlugins("afterRender"); + callback(animationOptions && animationOptions.onComplete, [ + context + ], chart); + } + function onAnimationProgress(context) { + const chart = context.chart; + const animationOptions = chart.options.animation; + callback(animationOptions && animationOptions.onProgress, [ + context + ], chart); + } + function getCanvas(item) { + if (_isDomSupported() && typeof item === "string") { + item = document.getElementById(item); + } else if (item && item.length) { + item = item[0]; + } + if (item && item.canvas) { + item = item.canvas; + } + return item; + } + const instances = {}; + const 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]; + if (move > 0 || intKey > start) { + obj[intKey + move] = value; + } + } + } + } + function determineLastEvent(e, lastEvent, inChartArea, isClick) { + if (!inChartArea || e.type === "mouseout") { + return null; + } + if (isClick) { + return lastEvent; + } + return e; + } + class Chart { + static register(...items) { + registry.add(...items); + invalidatePlugins(); + } + static unregister(...items) { + registry.remove(...items); + invalidatePlugins(); + } + constructor(item, userConfig) { + const config = this.config = new Config(userConfig); + const initialCanvas = getCanvas(item); + const existingChart = getChart(initialCanvas); + if (existingChart) { + throw new Error("Canvas is already in use. Chart with ID '" + existingChart.id + "' must be destroyed before the canvas with ID '" + existingChart.canvas.id + "' 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); + const canvas = context && context.canvas; + const height = canvas && canvas.height; + const 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 = false; + this._animationsDisabled = void 0; + this.$context = void 0; + this._doResize = debounce((mode) => this.update(mode), options.resizeDelay || 0); + this._dataChanges = []; + instances[this.id] = this; + if (!context || !canvas) { + console.error("Failed to create chart: can't acquire context from the given item"); + return; + } + animator.listen(this, "complete", onAnimationsComplete); + animator.listen(this, "progress", onAnimationProgress); + this._initialize(); + if (this.attached) { + this.update(); + } + } + get aspectRatio() { + const { options: { aspectRatio, maintainAspectRatio }, width, height, _aspectRatio } = this; + if (!isNullOrUndef(aspectRatio)) { + return aspectRatio; + } + if (maintainAspectRatio && _aspectRatio) { + return _aspectRatio; + } + return height ? width / height : null; + } + 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; + } + get registry() { + return registry; + } + _initialize() { + this.notifyPlugins("beforeInit"); + if (this.options.responsive) { + this.resize(); + } else { + retinaScale(this, this.options.devicePixelRatio); + } + this.bindEvents(); + this.notifyPlugins("afterInit"); + return this; + } + clear() { + clearCanvas(this.canvas, this.ctx); + return this; + } + stop() { + animator.stop(this); + return this; + } + resize(width, height) { + if (!animator.running(this)) { + this._resize(width, height); + } else { + this._resizeBeforeDraw = { + width, + height + }; + } + } + _resize(width, height) { + const options = this.options; + const canvas = this.canvas; + const aspectRatio = options.maintainAspectRatio && this.aspectRatio; + const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio); + const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio(); + const mode = this.width ? "resize" : "attach"; + this.width = newSize.width; + this.height = newSize.height; + this._aspectRatio = this.aspectRatio; + if (!retinaScale(this, newRatio, true)) { + return; + } + this.notifyPlugins("resize", { + size: newSize + }); + callback(options.onResize, [ + this, + newSize + ], this); + if (this.attached) { + if (this._doResize(mode)) { + this.render(); + } + } + } + ensureScalesHaveIDs() { + const options = this.options; + const scalesOptions = options.scales || {}; + each(scalesOptions, (axisOptions, axisID) => { + axisOptions.id = axisID; + }); + } + buildOrUpdateScales() { + const options = this.options; + const scaleOpts = options.scales; + const scales = this.scales; + const updated = Object.keys(scales).reduce((obj, id) => { + obj[id] = false; + return obj; + }, {}); + let items = []; + if (scaleOpts) { + items = items.concat(Object.keys(scaleOpts).map((id) => { + const scaleOptions = scaleOpts[id]; + const axis = determineAxis(id, scaleOptions); + const isRadial = axis === "r"; + const isHorizontal = axis === "x"; + return { + options: scaleOptions, + dposition: isRadial ? "chartArea" : isHorizontal ? "bottom" : "left", + dtype: isRadial ? "radialLinear" : isHorizontal ? "category" : "linear" + }; + })); + } + each(items, (item) => { + const scaleOptions = item.options; + const id = scaleOptions.id; + const axis = determineAxis(id, scaleOptions); + const scaleType = valueOrDefault(scaleOptions.type, item.dtype); + if (scaleOptions.position === void 0 || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) { + scaleOptions.position = item.dposition; + } + updated[id] = true; + let scale = null; + if (id in scales && scales[id].type === scaleType) { + scale = scales[id]; + } else { + const scaleClass = registry.getScale(scaleType); + scale = new scaleClass({ + id, + type: scaleType, + ctx: this.ctx, + chart: this + }); + scales[scale.id] = scale; + } + scale.init(scaleOptions, options); + }); + each(updated, (hasUpdated, id) => { + if (!hasUpdated) { + delete scales[id]; + } + }); + each(scales, (scale) => { + layouts.configure(this, scale, scale.options); + layouts.addBox(this, scale); + }); + } + _updateMetasets() { + const metasets = this._metasets; + const numData = this.data.datasets.length; + const numMeta = metasets.length; + metasets.sort((a, b) => a.index - b.index); + if (numMeta > numData) { + for (let i = numData; i < numMeta; ++i) { + this._destroyDatasetMeta(i); + } + metasets.splice(numData, numMeta - numData); + } + this._sortedMetasets = metasets.slice(0).sort(compare2Level("order", "index")); + } + _removeUnreferencedMetasets() { + const { _metasets: metasets, data: { datasets } } = this; + if (metasets.length > datasets.length) { + delete this._stacks; + } + metasets.forEach((meta, index2) => { + if (datasets.filter((x) => x === meta._dataset).length === 0) { + this._destroyDatasetMeta(index2); + } + }); + } + buildOrUpdateControllers() { + const newControllers = []; + const datasets = this.data.datasets; + let i, ilen; + this._removeUnreferencedMetasets(); + for (i = 0, ilen = datasets.length; i < ilen; i++) { + const dataset = datasets[i]; + let meta = this.getDatasetMeta(i); + const type = dataset.type || this.config.type; + if (meta.type && meta.type !== type) { + this._destroyDatasetMeta(i); + meta = this.getDatasetMeta(i); + } + meta.type = type; + meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options); + meta.order = dataset.order || 0; + meta.index = i; + meta.label = "" + dataset.label; + meta.visible = this.isDatasetVisible(i); + if (meta.controller) { + meta.controller.updateIndex(i); + meta.controller.linkScales(); + } else { + const ControllerClass = registry.getController(type); + const { datasetElementType, dataElementType } = defaults.datasets[type]; + Object.assign(ControllerClass, { + dataElementType: registry.getElement(dataElementType), + datasetElementType: datasetElementType && registry.getElement(datasetElementType) + }); + meta.controller = new ControllerClass(this, i); + newControllers.push(meta.controller); + } + } + this._updateMetasets(); + return newControllers; + } + _resetElements() { + each(this.data.datasets, (dataset, datasetIndex) => { + 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()); + const animsDisabled = this._animationsDisabled = !options.animation; + this._updateScales(); + this._checkEventBindings(); + this._updateHiddenIndices(); + this._plugins.invalidate(); + if (this.notifyPlugins("beforeUpdate", { + mode, + cancelable: true + }) === false) { + return; + } + const newControllers = this.buildOrUpdateControllers(); + this.notifyPlugins("beforeElementsUpdate"); + let minPadding = 0; + for (let i = 0, ilen = this.data.datasets.length; i < ilen; i++) { + const { controller } = this.getDatasetMeta(i); + const reset = !animsDisabled && newControllers.indexOf(controller) === -1; + controller.buildOrUpdateElements(reset); + minPadding = Math.max(+controller.getMaxOverflow(), minPadding); + } + minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0; + this._updateLayout(minPadding); + if (!animsDisabled) { + each(newControllers, (controller) => { + controller.reset(); + }); + } + this._updateDatasets(mode); + this.notifyPlugins("afterUpdate", { + mode + }); + this._layers.sort(compare2Level("z", "_idx")); + const { _active, _lastEvent } = this; + if (_lastEvent) { + this._eventHandler(_lastEvent, true); + } else if (_active.length) { + this._updateHoverStyles(_active, _active, true); + } + this.render(); + } + _updateScales() { + each(this.scales, (scale) => { + layouts.removeBox(this, scale); + }); + this.ensureScalesHaveIDs(); + this.buildOrUpdateScales(); + } + _checkEventBindings() { + const options = this.options; + const existingEvents = new Set(Object.keys(this._listeners)); + const newEvents = new Set(options.events); + if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) { + this.unbindEvents(); + this.bindEvents(); + } + } + _updateHiddenIndices() { + const { _hiddenIndices } = this; + const changes = this._getUniformDataChanges() || []; + for (const { method, start, count } of changes) { + const move = method === "_removeElements" ? -count : count; + moveNumericKeys(_hiddenIndices, start, move); + } + } + _getUniformDataChanges() { + const _dataChanges = this._dataChanges; + if (!_dataChanges || !_dataChanges.length) { + return; + } + this._dataChanges = []; + const datasetCount = this.data.datasets.length; + const makeSet = (idx) => new Set(_dataChanges.filter((c) => c[0] === idx).map((c, i) => i + "," + c.splice(1).join(","))); + const changeSet = makeSet(0); + for (let i = 1; i < datasetCount; i++) { + if (!setsEqual(changeSet, makeSet(i))) { + return; + } + } + return Array.from(changeSet).map((c) => c.split(",")).map((a) => ({ + method: a[1], + start: +a[2], + count: +a[3] + })); + } + _updateLayout(minPadding) { + if (this.notifyPlugins("beforeLayout", { + cancelable: true + }) === false) { + return; + } + layouts.update(this, this.width, this.height, minPadding); + const area = this.chartArea; + const noArea = area.width <= 0 || area.height <= 0; + this._layers = []; + each(this.boxes, (box) => { + if (noArea && box.position === "chartArea") { + return; + } + if (box.configure) { + box.configure(); + } + this._layers.push(...box._layers()); + }, this); + this._layers.forEach((item, index2) => { + item._idx = index2; + }); + this.notifyPlugins("afterLayout"); + } + _updateDatasets(mode) { + if (this.notifyPlugins("beforeDatasetsUpdate", { + mode, + cancelable: true + }) === false) { + return; + } + for (let i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { + this.getDatasetMeta(i).controller.configure(); + } + for (let i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { + this._updateDataset(i, isFunction(mode) ? mode({ + datasetIndex: i + }) : mode); + } + this.notifyPlugins("afterDatasetsUpdate", { + mode + }); + } + _updateDataset(index2, mode) { + const meta = this.getDatasetMeta(index2); + const args = { + meta, + index: index2, + mode, + cancelable: true + }; + if (this.notifyPlugins("beforeDatasetUpdate", args) === false) { + return; + } + meta.controller._update(mode); + args.cancelable = false; + this.notifyPlugins("afterDatasetUpdate", args); + } + render() { + if (this.notifyPlugins("beforeRender", { + cancelable: true + }) === false) { + return; + } + if (animator.has(this)) { + if (this.attached && !animator.running(this)) { + animator.start(this); + } + } else { + this.draw(); + onAnimationsComplete({ + chart: this + }); + } + } + draw() { + let i; + if (this._resizeBeforeDraw) { + const { width, height } = this._resizeBeforeDraw; + this._resizeBeforeDraw = null; + this._resize(width, height); + } + this.clear(); + if (this.width <= 0 || this.height <= 0) { + return; + } + if (this.notifyPlugins("beforeDraw", { + cancelable: true + }) === false) { + return; + } + const layers = this._layers; + for (i = 0; i < layers.length && layers[i].z <= 0; ++i) { + layers[i].draw(this.chartArea); + } + this._drawDatasets(); + for (; i < layers.length; ++i) { + layers[i].draw(this.chartArea); + } + this.notifyPlugins("afterDraw"); + } + _getSortedDatasetMetas(filterVisible) { + const metasets = this._sortedMetasets; + const result = []; + let i, ilen; + for (i = 0, ilen = metasets.length; i < ilen; ++i) { + const meta = metasets[i]; + if (!filterVisible || meta.visible) { + result.push(meta); + } + } + return result; + } + getSortedVisibleDatasetMetas() { + return this._getSortedDatasetMetas(true); + } + _drawDatasets() { + if (this.notifyPlugins("beforeDatasetsDraw", { + cancelable: true + }) === false) { + return; + } + const metasets = this.getSortedVisibleDatasetMetas(); + for (let i = metasets.length - 1; i >= 0; --i) { + this._drawDataset(metasets[i]); + } + this.notifyPlugins("afterDatasetsDraw"); + } + _drawDataset(meta) { + const ctx = this.ctx; + const args = { + meta, + index: meta.index, + cancelable: true + }; + const clip = getDatasetClipArea(this, meta); + if (this.notifyPlugins("beforeDatasetDraw", args) === false) { + return; + } + if (clip) { + clipArea(ctx, clip); + } + meta.controller.draw(); + if (clip) { + unclipArea(ctx); + } + args.cancelable = false; + this.notifyPlugins("afterDatasetDraw", args); + } + isPointInArea(point) { + return _isPointInArea(point, this.chartArea, this._minPadding); + } + getElementsAtEventForMode(e, mode, options, useFinalPosition) { + const method = Interaction.modes[mode]; + if (typeof method === "function") { + return method(this, e, options, useFinalPosition); + } + return []; + } + getDatasetMeta(datasetIndex) { + const dataset = this.data.datasets[datasetIndex]; + const metasets = this._metasets; + let meta = metasets.filter((x) => x && x._dataset === dataset).pop(); + if (!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: false + }; + metasets.push(meta); + } + return 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 false; + } + const meta = this.getDatasetMeta(datasetIndex); + return typeof meta.hidden === "boolean" ? !meta.hidden : !dataset.hidden; + } + setDatasetVisibility(datasetIndex, visible) { + const meta = this.getDatasetMeta(datasetIndex); + meta.hidden = !visible; + } + toggleDataVisibility(index2) { + this._hiddenIndices[index2] = !this._hiddenIndices[index2]; + } + getDataVisibility(index2) { + return !this._hiddenIndices[index2]; + } + _updateVisibility(datasetIndex, dataIndex, visible) { + const mode = visible ? "show" : "hide"; + const meta = this.getDatasetMeta(datasetIndex); + const anims = meta.controller._resolveAnimations(void 0, mode); + if (defined(dataIndex)) { + meta.data[dataIndex].hidden = !visible; + this.update(); + } else { + this.setDatasetVisibility(datasetIndex, visible); + anims.update(meta, { + visible + }); + this.update((ctx) => ctx.datasetIndex === datasetIndex ? mode : void 0); + } + } + hide(datasetIndex, dataIndex) { + this._updateVisibility(datasetIndex, dataIndex, false); + } + show(datasetIndex, dataIndex) { + this._updateVisibility(datasetIndex, dataIndex, true); + } + _destroyDatasetMeta(datasetIndex) { + const meta = this._metasets[datasetIndex]; + if (meta && meta.controller) { + meta.controller._destroy(); + } + delete this._metasets[datasetIndex]; + } + _stop() { + let i, ilen; + this.stop(); + animator.remove(this); + for (i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { + this._destroyDatasetMeta(i); + } + } + destroy() { + this.notifyPlugins("beforeDestroy"); + const { canvas, ctx } = this; + this._stop(); + this.config.clearCache(); + if (canvas) { + this.unbindEvents(); + clearCanvas(canvas, ctx); + this.platform.releaseContext(ctx); + this.canvas = null; + this.ctx = null; + } + delete instances[this.id]; + this.notifyPlugins("afterDestroy"); + } + toBase64Image(...args) { + return this.canvas.toDataURL(...args); + } + bindEvents() { + this.bindUserEvents(); + if (this.options.responsive) { + this.bindResponsiveEvents(); + } else { + this.attached = true; + } + } + bindUserEvents() { + const listeners = this._listeners; + const platform = this.platform; + const _add = (type, listener2) => { + platform.addEventListener(this, type, listener2); + listeners[type] = listener2; + }; + const listener = (e, x, y) => { + e.offsetX = x; + e.offsetY = y; + this._eventHandler(e); + }; + each(this.options.events, (type) => _add(type, listener)); + } + bindResponsiveEvents() { + if (!this._responsiveListeners) { + this._responsiveListeners = {}; + } + const listeners = this._responsiveListeners; + const platform = this.platform; + const _add = (type, listener2) => { + platform.addEventListener(this, type, listener2); + listeners[type] = listener2; + }; + const _remove = (type, listener2) => { + if (listeners[type]) { + platform.removeEventListener(this, type, listener2); + delete listeners[type]; + } + }; + const listener = (width, height) => { + if (this.canvas) { + this.resize(width, height); + } + }; + let detached; + const attached = () => { + _remove("attach", attached); + this.attached = true; + this.resize(); + _add("resize", listener); + _add("detach", detached); + }; + detached = () => { + this.attached = false; + _remove("resize", listener); + this._stop(); + this._resize(0, 0); + _add("attach", attached); + }; + if (platform.isAttached(this.canvas)) { + attached(); + } else { + 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; + if (mode === "dataset") { + meta = this.getDatasetMeta(items[0].datasetIndex); + meta.controller["_" + prefix + "DatasetHoverStyle"](); + } + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + const controller = item && this.getDatasetMeta(item.datasetIndex).controller; + if (controller) { + controller[prefix + "HoverStyle"](item.element, item.datasetIndex, item.index); + } + } + } + getActiveElements() { + return this._active || []; + } + setActiveElements(activeElements) { + const lastActive = this._active || []; + const active = activeElements.map(({ datasetIndex, index: index2 }) => { + const meta = this.getDatasetMeta(datasetIndex); + if (!meta) { + throw new Error("No dataset found at index " + datasetIndex); + } + return { + datasetIndex, + element: meta.data[index2], + index: index2 + }; + }); + const changed = !_elementsEqual(active, lastActive); + if (changed) { + this._active = active; + this._lastEvent = null; + this._updateHoverStyles(active, lastActive); + } + } + notifyPlugins(hook, args, filter) { + return this._plugins.notify(this, hook, args, filter); + } + isPluginEnabled(pluginId) { + return this._plugins._cache.filter((p) => p.plugin.id === pluginId).length === 1; + } + _updateHoverStyles(active, lastActive, replay) { + const hoverOptions = this.options.hover; + const diff = (a, b) => a.filter((x) => !b.some((y) => x.datasetIndex === y.datasetIndex && x.index === y.index)); + const deactivated = diff(lastActive, active); + const activated = replay ? active : diff(active, lastActive); + if (deactivated.length) { + this.updateHoverStyle(deactivated, hoverOptions.mode, false); + } + if (activated.length && hoverOptions.mode) { + this.updateHoverStyle(activated, hoverOptions.mode, true); + } + } + _eventHandler(e, replay) { + const args = { + event: e, + replay, + cancelable: true, + inChartArea: this.isPointInArea(e) + }; + const eventFilter = (plugin) => (plugin.options.events || this.options.events).includes(e.native.type); + if (this.notifyPlugins("beforeEvent", args, eventFilter) === false) { + return; + } + const changed = this._handleEvent(e, replay, args.inChartArea); + args.cancelable = false; + this.notifyPlugins("afterEvent", args, eventFilter); + if (changed || args.changed) { + this.render(); + } + return this; + } + _handleEvent(e, replay, inChartArea) { + const { _active: lastActive = [], options } = this; + const useFinalPosition = replay; + const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition); + const isClick = _isClickEvent(e); + const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick); + if (inChartArea) { + this._lastEvent = null; + callback(options.onHover, [ + e, + active, + this + ], this); + if (isClick) { + callback(options.onClick, [ + e, + active, + this + ], this); + } + } + const changed = !_elementsEqual(active, lastActive); + if (changed || replay) { + this._active = active; + this._updateHoverStyles(active, lastActive, replay); + } + this._lastEvent = lastEvent; + return changed; + } + _getActiveElements(e, lastActive, inChartArea, useFinalPosition) { + if (e.type === "mouseout") { + return []; + } + if (!inChartArea) { + return lastActive; + } + const hoverOptions = this.options.hover; + return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition); + } + } + __publicField(Chart, "defaults", defaults); + __publicField(Chart, "instances", instances); + __publicField(Chart, "overrides", overrides); + __publicField(Chart, "registry", registry); + __publicField(Chart, "version", version); + __publicField(Chart, "getChart", getChart); + function invalidatePlugins() { + return each(Chart.instances, (chart) => chart._plugins.invalidate()); + } + function setStyle(ctx, options, style = 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 getLineMethod(options) { + if (options.stepped) { + return _steppedLineTo; + } + if (options.tension || options.cubicInterpolationMode === "monotone") { + return _bezierCurveTo; + } + return lineTo; + } + function pathVars(points, segment, params = {}) { + const count = points.length; + const { start: paramsStart = 0, end: paramsEnd = count - 1 } = params; + const { start: segmentStart, end: segmentEnd } = segment; + const start = Math.max(paramsStart, segmentStart); + const end = Math.min(paramsEnd, segmentEnd); + const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd; + return { + count, + start, + loop: segment.loop, + ilen: end < start && !outside ? count + end - start : end - start + }; + } + function pathSegment(ctx, line, segment, params) { + const { points, options } = line; + const { count, start, loop, ilen } = pathVars(points, segment, params); + const lineMethod = getLineMethod(options); + let { move = true, reverse } = params || {}; + let i, point, prev; + for (i = 0; i <= ilen; ++i) { + point = points[(start + (reverse ? ilen - i : i)) % count]; + if (point.skip) { + continue; + } else if (move) { + ctx.moveTo(point.x, point.y); + move = false; + } else { + lineMethod(ctx, prev, point, reverse, options.stepped); + } + prev = point; + } + if (loop) { + point = points[(start + (reverse ? ilen : 0)) % count]; + lineMethod(ctx, prev, point, reverse, options.stepped); + } + return !!loop; + } + function fastPathSegment(ctx, line, segment, params) { + const points = line.points; + const { count, start, ilen } = pathVars(points, segment, params); + const { move = true, reverse } = params || {}; + let avgX = 0; + let countX = 0; + let i, point, prevX, minY, maxY, lastY; + const pointIndex = (index2) => (start + (reverse ? ilen - index2 : index2)) % count; + const drawX = () => { + if (minY !== maxY) { + ctx.lineTo(avgX, maxY); + ctx.lineTo(avgX, minY); + ctx.lineTo(avgX, lastY); + } + }; + if (move) { + point = points[pointIndex(0)]; + ctx.moveTo(point.x, point.y); + } + for (i = 0; i <= ilen; ++i) { + point = points[pointIndex(i)]; + if (point.skip) { + continue; + } + const x = point.x; + const y = point.y; + const truncX = x | 0; + if (truncX === prevX) { + if (y < minY) { + minY = y; + } else if (y > maxY) { + maxY = y; + } + avgX = (countX * avgX + x) / ++countX; + } else { + drawX(); + ctx.lineTo(x, y); + prevX = truncX; + countX = 0; + minY = maxY = y; + } + lastY = y; + } + drawX(); + } + function _getSegmentMethod(line) { + const opts = line.options; + const borderDash = opts.borderDash && opts.borderDash.length; + const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== "monotone" && !opts.stepped && !borderDash; + return useFastPath ? fastPathSegment : pathSegment; + } + function _getInterpolationMethod(options) { + if (options.stepped) { + return _steppedInterpolation; + } + if (options.tension || options.cubicInterpolationMode === "monotone") { + return _bezierInterpolation; + } + return _pointInLine; + } + function strokePathWithCache(ctx, line, start, count) { + let path = line._path; + if (!path) { + path = line._path = new Path2D(); + if (line.path(path, start, count)) { + path.closePath(); + } + } + setStyle(ctx, line.options); + ctx.stroke(path); + } + function strokePathDirect(ctx, line, start, count) { + const { segments, options } = line; + const segmentMethod = _getSegmentMethod(line); + for (const segment of segments) { + setStyle(ctx, options, segment.style); + ctx.beginPath(); + if (segmentMethod(ctx, line, segment, { + start, + end: start + count - 1 + })) { + ctx.closePath(); + } + ctx.stroke(); + } + } + const usePath2D = typeof Path2D === "function"; + function draw(ctx, line, start, count) { + if (usePath2D && !line.options.segment) { + strokePathWithCache(ctx, line, start, count); + } else { + strokePathDirect(ctx, line, start, count); + } + } + class LineElement extends Element { + constructor(cfg) { + super(); + this.animated = true; + 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 = false; + this._pointsUpdated = false; + this._datasetIndex = void 0; + if (cfg) { + Object.assign(this, cfg); + } + } + updateControlPoints(chartArea, indexAxis) { + const options = this.options; + if ((options.tension || options.cubicInterpolationMode === "monotone") && !options.stepped && !this._pointsUpdated) { + const loop = options.spanGaps ? this._loop : this._fullLoop; + _updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis); + this._pointsUpdated = true; + } + } + set points(points) { + this._points = points; + delete this._segments; + delete this._path; + this._pointsUpdated = false; + } + get points() { + return this._points; + } + get segments() { + return this._segments || (this._segments = _computeSegments(this, this.options.segment)); + } + first() { + const segments = this.segments; + const points = this.points; + return segments.length && points[segments[0].start]; + } + last() { + const segments = this.segments; + const points = this.points; + const count = segments.length; + return count && points[segments[count - 1].end]; + } + interpolate(point, property) { + const options = this.options; + const value = point[property]; + const points = this.points; + const segments = _boundSegments(this, { + property, + start: value, + end: value + }); + if (!segments.length) { + return; + } + const result = []; + const _interpolate = _getInterpolationMethod(options); + let i, ilen; + for (i = 0, ilen = segments.length; i < ilen; ++i) { + const { start, end } = segments[i]; + const p1 = points[start]; + const p2 = points[end]; + if (p1 === p2) { + result.push(p1); + continue; + } + const t = Math.abs((value - p1[property]) / (p2[property] - p1[property])); + const interpolated = _interpolate(p1, p2, t, options.stepped); + interpolated[property] = point[property]; + result.push(interpolated); + } + return result.length === 1 ? result[0] : result; + } + pathSegment(ctx, segment, params) { + const segmentMethod = _getSegmentMethod(this); + return segmentMethod(ctx, this, segment, params); + } + path(ctx, start, count) { + const segments = this.segments; + const segmentMethod = _getSegmentMethod(this); + let loop = this._loop; + start = start || 0; + count = count || this.points.length - start; + for (const segment of segments) { + loop &= segmentMethod(ctx, this, segment, { + start, + end: start + count - 1 + }); + } + return !!loop; + } + draw(ctx, chartArea, start, count) { + const options = this.options || {}; + const points = this.points || []; + if (points.length && options.borderWidth) { + ctx.save(); + draw(ctx, this, start, count); + ctx.restore(); + } + if (this.animated) { + this._pointsUpdated = false; + this._path = void 0; + } + } + } + __publicField(LineElement, "id", "line"); + __publicField(LineElement, "defaults", { + borderCapStyle: "butt", + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: "miter", + borderWidth: 3, + capBezierPoints: true, + cubicInterpolationMode: "default", + fill: false, + spanGaps: false, + stepped: false, + tension: 0 + }); + __publicField(LineElement, "defaultRoutes", { + backgroundColor: "backgroundColor", + borderColor: "borderColor" + }); + __publicField(LineElement, "descriptors", { + _scriptable: true, + _indexable: (name) => name !== "borderDash" && name !== "fill" + }); + function inRange$1(el, pos, axis, useFinalPosition) { + const options = el.options; + const { [axis]: value } = el.getProps([ + axis + ], useFinalPosition); + return Math.abs(pos - value) < options.radius + options.hitRadius; + } + class PointElement extends Element { + constructor(cfg) { + super(); + __publicField(this, "parsed"); + __publicField(this, "skip"); + __publicField(this, "stop"); + this.options = void 0; + this.parsed = void 0; + this.skip = void 0; + this.stop = void 0; + if (cfg) { + Object.assign(this, cfg); + } + } + inRange(mouseX, mouseY, useFinalPosition) { + const options = this.options; + const { x, y } = this.getProps([ + "x", + "y" + ], useFinalPosition); + return Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2) < Math.pow(options.hitRadius + options.radius, 2); + } + inXRange(mouseX, useFinalPosition) { + return inRange$1(this, mouseX, "x", useFinalPosition); + } + inYRange(mouseY, useFinalPosition) { + return inRange$1(this, mouseY, "y", useFinalPosition); + } + getCenterPoint(useFinalPosition) { + const { x, y } = this.getProps([ + "x", + "y" + ], useFinalPosition); + return { + x, + y + }; + } + size(options) { + options = options || this.options || {}; + let radius = options.radius || 0; + radius = Math.max(radius, radius && options.hoverRadius || 0); + const borderWidth = radius && options.borderWidth || 0; + return (radius + borderWidth) * 2; + } + draw(ctx, area) { + const options = this.options; + if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) { + return; + } + ctx.strokeStyle = options.borderColor; + ctx.lineWidth = options.borderWidth; + ctx.fillStyle = options.backgroundColor; + drawPoint(ctx, options, this.x, this.y); + } + getRange() { + const options = this.options || {}; + return options.radius + options.hitRadius; + } + } + __publicField(PointElement, "id", "point"); + /** + * @type {any} + */ + __publicField(PointElement, "defaults", { + borderWidth: 1, + hitRadius: 1, + hoverBorderWidth: 1, + hoverRadius: 4, + pointStyle: "circle", + radius: 3, + rotation: 0 + }); + /** + * @type {any} + */ + __publicField(PointElement, "defaultRoutes", { + backgroundColor: "backgroundColor", + borderColor: "borderColor" + }); + function _segments(line, target, property) { + const segments = line.segments; + const points = line.points; + const tpoints = target.points; + const parts = []; + for (const segment of segments) { + let { start, end } = segment; + end = _findSegmentEnd(start, end, points); + const bounds = _getBounds(property, points[start], points[end], segment.loop); + if (!target.segments) { + parts.push({ + source: segment, + target: bounds, + start: points[start], + end: points[end] + }); + continue; + } + const targetSegments = _boundSegments(target, bounds); + for (const tgt of targetSegments) { + const subBounds = _getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop); + const fillSources = _boundSegment(segment, points, subBounds); + for (const fillSource of fillSources) { + parts.push({ + source: fillSource, + target: tgt, + start: { + [property]: _getEdge(bounds, subBounds, "start", Math.max) + }, + end: { + [property]: _getEdge(bounds, subBounds, "end", Math.min) + } + }); + } + } + } + return parts; + } + function _getBounds(property, first, last, loop) { + if (loop) { + return; + } + let start = first[property]; + let end = last[property]; + if (property === "angle") { + start = _normalizeAngle(start); + end = _normalizeAngle(end); + } + return { + property, + start, + end + }; + } + function _pointsFromSegments(boundary, line) { + const { x = null, y = null } = boundary || {}; + const linePoints = line.points; + const points = []; + line.segments.forEach(({ start, end }) => { + end = _findSegmentEnd(start, end, linePoints); + const first = linePoints[start]; + const last = linePoints[end]; + if (y !== null) { + points.push({ + x: first.x, + y + }); + points.push({ + x: last.x, + y + }); + } else if (x !== null) { + points.push({ + x, + y: first.y + }); + points.push({ + x, + y: last.y + }); + } + }); + return points; + } + 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) { + if (a && b) { + return fn(a[prop], b[prop]); + } + return a ? a[prop] : b ? b[prop] : 0; + } + function _createBoundaryLine(boundary, line) { + let points = []; + let _loop = false; + if (isArray(boundary)) { + _loop = true; + points = boundary; + } else { + points = _pointsFromSegments(boundary, line); + } + return points.length ? new LineElement({ + points, + options: { + tension: 0 + }, + _loop, + _fullLoop: _loop + }) : null; + } + function _shouldApplyFill(source) { + return source && source.fill !== false; + } + function _resolveTarget(sources, index2, propagate) { + const source = sources[index2]; + let fill2 = source.fill; + const visited = [ + index2 + ]; + let target; + if (!propagate) { + return fill2; + } + while (fill2 !== false && visited.indexOf(fill2) === -1) { + if (!isNumberFinite(fill2)) { + return fill2; + } + target = sources[fill2]; + if (!target) { + return false; + } + if (target.visible) { + return fill2; + } + visited.push(fill2); + fill2 = target.fill; + } + return false; + } + function _decodeFill(line, index2, count) { + const fill2 = parseFillOption(line); + if (isObject(fill2)) { + return isNaN(fill2.value) ? false : fill2; + } + let target = parseFloat(fill2); + if (isNumberFinite(target) && Math.floor(target) === target) { + return decodeTargetIndex(fill2[0], index2, target, count); + } + return [ + "origin", + "start", + "end", + "stack", + "shape" + ].indexOf(fill2) >= 0 && fill2; + } + function decodeTargetIndex(firstCh, index2, target, count) { + if (firstCh === "-" || firstCh === "+") { + target = index2 + target; + } + if (target === index2 || target < 0 || target >= count) { + return false; + } + return target; + } + function _getTargetPixel(fill2, scale) { + let pixel = null; + if (fill2 === "start") { + pixel = scale.bottom; + } else if (fill2 === "end") { + pixel = scale.top; + } else if (isObject(fill2)) { + pixel = scale.getPixelForValue(fill2.value); + } else if (scale.getBasePixel) { + pixel = scale.getBasePixel(); + } + return pixel; + } + function _getTargetValue(fill2, scale, startValue) { + let value; + if (fill2 === "start") { + value = startValue; + } else if (fill2 === "end") { + value = scale.options.reverse ? scale.min : scale.max; + } else if (isObject(fill2)) { + value = fill2.value; + } else { + value = scale.getBaseValue(); + } + return value; + } + function parseFillOption(line) { + const options = line.options; + const fillOption = options.fill; + let fill2 = valueOrDefault(fillOption && fillOption.target, fillOption); + if (fill2 === void 0) { + fill2 = !!options.backgroundColor; + } + if (fill2 === false || fill2 === null) { + return false; + } + if (fill2 === true) { + return "origin"; + } + return fill2; + } + function _buildStackLine(source) { + const { scale, index: index2, line } = source; + const points = []; + const segments = line.segments; + const sourcePoints = line.points; + const linesBelow = getLinesBelow(scale, index2); + linesBelow.push(_createBoundaryLine({ + x: null, + y: scale.bottom + }, line)); + for (let i = 0; i < segments.length; i++) { + const segment = segments[i]; + for (let j = segment.start; j <= segment.end; j++) { + addPointsBelow(points, sourcePoints[j], linesBelow); + } + } + return new LineElement({ + points, + options: {} + }); + } + function getLinesBelow(scale, index2) { + const below = []; + const metas = scale.getMatchingVisibleMetas("line"); + for (let i = 0; i < metas.length; i++) { + const meta = metas[i]; + if (meta.index === index2) { + break; + } + if (!meta.hidden) { + below.unshift(meta.dataset); + } + } + return below; + } + function addPointsBelow(points, sourcePoint, linesBelow) { + const postponed = []; + for (let j = 0; j < linesBelow.length; j++) { + const line = linesBelow[j]; + const { first, last, point } = findPoint(line, sourcePoint, "x"); + if (!point || first && last) { + continue; + } + if (first) { + postponed.unshift(point); + } else { + points.push(point); + if (!last) { + break; + } + } + } + points.push(...postponed); + } + function findPoint(line, sourcePoint, property) { + const point = line.interpolate(sourcePoint, property); + if (!point) { + return {}; + } + const pointValue = point[property]; + const segments = line.segments; + const linePoints = line.points; + let first = false; + let last = false; + for (let i = 0; i < segments.length; i++) { + const segment = segments[i]; + const firstValue = linePoints[segment.start][property]; + const lastValue = linePoints[segment.end][property]; + if (_isBetween(pointValue, firstValue, lastValue)) { + first = pointValue === firstValue; + last = pointValue === lastValue; + break; + } + } + return { + first, + last, + point + }; + } + class simpleArc { + constructor(opts) { + this.x = opts.x; + this.y = opts.y; + this.radius = opts.radius; + } + pathSegment(ctx, bounds, opts) { + const { x, y, radius } = this; + bounds = bounds || { + start: 0, + end: TAU + }; + ctx.arc(x, y, radius, bounds.end, bounds.start, true); + return !opts.bounds; + } + interpolate(point) { + const { x, y, radius } = this; + const angle = point.angle; + return { + x: x + Math.cos(angle) * radius, + y: y + Math.sin(angle) * radius, + angle + }; + } + } + function _getTarget(source) { + const { chart, fill: fill2, line } = source; + if (isNumberFinite(fill2)) { + return getLineByIndex(chart, fill2); + } + if (fill2 === "stack") { + return _buildStackLine(source); + } + if (fill2 === "shape") { + return true; + } + const boundary = computeBoundary(source); + if (boundary instanceof simpleArc) { + return boundary; + } + return _createBoundaryLine(boundary, line); + } + function getLineByIndex(chart, index2) { + const meta = chart.getDatasetMeta(index2); + const visible = meta && chart.isDatasetVisible(index2); + return visible ? meta.dataset : null; + } + function computeBoundary(source) { + const scale = source.scale || {}; + if (scale.getPointPositionForValue) { + return computeCircularBoundary(source); + } + return computeLinearBoundary(source); + } + function computeLinearBoundary(source) { + const { scale = {}, fill: fill2 } = source; + const pixel = _getTargetPixel(fill2, scale); + if (isNumberFinite(pixel)) { + const horizontal = scale.isHorizontal(); + return { + x: horizontal ? pixel : null, + y: horizontal ? null : pixel + }; + } + return null; + } + function computeCircularBoundary(source) { + const { scale, fill: fill2 } = source; + const options = scale.options; + const length = scale.getLabels().length; + const start = options.reverse ? scale.max : scale.min; + const value = _getTargetValue(fill2, scale, start); + const target = []; + if (options.grid.circular) { + const center = scale.getPointPositionForValue(0, start); + return new simpleArc({ + x: center.x, + y: center.y, + radius: scale.getDistanceFromCenterForValue(value) + }); + } + for (let i = 0; i < length; ++i) { + target.push(scale.getPointPositionForValue(i, value)); + } + return target; + } + function _drawfill(ctx, source, area) { + const target = _getTarget(source); + const { chart, index: index2, line, scale, axis } = source; + const lineOpts = line.options; + const fillOption = lineOpts.fill; + const color2 = lineOpts.backgroundColor; + const { above = color2, below = color2 } = fillOption || {}; + const meta = chart.getDatasetMeta(index2); + const clip = getDatasetClipArea(chart, meta); + if (target && line.points.length) { + clipArea(ctx, area); + doFill(ctx, { + line, + target, + above, + below, + area, + scale, + axis, + clip + }); + unclipArea(ctx); + } + } + function doFill(ctx, cfg) { + const { line, target, above, below, area, scale, clip } = cfg; + const property = line._loop ? "angle" : cfg.axis; + ctx.save(); + let fillColor = below; + if (below !== above) { + if (property === "x") { + clipVertical(ctx, target, area.top); + fill(ctx, { + line, + target, + color: above, + scale, + property, + clip + }); + ctx.restore(); + ctx.save(); + clipVertical(ctx, target, area.bottom); + } else if (property === "y") { + clipHorizontal(ctx, target, area.left); + fill(ctx, { + line, + target, + color: below, + scale, + property, + clip + }); + ctx.restore(); + ctx.save(); + clipHorizontal(ctx, target, area.right); + fillColor = above; + } + } + fill(ctx, { + line, + target, + color: fillColor, + scale, + property, + clip + }); + ctx.restore(); + } + function clipVertical(ctx, target, clipY) { + const { segments, points } = target; + let first = true; + let lineLoop = false; + ctx.beginPath(); + for (const segment of segments) { + const { start, end } = segment; + const firstPoint = points[start]; + const lastPoint = points[_findSegmentEnd(start, end, points)]; + if (first) { + ctx.moveTo(firstPoint.x, firstPoint.y); + first = false; + } else { + ctx.lineTo(firstPoint.x, clipY); + ctx.lineTo(firstPoint.x, firstPoint.y); + } + lineLoop = !!target.pathSegment(ctx, segment, { + move: lineLoop + }); + if (lineLoop) { + ctx.closePath(); + } else { + ctx.lineTo(lastPoint.x, clipY); + } + } + ctx.lineTo(target.first().x, clipY); + ctx.closePath(); + ctx.clip(); + } + function clipHorizontal(ctx, target, clipX) { + const { segments, points } = target; + let first = true; + let lineLoop = false; + ctx.beginPath(); + for (const segment of segments) { + const { start, end } = segment; + const firstPoint = points[start]; + const lastPoint = points[_findSegmentEnd(start, end, points)]; + if (first) { + ctx.moveTo(firstPoint.x, firstPoint.y); + first = false; + } else { + ctx.lineTo(clipX, firstPoint.y); + ctx.lineTo(firstPoint.x, firstPoint.y); + } + lineLoop = !!target.pathSegment(ctx, segment, { + move: lineLoop + }); + if (lineLoop) { + ctx.closePath(); + } else { + ctx.lineTo(clipX, lastPoint.y); + } + } + ctx.lineTo(clipX, target.first().y); + ctx.closePath(); + ctx.clip(); + } + function fill(ctx, cfg) { + const { line, target, property, color: color2, scale, clip } = cfg; + const segments = _segments(line, target, property); + for (const { source: src, target: tgt, start, end } of segments) { + const { style: { backgroundColor = color2 } = {} } = src; + const notShape = target !== true; + ctx.save(); + ctx.fillStyle = backgroundColor; + clipBounds(ctx, scale, clip, notShape && _getBounds(property, start, end)); + ctx.beginPath(); + const lineLoop = !!line.pathSegment(ctx, src); + let loop; + if (notShape) { + if (lineLoop) { + ctx.closePath(); + } else { + interpolatedLineTo(ctx, target, end, property); + } + const targetLoop = !!target.pathSegment(ctx, tgt, { + move: lineLoop, + reverse: true + }); + loop = lineLoop && targetLoop; + if (!loop) { + interpolatedLineTo(ctx, target, start, property); + } + } + ctx.closePath(); + ctx.fill(loop ? "evenodd" : "nonzero"); + ctx.restore(); + } + } + function clipBounds(ctx, scale, clip, bounds) { + const chartArea = scale.chart.chartArea; + const { property, start, end } = bounds || {}; + if (property === "x" || property === "y") { + let left, top, right, bottom; + if (property === "x") { + left = start; + top = chartArea.top; + right = end; + bottom = chartArea.bottom; + } else { + left = chartArea.left; + top = start; + right = chartArea.right; + bottom = end; + } + ctx.beginPath(); + if (clip) { + left = Math.max(left, clip.left); + right = Math.min(right, clip.right); + top = Math.max(top, clip.top); + bottom = Math.min(bottom, clip.bottom); + } + ctx.rect(left, top, right - left, bottom - top); + ctx.clip(); + } + } + function interpolatedLineTo(ctx, target, point, property) { + const interpolatedPoint = target.interpolate(point, property); + if (interpolatedPoint) { + ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y); + } + } + var index = { + id: "filler", + afterDatasetsUpdate(chart, _args, options) { + const count = (chart.data.datasets || []).length; + const sources = []; + let meta, i, line, source; + for (i = 0; i < count; ++i) { + meta = chart.getDatasetMeta(i); + line = meta.dataset; + source = null; + if (line && line.options && line instanceof LineElement) { + source = { + visible: chart.isDatasetVisible(i), + index: i, + fill: _decodeFill(line, i, count), + chart, + axis: meta.controller.options.indexAxis, + scale: meta.vScale, + line + }; + } + meta.$filler = source; + sources.push(source); + } + for (i = 0; i < count; ++i) { + source = sources[i]; + if (!source || source.fill === false) { + continue; + } + source.fill = _resolveTarget(sources, i, options.propagate); + } + }, + beforeDraw(chart, _args, options) { + const draw2 = options.drawTime === "beforeDraw"; + const metasets = chart.getSortedVisibleDatasetMetas(); + const area = chart.chartArea; + for (let i = metasets.length - 1; i >= 0; --i) { + const source = metasets[i].$filler; + if (!source) { + continue; + } + source.line.updateControlPoints(area, source.axis); + if (draw2 && source.fill) { + _drawfill(chart.ctx, source, area); + } + } + }, + beforeDatasetsDraw(chart, _args, options) { + if (options.drawTime !== "beforeDatasetsDraw") { + return; + } + const metasets = chart.getSortedVisibleDatasetMetas(); + for (let i = metasets.length - 1; i >= 0; --i) { + const source = metasets[i].$filler; + if (_shouldApplyFill(source)) { + _drawfill(chart.ctx, source, chart.chartArea); + } + } + }, + beforeDatasetDraw(chart, args, options) { + const source = args.meta.$filler; + if (!_shouldApplyFill(source) || options.drawTime !== "beforeDatasetDraw") { + return; + } + _drawfill(chart.ctx, source, chart.chartArea); + }, + defaults: { + propagate: true, + drawTime: "beforeDatasetDraw" + } + }; + const getBoxSize = (labelOpts, fontSize) => { + let { boxHeight = fontSize, boxWidth = fontSize } = labelOpts; + if (labelOpts.usePointStyle) { + boxHeight = Math.min(boxHeight, fontSize); + boxWidth = labelOpts.pointStyleWidth || Math.min(boxWidth, fontSize); + } + return { + boxWidth, + boxHeight, + itemHeight: Math.max(fontSize, boxHeight) + }; + }; + const itemsEqual = (a, b) => a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index; + class Legend extends Element { + constructor(config) { + super(); + this._added = false; + this.legendHitBoxes = []; + this._hoveredItem = null; + this.doughnutMode = false; + 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() { + if (this.isHorizontal()) { + this.width = this.maxWidth; + this.left = this._margins.left; + this.right = this.width; + } else { + 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) || []; + if (labelOpts.filter) { + legendItems = legendItems.filter((item) => labelOpts.filter(item, this.chart.data)); + } + if (labelOpts.sort) { + legendItems = legendItems.sort((a, b) => labelOpts.sort(a, b, this.chart.data)); + } + if (this.options.reverse) { + legendItems.reverse(); + } + this.legendItems = legendItems; + } + fit() { + const { options, ctx } = this; + if (!options.display) { + this.width = this.height = 0; + return; + } + const labelOpts = options.labels; + const labelFont = toFont(labelOpts.font); + const fontSize = labelFont.size; + const titleHeight = this._computeTitleHeight(); + const { boxWidth, itemHeight } = getBoxSize(labelOpts, fontSize); + let width, height; + ctx.font = labelFont.string; + if (this.isHorizontal()) { + width = this.maxWidth; + height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10; + } else { + height = this.maxHeight; + width = this._fitCols(titleHeight, labelFont, 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, maxWidth, options: { labels: { padding } } } = this; + const hitboxes = this.legendHitBoxes = []; + const lineWidths = this.lineWidths = [ + 0 + ]; + const lineHeight = itemHeight + padding; + let totalHeight = titleHeight; + ctx.textAlign = "left"; + ctx.textBaseline = "middle"; + let row = -1; + let top = -lineHeight; + this.legendItems.forEach((legendItem, i) => { + const itemWidth = boxWidth + fontSize / 2 + ctx.measureText(legendItem.text).width; + if (i === 0 || 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, + row, + width: itemWidth, + height: itemHeight + }; + lineWidths[lineWidths.length - 1] += itemWidth + padding; + }); + return totalHeight; + } + _fitCols(titleHeight, labelFont, boxWidth, _itemHeight) { + const { ctx, maxHeight, options: { labels: { padding } } } = this; + const hitboxes = this.legendHitBoxes = []; + const columnSizes = this.columnSizes = []; + const heightLimit = maxHeight - titleHeight; + let totalWidth = padding; + let currentColWidth = 0; + let currentColHeight = 0; + let left = 0; + let col = 0; + this.legendItems.forEach((legendItem, i) => { + const { itemWidth, itemHeight } = calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight); + if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) { + totalWidth += currentColWidth + padding; + columnSizes.push({ + width: currentColWidth, + height: currentColHeight + }); + left += currentColWidth + padding; + col++; + currentColWidth = currentColHeight = 0; + } + hitboxes[i] = { + left, + top: currentColHeight, + col, + width: itemWidth, + height: itemHeight + }; + currentColWidth = Math.max(currentColWidth, itemWidth); + currentColHeight += itemHeight + padding; + }); + totalWidth += currentColWidth; + columnSizes.push({ + width: currentColWidth, + height: currentColHeight + }); + return totalWidth; + } + adjustHitBoxes() { + if (!this.options.display) { + return; + } + const titleHeight = this._computeTitleHeight(); + const { legendHitBoxes: hitboxes, options: { align, labels: { padding }, rtl } } = this; + const rtlHelper = getRtlAdapter(rtl, this.left, this.width); + if (this.isHorizontal()) { + let row = 0; + let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]); + for (const hitbox of hitboxes) { + if (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; + let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height); + for (const hitbox of hitboxes) { + if (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 this.options.position === "top" || this.options.position === "bottom"; + } + draw() { + if (this.options.display) { + const ctx = this.ctx; + clipArea(ctx, this); + this._draw(); + unclipArea(ctx); + } + } + _draw() { + const { options: opts, columnSizes, lineWidths, ctx } = this; + const { align, labels: labelOpts } = opts; + const defaultColor = defaults.color; + const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width); + const labelFont = toFont(labelOpts.font); + const { padding } = labelOpts; + const fontSize = labelFont.size; + const halfFontSize = fontSize / 2; + let cursor; + this.drawTitle(); + ctx.textAlign = rtlHelper.textAlign("left"); + ctx.textBaseline = "middle"; + ctx.lineWidth = 0.5; + ctx.font = labelFont.string; + const { boxWidth, boxHeight, itemHeight } = getBoxSize(labelOpts, fontSize); + const drawLegendBox = function(x, y, legendItem) { + if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) { + return; + } + ctx.save(); + const lineWidth = valueOrDefault(legendItem.lineWidth, 1); + 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, [])); + if (labelOpts.usePointStyle) { + const drawOptions = { + radius: boxHeight * Math.SQRT2 / 2, + pointStyle: legendItem.pointStyle, + rotation: legendItem.rotation, + borderWidth: lineWidth + }; + const centerX = rtlHelper.xPlus(x, boxWidth / 2); + const centerY = y + halfFontSize; + drawPointLegend(ctx, drawOptions, centerX, centerY, labelOpts.pointStyleWidth && boxWidth); + } else { + const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0); + const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth); + const borderRadius = toTRBLCorners(legendItem.borderRadius); + ctx.beginPath(); + if (Object.values(borderRadius).some((v) => v !== 0)) { + addRoundedRectPath(ctx, { + x: xBoxLeft, + y: yBoxTop, + w: boxWidth, + h: boxHeight, + radius: borderRadius + }); + } else { + ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight); + } + ctx.fill(); + if (lineWidth !== 0) { + ctx.stroke(); + } + } + ctx.restore(); + }; + const fillText = function(x, y, legendItem) { + renderText(ctx, legendItem.text, x, y + itemHeight / 2, labelFont, { + strikethrough: legendItem.hidden, + textAlign: rtlHelper.textAlign(legendItem.textAlign) + }); + }; + const isHorizontal = this.isHorizontal(); + const titleHeight = this._computeTitleHeight(); + if (isHorizontal) { + cursor = { + x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]), + y: this.top + padding + titleHeight, + line: 0 + }; + } else { + cursor = { + 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; + ctx.fillStyle = legendItem.fontColor; + const textWidth = ctx.measureText(legendItem.text).width; + const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign)); + const width = boxWidth + halfFontSize + textWidth; + let x = cursor.x; + let y = cursor.y; + rtlHelper.setWidth(this.width); + if (isHorizontal) { + if (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]); + } + } else if (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); + } + const realX = rtlHelper.x(x); + drawLegendBox(realX, y, legendItem); + x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl); + fillText(rtlHelper.x(x), y, legendItem); + if (isHorizontal) { + cursor.x += width + padding; + } else if (typeof legendItem.text !== "string") { + const fontLineHeight = labelFont.lineHeight; + cursor.y += calculateLegendItemHeight(legendItem, fontLineHeight) + padding; + } else { + cursor.y += lineHeight; + } + }); + restoreTextDirection(this.ctx, opts.textDirection); + } + drawTitle() { + const opts = this.options; + const titleOpts = opts.title; + const titleFont = toFont(titleOpts.font); + const titlePadding = toPadding(titleOpts.padding); + if (!titleOpts.display) { + return; + } + const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width); + const ctx = this.ctx; + const position = titleOpts.position; + const halfFontSize = titleFont.size / 2; + const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize; + let y; + let left = this.left; + let 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; + const titleFont = toFont(titleOpts.font); + const 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)) { + lh = this.legendHitBoxes; + for (i = 0; i < lh.length; ++i) { + hitBox = lh[i]; + if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width) && _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) { + return this.legendItems[i]; + } + } + } + return null; + } + handleEvent(e) { + const opts = this.options; + if (!isListened(e.type, opts)) { + return; + } + const hoveredItem = this._getLegendItemAt(e.x, e.y); + if (e.type === "mousemove" || e.type === "mouseout") { + const previous = this._hoveredItem; + const sameItem = itemsEqual(previous, hoveredItem); + if (previous && !sameItem) { + callback(opts.onLeave, [ + e, + previous, + this + ], this); + } + this._hoveredItem = hoveredItem; + if (hoveredItem && !sameItem) { + callback(opts.onHover, [ + e, + hoveredItem, + this + ], this); + } + } else if (hoveredItem) { + callback(opts.onClick, [ + e, + hoveredItem, + this + ], this); + } + } + } + function calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight) { + const itemWidth = calculateItemWidth(legendItem, boxWidth, labelFont, ctx); + const itemHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight); + return { + itemWidth, + itemHeight + }; + } + function calculateItemWidth(legendItem, boxWidth, labelFont, ctx) { + let legendItemText = legendItem.text; + if (legendItemText && typeof legendItemText !== "string") { + legendItemText = legendItemText.reduce((a, b) => a.length > b.length ? a : b); + } + return boxWidth + labelFont.size / 2 + ctx.measureText(legendItemText).width; + } + function calculateItemHeight(_itemHeight, legendItem, fontLineHeight) { + let itemHeight = _itemHeight; + if (typeof legendItem.text !== "string") { + itemHeight = calculateLegendItemHeight(legendItem, fontLineHeight); + } + return itemHeight; + } + function calculateLegendItemHeight(legendItem, fontLineHeight) { + const labelHeight = legendItem.text ? legendItem.text.length : 0; + return fontLineHeight * labelHeight; + } + function isListened(type, opts) { + if ((type === "mousemove" || type === "mouseout") && (opts.onHover || opts.onLeave)) { + return true; + } + if (opts.onClick && (type === "click" || type === "mouseup")) { + return true; + } + return false; + } + var plugin_legend = { + id: "legend", + _element: Legend, + start(chart, _args, options) { + const legend = chart.legend = new Legend({ + ctx: chart.ctx, + options, + chart + }); + layouts.configure(chart, legend, options); + layouts.addBox(chart, legend); + }, + stop(chart) { + layouts.removeBox(chart, chart.legend); + delete chart.legend; + }, + beforeUpdate(chart, _args, options) { + const legend = chart.legend; + layouts.configure(chart, legend, options); + legend.options = options; + }, + afterUpdate(chart) { + const legend = chart.legend; + legend.buildLabels(); + legend.adjustHitBoxes(); + }, + afterEvent(chart, args) { + if (!args.replay) { + chart.legend.handleEvent(args.event); + } + }, + defaults: { + display: true, + position: "top", + align: "center", + fullSize: true, + reverse: false, + weight: 1e3, + onClick(e, legendItem, legend) { + const index2 = legendItem.datasetIndex; + const ci = legend.chart; + if (ci.isDatasetVisible(index2)) { + ci.hide(index2); + legendItem.hidden = true; + } else { + ci.show(index2); + legendItem.hidden = false; + } + }, + onHover: null, + onLeave: null, + labels: { + color: (ctx) => ctx.chart.options.color, + boxWidth: 40, + padding: 10, + generateLabels(chart) { + const datasets = chart.data.datasets; + const { labels: { usePointStyle, pointStyle, textAlign, color: color2, useBorderRadius, borderRadius } } = chart.legend.options; + return chart._getSortedDatasetMetas().map((meta) => { + const style = meta.controller.getStyle(usePointStyle ? 0 : void 0); + const borderWidth = toPadding(style.borderWidth); + return { + text: datasets[meta.index].label, + fillStyle: style.backgroundColor, + fontColor: color2, + 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: useBorderRadius && (borderRadius || style.borderRadius), + datasetIndex: meta.index + }; + }, this); + } + }, + title: { + color: (ctx) => ctx.chart.options.color, + display: false, + position: "center", + text: "" + } + }, + descriptors: { + _scriptable: (name) => !name.startsWith("on"), + labels: { + _scriptable: (name) => ![ + "generateLabels", + "filter", + "sort" + ].includes(name) + } + } + }; + const positioners = { + average(items) { + if (!items.length) { + return false; + } + let i, len; + let xSet = /* @__PURE__ */ new Set(); + let y = 0; + let count = 0; + for (i = 0, len = items.length; i < len; ++i) { + const el = items[i].element; + if (el && el.hasValue()) { + const pos = el.tooltipPosition(); + xSet.add(pos.x); + y += pos.y; + ++count; + } + } + if (count === 0 || xSet.size === 0) { + return false; + } + const xAverage = [ + ...xSet + ].reduce((a, b) => a + b) / xSet.size; + return { + x: xAverage, + y: y / count + }; + }, + nearest(items, eventPosition) { + if (!items.length) { + return false; + } + let x = eventPosition.x; + let y = eventPosition.y; + let minDistance = Number.POSITIVE_INFINITY; + let i, len, nearestElement; + for (i = 0, len = items.length; i < len; ++i) { + const el = items[i].element; + if (el && el.hasValue()) { + const center = el.getCenterPoint(); + const d = distanceBetweenPoints(eventPosition, center); + if (d < minDistance) { + minDistance = d; + nearestElement = el; + } + } + } + if (nearestElement) { + const tp = nearestElement.tooltipPosition(); + x = tp.x; + y = tp.y; + } + return { + x, + y + }; + } + }; + function pushOrConcat(base, toPush) { + if (toPush) { + if (isArray(toPush)) { + Array.prototype.push.apply(base, toPush); + } else { + base.push(toPush); + } + } + return base; + } + function splitNewlines(str) { + if ((typeof str === "string" || str instanceof String) && str.indexOf("\n") > -1) { + return str.split("\n"); + } + return str; + } + function createTooltipItem(chart, item) { + const { element, datasetIndex, index: index2 } = item; + const controller = chart.getDatasetMeta(datasetIndex).controller; + const { label, value } = controller.getLabelAndValue(index2); + return { + chart, + label, + parsed: controller.getParsed(index2), + raw: chart.data.datasets[datasetIndex].data[index2], + formattedValue: value, + dataset: controller.getDataset(), + dataIndex: index2, + datasetIndex, + element + }; + } + function getTooltipSize(tooltip, options) { + const ctx = tooltip.chart.ctx; + const { body, footer, title } = tooltip; + const { boxWidth, boxHeight } = options; + const bodyFont = toFont(options.bodyFont); + const titleFont = toFont(options.titleFont); + const footerFont = toFont(options.footerFont); + const titleLineCount = title.length; + const footerLineCount = footer.length; + const bodyLineItemCount = body.length; + const padding = toPadding(options.padding); + let height = padding.height; + let width = 0; + let combinedBodyLength = body.reduce((count, bodyItem) => count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length, 0); + combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length; + if (titleLineCount) { + height += titleLineCount * titleFont.lineHeight + (titleLineCount - 1) * options.titleSpacing + options.titleMarginBottom; + } + if (combinedBodyLength) { + const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight; + height += bodyLineItemCount * bodyLineHeight + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight + (combinedBodyLength - 1) * options.bodySpacing; + } + if (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); + }; + 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; + return { + width, + height + }; + } + function determineYAlign(chart, size) { + const { y, height } = size; + if (y < height / 2) { + return "top"; + } else if (y > chart.height - height / 2) { + return "bottom"; + } + return "center"; + } + function doesNotFitWithAlign(xAlign, chart, options, size) { + const { x, width } = size; + const caret = options.caretSize + options.caretPadding; + if (xAlign === "left" && x + width + caret > chart.width) { + return true; + } + if (xAlign === "right" && x - width - caret < 0) { + return true; + } + } + function determineXAlign(chart, options, size, yAlign) { + const { x, width } = size; + const { width: chartWidth, chartArea: { left, right } } = chart; + let xAlign = "center"; + if (yAlign === "center") { + xAlign = x <= (left + right) / 2 ? "left" : "right"; + } else if (x <= width / 2) { + xAlign = "left"; + } else if (x >= chartWidth - width / 2) { + xAlign = "right"; + } + if (doesNotFitWithAlign(xAlign, chart, options, size)) { + xAlign = "center"; + } + return xAlign; + } + function determineAlignment(chart, options, size) { + const yAlign = size.yAlign || options.yAlign || determineYAlign(chart, size); + return { + xAlign: size.xAlign || options.xAlign || determineXAlign(chart, options, size, yAlign), + yAlign + }; + } + function alignX(size, xAlign) { + let { x, width } = size; + if (xAlign === "right") { + x -= width; + } else if (xAlign === "center") { + x -= width / 2; + } + return x; + } + function alignY(size, yAlign, paddingAndSize) { + let { y, height } = size; + if (yAlign === "top") { + y += paddingAndSize; + } else if (yAlign === "bottom") { + y -= height + paddingAndSize; + } else { + y -= height / 2; + } + return y; + } + function getBackgroundPoint(options, size, alignment, chart) { + const { caretSize, caretPadding, cornerRadius } = options; + const { xAlign, yAlign } = alignment; + const paddingAndSize = caretSize + caretPadding; + const { topLeft, topRight, bottomLeft, bottomRight } = toTRBLCorners(cornerRadius); + let x = alignX(size, xAlign); + const y = alignY(size, yAlign, paddingAndSize); + if (yAlign === "center") { + if (xAlign === "left") { + x += paddingAndSize; + } else if (xAlign === "right") { + x -= paddingAndSize; + } + } else if (xAlign === "left") { + x -= Math.max(topLeft, bottomLeft) + caretSize; + } else if (xAlign === "right") { + x += Math.max(topRight, bottomRight) + caretSize; + } + return { + 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 align === "center" ? tooltip.x + tooltip.width / 2 : align === "right" ? tooltip.x + tooltip.width - padding.right : tooltip.x + padding.left; + } + function getBeforeAfterBodyLines(callback2) { + return pushOrConcat([], splitNewlines(callback2)); + } + function createTooltipContext(parent, tooltip, tooltipItems) { + return createContext(parent, { + tooltip, + tooltipItems, + type: "tooltip" + }); + } + function overrideCallbacks(callbacks, context) { + const override = context && context.dataset && context.dataset.tooltip && context.dataset.tooltip.callbacks; + return override ? callbacks.override(override) : callbacks; + } + const defaultCallbacks = { + beforeTitle: noop, + title(tooltipItems) { + if (tooltipItems.length > 0) { + const item = tooltipItems[0]; + const labels = item.chart.data.labels; + const labelCount = labels ? labels.length : 0; + if (this && this.options && this.options.mode === "dataset") { + return item.dataset.label || ""; + } else if (item.label) { + return item.label; + } else if (labelCount > 0 && item.dataIndex < labelCount) { + return labels[item.dataIndex]; + } + } + return ""; + }, + afterTitle: noop, + beforeBody: noop, + beforeLabel: noop, + label(tooltipItem) { + if (this && this.options && this.options.mode === "dataset") { + return tooltipItem.label + ": " + tooltipItem.formattedValue || tooltipItem.formattedValue; + } + let label = tooltipItem.dataset.label || ""; + if (label) { + label += ": "; + } + const value = tooltipItem.formattedValue; + if (!isNullOrUndef(value)) { + label += value; + } + return label; + }, + labelColor(tooltipItem) { + const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex); + const options = meta.controller.getStyle(tooltipItem.dataIndex); + return { + borderColor: options.borderColor, + backgroundColor: options.backgroundColor, + borderWidth: options.borderWidth, + borderDash: options.borderDash, + borderDashOffset: options.borderDashOffset, + borderRadius: 0 + }; + }, + labelTextColor() { + return this.options.bodyColor; + }, + labelPointStyle(tooltipItem) { + const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex); + const options = meta.controller.getStyle(tooltipItem.dataIndex); + return { + pointStyle: options.pointStyle, + rotation: options.rotation + }; + }, + afterLabel: noop, + afterBody: noop, + beforeFooter: noop, + footer: noop, + afterFooter: noop + }; + function invokeCallbackWithFallback(callbacks, name, ctx, arg) { + const result = callbacks[name].call(ctx, arg); + if (typeof result === "undefined") { + return defaultCallbacks[name].call(ctx, arg); + } + return result; + } + 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; + 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; + const options = this.options.setContext(this.getContext()); + const opts = options.enabled && chart.options.animation && options.animations; + const animations = new Animations(this.chart, opts); + if (opts._cacheable) { + this._cachedAnimations = Object.freeze(animations); + } + return animations; + } + getContext() { + return this.$context || (this.$context = createTooltipContext(this.chart.getContext(), this, this._tooltipItems)); + } + getTitle(context, options) { + const { callbacks } = options; + const beforeTitle = invokeCallbackWithFallback(callbacks, "beforeTitle", this, context); + const title = invokeCallbackWithFallback(callbacks, "title", this, context); + const afterTitle = invokeCallbackWithFallback(callbacks, "afterTitle", this, context); + let lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeTitle)); + lines = pushOrConcat(lines, splitNewlines(title)); + lines = pushOrConcat(lines, splitNewlines(afterTitle)); + return lines; + } + getBeforeBody(tooltipItems, options) { + return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, "beforeBody", this, tooltipItems)); + } + getBody(tooltipItems, options) { + const { callbacks } = options; + const bodyItems = []; + each(tooltipItems, (context) => { + const bodyItem = { + before: [], + lines: [], + after: [] + }; + const scoped = overrideCallbacks(callbacks, context); + pushOrConcat(bodyItem.before, splitNewlines(invokeCallbackWithFallback(scoped, "beforeLabel", this, context))); + pushOrConcat(bodyItem.lines, invokeCallbackWithFallback(scoped, "label", this, context)); + pushOrConcat(bodyItem.after, splitNewlines(invokeCallbackWithFallback(scoped, "afterLabel", this, context))); + bodyItems.push(bodyItem); + }); + return bodyItems; + } + getAfterBody(tooltipItems, options) { + return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, "afterBody", this, tooltipItems)); + } + getFooter(tooltipItems, options) { + const { callbacks } = options; + const beforeFooter = invokeCallbackWithFallback(callbacks, "beforeFooter", this, tooltipItems); + const footer = invokeCallbackWithFallback(callbacks, "footer", this, tooltipItems); + const afterFooter = invokeCallbackWithFallback(callbacks, "afterFooter", this, tooltipItems); + let lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeFooter)); + lines = pushOrConcat(lines, splitNewlines(footer)); + lines = pushOrConcat(lines, splitNewlines(afterFooter)); + return lines; + } + _createItems(options) { + const active = this._active; + const data = this.chart.data; + const labelColors = []; + const labelPointStyles = []; + const labelTextColors = []; + let tooltipItems = []; + let i, len; + for (i = 0, len = active.length; i < len; ++i) { + tooltipItems.push(createTooltipItem(this.chart, active[i])); + } + if (options.filter) { + tooltipItems = tooltipItems.filter((element, index2, array) => options.filter(element, index2, array, data)); + } + if (options.itemSort) { + tooltipItems = tooltipItems.sort((a, b) => options.itemSort(a, b, data)); + } + each(tooltipItems, (context) => { + const scoped = overrideCallbacks(options.callbacks, context); + labelColors.push(invokeCallbackWithFallback(scoped, "labelColor", this, context)); + labelPointStyles.push(invokeCallbackWithFallback(scoped, "labelPointStyle", this, context)); + labelTextColors.push(invokeCallbackWithFallback(scoped, "labelTextColor", this, context)); + }); + this.labelColors = labelColors; + this.labelPointStyles = labelPointStyles; + this.labelTextColors = labelTextColors; + this.dataPoints = tooltipItems; + return tooltipItems; + } + update(changed, replay) { + const options = this.options.setContext(this.getContext()); + const active = this._active; + let properties; + let tooltipItems = []; + if (!active.length) { + if (this.opacity !== 0) { + properties = { + opacity: 0 + }; + } + } else { + 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); + const positionAndSize = Object.assign({}, position, size); + const alignment = determineAlignment(this.chart, options, positionAndSize); + const 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 + }; + } + this._tooltipItems = tooltipItems; + this.$context = void 0; + if (properties) { + this._resolveAnimations().update(this, properties); + } + if (changed && options.external) { + options.external.call(this, { + chart: this.chart, + tooltip: this, + 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, yAlign } = this; + const { caretSize, cornerRadius } = options; + const { topLeft, topRight, bottomLeft, bottomRight } = toTRBLCorners(cornerRadius); + const { x: ptX, y: ptY } = tooltipPoint; + const { width, height } = size; + let x1, x2, x3, y1, y2, y3; + if (yAlign === "center") { + y2 = ptY + height / 2; + if (xAlign === "left") { + x1 = ptX; + x2 = x1 - caretSize; + y1 = y2 + caretSize; + y3 = y2 - caretSize; + } else { + x1 = ptX + width; + x2 = x1 + caretSize; + y1 = y2 - caretSize; + y3 = y2 + caretSize; + } + x3 = x1; + } else { + if (xAlign === "left") { + x2 = ptX + Math.max(topLeft, bottomLeft) + caretSize; + } else if (xAlign === "right") { + x2 = ptX + width - Math.max(topRight, bottomRight) - caretSize; + } else { + x2 = this.caretX; + } + if (yAlign === "top") { + y1 = ptY; + y2 = y1 - caretSize; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else { + y1 = ptY + height; + y2 = y1 + caretSize; + x1 = x2 + caretSize; + x3 = x2 - caretSize; + } + y3 = y1; + } + return { + x1, + x2, + x3, + y1, + y2, + y3 + }; + } + drawTitle(pt, ctx, options) { + const title = this.title; + const length = title.length; + let titleFont, titleSpacing, i; + if (length) { + const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width); + 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; + for (i = 0; i < length; ++i) { + ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2); + pt.y += titleFont.lineHeight + titleSpacing; + if (i + 1 === length) { + pt.y += options.titleMarginBottom - titleSpacing; + } + } + } + } + _drawColorBox(ctx, pt, i, rtlHelper, options) { + const labelColor = this.labelColors[i]; + const labelPointStyle = this.labelPointStyles[i]; + const { boxHeight, boxWidth } = options; + const bodyFont = toFont(options.bodyFont); + const colorX = getAlignedX(this, "left", options); + const rtlColorX = rtlHelper.x(colorX); + const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0; + const colorY = pt.y + yOffSet; + if (options.usePointStyle) { + const drawOptions = { + radius: Math.min(boxWidth, boxHeight) / 2, + pointStyle: labelPointStyle.pointStyle, + rotation: labelPointStyle.rotation, + borderWidth: 1 + }; + const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2; + const centerY = colorY + boxHeight / 2; + ctx.strokeStyle = options.multiKeyBackground; + ctx.fillStyle = options.multiKeyBackground; + drawPoint(ctx, drawOptions, centerX, centerY); + ctx.strokeStyle = labelColor.borderColor; + ctx.fillStyle = labelColor.backgroundColor; + drawPoint(ctx, drawOptions, centerX, centerY); + } else { + ctx.lineWidth = isObject(labelColor.borderWidth) ? Math.max(...Object.values(labelColor.borderWidth)) : labelColor.borderWidth || 1; + ctx.strokeStyle = labelColor.borderColor; + ctx.setLineDash(labelColor.borderDash || []); + ctx.lineDashOffset = labelColor.borderDashOffset || 0; + const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth); + const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2); + const borderRadius = toTRBLCorners(labelColor.borderRadius); + if (Object.values(borderRadius).some((v) => v !== 0)) { + ctx.beginPath(); + ctx.fillStyle = options.multiKeyBackground; + addRoundedRectPath(ctx, { + x: outerX, + y: colorY, + w: boxWidth, + h: boxHeight, + radius: borderRadius + }); + ctx.fill(); + ctx.stroke(); + ctx.fillStyle = labelColor.backgroundColor; + ctx.beginPath(); + addRoundedRectPath(ctx, { + x: innerX, + y: colorY + 1, + w: boxWidth - 2, + h: boxHeight - 2, + radius: borderRadius + }); + ctx.fill(); + } else { + ctx.fillStyle = options.multiKeyBackground; + ctx.fillRect(outerX, colorY, boxWidth, boxHeight); + ctx.strokeRect(outerX, colorY, boxWidth, boxHeight); + ctx.fillStyle = labelColor.backgroundColor; + ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2); + } + } + ctx.fillStyle = this.labelTextColors[i]; + } + drawBody(pt, ctx, options) { + const { body } = this; + const { bodySpacing, bodyAlign, displayColors, boxHeight, boxWidth, boxPadding } = options; + const bodyFont = toFont(options.bodyFont); + let bodyLineHeight = bodyFont.lineHeight; + let xLinePadding = 0; + const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width); + const fillLineOfText = function(line) { + ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2); + pt.y += bodyLineHeight + bodySpacing; + }; + const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign); + let bodyItem, textColor, lines, i, j, ilen, jlen; + 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 && bodyAlignForCalculation !== "right" ? bodyAlign === "center" ? boxWidth / 2 + boxPadding : boxWidth + 2 + boxPadding : 0; + for (i = 0, ilen = body.length; i < ilen; ++i) { + bodyItem = body[i]; + textColor = this.labelTextColors[i]; + ctx.fillStyle = textColor; + each(bodyItem.before, fillLineOfText); + lines = bodyItem.lines; + if (displayColors && lines.length) { + this._drawColorBox(ctx, pt, i, rtlHelper, options); + bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight); + } + for (j = 0, jlen = lines.length; j < jlen; ++j) { + fillLineOfText(lines[j]); + bodyLineHeight = bodyFont.lineHeight; + } + each(bodyItem.after, fillLineOfText); + } + xLinePadding = 0; + bodyLineHeight = bodyFont.lineHeight; + each(this.afterBody, fillLineOfText); + pt.y -= bodySpacing; + } + drawFooter(pt, ctx, options) { + const footer = this.footer; + const length = footer.length; + let footerFont, i; + if (length) { + const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width); + pt.x = getAlignedX(this, options.footerAlign, options); + pt.y += options.footerMarginTop; + ctx.textAlign = rtlHelper.textAlign(options.footerAlign); + ctx.textBaseline = "middle"; + footerFont = toFont(options.footerFont); + ctx.fillStyle = options.footerColor; + ctx.font = footerFont.string; + for (i = 0; i < length; ++i) { + ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2); + pt.y += footerFont.lineHeight + options.footerSpacing; + } + } + } + drawBackground(pt, ctx, tooltipSize, options) { + const { xAlign, yAlign } = this; + const { x, y } = pt; + const { width, height } = tooltipSize; + const { topLeft, topRight, bottomLeft, bottomRight } = toTRBLCorners(options.cornerRadius); + ctx.fillStyle = options.backgroundColor; + ctx.strokeStyle = options.borderColor; + ctx.lineWidth = options.borderWidth; + ctx.beginPath(); + ctx.moveTo(x + topLeft, y); + if (yAlign === "top") { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x + width - topRight, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + topRight); + if (yAlign === "center" && xAlign === "right") { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x + width, y + height - bottomRight); + ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height); + if (yAlign === "bottom") { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x + bottomLeft, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft); + if (yAlign === "center" && xAlign === "left") { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x, y + topLeft); + ctx.quadraticCurveTo(x, y, x + topLeft, y); + ctx.closePath(); + ctx.fill(); + if (options.borderWidth > 0) { + ctx.stroke(); + } + } + _updateAnimationTarget(options) { + const chart = this.chart; + const anims = this.$animations; + const animX = anims && anims.x; + const 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); + const positionAndSize = Object.assign({}, position, this._size); + const alignment = determineAlignment(chart, options, positionAndSize); + const point = getBackgroundPoint(options, positionAndSize, alignment, chart); + if (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 + }; + const pt = { + x: this.x, + y: this.y + }; + opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity; + const padding = toPadding(options.padding); + const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length; + if (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; + const active = activeElements.map(({ datasetIndex, index: index2 }) => { + const meta = this.chart.getDatasetMeta(datasetIndex); + if (!meta) { + throw new Error("Cannot find a dataset at index " + datasetIndex); + } + return { + datasetIndex, + element: meta.data[index2], + index: index2 + }; + }); + const changed = !_elementsEqual(lastActive, active); + const positionChanged = this._positionChanged(active, eventPosition); + if (changed || positionChanged) { + this._active = active; + this._eventPosition = eventPosition; + this._ignoreReplayEvents = true; + this.update(true); + } + } + handleEvent(e, replay, inChartArea = true) { + if (replay && this._ignoreReplayEvents) { + return false; + } + this._ignoreReplayEvents = false; + const options = this.options; + const lastActive = this._active || []; + const active = this._getActiveElements(e, lastActive, replay, inChartArea); + const positionChanged = this._positionChanged(active, e); + const changed = replay || !_elementsEqual(active, lastActive) || positionChanged; + if (changed) { + this._active = active; + if (options.enabled || options.external) { + this._eventPosition = { + x: e.x, + y: e.y + }; + this.update(true, replay); + } + } + return changed; + } + _getActiveElements(e, lastActive, replay, inChartArea) { + const options = this.options; + if (e.type === "mouseout") { + return []; + } + if (!inChartArea) { + return lastActive.filter((i) => this.chart.data.datasets[i.datasetIndex] && this.chart.getDatasetMeta(i.datasetIndex).controller.getParsed(i.index) !== void 0); + } + const active = this.chart.getElementsAtEventForMode(e, options.mode, options, replay); + if (options.reverse) { + active.reverse(); + } + return active; + } + _positionChanged(active, e) { + const { caretX, caretY, options } = this; + const position = positioners[options.position].call(this, active, e); + return position !== false && (caretX !== position.x || caretY !== position.y); + } + } + __publicField(Tooltip, "positioners", positioners); + var plugin_tooltip = { + id: "tooltip", + _element: Tooltip, + positioners, + afterInit(chart, _args, options) { + if (options) { + chart.tooltip = new Tooltip({ + chart, + options + }); + } + }, + beforeUpdate(chart, _args, options) { + if (chart.tooltip) { + chart.tooltip.initialize(options); + } + }, + reset(chart, _args, options) { + if (chart.tooltip) { + chart.tooltip.initialize(options); + } + }, + afterDraw(chart) { + const tooltip = chart.tooltip; + if (tooltip && tooltip._willRender()) { + const args = { + tooltip + }; + if (chart.notifyPlugins("beforeTooltipDraw", { + ...args, + cancelable: true + }) === false) { + return; + } + tooltip.draw(chart.ctx); + chart.notifyPlugins("afterTooltipDraw", args); + } + }, + afterEvent(chart, args) { + if (chart.tooltip) { + const useFinalPosition = args.replay; + if (chart.tooltip.handleEvent(args.event, useFinalPosition, args.inChartArea)) { + args.changed = true; + } + } + }, + defaults: { + enabled: true, + 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: true, + 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: defaultCallbacks + }, + defaultRoutes: { + bodyFont: "font", + footerFont: "font", + titleFont: "font" + }, + descriptors: { + _scriptable: (name) => name !== "filter" && name !== "itemSort" && name !== "external", + _indexable: false, + callbacks: { + _scriptable: false, + _indexable: false + }, + animation: { + _fallback: false + }, + animations: { + _fallback: "animation" + } + }, + additionalOptionScopes: [ + "interaction" + ] + }; + const addIfString = (labels, raw, index2, addedLabels) => { + if (typeof raw === "string") { + index2 = labels.push(raw) - 1; + addedLabels.unshift({ + index: index2, + label: raw + }); + } else if (isNaN(raw)) { + index2 = null; + } + return index2; + }; + function findOrAddLabel(labels, raw, index2, addedLabels) { + const first = labels.indexOf(raw); + if (first === -1) { + return addIfString(labels, raw, index2, addedLabels); + } + const last = labels.lastIndexOf(raw); + return first !== last ? index2 : first; + } + const validIndex = (index2, max) => index2 === null ? null : _limitValue(Math.round(index2), 0, max); + function _getLabelForValue(value) { + const labels = this.getLabels(); + if (value >= 0 && value < labels.length) { + return labels[value]; + } + return value; + } + 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: index2, label } of added) { + if (labels[index2] === label) { + labels.splice(index2, 1); + } + } + this._addedLabels = []; + } + super.init(scaleOptions); + } + parse(raw, index2) { + if (isNullOrUndef(raw)) { + return null; + } + const labels = this.getLabels(); + index2 = isFinite(index2) && labels[index2] === raw ? index2 : findOrAddLabel(labels, raw, valueOrDefault(index2, raw), this._addedLabels); + return validIndex(index2, labels.length - 1); + } + determineDataLimits() { + const { minDefined, maxDefined } = this.getUserBounds(); + let { min, max } = this.getMinMax(true); + if (this.options.bounds === "ticks") { + if (!minDefined) { + min = 0; + } + if (!maxDefined) { + max = this.getLabels().length - 1; + } + } + this.min = min; + this.max = max; + } + buildTicks() { + const min = this.min; + const max = this.max; + const offset = this.options.offset; + const ticks = []; + let labels = this.getLabels(); + labels = min === 0 && 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 ? 0.5 : 0); + for (let value = min; value <= max; value++) { + ticks.push({ + value + }); + } + return ticks; + } + getLabelForValue(value) { + return _getLabelForValue.call(this, value); + } + configure() { + super.configure(); + if (!this.isHorizontal()) { + this._reversePixels = !this._reversePixels; + } + } + getPixelForValue(value) { + if (typeof value !== "number") { + value = this.parse(value); + } + return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange); + } + getPixelForTick(index2) { + const ticks = this.ticks; + if (index2 < 0 || index2 > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index2].value); + } + getValueForPixel(pixel) { + return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange); + } + getBasePixel() { + return this.bottom; + } + } + __publicField(CategoryScale, "id", "category"); + __publicField(CategoryScale, "defaults", { + ticks: { + callback: _getLabelForValue + } + }); + function generateTicks$1(generationOptions, dataRange) { + const ticks = []; + const MIN_SPACING = 1e-14; + const { bounds, step, min, max, precision, count, maxTicks, maxDigits, includeBounds } = generationOptions; + const unit = step || 1; + const maxSpaces = maxTicks - 1; + const { min: rmin, max: rmax } = dataRange; + const minDefined = !isNullOrUndef(min); + const maxDefined = !isNullOrUndef(max); + const countDefined = !isNullOrUndef(count); + const minSpacing = (rmax - rmin) / (maxDigits + 1); + let spacing = niceNum((rmax - rmin) / maxSpaces / unit) * unit; + let factor, niceMin, niceMax, numSpaces; + if (spacing < MIN_SPACING && !minDefined && !maxDefined) { + return [ + { + value: rmin + }, + { + value: rmax + } + ]; + } + numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing); + if (numSpaces > maxSpaces) { + spacing = niceNum(numSpaces * spacing / maxSpaces / unit) * unit; + } + if (!isNullOrUndef(precision)) { + factor = Math.pow(10, precision); + spacing = Math.ceil(spacing * factor) / factor; + } + if (bounds === "ticks") { + niceMin = Math.floor(rmin / spacing) * spacing; + niceMax = Math.ceil(rmax / spacing) * spacing; + } else { + niceMin = rmin; + niceMax = rmax; + } + if (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; + } else if (countDefined) { + niceMin = minDefined ? min : niceMin; + niceMax = maxDefined ? max : niceMax; + numSpaces = count - 1; + spacing = (niceMax - niceMin) / numSpaces; + } else { + numSpaces = (niceMax - niceMin) / spacing; + if (almostEquals(numSpaces, Math.round(numSpaces), spacing / 1e3)) { + numSpaces = Math.round(numSpaces); + } else { + 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; + if (minDefined) { + if (includeBounds && niceMin !== min) { + ticks.push({ + value: min + }); + if (niceMin < min) { + j++; + } + if (almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) { + j++; + } + } else if (niceMin < min) { + j++; + } + } + for (; j < numSpaces; ++j) { + const tickValue = Math.round((niceMin + j * spacing) * factor) / factor; + if (maxDefined && tickValue > max) { + break; + } + ticks.push({ + value: tickValue + }); + } + if (maxDefined && includeBounds && niceMax !== max) { + if (ticks.length && almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) { + ticks[ticks.length - 1].value = max; + } else { + ticks.push({ + value: max + }); + } + } else if (!maxDefined || niceMax === max) { + ticks.push({ + value: niceMax + }); + } + return ticks; + } + function relativeLabelSize(value, minSpacing, { horizontal, minRotation }) { + const rad = toRadians(minRotation); + const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 1e-3; + const length = 0.75 * minSpacing * ("" + value).length; + return Math.min(minSpacing / ratio, length); + } + 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, index2) { + if (isNullOrUndef(raw)) { + return null; + } + if ((typeof raw === "number" || raw instanceof Number) && !isFinite(+raw)) { + return null; + } + return +raw; + } + handleTickRangeOptions() { + const { beginAtZero } = this.options; + const { minDefined, maxDefined } = this.getUserBounds(); + let { min, max } = this; + const setMin = (v) => min = minDefined ? min : v; + const setMax = (v) => max = maxDefined ? max : v; + if (beginAtZero) { + const minSign = sign(min); + const maxSign = sign(max); + if (minSign < 0 && maxSign < 0) { + setMax(0); + } else if (minSign > 0 && maxSign > 0) { + setMin(0); + } + } + if (min === max) { + let offset = max === 0 ? 1 : Math.abs(max * 0.05); + setMax(max + offset); + if (!beginAtZero) { + setMin(min - offset); + } + } + this.min = min; + this.max = max; + } + getTickLimit() { + const tickOpts = this.options.ticks; + let { maxTicksLimit, stepSize } = tickOpts; + let maxTicks; + if (stepSize) { + maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1; + if (maxTicks > 1e3) { + console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`); + maxTicks = 1e3; + } + } else { + maxTicks = this.computeTickLimit(); + maxTicksLimit = maxTicksLimit || 11; + } + if (maxTicksLimit) { + maxTicks = Math.min(maxTicksLimit, maxTicks); + } + return maxTicks; + } + computeTickLimit() { + return Number.POSITIVE_INFINITY; + } + buildTicks() { + const opts = this.options; + const tickOpts = opts.ticks; + let maxTicks = this.getTickLimit(); + maxTicks = Math.max(2, maxTicks); + const numericGeneratorOptions = { + maxTicks, + bounds: opts.bounds, + min: opts.min, + max: opts.max, + precision: tickOpts.precision, + step: tickOpts.stepSize, + count: tickOpts.count, + maxDigits: this._maxDigits(), + horizontal: this.isHorizontal(), + minRotation: tickOpts.minRotation || 0, + includeBounds: tickOpts.includeBounds !== false + }; + const dataRange = this._range || this; + const ticks = generateTicks$1(numericGeneratorOptions, dataRange); + if (opts.bounds === "ticks") { + _setMinAndMaxByKey(ticks, this, "value"); + } + if (opts.reverse) { + ticks.reverse(); + this.start = this.max; + this.end = this.min; + } else { + this.start = this.min; + this.end = this.max; + } + return ticks; + } + configure() { + const ticks = this.ticks; + let start = this.min; + let end = this.max; + super.configure(); + if (this.options.offset && ticks.length) { + const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2; + start -= offset; + end += offset; + } + this._startValue = start; + this._endValue = end; + this._valueRange = end - start; + } + getLabelForValue(value) { + return formatNumber(value, this.chart.options.locale, this.options.ticks.format); + } + } + class LinearScale extends LinearScaleBase { + determineDataLimits() { + const { min, max } = this.getMinMax(true); + this.min = isNumberFinite(min) ? min : 0; + this.max = isNumberFinite(max) ? max : 1; + this.handleTickRangeOptions(); + } + computeTickLimit() { + const horizontal = this.isHorizontal(); + const length = horizontal ? this.width : this.height; + const minRotation = toRadians(this.options.ticks.minRotation); + const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 1e-3; + const tickFont = this._resolveTickFontOptions(0); + return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio)); + } + getPixelForValue(value) { + return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange); + } + getValueForPixel(pixel) { + return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange; + } + } + __publicField(LinearScale, "id", "linear"); + __publicField(LinearScale, "defaults", { + ticks: { + callback: Ticks.formatters.numeric + } + }); + const INTERVALS = { + millisecond: { + common: true, + size: 1, + steps: 1e3 + }, + second: { + common: true, + size: 1e3, + steps: 60 + }, + minute: { + common: true, + size: 6e4, + steps: 60 + }, + hour: { + common: true, + size: 36e5, + steps: 24 + }, + day: { + common: true, + size: 864e5, + steps: 30 + }, + week: { + common: false, + size: 6048e5, + steps: 4 + }, + month: { + common: true, + size: 2628e6, + steps: 12 + }, + quarter: { + common: false, + size: 7884e6, + steps: 4 + }, + year: { + common: true, + size: 3154e7 + } + }; + const UNITS = /* @__PURE__ */ Object.keys(INTERVALS); + function sorter(a, b) { + return a - b; + } + function parse(scale, input) { + if (isNullOrUndef(input)) { + return null; + } + const adapter = scale._adapter; + const { parser, round: round2, isoWeekday } = scale._parseOpts; + let value = input; + if (typeof parser === "function") { + value = parser(value); + } + if (!isNumberFinite(value)) { + value = typeof parser === "string" ? adapter.parse(value, parser) : adapter.parse(value); + } + if (value === null) { + return null; + } + if (round2) { + value = round2 === "week" && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, "isoWeek", isoWeekday) : adapter.startOf(value, round2); + } + return +value; + } + function determineUnitForAutoTicks(minUnit, min, max, capacity) { + const ilen = UNITS.length; + for (let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { + const interval = INTERVALS[UNITS[i]]; + const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER; + if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { + return UNITS[i]; + } + } + return UNITS[ilen - 1]; + } + function determineUnitForFormatting(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]; + } + function determineMajorUnit(unit) { + for (let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { + if (INTERVALS[UNITS[i]].common) { + return UNITS[i]; + } + } + } + function addTick(ticks, time, timestamps) { + if (!timestamps) { + ticks[time] = true; + } else if (timestamps.length) { + const { lo, hi } = _lookup(timestamps, time); + const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi]; + ticks[timestamp] = true; + } + } + function setMajorTicks(scale, ticks, map2, majorUnit) { + const adapter = scale._adapter; + const first = +adapter.startOf(ticks[0].value, majorUnit); + const last = ticks[ticks.length - 1].value; + let major, index2; + for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { + index2 = map2[major]; + if (index2 >= 0) { + ticks[index2].major = true; + } + } + return ticks; + } + function ticksFromTimestamps(scale, values, majorUnit) { + const ticks = []; + const map2 = {}; + const ilen = values.length; + let i, value; + for (i = 0; i < ilen; ++i) { + value = values[i]; + map2[value] = i; + ticks.push({ + value, + major: false + }); + } + return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map2, majorUnit); + } + class TimeScale extends Scale { + constructor(props) { + super(props); + this._cache = { + data: [], + labels: [], + all: [] + }; + this._unit = "day"; + this._majorUnit = void 0; + this._offsets = {}; + this._normalized = false; + this._parseOpts = void 0; + } + init(scaleOpts, opts = {}) { + const time = scaleOpts.time || (scaleOpts.time = {}); + const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date); + adapter.init(opts); + 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, index2) { + if (raw === void 0) { + return null; + } + return parse(this, raw); + } + beforeLayout() { + super.beforeLayout(); + this._cache = { + data: [], + labels: [], + all: [] + }; + } + determineDataLimits() { + const options = this.options; + const adapter = this._adapter; + const unit = options.time.unit || "day"; + let { min, max, minDefined, maxDefined } = this.getUserBounds(); + function _applyBounds(bounds) { + if (!minDefined && !isNaN(bounds.min)) { + min = Math.min(min, bounds.min); + } + if (!maxDefined && !isNaN(bounds.max)) { + max = Math.max(max, bounds.max); + } + } + if (!minDefined || !maxDefined) { + _applyBounds(this._getLabelBounds()); + if (options.bounds !== "ticks" || options.ticks.source !== "labels") { + _applyBounds(this.getMinMax(false)); + } + } + 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; + let max = Number.NEGATIVE_INFINITY; + if (arr.length) { + min = arr[0]; + max = arr[arr.length - 1]; + } + return { + min, + max + }; + } + buildTicks() { + const options = this.options; + const timeOpts = options.time; + const tickOpts = options.ticks; + const timestamps = tickOpts.source === "labels" ? this.getLabelTimestamps() : this._generate(); + if (options.bounds === "ticks" && timestamps.length) { + this.min = this._userMin || timestamps[0]; + this.max = this._userMax || timestamps[timestamps.length - 1]; + } + const min = this.min; + const max = this.max; + const ticks = _filterBetween(timestamps, min, max); + this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max)); + this._majorUnit = !tickOpts.major.enabled || this._unit === "year" ? void 0 : determineMajorUnit(this._unit); + this.initOffsets(timestamps); + if (options.reverse) { + ticks.reverse(); + } + return ticksFromTimestamps(this, ticks, this._majorUnit); + } + afterAutoSkip() { + if (this.options.offsetAfterAutoskip) { + this.initOffsets(this.ticks.map((tick) => +tick.value)); + } + } + initOffsets(timestamps = []) { + let start = 0; + let end = 0; + let first, last; + if (this.options.offset && timestamps.length) { + first = this.getDecimalForValue(timestamps[0]); + if (timestamps.length === 1) { + start = 1 - first; + } else { + start = (this.getDecimalForValue(timestamps[1]) - first) / 2; + } + last = this.getDecimalForValue(timestamps[timestamps.length - 1]); + if (timestamps.length === 1) { + end = last; + } else { + end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2; + } + } + const limit = timestamps.length < 3 ? 0.5 : 0.25; + start = _limitValue(start, 0, limit); + end = _limitValue(end, 0, limit); + this._offsets = { + start, + end, + factor: 1 / (start + 1 + end) + }; + } + _generate() { + const adapter = this._adapter; + const min = this.min; + const max = this.max; + const options = this.options; + const timeOpts = options.time; + const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min)); + const stepSize = valueOrDefault(options.ticks.stepSize, 1); + const weekday = minor === "week" ? timeOpts.isoWeekday : false; + const hasWeekday = isNumber(weekday) || weekday === true; + const ticks = {}; + let first = min; + let time, count; + if (hasWeekday) { + first = +adapter.startOf(first, "isoWeek", weekday); + } + first = +adapter.startOf(first, hasWeekday ? "day" : minor); + if (adapter.diff(max, min, minor) > 1e5 * stepSize) { + throw new Error(min + " and " + max + " are too far apart with stepSize of " + stepSize + " " + minor); + } + const timestamps = options.ticks.source === "data" && this.getDataTimestamps(); + for (time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++) { + addTick(ticks, time, timestamps); + } + if (time === max || options.bounds === "ticks" || count === 1) { + addTick(ticks, time, timestamps); + } + return Object.keys(ticks).sort(sorter).map((x) => +x); + } + getLabelForValue(value) { + const adapter = this._adapter; + const timeOpts = this.options.time; + if (timeOpts.tooltipFormat) { + return adapter.format(value, timeOpts.tooltipFormat); + } + return adapter.format(value, timeOpts.displayFormats.datetime); + } + format(value, format) { + const options = this.options; + const formats = options.time.displayFormats; + const unit = this._unit; + const fmt = format || formats[unit]; + return this._adapter.format(value, fmt); + } + _tickFormatFunction(time, index2, ticks, format) { + const options = this.options; + const formatter = options.ticks.callback; + if (formatter) { + return callback(formatter, [ + time, + index2, + ticks + ], this); + } + const formats = options.time.displayFormats; + const unit = this._unit; + const majorUnit = this._majorUnit; + const minorFormat = unit && formats[unit]; + const majorFormat = majorUnit && formats[majorUnit]; + const tick = ticks[index2]; + const major = majorUnit && majorFormat && tick && tick.major; + return this._adapter.format(time, format || (major ? majorFormat : minorFormat)); + } + generateTickLabels(ticks) { + let i, ilen, tick; + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + tick = ticks[i]; + tick.label = this._tickFormatFunction(tick.value, i, ticks); + } + } + getDecimalForValue(value) { + return value === null ? NaN : (value - this.min) / (this.max - this.min); + } + getPixelForValue(value) { + const offsets = this._offsets; + const pos = this.getDecimalForValue(value); + return this.getPixelForDecimal((offsets.start + pos) * offsets.factor); + } + getValueForPixel(pixel) { + const offsets = this._offsets; + const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + return this.min + pos * (this.max - this.min); + } + _getLabelSize(label) { + const ticksOpts = this.options.ticks; + const tickLabelWidth = this.ctx.measureText(label).width; + const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); + const cosRotation = Math.cos(angle); + const sinRotation = Math.sin(angle); + const tickFontSize = this._resolveTickFontOptions(0).size; + return { + w: tickLabelWidth * cosRotation + tickFontSize * sinRotation, + h: tickLabelWidth * sinRotation + tickFontSize * cosRotation + }; + } + _getLabelCapacity(exampleTime) { + const timeOpts = this.options.time; + const displayFormats = timeOpts.displayFormats; + const format = displayFormats[timeOpts.unit] || displayFormats.millisecond; + const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [ + exampleTime + ], this._majorUnit), format); + const size = this._getLabelSize(exampleLabel); + const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1; + return capacity > 0 ? capacity : 1; + } + getDataTimestamps() { + let timestamps = this._cache.data || []; + let i, ilen; + 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 < ilen; ++i) { + timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this)); + } + return this._cache.data = this.normalize(timestamps); + } + getLabelTimestamps() { + const timestamps = this._cache.labels || []; + let i, ilen; + if (timestamps.length) { + return timestamps; + } + const labels = this.getLabels(); + for (i = 0, ilen = labels.length; i < ilen; ++i) { + timestamps.push(parse(this, labels[i])); + } + return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps); + } + normalize(values) { + return _arrayUnique(values.sort(sorter)); + } + } + __publicField(TimeScale, "id", "time"); + __publicField(TimeScale, "defaults", { + bounds: "data", + adapters: {}, + time: { + parser: false, + unit: false, + round: false, + isoWeekday: false, + minUnit: "millisecond", + displayFormats: {} + }, + ticks: { + source: "auto", + callback: false, + major: { + enabled: false + } + } + }); + function interpolate(table, val, reverse) { + let lo = 0; + let hi = table.length - 1; + let prevSource, nextSource, prevTarget, nextTarget; + if (reverse) { + if (val >= table[lo].pos && val <= table[hi].pos) { + ({ lo, hi } = _lookupByKey(table, "pos", val)); + } + ({ pos: prevSource, time: prevTarget } = table[lo]); + ({ pos: nextSource, time: nextTarget } = table[hi]); + } else { + if (val >= table[lo].time && val <= table[hi].time) { + ({ lo, 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; + } + class TimeSeriesScale extends TimeScale { + constructor(props) { + super(props); + this._table = []; + this._minPos = void 0; + this._tableRange = void 0; + } + initOffsets() { + const timestamps = this._getTimestampsForTable(); + const 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, max } = this; + const items = []; + const table = []; + let i, ilen, prev, curr, next; + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + curr = timestamps[i]; + if (curr >= 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 < ilen; ++i) { + next = items[i + 1]; + prev = items[i - 1]; + curr = items[i]; + if (Math.round((next + prev) / 2) !== curr) { + table.push({ + time: curr, + pos: i / (ilen - 1) + }); + } + } + return table; + } + _generate() { + const min = this.min; + const max = this.max; + let timestamps = super.getDataTimestamps(); + if (!timestamps.includes(min) || !timestamps.length) { + timestamps.splice(0, 0, min); + } + if (!timestamps.includes(max) || timestamps.length === 1) { + timestamps.push(max); + } + return timestamps.sort((a, b) => a - b); + } + _getTimestampsForTable() { + let timestamps = this._cache.all || []; + if (timestamps.length) { + return timestamps; + } + const data = this.getDataTimestamps(); + const label = this.getLabelTimestamps(); + if (data.length && label.length) { + timestamps = this.normalize(data.concat(label)); + } else { + timestamps = data.length ? data : label; + } + timestamps = this._cache.all = timestamps; + return timestamps; + } + getDecimalForValue(value) { + return (interpolate(this._table, value) - this._minPos) / this._tableRange; + } + getValueForPixel(pixel) { + const offsets = this._offsets; + const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + return interpolate(this._table, decimal * this._tableRange + this._minPos, true); + } + } + __publicField(TimeSeriesScale, "id", "timeseries"); + __publicField(TimeSeriesScale, "defaults", TimeScale.defaults); + function normalizeComponent(scriptExports, render, staticRenderFns, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) { + var options = typeof scriptExports === "function" ? scriptExports.options : scriptExports; + if (render) { + options.render = render; + options.staticRenderFns = staticRenderFns; + options._compiled = true; + } + return { + exports: scriptExports, + options + }; + } + Chart.register( + LineController, + LineElement, + PointElement, + LinearScale, + CategoryScale, + index, + plugin_tooltip, + plugin_legend + ); + const CHART_COLORS = [ + "#4271ae", + "#c82829", + "#8959a8", + "#4d9a0f", + "#f5871f", + "#3e999f", + "#718c00", + "#a3685a", + "#525252", + "#eab700" + ]; + const _sfc_main = { + props: { + analyticsData: { + type: Object, + default: () => ({}) + } + }, + data() { + return { + startDate: "", + endDate: "", + data: this.analyticsData || {}, + chart: null, + users: [], + selectedEmails: [], + selectedPages: [], + showTopPagesOnly: true + }; + }, + computed: { + hasData() { + return this.data && this.data.totalVisits > 0; + }, + pagesPerSession() { + var _a; + if (!((_a = this.data) == null ? void 0 : _a.uniqueSessions)) return "0"; + return (this.data.totalVisits / this.data.uniqueSessions).toFixed(1); + }, + userOptions() { + return this.users.map((u) => ({ value: u.email, text: u.label })); + }, + pageOptions() { + var _a; + if (!((_a = this.data) == null ? void 0 : _a.visitsByPage)) return []; + return Object.keys(this.data.visitsByPage).map((p) => ({ value: p, text: p })).sort((a, b) => a.text.localeCompare(b.text)); + } + }, + watch: { + analyticsData(newVal) { + this.data = newVal || {}; + this.renderChart(); + } + }, + mounted() { + this.setDefaultDates(); + this.fetchData(); + }, + beforeUnmount() { + this.destroyChart(); + }, + methods: { + setDefaultDates() { + const now = /* @__PURE__ */ new Date(); + const thirtyDaysAgo = new Date(now); + thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); + this.endDate = now.toISOString().split("T")[0]; + this.startDate = thirtyDaysAgo.toISOString().split("T")[0]; + }, + onUserSelectionChange(emails) { + this.selectedEmails = emails; + this.fetchData(); + }, + onPageSelectionChange(pages) { + this.selectedPages = pages; + this.fetchData(); + }, + onToggleTopPages(value) { + this.showTopPagesOnly = value; + this.renderChart(); + }, + async fetchData() { + const params = new URLSearchParams(); + if (this.startDate) params.set("startDate", this.startDate); + if (this.endDate) params.set("endDate", this.endDate); + if (this.selectedEmails.length) { + params.set("emails", this.selectedEmails.join(",")); + } + if (this.selectedPages.length) { + params.set("pageNames", this.selectedPages.join(",")); + } + try { + const response = await fetch(`/analytics-data.json?${params}`); + const json = await response.json(); + console.log(json.data); + if (json.status === "success") { + this.data = json.data; + } + if (json.users && !this.users.length) { + this.users = json.users; + } + this.renderChart(); + } catch (e) { + console.error("Analytics fetch error:", e); + this.renderChart(); + } + }, + destroyChart() { + if (this.chart) { + this.chart.destroy(); + this.chart = null; + } + }, + formatDateFR(isoDate) { + const [y, m, d] = isoDate.split("-"); + return `${d}/${m}/${y}`; + }, + renderChart() { + var _a; + this.destroyChart(); + const canvas = this.$refs.chartCanvas; + if (!canvas || !((_a = this.data) == null ? void 0 : _a.visitsByDay)) return; + const allDays = Object.keys(this.data.visitsByDay); + if (!allDays.length) return; + const labels = allDays.map((d) => this.formatDateFR(d)); + const showPerPage = this.selectedPages.length === 0 && this.showTopPagesOnly && this.data.visitsByDayByPage; + let datasets; + let maxValue; + if (showPerPage) { + const topPages = this.data.visitsByPage ? Object.keys(this.data.visitsByPage) : []; + const pages = topPages; + datasets = pages.map((page, i) => { + const color2 = CHART_COLORS[i % CHART_COLORS.length]; + const values = allDays.map( + (day) => { + var _a2; + return ((_a2 = this.data.visitsByDayByPage[page]) == null ? void 0 : _a2[day]) || 0; + } + ); + return { + label: page, + data: values, + borderColor: color2, + backgroundColor: "transparent", + fill: false, + tension: 0.3, + pointRadius: 2, + pointHoverRadius: 4, + borderWidth: 2 + }; + }); + maxValue = Math.max(...datasets.flatMap((ds) => ds.data)); + } else { + const values = Object.values(this.data.visitsByDay); + datasets = [ + { + label: "Visites", + data: values, + borderColor: "#4271ae", + backgroundColor: "rgba(66, 113, 174, 0.1)", + fill: true, + tension: 0.3, + pointRadius: 3, + pointHoverRadius: 5 + } + ]; + maxValue = Math.max(...values); + } + this.chart = new Chart(canvas, { + type: "line", + data: { labels, datasets }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + tooltip: { + mode: "index", + intersect: false + }, + legend: { + display: showPerPage, + position: "bottom", + labels: { boxWidth: 12, padding: 12 } + } + }, + scales: { + y: { + beginAtZero: true, + max: maxValue + 3, + ticks: { precision: 0 } + } + } + } + }); + } + } + }; + var _sfc_render = function render() { + var _vm = this, _c = _vm._self._c; + return _c("div", { staticClass: "k-analytics-dashboard" }, [_c("div", { staticClass: "k-analytics-filters" }, [_c("div", { staticClass: "k-analytics-date-filter" }, [_vm._m(0), _c("div", { staticClass: "k-date-inputs-wrapper" }, [_c("label", [_vm._v(" Du "), _c("input", { directives: [{ name: "model", rawName: "v-model", value: _vm.startDate, expression: "startDate" }], attrs: { "type": "date" }, domProps: { "value": _vm.startDate }, on: { "change": _vm.fetchData, "input": function($event) { + if ($event.target.composing) return; + _vm.startDate = $event.target.value; + } } })]), _c("label", [_vm._v(" Au "), _c("input", { directives: [{ name: "model", rawName: "v-model", value: _vm.endDate, expression: "endDate" }], attrs: { "type": "date" }, domProps: { "value": _vm.endDate }, on: { "change": _vm.fetchData, "input": function($event) { + if ($event.target.composing) return; + _vm.endDate = $event.target.value; + } } })])])]), _c("div", { staticClass: "k-analytics-user-filter" }, [_c("k-multiselect-field", { attrs: { "options": _vm.userOptions, "value": _vm.selectedEmails, "label": "Filtrer par utilisateur(s)", "search": "true", "name": "user" }, on: { "input": _vm.onUserSelectionChange } })], 1), _c("div", { staticClass: "k-analytics-page-filter" }, [_c("k-multiselect-field", { attrs: { "options": _vm.pageOptions, "value": _vm.selectedPages, "label": "Filtrer par page(s)", "search": "true", "name": "page" }, on: { "input": _vm.onPageSelectionChange } })], 1)]), !_vm.hasData ? _c("div", { staticClass: "k-analytics-empty" }, [_c("p", [_vm._v("Aucune donnée à afficher")])]) : [_c("div", { staticClass: "k-analytics-grid" }, [_c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Sessions uniques")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.data.uniqueSessions))])]), _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages vues")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.data.totalVisits))])]), _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages / session")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.pagesPerSession))])])]), _c("div", { staticClass: "k-analytics-chart-container" }, [_c("div", { staticClass: "k-analytics-chart-header" }, [_c("h3", [_vm._v("Visites par jour")]), _vm.selectedPages.length === 0 ? _c("k-toggle-field", { attrs: { "value": _vm.showTopPagesOnly, "before": "Moyenne", "after": "Pages les + visitées", "text": " ", "name": "topPages" }, on: { "input": _vm.onToggleTopPages } }) : _vm._e()], 1), _c("canvas", { ref: "chartCanvas" })]), _vm.data.visitsByPage && Object.keys(_vm.data.visitsByPage).length ? _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages les plus visitées")]), _c("ul", { staticClass: "k-analytics-list" }, _vm._l(_vm.data.visitsByPage, function(count, page) { + return _c("li", { key: page }, [_c("span", { staticClass: "k-analytics-list-label" }, [_vm._v(_vm._s(page))]), _c("span", { staticClass: "k-analytics-list-value" }, [_vm._v(_vm._s(count))])]); + }), 0)]) : _vm._e()]], 2); + }; + var _sfc_staticRenderFns = [function() { + var _vm = this, _c = _vm._self._c; + return _c("header", { staticClass: "k-field-header" }, [_c("label", { staticClass: "k-label k-field-label", attrs: { "title": "Filtrer par dates" } }, [_c("span", { staticClass: "k-label-text" }, [_vm._v("Filtrer par dates ")])])]); + }]; + _sfc_render._withStripped = true; + var __component__ = /* @__PURE__ */ normalizeComponent( + _sfc_main, + _sfc_render, + _sfc_staticRenderFns + ); + __component__.options.__file = "/Users/adrienpayet/Documents/code/en-cours/design-to-pack/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue"; + const AnalyticsDashboard = __component__.exports; + window.panel.plugin("adrienpayet/analytics", { + fields: { + "analytics-dashboard": AnalyticsDashboard + } + }); +})(); diff --git a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue index ea276b1..addff4f 100644 --- a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue +++ b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue @@ -61,7 +61,18 @@
-

Visites par jour

+
+

Visites par jour

+ +
@@ -135,6 +146,7 @@ export default { users: [], selectedEmails: [], selectedPages: [], + showTopPagesOnly: true, }; }, @@ -193,6 +205,11 @@ export default { this.fetchData(); }, + onToggleTopPages(value) { + this.showTopPagesOnly = value; + this.renderChart(); + }, + async fetchData() { const params = new URLSearchParams(); if (this.startDate) params.set('startDate', this.startDate); @@ -248,13 +265,18 @@ export default { const labels = allDays.map((d) => this.formatDateFR(d)); const showPerPage = - this.selectedPages.length === 0 && this.data.visitsByDayByPage; + this.selectedPages.length === 0 && + this.showTopPagesOnly && + this.data.visitsByDayByPage; let datasets; let maxValue; if (showPerPage) { - const pages = Object.keys(this.data.visitsByDayByPage); + const topPages = this.data.visitsByPage + ? Object.keys(this.data.visitsByPage) + : []; + const pages = topPages; datasets = pages.map((page, i) => { const color = CHART_COLORS[i % CHART_COLORS.length]; const values = allDays.map( @@ -272,9 +294,7 @@ export default { borderWidth: 2, }; }); - maxValue = Math.max( - ...datasets.flatMap((ds) => ds.data) - ); + maxValue = Math.max(...datasets.flatMap((ds) => ds.data)); } else { const values = Object.values(this.data.visitsByDay); datasets = [ @@ -416,11 +436,18 @@ export default { box-shadow: var(--shadow); } -.k-analytics-chart-container h3 { +.k-analytics-chart-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.k-analytics-chart-header h3 { font-size: 0.875rem; font-weight: 600; color: var(--color-text); - margin: 0 0 1rem 0; + margin: 0; } .k-analytics-chart-container canvas { From 17bf3f83c42c8cd17aff647d4aa08c357ac570f3 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 13:26:01 +0100 Subject: [PATCH 42/52] analytics blueprint : disable panel view buttons --- public/site/plugins/analytics/blueprints/pages/analytics.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/site/plugins/analytics/blueprints/pages/analytics.yml b/public/site/plugins/analytics/blueprints/pages/analytics.yml index 5c2597c..335f6d6 100644 --- a/public/site/plugins/analytics/blueprints/pages/analytics.yml +++ b/public/site/plugins/analytics/blueprints/pages/analytics.yml @@ -1,5 +1,9 @@ title: Analytics icon: chart +buttons: false + +status: + - unlisted columns: - width: 1/1 From bcead4e694f1397a27bb8f9b37ae7ef6176e7c82 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 13:27:23 +0100 Subject: [PATCH 43/52] analytics blueprint : disabled change title --- public/site/plugins/analytics/blueprints/pages/analytics.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/site/plugins/analytics/blueprints/pages/analytics.yml b/public/site/plugins/analytics/blueprints/pages/analytics.yml index 335f6d6..432b91e 100644 --- a/public/site/plugins/analytics/blueprints/pages/analytics.yml +++ b/public/site/plugins/analytics/blueprints/pages/analytics.yml @@ -1,6 +1,8 @@ title: Analytics icon: chart buttons: false +options: + changeTitle: false status: - unlisted From 075e511a6a67c080e1498db662b3cae27c636f5b Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 13:29:02 +0100 Subject: [PATCH 44/52] plugin > analytics > chart > caption : sort alpahetically --- public/site/plugins/analytics/index.js | 2 +- .../plugins/analytics/src/components/AnalyticsDashboard.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/site/plugins/analytics/index.js b/public/site/plugins/analytics/index.js index e07aa94..3253af0 100644 --- a/public/site/plugins/analytics/index.js +++ b/public/site/plugins/analytics/index.js @@ -11604,7 +11604,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy let maxValue; if (showPerPage) { const topPages = this.data.visitsByPage ? Object.keys(this.data.visitsByPage) : []; - const pages = topPages; + const pages = topPages.sort((a, b) => a.localeCompare(b)); datasets = pages.map((page, i) => { const color2 = CHART_COLORS[i % CHART_COLORS.length]; const values = allDays.map( diff --git a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue index addff4f..cd8f1aa 100644 --- a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue +++ b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue @@ -276,7 +276,7 @@ export default { const topPages = this.data.visitsByPage ? Object.keys(this.data.visitsByPage) : []; - const pages = topPages; + const pages = topPages.sort((a, b) => a.localeCompare(b)); datasets = pages.map((page, i) => { const color = CHART_COLORS[i % CHART_COLORS.length]; const values = allDays.map( From ea90f512cf887c16d02cb5afe3d405adcb51a0c5 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 14:01:27 +0100 Subject: [PATCH 45/52] =?UTF-8?q?feat:=20inversion=20relation=20User?= =?UTF-8?q?=E2=86=92Projects,=20les=20projets=20pointent=20vers=20les=20ut?= =?UTF-8?q?ilisateurs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le champ `users` est désormais sur le blueprint projet. Les blueprints pochet/client perdent leur champ `projects`. La logique PHP (user-projects, managers, controller, template, mark-all-read) lit project.users au lieu de user.projects. Script de migration inclus. Co-Authored-By: Claude Opus 4.6 --- public/site/blueprints/pages/project.yml | 4 + public/site/blueprints/users/client.yml | 7 +- public/site/blueprints/users/pochet.yml | 5 -- public/site/config/config.php | 1 + .../config/routes/migrate-user-projects.php | 75 +++++++++++++++++++ public/site/controllers/site.php | 8 +- public/site/models/project.php | 12 +-- .../notifications/routes/mark-all-read.php | 2 +- public/site/plugins/user-projects/index.php | 10 ++- public/site/templates/projects.json.php | 2 +- 10 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 public/site/config/routes/migrate-user-projects.php diff --git a/public/site/blueprints/pages/project.yml b/public/site/blueprints/pages/project.yml index 6962aa5..6243b53 100644 --- a/public/site/blueprints/pages/project.yml +++ b/public/site/blueprints/pages/project.yml @@ -85,6 +85,10 @@ tabs: query: page.logo.toFile layout: cardlets required: true + users: + label: Utilisateurs assignés + type: users + multiple: true - width: 2/3 sections: diff --git a/public/site/blueprints/users/client.yml b/public/site/blueprints/users/client.yml index 9476739..f321069 100644 --- a/public/site/blueprints/users/client.yml +++ b/public/site/blueprints/users/client.yml @@ -36,7 +36,7 @@ sections: fr: Projet(s) en cours en: Current project(s) link: https://designtopack.morphozbygroupepochet.com/ - value: "{{ user.projects.toPages.count }}" + value: "{{ user.currentProjects.count }}" icon: folder content: label: ' ' @@ -52,8 +52,3 @@ sections: layout: cardlets required: true width: 1/2 - projects: - label: Projets - type: pages - query: page('projects').children - width: 1/2 diff --git a/public/site/blueprints/users/pochet.yml b/public/site/blueprints/users/pochet.yml index 2030b05..fab2a50 100644 --- a/public/site/blueprints/users/pochet.yml +++ b/public/site/blueprints/users/pochet.yml @@ -14,11 +14,6 @@ fields: - Sales Manager default: Project Panager width: 1/4 - projects: - label: Projets - type: pages - query: page('projects').children - width: 3/4 hiddenProjects: label: Projets masqués type: pages diff --git a/public/site/config/config.php b/public/site/config/config.php index 4ac5104..280672f 100644 --- a/public/site/config/config.php +++ b/public/site/config/config.php @@ -44,6 +44,7 @@ return [ require(__DIR__ . '/routes/request-project-creation.php'), require(__DIR__ . '/routes/request-optimization-appointment.php'), require(__DIR__ . '/routes/migrate-notifications.php'), + require(__DIR__ . '/routes/migrate-user-projects.php'), ], 'hooks' => [ 'page.create:after' => require_once(__DIR__ . '/hooks/create-steps.php'), diff --git a/public/site/config/routes/migrate-user-projects.php b/public/site/config/routes/migrate-user-projects.php new file mode 100644 index 0000000..abe71bf --- /dev/null +++ b/public/site/config/routes/migrate-user-projects.php @@ -0,0 +1,75 @@ + 'migrate-user-projects.json', + 'method' => 'POST', + 'action' => function () { + $user = kirby()->user(); + + if (!$user || $user->role()->id() !== 'admin') { + return [ + 'status' => 'error', + 'message' => 'Cette action nécessite les droits administrateur.' + ]; + } + + $migrated = []; + $errors = []; + + $nonAdminUsers = kirby()->users()->filter(fn($u) => $u->role()->id() !== 'admin'); + + foreach ($nonAdminUsers as $u) { + if (!$u->projects()->exists() || $u->projects()->isEmpty()) { + continue; + } + + $userProjects = $u->projects()->toPages(); + + foreach ($userProjects as $project) { + try { + $currentUsers = $project->users()->yaml(); + + $userUuid = $u->uuid()->toString(); + + if (in_array($userUuid, $currentUsers)) { + continue; + } + + $currentUsers[] = $userUuid; + $project->update(['users' => $currentUsers]); + + $migrated[] = [ + 'user' => $u->name()->value(), + 'email' => $u->email(), + 'project' => $project->title()->value(), + ]; + } catch (\Throwable $th) { + $errors[] = [ + 'user' => $u->email(), + 'project' => $project->title()->value(), + 'error' => $th->getMessage() + ]; + } + } + } + + return [ + 'status' => 'success', + 'message' => count($migrated) . ' assignations migrées.', + 'migrated' => $migrated, + 'errors' => $errors + ]; + } +]; diff --git a/public/site/controllers/site.php b/public/site/controllers/site.php index 27524bd..14a8e61 100644 --- a/public/site/controllers/site.php +++ b/public/site/controllers/site.php @@ -27,8 +27,12 @@ return function ($page, $kirby, $site) { } } - if ($kirby->user()->projects()->exists() && $kirby->user()->projects()->isNotEmpty()) { - $userData['projects'] = $kirby->user()->projects()->toPages()->map(function ($project) { + $userProjects = $kirby->user()->currentProjects()->merge( + $kirby->user()->archivedProjects() + ); + + if ($userProjects->count() > 0) { + $userData['projects'] = $userProjects->map(function ($project) { return [ "title" => (string) $project->title(), "uri" => (string) $project->uri(), diff --git a/public/site/models/project.php b/public/site/models/project.php index 15f663e..4ea0543 100644 --- a/public/site/models/project.php +++ b/public/site/models/project.php @@ -222,12 +222,12 @@ class ProjectPage extends NotificationsPage { // } public function managers() { - return kirby()->users()->filter(function($user) { - if ($user->role() != 'admin' && $user->projects()->isEmpty()) { - return false; - } - - return $user->role() == 'admin' || $user->projects()->toPages()->has($this); + if ($this->users()->isEmpty()) { + return kirby()->users()->filterBy('role', 'admin'); + } + $projectUsers = $this->users()->toUsers(); + return kirby()->users()->filter(function($user) use ($projectUsers) { + return $user->role() == 'admin' || $projectUsers->has($user); }); } } \ No newline at end of file diff --git a/public/site/plugins/notifications/routes/mark-all-read.php b/public/site/plugins/notifications/routes/mark-all-read.php index c06cc80..7c2a071 100644 --- a/public/site/plugins/notifications/routes/mark-all-read.php +++ b/public/site/plugins/notifications/routes/mark-all-read.php @@ -23,7 +23,7 @@ return [ if ($user->role()->name() === 'admin') { $projects = page('projects')->children()->toArray(); } else { - $projects = $user->projects()->toPages()->toArray(); + $projects = $user->currentProjects()->toArray(); } $count = $collector->markAllAsRead($projects, $user); diff --git a/public/site/plugins/user-projects/index.php b/public/site/plugins/user-projects/index.php index 15c5aad..733a9b3 100644 --- a/public/site/plugins/user-projects/index.php +++ b/public/site/plugins/user-projects/index.php @@ -5,16 +5,18 @@ Kirby::plugin('adrienpayet/pdc-authorized-projects', [ 'currentProjects' => function() { if ($this->role() == 'admin') { return page('projects')->children()->listed(); - } else { - return $this->projects()->toPages()->listed(); } + return page('projects')->children()->listed()->filter(function($project) { + return $project->users()->toUsers()->has($this); + }); }, 'archivedProjects' => function() { if ($this->role() == 'admin') { return page('projects')->children()->unlisted(); - } else { - return $this->projects()->toPages()->unlisted(); } + return page('projects')->children()->unlisted()->filter(function($project) { + return $project->users()->toUsers()->has($this); + }); }, ] ]); diff --git a/public/site/templates/projects.json.php b/public/site/templates/projects.json.php index 2b2eaad..542ee7e 100644 --- a/public/site/templates/projects.json.php +++ b/public/site/templates/projects.json.php @@ -46,7 +46,7 @@ $currentUser = $kirby->user(); try { $children = $currentUser->role() == 'admin' ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser))->values() - : $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser))->values(); + : $currentUser->currentProjects()->map(fn($project) => getProjectData($project, $currentUser))->values(); } catch (\Throwable $th) { throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); $children = []; From 6896e558ab0f519fb3fb8bb29d99118bf5a2d459 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 15:52:20 +0100 Subject: [PATCH 46/52] fix track.json route --- public/site/plugins/analytics/routes/track.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/site/plugins/analytics/routes/track.php b/public/site/plugins/analytics/routes/track.php index e02a0e2..2001c4d 100644 --- a/public/site/plugins/analytics/routes/track.php +++ b/public/site/plugins/analytics/routes/track.php @@ -43,7 +43,7 @@ return [ } $visit = new Visit([ - 'email' => $user->email()->value(), + 'email' => (string) $user->email(), 'country' => $country, 'sessionId' => $data['sessionId'] ?? '', 'pageUrl' => $data['pageUrl'] ?? '', From 059fb0e5b0823857e75ac8628948441751cca84c Mon Sep 17 00:00:00 2001 From: isUnknown Date: Tue, 3 Mar 2026 17:18:50 +0100 Subject: [PATCH 47/52] fix: guard contre les projets avec un mauvais template Kirby MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Projets utilisant default.txt au lieu de project.txt retournent des Field objects au lieu de tableaux pour steps et notifications. Ajout de Array.isArray() pour éviter que ces projets cassent l'ensemble des notifications, avec console.error pour les identifier. Co-Authored-By: Claude Sonnet 4.6 --- src/components/project/Project.vue | 1 + src/stores/user.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/project/Project.vue b/src/components/project/Project.vue index 7d048e3..3dfaef3 100644 --- a/src/components/project/Project.vue +++ b/src/components/project/Project.vue @@ -21,6 +21,7 @@
    { if (!projects.value || !user.value) return []; return projects.value.flatMap((project) => { - if (!project.notifications) return []; + if (!Array.isArray(project.notifications)) { + if (project.notifications) console.error(`[Notifications] project.notifications n'est pas un tableau pour "${project.slug}"`, project.notifications); + return []; + } return project.notifications.map((notification) => ({ ...notification, From 2d3be6d14040483fc3b37ce5ff9115e6ce3fceac Mon Sep 17 00:00:00 2001 From: isUnknown Date: Wed, 4 Mar 2026 13:40:42 +0100 Subject: [PATCH 48/52] =?UTF-8?q?i18n:=20traduire=20les=20cha=C3=AEnes=20h?= =?UTF-8?q?ardcod=C3=A9es=20restantes=20en=20anglais/fran=C3=A7ais?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DateTime & Project : locale dayjs dynamique selon la langue active - Images : bouton "Ajouter une ou plusieurs images" via t() - ImagesEditPanel : placeholder, legend tags et bouton ajout via t() - DynamicView : clé PHP "Autres pistes" traduite via t() - Nouvelles clés : forms.selectTags, buttons.addSelectedImages, virtualSample.otherTracks Co-Authored-By: Claude Sonnet 4.6 --- src/components/project/Project.vue | 18 +++++++++--------- src/components/project/brief/Images.vue | 4 +++- .../brief/add-images-modal/ImagesEditPanel.vue | 8 +++++--- src/components/project/cards/DateTime.vue | 5 ++++- .../project/virtual-sample/DynamicView.vue | 2 +- src/locales/en.json | 9 ++++++--- src/locales/fr.json | 9 ++++++--- 7 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/components/project/Project.vue b/src/components/project/Project.vue index 3dfaef3..901805f 100644 --- a/src/components/project/Project.vue +++ b/src/components/project/Project.vue @@ -15,8 +15,8 @@

    - Dernière mise à jour le - + {{ t('dates.updatedOn') }} +

    @@ -42,17 +42,17 @@ diff --git a/src/components/project/brief/Images.vue b/src/components/project/brief/Images.vue index 2616eea..c62cc8b 100644 --- a/src/components/project/brief/Images.vue +++ b/src/components/project/brief/Images.vue @@ -10,7 +10,7 @@ class="flex flex-col | bg-white | border border-grey-200 | text-grey-800 | font-medium | rounded-2xl" @click="isAddImagesModalOpen = true" > - Ajouter une ou plusieurs images + {{ t('forms.addImages') }} @@ -109,8 +109,10 @@ import StringUtils from "../../../../utils/string"; import { storeToRefs } from "pinia"; import { useAddImagesModalStore } from "../../../../stores/addImagesModal"; import { computed } from "vue"; +import { useI18n } from "vue-i18n"; const { page } = storeToRefs(usePageStore()); +const { t } = useI18n(); const { activeTab } = storeToRefs(useAddImagesModalStore()); diff --git a/src/components/project/cards/DateTime.vue b/src/components/project/cards/DateTime.vue index c27a04e..ccbb529 100644 --- a/src/components/project/cards/DateTime.vue +++ b/src/components/project/cards/DateTime.vue @@ -3,7 +3,7 @@ {{ dayjs(date).locale(locale).format("DD MMMM YYYY") }} @@ -11,6 +11,9 @@ diff --git a/src/components/project/virtual-sample/DynamicView.vue b/src/components/project/virtual-sample/DynamicView.vue index ad3c65e..a725334 100644 --- a/src/components/project/virtual-sample/DynamicView.vue +++ b/src/components/project/virtual-sample/DynamicView.vue @@ -87,7 +87,7 @@ const tracks = computed(() => { for (const key in raw) { list.push({ - title: key, + title: key === 'Autres pistes' ? t('virtualSample.otherTracks') : key, slug: slugify(key), variations: raw[key] || [], }); diff --git a/src/locales/en.json b/src/locales/en.json index 0ddf7d5..12c6e26 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -70,7 +70,8 @@ "loopAnimation": "Loop animation", "stopAnimation": "Stop animation", "compareTracks": "Compare tracks", - "exitCompare": "Exit compare mode" + "exitCompare": "Exit compare mode", + "addSelectedImages": "Add selected images" }, "forms": { "email": "Email", @@ -94,7 +95,8 @@ "tags": "Tags", "selectVariation": "Select a variation", "uploadedFiles": "Uploaded files", - "addImages": "Add one or more images" + "addImages": "Add one or more images", + "selectTags": "Select one or more tags" }, "auth": { "login": "Login", @@ -184,7 +186,8 @@ "dynamicPresentation": "Dynamic presentation", "staticView": "Static view", "noContent": "Content not available for this track", - "selectToCompare": "Select the track you want to compare" + "selectToCompare": "Select the track you want to compare", + "otherTracks": "Other tracks" }, "errors": { "saveFailed": "Save failed", diff --git a/src/locales/fr.json b/src/locales/fr.json index b155bcb..df53b21 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -70,7 +70,8 @@ "loopAnimation": "Animation en boucle", "stopAnimation": "Arrêter l'animation", "compareTracks": "Comparer les pistes", - "exitCompare": "Quitter le mode comparer" + "exitCompare": "Quitter le mode comparer", + "addSelectedImages": "Ajouter les images sélectionnées" }, "forms": { "email": "Email", @@ -94,7 +95,8 @@ "tags": "Tags", "selectVariation": "Sélectionnez une déclinaison", "uploadedFiles": "Fichiers importés", - "addImages": "Ajouter une ou plusieurs images" + "addImages": "Ajouter une ou plusieurs images", + "selectTags": "Sélectionner un ou plusieurs tags" }, "auth": { "login": "Connexion", @@ -184,7 +186,8 @@ "dynamicPresentation": "Présentation dynamique", "staticView": "Vue statique", "noContent": "Contenu non disponible pour cette piste", - "selectToCompare": "Sélectionnez sur la piste que vous souhaitez comparer" + "selectToCompare": "Sélectionnez sur la piste que vous souhaitez comparer", + "otherTracks": "Autres pistes" }, "errors": { "saveFailed": "Erreur lors de la sauvegarde", From cd1b374d103697d794d53da0322c0b6989e925b6 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Wed, 4 Mar 2026 13:48:33 +0100 Subject: [PATCH 49/52] fix: corriger les erreurs sur la route /login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Échapper le @ dans les placeholders email (vue-i18n v9 interprète @ comme message linking) - Gérer l'erreur silencieusement quand projects.json est inaccessible (user non connecté) Co-Authored-By: Claude Sonnet 4.6 --- src/locales/en.json | 2 +- src/locales/fr.json | 2 +- src/stores/projects.js | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 12c6e26..78442ce 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -75,7 +75,7 @@ }, "forms": { "email": "Email", - "emailPlaceholder": "email@example.com", + "emailPlaceholder": "email{'@'}example.com", "password": "Password", "passwordPlaceholder": "Minimum 8 characters", "newPassword": "New password", diff --git a/src/locales/fr.json b/src/locales/fr.json index df53b21..5f6cd69 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -75,7 +75,7 @@ }, "forms": { "email": "Email", - "emailPlaceholder": "mail@exemple.com", + "emailPlaceholder": "mail{'@'}exemple.com", "password": "Mot de passe", "passwordPlaceholder": "Minimum 8 caractères", "newPassword": "Nouveau mot de passe", diff --git a/src/stores/projects.js b/src/stores/projects.js index 870a543..be62742 100644 --- a/src/stores/projects.js +++ b/src/stores/projects.js @@ -52,6 +52,8 @@ export const useProjectsStore = defineStore('projects', () => { isProjectsLoading.value = false; projects.value = json.page.children; // }, 3000); + }).catch(() => { + isProjectsLoading.value = false; }); // Functions From 7425a1493dc9f4cc03544515aa67f52a82bdcf5e Mon Sep 17 00:00:00 2001 From: isUnknown Date: Wed, 4 Mar 2026 14:44:00 +0100 Subject: [PATCH 50/52] =?UTF-8?q?feat:=20admin=20voit=20tous=20les=20proje?= =?UTF-8?q?ts=20seulement=20si=20aucun=20ne=20lui=20est=20assign=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Si un admin a au moins un projet assigné, il ne voit que ses projets. S'il n'en a aucun, il voit tous les projets (comportement précédent). Co-Authored-By: Claude Sonnet 4.6 --- public/site/plugins/user-projects/index.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/public/site/plugins/user-projects/index.php b/public/site/plugins/user-projects/index.php index 733a9b3..c1a4f3f 100644 --- a/public/site/plugins/user-projects/index.php +++ b/public/site/plugins/user-projects/index.php @@ -3,20 +3,21 @@ Kirby::plugin('adrienpayet/pdc-authorized-projects', [ 'userMethods' => [ 'currentProjects' => function() { - if ($this->role() == 'admin') { - return page('projects')->children()->listed(); + $listed = page('projects')->children()->listed(); + if ($this->role() == 'admin' && $this->hasNoAssignedProjects()) { + return $listed; } - return page('projects')->children()->listed()->filter(function($project) { - return $project->users()->toUsers()->has($this); - }); + return $listed->filter(fn($project) => $project->users()->toUsers()->has($this)); }, 'archivedProjects' => function() { - if ($this->role() == 'admin') { - return page('projects')->children()->unlisted(); + $unlisted = page('projects')->children()->unlisted(); + if ($this->role() == 'admin' && $this->hasNoAssignedProjects()) { + return $unlisted; } - return page('projects')->children()->unlisted()->filter(function($project) { - return $project->users()->toUsers()->has($this); - }); + return $unlisted->filter(fn($project) => $project->users()->toUsers()->has($this)); + }, + 'hasNoAssignedProjects' => function() { + return page('projects')->children()->filter(fn($p) => $p->users()->toUsers()->has($this))->isEmpty(); }, ] ]); From aceb8c7fdb46fc753ac68718c6f100aa8ec90b4b Mon Sep 17 00:00:00 2001 From: isUnknown Date: Wed, 4 Mar 2026 15:07:04 +0100 Subject: [PATCH 51/52] fix: appliquer le filtre d'assignation admin dans projects.json.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le template bypassait currentProjects() pour les admins. Maintenant : - Admin sans projets assignés → tous les projets (comportement d'origine) - Admin avec projets assignés → uniquement ses projets (listed + unlisted) - Autres rôles → inchangé Co-Authored-By: Claude Sonnet 4.6 --- public/site/templates/projects.json.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/public/site/templates/projects.json.php b/public/site/templates/projects.json.php index 542ee7e..6f589eb 100644 --- a/public/site/templates/projects.json.php +++ b/public/site/templates/projects.json.php @@ -44,9 +44,12 @@ function getProjectData($project, $user) $currentUser = $kirby->user(); try { - $children = $currentUser->role() == 'admin' - ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser))->values() - : $currentUser->currentProjects()->map(fn($project) => getProjectData($project, $currentUser))->values(); + if ($currentUser->role() == 'admin' && $currentUser->hasNoAssignedProjects()) { + $projectCollection = $page->childrenAndDrafts(); + } else { + $projectCollection = $currentUser->currentProjects()->merge($currentUser->archivedProjects()); + } + $children = $projectCollection->map(fn($project) => getProjectData($project, $currentUser))->values(); } catch (\Throwable $th) { throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); $children = []; From 86d48e415cf98f12a4101b885c4af219a0bdb543 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Sun, 8 Mar 2026 17:46:55 +0100 Subject: [PATCH 52/52] fix: ajuster le style du plugin analytics et corriger le nom du toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Supprime le doublon de séparateur dans le menu - Retire les box-shadow/padding superflus, ajuste les marges - Corrige le name du toggle topPages en kebab-case (top-pages) Co-Authored-By: Claude Sonnet 4.6 --- public/site/config/menu.php | 1 - public/site/plugins/analytics/index.css | 18 ++++----------- public/site/plugins/analytics/index.js | 2 +- .../src/components/AnalyticsDashboard.vue | 22 +++++-------------- 4 files changed, 11 insertions(+), 32 deletions(-) diff --git a/public/site/config/menu.php b/public/site/config/menu.php index dc00b7e..8198cbd 100644 --- a/public/site/config/menu.php +++ b/public/site/config/menu.php @@ -59,7 +59,6 @@ $menu = [ } ], '-', - '-', 'analytics' => [ 'label' => 'Analytics', 'icon' => 'chart', diff --git a/public/site/plugins/analytics/index.css b/public/site/plugins/analytics/index.css index a0dd05a..67f9106 100644 --- a/public/site/plugins/analytics/index.css +++ b/public/site/plugins/analytics/index.css @@ -1,18 +1,13 @@ -.k-analytics-dashboard { - padding: 1.5rem 0; -} .k-analytics-filters { display: flex; gap: 1rem; - margin-bottom: 1.5rem; } .k-analytics-filters label { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; - color: var(--color-text-light); } .k-date-inputs-wrapper { display: flex; @@ -29,33 +24,32 @@ display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; - margin-bottom: 1.5rem; + margin: 3rem 0; } .k-analytics-user-filter { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; - color: var(--color-text-light); margin-left: 2rem; } .k-field-name-user, .k-field-name-page { min-width: 15rem; } +.k-field-name-top-pages { + margin-left: 1rem; +} .k-analytics-page-filter { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; - color: var(--color-text-light); margin-left: 2rem; } .k-analytics-card { background: var(--color-background); border-radius: var(--rounded); - padding: 1.5rem; - box-shadow: var(--shadow); } .k-analytics-card h3 { font-size: 0.75rem; @@ -74,13 +68,10 @@ .k-analytics-chart-container { background: var(--color-background); border-radius: var(--rounded); - padding: 1.5rem; margin-bottom: 1.5rem; - box-shadow: var(--shadow); } .k-analytics-chart-header { display: flex; - justify-content: space-between; align-items: center; margin-bottom: 1rem; } @@ -98,7 +89,6 @@ border-radius: var(--rounded); padding: 3rem; text-align: center; - box-shadow: var(--shadow); } .k-analytics-empty p { margin: 0; diff --git a/public/site/plugins/analytics/index.js b/public/site/plugins/analytics/index.js index 3253af0..51f7aa4 100644 --- a/public/site/plugins/analytics/index.js +++ b/public/site/plugins/analytics/index.js @@ -11679,7 +11679,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy } } })]), _c("label", [_vm._v(" Au "), _c("input", { directives: [{ name: "model", rawName: "v-model", value: _vm.endDate, expression: "endDate" }], attrs: { "type": "date" }, domProps: { "value": _vm.endDate }, on: { "change": _vm.fetchData, "input": function($event) { if ($event.target.composing) return; _vm.endDate = $event.target.value; - } } })])])]), _c("div", { staticClass: "k-analytics-user-filter" }, [_c("k-multiselect-field", { attrs: { "options": _vm.userOptions, "value": _vm.selectedEmails, "label": "Filtrer par utilisateur(s)", "search": "true", "name": "user" }, on: { "input": _vm.onUserSelectionChange } })], 1), _c("div", { staticClass: "k-analytics-page-filter" }, [_c("k-multiselect-field", { attrs: { "options": _vm.pageOptions, "value": _vm.selectedPages, "label": "Filtrer par page(s)", "search": "true", "name": "page" }, on: { "input": _vm.onPageSelectionChange } })], 1)]), !_vm.hasData ? _c("div", { staticClass: "k-analytics-empty" }, [_c("p", [_vm._v("Aucune donnée à afficher")])]) : [_c("div", { staticClass: "k-analytics-grid" }, [_c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Sessions uniques")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.data.uniqueSessions))])]), _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages vues")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.data.totalVisits))])]), _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages / session")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.pagesPerSession))])])]), _c("div", { staticClass: "k-analytics-chart-container" }, [_c("div", { staticClass: "k-analytics-chart-header" }, [_c("h3", [_vm._v("Visites par jour")]), _vm.selectedPages.length === 0 ? _c("k-toggle-field", { attrs: { "value": _vm.showTopPagesOnly, "before": "Moyenne", "after": "Pages les + visitées", "text": " ", "name": "topPages" }, on: { "input": _vm.onToggleTopPages } }) : _vm._e()], 1), _c("canvas", { ref: "chartCanvas" })]), _vm.data.visitsByPage && Object.keys(_vm.data.visitsByPage).length ? _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages les plus visitées")]), _c("ul", { staticClass: "k-analytics-list" }, _vm._l(_vm.data.visitsByPage, function(count, page) { + } } })])])]), _c("div", { staticClass: "k-analytics-user-filter" }, [_c("k-multiselect-field", { attrs: { "options": _vm.userOptions, "value": _vm.selectedEmails, "label": "Filtrer par utilisateur(s)", "search": "true", "name": "user" }, on: { "input": _vm.onUserSelectionChange } })], 1), _c("div", { staticClass: "k-analytics-page-filter" }, [_c("k-multiselect-field", { attrs: { "options": _vm.pageOptions, "value": _vm.selectedPages, "label": "Filtrer par page(s)", "search": "true", "name": "page" }, on: { "input": _vm.onPageSelectionChange } })], 1)]), !_vm.hasData ? _c("div", { staticClass: "k-analytics-empty" }, [_c("p", [_vm._v("Aucune donnée à afficher")])]) : [_c("div", { staticClass: "k-analytics-grid" }, [_c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Sessions uniques")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.data.uniqueSessions))])]), _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages vues")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.data.totalVisits))])]), _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages / session")]), _c("div", { staticClass: "k-analytics-metric" }, [_vm._v(_vm._s(_vm.pagesPerSession))])])]), _c("div", { staticClass: "k-analytics-chart-container" }, [_c("div", { staticClass: "k-analytics-chart-header" }, [_c("h3", [_vm._v("Visites par jour")]), _vm.selectedPages.length === 0 ? _c("k-toggle-field", { attrs: { "value": _vm.showTopPagesOnly, "before": "Moyenne", "after": "Pages les + visitées", "text": " ", "name": "top-pages" }, on: { "input": _vm.onToggleTopPages } }) : _vm._e()], 1), _c("canvas", { ref: "chartCanvas" })]), _vm.data.visitsByPage && Object.keys(_vm.data.visitsByPage).length ? _c("div", { staticClass: "k-analytics-card" }, [_c("h3", [_vm._v("Pages les plus visitées")]), _c("ul", { staticClass: "k-analytics-list" }, _vm._l(_vm.data.visitsByPage, function(count, page) { return _c("li", { key: page }, [_c("span", { staticClass: "k-analytics-list-label" }, [_vm._v(_vm._s(page))]), _c("span", { staticClass: "k-analytics-list-value" }, [_vm._v(_vm._s(count))])]); }), 0)]) : _vm._e()]], 2); }; diff --git a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue index cd8f1aa..879bd59 100644 --- a/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue +++ b/public/site/plugins/analytics/src/components/AnalyticsDashboard.vue @@ -69,7 +69,7 @@ before="Moyenne" after="Pages les + visitées" text=" " - name="topPages" + name="top-pages" @input="onToggleTopPages" /> @@ -344,14 +344,9 @@ export default {