From b9544391c525d50db32adcca236650c688da1514 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Sun, 20 Feb 2022 15:33:27 +0100 Subject: [PATCH 01/31] playing around --- code/index.html | 17 +++++++--- code/script.js | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ code/style.css | 44 +++++++++++++++++++++++-- 3 files changed, 141 insertions(+), 6 deletions(-) diff --git a/code/index.html b/code/index.html index 2fb5e0ae..42005308 100644 --- a/code/index.html +++ b/code/index.html @@ -4,13 +4,22 @@ - Project GitHub Tracker + Project GitHub Tracker | Michael Chang + + + + + + -

GitHub Tracker

-

Projects:

-
+
+ +
+
+

Projects:

+
diff --git a/code/script.js b/code/script.js index e69de29b..ca9b4a7e 100644 --- a/code/script.js +++ b/code/script.js @@ -0,0 +1,86 @@ +//Global Dom Selectors +const header = document.querySelector('header') +const projects = document.getElementById('projects') + +// Github Authentication +const username = 'michaelchangdk' +const GITHUB_API = `https://api.github.com/users/${username}/repos` +const GITHUB_TOKEN = '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' +const options = { + method: 'GET', + headers: { + Authorization: `token ${GITHUB_TOKEN}` + } + } + +fetch (GITHUB_API, options) + .then((res) => res.json()) + .then((data => { + + // Function to Sort Projects By Created Date + compareCreate = (a, b) => { + if (a.created_at < b.created_at) { + return -1; + } if (a.created_at > b.created_at) { + return 1; + } + return 0; + }; + + // Filter Technigo Projects Only & Sort by Date Created + const technigoProjects = data.filter(item => item.archive_url.includes('project') === true).sort(compareCreate); + console.log(technigoProjects); + + // fetch(technigoProjects[i].commits_url.replace("{/sha}", ""), options) + // .then((res) => res.json()) + // .then((json) => { + // // console.log(json); + // let numberOfCommits = json.length; + // }) + + // Create Header Section + header.innerHTML = ` +
+ profile picture +

michaelchangdk

+
+
+

Technigo Projects GitHub Tracker

+
+ ` + + + // Create Main Project Elements + for (let i = 0; i < technigoProjects.length; i++) { + console.log(technigoProjects[i]); + let numberOfCommits + fetch(technigoProjects[i].commits_url.replace("{/sha}", ""), options) + .then((res) => res.json()) + .then((json) => { + // Function to Sort Commits by 1st Commit + // compareCommit = (a, b) => { + // if (a[0].commit.author.date < b[0].commit.author.date) { + // return -1; + // } if (a[0].commit.author.date > b[0].commit.author.date) { + // return 1; + // } + // return 0; + // }; + console.log(json); + console.log(json[0].commit.author.date) + numberOfCommits = json.length; + }) + + projects.innerHTML += ` +
+
+

Week ${i+1}: ${technigoProjects[i].name}

+
+
+

${technigoProjects[i].name} can be found here. +

Number of commits: ${numberOfCommits}

+
+
+ ` + } + })) \ No newline at end of file diff --git a/code/style.css b/code/style.css index 7c8ad447..edaa4a7c 100644 --- a/code/style.css +++ b/code/style.css @@ -1,3 +1,43 @@ +/* Remove Pre-set Styling */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + text-decoration: none; +} + body { - background: #FFECE9; -} \ No newline at end of file + background: #DCD8DC; +} + +/* Header */ + +header { + /* display: flex; + justify-content: center; + align-items: center; */ + font-family: 'Roboto Mono', monospace; +} + +.header-1 { + display: flex; + /* justify-content: center; */ + align-items: center; +} + +.header-2 { + display: flex; + /* justify-content: center; + align-items: center; */ +} + +.avatar { + border-radius: 50%; + height: 40px; +} + +/* Projects Section */ \ No newline at end of file From b2dab5fb0bdbd40682b7f8710614992f41188e1c Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Mon, 21 Feb 2022 17:37:12 +0100 Subject: [PATCH 02/31] finished quite a bit today --- code/index.html | 6 +- code/script.js | 204 ++++++++++++++++++++++++++++++------------------ code/style.css | 92 ++++++++++++++++++---- 3 files changed, 205 insertions(+), 97 deletions(-) diff --git a/code/index.html b/code/index.html index 42005308..cec50135 100644 --- a/code/index.html +++ b/code/index.html @@ -4,7 +4,7 @@ - Project GitHub Tracker | Michael Chang + Technigo Projects GitHub Tracker | michaelchangdk @@ -17,8 +17,8 @@
-
-

Projects:

+
+

Technigo Projects:

diff --git a/code/script.js b/code/script.js index ca9b4a7e..ce124391 100644 --- a/code/script.js +++ b/code/script.js @@ -2,85 +2,133 @@ const header = document.querySelector('header') const projects = document.getElementById('projects') -// Github Authentication +// Github API const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` -const GITHUB_TOKEN = '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' -const options = { - method: 'GET', - headers: { - Authorization: `token ${GITHUB_TOKEN}` - } - } - -fetch (GITHUB_API, options) - .then((res) => res.json()) - .then((data => { - - // Function to Sort Projects By Created Date - compareCreate = (a, b) => { - if (a.created_at < b.created_at) { - return -1; - } if (a.created_at > b.created_at) { - return 1; - } - return 0; - }; - - // Filter Technigo Projects Only & Sort by Date Created - const technigoProjects = data.filter(item => item.archive_url.includes('project') === true).sort(compareCreate); - console.log(technigoProjects); - - // fetch(technigoProjects[i].commits_url.replace("{/sha}", ""), options) - // .then((res) => res.json()) - // .then((json) => { - // // console.log(json); - // let numberOfCommits = json.length; - // }) - - // Create Header Section - header.innerHTML = ` -
- profile picture -

michaelchangdk

-
-
-

Technigo Projects GitHub Tracker

-
- ` - - - // Create Main Project Elements - for (let i = 0; i < technigoProjects.length; i++) { - console.log(technigoProjects[i]); - let numberOfCommits - fetch(technigoProjects[i].commits_url.replace("{/sha}", ""), options) - .then((res) => res.json()) - .then((json) => { - // Function to Sort Commits by 1st Commit - // compareCommit = (a, b) => { - // if (a[0].commit.author.date < b[0].commit.author.date) { - // return -1; - // } if (a[0].commit.author.date > b[0].commit.author.date) { - // return 1; - // } - // return 0; - // }; - console.log(json); - console.log(json[0].commit.author.date) - numberOfCommits = json.length; + +// Function to Sort Projects By Created Date +compareCreateDate = (a, b) => { + if (a.created_at < b.created_at) { + return -1; + } if (a.created_at > b.created_at) { + return 1; + } + return 0; +}; + +const getProjects = async () => { + const projectsWait = await fetch(GITHUB_API); + const data = await projectsWait.json(); + + // Filter Technigo Projects Only & Sort by Date Created + const technigoProjects = data.filter(item => item.archive_url.includes('project') === true).sort(compareCreateDate); + console.log(technigoProjects); + + // Create Header Section + header.innerHTML = ` + profile picture +

${technigoProjects[0].owner.login}

+ ` + + // Create Main Project Elements & Fetch Number of Commits + for (let i = 0; i < technigoProjects.length; i++) { + fetch(technigoProjects[i].commits_url.replace("{/sha}", "")) + .then(res => res.json()) + .then(data => { + // console.log('commit data', data) + + // Number of Commits + let numberOfCommits = data.length; + + // Last Commit Message - Committed Netlify Link to add to live link. + let lastCommitLink = data[0].commit.message + + // Last Commit Date which is NOT the Netlify commit (Index 1 instead of 0) + // Formatting dates to look nice for the tracker + let lastCommitDateRaw = data[1].commit.committer.date.substring(0,10) + // Removing the 0 from dates before the 10th for DAYS + let lastCommitDayRaw = lastCommitDateRaw.substring(8,10) + let lastCommitDay + if (lastCommitDayRaw < 10) { + lastCommitDay = lastCommitDayRaw.replace("0", "") + } else { + lastCommitDay = lastCommitDayRaw; + } + // Formatting MONTHS + const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + let lastCommitMonthNumber = lastCommitDateRaw.substring(5,7).replace("0","") + let lastCommitMonth = months[lastCommitMonthNumber - 1] + + // Extracting YEAR + let lastCommitYear = lastCommitDateRaw.substring(0,4) + + // Creating Project ID + let projectName = technigoProjects[i].name.replaceAll("-",""); + + // Creating the Project Elements + projects.innerHTML += ` +
+ +
+

Number of commits: ${numberOfCommits}

+

Last commit date: ${lastCommitMonth} ${lastCommitDay}, ${lastCommitYear}

+

Repo can be found here. +

View it live here. +

+
+ ` + + // Fetching the pull requests + fetch('https://api.github.com/repos/technigo/' + technigoProjects[i].name + '/pulls?per_page=100') + .then(res => res.json()) + .then(data => { + // console.log(data); + const filteredPR = data.filter(function (el) { + return el.user.login === "michaelchangdk" + }) + document.getElementById(`${projectName}2`).innerHTML += ` +

Pull request with comments here. + ` + // console.log(filteredPR[0].review_comments_url) }) + }) + } +} + +// Function for opening and closing the projects list +const openSesame = (param) => { + // console.log('you got clicked! bitch') + let projectID = param; + let projectHeader = document.getElementById(`${projectID}`); + let projectBody = projectHeader.nextElementSibling; + if (projectBody.style.display === "none") { + projectBody.style.display = "block"; + projectHeader.classList.add("project-header--active") + } else { + projectBody.style.display = "none"; + projectHeader.classList.remove("project-header--active") + } +} + +//Remember to pass along your filtered repos as an argument when you are calling this function + +// const getPullRequests = (repos) => { +// //Get all the PRs for each project. +// repos.forEach(repo => { +// fetch('https://api.github.com/repos/technigo/' + repo.name + '/pulls') +// .then(res => res.json()) +// .then(data => { +// //TODO +// //1. Find only the PR that you made by comparing pull.user.login +// // with repo.owner.login +// //2. Now you're able to get the commits for each repo by using +// // the commits_url as an argument to call another function +// //3. You can also get the comments for each PR by calling +// // another function with the review_comments_url as argument +// }) +// }) +// } - projects.innerHTML += ` -

-
-

Week ${i+1}: ${technigoProjects[i].name}

-
-
-

${technigoProjects[i].name} can be found here. -

Number of commits: ${numberOfCommits}

-
-
- ` - } - })) \ No newline at end of file +getProjects(); diff --git a/code/style.css b/code/style.css index edaa4a7c..39daf8e0 100644 --- a/code/style.css +++ b/code/style.css @@ -11,28 +11,24 @@ } body { - background: #DCD8DC; + background: white; + font-family: 'Roboto Mono', monospace; + padding: 9vmin; +} + +a { + color: black; + text-decoration: underline; } /* Header */ header { - /* display: flex; - justify-content: center; - align-items: center; */ - font-family: 'Roboto Mono', monospace; -} - -.header-1 { display: flex; - /* justify-content: center; */ align-items: center; -} - -.header-2 { - display: flex; - /* justify-content: center; - align-items: center; */ + gap: 2vmin; + justify-content: center; + margin-bottom: 5vmin; } .avatar { @@ -40,4 +36,68 @@ header { height: 40px; } -/* Projects Section */ \ No newline at end of file +/* Projects Section */ + +/* Projects Accordion Section */ +.projects { + /* border-radius: 5px; */ + overflow: hidden; + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + +.projects-header { + background-color: #2D2E2F; + padding: 15px 0 20px; + color: white; + text-align: center; + border-radius: 5px; + overflow: hidden; +} + +.project-wrapper { + margin: 5vmin 0; + border-radius: 5px; + overflow: hidden; +} + +.project-header { + color: #2d2e2f; + background-color: #DCD8DC; + font-family: 'Roboto Mono', monospace; + font-weight: 700; + font-size: 18px; + text-align: left; + padding: 10px 25px; + width: 100%; + border: none; + outline: none; + /* border-radius: 5px; */ +} + +.project-info { + display: none; + color: #2d2e2f; + background-color: #bfb8b9; + font-family: 'Montserrat', sans-serif; + font-size: 16px; + font-weight: 500; + line-height: 1.5; + padding: 10px 25px; +} + +/* Adding background color to button when clicked on and when moused over */ +.project-header:hover { + background-color: #726a6a; + color: #DCD8DC; + cursor: pointer; + } + +/* Add .active in JS */ +.project-header--active { + background-color: #726a6a; + color: #DCD8DC; + cursor: pointer; + border-radius: 0; +} \ No newline at end of file From 2074c8741ccd307372ed2a9ec2aa69e908994d34 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 12:00:07 +0100 Subject: [PATCH 03/31] finally figured out how to do this damn token thing --- code/.gitignore | 8 ++++++ code/chart.js | 2 +- code/index.html | 8 ++++-- code/package-lock.json | 24 +++++++++++++++++ code/package.json | 5 ++++ code/script.js | 59 +++++++++++++++++++++++++----------------- 6 files changed, 79 insertions(+), 27 deletions(-) create mode 100644 code/.gitignore create mode 100644 code/package-lock.json create mode 100644 code/package.json diff --git a/code/.gitignore b/code/.gitignore new file mode 100644 index 00000000..ae92fb00 --- /dev/null +++ b/code/.gitignore @@ -0,0 +1,8 @@ +#other +token.js + +# Ignore node_modules folder +node_modules + +# Ignore files related to API keys +.env \ No newline at end of file diff --git a/code/chart.js b/code/chart.js index 92e85a30..d29b65a9 100644 --- a/code/chart.js +++ b/code/chart.js @@ -1,4 +1,4 @@ //DOM-selector for the canvas 👇 -const ctx = document.getElementById('chart').getContext('2d') +const ctx = document.getElementById('myChart').getContext('2d') //"Draw" the chart here 👇 diff --git a/code/index.html b/code/index.html index cec50135..f92ab85d 100644 --- a/code/index.html +++ b/code/index.html @@ -22,9 +22,13 @@

Technigo Projects:

- +
+ + +
+ - + \ No newline at end of file diff --git a/code/package-lock.json b/code/package-lock.json new file mode 100644 index 00000000..8627f416 --- /dev/null +++ b/code/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "project-github-tracker", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "chart.js": "^3.7.1" + } + }, + "node_modules/chart.js": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", + "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" + } + }, + "dependencies": { + "chart.js": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", + "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" + } + } +} diff --git a/code/package.json b/code/package.json new file mode 100644 index 00000000..832728d9 --- /dev/null +++ b/code/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "chart.js": "^3.7.1" + } +} diff --git a/code/script.js b/code/script.js index ce124391..f95992f1 100644 --- a/code/script.js +++ b/code/script.js @@ -6,6 +6,15 @@ const projects = document.getElementById('projects') const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` +// GITHUB Authentication - TOKEN REVOKED AFTER COMMIT? +const GITHUB_TOKEN = TOKEN || process.env.API_KEY; +const options = { + method: 'GET', + headers: { + Authorization: `token ${GITHUB_TOKEN}` + } +} + // Function to Sort Projects By Created Date compareCreateDate = (a, b) => { if (a.created_at < b.created_at) { @@ -17,7 +26,7 @@ compareCreateDate = (a, b) => { }; const getProjects = async () => { - const projectsWait = await fetch(GITHUB_API); + const projectsWait = await fetch(GITHUB_API, options); const data = await projectsWait.json(); // Filter Technigo Projects Only & Sort by Date Created @@ -32,7 +41,7 @@ const getProjects = async () => { // Create Main Project Elements & Fetch Number of Commits for (let i = 0; i < technigoProjects.length; i++) { - fetch(technigoProjects[i].commits_url.replace("{/sha}", "")) + fetch(technigoProjects[i].commits_url.replace("{/sha}", ""), options) .then(res => res.json()) .then(data => { // console.log('commit data', data) @@ -81,7 +90,7 @@ const getProjects = async () => { ` // Fetching the pull requests - fetch('https://api.github.com/repos/technigo/' + technigoProjects[i].name + '/pulls?per_page=100') + fetch('https://api.github.com/repos/technigo/' + technigoProjects[i].name + '/pulls?per_page=100', options) .then(res => res.json()) .then(data => { // console.log(data); @@ -92,15 +101,36 @@ const getProjects = async () => {

Pull request with comments here. ` // console.log(filteredPR[0].review_comments_url) + + // Fetch Comments + // fetch(filteredPR[0].review_comments_url, options) + // .then(res => res.json()) + // .then(data => { + // // console.log(data); + // document.getElementById(`${projectName}2`).innerHTML += ` + //
+ //

Review comments:

+ // ` + // for (let i = 0; i < data.length; i++) { + // if (data[i].user.login !== username) { + // // console.log(data[i].body); + // document.getElementById(`${projectName}2`).innerHTML += ` + // + // ` + // } + // } + // }) + }) }) } } // Function for opening and closing the projects list -const openSesame = (param) => { +const openSesame = (projectID) => { // console.log('you got clicked! bitch') - let projectID = param; let projectHeader = document.getElementById(`${projectID}`); let projectBody = projectHeader.nextElementSibling; if (projectBody.style.display === "none") { @@ -112,23 +142,4 @@ const openSesame = (param) => { } } -//Remember to pass along your filtered repos as an argument when you are calling this function - -// const getPullRequests = (repos) => { -// //Get all the PRs for each project. -// repos.forEach(repo => { -// fetch('https://api.github.com/repos/technigo/' + repo.name + '/pulls') -// .then(res => res.json()) -// .then(data => { -// //TODO -// //1. Find only the PR that you made by comparing pull.user.login -// // with repo.owner.login -// //2. Now you're able to get the commits for each repo by using -// // the commits_url as an argument to call another function -// //3. You can also get the comments for each PR by calling -// // another function with the review_comments_url as argument -// }) -// }) -// } - getProjects(); From b839392173c06b623f5733cb5e397744330ec334 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 12:21:42 +0100 Subject: [PATCH 04/31] added --save to the chart install --- node_modules/.package-lock.json | 12 + node_modules/chart.js/LICENSE.md | 9 + node_modules/chart.js/README.md | 38 + node_modules/chart.js/auto/auto.esm.d.ts | 4 + node_modules/chart.js/auto/auto.esm.js | 5 + node_modules/chart.js/auto/auto.js | 1 + node_modules/chart.js/auto/package.json | 8 + node_modules/chart.js/dist/chart.esm.js | 10627 +++++++++++++ node_modules/chart.js/dist/chart.js | 13269 ++++++++++++++++ node_modules/chart.js/dist/chart.min.js | 13 + .../chart.js/dist/chunks/helpers.segment.js | 2503 +++ node_modules/chart.js/dist/helpers.esm.js | 7 + .../chart.js/helpers/helpers.esm.d.ts | 1 + node_modules/chart.js/helpers/helpers.esm.js | 1 + node_modules/chart.js/helpers/helpers.js | 1 + node_modules/chart.js/helpers/package.json | 8 + node_modules/chart.js/package.json | 111 + node_modules/chart.js/types/adapters.d.ts | 63 + node_modules/chart.js/types/animation.d.ts | 33 + node_modules/chart.js/types/basic.d.ts | 3 + node_modules/chart.js/types/color.d.ts | 1 + node_modules/chart.js/types/element.d.ts | 17 + node_modules/chart.js/types/geometric.d.ts | 37 + .../types/helpers/helpers.canvas.d.ts | 101 + .../types/helpers/helpers.collection.d.ts | 20 + .../chart.js/types/helpers/helpers.color.d.ts | 33 + .../chart.js/types/helpers/helpers.core.d.ts | 157 + .../chart.js/types/helpers/helpers.curve.d.ts | 34 + .../chart.js/types/helpers/helpers.dom.d.ts | 20 + .../types/helpers/helpers.easing.d.ts | 5 + .../types/helpers/helpers.extras.d.ts | 23 + .../types/helpers/helpers.interpolation.d.ts | 1 + .../chart.js/types/helpers/helpers.intl.d.ts | 7 + .../chart.js/types/helpers/helpers.math.d.ts | 17 + .../types/helpers/helpers.options.d.ts | 61 + .../chart.js/types/helpers/helpers.rtl.d.ts | 12 + .../types/helpers/helpers.segment.d.ts | 1 + .../chart.js/types/helpers/index.d.ts | 15 + node_modules/chart.js/types/index.esm.d.ts | 3601 +++++ node_modules/chart.js/types/layout.d.ts | 65 + node_modules/chart.js/types/utils.d.ts | 21 + package-lock.json | 24 + package.json | 5 + 43 files changed, 30995 insertions(+) create mode 100644 node_modules/.package-lock.json create mode 100644 node_modules/chart.js/LICENSE.md create mode 100644 node_modules/chart.js/README.md create mode 100644 node_modules/chart.js/auto/auto.esm.d.ts create mode 100644 node_modules/chart.js/auto/auto.esm.js create mode 100644 node_modules/chart.js/auto/auto.js create mode 100644 node_modules/chart.js/auto/package.json create mode 100644 node_modules/chart.js/dist/chart.esm.js create mode 100644 node_modules/chart.js/dist/chart.js create mode 100644 node_modules/chart.js/dist/chart.min.js create mode 100644 node_modules/chart.js/dist/chunks/helpers.segment.js create mode 100644 node_modules/chart.js/dist/helpers.esm.js create mode 100644 node_modules/chart.js/helpers/helpers.esm.d.ts create mode 100644 node_modules/chart.js/helpers/helpers.esm.js create mode 100644 node_modules/chart.js/helpers/helpers.js create mode 100644 node_modules/chart.js/helpers/package.json create mode 100644 node_modules/chart.js/package.json create mode 100644 node_modules/chart.js/types/adapters.d.ts create mode 100644 node_modules/chart.js/types/animation.d.ts create mode 100644 node_modules/chart.js/types/basic.d.ts create mode 100644 node_modules/chart.js/types/color.d.ts create mode 100644 node_modules/chart.js/types/element.d.ts create mode 100644 node_modules/chart.js/types/geometric.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.canvas.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.collection.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.color.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.core.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.curve.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.dom.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.easing.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.extras.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.interpolation.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.intl.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.math.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.options.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.rtl.d.ts create mode 100644 node_modules/chart.js/types/helpers/helpers.segment.d.ts create mode 100644 node_modules/chart.js/types/helpers/index.d.ts create mode 100644 node_modules/chart.js/types/index.esm.d.ts create mode 100644 node_modules/chart.js/types/layout.d.ts create mode 100644 node_modules/chart.js/types/utils.d.ts create mode 100644 package-lock.json create mode 100644 package.json diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json new file mode 100644 index 00000000..28bd1f48 --- /dev/null +++ b/node_modules/.package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "project-github-tracker", + "lockfileVersion": 2, + "requires": true, + "packages": { + "node_modules/chart.js": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", + "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" + } + } +} diff --git a/node_modules/chart.js/LICENSE.md b/node_modules/chart.js/LICENSE.md new file mode 100644 index 00000000..5060fabc --- /dev/null +++ b/node_modules/chart.js/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2014-2021 Chart.js Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/chart.js/README.md b/node_modules/chart.js/README.md new file mode 100644 index 00000000..f485b4eb --- /dev/null +++ b/node_modules/chart.js/README.md @@ -0,0 +1,38 @@ +

+ + https://www.chartjs.org/
+
+ Simple yet flexible JavaScript charting for designers & developers +

+ +

+ Downloads + GitHub Workflow Status + Coverage + Awesome + Slack +

+ +## Documentation + +All the links point to the new version 3 of the lib. + +* [Introduction](https://www.chartjs.org/docs/latest/) +* [Getting Started](https://www.chartjs.org/docs/latest/getting-started/index) +* [General](https://www.chartjs.org/docs/latest/general/data-structures) +* [Configuration](https://www.chartjs.org/docs/latest/configuration/index) +* [Charts](https://www.chartjs.org/docs/latest/charts/line) +* [Axes](https://www.chartjs.org/docs/latest/axes/index) +* [Developers](https://www.chartjs.org/docs/latest/developers/index) +* [Popular Extensions](https://github.com/chartjs/awesome) +* [Samples](https://www.chartjs.org/samples/) + +In case you are looking for the docs of version 2, you will have to specify the specific version in the url like this: [https://www.chartjs.org/docs/2.9.4/](https://www.chartjs.org/docs/2.9.4/) + +## Contributing + +Instructions on building and testing Chart.js can be found in [the documentation](https://www.chartjs.org/docs/master/developers/contributing.html#building-and-testing). Before submitting an issue or a pull request, please take a moment to look over the [contributing guidelines](https://www.chartjs.org/docs/master/developers/contributing) first. For support, please post questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/chartjs) with the `chartjs` tag. + +## License + +Chart.js is available under the [MIT license](LICENSE.md). diff --git a/node_modules/chart.js/auto/auto.esm.d.ts b/node_modules/chart.js/auto/auto.esm.d.ts new file mode 100644 index 00000000..f0bc3805 --- /dev/null +++ b/node_modules/chart.js/auto/auto.esm.d.ts @@ -0,0 +1,4 @@ +import { Chart } from '../types/index.esm'; + +export * from '../types/index.esm'; +export default Chart; diff --git a/node_modules/chart.js/auto/auto.esm.js b/node_modules/chart.js/auto/auto.esm.js new file mode 100644 index 00000000..42626764 --- /dev/null +++ b/node_modules/chart.js/auto/auto.esm.js @@ -0,0 +1,5 @@ +import {Chart, registerables} from '../dist/chart.esm'; + +Chart.register(...registerables); + +export default Chart; diff --git a/node_modules/chart.js/auto/auto.js b/node_modules/chart.js/auto/auto.js new file mode 100644 index 00000000..235580fe --- /dev/null +++ b/node_modules/chart.js/auto/auto.js @@ -0,0 +1 @@ +module.exports = require('../dist/chart'); diff --git a/node_modules/chart.js/auto/package.json b/node_modules/chart.js/auto/package.json new file mode 100644 index 00000000..c2ad5b2c --- /dev/null +++ b/node_modules/chart.js/auto/package.json @@ -0,0 +1,8 @@ +{ + "name": "chart.js-auto", + "private": true, + "description": "auto registering package", + "main": "auto.js", + "module": "auto.esm.js", + "types": "auto.esm.d.ts" +} diff --git a/node_modules/chart.js/dist/chart.esm.js b/node_modules/chart.js/dist/chart.esm.js new file mode 100644 index 00000000..2306fa88 --- /dev/null +++ b/node_modules/chart.js/dist/chart.esm.js @@ -0,0 +1,10627 @@ +/*! + * Chart.js v3.7.1 + * https://www.chartjs.org + * (c) 2022 Chart.js Contributors + * Released under the MIT License + */ +import { r as requestAnimFrame, a as resolve, e as effects, c as color, d as defaults, i as isObject, b as isArray, v as valueOrDefault, u as unlistenArrayEvents, l as listenArrayEvents, f as resolveObjectKey, g as isNumberFinite, h as createContext, j as defined, s as sign, k as isNullOrUndef, _ as _arrayUnique, t as toRadians, m as toPercentage, n as toDimension, T as TAU, o as formatNumber, p as _angleBetween, H as HALF_PI, P as PI, q as isNumber, w as _limitValue, x as _lookupByKey, y as getRelativePosition$1, z as _isPointInArea, A as _rlookupByKey, B as getAngleFromPoint, C as toPadding, D as each, E as getMaximumSize, F as _getParentNode, G as readUsedSize, I as throttled, J as supportsEventListenerOptions, K as _isDomSupported, L as log10, M as _factorize, N as finiteOrDefault, O as callback, Q as _addGrace, R as toDegrees, S as _measureText, U as _int16Range, V as _alignPixel, W as clipArea, X as renderText, Y as unclipArea, Z as toFont, $ as _toLeftRightCenter, a0 as _alignStartEnd, a1 as overrides, a2 as merge, a3 as _capitalize, a4 as descriptors, a5 as isFunction, a6 as _attachContext, a7 as _createResolver, a8 as _descriptors, a9 as mergeIf, aa as uid, ab as debounce, ac as retinaScale, ad as clearCanvas, ae as setsEqual, af as _elementsEqual, ag as _isClickEvent, ah as _isBetween, ai as _readValueToProps, aj as _updateBezierControlPoints, ak as _computeSegments, al as _boundSegments, am as _steppedInterpolation, an as _bezierInterpolation, ao as _pointInLine, ap as _steppedLineTo, aq as _bezierCurveTo, ar as drawPoint, as as addRoundedRectPath, at as toTRBL, au as toTRBLCorners, av as _boundSegment, aw as _normalizeAngle, ax as getRtlAdapter, ay as overrideTextDirection, az as _textX, aA as restoreTextDirection, aB as noop, aC as distanceBetweenPoints, aD as _setMinAndMaxByKey, aE as niceNum, aF as almostWhole, aG as almostEquals, aH as _decimalPlaces, aI as _longestText, aJ as _filterBetween, aK as _lookup } from './chunks/helpers.segment.js'; +export { d as defaults } from './chunks/helpers.segment.js'; + +class Animator { + constructor() { + this._request = null; + this._charts = new Map(); + this._running = false; + this._lastDate = undefined; + } + _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 draw = 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); + draw = true; + } else { + items[i] = items[items.length - 1]; + items.pop(); + } + } + if (draw) { + 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 = new Animator(); + +const transparent = 'transparent'; +const interpolators = { + boolean(from, to, factor) { + return factor > 0.5 ? to : from; + }, + color(from, to, factor) { + const c0 = color(from || transparent); + const c1 = c0.valid && color(to || transparent); + return c1 && c1.valid + ? c1.mix(c0, factor).hexString() + : to; + }, + number(from, to, factor) { + return from + (to - from) * factor; + } +}; +class Animation { + constructor(cfg, target, prop, to) { + const currentValue = target[prop]; + to = resolve([cfg.to, to, currentValue, cfg.from]); + const from = resolve([cfg.from, currentValue, to]); + this._active = true; + this._fn = cfg.fn || interpolators[cfg.type || typeof from]; + this._easing = effects[cfg.easing] || effects.linear; + this._start = Math.floor(Date.now() + (cfg.delay || 0)); + this._duration = this._total = Math.floor(cfg.duration); + this._loop = !!cfg.loop; + this._target = target; + this._prop = prop; + this._from = from; + this._to = to; + this._promises = undefined; + } + active() { + return this._active; + } + update(cfg, to, 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, to, currentValue, cfg.from]); + this._from = resolve([cfg.from, currentValue, to]); + } + } + 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 from = this._from; + const loop = this._loop; + const to = this._to; + let factor; + this._active = from !== to && (loop || (elapsed < duration)); + if (!this._active) { + this._target[prop] = to; + this._notify(true); + return; + } + if (elapsed < 0) { + this._target[prop] = from; + 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(from, to, 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](); + } + } +} + +const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension']; +const colors = ['color', 'borderColor', 'backgroundColor']; +defaults.set('animation', { + delay: undefined, + duration: 1000, + easing: 'easeOutQuart', + fn: undefined, + from: undefined, + loop: undefined, + to: undefined, + type: undefined, +}); +const animationOptions = Object.keys(defaults.animation); +defaults.describe('animation', { + _fallback: false, + _indexable: false, + _scriptable: (name) => name !== 'onProgress' && name !== 'onComplete' && name !== 'fn', +}); +defaults.set('animations', { + colors: { + type: 'color', + properties: colors + }, + numbers: { + type: 'number', + properties: numbers + }, +}); +defaults.describe('animations', { + _fallback: 'animation', +}); +defaults.set('transitions', { + active: { + animation: { + duration: 400 + } + }, + resize: { + animation: { + duration: 0 + } + }, + show: { + animations: { + colors: { + from: 'transparent' + }, + visible: { + type: 'boolean', + duration: 0 + }, + } + }, + hide: { + animations: { + colors: { + to: 'transparent' + }, + visible: { + type: 'boolean', + easing: 'linear', + fn: v => v | 0 + }, + } + } +}); +class Animations { + constructor(chart, config) { + this._chart = chart; + this._properties = new Map(); + this.configure(config); + } + configure(config) { + if (!isObject(config)) { + return; + } + const animatedProps = this._properties; + Object.getOwnPropertyNames(config).forEach(key => { + const cfg = config[key]; + if (!isObject(cfg)) { + return; + } + const resolved = {}; + for (const option of animationOptions) { + resolved[option] = cfg[option]; + } + (isArray(cfg.properties) && cfg.properties || [key]).forEach((prop) => { + 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 === undefined ? allowedOverflow : 0; + const max = opts.max === undefined ? 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; + } + for (i = 0, ilen = keys.length; i < ilen; ++i) { + datasetIndex = +keys[i]; + if (datasetIndex === dsIndex) { + if (options.all) { + continue; + } + break; + } + otherValue = stack.values[datasetIndex]; + if (isNumberFinite(otherValue) && (singleMode || (value === 0 || sign(value) === sign(otherValue)))) { + value += otherValue; + } + } + return value; +} +function convertObjectDataToArray(data) { + 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] = { + x: key, + y: data[key] + }; + } + return adata; +} +function isStacked(scale, meta) { + const stacked = scale && scale.options.stacked; + return stacked || (stacked === undefined && meta.stack !== undefined); +} +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]: index, [vAxis]: value} = item; + const itemStacks = item._stacks || (item._stacks = {}); + stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index); + stack[datasetIndex] = value; + stack._top = getLastIndexInStack(stack, vScale, true, meta.type); + stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type); + } +} +function getFirstScaleId(chart, axis) { + const scales = chart.scales; + return Object.keys(scales).filter(key => scales[key].axis === axis).shift(); +} +function createDatasetContext(parent, index) { + return createContext(parent, + { + active: false, + dataset: undefined, + datasetIndex: index, + index, + mode: 'default', + type: 'dataset' + } + ); +} +function createDataContext(parent, index, element) { + return createContext(parent, { + active: false, + dataIndex: index, + parsed: undefined, + raw: undefined, + element, + index, + 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] === undefined || stacks[axis][datasetIndex] === undefined) { + return; + } + delete stacks[axis][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 = undefined; + this._parsing = false; + this._data = undefined; + this._objectData = undefined; + this._sharedOptions = undefined; + this._drawStart = undefined; + this._drawCount = undefined; + this.enableOptionSharing = false; + this.$context = undefined; + this._syncList = []; + this.initialize(); + } + initialize() { + const meta = this._cachedMeta; + this.configure(); + this.linkScales(); + meta._stacked = isStacked(meta.vScale, meta); + this.addElements(); + } + 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)) { + this._data = convertObjectDataToArray(data); + } 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); + } + } + 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, index; + for (i = 0, ilen = count; i < ilen; ++i) { + index = i + start; + parsed[i] = { + [iAxis]: singleScale || iScale.parse(labels[index], index), + [vAxis]: vScale.parse(data[index], index) + }; + } + return parsed; + } + parseArrayData(meta, data, start, count) { + const {xScale, yScale} = meta; + const parsed = new Array(count); + let i, ilen, index, item; + for (i = 0, ilen = count; i < ilen; ++i) { + index = i + start; + item = data[index]; + parsed[i] = { + x: xScale.parse(item[0], index), + y: yScale.parse(item[1], index) + }; + } + 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, index, item; + for (i = 0, ilen = count; i < ilen; ++i) { + index = i + start; + item = data[index]; + parsed[i] = { + x: xScale.parse(resolveObjectKey(item, xAxisKey), index), + y: yScale.parse(resolveObjectKey(item, yAxisKey), index) + }; + } + return parsed; + } + getParsed(index) { + return this._cachedMeta._parsed[index]; + } + getDataElement(index) { + return this._cachedMeta.data[index]; + } + 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] + }; + 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(index) { + const meta = this._cachedMeta; + const iScale = meta.iScale; + const vScale = meta.vScale; + const parsed = this.getParsed(index); + 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(index, active) { + const mode = active ? 'active' : 'default'; + return index === undefined && this._cachedMeta.dataset + ? this.resolveDatasetElementOptions(mode) + : this.resolveDataElementOptions(index || 0, mode); + } + getContext(index, active, mode) { + const dataset = this.getDataset(); + let context; + if (index >= 0 && index < this._cachedMeta.data.length) { + const element = this._cachedMeta.data[index]; + context = element.$context || + (element.$context = createDataContext(this.getContext(), index, element)); + context.parsed = this.getParsed(index); + context.raw = dataset.data[index]; + context.index = context.dataIndex = index; + } 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(index, mode) { + return this._resolveElementOptions(this.dataElementType.id, mode, index); + } + _resolveElementOptions(elementType, mode = 'default', index) { + const active = mode === 'active'; + const cache = this._cachedDataOpts; + const cacheKey = elementType + '-' + mode; + const cached = cache[cacheKey]; + const sharing = this.enableOptionSharing && defined(index); + 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 names = Object.keys(defaults.elements[elementType]); + const context = () => this.getContext(index, active); + const values = config.resolveNamedOptions(scopes, names, context, prefixes); + if (values.$shared) { + values.$shared = sharing; + cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing)); + } + return values; + } + _resolveAnimations(index, 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(index, 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; + } + updateElement(element, index, properties, mode) { + if (isDirectUpdateMode(mode)) { + Object.assign(element, properties); + } else { + this._resolveAnimations(index, mode).update(element, properties); + } + } + updateSharedOptions(sharedOptions, mode, newOptions) { + if (sharedOptions && !isDirectUpdateMode(mode)) { + this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions); + } + } + _setStyle(element, index, mode, active) { + element.active = active; + const options = this.getStyle(index, active); + this._resolveAnimations(index, mode, active).update(element, { + options: (!active && this.getSharedOptions(options)) || options + }); + } + removeHoverStyle(element, datasetIndex, index) { + this._setStyle(element, index, 'active', false); + } + setHoverStyle(element, datasetIndex, index) { + this._setStyle(element, index, 'active', true); + } + _removeDatasetHoverStyle() { + const element = this._cachedMeta.dataset; + if (element) { + this._setStyle(element, undefined, 'active', false); + } + } + _setDatasetHoverStyle() { + const element = this._cachedMeta.dataset; + if (element) { + this._setStyle(element, undefined, '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]); + } +} +DatasetController.defaults = {}; +DatasetController.prototype.datasetElementType = null; +DatasetController.prototype.dataElementType = null; + +function getAllScaleValues(scale, type) { + if (!scale._cache.$bar) { + const visibleMetas = scale.getMatchingVisibleMetas(type); + let values = []; + for (let i = 0, ilen = visibleMetas.length; i < ilen; i++) { + values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale)); + } + scale._cache.$bar = _arrayUnique(values.sort((a, b) => a - b)); + } + return scale._cache.$bar; +} +function computeMinSampleSize(meta) { + const scale = meta.iScale; + const values = getAllScaleValues(scale, meta.type); + let min = scale._length; + let i, ilen, curr, prev; + const updateMinAndPrev = () => { + if (curr === 32767 || curr === -32768) { + return; + } + if (defined(prev)) { + min = Math.min(min, Math.abs(curr - prev) || min); + } + prev = curr; + }; + for (i = 0, ilen = values.length; i < ilen; ++i) { + curr = scale.getPixelForValue(values[i]); + updateMinAndPrev(); + } + prev = undefined; + for (i = 0, ilen = scale.ticks.length; i < ilen; ++i) { + curr = scale.getPixelForTick(i); + updateMinAndPrev(); + } + return min; +} +function computeFitCategoryTraits(index, ruler, options, stackCount) { + const thickness = options.barThickness; + let size, ratio; + if (isNullOrUndef(thickness)) { + size = ruler.min * options.categoryPercentage; + ratio = options.barPercentage; + } else { + size = thickness * stackCount; + ratio = 1; + } + return { + chunk: size / stackCount, + ratio, + start: ruler.pixels[index] - (size / 2) + }; +} +function computeFlexCategoryTraits(index, ruler, options, stackCount) { + const pixels = ruler.pixels; + const curr = pixels[index]; + let prev = index > 0 ? pixels[index - 1] : null; + let next = index < pixels.length - 1 ? pixels[index + 1] : null; + const percent = options.categoryPercentage; + if (prev === null) { + prev = curr - (next === null ? ruler.end - ruler.start : next - curr); + } + if (next === null) { + next = curr + curr - prev; + } + const start = curr - (curr - Math.min(prev, next)) / 2 * percent; + const size = Math.abs(next - prev) / 2 * percent; + return { + chunk: size / stackCount, + ratio: options.barPercentage, + start + }; +} +function parseFloatBar(entry, item, vScale, i) { + const startValue = vScale.parse(entry[0], i); + const endValue = vScale.parse(entry[1], i); + const min = Math.min(startValue, endValue); + const max = Math.max(startValue, endValue); + let barStart = min; + let barEnd = max; + if (Math.abs(min) > Math.abs(max)) { + barStart = max; + barEnd = min; + } + item[vScale.axis] = barEnd; + item._custom = { + barStart, + barEnd, + start: startValue, + end: endValue, + min, + max + }; +} +function parseValue(entry, item, vScale, i) { + if (isArray(entry)) { + parseFloatBar(entry, item, vScale, i); + } else { + item[vScale.axis] = vScale.parse(entry, i); + } + return item; +} +function parseArrayOrPrimitive(meta, data, start, count) { + const iScale = meta.iScale; + const vScale = meta.vScale; + const labels = iScale.getLabels(); + const singleScale = iScale === vScale; + const parsed = []; + let i, ilen, item, entry; + for (i = start, ilen = start + count; i < ilen; ++i) { + entry = data[i]; + item = {}; + item[iScale.axis] = singleScale || iScale.parse(labels[i], i); + parsed.push(parseValue(entry, item, vScale, i)); + } + return parsed; +} +function isFloatBar(custom) { + return custom && custom.barStart !== undefined && custom.barEnd !== undefined; +} +function barSign(size, vScale, actualBase) { + if (size !== 0) { + return sign(size); + } + return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1); +} +function borderProps(properties) { + let reverse, start, end, top, bottom; + if (properties.horizontal) { + reverse = properties.base > properties.x; + start = 'left'; + end = 'right'; + } else { + reverse = properties.base < properties.y; + start = 'bottom'; + end = 'top'; + } + if (reverse) { + top = 'end'; + bottom = 'start'; + } else { + top = 'start'; + bottom = 'end'; + } + return {start, end, reverse, top, bottom}; +} +function setBorderSkipped(properties, options, stack, index) { + let edge = options.borderSkipped; + const res = {}; + if (!edge) { + properties.borderSkipped = res; + return; + } + const {start, end, reverse, top, bottom} = borderProps(properties); + if (edge === 'middle' && stack) { + properties.enableBorderRadius = true; + if ((stack._top || 0) === index) { + edge = top; + } else if ((stack._bottom || 0) === index) { + edge = bottom; + } else { + res[parseEdge(bottom, start, end, reverse)] = true; + edge = top; + } + } + res[parseEdge(edge, start, end, reverse)] = true; + properties.borderSkipped = res; +} +function parseEdge(edge, a, b, reverse) { + if (reverse) { + edge = swap(edge, a, b); + edge = startEnd(edge, b, a); + } else { + edge = startEnd(edge, a, b); + } + return edge; +} +function swap(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} +function startEnd(v, start, end) { + return v === 'start' ? start : v === 'end' ? end : v; +} +function setInflateAmount(properties, {inflateAmount}, ratio) { + properties.inflateAmount = inflateAmount === 'auto' + ? ratio === 1 ? 0.33 : 0 + : inflateAmount; +} +class BarController extends DatasetController { + parsePrimitiveData(meta, data, start, count) { + return parseArrayOrPrimitive(meta, data, start, count); + } + parseArrayData(meta, data, start, count) { + return parseArrayOrPrimitive(meta, data, start, count); + } + parseObjectData(meta, data, start, count) { + const {iScale, vScale} = meta; + const {xAxisKey = 'x', yAxisKey = 'y'} = this._parsing; + const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey; + const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey; + const parsed = []; + let i, ilen, item, obj; + for (i = start, ilen = start + count; i < ilen; ++i) { + obj = data[i]; + item = {}; + item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i); + parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i)); + } + return parsed; + } + updateRangeFromParsed(range, scale, parsed, stack) { + super.updateRangeFromParsed(range, scale, parsed, stack); + const custom = parsed._custom; + if (custom && scale === this._cachedMeta.vScale) { + range.min = Math.min(range.min, custom.min); + range.max = Math.max(range.max, custom.max); + } + } + getMaxOverflow() { + return 0; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const {iScale, vScale} = meta; + const parsed = this.getParsed(index); + const custom = parsed._custom; + const value = isFloatBar(custom) + ? '[' + custom.start + ', ' + custom.end + ']' + : '' + vScale.getLabelForValue(parsed[vScale.axis]); + return { + label: '' + iScale.getLabelForValue(parsed[iScale.axis]), + value + }; + } + initialize() { + this.enableOptionSharing = true; + super.initialize(); + const meta = this._cachedMeta; + meta.stack = this.getDataset().stack; + } + update(mode) { + const meta = this._cachedMeta; + this.updateElements(meta.data, 0, meta.data.length, mode); + } + updateElements(bars, start, count, mode) { + const reset = mode === 'reset'; + const {index, _cachedMeta: {vScale}} = this; + const base = vScale.getBasePixel(); + const horizontal = vScale.isHorizontal(); + const ruler = this._getRuler(); + const firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + this.updateSharedOptions(sharedOptions, mode, firstOpts); + for (let i = start; i < start + count; i++) { + const parsed = this.getParsed(i); + const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : this._calculateBarValuePixels(i); + const ipixels = this._calculateBarIndexPixels(i, ruler); + const stack = (parsed._stacks || {})[vScale.axis]; + const properties = { + horizontal, + base: vpixels.base, + enableBorderRadius: !stack || isFloatBar(parsed._custom) || (index === stack._top || index === stack._bottom), + x: horizontal ? vpixels.head : ipixels.center, + y: horizontal ? ipixels.center : vpixels.head, + height: horizontal ? ipixels.size : Math.abs(vpixels.size), + width: horizontal ? Math.abs(vpixels.size) : ipixels.size + }; + if (includeOptions) { + properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode); + } + const options = properties.options || bars[i].options; + setBorderSkipped(properties, options, stack, index); + setInflateAmount(properties, options, ruler.ratio); + this.updateElement(bars[i], i, properties, mode); + } + } + _getStacks(last, dataIndex) { + const meta = this._cachedMeta; + const iScale = meta.iScale; + const metasets = iScale.getMatchingVisibleMetas(this._type); + const stacked = iScale.options.stacked; + const ilen = metasets.length; + const stacks = []; + let i, item; + for (i = 0; i < ilen; ++i) { + item = metasets[i]; + if (!item.controller.options.grouped) { + continue; + } + if (typeof dataIndex !== 'undefined') { + const val = item.controller.getParsed(dataIndex)[ + item.controller._cachedMeta.vScale.axis + ]; + if (isNullOrUndef(val) || isNaN(val)) { + continue; + } + } + if (stacked === false || stacks.indexOf(item.stack) === -1 || + (stacked === undefined && item.stack === undefined)) { + stacks.push(item.stack); + } + if (item.index === last) { + break; + } + } + if (!stacks.length) { + stacks.push(undefined); + } + return stacks; + } + _getStackCount(index) { + return this._getStacks(undefined, index).length; + } + _getStackIndex(datasetIndex, name, dataIndex) { + const stacks = this._getStacks(datasetIndex, dataIndex); + const index = (name !== undefined) + ? stacks.indexOf(name) + : -1; + return (index === -1) + ? stacks.length - 1 + : index; + } + _getRuler() { + const opts = this.options; + const meta = this._cachedMeta; + const iScale = meta.iScale; + const pixels = []; + let i, ilen; + for (i = 0, ilen = meta.data.length; i < ilen; ++i) { + pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i)); + } + const barThickness = opts.barThickness; + const min = barThickness || computeMinSampleSize(meta); + return { + min, + pixels, + start: iScale._startPixel, + end: iScale._endPixel, + stackCount: this._getStackCount(), + scale: iScale, + grouped: opts.grouped, + ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage + }; + } + _calculateBarValuePixels(index) { + const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = this; + const actualBase = baseValue || 0; + const parsed = this.getParsed(index); + const custom = parsed._custom; + const floating = isFloatBar(custom); + let value = parsed[vScale.axis]; + let start = 0; + let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value; + let head, size; + if (length !== value) { + start = length - value; + length = value; + } + if (floating) { + value = custom.barStart; + length = custom.barEnd - custom.barStart; + if (value !== 0 && sign(value) !== sign(custom.barEnd)) { + start = 0; + } + start += value; + } + const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start; + let base = vScale.getPixelForValue(startValue); + if (this.chart.getDataVisibility(index)) { + head = vScale.getPixelForValue(start + length); + } else { + head = base; + } + size = head - base; + if (Math.abs(size) < minBarLength) { + size = barSign(size, vScale, actualBase) * minBarLength; + if (value === actualBase) { + base -= size / 2; + } + head = base + size; + } + if (base === vScale.getPixelForValue(actualBase)) { + const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2; + base += halfGrid; + size -= halfGrid; + } + return { + size, + base, + head, + center: head + size / 2 + }; + } + _calculateBarIndexPixels(index, ruler) { + const scale = ruler.scale; + const options = this.options; + const skipNull = options.skipNull; + const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity); + let center, size; + if (ruler.grouped) { + const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount; + const range = options.barThickness === 'flex' + ? computeFlexCategoryTraits(index, ruler, options, stackCount) + : computeFitCategoryTraits(index, ruler, options, stackCount); + const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined); + center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); + size = Math.min(maxBarThickness, range.chunk * range.ratio); + } else { + center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index); + size = Math.min(maxBarThickness, ruler.min * ruler.ratio); + } + return { + base: center - size / 2, + head: center + size / 2, + center, + size + }; + } + draw() { + const meta = this._cachedMeta; + const vScale = meta.vScale; + const rects = meta.data; + const ilen = rects.length; + let i = 0; + for (; i < ilen; ++i) { + if (this.getParsed(i)[vScale.axis] !== null) { + rects[i].draw(this._ctx); + } + } + } +} +BarController.id = 'bar'; +BarController.defaults = { + datasetElementType: false, + dataElementType: 'bar', + categoryPercentage: 0.8, + barPercentage: 0.9, + grouped: true, + animations: { + numbers: { + type: 'number', + properties: ['x', 'y', 'base', 'width', 'height'] + } + } +}; +BarController.overrides = { + scales: { + _index_: { + type: 'category', + offset: true, + grid: { + offset: true + } + }, + _value_: { + type: 'linear', + beginAtZero: true, + } + } +}; + +class BubbleController extends DatasetController { + initialize() { + this.enableOptionSharing = true; + super.initialize(); + } + parsePrimitiveData(meta, data, start, count) { + const parsed = super.parsePrimitiveData(meta, data, start, count); + for (let i = 0; i < parsed.length; i++) { + parsed[i]._custom = this.resolveDataElementOptions(i + start).radius; + } + return parsed; + } + parseArrayData(meta, data, start, count) { + const parsed = super.parseArrayData(meta, data, start, count); + for (let i = 0; i < parsed.length; i++) { + const item = data[start + i]; + parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius); + } + return parsed; + } + parseObjectData(meta, data, start, count) { + const parsed = super.parseObjectData(meta, data, start, count); + for (let i = 0; i < parsed.length; i++) { + const item = data[start + i]; + parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius); + } + return parsed; + } + getMaxOverflow() { + const data = this._cachedMeta.data; + let max = 0; + for (let i = data.length - 1; i >= 0; --i) { + max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2); + } + return max > 0 && max; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const {xScale, yScale} = meta; + const parsed = this.getParsed(index); + const x = xScale.getLabelForValue(parsed.x); + const y = yScale.getLabelForValue(parsed.y); + const r = parsed._custom; + return { + label: meta.label, + value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')' + }; + } + update(mode) { + const points = this._cachedMeta.data; + this.updateElements(points, 0, points.length, mode); + } + updateElements(points, start, count, mode) { + const reset = mode === 'reset'; + const {iScale, vScale} = this._cachedMeta; + const firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + const iAxis = iScale.axis; + const vAxis = vScale.axis; + for (let i = start; i < start + count; i++) { + const point = points[i]; + const parsed = !reset && this.getParsed(i); + const properties = {}; + const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]); + const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]); + properties.skip = isNaN(iPixel) || isNaN(vPixel); + if (includeOptions) { + properties.options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); + if (reset) { + properties.options.radius = 0; + } + } + this.updateElement(point, i, properties, mode); + } + this.updateSharedOptions(sharedOptions, mode, firstOpts); + } + resolveDataElementOptions(index, mode) { + const parsed = this.getParsed(index); + let values = super.resolveDataElementOptions(index, mode); + if (values.$shared) { + values = Object.assign({}, values, {$shared: false}); + } + const radius = values.radius; + if (mode !== 'active') { + values.radius = 0; + } + values.radius += valueOrDefault(parsed && parsed._custom, radius); + return values; + } +} +BubbleController.id = 'bubble'; +BubbleController.defaults = { + datasetElementType: false, + dataElementType: 'point', + animations: { + numbers: { + type: 'number', + properties: ['x', 'y', 'borderWidth', 'radius'] + } + } +}; +BubbleController.overrides = { + scales: { + x: { + type: 'linear' + }, + y: { + type: 'linear' + } + }, + plugins: { + tooltip: { + callbacks: { + title() { + return ''; + } + } + } + } +}; + +function getRatioAndOffset(rotation, circumference, cutout) { + let ratioX = 1; + let ratioY = 1; + let offsetX = 0; + let offsetY = 0; + if (circumference < TAU) { + const startAngle = rotation; + const endAngle = startAngle + circumference; + const startX = Math.cos(startAngle); + const startY = Math.sin(startAngle); + const endX = Math.cos(endAngle); + const endY = Math.sin(endAngle); + const calcMax = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout); + const calcMin = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout); + const maxX = calcMax(0, startX, endX); + const maxY = calcMax(HALF_PI, startY, endY); + const minX = calcMin(PI, startX, endX); + const minY = calcMin(PI + HALF_PI, startY, endY); + ratioX = (maxX - minX) / 2; + ratioY = (maxY - minY) / 2; + offsetX = -(maxX + minX) / 2; + offsetY = -(maxY + minY) / 2; + } + return {ratioX, ratioY, offsetX, offsetY}; +} +class DoughnutController extends DatasetController { + constructor(chart, datasetIndex) { + super(chart, datasetIndex); + this.enableOptionSharing = true; + this.innerRadius = undefined; + this.outerRadius = undefined; + this.offsetX = undefined; + this.offsetY = undefined; + } + linkScales() {} + parse(start, count) { + const data = this.getDataset().data; + const meta = this._cachedMeta; + if (this._parsing === false) { + meta._parsed = data; + } else { + let getter = (i) => +data[i]; + if (isObject(data[start])) { + const {key = 'value'} = this._parsing; + getter = (i) => +resolveObjectKey(data[i], key); + } + let i, ilen; + for (i = start, ilen = start + count; i < ilen; ++i) { + meta._parsed[i] = getter(i); + } + } + } + _getRotation() { + return toRadians(this.options.rotation - 90); + } + _getCircumference() { + return toRadians(this.options.circumference); + } + _getRotationExtents() { + let min = TAU; + let max = -TAU; + for (let i = 0; i < this.chart.data.datasets.length; ++i) { + if (this.chart.isDatasetVisible(i)) { + const controller = this.chart.getDatasetMeta(i).controller; + const rotation = controller._getRotation(); + const circumference = controller._getCircumference(); + min = Math.min(min, rotation); + max = Math.max(max, rotation + circumference); + } + } + return { + rotation: min, + circumference: max - min, + }; + } + update(mode) { + const chart = this.chart; + const {chartArea} = chart; + const meta = this._cachedMeta; + const arcs = meta.data; + const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing; + const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0); + const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1); + const chartWeight = this._getRingWeight(this.index); + const {circumference, rotation} = this._getRotationExtents(); + const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout); + const maxWidth = (chartArea.width - spacing) / ratioX; + const maxHeight = (chartArea.height - spacing) / ratioY; + const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); + const outerRadius = toDimension(this.options.radius, maxRadius); + const innerRadius = Math.max(outerRadius * cutout, 0); + const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal(); + this.offsetX = offsetX * outerRadius; + this.offsetY = offsetY * outerRadius; + meta.total = this.calculateTotal(); + this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index); + this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0); + this.updateElements(arcs, 0, arcs.length, mode); + } + _circumference(i, reset) { + const opts = this.options; + const meta = this._cachedMeta; + const circumference = this._getCircumference(); + if ((reset && opts.animation.animateRotate) || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) { + return 0; + } + return this.calculateCircumference(meta._parsed[i] * circumference / TAU); + } + updateElements(arcs, start, count, mode) { + const reset = mode === 'reset'; + const chart = this.chart; + const chartArea = chart.chartArea; + const opts = chart.options; + const animationOpts = opts.animation; + const centerX = (chartArea.left + chartArea.right) / 2; + const centerY = (chartArea.top + chartArea.bottom) / 2; + const animateScale = reset && animationOpts.animateScale; + const innerRadius = animateScale ? 0 : this.innerRadius; + const outerRadius = animateScale ? 0 : this.outerRadius; + const firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + let startAngle = this._getRotation(); + let i; + for (i = 0; i < start; ++i) { + startAngle += this._circumference(i, reset); + } + for (i = start; i < start + count; ++i) { + const circumference = this._circumference(i, reset); + const arc = arcs[i]; + const properties = { + x: centerX + this.offsetX, + y: centerY + this.offsetY, + startAngle, + endAngle: startAngle + circumference, + circumference, + outerRadius, + innerRadius + }; + if (includeOptions) { + properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode); + } + startAngle += circumference; + this.updateElement(arc, i, properties, mode); + } + this.updateSharedOptions(sharedOptions, mode, firstOpts); + } + calculateTotal() { + const meta = this._cachedMeta; + const metaData = meta.data; + let total = 0; + let i; + for (i = 0; i < metaData.length; i++) { + const value = meta._parsed[i]; + if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) { + total += Math.abs(value); + } + } + return total; + } + calculateCircumference(value) { + const total = this._cachedMeta.total; + if (total > 0 && !isNaN(value)) { + return TAU * (Math.abs(value) / total); + } + return 0; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const chart = this.chart; + const labels = chart.data.labels || []; + const value = formatNumber(meta._parsed[index], chart.options.locale); + return { + label: labels[index] || '', + value, + }; + } + getMaxBorderWidth(arcs) { + let max = 0; + const chart = this.chart; + let i, ilen, meta, controller, options; + if (!arcs) { + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + arcs = meta.data; + controller = meta.controller; + break; + } + } + } + if (!arcs) { + return 0; + } + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + options = controller.resolveDataElementOptions(i); + if (options.borderAlign !== 'inner') { + max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0); + } + } + return max; + } + getMaxOffset(arcs) { + let max = 0; + for (let i = 0, ilen = arcs.length; i < ilen; ++i) { + const options = this.resolveDataElementOptions(i); + max = Math.max(max, options.offset || 0, options.hoverOffset || 0); + } + return max; + } + _getRingWeightOffset(datasetIndex) { + let ringWeightOffset = 0; + for (let i = 0; i < datasetIndex; ++i) { + if (this.chart.isDatasetVisible(i)) { + ringWeightOffset += this._getRingWeight(i); + } + } + return ringWeightOffset; + } + _getRingWeight(datasetIndex) { + return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0); + } + _getVisibleDatasetWeightTotal() { + return this._getRingWeightOffset(this.chart.data.datasets.length) || 1; + } +} +DoughnutController.id = 'doughnut'; +DoughnutController.defaults = { + datasetElementType: false, + dataElementType: 'arc', + animation: { + animateRotate: true, + animateScale: false + }, + animations: { + numbers: { + type: 'number', + properties: ['circumference', 'endAngle', 'innerRadius', 'outerRadius', 'startAngle', 'x', 'y', 'offset', 'borderWidth', 'spacing'] + }, + }, + cutout: '50%', + rotation: 0, + circumference: 360, + radius: '100%', + spacing: 0, + indexAxis: 'r', +}; +DoughnutController.descriptors = { + _scriptable: (name) => name !== 'spacing', + _indexable: (name) => name !== 'spacing', +}; +DoughnutController.overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(chart) { + const data = chart.data; + if (data.labels.length && data.datasets.length) { + const {labels: {pointStyle}} = chart.legend.options; + return data.labels.map((label, i) => { + const meta = chart.getDatasetMeta(0); + const style = meta.controller.getStyle(i); + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + pointStyle: pointStyle, + hidden: !chart.getDataVisibility(i), + index: i + }; + }); + } + return []; + } + }, + onClick(e, legendItem, legend) { + legend.chart.toggleDataVisibility(legendItem.index); + legend.chart.update(); + } + }, + tooltip: { + callbacks: { + title() { + return ''; + }, + label(tooltipItem) { + let dataLabel = tooltipItem.label; + const value = ': ' + tooltipItem.formattedValue; + if (isArray(dataLabel)) { + dataLabel = dataLabel.slice(); + dataLabel[0] += value; + } else { + dataLabel += value; + } + return dataLabel; + } + } + } + } +}; + +class LineController extends DatasetController { + initialize() { + this.enableOptionSharing = 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, undefined, { + 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 firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + 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'; + let prevParsed = start > 0 && this.getParsed(start - 1); + for (let i = start; i < start + count; ++i) { + const point = points[i]; + const parsed = this.getParsed(i); + const properties = directUpdate ? point : {}; + 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 && (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; + } + this.updateSharedOptions(sharedOptions, mode, firstOpts); + } + 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(); + } +} +LineController.id = 'line'; +LineController.defaults = { + datasetElementType: 'line', + dataElementType: 'point', + showLine: true, + spanGaps: false, +}; +LineController.overrides = { + scales: { + _index_: { + type: 'category', + }, + _value_: { + type: 'linear', + }, + } +}; +function getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) { + const pointCount = points.length; + let start = 0; + let count = pointCount; + if (meta._sorted) { + const {iScale, _parsed} = meta; + const axis = iScale.axis; + const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); + if (minDefined) { + start = _limitValue(Math.min( + _lookupByKey(_parsed, iScale.axis, min).lo, + animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), + 0, pointCount - 1); + } + if (maxDefined) { + count = _limitValue(Math.max( + _lookupByKey(_parsed, iScale.axis, max).hi + 1, + animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max)).hi + 1), + 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; +} + +class PolarAreaController extends DatasetController { + constructor(chart, datasetIndex) { + super(chart, datasetIndex); + this.innerRadius = undefined; + this.outerRadius = undefined; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const chart = this.chart; + const labels = chart.data.labels || []; + const value = formatNumber(meta._parsed[index].r, chart.options.locale); + return { + label: labels[index] || '', + value, + }; + } + update(mode) { + const arcs = this._cachedMeta.data; + this._updateRadius(); + this.updateElements(arcs, 0, arcs.length, mode); + } + _updateRadius() { + const chart = this.chart; + const chartArea = chart.chartArea; + const opts = chart.options; + const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + const outerRadius = Math.max(minSize / 2, 0); + const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); + const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount(); + this.outerRadius = outerRadius - (radiusLength * this.index); + this.innerRadius = this.outerRadius - radiusLength; + } + updateElements(arcs, start, count, mode) { + const reset = mode === 'reset'; + const chart = this.chart; + const dataset = this.getDataset(); + const opts = chart.options; + const animationOpts = opts.animation; + const scale = this._cachedMeta.rScale; + const centerX = scale.xCenter; + const centerY = scale.yCenter; + const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI; + let angle = datasetStartAngle; + let i; + const defaultAngle = 360 / this.countVisibleElements(); + for (i = 0; i < start; ++i) { + angle += this._computeAngle(i, mode, defaultAngle); + } + for (i = start; i < start + count; i++) { + const arc = arcs[i]; + let startAngle = angle; + let endAngle = angle + this._computeAngle(i, mode, defaultAngle); + let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0; + angle = endAngle; + if (reset) { + if (animationOpts.animateScale) { + outerRadius = 0; + } + if (animationOpts.animateRotate) { + startAngle = endAngle = datasetStartAngle; + } + } + const properties = { + x: centerX, + y: centerY, + innerRadius: 0, + outerRadius, + startAngle, + endAngle, + options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode) + }; + this.updateElement(arc, i, properties, mode); + } + } + countVisibleElements() { + const dataset = this.getDataset(); + const meta = this._cachedMeta; + let count = 0; + meta.data.forEach((element, index) => { + if (!isNaN(dataset.data[index]) && this.chart.getDataVisibility(index)) { + count++; + } + }); + return count; + } + _computeAngle(index, mode, defaultAngle) { + return this.chart.getDataVisibility(index) + ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) + : 0; + } +} +PolarAreaController.id = 'polarArea'; +PolarAreaController.defaults = { + dataElementType: 'arc', + animation: { + animateRotate: true, + animateScale: true + }, + animations: { + numbers: { + type: 'number', + properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius'] + }, + }, + indexAxis: 'r', + startAngle: 0, +}; +PolarAreaController.overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(chart) { + const data = chart.data; + if (data.labels.length && data.datasets.length) { + const {labels: {pointStyle}} = chart.legend.options; + return data.labels.map((label, i) => { + const meta = chart.getDatasetMeta(0); + const style = meta.controller.getStyle(i); + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + pointStyle: pointStyle, + hidden: !chart.getDataVisibility(i), + index: i + }; + }); + } + return []; + } + }, + onClick(e, legendItem, legend) { + legend.chart.toggleDataVisibility(legendItem.index); + legend.chart.update(); + } + }, + tooltip: { + callbacks: { + title() { + return ''; + }, + label(context) { + return context.chart.data.labels[context.dataIndex] + ': ' + context.formattedValue; + } + } + } + }, + scales: { + r: { + type: 'radialLinear', + angleLines: { + display: false + }, + beginAtZero: true, + grid: { + circular: true + }, + pointLabels: { + display: false + }, + startAngle: 0 + } + } +}; + +class PieController extends DoughnutController { +} +PieController.id = 'pie'; +PieController.defaults = { + cutout: 0, + rotation: 0, + circumference: 360, + radius: '100%' +}; + +class RadarController extends DatasetController { + getLabelAndValue(index) { + const vScale = this._cachedMeta.vScale; + const parsed = this.getParsed(index); + return { + label: vScale.getLabels()[index], + value: '' + vScale.getLabelForValue(parsed[vScale.axis]) + }; + } + update(mode) { + const meta = this._cachedMeta; + const line = meta.dataset; + const points = meta.data || []; + const labels = meta.iScale.getLabels(); + line.points = points; + if (mode !== 'resize') { + const options = this.resolveDatasetElementOptions(mode); + if (!this.options.showLine) { + options.borderWidth = 0; + } + const properties = { + _loop: true, + _fullLoop: labels.length === points.length, + options + }; + this.updateElement(line, undefined, properties, mode); + } + this.updateElements(points, 0, points.length, mode); + } + updateElements(points, start, count, mode) { + const dataset = this.getDataset(); + const scale = this._cachedMeta.rScale; + const reset = mode === 'reset'; + for (let i = start; i < start + count; i++) { + const point = points[i]; + const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); + const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]); + const x = reset ? scale.xCenter : pointPosition.x; + const y = reset ? scale.yCenter : pointPosition.y; + const properties = { + x, + y, + angle: pointPosition.angle, + skip: isNaN(x) || isNaN(y), + options + }; + this.updateElement(point, i, properties, mode); + } + } +} +RadarController.id = 'radar'; +RadarController.defaults = { + datasetElementType: 'line', + dataElementType: 'point', + indexAxis: 'r', + showLine: true, + elements: { + line: { + fill: 'start' + } + }, +}; +RadarController.overrides = { + aspectRatio: 1, + scales: { + r: { + type: 'radialLinear', + } + } +}; + +class ScatterController extends LineController { +} +ScatterController.id = 'scatter'; +ScatterController.defaults = { + showLine: false, + fill: false +}; +ScatterController.overrides = { + interaction: { + mode: 'point' + }, + plugins: { + tooltip: { + callbacks: { + title() { + return ''; + }, + label(item) { + return '(' + item.label + ', ' + item.formattedValue + ')'; + } + } + } + }, + scales: { + x: { + type: 'linear' + }, + y: { + type: 'linear' + } + } +}; + +var controllers = /*#__PURE__*/Object.freeze({ +__proto__: null, +BarController: BarController, +BubbleController: BubbleController, +DoughnutController: DoughnutController, +LineController: LineController, +PolarAreaController: PolarAreaController, +PieController: PieController, +RadarController: RadarController, +ScatterController: ScatterController +}); + +function abstract() { + throw new Error('This method is not implemented: Check that a complete date adapter is provided.'); +} +class DateAdapter { + constructor(options) { + this.options = options || {}; + } + formats() { + return abstract(); + } + parse(value, format) { + return abstract(); + } + format(timestamp, format) { + return abstract(); + } + add(timestamp, amount, unit) { + return abstract(); + } + diff(a, b, unit) { + return abstract(); + } + startOf(timestamp, unit, weekday) { + return abstract(); + } + endOf(timestamp, unit) { + return abstract(); + } +} +DateAdapter.override = function(members) { + Object.assign(DateAdapter.prototype, members); +}; +var adapters = { + _date: DateAdapter +}; + +function getRelativePosition(e, chart) { + if ('native' in e) { + return { + x: e.x, + y: e.y + }; + } + return getRelativePosition$1(e, chart); +} +function evaluateAllVisibleItems(chart, handler) { + const metasets = chart.getSortedVisibleDatasetMetas(); + let index, data, element; + for (let i = 0, ilen = metasets.length; i < ilen; ++i) { + ({index, data} = metasets[i]); + for (let j = 0, jlen = data.length; j < jlen; ++j) { + element = data[j]; + if (!element.skip) { + handler(element, index, j); + } + } + } +} +function binarySearch(metaset, axis, value, intersect) { + const {controller, data, _sorted} = metaset; + const iScale = controller._cachedMeta.iScale; + if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) { + const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey; + if (!intersect) { + return lookupMethod(data, axis, value); + } 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 optimizedEvaluateItems(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, 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, index, 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) { + const items = []; + if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { + return items; + } + const evaluationFunc = function(element, datasetIndex, index) { + if (element.inRange(position.x, position.y, useFinalPosition)) { + items.push({element, datasetIndex, index}); + } + }; + optimizedEvaluateItems(chart, axis, position, evaluationFunc, true); + return items; +} +function getNearestRadialItems(chart, position, axis, useFinalPosition) { + let items = []; + function evaluationFunc(element, datasetIndex, index) { + 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}); + } + } + optimizedEvaluateItems(chart, axis, position, evaluationFunc); + return items; +} +function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition) { + let items = []; + const distanceMetric = getDistanceMetricForAxis(axis); + let minDistance = Number.POSITIVE_INFINITY; + function evaluationFunc(element, datasetIndex, index) { + const inRange = element.inRange(position.x, position.y, useFinalPosition); + if (intersect && !inRange) { + return; + } + const center = element.getCenterPoint(useFinalPosition); + const pointInArea = _isPointInArea(center, chart.chartArea, chart._minPadding); + if (!pointInArea && !inRange) { + return; + } + const distance = distanceMetric(position, center); + if (distance < minDistance) { + items = [{element, datasetIndex, index}]; + minDistance = distance; + } else if (distance === minDistance) { + items.push({element, datasetIndex, index}); + } + } + optimizedEvaluateItems(chart, axis, position, evaluationFunc); + return items; +} +function getNearestItems(chart, position, axis, intersect, useFinalPosition) { + if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { + return []; + } + return axis === 'r' && !intersect + ? getNearestRadialItems(chart, position, axis, useFinalPosition) + : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition); +} +function getAxisItems(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const items = []; + const axis = options.axis; + const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange'; + let intersectsItem = false; + evaluateAllVisibleItems(chart, (element, datasetIndex, index) => { + if (element[rangeMethod](position[axis], useFinalPosition)) { + items.push({element, datasetIndex, index}); + } + if (element.inRange(position.x, position.y, useFinalPosition)) { + intersectsItem = true; + } + }); + if (options.intersect && !intersectsItem) { + return []; + } + return items; +} +var Interaction = { + modes: { + index(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'x'; + const items = options.intersect + ? getIntersectItems(chart, position, axis, useFinalPosition) + : getNearestItems(chart, position, axis, false, useFinalPosition); + const elements = []; + if (!items.length) { + return []; + } + chart.getSortedVisibleDatasetMetas().forEach((meta) => { + const index = items[0].index; + const element = meta.data[index]; + if (element && !element.skip) { + elements.push({element, datasetIndex: meta.index, index}); + } + }); + return elements; + }, + dataset(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'xy'; + let items = options.intersect + ? getIntersectItems(chart, position, axis, useFinalPosition) : + getNearestItems(chart, position, axis, false, useFinalPosition); + 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'; + return getIntersectItems(chart, position, axis, useFinalPosition); + }, + nearest(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'xy'; + return getNearestItems(chart, position, axis, options.intersect, useFinalPosition); + }, + x(chart, e, options, useFinalPosition) { + return getAxisItems(chart, e, {axis: 'x', intersect: options.intersect}, useFinalPosition); + }, + y(chart, e, options, useFinalPosition) { + return getAxisItems(chart, e, {axis: 'y', intersect: 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(layouts) { + const stacks = {}; + for (const wrap of layouts) { + 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(layouts, params) { + const stacks = buildStacks(layouts); + const {vBoxMaxWidth, hBoxMaxHeight} = params; + let i, ilen, layout; + for (i = 0, ilen = layouts.length; i < ilen; ++i) { + layout = layouts[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(positions) { + const margin = {left: 0, top: 0, right: 0, bottom: 0}; + positions.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] || {count: 1, 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; +} +defaults.set('layout', { + autoPadding: true, + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } +}); +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 index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; + if (index !== -1) { + chart.boxes.splice(index, 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 !== undefined) { + canvas.width = displayWidth; + } + } + if (isNullOrEmpty(renderHeight)) { + if (canvas.style.height === '') { + canvas.height = canvas.width / (aspectRatio || 2); + } else { + const displayHeight = readUsedSize(canvas, 'height'); + if (displayHeight !== undefined) { + canvas.height = displayHeight; + } + } + } + return canvas; +} +const eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; +function addListener(node, type, listener) { + node.addEventListener(type, listener, eventListenerOptions); +} +function removeListener(chart, type, listener) { + chart.canvas.removeEventListener(type, listener, eventListenerOptions); +} +function fromNativeEvent(event, chart) { + const type = EVENT_TYPES[event.type] || event.type; + const {x, y} = getRelativePosition$1(event, chart); + return { + type, + chart, + native: event, + x: x !== undefined ? x : null, + y: y !== undefined ? 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 = 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, (args) => { + const event = args[0]; + return [event, event.offsetX, event.offsetY]; + }); + 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] = undefined; + } + getDevicePixelRatio() { + return window.devicePixelRatio; + } + getMaximumSize(canvas, width, height, aspectRatio) { + return getMaximumSize(canvas, width, height, aspectRatio); + } + isAttached(canvas) { + const container = _getParentNode(canvas); + return !!(container && container.isConnected); + } +} + +function _detectPlatform(canvas) { + if (!_isDomSupported() || (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas)) { + return BasicPlatform; + } + return DomPlatform; +} + +class Element { + constructor() { + this.x = undefined; + this.y = undefined; + this.active = false; + this.options = undefined; + this.$animations = undefined; + } + 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; + } +} +Element.defaults = {}; +Element.defaultRoutes = undefined; + +const formatters = { + values(value) { + return isArray(value) ? value : '' + value; + }, + numeric(tickValue, index, 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 > 1e+15) { + notation = 'scientific'; + } + delta = calculateDelta(tickValue, ticks); + } + const logDelta = log10(Math.abs(delta)); + const numDecimal = 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); + }, + logarithmic(tickValue, index, ticks) { + if (tickValue === 0) { + return '0'; + } + const remain = tickValue / (Math.pow(10, Math.floor(log10(tickValue)))); + if (remain === 1 || remain === 2 || remain === 5) { + return formatters.numeric.call(this, tickValue, index, ticks); + } + return ''; + } +}; +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}; + +defaults.set('scale', { + display: true, + offset: false, + reverse: false, + beginAtZero: false, + bounds: 'ticks', + grace: 0, + grid: { + display: true, + lineWidth: 1, + drawBorder: true, + drawOnChartArea: true, + drawTicks: true, + tickLength: 8, + tickWidth: (_ctx, options) => options.lineWidth, + tickColor: (_ctx, options) => options.color, + offset: false, + borderDash: [], + borderDashOffset: 0.0, + borderWidth: 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, + } +}); +defaults.route('scale.ticks', 'color', '', 'color'); +defaults.route('scale.grid', 'color', '', 'borderColor'); +defaults.route('scale.grid', 'borderColor', '', 'borderColor'); +defaults.route('scale.title', 'color', '', 'color'); +defaults.describe('scale', { + _fallback: false, + _scriptable: (name) => !name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser', + _indexable: (name) => name !== 'borderDash' && name !== 'tickBorderDash', +}); +defaults.describe('scales', { + _fallback: 'scale', +}); +defaults.describe('scale.ticks', { + _scriptable: (name) => name !== 'backdropPadding' && name !== 'callback', + _indexable: (name) => name !== 'backdropPadding', +}); + +function autoSkip(scale, ticks) { + const tickOpts = scale.options.ticks; + const ticksLimit = tickOpts.maxTicksLimit || determineMaxTicks(scale); + 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; +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, index, offsetGridLines) { + const length = scale.ticks.length; + const validIndex = Math.min(index, length - 1); + const start = scale._startPixel; + const end = scale._endPixel; + const epsilon = 1e-6; + let lineValue = scale.getPixelForTick(validIndex); + let offset; + if (offsetGridLines) { + if (length === 1) { + offset = Math.max(lineValue - start, end - lineValue); + } else if (index === 0) { + offset = (scale.getPixelForTick(1) - lineValue) / 2; + } else { + offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; + } + lineValue += validIndex < index ? 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, index, tick) { + return createContext(parent, { + tick, + index, + 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 = undefined; + this.ctx = cfg.ctx; + this.chart = cfg.chart; + this.top = undefined; + this.bottom = undefined; + this.left = undefined; + this.right = undefined; + this.width = undefined; + this.height = undefined; + this._margins = { + left: 0, + right: 0, + top: 0, + bottom: 0 + }; + this.maxWidth = undefined; + this.maxHeight = undefined; + this.paddingTop = undefined; + this.paddingBottom = undefined; + this.paddingLeft = undefined; + this.paddingRight = undefined; + this.axis = undefined; + this.labelRotation = undefined; + this.min = undefined; + this.max = undefined; + this._range = undefined; + this.ticks = []; + this._gridLineItems = null; + this._labelItems = null; + this._labelSizes = null; + this._length = 0; + this._maxLength = 0; + this._longestTextCache = {}; + this._startPixel = undefined; + this._endPixel = undefined; + this._reversePixels = false; + this._userMax = undefined; + this._userMin = undefined; + this._suggestedMax = undefined; + this._suggestedMin = undefined; + this._ticksLength = 0; + this._borderValue = 0; + this._cache = {}; + this._dataLimitsCached = false; + this.$context = undefined; + } + init(options) { + this.options = options.setContext(this.getContext()); + this.axis = options.axis; + this._userMin = this.parse(options.min); + this._userMax = this.parse(options.max); + this._suggestedMin = this.parse(options.suggestedMin); + this._suggestedMax = this.parse(options.suggestedMax); + } + parse(raw, index) { + return raw; + } + getUserBounds() { + let {_userMin, _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 || []; + } + 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; + } + 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 = this.ticks.length; + 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]); + } + 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 { + 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); + } + return labelSizes; + } + _computeLabelSizes(ticks, length) { + const {ctx, _longestTextCache: caches} = this; + const widths = []; + const heights = []; + let widestLabelSize = 0; + let highestLabelSize = 0; + let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel; + for (i = 0; i < length; ++i) { + 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, index) { + return NaN; + } + getValueForPixel(pixel) {} + getPixelForTick(index) { + const ticks = this.ticks; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index].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(index) { + const ticks = this.ticks || []; + if (index >= 0 && index < ticks.length) { + const tick = ticks[index]; + return tick.$context || + (tick.$context = createTickContext(this.getContext(), index, 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} = 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 = grid.setContext(this.getContext()); + const axisWidth = borderOpts.drawBorder ? borderOpts.borderWidth : 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 optsAtIndex = grid.setContext(this.getContext(i)); + const lineWidth = optsAtIndex.lineWidth; + const lineColor = optsAtIndex.color; + const borderDash = grid.borderDash || []; + const borderDashOffset = optsAtIndex.borderDashOffset; + const tickWidth = optsAtIndex.tickWidth; + const tickColor = optsAtIndex.tickColor; + const tickBorderDash = optsAtIndex.tickBorderDash || []; + const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset; + lineValue = getPixelForGridLine(this, i, offset); + if (lineValue === undefined) { + 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 color = optsAtIndex.color; + const strokeColor = optsAtIndex.textStrokeColor; + const strokeWidth = optsAtIndex.textStrokeWidth; + if (isHorizontal) { + x = pixel; + 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; + } + } 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 = y + textOffset - labelPadding.top; + let left = x - 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; + } + backdrop = { + left, + top, + width: width + labelPadding.width, + height: height + labelPadding.height, + color: optsAtIndex.backdropColor, + }; + } + items.push({ + rotation, + label, + font, + color, + strokeColor, + strokeWidth, + textOffset, + textAlign, + 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'; + } + 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 index = ticks.findIndex(t => t.value === value); + if (index >= 0) { + const opts = grid.setContext(this.getContext(index)); + 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: {grid}} = this; + const borderOpts = grid.setContext(this.getContext()); + const axisWidth = grid.drawBorder ? borderOpts.borderWidth : 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.borderWidth; + ctx.strokeStyle = borderOpts.borderColor; + 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._labelItems || (this._labelItems = this._computeLabelItems(chartArea)); + let i, ilen; + for (i = 0, ilen = items.length; i < ilen; ++i) { + const item = items[i]; + const tickFont = item.font; + const label = item.label; + if (item.backdrop) { + ctx.fillStyle = item.backdrop.color; + ctx.fillRect(item.backdrop.left, item.backdrop.top, item.backdrop.width, item.backdrop.height); + } + let y = item.textOffset; + renderText(ctx, label, 0, y, tickFont, item); + } + 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); + 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: gz + 1, + 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(index) { + const opts = this.options.ticks.setContext(this.getContext(index)); + 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 = 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(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, registry, component) { + const camelMethod = _capitalize(method); + callback(component['before' + camelMethod], [], component); + registry[method](component); + callback(component['after' + camelMethod], [], component); + } + _getRegistryForType(type) { + for (let i = 0; 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 === undefined) { + throw new Error('"' + id + '" is not a registered ' + type + '.'); + } + return item; + } +} +var registry = new Registry(); + +class PluginService { + constructor() { + this._init = []; + } + notify(chart, hook, args, filter) { + if (hook === 'beforeInit') { + this._init = this._createDescriptors(chart, true); + this._notify(this._init, chart, 'install'); + } + const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart); + const result = this._notify(descriptors, chart, hook, args); + if (hook === 'afterDestroy') { + this._notify(descriptors, chart, 'stop'); + this._notify(this._init, chart, 'uninstall'); + } + return result; + } + _notify(descriptors, chart, hook, args) { + args = args || {}; + for (const descriptor of descriptors) { + 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 = undefined; + } + } + _descriptors(chart) { + if (this._cache) { + return this._cache; + } + const descriptors = this._cache = this._createDescriptors(chart); + this._notifyStateChanges(chart); + return descriptors; + } + _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 descriptors = this._cache; + const diff = (a, b) => a.filter(x => !b.some(y => x.plugin.id === y.plugin.id)); + this._notify(diff(previousDescriptors, descriptors), chart, 'stop'); + this._notify(diff(descriptors, previousDescriptors), chart, 'start'); + } +} +function allPlugins(config) { + 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); + } + } + return plugins; +} +function getOpts(options, all) { + if (!all && options === false) { + return null; + } + if (options === true) { + return {}; + } + return options; +} +function createDescriptors(chart, plugins, options, all) { + const result = []; + const context = chart.getContext(); + for (let i = 0; i < plugins.length; i++) { + const plugin = plugins[i]; + const id = plugin.id; + const opts = getOpts(options[id], all); + if (opts === null) { + continue; + } + result.push({ + plugin, + options: pluginOpts(chart.config, plugin, opts, context) + }); + } + return result; +} +function pluginOpts(config, plugin, opts, context) { + const keys = config.pluginScopeKeys(plugin); + const scopes = config.getOptionScopes(opts, keys); + 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 axisFromPosition(position) { + if (position === 'top' || position === 'bottom') { + return 'x'; + } + if (position === 'left' || position === 'right') { + return 'y'; + } +} +function determineAxis(id, scaleOptions) { + if (id === 'x' || id === 'y') { + return id; + } + return scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.charAt(0).toLowerCase(); +} +function mergeScaleConfig(config, options) { + const chartDefaults = overrides[config.type] || {scales: {}}; + const configScales = options.scales || {}; + const chartIndexAxis = getIndexAxis(config.type, options); + const firstIDs = Object.create(null); + const scales = 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); + const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis); + const defaultScaleOptions = chartDefaults.scales || {}; + firstIDs[axis] = firstIDs[axis] || id; + scales[id] = mergeIf(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'] || firstIDs[axis] || axis; + scales[id] = scales[id] || 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 = new Map(); +const keysCached = 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 = (set, obj, key) => { + const opts = resolveObjectKey(obj, key); + if (opts !== undefined) { + set.add(opts); + } +}; +class Config { + constructor(config) { + this._config = initConfig(config); + this._scopeCache = new Map(); + this._resolverCache = new Map(); + } + get platform() { + return this._config.platform; + } + get type() { + return this._config.type; + } + set type(type) { + this._config.type = type; + } + get data() { + return this._config.data; + } + set data(data) { + this._config.data = initData(data); + } + get options() { + return this._config.options; + } + set options(options) { + this._config.options = options; + } + get plugins() { + return this._config.plugins; + } + update() { + const config = this._config; + this.clearCache(); + initOptions(config); + } + clearCache() { + this._scopeCache.clear(); + this._resolverCache.clear(); + } + datasetScopeKeys(datasetType) { + return cachedKeys(datasetType, + () => [[ + `datasets.${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 = 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 = 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(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, names, context, prefixes = ['']) { + const result = {$shared: true}; + const {resolver, subPrefixes} = getResolver(this._resolverCache, scopes, prefixes); + let options = resolver; + if (needContext(resolver, names)) { + result.$shared = false; + context = isFunction(context) ? context() : context; + const subResolver = this.createResolver(scopes, context, subPrefixes); + options = _attachContext(resolver, context, subResolver); + } + for (const prop of names) { + result[prop] = options[prop]; + } + return result; + } + createResolver(scopes, context, prefixes = [''], descriptorDefaults) { + const {resolver} = getResolver(this._resolverCache, scopes, prefixes); + return isObject(context) + ? _attachContext(resolver, context, undefined, descriptorDefaults) + : resolver; + } +} +function getResolver(resolverCache, scopes, prefixes) { + let cache = resolverCache.get(scopes); + if (!cache) { + cache = 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).reduce((acc, key) => acc || isFunction(value[key]), false); +function needContext(proxy, names) { + const {isScriptable, isIndexable} = _descriptors(proxy); + for (const prop of names) { + 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 = "3.7.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 { + 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 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 = undefined; + this.boxes = []; + this.currentDevicePixelRatio = undefined; + this.chartArea = undefined; + this._active = []; + this._lastEvent = undefined; + this._listeners = {}; + this._responsiveListeners = undefined; + this._sortedMetasets = []; + this.scales = {}; + this._plugins = new PluginService(); + this.$proxies = {}; + this._hiddenIndices = {}; + this.attached = false; + this._animationsDisabled = undefined; + this.$context = undefined; + 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; + } + _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 === undefined || 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, index) => { + if (datasets.filter(x => x === meta._dataset).length === 0) { + this._destroyDatasetMeta(index); + } + }); + } + 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.prototype, { + 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, index) => { + item._idx = index; + }); + 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(index, mode) { + const meta = this.getDatasetMeta(index); + const args = {meta, index, 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._resize(width, height); + this._resizeBeforeDraw = null; + } + 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 clip = meta._clip; + const useClip = !clip.disabled; + const area = this.chartArea; + const args = { + meta, + index: meta.index, + cancelable: true + }; + if (this.notifyPlugins('beforeDatasetDraw', args) === false) { + return; + } + if (useClip) { + clipArea(ctx, { + left: clip.left === false ? 0 : area.left - clip.left, + right: clip.right === false ? this.width : area.right + clip.right, + top: clip.top === false ? 0 : area.top - clip.top, + bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom + }); + } + meta.controller.draw(); + if (useClip) { + unclipArea(ctx); + } + args.cancelable = false; + this.notifyPlugins('afterDatasetDraw', args); + } + 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(index) { + this._hiddenIndices[index] = !this._hiddenIndices[index]; + } + getDataVisibility(index) { + return !this._hiddenIndices[index]; + } + _updateVisibility(datasetIndex, dataIndex, visible) { + const mode = visible ? 'show' : 'hide'; + const meta = this.getDatasetMeta(datasetIndex); + const anims = meta.controller._resolveAnimations(undefined, 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 : undefined); + } + } + 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; + } + this.notifyPlugins('destroy'); + 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, listener) => { + platform.addEventListener(this, type, listener); + listeners[type] = listener; + }; + 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, listener) => { + platform.addEventListener(this, type, listener); + listeners[type] = listener; + }; + const _remove = (type, listener) => { + if (listeners[type]) { + platform.removeEventListener(this, type, listener); + 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 = undefined; + } + 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}) => { + const meta = this.getDatasetMeta(datasetIndex); + if (!meta) { + throw new Error('No dataset found at index ' + datasetIndex); + } + return { + datasetIndex, + element: meta.data[index], + index, + }; + }); + 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); + } + _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: _isPointInArea(e, this.chartArea, this._minPadding) + }; + 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); + } +} +const invalidatePlugins = () => each(Chart.instances, (chart) => chart._plugins.invalidate()); +const enumerable = true; +Object.defineProperties(Chart, { + defaults: { + enumerable, + value: defaults + }, + instances: { + enumerable, + value: instances + }, + overrides: { + enumerable, + value: overrides + }, + registry: { + enumerable, + value: registry + }, + version: { + enumerable, + value: version + }, + getChart: { + enumerable, + value: getChart + }, + register: { + enumerable, + value: (...items) => { + registry.add(...items); + invalidatePlugins(); + } + }, + unregister: { + enumerable, + value: (...items) => { + registry.remove(...items); + invalidatePlugins(); + } + } +}); + +function clipArc(ctx, element, endAngle) { + const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element; + let angleMargin = pixelMargin / outerRadius; + ctx.beginPath(); + ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin); + if (innerRadius > pixelMargin) { + angleMargin = pixelMargin / innerRadius; + ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true); + } else { + ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI); + } + ctx.closePath(); + ctx.clip(); +} +function toRadiusCorners(value) { + return _readValueToProps(value, ['outerStart', 'outerEnd', 'innerStart', 'innerEnd']); +} +function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) { + const o = toRadiusCorners(arc.options.borderRadius); + const halfThickness = (outerRadius - innerRadius) / 2; + const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2); + const computeOuterLimit = (val) => { + const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2; + return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit)); + }; + return { + outerStart: computeOuterLimit(o.outerStart), + outerEnd: computeOuterLimit(o.outerEnd), + innerStart: _limitValue(o.innerStart, 0, innerLimit), + innerEnd: _limitValue(o.innerEnd, 0, innerLimit), + }; +} +function rThetaToXY(r, theta, x, y) { + return { + x: x + r * Math.cos(theta), + y: y + r * Math.sin(theta), + }; +} +function pathArc(ctx, element, offset, spacing, end) { + const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element; + const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0); + const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0; + let spacingOffset = 0; + const alpha = end - start; + if (spacing) { + const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0; + const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0; + const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2; + const adjustedAngle = avNogSpacingRadius !== 0 ? (alpha * avNogSpacingRadius) / (avNogSpacingRadius + spacing) : alpha; + spacingOffset = (alpha - adjustedAngle) / 2; + } + const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius; + const angleOffset = (alpha - beta) / 2; + const startAngle = start + angleOffset + spacingOffset; + const endAngle = end - angleOffset - spacingOffset; + const {outerStart, outerEnd, innerStart, innerEnd} = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle); + const outerStartAdjustedRadius = outerRadius - outerStart; + const outerEndAdjustedRadius = outerRadius - outerEnd; + const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius; + const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius; + const innerStartAdjustedRadius = innerRadius + innerStart; + const innerEndAdjustedRadius = innerRadius + innerEnd; + const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius; + const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius; + ctx.beginPath(); + ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle); + if (outerEnd > 0) { + const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI); + } + const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y); + ctx.lineTo(p4.x, p4.y); + if (innerEnd > 0) { + const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI); + } + ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true); + if (innerStart > 0) { + const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI); + } + const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y); + ctx.lineTo(p8.x, p8.y); + if (outerStart > 0) { + const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle); + } + ctx.closePath(); +} +function drawArc(ctx, element, offset, spacing) { + const {fullCircles, startAngle, circumference} = element; + let endAngle = element.endAngle; + if (fullCircles) { + pathArc(ctx, element, offset, spacing, startAngle + TAU); + for (let i = 0; i < fullCircles; ++i) { + ctx.fill(); + } + if (!isNaN(circumference)) { + endAngle = startAngle + circumference % TAU; + if (circumference % TAU === 0) { + endAngle += TAU; + } + } + } + pathArc(ctx, element, offset, spacing, endAngle); + ctx.fill(); + return endAngle; +} +function drawFullCircleBorders(ctx, element, inner) { + const {x, y, startAngle, pixelMargin, fullCircles} = element; + const outerRadius = Math.max(element.outerRadius - pixelMargin, 0); + const innerRadius = element.innerRadius + pixelMargin; + let i; + if (inner) { + clipArc(ctx, element, startAngle + TAU); + } + ctx.beginPath(); + ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true); + for (i = 0; i < fullCircles; ++i) { + ctx.stroke(); + } + ctx.beginPath(); + ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU); + for (i = 0; i < fullCircles; ++i) { + ctx.stroke(); + } +} +function drawBorder(ctx, element, offset, spacing, endAngle) { + const {options} = element; + const {borderWidth, borderJoinStyle} = options; + const inner = options.borderAlign === 'inner'; + if (!borderWidth) { + return; + } + if (inner) { + ctx.lineWidth = borderWidth * 2; + ctx.lineJoin = borderJoinStyle || 'round'; + } else { + ctx.lineWidth = borderWidth; + ctx.lineJoin = borderJoinStyle || 'bevel'; + } + if (element.fullCircles) { + drawFullCircleBorders(ctx, element, inner); + } + if (inner) { + clipArc(ctx, element, endAngle); + } + pathArc(ctx, element, offset, spacing, endAngle); + ctx.stroke(); +} +class ArcElement extends Element { + constructor(cfg) { + super(); + this.options = undefined; + this.circumference = undefined; + this.startAngle = undefined; + this.endAngle = undefined; + this.innerRadius = undefined; + this.outerRadius = undefined; + this.pixelMargin = 0; + this.fullCircles = 0; + if (cfg) { + Object.assign(this, cfg); + } + } + inRange(chartX, chartY, useFinalPosition) { + const point = this.getProps(['x', 'y'], useFinalPosition); + const {angle, distance} = getAngleFromPoint(point, {x: chartX, y: chartY}); + const {startAngle, endAngle, innerRadius, outerRadius, circumference} = this.getProps([ + 'startAngle', + 'endAngle', + 'innerRadius', + 'outerRadius', + 'circumference' + ], useFinalPosition); + const rAdjust = this.options.spacing / 2; + const _circumference = valueOrDefault(circumference, endAngle - startAngle); + const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle); + const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust); + return (betweenAngles && withinRadius); + } + getCenterPoint(useFinalPosition) { + const {x, y, startAngle, endAngle, innerRadius, outerRadius} = this.getProps([ + 'x', + 'y', + 'startAngle', + 'endAngle', + 'innerRadius', + 'outerRadius', + 'circumference', + ], useFinalPosition); + const {offset, spacing} = this.options; + const halfAngle = (startAngle + endAngle) / 2; + const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2; + return { + x: x + Math.cos(halfAngle) * halfRadius, + y: y + Math.sin(halfAngle) * halfRadius + }; + } + tooltipPosition(useFinalPosition) { + return this.getCenterPoint(useFinalPosition); + } + draw(ctx) { + const {options, circumference} = this; + const offset = (options.offset || 0) / 2; + const spacing = (options.spacing || 0) / 2; + this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0; + this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0; + if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) { + return; + } + ctx.save(); + let radiusOffset = 0; + if (offset) { + radiusOffset = offset / 2; + const halfAngle = (this.startAngle + this.endAngle) / 2; + ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset); + if (this.circumference >= PI) { + radiusOffset = offset; + } + } + ctx.fillStyle = options.backgroundColor; + ctx.strokeStyle = options.borderColor; + const endAngle = drawArc(ctx, this, radiusOffset, spacing); + drawBorder(ctx, this, radiusOffset, spacing, endAngle); + ctx.restore(); + } +} +ArcElement.id = 'arc'; +ArcElement.defaults = { + borderAlign: 'center', + borderColor: '#fff', + borderJoinStyle: undefined, + borderRadius: 0, + borderWidth: 2, + offset: 0, + spacing: 0, + angle: undefined, +}; +ArcElement.defaultRoutes = { + backgroundColor: 'backgroundColor' +}; + +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 = (index) => (start + (reverse ? ilen - index : index)) % 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 = undefined; + this._chart = undefined; + this._loop = undefined; + this._fullLoop = undefined; + this._path = undefined; + this._points = undefined; + this._segments = undefined; + this._decimated = false; + this._pointsUpdated = false; + this._datasetIndex = undefined; + 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 = undefined; + } + } +} +LineElement.id = 'line'; +LineElement.defaults = { + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: 'miter', + borderWidth: 3, + capBezierPoints: true, + cubicInterpolationMode: 'default', + fill: false, + spanGaps: false, + stepped: false, + tension: 0, +}; +LineElement.defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' +}; +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(); + this.options = undefined; + this.parsed = undefined; + this.skip = undefined; + this.stop = undefined; + 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; + } +} +PointElement.id = 'point'; +PointElement.defaults = { + borderWidth: 1, + hitRadius: 1, + hoverBorderWidth: 1, + hoverRadius: 4, + pointStyle: 'circle', + radius: 3, + rotation: 0 +}; +PointElement.defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' +}; + +function getBarBounds(bar, useFinalPosition) { + const {x, y, base, width, height} = bar.getProps(['x', 'y', 'base', 'width', 'height'], useFinalPosition); + let left, right, top, bottom, half; + if (bar.horizontal) { + half = height / 2; + left = Math.min(x, base); + right = Math.max(x, base); + top = y - half; + bottom = y + half; + } else { + half = width / 2; + left = x - half; + right = x + half; + top = Math.min(y, base); + bottom = Math.max(y, base); + } + return {left, top, right, bottom}; +} +function skipOrLimit(skip, value, min, max) { + return skip ? 0 : _limitValue(value, min, max); +} +function parseBorderWidth(bar, maxW, maxH) { + const value = bar.options.borderWidth; + const skip = bar.borderSkipped; + const o = toTRBL(value); + return { + t: skipOrLimit(skip.top, o.top, 0, maxH), + r: skipOrLimit(skip.right, o.right, 0, maxW), + b: skipOrLimit(skip.bottom, o.bottom, 0, maxH), + l: skipOrLimit(skip.left, o.left, 0, maxW) + }; +} +function parseBorderRadius(bar, maxW, maxH) { + const {enableBorderRadius} = bar.getProps(['enableBorderRadius']); + const value = bar.options.borderRadius; + const o = toTRBLCorners(value); + const maxR = Math.min(maxW, maxH); + const skip = bar.borderSkipped; + const enableBorder = enableBorderRadius || isObject(value); + return { + topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR), + topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR), + bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR), + bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR) + }; +} +function boundingRects(bar) { + const bounds = getBarBounds(bar); + const width = bounds.right - bounds.left; + const height = bounds.bottom - bounds.top; + const border = parseBorderWidth(bar, width / 2, height / 2); + const radius = parseBorderRadius(bar, width / 2, height / 2); + return { + outer: { + x: bounds.left, + y: bounds.top, + w: width, + h: height, + radius + }, + inner: { + x: bounds.left + border.l, + y: bounds.top + border.t, + w: width - border.l - border.r, + h: height - border.t - border.b, + radius: { + topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)), + topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)), + bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)), + bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)), + } + } + }; +} +function inRange(bar, x, y, useFinalPosition) { + const skipX = x === null; + const skipY = y === null; + const skipBoth = skipX && skipY; + const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition); + return bounds + && (skipX || _isBetween(x, bounds.left, bounds.right)) + && (skipY || _isBetween(y, bounds.top, bounds.bottom)); +} +function hasRadius(radius) { + return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight; +} +function addNormalRectPath(ctx, rect) { + ctx.rect(rect.x, rect.y, rect.w, rect.h); +} +function inflateRect(rect, amount, refRect = {}) { + const x = rect.x !== refRect.x ? -amount : 0; + const y = rect.y !== refRect.y ? -amount : 0; + const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x; + const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y; + return { + x: rect.x + x, + y: rect.y + y, + w: rect.w + w, + h: rect.h + h, + radius: rect.radius + }; +} +class BarElement extends Element { + constructor(cfg) { + super(); + this.options = undefined; + this.horizontal = undefined; + this.base = undefined; + this.width = undefined; + this.height = undefined; + this.inflateAmount = undefined; + if (cfg) { + Object.assign(this, cfg); + } + } + draw(ctx) { + const {inflateAmount, options: {borderColor, backgroundColor}} = this; + const {inner, outer} = boundingRects(this); + const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath; + ctx.save(); + if (outer.w !== inner.w || outer.h !== inner.h) { + ctx.beginPath(); + addRectPath(ctx, inflateRect(outer, inflateAmount, inner)); + ctx.clip(); + addRectPath(ctx, inflateRect(inner, -inflateAmount, outer)); + ctx.fillStyle = borderColor; + ctx.fill('evenodd'); + } + ctx.beginPath(); + addRectPath(ctx, inflateRect(inner, inflateAmount)); + ctx.fillStyle = backgroundColor; + ctx.fill(); + ctx.restore(); + } + inRange(mouseX, mouseY, useFinalPosition) { + return inRange(this, mouseX, mouseY, useFinalPosition); + } + inXRange(mouseX, useFinalPosition) { + return inRange(this, mouseX, null, useFinalPosition); + } + inYRange(mouseY, useFinalPosition) { + return inRange(this, null, mouseY, useFinalPosition); + } + getCenterPoint(useFinalPosition) { + const {x, y, base, horizontal} = this.getProps(['x', 'y', 'base', 'horizontal'], useFinalPosition); + return { + x: horizontal ? (x + base) / 2 : x, + y: horizontal ? y : (y + base) / 2 + }; + } + getRange(axis) { + return axis === 'x' ? this.width / 2 : this.height / 2; + } +} +BarElement.id = 'bar'; +BarElement.defaults = { + borderSkipped: 'start', + borderWidth: 0, + borderRadius: 0, + inflateAmount: 'auto', + pointStyle: undefined +}; +BarElement.defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' +}; + +var elements = /*#__PURE__*/Object.freeze({ +__proto__: null, +ArcElement: ArcElement, +LineElement: LineElement, +PointElement: PointElement, +BarElement: BarElement +}); + +function lttbDecimation(data, start, count, availableWidth, options) { + const samples = options.samples || availableWidth; + if (samples >= count) { + return data.slice(start, start + count); + } + const decimated = []; + const bucketWidth = (count - 2) / (samples - 2); + let sampledIndex = 0; + const endIndex = start + count - 1; + let a = start; + let i, maxAreaPoint, maxArea, area, nextA; + decimated[sampledIndex++] = data[a]; + for (i = 0; i < samples - 2; i++) { + let avgX = 0; + let avgY = 0; + let j; + const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start; + const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start; + const avgRangeLength = avgRangeEnd - avgRangeStart; + for (j = avgRangeStart; j < avgRangeEnd; j++) { + avgX += data[j].x; + avgY += data[j].y; + } + avgX /= avgRangeLength; + avgY /= avgRangeLength; + const rangeOffs = Math.floor(i * bucketWidth) + 1 + start; + const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start; + const {x: pointAx, y: pointAy} = data[a]; + maxArea = area = -1; + for (j = rangeOffs; j < rangeTo; j++) { + area = 0.5 * Math.abs( + (pointAx - avgX) * (data[j].y - pointAy) - + (pointAx - data[j].x) * (avgY - pointAy) + ); + if (area > maxArea) { + maxArea = area; + maxAreaPoint = data[j]; + nextA = j; + } + } + decimated[sampledIndex++] = maxAreaPoint; + a = nextA; + } + decimated[sampledIndex++] = data[endIndex]; + return decimated; +} +function minMaxDecimation(data, start, count, availableWidth) { + let avgX = 0; + let countX = 0; + let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY; + const decimated = []; + const endIndex = start + count - 1; + const xMin = data[start].x; + const xMax = data[endIndex].x; + const dx = xMax - xMin; + for (i = start; i < start + count; ++i) { + point = data[i]; + x = (point.x - xMin) / dx * availableWidth; + y = point.y; + const truncX = x | 0; + if (truncX === prevX) { + if (y < minY) { + minY = y; + minIndex = i; + } else if (y > maxY) { + maxY = y; + maxIndex = i; + } + avgX = (countX * avgX + point.x) / ++countX; + } else { + const lastIndex = i - 1; + if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) { + const intermediateIndex1 = Math.min(minIndex, maxIndex); + const intermediateIndex2 = Math.max(minIndex, maxIndex); + if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) { + decimated.push({ + ...data[intermediateIndex1], + x: avgX, + }); + } + if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) { + decimated.push({ + ...data[intermediateIndex2], + x: avgX + }); + } + } + if (i > 0 && lastIndex !== startIndex) { + decimated.push(data[lastIndex]); + } + decimated.push(point); + prevX = truncX; + countX = 0; + minY = maxY = y; + minIndex = maxIndex = startIndex = i; + } + } + return decimated; +} +function cleanDecimatedDataset(dataset) { + if (dataset._decimated) { + const data = dataset._data; + delete dataset._decimated; + delete dataset._data; + Object.defineProperty(dataset, 'data', {value: data}); + } +} +function cleanDecimatedData(chart) { + chart.data.datasets.forEach((dataset) => { + cleanDecimatedDataset(dataset); + }); +} +function getStartAndCountOfVisiblePointsSimplified(meta, points) { + const pointCount = points.length; + let start = 0; + let count; + const {iScale} = meta; + const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); + if (minDefined) { + start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1); + } + if (maxDefined) { + count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start; + } else { + count = pointCount - start; + } + return {start, count}; +} +var plugin_decimation = { + id: 'decimation', + defaults: { + algorithm: 'min-max', + enabled: false, + }, + beforeElementsUpdate: (chart, args, options) => { + if (!options.enabled) { + cleanDecimatedData(chart); + return; + } + const availableWidth = chart.width; + chart.data.datasets.forEach((dataset, datasetIndex) => { + const {_data, indexAxis} = dataset; + const meta = chart.getDatasetMeta(datasetIndex); + const data = _data || dataset.data; + if (resolve([indexAxis, chart.options.indexAxis]) === 'y') { + return; + } + if (meta.type !== 'line') { + return; + } + const xAxis = chart.scales[meta.xAxisID]; + if (xAxis.type !== 'linear' && xAxis.type !== 'time') { + return; + } + if (chart.options.parsing) { + return; + } + let {start, count} = getStartAndCountOfVisiblePointsSimplified(meta, data); + const threshold = options.threshold || 4 * availableWidth; + if (count <= threshold) { + cleanDecimatedDataset(dataset); + return; + } + if (isNullOrUndef(_data)) { + dataset._data = data; + delete dataset.data; + Object.defineProperty(dataset, 'data', { + configurable: true, + enumerable: true, + get: function() { + return this._decimated; + }, + set: function(d) { + this._data = d; + } + }); + } + let decimated; + switch (options.algorithm) { + case 'lttb': + decimated = lttbDecimation(data, start, count, availableWidth, options); + break; + case 'min-max': + decimated = minMaxDecimation(data, start, count, availableWidth); + break; + default: + throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`); + } + dataset._decimated = decimated; + }); + }, + destroy(chart) { + cleanDecimatedData(chart); + } +}; + +function getLineByIndex(chart, index) { + const meta = chart.getDatasetMeta(index); + const visible = meta && chart.isDatasetVisible(index); + return visible ? meta.dataset : null; +} +function parseFillOption(line) { + const options = line.options; + const fillOption = options.fill; + let fill = valueOrDefault(fillOption && fillOption.target, fillOption); + if (fill === undefined) { + fill = !!options.backgroundColor; + } + if (fill === false || fill === null) { + return false; + } + if (fill === true) { + return 'origin'; + } + return fill; +} +function decodeFill(line, index, count) { + const fill = parseFillOption(line); + if (isObject(fill)) { + return isNaN(fill.value) ? false : fill; + } + let target = parseFloat(fill); + if (isNumberFinite(target) && Math.floor(target) === target) { + if (fill[0] === '-' || fill[0] === '+') { + target = index + target; + } + if (target === index || target < 0 || target >= count) { + return false; + } + return target; + } + return ['origin', 'start', 'end', 'stack', 'shape'].indexOf(fill) >= 0 && fill; +} +function computeLinearBoundary(source) { + const {scale = {}, fill} = source; + let target = null; + let horizontal; + if (fill === 'start') { + target = scale.bottom; + } else if (fill === 'end') { + target = scale.top; + } else if (isObject(fill)) { + target = scale.getPixelForValue(fill.value); + } else if (scale.getBasePixel) { + target = scale.getBasePixel(); + } + if (isNumberFinite(target)) { + horizontal = scale.isHorizontal(); + return { + x: horizontal ? target : null, + y: horizontal ? null : target + }; + } + return null; +} +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 computeCircularBoundary(source) { + const {scale, fill} = source; + const options = scale.options; + const length = scale.getLabels().length; + const target = []; + const start = options.reverse ? scale.max : scale.min; + const end = options.reverse ? scale.min : scale.max; + let i, center, value; + if (fill === 'start') { + value = start; + } else if (fill === 'end') { + value = end; + } else if (isObject(fill)) { + value = fill.value; + } else { + value = scale.getBaseValue(); + } + if (options.grid.circular) { + center = scale.getPointPositionForValue(0, start); + return new simpleArc({ + x: center.x, + y: center.y, + radius: scale.getDistanceFromCenterForValue(value) + }); + } + for (i = 0; i < length; ++i) { + target.push(scale.getPointPositionForValue(i, value)); + } + return target; +} +function computeBoundary(source) { + const scale = source.scale || {}; + if (scale.getPointPositionForValue) { + return computeCircularBoundary(source); + } + return computeLinearBoundary(source); +} +function findSegmentEnd(start, end, points) { + for (;end > start; end--) { + const point = points[end]; + if (!isNaN(point.x) && !isNaN(point.y)) { + break; + } + } + return 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 buildStackLine(source) { + const {scale, index, line} = source; + const points = []; + const segments = line.segments; + const sourcePoints = line.points; + const linesBelow = getLinesBelow(scale, index); + 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, index) { + const below = []; + const metas = scale.getMatchingVisibleMetas('line'); + for (let i = 0; i < metas.length; i++) { + const meta = metas[i]; + if (meta.index === index) { + 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}; +} +function getTarget(source) { + const {chart, fill, line} = source; + if (isNumberFinite(fill)) { + return getLineByIndex(chart, fill); + } + if (fill === 'stack') { + return buildStackLine(source); + } + if (fill === 'shape') { + return true; + } + const boundary = computeBoundary(source); + if (boundary instanceof simpleArc) { + return boundary; + } + return createBoundaryLine(boundary, line); +} +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 resolveTarget(sources, index, propagate) { + const source = sources[index]; + let fill = source.fill; + const visited = [index]; + let target; + if (!propagate) { + return fill; + } + while (fill !== false && visited.indexOf(fill) === -1) { + if (!isNumberFinite(fill)) { + return fill; + } + target = sources[fill]; + if (!target) { + return false; + } + if (target.visible) { + return fill; + } + visited.push(fill); + fill = target.fill; + } + return false; +} +function _clip(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 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 _getEdge(a, b, prop, fn) { + if (a && b) { + return fn(a[prop], b[prop]); + } + return a ? a[prop] : b ? b[prop] : 0; +} +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 clipBounds(ctx, scale, bounds) { + const {top, bottom} = scale.chart.chartArea; + const {property, start, end} = bounds || {}; + if (property === 'x') { + ctx.beginPath(); + ctx.rect(start, top, end - start, bottom - top); + ctx.clip(); + } +} +function interpolatedLineTo(ctx, target, point, property) { + const interpolatedPoint = target.interpolate(point, property); + if (interpolatedPoint) { + ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y); + } +} +function _fill(ctx, cfg) { + const {line, target, property, color, scale} = cfg; + const segments = _segments(line, target, property); + for (const {source: src, target: tgt, start, end} of segments) { + const {style: {backgroundColor = color} = {}} = src; + const notShape = target !== true; + ctx.save(); + ctx.fillStyle = backgroundColor; + clipBounds(ctx, scale, 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 doFill(ctx, cfg) { + const {line, target, above, below, area, scale} = cfg; + const property = line._loop ? 'angle' : cfg.axis; + ctx.save(); + if (property === 'x' && below !== above) { + _clip(ctx, target, area.top); + _fill(ctx, {line, target, color: above, scale, property}); + ctx.restore(); + ctx.save(); + _clip(ctx, target, area.bottom); + } + _fill(ctx, {line, target, color: below, scale, property}); + ctx.restore(); +} +function drawfill(ctx, source, area) { + const target = getTarget(source); + const {line, scale, axis} = source; + const lineOpts = line.options; + const fillOption = lineOpts.fill; + const color = lineOpts.backgroundColor; + const {above = color, below = color} = fillOption || {}; + if (target && line.points.length) { + clipArea(ctx, area); + doFill(ctx, {line, target, above, below, area, scale, axis}); + unclipArea(ctx); + } +} +var plugin_filler = { + 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 draw = 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 (draw) { + 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 (source) { + drawfill(chart.ctx, source, chart.chartArea); + } + } + }, + beforeDatasetDraw(chart, args, options) { + const source = args.meta.$filler; + if (!source || source.fill === false || 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 = 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 = undefined; + this.columnSizes = undefined; + this.lineWidths = undefined; + this.maxHeight = undefined; + this.maxWidth = undefined; + this.top = undefined; + this.bottom = undefined; + this.left = undefined; + this.right = undefined; + this.height = undefined; + this.width = undefined; + this._margins = undefined; + this.position = undefined; + this.weight = undefined; + this.fullSize = undefined; + } + 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, fontSize, boxWidth, itemHeight) + 10; + } + this.width = Math.min(width, options.maxWidth || this.maxWidth); + this.height = Math.min(height, options.maxHeight || this.maxHeight); + } + _fitRows(titleHeight, fontSize, boxWidth, itemHeight) { + const {ctx, 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, fontSize, 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 = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + 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 {color: fontColor, 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: boxWidth * Math.SQRT2 / 2, + pointStyle: legendItem.pointStyle, + rotation: legendItem.rotation, + borderWidth: lineWidth + }; + const centerX = rtlHelper.xPlus(x, boxWidth / 2); + const centerY = y + halfFontSize; + drawPoint(ctx, drawOptions, centerX, centerY); + } 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 || fontColor; + ctx.fillStyle = legendItem.fontColor || 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 { + 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') { + 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 isListened(type, opts) { + if (type === 'mousemove' && (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: 1000, + onClick(e, legendItem, legend) { + const index = legendItem.datasetIndex; + const ci = legend.chart; + if (ci.isDatasetVisible(index)) { + ci.hide(index); + legendItem.hidden = true; + } else { + ci.show(index); + 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}} = chart.legend.options; + return chart._getSortedDatasetMetas().map((meta) => { + const style = meta.controller.getStyle(usePointStyle ? 0 : undefined); + const borderWidth = toPadding(style.borderWidth); + return { + text: datasets[meta.index].label, + fillStyle: style.backgroundColor, + fontColor: color, + hidden: !meta.visible, + lineCap: style.borderCapStyle, + lineDash: style.borderDash, + lineDashOffset: style.borderDashOffset, + lineJoin: style.borderJoinStyle, + lineWidth: (borderWidth.width + borderWidth.height) / 4, + strokeStyle: style.borderColor, + pointStyle: pointStyle || style.pointStyle, + rotation: style.rotation, + textAlign: textAlign || style.textAlign, + borderRadius: 0, + datasetIndex: meta.index + }; + }, this); + } + }, + title: { + color: (ctx) => ctx.chart.options.color, + display: false, + position: 'center', + text: '', + } + }, + descriptors: { + _scriptable: (name) => !name.startsWith('on'), + labels: { + _scriptable: (name) => !['generateLabels', 'filter', 'sort'].includes(name), + } + }, +}; + +class Title extends Element { + constructor(config) { + super(); + this.chart = config.chart; + this.options = config.options; + this.ctx = config.ctx; + this._padding = undefined; + this.top = undefined; + this.bottom = undefined; + this.left = undefined; + this.right = undefined; + this.width = undefined; + this.height = undefined; + this.position = undefined; + this.weight = undefined; + this.fullSize = undefined; + } + update(maxWidth, maxHeight) { + const opts = this.options; + this.left = 0; + this.top = 0; + if (!opts.display) { + this.width = this.height = this.right = this.bottom = 0; + return; + } + this.width = this.right = maxWidth; + this.height = this.bottom = maxHeight; + const lineCount = isArray(opts.text) ? opts.text.length : 1; + this._padding = toPadding(opts.padding); + const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height; + if (this.isHorizontal()) { + this.height = textSize; + } else { + this.width = textSize; + } + } + isHorizontal() { + const pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + } + _drawArgs(offset) { + const {top, left, bottom, right, options} = this; + const align = options.align; + let rotation = 0; + let maxWidth, titleX, titleY; + if (this.isHorizontal()) { + titleX = _alignStartEnd(align, left, right); + titleY = top + offset; + maxWidth = right - left; + } else { + if (options.position === 'left') { + titleX = left + offset; + titleY = _alignStartEnd(align, bottom, top); + rotation = PI * -0.5; + } else { + titleX = right - offset; + titleY = _alignStartEnd(align, top, bottom); + rotation = PI * 0.5; + } + maxWidth = bottom - top; + } + return {titleX, titleY, maxWidth, rotation}; + } + draw() { + const ctx = this.ctx; + const opts = this.options; + if (!opts.display) { + return; + } + const fontOpts = toFont(opts.font); + const lineHeight = fontOpts.lineHeight; + const offset = lineHeight / 2 + this._padding.top; + const {titleX, titleY, maxWidth, rotation} = this._drawArgs(offset); + renderText(ctx, opts.text, 0, 0, fontOpts, { + color: opts.color, + maxWidth, + rotation, + textAlign: _toLeftRightCenter(opts.align), + textBaseline: 'middle', + translation: [titleX, titleY], + }); + } +} +function createTitle(chart, titleOpts) { + const title = new Title({ + ctx: chart.ctx, + options: titleOpts, + chart + }); + layouts.configure(chart, title, titleOpts); + layouts.addBox(chart, title); + chart.titleBlock = title; +} +var plugin_title = { + id: 'title', + _element: Title, + start(chart, _args, options) { + createTitle(chart, options); + }, + stop(chart) { + const titleBlock = chart.titleBlock; + layouts.removeBox(chart, titleBlock); + delete chart.titleBlock; + }, + beforeUpdate(chart, _args, options) { + const title = chart.titleBlock; + layouts.configure(chart, title, options); + title.options = options; + }, + defaults: { + align: 'center', + display: false, + font: { + weight: 'bold', + }, + fullSize: true, + padding: 10, + position: 'top', + text: '', + weight: 2000 + }, + defaultRoutes: { + color: 'color' + }, + descriptors: { + _scriptable: true, + _indexable: false, + }, +}; + +const map = new WeakMap(); +var plugin_subtitle = { + id: 'subtitle', + start(chart, _args, options) { + const title = new Title({ + ctx: chart.ctx, + options, + chart + }); + layouts.configure(chart, title, options); + layouts.addBox(chart, title); + map.set(chart, title); + }, + stop(chart) { + layouts.removeBox(chart, map.get(chart)); + map.delete(chart); + }, + beforeUpdate(chart, _args, options) { + const title = map.get(chart); + layouts.configure(chart, title, options); + title.options = options; + }, + defaults: { + align: 'center', + display: false, + font: { + weight: 'normal', + }, + fullSize: true, + padding: 0, + position: 'top', + text: '', + weight: 1500 + }, + defaultRoutes: { + color: 'color' + }, + descriptors: { + _scriptable: true, + _indexable: false, + }, +}; + +const positioners = { + average(items) { + if (!items.length) { + return false; + } + let i, len; + let x = 0; + 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(); + x += pos.x; + y += pos.y; + ++count; + } + } + return { + x: x / count, + 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} = item; + const controller = chart.getDatasetMeta(datasetIndex).controller; + const {label, value} = controller.getLabelAndValue(index); + return { + chart, + label, + parsed: controller.getParsed(index), + raw: chart.data.datasets[datasetIndex].data[index], + formattedValue: value, + dataset: controller.getDataset(), + dataIndex: index, + 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(callback) { + return pushOrConcat([], splitNewlines(callback)); +} +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; +} +class Tooltip extends Element { + constructor(config) { + super(); + this.opacity = 0; + this._active = []; + this._eventPosition = undefined; + this._size = undefined; + this._cachedAnimations = undefined; + this._tooltipItems = []; + this.$animations = undefined; + this.$context = undefined; + this.chart = config.chart || config._chart; + this._chart = this.chart; + this.options = config.options; + this.dataPoints = undefined; + this.title = undefined; + this.beforeBody = undefined; + this.body = undefined; + this.afterBody = undefined; + this.footer = undefined; + this.xAlign = undefined; + this.yAlign = undefined; + this.x = undefined; + this.y = undefined; + this.height = undefined; + this.width = undefined; + this.caretX = undefined; + this.caretY = undefined; + this.labelColors = undefined; + this.labelPointStyles = undefined; + this.labelTextColors = undefined; + } + initialize(options) { + this.options = options; + this._cachedAnimations = undefined; + this.$context = undefined; + } + _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 = callbacks.beforeTitle.apply(this, [context]); + const title = callbacks.title.apply(this, [context]); + const afterTitle = callbacks.afterTitle.apply(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(options.callbacks.beforeBody.apply(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(scoped.beforeLabel.call(this, context))); + pushOrConcat(bodyItem.lines, scoped.label.call(this, context)); + pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(this, context))); + bodyItems.push(bodyItem); + }); + return bodyItems; + } + getAfterBody(tooltipItems, options) { + return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this, [tooltipItems])); + } + getFooter(tooltipItems, options) { + const {callbacks} = options; + const beforeFooter = callbacks.beforeFooter.apply(this, [tooltipItems]); + const footer = callbacks.footer.apply(this, [tooltipItems]); + const afterFooter = callbacks.afterFooter.apply(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, index, array) => options.filter(element, index, 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(scoped.labelColor.call(this, context)); + labelPointStyles.push(scoped.labelPointStyle.call(this, context)); + labelTextColors.push(scoped.labelTextColor.call(this, context)); + }); + this.labelColors = labelColors; + this.labelPointStyles = labelPointStyles; + this.labelTextColors = labelTextColors; + this.dataPoints = tooltipItems; + 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 = undefined; + 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 labelColors = this.labelColors[i]; + const labelPointStyle = this.labelPointStyles[i]; + const {boxHeight, boxWidth, boxPadding} = 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 = labelColors.borderColor; + ctx.fillStyle = labelColors.backgroundColor; + drawPoint(ctx, drawOptions, centerX, centerY); + } else { + ctx.lineWidth = labelColors.borderWidth || 1; + ctx.strokeStyle = labelColors.borderColor; + ctx.setLineDash(labelColors.borderDash || []); + ctx.lineDashOffset = labelColors.borderDashOffset || 0; + const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth - boxPadding); + const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - boxPadding - 2); + const borderRadius = toTRBLCorners(labelColors.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 = labelColors.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 = labelColors.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); + } + } + } + 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}) => { + const meta = this.chart.getDatasetMeta(datasetIndex); + if (!meta) { + throw new Error('Cannot find a dataset at index ' + datasetIndex); + } + return { + datasetIndex, + element: meta.data[index], + index, + }; + }); + 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; + } + 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); + } +} +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; + const args = { + tooltip + }; + if (chart.notifyPlugins('beforeTooltipDraw', args) === false) { + return; + } + if (tooltip) { + 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: { + 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 + } + }, + 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'] +}; + +var plugins = /*#__PURE__*/Object.freeze({ +__proto__: null, +Decimation: plugin_decimation, +Filler: plugin_filler, +Legend: plugin_legend, +SubTitle: plugin_subtitle, +Title: plugin_title, +Tooltip: plugin_tooltip +}); + +const addIfString = (labels, raw, index, addedLabels) => { + if (typeof raw === 'string') { + index = labels.push(raw) - 1; + addedLabels.unshift({index, label: raw}); + } else if (isNaN(raw)) { + index = null; + } + return index; +}; +function findOrAddLabel(labels, raw, index, addedLabels) { + const first = labels.indexOf(raw); + if (first === -1) { + return addIfString(labels, raw, index, addedLabels); + } + const last = labels.lastIndexOf(raw); + return first !== last ? index : first; +} +const validIndex = (index, max) => index === null ? null : _limitValue(Math.round(index), 0, max); +class CategoryScale extends Scale { + constructor(cfg) { + super(cfg); + this._startValue = undefined; + this._valueRange = 0; + this._addedLabels = []; + } + init(scaleOptions) { + const added = this._addedLabels; + if (added.length) { + const labels = this.getLabels(); + for (const {index, label} of added) { + if (labels[index] === label) { + labels.splice(index, 1); + } + } + this._addedLabels = []; + } + super.init(scaleOptions); + } + parse(raw, index) { + if (isNullOrUndef(raw)) { + return null; + } + const labels = this.getLabels(); + index = isFinite(index) && labels[index] === raw ? index + : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels); + return validIndex(index, 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) { + const labels = this.getLabels(); + if (value >= 0 && value < labels.length) { + return labels[value]; + } + return 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(index) { + const ticks = this.ticks; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index].value); + } + getValueForPixel(pixel) { + return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange); + } + getBasePixel() { + return this.bottom; + } +} +CategoryScale.id = 'category'; +CategoryScale.defaults = { + ticks: { + callback: CategoryScale.prototype.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 / 1000)) { + 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 / 1000)) { + 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) { + ticks.push({value: Math.round((niceMin + j * spacing) * factor) / factor}); + } + 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)) || 0.001; + const length = 0.75 * minSpacing * ('' + value).length; + return Math.min(minSpacing / ratio, length); +} +class LinearScaleBase extends Scale { + constructor(cfg) { + super(cfg); + this.start = undefined; + this.end = undefined; + this._startValue = undefined; + this._endValue = undefined; + this._valueRange = 0; + } + parse(raw, index) { + 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 = 1; + if (max >= Number.MAX_SAFE_INTEGER || min <= Number.MIN_SAFE_INTEGER) { + offset = 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 > 1000) { + console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`); + maxTicks = 1000; + } + } 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)) || 0.001; + 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; + } +} +LinearScale.id = 'linear'; +LinearScale.defaults = { + ticks: { + callback: Ticks.formatters.numeric + } +}; + +function isMajor(tickVal) { + const remain = tickVal / (Math.pow(10, Math.floor(log10(tickVal)))); + return remain === 1; +} +function generateTicks(generationOptions, dataRange) { + const endExp = Math.floor(log10(dataRange.max)); + const endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); + const ticks = []; + let tickVal = finiteOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); + let exp = Math.floor(log10(tickVal)); + let significand = Math.floor(tickVal / Math.pow(10, exp)); + let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; + do { + ticks.push({value: tickVal, major: isMajor(tickVal)}); + ++significand; + if (significand === 10) { + significand = 1; + ++exp; + precision = exp >= 0 ? 1 : precision; + } + tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; + } while (exp < endExp || (exp === endExp && significand < endSignificand)); + const lastTick = finiteOrDefault(generationOptions.max, tickVal); + ticks.push({value: lastTick, major: isMajor(tickVal)}); + return ticks; +} +class LogarithmicScale extends Scale { + constructor(cfg) { + super(cfg); + this.start = undefined; + this.end = undefined; + this._startValue = undefined; + this._valueRange = 0; + } + parse(raw, index) { + const value = LinearScaleBase.prototype.parse.apply(this, [raw, index]); + if (value === 0) { + this._zero = true; + return undefined; + } + return isNumberFinite(value) && value > 0 ? value : null; + } + determineDataLimits() { + const {min, max} = this.getMinMax(true); + this.min = isNumberFinite(min) ? Math.max(0, min) : null; + this.max = isNumberFinite(max) ? Math.max(0, max) : null; + if (this.options.beginAtZero) { + this._zero = true; + } + this.handleTickRangeOptions(); + } + handleTickRangeOptions() { + const {minDefined, maxDefined} = this.getUserBounds(); + let min = this.min; + let max = this.max; + const setMin = v => (min = minDefined ? min : v); + const setMax = v => (max = maxDefined ? max : v); + const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m); + if (min === max) { + if (min <= 0) { + setMin(1); + setMax(10); + } else { + setMin(exp(min, -1)); + setMax(exp(max, +1)); + } + } + if (min <= 0) { + setMin(exp(max, -1)); + } + if (max <= 0) { + setMax(exp(min, +1)); + } + if (this._zero && this.min !== this._suggestedMin && min === exp(this.min, 0)) { + setMin(exp(min, -1)); + } + this.min = min; + this.max = max; + } + buildTicks() { + const opts = this.options; + const generationOptions = { + min: this._userMin, + max: this._userMax + }; + const ticks = generateTicks(generationOptions, this); + 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; + } + getLabelForValue(value) { + return value === undefined + ? '0' + : formatNumber(value, this.chart.options.locale, this.options.ticks.format); + } + configure() { + const start = this.min; + super.configure(); + this._startValue = log10(start); + this._valueRange = log10(this.max) - log10(start); + } + getPixelForValue(value) { + if (value === undefined || value === 0) { + value = this.min; + } + if (value === null || isNaN(value)) { + return NaN; + } + return this.getPixelForDecimal(value === this.min + ? 0 + : (log10(value) - this._startValue) / this._valueRange); + } + getValueForPixel(pixel) { + const decimal = this.getDecimalForPixel(pixel); + return Math.pow(10, this._startValue + decimal * this._valueRange); + } +} +LogarithmicScale.id = 'logarithmic'; +LogarithmicScale.defaults = { + ticks: { + callback: Ticks.formatters.logarithmic, + major: { + enabled: true + } + } +}; + +function getTickBackdropHeight(opts) { + const tickOpts = opts.ticks; + if (tickOpts.display && opts.display) { + const padding = toPadding(tickOpts.backdropPadding); + return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height; + } + return 0; +} +function measureLabelSize(ctx, font, label) { + label = isArray(label) ? label : [label]; + return { + w: _longestText(ctx, font.string, label), + h: label.length * font.lineHeight + }; +} +function determineLimits(angle, pos, size, min, max) { + if (angle === min || angle === max) { + return { + start: pos - (size / 2), + end: pos + (size / 2) + }; + } else if (angle < min || angle > max) { + return { + start: pos - size, + end: pos + }; + } + return { + start: pos, + end: pos + size + }; +} +function fitWithPointLabels(scale) { + const orig = { + l: scale.left + scale._padding.left, + r: scale.right - scale._padding.right, + t: scale.top + scale._padding.top, + b: scale.bottom - scale._padding.bottom + }; + const limits = Object.assign({}, orig); + const labelSizes = []; + const padding = []; + const valueCount = scale._pointLabels.length; + const pointLabelOpts = scale.options.pointLabels; + const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0; + for (let i = 0; i < valueCount; i++) { + const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i)); + padding[i] = opts.padding; + const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle); + const plFont = toFont(opts.font); + const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]); + labelSizes[i] = textSize; + const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle); + const angle = Math.round(toDegrees(angleRadians)); + const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); + const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); + updateLimits(limits, orig, angleRadians, hLimits, vLimits); + } + scale.setCenterPoint( + orig.l - limits.l, + limits.r - orig.r, + orig.t - limits.t, + limits.b - orig.b + ); + scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding); +} +function updateLimits(limits, orig, angle, hLimits, vLimits) { + const sin = Math.abs(Math.sin(angle)); + const cos = Math.abs(Math.cos(angle)); + let x = 0; + let y = 0; + if (hLimits.start < orig.l) { + x = (orig.l - hLimits.start) / sin; + limits.l = Math.min(limits.l, orig.l - x); + } else if (hLimits.end > orig.r) { + x = (hLimits.end - orig.r) / sin; + limits.r = Math.max(limits.r, orig.r + x); + } + if (vLimits.start < orig.t) { + y = (orig.t - vLimits.start) / cos; + limits.t = Math.min(limits.t, orig.t - y); + } else if (vLimits.end > orig.b) { + y = (vLimits.end - orig.b) / cos; + limits.b = Math.max(limits.b, orig.b + y); + } +} +function buildPointLabelItems(scale, labelSizes, padding) { + const items = []; + const valueCount = scale._pointLabels.length; + const opts = scale.options; + const extra = getTickBackdropHeight(opts) / 2; + const outerDistance = scale.drawingArea; + const additionalAngle = opts.pointLabels.centerPointLabels ? PI / valueCount : 0; + for (let i = 0; i < valueCount; i++) { + const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], additionalAngle); + const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI))); + const size = labelSizes[i]; + const y = yForAngle(pointLabelPosition.y, size.h, angle); + const textAlign = getTextAlignForAngle(angle); + const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign); + items.push({ + x: pointLabelPosition.x, + y, + textAlign, + left, + top: y, + right: left + size.w, + bottom: y + size.h + }); + } + return items; +} +function getTextAlignForAngle(angle) { + if (angle === 0 || angle === 180) { + return 'center'; + } else if (angle < 180) { + return 'left'; + } + return 'right'; +} +function leftForTextAlign(x, w, align) { + if (align === 'right') { + x -= w; + } else if (align === 'center') { + x -= (w / 2); + } + return x; +} +function yForAngle(y, h, angle) { + if (angle === 90 || angle === 270) { + y -= (h / 2); + } else if (angle > 270 || angle < 90) { + y -= h; + } + return y; +} +function drawPointLabels(scale, labelCount) { + const {ctx, options: {pointLabels}} = scale; + for (let i = labelCount - 1; i >= 0; i--) { + const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i)); + const plFont = toFont(optsAtIndex.font); + const {x, y, textAlign, left, top, right, bottom} = scale._pointLabelItems[i]; + const {backdropColor} = optsAtIndex; + if (!isNullOrUndef(backdropColor)) { + const padding = toPadding(optsAtIndex.backdropPadding); + ctx.fillStyle = backdropColor; + ctx.fillRect(left - padding.left, top - padding.top, right - left + padding.width, bottom - top + padding.height); + } + renderText( + ctx, + scale._pointLabels[i], + x, + y + (plFont.lineHeight / 2), + plFont, + { + color: optsAtIndex.color, + textAlign: textAlign, + textBaseline: 'middle' + } + ); + } +} +function pathRadiusLine(scale, radius, circular, labelCount) { + const {ctx} = scale; + if (circular) { + ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU); + } else { + let pointPosition = scale.getPointPosition(0, radius); + ctx.moveTo(pointPosition.x, pointPosition.y); + for (let i = 1; i < labelCount; i++) { + pointPosition = scale.getPointPosition(i, radius); + ctx.lineTo(pointPosition.x, pointPosition.y); + } + } +} +function drawRadiusLine(scale, gridLineOpts, radius, labelCount) { + const ctx = scale.ctx; + const circular = gridLineOpts.circular; + const {color, lineWidth} = gridLineOpts; + if ((!circular && !labelCount) || !color || !lineWidth || radius < 0) { + return; + } + ctx.save(); + ctx.strokeStyle = color; + ctx.lineWidth = lineWidth; + ctx.setLineDash(gridLineOpts.borderDash); + ctx.lineDashOffset = gridLineOpts.borderDashOffset; + ctx.beginPath(); + pathRadiusLine(scale, radius, circular, labelCount); + ctx.closePath(); + ctx.stroke(); + ctx.restore(); +} +function createPointLabelContext(parent, index, label) { + return createContext(parent, { + label, + index, + type: 'pointLabel' + }); +} +class RadialLinearScale extends LinearScaleBase { + constructor(cfg) { + super(cfg); + this.xCenter = undefined; + this.yCenter = undefined; + this.drawingArea = undefined; + this._pointLabels = []; + this._pointLabelItems = []; + } + setDimensions() { + const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2); + const w = this.width = this.maxWidth - padding.width; + const h = this.height = this.maxHeight - padding.height; + this.xCenter = Math.floor(this.left + w / 2 + padding.left); + this.yCenter = Math.floor(this.top + h / 2 + padding.top); + this.drawingArea = Math.floor(Math.min(w, h) / 2); + } + determineDataLimits() { + const {min, max} = this.getMinMax(false); + this.min = isNumberFinite(min) && !isNaN(min) ? min : 0; + this.max = isNumberFinite(max) && !isNaN(max) ? max : 0; + this.handleTickRangeOptions(); + } + computeTickLimit() { + return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); + } + generateTickLabels(ticks) { + LinearScaleBase.prototype.generateTickLabels.call(this, ticks); + this._pointLabels = this.getLabels() + .map((value, index) => { + const label = callback(this.options.pointLabels.callback, [value, index], this); + return label || label === 0 ? label : ''; + }) + .filter((v, i) => this.chart.getDataVisibility(i)); + } + fit() { + const opts = this.options; + if (opts.display && opts.pointLabels.display) { + fitWithPointLabels(this); + } else { + this.setCenterPoint(0, 0, 0, 0); + } + } + setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) { + this.xCenter += Math.floor((leftMovement - rightMovement) / 2); + this.yCenter += Math.floor((topMovement - bottomMovement) / 2); + this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement)); + } + getIndexAngle(index) { + const angleMultiplier = TAU / (this._pointLabels.length || 1); + const startAngle = this.options.startAngle || 0; + return _normalizeAngle(index * angleMultiplier + toRadians(startAngle)); + } + getDistanceFromCenterForValue(value) { + if (isNullOrUndef(value)) { + return NaN; + } + const scalingFactor = this.drawingArea / (this.max - this.min); + if (this.options.reverse) { + return (this.max - value) * scalingFactor; + } + return (value - this.min) * scalingFactor; + } + getValueForDistanceFromCenter(distance) { + if (isNullOrUndef(distance)) { + return NaN; + } + const scaledDistance = distance / (this.drawingArea / (this.max - this.min)); + return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance; + } + getPointLabelContext(index) { + const pointLabels = this._pointLabels || []; + if (index >= 0 && index < pointLabels.length) { + const pointLabel = pointLabels[index]; + return createPointLabelContext(this.getContext(), index, pointLabel); + } + } + getPointPosition(index, distanceFromCenter, additionalAngle = 0) { + const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle; + return { + x: Math.cos(angle) * distanceFromCenter + this.xCenter, + y: Math.sin(angle) * distanceFromCenter + this.yCenter, + angle + }; + } + getPointPositionForValue(index, value) { + return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); + } + getBasePosition(index) { + return this.getPointPositionForValue(index || 0, this.getBaseValue()); + } + getPointLabelPosition(index) { + const {left, top, right, bottom} = this._pointLabelItems[index]; + return { + left, + top, + right, + bottom, + }; + } + drawBackground() { + const {backgroundColor, grid: {circular}} = this.options; + if (backgroundColor) { + const ctx = this.ctx; + ctx.save(); + ctx.beginPath(); + pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length); + ctx.closePath(); + ctx.fillStyle = backgroundColor; + ctx.fill(); + ctx.restore(); + } + } + drawGrid() { + const ctx = this.ctx; + const opts = this.options; + const {angleLines, grid} = opts; + const labelCount = this._pointLabels.length; + let i, offset, position; + if (opts.pointLabels.display) { + drawPointLabels(this, labelCount); + } + if (grid.display) { + this.ticks.forEach((tick, index) => { + if (index !== 0) { + offset = this.getDistanceFromCenterForValue(tick.value); + const optsAtIndex = grid.setContext(this.getContext(index - 1)); + drawRadiusLine(this, optsAtIndex, offset, labelCount); + } + }); + } + if (angleLines.display) { + ctx.save(); + for (i = labelCount - 1; i >= 0; i--) { + const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i)); + const {color, lineWidth} = optsAtIndex; + if (!lineWidth || !color) { + continue; + } + ctx.lineWidth = lineWidth; + ctx.strokeStyle = color; + ctx.setLineDash(optsAtIndex.borderDash); + ctx.lineDashOffset = optsAtIndex.borderDashOffset; + offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max); + position = this.getPointPosition(i, offset); + ctx.beginPath(); + ctx.moveTo(this.xCenter, this.yCenter); + ctx.lineTo(position.x, position.y); + ctx.stroke(); + } + ctx.restore(); + } + } + drawBorder() {} + drawLabels() { + const ctx = this.ctx; + const opts = this.options; + const tickOpts = opts.ticks; + if (!tickOpts.display) { + return; + } + const startAngle = this.getIndexAngle(0); + let offset, width; + ctx.save(); + ctx.translate(this.xCenter, this.yCenter); + ctx.rotate(startAngle); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + this.ticks.forEach((tick, index) => { + if (index === 0 && !opts.reverse) { + return; + } + const optsAtIndex = tickOpts.setContext(this.getContext(index)); + const tickFont = toFont(optsAtIndex.font); + offset = this.getDistanceFromCenterForValue(this.ticks[index].value); + if (optsAtIndex.showLabelBackdrop) { + ctx.font = tickFont.string; + width = ctx.measureText(tick.label).width; + ctx.fillStyle = optsAtIndex.backdropColor; + const padding = toPadding(optsAtIndex.backdropPadding); + ctx.fillRect( + -width / 2 - padding.left, + -offset - tickFont.size / 2 - padding.top, + width + padding.width, + tickFont.size + padding.height + ); + } + renderText(ctx, tick.label, 0, -offset, tickFont, { + color: optsAtIndex.color, + }); + }); + ctx.restore(); + } + drawTitle() {} +} +RadialLinearScale.id = 'radialLinear'; +RadialLinearScale.defaults = { + display: true, + animate: true, + position: 'chartArea', + angleLines: { + display: true, + lineWidth: 1, + borderDash: [], + borderDashOffset: 0.0 + }, + grid: { + circular: false + }, + startAngle: 0, + ticks: { + showLabelBackdrop: true, + callback: Ticks.formatters.numeric + }, + pointLabels: { + backdropColor: undefined, + backdropPadding: 2, + display: true, + font: { + size: 10 + }, + callback(label) { + return label; + }, + padding: 5, + centerPointLabels: false + } +}; +RadialLinearScale.defaultRoutes = { + 'angleLines.color': 'borderColor', + 'pointLabels.color': 'color', + 'ticks.color': 'color' +}; +RadialLinearScale.descriptors = { + angleLines: { + _fallback: 'grid' + } +}; + +const INTERVALS = { + millisecond: {common: true, size: 1, steps: 1000}, + second: {common: true, size: 1000, steps: 60}, + minute: {common: true, size: 60000, steps: 60}, + hour: {common: true, size: 3600000, steps: 24}, + day: {common: true, size: 86400000, steps: 30}, + week: {common: false, size: 604800000, steps: 4}, + month: {common: true, size: 2.628e9, steps: 12}, + quarter: {common: false, size: 7.884e9, steps: 4}, + year: {common: true, size: 3.154e10} +}; +const UNITS = (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, 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 (round) { + value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) + ? adapter.startOf(value, 'isoWeek', isoWeekday) + : adapter.startOf(value, round); + } + 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, map, majorUnit) { + const adapter = scale._adapter; + const first = +adapter.startOf(ticks[0].value, majorUnit); + const last = ticks[ticks.length - 1].value; + let major, index; + for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { + index = map[major]; + if (index >= 0) { + ticks[index].major = true; + } + } + return ticks; +} +function ticksFromTimestamps(scale, values, majorUnit) { + const ticks = []; + const map = {}; + const ilen = values.length; + let i, value; + for (i = 0; i < ilen; ++i) { + value = values[i]; + map[value] = i; + ticks.push({ + value, + major: false + }); + } + return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); +} +class TimeScale extends Scale { + constructor(props) { + super(props); + this._cache = { + data: [], + labels: [], + all: [] + }; + this._unit = 'day'; + this._majorUnit = undefined; + this._offsets = {}; + this._normalized = false; + this._parseOpts = undefined; + } + init(scaleOpts, opts) { + const time = scaleOpts.time || (scaleOpts.time = {}); + const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date); + mergeIf(time.displayFormats, adapter.formats()); + this._parseOpts = { + parser: time.parser, + round: time.round, + isoWeekday: time.isoWeekday + }; + super.init(scaleOpts); + this._normalized = opts.normalized; + } + parse(raw, index) { + if (raw === undefined) { + 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' ? undefined + : determineMajorUnit(this._unit); + this.initOffsets(timestamps); + if (options.reverse) { + ticks.reverse(); + } + return ticksFromTimestamps(this, ticks, this._majorUnit); + } + 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(timeOpts.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) > 100000 * 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((a, b) => a - b).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); + } + _tickFormatFunction(time, index, ticks, format) { + const options = this.options; + 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[index]; + const major = majorUnit && majorFormat && tick && tick.major; + const label = this._adapter.format(time, format || (major ? majorFormat : minorFormat)); + const formatter = options.ticks.callback; + return formatter ? callback(formatter, [label, index, ticks], this) : label; + } + generateTickLabels(ticks) { + let i, ilen, tick; + for (i = 0, ilen = ticks.length; 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)); + } +} +TimeScale.id = 'time'; +TimeScale.defaults = { + bounds: 'data', + adapters: {}, + time: { + parser: false, + unit: false, + round: false, + isoWeekday: false, + minUnit: 'millisecond', + displayFormats: {} + }, + ticks: { + source: 'auto', + 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 = undefined; + this._tableRange = undefined; + } + 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; + } + _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); + } +} +TimeSeriesScale.id = 'timeseries'; +TimeSeriesScale.defaults = TimeScale.defaults; + +var scales = /*#__PURE__*/Object.freeze({ +__proto__: null, +CategoryScale: CategoryScale, +LinearScale: LinearScale, +LogarithmicScale: LogarithmicScale, +RadialLinearScale: RadialLinearScale, +TimeScale: TimeScale, +TimeSeriesScale: TimeSeriesScale +}); + +const registerables = [ + controllers, + elements, + plugins, + scales, +]; + +export { Animation, Animations, ArcElement, BarController, BarElement, BasePlatform, BasicPlatform, BubbleController, CategoryScale, Chart, DatasetController, plugin_decimation as Decimation, DomPlatform, DoughnutController, Element, plugin_filler as Filler, Interaction, plugin_legend as Legend, LineController, LineElement, LinearScale, LogarithmicScale, PieController, PointElement, PolarAreaController, RadarController, RadialLinearScale, Scale, ScatterController, plugin_subtitle as SubTitle, Ticks, TimeScale, TimeSeriesScale, plugin_title as Title, plugin_tooltip as Tooltip, adapters as _adapters, _detectPlatform, animator, controllers, elements, layouts, plugins, registerables, registry, scales }; diff --git a/node_modules/chart.js/dist/chart.js b/node_modules/chart.js/dist/chart.js new file mode 100644 index 00000000..58990616 --- /dev/null +++ b/node_modules/chart.js/dist/chart.js @@ -0,0 +1,13269 @@ +/*! + * Chart.js v3.7.1 + * https://www.chartjs.org + * (c) 2022 Chart.js Contributors + * Released under the MIT License + */ +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : +typeof define === 'function' && define.amd ? define(factory) : +(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory()); +})(this, (function () { 'use strict'; + +function fontString(pixelSize, fontStyle, fontFamily) { + return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; +} +const requestAnimFrame = (function() { + if (typeof window === 'undefined') { + return function(callback) { + return callback(); + }; + } + return window.requestAnimationFrame; +}()); +function throttled(fn, thisArg, updateFn) { + const updateArgs = updateFn || ((args) => Array.prototype.slice.call(args)); + let ticking = false; + let args = []; + return function(...rest) { + args = updateArgs(rest); + if (!ticking) { + ticking = true; + requestAnimFrame.call(window, () => { + ticking = false; + fn.apply(thisArg, args); + }); + } + }; +} +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; +}; + +class Animator { + constructor() { + this._request = null; + this._charts = new Map(); + this._running = false; + this._lastDate = undefined; + } + _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 draw = 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); + draw = true; + } else { + items[i] = items[items.length - 1]; + items.pop(); + } + } + if (draw) { + 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 = new Animator(); + +/*! + * @kurkle/color v0.1.9 + * https://github.com/kurkle/color#readme + * (c) 2020 Jukka Kurkela + * Released under the MIT License + */ +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 & 0xF]; +const h2 = (b) => hex[(b & 0xF0) >> 4] + hex[b & 0xF]; +const eq = (b) => (((b & 0xF0) >> 4) === (b & 0xF)); +function isShort(v) { + return 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; +} +function hexString(v) { + var f = isShort(v) ? h1 : h2; + return v + ? '#' + f(v.r) + f(v.g) + f(v.b) + (v.a < 255 ? f(v.a) : '') + : v; +} +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 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 = 255 & (m[8] ? p2b(v) : v * 255); + } + r = +m[1]; + g = +m[3]; + b = +m[5]; + r = 255 & (m[2] ? p2b(r) : r); + g = 255 & (m[4] ? p2b(g) : g); + b = 255 & (m[6] ? p2b(b) : b); + return { + r: r, + g: g, + b: b, + a: 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 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 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 = max === r + ? ((g - b) / d) + (g < b ? 6 : 0) + : max === g + ? (b - r) / d + 2 + : (r - g) / d + 4; + 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: 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$1$1 = { + 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 = { + 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); + const tkeys = Object.keys(map$1$1); + 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$1$1[k]); + } + k = parseInt(names[ok], 16); + unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF]; + } + return unpacked; +} +let names$1; +function nameParse(str) { + if (!names$1) { + names$1 = unpack(); + names$1.transparent = [0, 0, 0, 0]; + } + const a = names$1[str.toLowerCase()]; + return a && { + r: a[0], + g: a[1], + b: a[2], + a: a.length === 4 ? a[3] : 255 + }; +} +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) : this._rgb; + } + hexString() { + return this._valid ? hexString(this._rgb) : this._rgb; + } + hslString() { + return this._valid ? hslString(this._rgb) : this._rgb; + } + mix(color, weight) { + const me = this; + if (color) { + const c1 = me.rgb; + const c2 = color.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.0; + w2 = 1 - w1; + c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5; + c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5; + c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5; + c1.a = p * c1.a + (1 - p) * c2.a; + me.rgb = c1; + } + return me; + } + 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; + } +} +function index_esm(input) { + return new Color(input); +} + +const isPatternOrGradient = (value) => value instanceof CanvasGradient || value instanceof CanvasPattern; +function color(value) { + return isPatternOrGradient(value) ? value : index_esm(value); +} +function getHoverColor(value) { + return isPatternOrGradient(value) + ? value + : index_esm(value).saturate(0.5).darken(0.1).hexString(); +} + +function noop() {} +const uid = (function() { + let id = 0; + return function() { + return id++; + }; +}()); +function isNullOrUndef(value) { + return value === null || typeof value === 'undefined'; +} +function isArray(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + const type = Object.prototype.toString.call(value); + if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { + return true; + } + return false; +} +function isObject(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; +} +const isNumberFinite = (value) => (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 toPercentage = (value, dimension) => + typeof value === 'string' && value.endsWith('%') ? + parseFloat(value) / 100 + : value / dimension; +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; + if (reverse) { + for (i = len - 1; i >= 0; i--) { + fn.call(thisArg, loopable[i], i); + } + } else { + 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 = 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; + for (let i = 0; i < ilen; ++i) { + source = sources[i]; + if (!isObject(source)) { + continue; + } + const keys = Object.keys(source); + for (let k = 0, klen = keys.length; k < klen; ++k) { + merger(keys[k], target, source, 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); + } +} +function _deprecated(scope, value, previous, current) { + if (value !== undefined) { + console.warn(scope + ': "' + previous + + '" is deprecated. Please use "' + current + '" instead'); + } +} +const emptyString = ''; +const dot = '.'; +function indexOfDotOrLength(key, start) { + const idx = key.indexOf(dot, start); + return idx === -1 ? key.length : idx; +} +function resolveObjectKey(obj, key) { + if (key === emptyString) { + return obj; + } + let pos = 0; + let idx = indexOfDotOrLength(key, pos); + while (obj && idx > pos) { + obj = obj[key.substr(pos, idx - pos)]; + pos = idx + 1; + idx = indexOfDotOrLength(key, pos); + } + return 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 overrides = Object.create(null); +const descriptors = 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] = 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(_descriptors) { + this.animation = undefined; + 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 + }; + this.maintainAspectRatio = true; + this.onHover = null; + this.onClick = null; + this.parsing = true; + this.plugins = {}; + this.responsive = true; + this.scale = undefined; + this.scales = {}; + this.showLine = true; + this.drawActiveElementsOnTop = true; + this.describe(_descriptors); + } + set(scope, values) { + return set(this, scope, values); + } + get(scope) { + return getScope$1(this, scope); + } + describe(scope, values) { + return set(descriptors, scope, values); + } + override(scope, values) { + return set(overrides, scope, values); + } + route(scope, name, targetScope, targetName) { + const scopeObject = getScope$1(this, scope); + 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; + } + } + }); + } +} +var defaults = new Defaults({ + _scriptable: (name) => !name.startsWith('on'), + _indexable: (name) => name !== 'events', + hover: { + _fallback: 'interaction' + }, + interaction: { + _scriptable: false, + _indexable: false, + } +}); + +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 niceNum(range) { + const roundedRange = Math.round(range); + range = almostEquals(range, roundedRange, range / 1000) ? 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 isNumber(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} +function almostEquals(x, y, epsilon) { + return Math.abs(x - y) < epsilon; +} +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 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 _longestText(ctx, font, arrayOfThings, cache) { + cache = cache || {}; + let data = cache.data = cache.data || {}; + let gc = cache.garbageCollect = cache.garbageCollect || []; + if (cache.font !== font) { + data = cache.data = {}; + gc = cache.garbageCollect = []; + cache.font = font; + } + ctx.save(); + ctx.font = font; + let longest = 0; + const ilen = arrayOfThings.length; + let i, j, jlen, thing, nestedThing; + for (i = 0; i < ilen; i++) { + thing = arrayOfThings[i]; + if (thing !== undefined && thing !== null && isArray(thing) !== true) { + longest = _measureText(ctx, data, gc, longest, thing); + } else if (isArray(thing)) { + for (j = 0, jlen = thing.length; j < jlen; j++) { + nestedThing = thing[j]; + if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) { + longest = _measureText(ctx, data, gc, longest, nestedThing); + } + } + } + } + ctx.restore(); + const gcLen = gc.length / 2; + if (gcLen > arrayOfThings.length) { + for (i = 0; i < gcLen; i++) { + delete data[gc[i]]; + } + gc.splice(0, gcLen); + } + 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) { + 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) { + let type, xOffset, yOffset, size, cornerRadius; + 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: + ctx.arc(x, y, radius, 0, TAU); + ctx.closePath(); + break; + case 'triangle': + ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case 'rectRounded': + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + yOffset = Math.sin(rad + QUARTER_PI) * size; + ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case 'rect': + if (!rotation) { + size = Math.SQRT1_2 * radius; + ctx.rect(x - size, y - size, 2 * size, 2 * size); + break; + } + rad += QUARTER_PI; + case 'rectRot': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + yOffset, y - xOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.lineTo(x - yOffset, y + xOffset); + ctx.closePath(); + break; + case 'crossRot': + rad += QUARTER_PI; + case 'cross': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'star': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + rad += QUARTER_PI; + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'line': + xOffset = 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) * radius, y + Math.sin(rad) * radius); + 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.0; + 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 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 (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 += font.lineHeight; + } + ctx.restore(); +} +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 addRoundedRectPath(ctx, rect) { + const {x, y, w, h, radius} = rect; + ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_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); +} + +function _lookup(table, value, cmp) { + cmp = cmp || ((index) => table[index] < 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) => + _lookup(table, value, index => table[index][key] < value); +const _rlookupByKey = (table, key, value) => + _lookup(table, value, index => table[index][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 index = listeners.indexOf(listener); + if (index !== -1) { + listeners.splice(index, 1); + } + if (listeners.length > 0) { + return; + } + arrayEvents.forEach((key) => { + delete array[key]; + }); + delete array._chartjs; +} +function _arrayUnique(items) { + const set = new Set(); + let i, ilen; + for (i = 0, ilen = items.length; i < ilen; ++i) { + set.add(items[i]); + } + if (set.size === ilen) { + return items; + } + return Array.from(set); +} + +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) => window.getComputedStyle(element, null); +function getStyle(el, property) { + return getComputedStyle(el).getPropertyValue(property); +} +const positions = ['top', 'right', 'bottom', 'left']; +function getPositionedStyle(styles, style, suffix) { + const result = {}; + suffix = suffix ? '-' + suffix : ''; + for (let i = 0; i < 4; i++) { + const pos = positions[i]; + result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0; + } + 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(evt, canvas) { + const e = evt.native || evt; + 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$1(evt, chart) { + 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(evt, 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 === undefined || height === undefined) { + const container = _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 ? Math.floor(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); + } + return { + width, + height + }; +} +function retinaScale(chart, forceRatio, forceStyle) { + const pixelRatio = forceRatio || 1; + const deviceHeight = Math.floor(chart.height * pixelRatio); + const deviceWidth = Math.floor(chart.width * pixelRatio); + chart.height = deviceHeight / pixelRatio; + chart.width = deviceWidth / pixelRatio; + 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; + } + }; + 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] : undefined; +} + +function getRelativePosition(e, chart) { + if ('native' in e) { + return { + x: e.x, + y: e.y + }; + } + return getRelativePosition$1(e, chart); +} +function evaluateAllVisibleItems(chart, handler) { + const metasets = chart.getSortedVisibleDatasetMetas(); + let index, data, element; + for (let i = 0, ilen = metasets.length; i < ilen; ++i) { + ({index, data} = metasets[i]); + for (let j = 0, jlen = data.length; j < jlen; ++j) { + element = data[j]; + if (!element.skip) { + handler(element, index, j); + } + } + } +} +function binarySearch(metaset, axis, value, intersect) { + const {controller, data, _sorted} = metaset; + const iScale = controller._cachedMeta.iScale; + if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) { + const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey; + if (!intersect) { + return lookupMethod(data, axis, value); + } 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 optimizedEvaluateItems(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, 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, index, 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) { + const items = []; + if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { + return items; + } + const evaluationFunc = function(element, datasetIndex, index) { + if (element.inRange(position.x, position.y, useFinalPosition)) { + items.push({element, datasetIndex, index}); + } + }; + optimizedEvaluateItems(chart, axis, position, evaluationFunc, true); + return items; +} +function getNearestRadialItems(chart, position, axis, useFinalPosition) { + let items = []; + function evaluationFunc(element, datasetIndex, index) { + 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}); + } + } + optimizedEvaluateItems(chart, axis, position, evaluationFunc); + return items; +} +function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition) { + let items = []; + const distanceMetric = getDistanceMetricForAxis(axis); + let minDistance = Number.POSITIVE_INFINITY; + function evaluationFunc(element, datasetIndex, index) { + const inRange = element.inRange(position.x, position.y, useFinalPosition); + if (intersect && !inRange) { + return; + } + const center = element.getCenterPoint(useFinalPosition); + const pointInArea = _isPointInArea(center, chart.chartArea, chart._minPadding); + if (!pointInArea && !inRange) { + return; + } + const distance = distanceMetric(position, center); + if (distance < minDistance) { + items = [{element, datasetIndex, index}]; + minDistance = distance; + } else if (distance === minDistance) { + items.push({element, datasetIndex, index}); + } + } + optimizedEvaluateItems(chart, axis, position, evaluationFunc); + return items; +} +function getNearestItems(chart, position, axis, intersect, useFinalPosition) { + if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { + return []; + } + return axis === 'r' && !intersect + ? getNearestRadialItems(chart, position, axis, useFinalPosition) + : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition); +} +function getAxisItems(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const items = []; + const axis = options.axis; + const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange'; + let intersectsItem = false; + evaluateAllVisibleItems(chart, (element, datasetIndex, index) => { + if (element[rangeMethod](position[axis], useFinalPosition)) { + items.push({element, datasetIndex, index}); + } + if (element.inRange(position.x, position.y, useFinalPosition)) { + intersectsItem = true; + } + }); + if (options.intersect && !intersectsItem) { + return []; + } + return items; +} +var Interaction = { + modes: { + index(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'x'; + const items = options.intersect + ? getIntersectItems(chart, position, axis, useFinalPosition) + : getNearestItems(chart, position, axis, false, useFinalPosition); + const elements = []; + if (!items.length) { + return []; + } + chart.getSortedVisibleDatasetMetas().forEach((meta) => { + const index = items[0].index; + const element = meta.data[index]; + if (element && !element.skip) { + elements.push({element, datasetIndex: meta.index, index}); + } + }); + return elements; + }, + dataset(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'xy'; + let items = options.intersect + ? getIntersectItems(chart, position, axis, useFinalPosition) : + getNearestItems(chart, position, axis, false, useFinalPosition); + 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'; + return getIntersectItems(chart, position, axis, useFinalPosition); + }, + nearest(chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'xy'; + return getNearestItems(chart, position, axis, options.intersect, useFinalPosition); + }, + x(chart, e, options, useFinalPosition) { + return getAxisItems(chart, e, {axis: 'x', intersect: options.intersect}, useFinalPosition); + }, + y(chart, e, options, useFinalPosition) { + return getAxisItems(chart, e, {axis: 'y', intersect: options.intersect}, useFinalPosition); + } + } +}; + +const LINE_HEIGHT = new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); +const FONT_STYLE = new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/); +function toLineHeight(value, size) { + const matches = ('' + value).match(LINE_HEIGHT); + if (!matches || 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 = ''; + } + 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, index, info) { + let cacheable = true; + let i, ilen, value; + for (i = 0, ilen = inputs.length; i < ilen; ++i) { + value = inputs[i]; + if (value === undefined) { + continue; + } + if (context !== undefined && typeof value === 'function') { + value = value(context); + cacheable = false; + } + if (index !== undefined && isArray(value)) { + value = value[index % value.length]; + cacheable = false; + } + if (value !== undefined) { + if (info && !cacheable) { + info.cacheable = false; + } + 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); +} + +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(layouts) { + const stacks = {}; + for (const wrap of layouts) { + 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(layouts, params) { + const stacks = buildStacks(layouts); + const {vBoxMaxWidth, hBoxMaxHeight} = params; + let i, ilen, layout; + for (i = 0, ilen = layouts.length; i < ilen; ++i) { + layout = layouts[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(positions) { + const margin = {left: 0, top: 0, right: 0, bottom: 0}; + positions.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] || {count: 1, 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; +} +defaults.set('layout', { + autoPadding: true, + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } +}); +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 index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; + if (index !== -1) { + chart.boxes.splice(index, 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}); + }); + } +}; + +function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback, getTarget = () => scopes[0]) { + if (!defined(fallback)) { + fallback = _resolve('_fallback', scopes); + } + const cache = { + [Symbol.toStringTag]: 'Object', + _cacheable: true, + _scopes: scopes, + _rootScopes: rootScopes, + _fallback: fallback, + _getTarget: getTarget, + override: (scope) => _createResolver([scope, ...scopes], prefixes, rootScopes, fallback), + }; + return new Proxy(cache, { + deleteProperty(target, prop) { + delete target[prop]; + delete target._keys; + delete scopes[0][prop]; + return true; + }, + get(target, prop) { + return _cached(target, prop, + () => _resolveWithPrefixes(prop, prefixes, scopes, target)); + }, + getOwnPropertyDescriptor(target, prop) { + return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop); + }, + getPrototypeOf() { + return Reflect.getPrototypeOf(scopes[0]); + }, + has(target, prop) { + return getKeysFromAllScopes(target).includes(prop); + }, + ownKeys(target) { + return getKeysFromAllScopes(target); + }, + 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: new Set(), + _descriptors: _descriptors(proxy, descriptorDefaults), + setContext: (ctx) => _attachContext(proxy, ctx, subProxy, descriptorDefaults), + override: (scope) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults) + }; + return new Proxy(cache, { + deleteProperty(target, prop) { + delete target[prop]; + delete proxy[prop]; + return true; + }, + get(target, prop, receiver) { + return _cached(target, prop, + () => _resolveWithContext(target, prop, receiver)); + }, + getOwnPropertyDescriptor(target, prop) { + return target._descriptors.allKeys + ? Reflect.has(proxy, prop) ? {enumerable: true, configurable: true} : undefined + : Reflect.getOwnPropertyDescriptor(proxy, prop); + }, + getPrototypeOf() { + return Reflect.getPrototypeOf(proxy); + }, + has(target, prop) { + return Reflect.has(proxy, prop); + }, + ownKeys() { + return Reflect.ownKeys(proxy); + }, + set(target, prop, value) { + proxy[prop] = value; + delete target[prop]; + return true; + } + }); +} +function _descriptors(proxy, defaults = {scriptable: true, indexable: true}) { + const {_scriptable = defaults.scriptable, _indexable = defaults.indexable, _allKeys = defaults.allKeys} = proxy; + return { + allKeys: _allKeys, + scriptable: _scriptable, + indexable: _indexable, + isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable, + isIndexable: isFunction(_indexable) ? _indexable : () => _indexable + }; +} +const readKey = (prefix, name) => prefix ? prefix + _capitalize(name) : name; +const needsSubResolver = (prop, value) => isObject(value) && prop !== 'adapters' && + (Object.getPrototypeOf(value) === null || value.constructor === Object); +function _cached(target, prop, resolve) { + if (Object.prototype.hasOwnProperty.call(target, prop)) { + return target[prop]; + } + const value = resolve(); + target[prop] = value; + return value; +} +function _resolveWithContext(target, prop, receiver) { + const {_proxy, _context, _subProxy, _descriptors: descriptors} = target; + let value = _proxy[prop]; + if (isFunction(value) && descriptors.isScriptable(prop)) { + value = _resolveScriptable(prop, value, target, receiver); + } + if (isArray(value) && value.length) { + value = _resolveArray(prop, value, target, descriptors.isIndexable); + } + if (needsSubResolver(prop, value)) { + value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors); + } + return value; +} +function _resolveScriptable(prop, value, 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); + value = value(_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: descriptors} = target; + if (defined(_context.index) && isIndexable(prop)) { + value = 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], descriptors)); + } + } + 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) : undefined; +function addScopes(set, parentScopes, key, parentFallback, value) { + for (const parent of parentScopes) { + const scope = getScope(key, parent); + if (scope) { + set.add(scope); + const fallback = resolveFallback(scope._fallback, key, value); + if (defined(fallback) && fallback !== key && fallback !== parentFallback) { + return fallback; + } + } else if (scope === false && defined(parentFallback) && 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 set = new Set(); + set.add(value); + let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value); + if (key === null) { + return false; + } + if (defined(fallback) && fallback !== prop) { + key = addScopesFromKey(set, allScopes, fallback, key, value); + if (key === null) { + return false; + } + } + return _createResolver(Array.from(set), [''], rootScopes, fallback, + () => subGetTarget(resolver, prop, value)); +} +function addScopesFromKey(set, allScopes, key, fallback, item) { + while (key) { + key = addScopes(set, 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 (defined(value)) { + return needsSubResolver(prop, value) + ? createSubResolver(scopes, proxy, prop, value) + : value; + } + } +} +function _resolve(key, scopes) { + for (const scope of scopes) { + if (!scope) { + continue; + } + const value = scope[key]; + if (defined(value)) { + return value; + } + } +} +function getKeysFromAllScopes(target) { + let keys = target._keys; + if (!keys) { + keys = target._keys = resolveKeysFromAllScopes(target._scopes); + } + return keys; +} +function resolveKeysFromAllScopes(scopes) { + const set = new Set(); + for (const scope of scopes) { + for (const key of Object.keys(scope).filter(k => !k.startsWith('_'))) { + set.add(key); + } + } + return Array.from(set); +} + +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); + } +} + +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 _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 intlCache = 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 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 !== undefined) { + 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) { + return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle); +} + +var helpers = /*#__PURE__*/Object.freeze({ +__proto__: null, +easingEffects: effects, +color: color, +getHoverColor: getHoverColor, +noop: noop, +uid: uid, +isNullOrUndef: isNullOrUndef, +isArray: isArray, +isObject: isObject, +isFinite: isNumberFinite, +finiteOrDefault: finiteOrDefault, +valueOrDefault: valueOrDefault, +toPercentage: toPercentage, +toDimension: toDimension, +callback: callback, +each: each, +_elementsEqual: _elementsEqual, +clone: clone, +_merger: _merger, +merge: merge, +mergeIf: mergeIf, +_mergerIf: _mergerIf, +_deprecated: _deprecated, +resolveObjectKey: resolveObjectKey, +_capitalize: _capitalize, +defined: defined, +isFunction: isFunction, +setsEqual: setsEqual, +_isClickEvent: _isClickEvent, +toFontString: toFontString, +_measureText: _measureText, +_longestText: _longestText, +_alignPixel: _alignPixel, +clearCanvas: clearCanvas, +drawPoint: drawPoint, +_isPointInArea: _isPointInArea, +clipArea: clipArea, +unclipArea: unclipArea, +_steppedLineTo: _steppedLineTo, +_bezierCurveTo: _bezierCurveTo, +renderText: renderText, +addRoundedRectPath: addRoundedRectPath, +_lookup: _lookup, +_lookupByKey: _lookupByKey, +_rlookupByKey: _rlookupByKey, +_filterBetween: _filterBetween, +listenArrayEvents: listenArrayEvents, +unlistenArrayEvents: unlistenArrayEvents, +_arrayUnique: _arrayUnique, +_createResolver: _createResolver, +_attachContext: _attachContext, +_descriptors: _descriptors, +splineCurve: splineCurve, +splineCurveMonotone: splineCurveMonotone, +_updateBezierControlPoints: _updateBezierControlPoints, +_isDomSupported: _isDomSupported, +_getParentNode: _getParentNode, +getStyle: getStyle, +getRelativePosition: getRelativePosition$1, +getMaximumSize: getMaximumSize, +retinaScale: retinaScale, +supportsEventListenerOptions: supportsEventListenerOptions, +readUsedSize: readUsedSize, +fontString: fontString, +requestAnimFrame: requestAnimFrame, +throttled: throttled, +debounce: debounce, +_toLeftRightCenter: _toLeftRightCenter, +_alignStartEnd: _alignStartEnd, +_textX: _textX, +_pointInLine: _pointInLine, +_steppedInterpolation: _steppedInterpolation, +_bezierInterpolation: _bezierInterpolation, +formatNumber: formatNumber, +toLineHeight: toLineHeight, +_readValueToProps: _readValueToProps, +toTRBL: toTRBL, +toTRBLCorners: toTRBLCorners, +toPadding: toPadding, +toFont: toFont, +resolve: resolve, +_addGrace: _addGrace, +createContext: createContext, +PI: PI, +TAU: TAU, +PITAU: PITAU, +INFINITY: INFINITY, +RAD_PER_DEG: RAD_PER_DEG, +HALF_PI: HALF_PI, +QUARTER_PI: QUARTER_PI, +TWO_THIRDS_PI: TWO_THIRDS_PI, +log10: log10, +sign: sign, +niceNum: niceNum, +_factorize: _factorize, +isNumber: isNumber, +almostEquals: almostEquals, +almostWhole: almostWhole, +_setMinAndMaxByKey: _setMinAndMaxByKey, +toRadians: toRadians, +toDegrees: toDegrees, +_decimalPlaces: _decimalPlaces, +getAngleFromPoint: getAngleFromPoint, +distanceBetweenPoints: distanceBetweenPoints, +_angleDiff: _angleDiff, +_normalizeAngle: _normalizeAngle, +_angleBetween: _angleBetween, +_limitValue: _limitValue, +_int16Range: _int16Range, +_isBetween: _isBetween, +getRtlAdapter: getRtlAdapter, +overrideTextDirection: overrideTextDirection, +restoreTextDirection: restoreTextDirection, +_boundSegment: _boundSegment, +_boundSegments: _boundSegments, +_computeSegments: _computeSegments +}); + +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 !== undefined) { + canvas.width = displayWidth; + } + } + if (isNullOrEmpty(renderHeight)) { + if (canvas.style.height === '') { + canvas.height = canvas.width / (aspectRatio || 2); + } else { + const displayHeight = readUsedSize(canvas, 'height'); + if (displayHeight !== undefined) { + canvas.height = displayHeight; + } + } + } + return canvas; +} +const eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; +function addListener(node, type, listener) { + node.addEventListener(type, listener, eventListenerOptions); +} +function removeListener(chart, type, listener) { + chart.canvas.removeEventListener(type, listener, eventListenerOptions); +} +function fromNativeEvent(event, chart) { + const type = EVENT_TYPES[event.type] || event.type; + const {x, y} = getRelativePosition$1(event, chart); + return { + type, + chart, + native: event, + x: x !== undefined ? x : null, + y: y !== undefined ? 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 = 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, (args) => { + const event = args[0]; + return [event, event.offsetX, event.offsetY]; + }); + 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] = undefined; + } + getDevicePixelRatio() { + return window.devicePixelRatio; + } + getMaximumSize(canvas, width, height, aspectRatio) { + return getMaximumSize(canvas, width, height, aspectRatio); + } + isAttached(canvas) { + const container = _getParentNode(canvas); + return !!(container && container.isConnected); + } +} + +function _detectPlatform(canvas) { + if (!_isDomSupported() || (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas)) { + return BasicPlatform; + } + return DomPlatform; +} + +var platforms = /*#__PURE__*/Object.freeze({ +__proto__: null, +_detectPlatform: _detectPlatform, +BasePlatform: BasePlatform, +BasicPlatform: BasicPlatform, +DomPlatform: DomPlatform +}); + +const transparent = 'transparent'; +const interpolators = { + boolean(from, to, factor) { + return factor > 0.5 ? to : from; + }, + color(from, to, factor) { + const c0 = color(from || transparent); + const c1 = c0.valid && color(to || transparent); + return c1 && c1.valid + ? c1.mix(c0, factor).hexString() + : to; + }, + number(from, to, factor) { + return from + (to - from) * factor; + } +}; +class Animation { + constructor(cfg, target, prop, to) { + const currentValue = target[prop]; + to = resolve([cfg.to, to, currentValue, cfg.from]); + const from = resolve([cfg.from, currentValue, to]); + this._active = true; + this._fn = cfg.fn || interpolators[cfg.type || typeof from]; + this._easing = effects[cfg.easing] || effects.linear; + this._start = Math.floor(Date.now() + (cfg.delay || 0)); + this._duration = this._total = Math.floor(cfg.duration); + this._loop = !!cfg.loop; + this._target = target; + this._prop = prop; + this._from = from; + this._to = to; + this._promises = undefined; + } + active() { + return this._active; + } + update(cfg, to, 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, to, currentValue, cfg.from]); + this._from = resolve([cfg.from, currentValue, to]); + } + } + 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 from = this._from; + const loop = this._loop; + const to = this._to; + let factor; + this._active = from !== to && (loop || (elapsed < duration)); + if (!this._active) { + this._target[prop] = to; + this._notify(true); + return; + } + if (elapsed < 0) { + this._target[prop] = from; + 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(from, to, 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](); + } + } +} + +const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension']; +const colors = ['color', 'borderColor', 'backgroundColor']; +defaults.set('animation', { + delay: undefined, + duration: 1000, + easing: 'easeOutQuart', + fn: undefined, + from: undefined, + loop: undefined, + to: undefined, + type: undefined, +}); +const animationOptions = Object.keys(defaults.animation); +defaults.describe('animation', { + _fallback: false, + _indexable: false, + _scriptable: (name) => name !== 'onProgress' && name !== 'onComplete' && name !== 'fn', +}); +defaults.set('animations', { + colors: { + type: 'color', + properties: colors + }, + numbers: { + type: 'number', + properties: numbers + }, +}); +defaults.describe('animations', { + _fallback: 'animation', +}); +defaults.set('transitions', { + active: { + animation: { + duration: 400 + } + }, + resize: { + animation: { + duration: 0 + } + }, + show: { + animations: { + colors: { + from: 'transparent' + }, + visible: { + type: 'boolean', + duration: 0 + }, + } + }, + hide: { + animations: { + colors: { + to: 'transparent' + }, + visible: { + type: 'boolean', + easing: 'linear', + fn: v => v | 0 + }, + } + } +}); +class Animations { + constructor(chart, config) { + this._chart = chart; + this._properties = new Map(); + this.configure(config); + } + configure(config) { + if (!isObject(config)) { + return; + } + const animatedProps = this._properties; + Object.getOwnPropertyNames(config).forEach(key => { + const cfg = config[key]; + if (!isObject(cfg)) { + return; + } + const resolved = {}; + for (const option of animationOptions) { + resolved[option] = cfg[option]; + } + (isArray(cfg.properties) && cfg.properties || [key]).forEach((prop) => { + 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 === undefined ? allowedOverflow : 0; + const max = opts.max === undefined ? 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; + } + for (i = 0, ilen = keys.length; i < ilen; ++i) { + datasetIndex = +keys[i]; + if (datasetIndex === dsIndex) { + if (options.all) { + continue; + } + break; + } + otherValue = stack.values[datasetIndex]; + if (isNumberFinite(otherValue) && (singleMode || (value === 0 || sign(value) === sign(otherValue)))) { + value += otherValue; + } + } + return value; +} +function convertObjectDataToArray(data) { + 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] = { + x: key, + y: data[key] + }; + } + return adata; +} +function isStacked(scale, meta) { + const stacked = scale && scale.options.stacked; + return stacked || (stacked === undefined && meta.stack !== undefined); +} +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]: index, [vAxis]: value} = item; + const itemStacks = item._stacks || (item._stacks = {}); + stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index); + stack[datasetIndex] = value; + stack._top = getLastIndexInStack(stack, vScale, true, meta.type); + stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type); + } +} +function getFirstScaleId(chart, axis) { + const scales = chart.scales; + return Object.keys(scales).filter(key => scales[key].axis === axis).shift(); +} +function createDatasetContext(parent, index) { + return createContext(parent, + { + active: false, + dataset: undefined, + datasetIndex: index, + index, + mode: 'default', + type: 'dataset' + } + ); +} +function createDataContext(parent, index, element) { + return createContext(parent, { + active: false, + dataIndex: index, + parsed: undefined, + raw: undefined, + element, + index, + 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] === undefined || stacks[axis][datasetIndex] === undefined) { + return; + } + delete stacks[axis][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 = undefined; + this._parsing = false; + this._data = undefined; + this._objectData = undefined; + this._sharedOptions = undefined; + this._drawStart = undefined; + this._drawCount = undefined; + this.enableOptionSharing = false; + this.$context = undefined; + this._syncList = []; + this.initialize(); + } + initialize() { + const meta = this._cachedMeta; + this.configure(); + this.linkScales(); + meta._stacked = isStacked(meta.vScale, meta); + this.addElements(); + } + 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)) { + this._data = convertObjectDataToArray(data); + } 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); + } + } + 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, index; + for (i = 0, ilen = count; i < ilen; ++i) { + index = i + start; + parsed[i] = { + [iAxis]: singleScale || iScale.parse(labels[index], index), + [vAxis]: vScale.parse(data[index], index) + }; + } + return parsed; + } + parseArrayData(meta, data, start, count) { + const {xScale, yScale} = meta; + const parsed = new Array(count); + let i, ilen, index, item; + for (i = 0, ilen = count; i < ilen; ++i) { + index = i + start; + item = data[index]; + parsed[i] = { + x: xScale.parse(item[0], index), + y: yScale.parse(item[1], index) + }; + } + 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, index, item; + for (i = 0, ilen = count; i < ilen; ++i) { + index = i + start; + item = data[index]; + parsed[i] = { + x: xScale.parse(resolveObjectKey(item, xAxisKey), index), + y: yScale.parse(resolveObjectKey(item, yAxisKey), index) + }; + } + return parsed; + } + getParsed(index) { + return this._cachedMeta._parsed[index]; + } + getDataElement(index) { + return this._cachedMeta.data[index]; + } + 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] + }; + 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(index) { + const meta = this._cachedMeta; + const iScale = meta.iScale; + const vScale = meta.vScale; + const parsed = this.getParsed(index); + 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(index, active) { + const mode = active ? 'active' : 'default'; + return index === undefined && this._cachedMeta.dataset + ? this.resolveDatasetElementOptions(mode) + : this.resolveDataElementOptions(index || 0, mode); + } + getContext(index, active, mode) { + const dataset = this.getDataset(); + let context; + if (index >= 0 && index < this._cachedMeta.data.length) { + const element = this._cachedMeta.data[index]; + context = element.$context || + (element.$context = createDataContext(this.getContext(), index, element)); + context.parsed = this.getParsed(index); + context.raw = dataset.data[index]; + context.index = context.dataIndex = index; + } 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(index, mode) { + return this._resolveElementOptions(this.dataElementType.id, mode, index); + } + _resolveElementOptions(elementType, mode = 'default', index) { + const active = mode === 'active'; + const cache = this._cachedDataOpts; + const cacheKey = elementType + '-' + mode; + const cached = cache[cacheKey]; + const sharing = this.enableOptionSharing && defined(index); + 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 names = Object.keys(defaults.elements[elementType]); + const context = () => this.getContext(index, active); + const values = config.resolveNamedOptions(scopes, names, context, prefixes); + if (values.$shared) { + values.$shared = sharing; + cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing)); + } + return values; + } + _resolveAnimations(index, 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(index, 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; + } + updateElement(element, index, properties, mode) { + if (isDirectUpdateMode(mode)) { + Object.assign(element, properties); + } else { + this._resolveAnimations(index, mode).update(element, properties); + } + } + updateSharedOptions(sharedOptions, mode, newOptions) { + if (sharedOptions && !isDirectUpdateMode(mode)) { + this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions); + } + } + _setStyle(element, index, mode, active) { + element.active = active; + const options = this.getStyle(index, active); + this._resolveAnimations(index, mode, active).update(element, { + options: (!active && this.getSharedOptions(options)) || options + }); + } + removeHoverStyle(element, datasetIndex, index) { + this._setStyle(element, index, 'active', false); + } + setHoverStyle(element, datasetIndex, index) { + this._setStyle(element, index, 'active', true); + } + _removeDatasetHoverStyle() { + const element = this._cachedMeta.dataset; + if (element) { + this._setStyle(element, undefined, 'active', false); + } + } + _setDatasetHoverStyle() { + const element = this._cachedMeta.dataset; + if (element) { + this._setStyle(element, undefined, '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]); + } +} +DatasetController.defaults = {}; +DatasetController.prototype.datasetElementType = null; +DatasetController.prototype.dataElementType = null; + +class Element { + constructor() { + this.x = undefined; + this.y = undefined; + this.active = false; + this.options = undefined; + this.$animations = undefined; + } + 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; + } +} +Element.defaults = {}; +Element.defaultRoutes = undefined; + +const formatters = { + values(value) { + return isArray(value) ? value : '' + value; + }, + numeric(tickValue, index, 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 > 1e+15) { + notation = 'scientific'; + } + delta = calculateDelta(tickValue, ticks); + } + const logDelta = log10(Math.abs(delta)); + const numDecimal = 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); + }, + logarithmic(tickValue, index, ticks) { + if (tickValue === 0) { + return '0'; + } + const remain = tickValue / (Math.pow(10, Math.floor(log10(tickValue)))); + if (remain === 1 || remain === 2 || remain === 5) { + return formatters.numeric.call(this, tickValue, index, ticks); + } + return ''; + } +}; +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}; + +defaults.set('scale', { + display: true, + offset: false, + reverse: false, + beginAtZero: false, + bounds: 'ticks', + grace: 0, + grid: { + display: true, + lineWidth: 1, + drawBorder: true, + drawOnChartArea: true, + drawTicks: true, + tickLength: 8, + tickWidth: (_ctx, options) => options.lineWidth, + tickColor: (_ctx, options) => options.color, + offset: false, + borderDash: [], + borderDashOffset: 0.0, + borderWidth: 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, + } +}); +defaults.route('scale.ticks', 'color', '', 'color'); +defaults.route('scale.grid', 'color', '', 'borderColor'); +defaults.route('scale.grid', 'borderColor', '', 'borderColor'); +defaults.route('scale.title', 'color', '', 'color'); +defaults.describe('scale', { + _fallback: false, + _scriptable: (name) => !name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser', + _indexable: (name) => name !== 'borderDash' && name !== 'tickBorderDash', +}); +defaults.describe('scales', { + _fallback: 'scale', +}); +defaults.describe('scale.ticks', { + _scriptable: (name) => name !== 'backdropPadding' && name !== 'callback', + _indexable: (name) => name !== 'backdropPadding', +}); + +function autoSkip(scale, ticks) { + const tickOpts = scale.options.ticks; + const ticksLimit = tickOpts.maxTicksLimit || determineMaxTicks(scale); + 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; +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, index, offsetGridLines) { + const length = scale.ticks.length; + const validIndex = Math.min(index, length - 1); + const start = scale._startPixel; + const end = scale._endPixel; + const epsilon = 1e-6; + let lineValue = scale.getPixelForTick(validIndex); + let offset; + if (offsetGridLines) { + if (length === 1) { + offset = Math.max(lineValue - start, end - lineValue); + } else if (index === 0) { + offset = (scale.getPixelForTick(1) - lineValue) / 2; + } else { + offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; + } + lineValue += validIndex < index ? 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, index, tick) { + return createContext(parent, { + tick, + index, + 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 = undefined; + this.ctx = cfg.ctx; + this.chart = cfg.chart; + this.top = undefined; + this.bottom = undefined; + this.left = undefined; + this.right = undefined; + this.width = undefined; + this.height = undefined; + this._margins = { + left: 0, + right: 0, + top: 0, + bottom: 0 + }; + this.maxWidth = undefined; + this.maxHeight = undefined; + this.paddingTop = undefined; + this.paddingBottom = undefined; + this.paddingLeft = undefined; + this.paddingRight = undefined; + this.axis = undefined; + this.labelRotation = undefined; + this.min = undefined; + this.max = undefined; + this._range = undefined; + this.ticks = []; + this._gridLineItems = null; + this._labelItems = null; + this._labelSizes = null; + this._length = 0; + this._maxLength = 0; + this._longestTextCache = {}; + this._startPixel = undefined; + this._endPixel = undefined; + this._reversePixels = false; + this._userMax = undefined; + this._userMin = undefined; + this._suggestedMax = undefined; + this._suggestedMin = undefined; + this._ticksLength = 0; + this._borderValue = 0; + this._cache = {}; + this._dataLimitsCached = false; + this.$context = undefined; + } + init(options) { + this.options = options.setContext(this.getContext()); + this.axis = options.axis; + this._userMin = this.parse(options.min); + this._userMax = this.parse(options.max); + this._suggestedMin = this.parse(options.suggestedMin); + this._suggestedMax = this.parse(options.suggestedMax); + } + parse(raw, index) { + return raw; + } + getUserBounds() { + let {_userMin, _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 || []; + } + 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; + } + 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 = this.ticks.length; + 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]); + } + 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 { + 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); + } + return labelSizes; + } + _computeLabelSizes(ticks, length) { + const {ctx, _longestTextCache: caches} = this; + const widths = []; + const heights = []; + let widestLabelSize = 0; + let highestLabelSize = 0; + let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel; + for (i = 0; i < length; ++i) { + 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, index) { + return NaN; + } + getValueForPixel(pixel) {} + getPixelForTick(index) { + const ticks = this.ticks; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index].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(index) { + const ticks = this.ticks || []; + if (index >= 0 && index < ticks.length) { + const tick = ticks[index]; + return tick.$context || + (tick.$context = createTickContext(this.getContext(), index, 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} = 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 = grid.setContext(this.getContext()); + const axisWidth = borderOpts.drawBorder ? borderOpts.borderWidth : 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 optsAtIndex = grid.setContext(this.getContext(i)); + const lineWidth = optsAtIndex.lineWidth; + const lineColor = optsAtIndex.color; + const borderDash = grid.borderDash || []; + const borderDashOffset = optsAtIndex.borderDashOffset; + const tickWidth = optsAtIndex.tickWidth; + const tickColor = optsAtIndex.tickColor; + const tickBorderDash = optsAtIndex.tickBorderDash || []; + const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset; + lineValue = getPixelForGridLine(this, i, offset); + if (lineValue === undefined) { + 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 color = optsAtIndex.color; + const strokeColor = optsAtIndex.textStrokeColor; + const strokeWidth = optsAtIndex.textStrokeWidth; + if (isHorizontal) { + x = pixel; + 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; + } + } 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 = y + textOffset - labelPadding.top; + let left = x - 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; + } + backdrop = { + left, + top, + width: width + labelPadding.width, + height: height + labelPadding.height, + color: optsAtIndex.backdropColor, + }; + } + items.push({ + rotation, + label, + font, + color, + strokeColor, + strokeWidth, + textOffset, + textAlign, + 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'; + } + 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 index = ticks.findIndex(t => t.value === value); + if (index >= 0) { + const opts = grid.setContext(this.getContext(index)); + 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: {grid}} = this; + const borderOpts = grid.setContext(this.getContext()); + const axisWidth = grid.drawBorder ? borderOpts.borderWidth : 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.borderWidth; + ctx.strokeStyle = borderOpts.borderColor; + 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._labelItems || (this._labelItems = this._computeLabelItems(chartArea)); + let i, ilen; + for (i = 0, ilen = items.length; i < ilen; ++i) { + const item = items[i]; + const tickFont = item.font; + const label = item.label; + if (item.backdrop) { + ctx.fillStyle = item.backdrop.color; + ctx.fillRect(item.backdrop.left, item.backdrop.top, item.backdrop.width, item.backdrop.height); + } + let y = item.textOffset; + renderText(ctx, label, 0, y, tickFont, item); + } + 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); + 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: gz + 1, + 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(index) { + const opts = this.options.ticks.setContext(this.getContext(index)); + 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 = 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(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, registry, component) { + const camelMethod = _capitalize(method); + callback(component['before' + camelMethod], [], component); + registry[method](component); + callback(component['after' + camelMethod], [], component); + } + _getRegistryForType(type) { + for (let i = 0; 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 === undefined) { + throw new Error('"' + id + '" is not a registered ' + type + '.'); + } + return item; + } +} +var registry = new Registry(); + +class PluginService { + constructor() { + this._init = []; + } + notify(chart, hook, args, filter) { + if (hook === 'beforeInit') { + this._init = this._createDescriptors(chart, true); + this._notify(this._init, chart, 'install'); + } + const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart); + const result = this._notify(descriptors, chart, hook, args); + if (hook === 'afterDestroy') { + this._notify(descriptors, chart, 'stop'); + this._notify(this._init, chart, 'uninstall'); + } + return result; + } + _notify(descriptors, chart, hook, args) { + args = args || {}; + for (const descriptor of descriptors) { + 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 = undefined; + } + } + _descriptors(chart) { + if (this._cache) { + return this._cache; + } + const descriptors = this._cache = this._createDescriptors(chart); + this._notifyStateChanges(chart); + return descriptors; + } + _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 descriptors = this._cache; + const diff = (a, b) => a.filter(x => !b.some(y => x.plugin.id === y.plugin.id)); + this._notify(diff(previousDescriptors, descriptors), chart, 'stop'); + this._notify(diff(descriptors, previousDescriptors), chart, 'start'); + } +} +function allPlugins(config) { + 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); + } + } + return plugins; +} +function getOpts(options, all) { + if (!all && options === false) { + return null; + } + if (options === true) { + return {}; + } + return options; +} +function createDescriptors(chart, plugins, options, all) { + const result = []; + const context = chart.getContext(); + for (let i = 0; i < plugins.length; i++) { + const plugin = plugins[i]; + const id = plugin.id; + const opts = getOpts(options[id], all); + if (opts === null) { + continue; + } + result.push({ + plugin, + options: pluginOpts(chart.config, plugin, opts, context) + }); + } + return result; +} +function pluginOpts(config, plugin, opts, context) { + const keys = config.pluginScopeKeys(plugin); + const scopes = config.getOptionScopes(opts, keys); + 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 axisFromPosition(position) { + if (position === 'top' || position === 'bottom') { + return 'x'; + } + if (position === 'left' || position === 'right') { + return 'y'; + } +} +function determineAxis(id, scaleOptions) { + if (id === 'x' || id === 'y') { + return id; + } + return scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.charAt(0).toLowerCase(); +} +function mergeScaleConfig(config, options) { + const chartDefaults = overrides[config.type] || {scales: {}}; + const configScales = options.scales || {}; + const chartIndexAxis = getIndexAxis(config.type, options); + const firstIDs = Object.create(null); + const scales = 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); + const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis); + const defaultScaleOptions = chartDefaults.scales || {}; + firstIDs[axis] = firstIDs[axis] || id; + scales[id] = mergeIf(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'] || firstIDs[axis] || axis; + scales[id] = scales[id] || 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 = new Map(); +const keysCached = 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 = (set, obj, key) => { + const opts = resolveObjectKey(obj, key); + if (opts !== undefined) { + set.add(opts); + } +}; +class Config { + constructor(config) { + this._config = initConfig(config); + this._scopeCache = new Map(); + this._resolverCache = new Map(); + } + get platform() { + return this._config.platform; + } + get type() { + return this._config.type; + } + set type(type) { + this._config.type = type; + } + get data() { + return this._config.data; + } + set data(data) { + this._config.data = initData(data); + } + get options() { + return this._config.options; + } + set options(options) { + this._config.options = options; + } + get plugins() { + return this._config.plugins; + } + update() { + const config = this._config; + this.clearCache(); + initOptions(config); + } + clearCache() { + this._scopeCache.clear(); + this._resolverCache.clear(); + } + datasetScopeKeys(datasetType) { + return cachedKeys(datasetType, + () => [[ + `datasets.${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 = 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 = 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(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, names, context, prefixes = ['']) { + const result = {$shared: true}; + const {resolver, subPrefixes} = getResolver(this._resolverCache, scopes, prefixes); + let options = resolver; + if (needContext(resolver, names)) { + result.$shared = false; + context = isFunction(context) ? context() : context; + const subResolver = this.createResolver(scopes, context, subPrefixes); + options = _attachContext(resolver, context, subResolver); + } + for (const prop of names) { + result[prop] = options[prop]; + } + return result; + } + createResolver(scopes, context, prefixes = [''], descriptorDefaults) { + const {resolver} = getResolver(this._resolverCache, scopes, prefixes); + return isObject(context) + ? _attachContext(resolver, context, undefined, descriptorDefaults) + : resolver; + } +} +function getResolver(resolverCache, scopes, prefixes) { + let cache = resolverCache.get(scopes); + if (!cache) { + cache = 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).reduce((acc, key) => acc || isFunction(value[key]), false); +function needContext(proxy, names) { + const {isScriptable, isIndexable} = _descriptors(proxy); + for (const prop of names) { + 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 = "3.7.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 { + 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 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 = undefined; + this.boxes = []; + this.currentDevicePixelRatio = undefined; + this.chartArea = undefined; + this._active = []; + this._lastEvent = undefined; + this._listeners = {}; + this._responsiveListeners = undefined; + this._sortedMetasets = []; + this.scales = {}; + this._plugins = new PluginService(); + this.$proxies = {}; + this._hiddenIndices = {}; + this.attached = false; + this._animationsDisabled = undefined; + this.$context = undefined; + 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; + } + _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 === undefined || 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, index) => { + if (datasets.filter(x => x === meta._dataset).length === 0) { + this._destroyDatasetMeta(index); + } + }); + } + 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.prototype, { + 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, index) => { + item._idx = index; + }); + 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(index, mode) { + const meta = this.getDatasetMeta(index); + const args = {meta, index, 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._resize(width, height); + this._resizeBeforeDraw = null; + } + 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 clip = meta._clip; + const useClip = !clip.disabled; + const area = this.chartArea; + const args = { + meta, + index: meta.index, + cancelable: true + }; + if (this.notifyPlugins('beforeDatasetDraw', args) === false) { + return; + } + if (useClip) { + clipArea(ctx, { + left: clip.left === false ? 0 : area.left - clip.left, + right: clip.right === false ? this.width : area.right + clip.right, + top: clip.top === false ? 0 : area.top - clip.top, + bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom + }); + } + meta.controller.draw(); + if (useClip) { + unclipArea(ctx); + } + args.cancelable = false; + this.notifyPlugins('afterDatasetDraw', args); + } + 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(index) { + this._hiddenIndices[index] = !this._hiddenIndices[index]; + } + getDataVisibility(index) { + return !this._hiddenIndices[index]; + } + _updateVisibility(datasetIndex, dataIndex, visible) { + const mode = visible ? 'show' : 'hide'; + const meta = this.getDatasetMeta(datasetIndex); + const anims = meta.controller._resolveAnimations(undefined, 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 : undefined); + } + } + 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; + } + this.notifyPlugins('destroy'); + 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, listener) => { + platform.addEventListener(this, type, listener); + listeners[type] = listener; + }; + 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, listener) => { + platform.addEventListener(this, type, listener); + listeners[type] = listener; + }; + const _remove = (type, listener) => { + if (listeners[type]) { + platform.removeEventListener(this, type, listener); + 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 = undefined; + } + 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}) => { + const meta = this.getDatasetMeta(datasetIndex); + if (!meta) { + throw new Error('No dataset found at index ' + datasetIndex); + } + return { + datasetIndex, + element: meta.data[index], + index, + }; + }); + 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); + } + _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: _isPointInArea(e, this.chartArea, this._minPadding) + }; + 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); + } +} +const invalidatePlugins = () => each(Chart.instances, (chart) => chart._plugins.invalidate()); +const enumerable = true; +Object.defineProperties(Chart, { + defaults: { + enumerable, + value: defaults + }, + instances: { + enumerable, + value: instances + }, + overrides: { + enumerable, + value: overrides + }, + registry: { + enumerable, + value: registry + }, + version: { + enumerable, + value: version + }, + getChart: { + enumerable, + value: getChart + }, + register: { + enumerable, + value: (...items) => { + registry.add(...items); + invalidatePlugins(); + } + }, + unregister: { + enumerable, + value: (...items) => { + registry.remove(...items); + invalidatePlugins(); + } + } +}); + +function abstract() { + throw new Error('This method is not implemented: Check that a complete date adapter is provided.'); +} +class DateAdapter { + constructor(options) { + this.options = options || {}; + } + formats() { + return abstract(); + } + parse(value, format) { + return abstract(); + } + format(timestamp, format) { + return abstract(); + } + add(timestamp, amount, unit) { + return abstract(); + } + diff(a, b, unit) { + return abstract(); + } + startOf(timestamp, unit, weekday) { + return abstract(); + } + endOf(timestamp, unit) { + return abstract(); + } +} +DateAdapter.override = function(members) { + Object.assign(DateAdapter.prototype, members); +}; +var _adapters = { + _date: DateAdapter +}; + +function getAllScaleValues(scale, type) { + if (!scale._cache.$bar) { + const visibleMetas = scale.getMatchingVisibleMetas(type); + let values = []; + for (let i = 0, ilen = visibleMetas.length; i < ilen; i++) { + values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale)); + } + scale._cache.$bar = _arrayUnique(values.sort((a, b) => a - b)); + } + return scale._cache.$bar; +} +function computeMinSampleSize(meta) { + const scale = meta.iScale; + const values = getAllScaleValues(scale, meta.type); + let min = scale._length; + let i, ilen, curr, prev; + const updateMinAndPrev = () => { + if (curr === 32767 || curr === -32768) { + return; + } + if (defined(prev)) { + min = Math.min(min, Math.abs(curr - prev) || min); + } + prev = curr; + }; + for (i = 0, ilen = values.length; i < ilen; ++i) { + curr = scale.getPixelForValue(values[i]); + updateMinAndPrev(); + } + prev = undefined; + for (i = 0, ilen = scale.ticks.length; i < ilen; ++i) { + curr = scale.getPixelForTick(i); + updateMinAndPrev(); + } + return min; +} +function computeFitCategoryTraits(index, ruler, options, stackCount) { + const thickness = options.barThickness; + let size, ratio; + if (isNullOrUndef(thickness)) { + size = ruler.min * options.categoryPercentage; + ratio = options.barPercentage; + } else { + size = thickness * stackCount; + ratio = 1; + } + return { + chunk: size / stackCount, + ratio, + start: ruler.pixels[index] - (size / 2) + }; +} +function computeFlexCategoryTraits(index, ruler, options, stackCount) { + const pixels = ruler.pixels; + const curr = pixels[index]; + let prev = index > 0 ? pixels[index - 1] : null; + let next = index < pixels.length - 1 ? pixels[index + 1] : null; + const percent = options.categoryPercentage; + if (prev === null) { + prev = curr - (next === null ? ruler.end - ruler.start : next - curr); + } + if (next === null) { + next = curr + curr - prev; + } + const start = curr - (curr - Math.min(prev, next)) / 2 * percent; + const size = Math.abs(next - prev) / 2 * percent; + return { + chunk: size / stackCount, + ratio: options.barPercentage, + start + }; +} +function parseFloatBar(entry, item, vScale, i) { + const startValue = vScale.parse(entry[0], i); + const endValue = vScale.parse(entry[1], i); + const min = Math.min(startValue, endValue); + const max = Math.max(startValue, endValue); + let barStart = min; + let barEnd = max; + if (Math.abs(min) > Math.abs(max)) { + barStart = max; + barEnd = min; + } + item[vScale.axis] = barEnd; + item._custom = { + barStart, + barEnd, + start: startValue, + end: endValue, + min, + max + }; +} +function parseValue(entry, item, vScale, i) { + if (isArray(entry)) { + parseFloatBar(entry, item, vScale, i); + } else { + item[vScale.axis] = vScale.parse(entry, i); + } + return item; +} +function parseArrayOrPrimitive(meta, data, start, count) { + const iScale = meta.iScale; + const vScale = meta.vScale; + const labels = iScale.getLabels(); + const singleScale = iScale === vScale; + const parsed = []; + let i, ilen, item, entry; + for (i = start, ilen = start + count; i < ilen; ++i) { + entry = data[i]; + item = {}; + item[iScale.axis] = singleScale || iScale.parse(labels[i], i); + parsed.push(parseValue(entry, item, vScale, i)); + } + return parsed; +} +function isFloatBar(custom) { + return custom && custom.barStart !== undefined && custom.barEnd !== undefined; +} +function barSign(size, vScale, actualBase) { + if (size !== 0) { + return sign(size); + } + return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1); +} +function borderProps(properties) { + let reverse, start, end, top, bottom; + if (properties.horizontal) { + reverse = properties.base > properties.x; + start = 'left'; + end = 'right'; + } else { + reverse = properties.base < properties.y; + start = 'bottom'; + end = 'top'; + } + if (reverse) { + top = 'end'; + bottom = 'start'; + } else { + top = 'start'; + bottom = 'end'; + } + return {start, end, reverse, top, bottom}; +} +function setBorderSkipped(properties, options, stack, index) { + let edge = options.borderSkipped; + const res = {}; + if (!edge) { + properties.borderSkipped = res; + return; + } + const {start, end, reverse, top, bottom} = borderProps(properties); + if (edge === 'middle' && stack) { + properties.enableBorderRadius = true; + if ((stack._top || 0) === index) { + edge = top; + } else if ((stack._bottom || 0) === index) { + edge = bottom; + } else { + res[parseEdge(bottom, start, end, reverse)] = true; + edge = top; + } + } + res[parseEdge(edge, start, end, reverse)] = true; + properties.borderSkipped = res; +} +function parseEdge(edge, a, b, reverse) { + if (reverse) { + edge = swap(edge, a, b); + edge = startEnd(edge, b, a); + } else { + edge = startEnd(edge, a, b); + } + return edge; +} +function swap(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} +function startEnd(v, start, end) { + return v === 'start' ? start : v === 'end' ? end : v; +} +function setInflateAmount(properties, {inflateAmount}, ratio) { + properties.inflateAmount = inflateAmount === 'auto' + ? ratio === 1 ? 0.33 : 0 + : inflateAmount; +} +class BarController extends DatasetController { + parsePrimitiveData(meta, data, start, count) { + return parseArrayOrPrimitive(meta, data, start, count); + } + parseArrayData(meta, data, start, count) { + return parseArrayOrPrimitive(meta, data, start, count); + } + parseObjectData(meta, data, start, count) { + const {iScale, vScale} = meta; + const {xAxisKey = 'x', yAxisKey = 'y'} = this._parsing; + const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey; + const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey; + const parsed = []; + let i, ilen, item, obj; + for (i = start, ilen = start + count; i < ilen; ++i) { + obj = data[i]; + item = {}; + item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i); + parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i)); + } + return parsed; + } + updateRangeFromParsed(range, scale, parsed, stack) { + super.updateRangeFromParsed(range, scale, parsed, stack); + const custom = parsed._custom; + if (custom && scale === this._cachedMeta.vScale) { + range.min = Math.min(range.min, custom.min); + range.max = Math.max(range.max, custom.max); + } + } + getMaxOverflow() { + return 0; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const {iScale, vScale} = meta; + const parsed = this.getParsed(index); + const custom = parsed._custom; + const value = isFloatBar(custom) + ? '[' + custom.start + ', ' + custom.end + ']' + : '' + vScale.getLabelForValue(parsed[vScale.axis]); + return { + label: '' + iScale.getLabelForValue(parsed[iScale.axis]), + value + }; + } + initialize() { + this.enableOptionSharing = true; + super.initialize(); + const meta = this._cachedMeta; + meta.stack = this.getDataset().stack; + } + update(mode) { + const meta = this._cachedMeta; + this.updateElements(meta.data, 0, meta.data.length, mode); + } + updateElements(bars, start, count, mode) { + const reset = mode === 'reset'; + const {index, _cachedMeta: {vScale}} = this; + const base = vScale.getBasePixel(); + const horizontal = vScale.isHorizontal(); + const ruler = this._getRuler(); + const firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + this.updateSharedOptions(sharedOptions, mode, firstOpts); + for (let i = start; i < start + count; i++) { + const parsed = this.getParsed(i); + const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : this._calculateBarValuePixels(i); + const ipixels = this._calculateBarIndexPixels(i, ruler); + const stack = (parsed._stacks || {})[vScale.axis]; + const properties = { + horizontal, + base: vpixels.base, + enableBorderRadius: !stack || isFloatBar(parsed._custom) || (index === stack._top || index === stack._bottom), + x: horizontal ? vpixels.head : ipixels.center, + y: horizontal ? ipixels.center : vpixels.head, + height: horizontal ? ipixels.size : Math.abs(vpixels.size), + width: horizontal ? Math.abs(vpixels.size) : ipixels.size + }; + if (includeOptions) { + properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode); + } + const options = properties.options || bars[i].options; + setBorderSkipped(properties, options, stack, index); + setInflateAmount(properties, options, ruler.ratio); + this.updateElement(bars[i], i, properties, mode); + } + } + _getStacks(last, dataIndex) { + const meta = this._cachedMeta; + const iScale = meta.iScale; + const metasets = iScale.getMatchingVisibleMetas(this._type); + const stacked = iScale.options.stacked; + const ilen = metasets.length; + const stacks = []; + let i, item; + for (i = 0; i < ilen; ++i) { + item = metasets[i]; + if (!item.controller.options.grouped) { + continue; + } + if (typeof dataIndex !== 'undefined') { + const val = item.controller.getParsed(dataIndex)[ + item.controller._cachedMeta.vScale.axis + ]; + if (isNullOrUndef(val) || isNaN(val)) { + continue; + } + } + if (stacked === false || stacks.indexOf(item.stack) === -1 || + (stacked === undefined && item.stack === undefined)) { + stacks.push(item.stack); + } + if (item.index === last) { + break; + } + } + if (!stacks.length) { + stacks.push(undefined); + } + return stacks; + } + _getStackCount(index) { + return this._getStacks(undefined, index).length; + } + _getStackIndex(datasetIndex, name, dataIndex) { + const stacks = this._getStacks(datasetIndex, dataIndex); + const index = (name !== undefined) + ? stacks.indexOf(name) + : -1; + return (index === -1) + ? stacks.length - 1 + : index; + } + _getRuler() { + const opts = this.options; + const meta = this._cachedMeta; + const iScale = meta.iScale; + const pixels = []; + let i, ilen; + for (i = 0, ilen = meta.data.length; i < ilen; ++i) { + pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i)); + } + const barThickness = opts.barThickness; + const min = barThickness || computeMinSampleSize(meta); + return { + min, + pixels, + start: iScale._startPixel, + end: iScale._endPixel, + stackCount: this._getStackCount(), + scale: iScale, + grouped: opts.grouped, + ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage + }; + } + _calculateBarValuePixels(index) { + const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = this; + const actualBase = baseValue || 0; + const parsed = this.getParsed(index); + const custom = parsed._custom; + const floating = isFloatBar(custom); + let value = parsed[vScale.axis]; + let start = 0; + let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value; + let head, size; + if (length !== value) { + start = length - value; + length = value; + } + if (floating) { + value = custom.barStart; + length = custom.barEnd - custom.barStart; + if (value !== 0 && sign(value) !== sign(custom.barEnd)) { + start = 0; + } + start += value; + } + const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start; + let base = vScale.getPixelForValue(startValue); + if (this.chart.getDataVisibility(index)) { + head = vScale.getPixelForValue(start + length); + } else { + head = base; + } + size = head - base; + if (Math.abs(size) < minBarLength) { + size = barSign(size, vScale, actualBase) * minBarLength; + if (value === actualBase) { + base -= size / 2; + } + head = base + size; + } + if (base === vScale.getPixelForValue(actualBase)) { + const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2; + base += halfGrid; + size -= halfGrid; + } + return { + size, + base, + head, + center: head + size / 2 + }; + } + _calculateBarIndexPixels(index, ruler) { + const scale = ruler.scale; + const options = this.options; + const skipNull = options.skipNull; + const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity); + let center, size; + if (ruler.grouped) { + const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount; + const range = options.barThickness === 'flex' + ? computeFlexCategoryTraits(index, ruler, options, stackCount) + : computeFitCategoryTraits(index, ruler, options, stackCount); + const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined); + center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); + size = Math.min(maxBarThickness, range.chunk * range.ratio); + } else { + center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index); + size = Math.min(maxBarThickness, ruler.min * ruler.ratio); + } + return { + base: center - size / 2, + head: center + size / 2, + center, + size + }; + } + draw() { + const meta = this._cachedMeta; + const vScale = meta.vScale; + const rects = meta.data; + const ilen = rects.length; + let i = 0; + for (; i < ilen; ++i) { + if (this.getParsed(i)[vScale.axis] !== null) { + rects[i].draw(this._ctx); + } + } + } +} +BarController.id = 'bar'; +BarController.defaults = { + datasetElementType: false, + dataElementType: 'bar', + categoryPercentage: 0.8, + barPercentage: 0.9, + grouped: true, + animations: { + numbers: { + type: 'number', + properties: ['x', 'y', 'base', 'width', 'height'] + } + } +}; +BarController.overrides = { + scales: { + _index_: { + type: 'category', + offset: true, + grid: { + offset: true + } + }, + _value_: { + type: 'linear', + beginAtZero: true, + } + } +}; + +class BubbleController extends DatasetController { + initialize() { + this.enableOptionSharing = true; + super.initialize(); + } + parsePrimitiveData(meta, data, start, count) { + const parsed = super.parsePrimitiveData(meta, data, start, count); + for (let i = 0; i < parsed.length; i++) { + parsed[i]._custom = this.resolveDataElementOptions(i + start).radius; + } + return parsed; + } + parseArrayData(meta, data, start, count) { + const parsed = super.parseArrayData(meta, data, start, count); + for (let i = 0; i < parsed.length; i++) { + const item = data[start + i]; + parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius); + } + return parsed; + } + parseObjectData(meta, data, start, count) { + const parsed = super.parseObjectData(meta, data, start, count); + for (let i = 0; i < parsed.length; i++) { + const item = data[start + i]; + parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius); + } + return parsed; + } + getMaxOverflow() { + const data = this._cachedMeta.data; + let max = 0; + for (let i = data.length - 1; i >= 0; --i) { + max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2); + } + return max > 0 && max; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const {xScale, yScale} = meta; + const parsed = this.getParsed(index); + const x = xScale.getLabelForValue(parsed.x); + const y = yScale.getLabelForValue(parsed.y); + const r = parsed._custom; + return { + label: meta.label, + value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')' + }; + } + update(mode) { + const points = this._cachedMeta.data; + this.updateElements(points, 0, points.length, mode); + } + updateElements(points, start, count, mode) { + const reset = mode === 'reset'; + const {iScale, vScale} = this._cachedMeta; + const firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + const iAxis = iScale.axis; + const vAxis = vScale.axis; + for (let i = start; i < start + count; i++) { + const point = points[i]; + const parsed = !reset && this.getParsed(i); + const properties = {}; + const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]); + const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]); + properties.skip = isNaN(iPixel) || isNaN(vPixel); + if (includeOptions) { + properties.options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); + if (reset) { + properties.options.radius = 0; + } + } + this.updateElement(point, i, properties, mode); + } + this.updateSharedOptions(sharedOptions, mode, firstOpts); + } + resolveDataElementOptions(index, mode) { + const parsed = this.getParsed(index); + let values = super.resolveDataElementOptions(index, mode); + if (values.$shared) { + values = Object.assign({}, values, {$shared: false}); + } + const radius = values.radius; + if (mode !== 'active') { + values.radius = 0; + } + values.radius += valueOrDefault(parsed && parsed._custom, radius); + return values; + } +} +BubbleController.id = 'bubble'; +BubbleController.defaults = { + datasetElementType: false, + dataElementType: 'point', + animations: { + numbers: { + type: 'number', + properties: ['x', 'y', 'borderWidth', 'radius'] + } + } +}; +BubbleController.overrides = { + scales: { + x: { + type: 'linear' + }, + y: { + type: 'linear' + } + }, + plugins: { + tooltip: { + callbacks: { + title() { + return ''; + } + } + } + } +}; + +function getRatioAndOffset(rotation, circumference, cutout) { + let ratioX = 1; + let ratioY = 1; + let offsetX = 0; + let offsetY = 0; + if (circumference < TAU) { + const startAngle = rotation; + const endAngle = startAngle + circumference; + const startX = Math.cos(startAngle); + const startY = Math.sin(startAngle); + const endX = Math.cos(endAngle); + const endY = Math.sin(endAngle); + const calcMax = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout); + const calcMin = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout); + const maxX = calcMax(0, startX, endX); + const maxY = calcMax(HALF_PI, startY, endY); + const minX = calcMin(PI, startX, endX); + const minY = calcMin(PI + HALF_PI, startY, endY); + ratioX = (maxX - minX) / 2; + ratioY = (maxY - minY) / 2; + offsetX = -(maxX + minX) / 2; + offsetY = -(maxY + minY) / 2; + } + return {ratioX, ratioY, offsetX, offsetY}; +} +class DoughnutController extends DatasetController { + constructor(chart, datasetIndex) { + super(chart, datasetIndex); + this.enableOptionSharing = true; + this.innerRadius = undefined; + this.outerRadius = undefined; + this.offsetX = undefined; + this.offsetY = undefined; + } + linkScales() {} + parse(start, count) { + const data = this.getDataset().data; + const meta = this._cachedMeta; + if (this._parsing === false) { + meta._parsed = data; + } else { + let getter = (i) => +data[i]; + if (isObject(data[start])) { + const {key = 'value'} = this._parsing; + getter = (i) => +resolveObjectKey(data[i], key); + } + let i, ilen; + for (i = start, ilen = start + count; i < ilen; ++i) { + meta._parsed[i] = getter(i); + } + } + } + _getRotation() { + return toRadians(this.options.rotation - 90); + } + _getCircumference() { + return toRadians(this.options.circumference); + } + _getRotationExtents() { + let min = TAU; + let max = -TAU; + for (let i = 0; i < this.chart.data.datasets.length; ++i) { + if (this.chart.isDatasetVisible(i)) { + const controller = this.chart.getDatasetMeta(i).controller; + const rotation = controller._getRotation(); + const circumference = controller._getCircumference(); + min = Math.min(min, rotation); + max = Math.max(max, rotation + circumference); + } + } + return { + rotation: min, + circumference: max - min, + }; + } + update(mode) { + const chart = this.chart; + const {chartArea} = chart; + const meta = this._cachedMeta; + const arcs = meta.data; + const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing; + const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0); + const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1); + const chartWeight = this._getRingWeight(this.index); + const {circumference, rotation} = this._getRotationExtents(); + const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout); + const maxWidth = (chartArea.width - spacing) / ratioX; + const maxHeight = (chartArea.height - spacing) / ratioY; + const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); + const outerRadius = toDimension(this.options.radius, maxRadius); + const innerRadius = Math.max(outerRadius * cutout, 0); + const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal(); + this.offsetX = offsetX * outerRadius; + this.offsetY = offsetY * outerRadius; + meta.total = this.calculateTotal(); + this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index); + this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0); + this.updateElements(arcs, 0, arcs.length, mode); + } + _circumference(i, reset) { + const opts = this.options; + const meta = this._cachedMeta; + const circumference = this._getCircumference(); + if ((reset && opts.animation.animateRotate) || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) { + return 0; + } + return this.calculateCircumference(meta._parsed[i] * circumference / TAU); + } + updateElements(arcs, start, count, mode) { + const reset = mode === 'reset'; + const chart = this.chart; + const chartArea = chart.chartArea; + const opts = chart.options; + const animationOpts = opts.animation; + const centerX = (chartArea.left + chartArea.right) / 2; + const centerY = (chartArea.top + chartArea.bottom) / 2; + const animateScale = reset && animationOpts.animateScale; + const innerRadius = animateScale ? 0 : this.innerRadius; + const outerRadius = animateScale ? 0 : this.outerRadius; + const firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + let startAngle = this._getRotation(); + let i; + for (i = 0; i < start; ++i) { + startAngle += this._circumference(i, reset); + } + for (i = start; i < start + count; ++i) { + const circumference = this._circumference(i, reset); + const arc = arcs[i]; + const properties = { + x: centerX + this.offsetX, + y: centerY + this.offsetY, + startAngle, + endAngle: startAngle + circumference, + circumference, + outerRadius, + innerRadius + }; + if (includeOptions) { + properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode); + } + startAngle += circumference; + this.updateElement(arc, i, properties, mode); + } + this.updateSharedOptions(sharedOptions, mode, firstOpts); + } + calculateTotal() { + const meta = this._cachedMeta; + const metaData = meta.data; + let total = 0; + let i; + for (i = 0; i < metaData.length; i++) { + const value = meta._parsed[i]; + if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) { + total += Math.abs(value); + } + } + return total; + } + calculateCircumference(value) { + const total = this._cachedMeta.total; + if (total > 0 && !isNaN(value)) { + return TAU * (Math.abs(value) / total); + } + return 0; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const chart = this.chart; + const labels = chart.data.labels || []; + const value = formatNumber(meta._parsed[index], chart.options.locale); + return { + label: labels[index] || '', + value, + }; + } + getMaxBorderWidth(arcs) { + let max = 0; + const chart = this.chart; + let i, ilen, meta, controller, options; + if (!arcs) { + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + arcs = meta.data; + controller = meta.controller; + break; + } + } + } + if (!arcs) { + return 0; + } + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + options = controller.resolveDataElementOptions(i); + if (options.borderAlign !== 'inner') { + max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0); + } + } + return max; + } + getMaxOffset(arcs) { + let max = 0; + for (let i = 0, ilen = arcs.length; i < ilen; ++i) { + const options = this.resolveDataElementOptions(i); + max = Math.max(max, options.offset || 0, options.hoverOffset || 0); + } + return max; + } + _getRingWeightOffset(datasetIndex) { + let ringWeightOffset = 0; + for (let i = 0; i < datasetIndex; ++i) { + if (this.chart.isDatasetVisible(i)) { + ringWeightOffset += this._getRingWeight(i); + } + } + return ringWeightOffset; + } + _getRingWeight(datasetIndex) { + return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0); + } + _getVisibleDatasetWeightTotal() { + return this._getRingWeightOffset(this.chart.data.datasets.length) || 1; + } +} +DoughnutController.id = 'doughnut'; +DoughnutController.defaults = { + datasetElementType: false, + dataElementType: 'arc', + animation: { + animateRotate: true, + animateScale: false + }, + animations: { + numbers: { + type: 'number', + properties: ['circumference', 'endAngle', 'innerRadius', 'outerRadius', 'startAngle', 'x', 'y', 'offset', 'borderWidth', 'spacing'] + }, + }, + cutout: '50%', + rotation: 0, + circumference: 360, + radius: '100%', + spacing: 0, + indexAxis: 'r', +}; +DoughnutController.descriptors = { + _scriptable: (name) => name !== 'spacing', + _indexable: (name) => name !== 'spacing', +}; +DoughnutController.overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(chart) { + const data = chart.data; + if (data.labels.length && data.datasets.length) { + const {labels: {pointStyle}} = chart.legend.options; + return data.labels.map((label, i) => { + const meta = chart.getDatasetMeta(0); + const style = meta.controller.getStyle(i); + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + pointStyle: pointStyle, + hidden: !chart.getDataVisibility(i), + index: i + }; + }); + } + return []; + } + }, + onClick(e, legendItem, legend) { + legend.chart.toggleDataVisibility(legendItem.index); + legend.chart.update(); + } + }, + tooltip: { + callbacks: { + title() { + return ''; + }, + label(tooltipItem) { + let dataLabel = tooltipItem.label; + const value = ': ' + tooltipItem.formattedValue; + if (isArray(dataLabel)) { + dataLabel = dataLabel.slice(); + dataLabel[0] += value; + } else { + dataLabel += value; + } + return dataLabel; + } + } + } + } +}; + +class LineController extends DatasetController { + initialize() { + this.enableOptionSharing = 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, undefined, { + 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 firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions); + 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'; + let prevParsed = start > 0 && this.getParsed(start - 1); + for (let i = start; i < start + count; ++i) { + const point = points[i]; + const parsed = this.getParsed(i); + const properties = directUpdate ? point : {}; + 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 && (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; + } + this.updateSharedOptions(sharedOptions, mode, firstOpts); + } + 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(); + } +} +LineController.id = 'line'; +LineController.defaults = { + datasetElementType: 'line', + dataElementType: 'point', + showLine: true, + spanGaps: false, +}; +LineController.overrides = { + scales: { + _index_: { + type: 'category', + }, + _value_: { + type: 'linear', + }, + } +}; +function getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) { + const pointCount = points.length; + let start = 0; + let count = pointCount; + if (meta._sorted) { + const {iScale, _parsed} = meta; + const axis = iScale.axis; + const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); + if (minDefined) { + start = _limitValue(Math.min( + _lookupByKey(_parsed, iScale.axis, min).lo, + animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), + 0, pointCount - 1); + } + if (maxDefined) { + count = _limitValue(Math.max( + _lookupByKey(_parsed, iScale.axis, max).hi + 1, + animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max)).hi + 1), + 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; +} + +class PolarAreaController extends DatasetController { + constructor(chart, datasetIndex) { + super(chart, datasetIndex); + this.innerRadius = undefined; + this.outerRadius = undefined; + } + getLabelAndValue(index) { + const meta = this._cachedMeta; + const chart = this.chart; + const labels = chart.data.labels || []; + const value = formatNumber(meta._parsed[index].r, chart.options.locale); + return { + label: labels[index] || '', + value, + }; + } + update(mode) { + const arcs = this._cachedMeta.data; + this._updateRadius(); + this.updateElements(arcs, 0, arcs.length, mode); + } + _updateRadius() { + const chart = this.chart; + const chartArea = chart.chartArea; + const opts = chart.options; + const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + const outerRadius = Math.max(minSize / 2, 0); + const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); + const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount(); + this.outerRadius = outerRadius - (radiusLength * this.index); + this.innerRadius = this.outerRadius - radiusLength; + } + updateElements(arcs, start, count, mode) { + const reset = mode === 'reset'; + const chart = this.chart; + const dataset = this.getDataset(); + const opts = chart.options; + const animationOpts = opts.animation; + const scale = this._cachedMeta.rScale; + const centerX = scale.xCenter; + const centerY = scale.yCenter; + const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI; + let angle = datasetStartAngle; + let i; + const defaultAngle = 360 / this.countVisibleElements(); + for (i = 0; i < start; ++i) { + angle += this._computeAngle(i, mode, defaultAngle); + } + for (i = start; i < start + count; i++) { + const arc = arcs[i]; + let startAngle = angle; + let endAngle = angle + this._computeAngle(i, mode, defaultAngle); + let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0; + angle = endAngle; + if (reset) { + if (animationOpts.animateScale) { + outerRadius = 0; + } + if (animationOpts.animateRotate) { + startAngle = endAngle = datasetStartAngle; + } + } + const properties = { + x: centerX, + y: centerY, + innerRadius: 0, + outerRadius, + startAngle, + endAngle, + options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode) + }; + this.updateElement(arc, i, properties, mode); + } + } + countVisibleElements() { + const dataset = this.getDataset(); + const meta = this._cachedMeta; + let count = 0; + meta.data.forEach((element, index) => { + if (!isNaN(dataset.data[index]) && this.chart.getDataVisibility(index)) { + count++; + } + }); + return count; + } + _computeAngle(index, mode, defaultAngle) { + return this.chart.getDataVisibility(index) + ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) + : 0; + } +} +PolarAreaController.id = 'polarArea'; +PolarAreaController.defaults = { + dataElementType: 'arc', + animation: { + animateRotate: true, + animateScale: true + }, + animations: { + numbers: { + type: 'number', + properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius'] + }, + }, + indexAxis: 'r', + startAngle: 0, +}; +PolarAreaController.overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(chart) { + const data = chart.data; + if (data.labels.length && data.datasets.length) { + const {labels: {pointStyle}} = chart.legend.options; + return data.labels.map((label, i) => { + const meta = chart.getDatasetMeta(0); + const style = meta.controller.getStyle(i); + return { + text: label, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + pointStyle: pointStyle, + hidden: !chart.getDataVisibility(i), + index: i + }; + }); + } + return []; + } + }, + onClick(e, legendItem, legend) { + legend.chart.toggleDataVisibility(legendItem.index); + legend.chart.update(); + } + }, + tooltip: { + callbacks: { + title() { + return ''; + }, + label(context) { + return context.chart.data.labels[context.dataIndex] + ': ' + context.formattedValue; + } + } + } + }, + scales: { + r: { + type: 'radialLinear', + angleLines: { + display: false + }, + beginAtZero: true, + grid: { + circular: true + }, + pointLabels: { + display: false + }, + startAngle: 0 + } + } +}; + +class PieController extends DoughnutController { +} +PieController.id = 'pie'; +PieController.defaults = { + cutout: 0, + rotation: 0, + circumference: 360, + radius: '100%' +}; + +class RadarController extends DatasetController { + getLabelAndValue(index) { + const vScale = this._cachedMeta.vScale; + const parsed = this.getParsed(index); + return { + label: vScale.getLabels()[index], + value: '' + vScale.getLabelForValue(parsed[vScale.axis]) + }; + } + update(mode) { + const meta = this._cachedMeta; + const line = meta.dataset; + const points = meta.data || []; + const labels = meta.iScale.getLabels(); + line.points = points; + if (mode !== 'resize') { + const options = this.resolveDatasetElementOptions(mode); + if (!this.options.showLine) { + options.borderWidth = 0; + } + const properties = { + _loop: true, + _fullLoop: labels.length === points.length, + options + }; + this.updateElement(line, undefined, properties, mode); + } + this.updateElements(points, 0, points.length, mode); + } + updateElements(points, start, count, mode) { + const dataset = this.getDataset(); + const scale = this._cachedMeta.rScale; + const reset = mode === 'reset'; + for (let i = start; i < start + count; i++) { + const point = points[i]; + const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); + const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]); + const x = reset ? scale.xCenter : pointPosition.x; + const y = reset ? scale.yCenter : pointPosition.y; + const properties = { + x, + y, + angle: pointPosition.angle, + skip: isNaN(x) || isNaN(y), + options + }; + this.updateElement(point, i, properties, mode); + } + } +} +RadarController.id = 'radar'; +RadarController.defaults = { + datasetElementType: 'line', + dataElementType: 'point', + indexAxis: 'r', + showLine: true, + elements: { + line: { + fill: 'start' + } + }, +}; +RadarController.overrides = { + aspectRatio: 1, + scales: { + r: { + type: 'radialLinear', + } + } +}; + +class ScatterController extends LineController { +} +ScatterController.id = 'scatter'; +ScatterController.defaults = { + showLine: false, + fill: false +}; +ScatterController.overrides = { + interaction: { + mode: 'point' + }, + plugins: { + tooltip: { + callbacks: { + title() { + return ''; + }, + label(item) { + return '(' + item.label + ', ' + item.formattedValue + ')'; + } + } + } + }, + scales: { + x: { + type: 'linear' + }, + y: { + type: 'linear' + } + } +}; + +var controllers = /*#__PURE__*/Object.freeze({ +__proto__: null, +BarController: BarController, +BubbleController: BubbleController, +DoughnutController: DoughnutController, +LineController: LineController, +PolarAreaController: PolarAreaController, +PieController: PieController, +RadarController: RadarController, +ScatterController: ScatterController +}); + +function clipArc(ctx, element, endAngle) { + const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element; + let angleMargin = pixelMargin / outerRadius; + ctx.beginPath(); + ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin); + if (innerRadius > pixelMargin) { + angleMargin = pixelMargin / innerRadius; + ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true); + } else { + ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI); + } + ctx.closePath(); + ctx.clip(); +} +function toRadiusCorners(value) { + return _readValueToProps(value, ['outerStart', 'outerEnd', 'innerStart', 'innerEnd']); +} +function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) { + const o = toRadiusCorners(arc.options.borderRadius); + const halfThickness = (outerRadius - innerRadius) / 2; + const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2); + const computeOuterLimit = (val) => { + const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2; + return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit)); + }; + return { + outerStart: computeOuterLimit(o.outerStart), + outerEnd: computeOuterLimit(o.outerEnd), + innerStart: _limitValue(o.innerStart, 0, innerLimit), + innerEnd: _limitValue(o.innerEnd, 0, innerLimit), + }; +} +function rThetaToXY(r, theta, x, y) { + return { + x: x + r * Math.cos(theta), + y: y + r * Math.sin(theta), + }; +} +function pathArc(ctx, element, offset, spacing, end) { + const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element; + const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0); + const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0; + let spacingOffset = 0; + const alpha = end - start; + if (spacing) { + const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0; + const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0; + const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2; + const adjustedAngle = avNogSpacingRadius !== 0 ? (alpha * avNogSpacingRadius) / (avNogSpacingRadius + spacing) : alpha; + spacingOffset = (alpha - adjustedAngle) / 2; + } + const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius; + const angleOffset = (alpha - beta) / 2; + const startAngle = start + angleOffset + spacingOffset; + const endAngle = end - angleOffset - spacingOffset; + const {outerStart, outerEnd, innerStart, innerEnd} = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle); + const outerStartAdjustedRadius = outerRadius - outerStart; + const outerEndAdjustedRadius = outerRadius - outerEnd; + const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius; + const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius; + const innerStartAdjustedRadius = innerRadius + innerStart; + const innerEndAdjustedRadius = innerRadius + innerEnd; + const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius; + const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius; + ctx.beginPath(); + ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle); + if (outerEnd > 0) { + const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI); + } + const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y); + ctx.lineTo(p4.x, p4.y); + if (innerEnd > 0) { + const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI); + } + ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true); + if (innerStart > 0) { + const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI); + } + const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y); + ctx.lineTo(p8.x, p8.y); + if (outerStart > 0) { + const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle); + } + ctx.closePath(); +} +function drawArc(ctx, element, offset, spacing) { + const {fullCircles, startAngle, circumference} = element; + let endAngle = element.endAngle; + if (fullCircles) { + pathArc(ctx, element, offset, spacing, startAngle + TAU); + for (let i = 0; i < fullCircles; ++i) { + ctx.fill(); + } + if (!isNaN(circumference)) { + endAngle = startAngle + circumference % TAU; + if (circumference % TAU === 0) { + endAngle += TAU; + } + } + } + pathArc(ctx, element, offset, spacing, endAngle); + ctx.fill(); + return endAngle; +} +function drawFullCircleBorders(ctx, element, inner) { + const {x, y, startAngle, pixelMargin, fullCircles} = element; + const outerRadius = Math.max(element.outerRadius - pixelMargin, 0); + const innerRadius = element.innerRadius + pixelMargin; + let i; + if (inner) { + clipArc(ctx, element, startAngle + TAU); + } + ctx.beginPath(); + ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true); + for (i = 0; i < fullCircles; ++i) { + ctx.stroke(); + } + ctx.beginPath(); + ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU); + for (i = 0; i < fullCircles; ++i) { + ctx.stroke(); + } +} +function drawBorder(ctx, element, offset, spacing, endAngle) { + const {options} = element; + const {borderWidth, borderJoinStyle} = options; + const inner = options.borderAlign === 'inner'; + if (!borderWidth) { + return; + } + if (inner) { + ctx.lineWidth = borderWidth * 2; + ctx.lineJoin = borderJoinStyle || 'round'; + } else { + ctx.lineWidth = borderWidth; + ctx.lineJoin = borderJoinStyle || 'bevel'; + } + if (element.fullCircles) { + drawFullCircleBorders(ctx, element, inner); + } + if (inner) { + clipArc(ctx, element, endAngle); + } + pathArc(ctx, element, offset, spacing, endAngle); + ctx.stroke(); +} +class ArcElement extends Element { + constructor(cfg) { + super(); + this.options = undefined; + this.circumference = undefined; + this.startAngle = undefined; + this.endAngle = undefined; + this.innerRadius = undefined; + this.outerRadius = undefined; + this.pixelMargin = 0; + this.fullCircles = 0; + if (cfg) { + Object.assign(this, cfg); + } + } + inRange(chartX, chartY, useFinalPosition) { + const point = this.getProps(['x', 'y'], useFinalPosition); + const {angle, distance} = getAngleFromPoint(point, {x: chartX, y: chartY}); + const {startAngle, endAngle, innerRadius, outerRadius, circumference} = this.getProps([ + 'startAngle', + 'endAngle', + 'innerRadius', + 'outerRadius', + 'circumference' + ], useFinalPosition); + const rAdjust = this.options.spacing / 2; + const _circumference = valueOrDefault(circumference, endAngle - startAngle); + const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle); + const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust); + return (betweenAngles && withinRadius); + } + getCenterPoint(useFinalPosition) { + const {x, y, startAngle, endAngle, innerRadius, outerRadius} = this.getProps([ + 'x', + 'y', + 'startAngle', + 'endAngle', + 'innerRadius', + 'outerRadius', + 'circumference', + ], useFinalPosition); + const {offset, spacing} = this.options; + const halfAngle = (startAngle + endAngle) / 2; + const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2; + return { + x: x + Math.cos(halfAngle) * halfRadius, + y: y + Math.sin(halfAngle) * halfRadius + }; + } + tooltipPosition(useFinalPosition) { + return this.getCenterPoint(useFinalPosition); + } + draw(ctx) { + const {options, circumference} = this; + const offset = (options.offset || 0) / 2; + const spacing = (options.spacing || 0) / 2; + this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0; + this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0; + if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) { + return; + } + ctx.save(); + let radiusOffset = 0; + if (offset) { + radiusOffset = offset / 2; + const halfAngle = (this.startAngle + this.endAngle) / 2; + ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset); + if (this.circumference >= PI) { + radiusOffset = offset; + } + } + ctx.fillStyle = options.backgroundColor; + ctx.strokeStyle = options.borderColor; + const endAngle = drawArc(ctx, this, radiusOffset, spacing); + drawBorder(ctx, this, radiusOffset, spacing, endAngle); + ctx.restore(); + } +} +ArcElement.id = 'arc'; +ArcElement.defaults = { + borderAlign: 'center', + borderColor: '#fff', + borderJoinStyle: undefined, + borderRadius: 0, + borderWidth: 2, + offset: 0, + spacing: 0, + angle: undefined, +}; +ArcElement.defaultRoutes = { + backgroundColor: 'backgroundColor' +}; + +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 = (index) => (start + (reverse ? ilen - index : index)) % 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 = undefined; + this._chart = undefined; + this._loop = undefined; + this._fullLoop = undefined; + this._path = undefined; + this._points = undefined; + this._segments = undefined; + this._decimated = false; + this._pointsUpdated = false; + this._datasetIndex = undefined; + 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 = undefined; + } + } +} +LineElement.id = 'line'; +LineElement.defaults = { + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: 'miter', + borderWidth: 3, + capBezierPoints: true, + cubicInterpolationMode: 'default', + fill: false, + spanGaps: false, + stepped: false, + tension: 0, +}; +LineElement.defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' +}; +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(); + this.options = undefined; + this.parsed = undefined; + this.skip = undefined; + this.stop = undefined; + 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; + } +} +PointElement.id = 'point'; +PointElement.defaults = { + borderWidth: 1, + hitRadius: 1, + hoverBorderWidth: 1, + hoverRadius: 4, + pointStyle: 'circle', + radius: 3, + rotation: 0 +}; +PointElement.defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' +}; + +function getBarBounds(bar, useFinalPosition) { + const {x, y, base, width, height} = bar.getProps(['x', 'y', 'base', 'width', 'height'], useFinalPosition); + let left, right, top, bottom, half; + if (bar.horizontal) { + half = height / 2; + left = Math.min(x, base); + right = Math.max(x, base); + top = y - half; + bottom = y + half; + } else { + half = width / 2; + left = x - half; + right = x + half; + top = Math.min(y, base); + bottom = Math.max(y, base); + } + return {left, top, right, bottom}; +} +function skipOrLimit(skip, value, min, max) { + return skip ? 0 : _limitValue(value, min, max); +} +function parseBorderWidth(bar, maxW, maxH) { + const value = bar.options.borderWidth; + const skip = bar.borderSkipped; + const o = toTRBL(value); + return { + t: skipOrLimit(skip.top, o.top, 0, maxH), + r: skipOrLimit(skip.right, o.right, 0, maxW), + b: skipOrLimit(skip.bottom, o.bottom, 0, maxH), + l: skipOrLimit(skip.left, o.left, 0, maxW) + }; +} +function parseBorderRadius(bar, maxW, maxH) { + const {enableBorderRadius} = bar.getProps(['enableBorderRadius']); + const value = bar.options.borderRadius; + const o = toTRBLCorners(value); + const maxR = Math.min(maxW, maxH); + const skip = bar.borderSkipped; + const enableBorder = enableBorderRadius || isObject(value); + return { + topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR), + topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR), + bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR), + bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR) + }; +} +function boundingRects(bar) { + const bounds = getBarBounds(bar); + const width = bounds.right - bounds.left; + const height = bounds.bottom - bounds.top; + const border = parseBorderWidth(bar, width / 2, height / 2); + const radius = parseBorderRadius(bar, width / 2, height / 2); + return { + outer: { + x: bounds.left, + y: bounds.top, + w: width, + h: height, + radius + }, + inner: { + x: bounds.left + border.l, + y: bounds.top + border.t, + w: width - border.l - border.r, + h: height - border.t - border.b, + radius: { + topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)), + topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)), + bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)), + bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)), + } + } + }; +} +function inRange(bar, x, y, useFinalPosition) { + const skipX = x === null; + const skipY = y === null; + const skipBoth = skipX && skipY; + const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition); + return bounds + && (skipX || _isBetween(x, bounds.left, bounds.right)) + && (skipY || _isBetween(y, bounds.top, bounds.bottom)); +} +function hasRadius(radius) { + return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight; +} +function addNormalRectPath(ctx, rect) { + ctx.rect(rect.x, rect.y, rect.w, rect.h); +} +function inflateRect(rect, amount, refRect = {}) { + const x = rect.x !== refRect.x ? -amount : 0; + const y = rect.y !== refRect.y ? -amount : 0; + const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x; + const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y; + return { + x: rect.x + x, + y: rect.y + y, + w: rect.w + w, + h: rect.h + h, + radius: rect.radius + }; +} +class BarElement extends Element { + constructor(cfg) { + super(); + this.options = undefined; + this.horizontal = undefined; + this.base = undefined; + this.width = undefined; + this.height = undefined; + this.inflateAmount = undefined; + if (cfg) { + Object.assign(this, cfg); + } + } + draw(ctx) { + const {inflateAmount, options: {borderColor, backgroundColor}} = this; + const {inner, outer} = boundingRects(this); + const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath; + ctx.save(); + if (outer.w !== inner.w || outer.h !== inner.h) { + ctx.beginPath(); + addRectPath(ctx, inflateRect(outer, inflateAmount, inner)); + ctx.clip(); + addRectPath(ctx, inflateRect(inner, -inflateAmount, outer)); + ctx.fillStyle = borderColor; + ctx.fill('evenodd'); + } + ctx.beginPath(); + addRectPath(ctx, inflateRect(inner, inflateAmount)); + ctx.fillStyle = backgroundColor; + ctx.fill(); + ctx.restore(); + } + inRange(mouseX, mouseY, useFinalPosition) { + return inRange(this, mouseX, mouseY, useFinalPosition); + } + inXRange(mouseX, useFinalPosition) { + return inRange(this, mouseX, null, useFinalPosition); + } + inYRange(mouseY, useFinalPosition) { + return inRange(this, null, mouseY, useFinalPosition); + } + getCenterPoint(useFinalPosition) { + const {x, y, base, horizontal} = this.getProps(['x', 'y', 'base', 'horizontal'], useFinalPosition); + return { + x: horizontal ? (x + base) / 2 : x, + y: horizontal ? y : (y + base) / 2 + }; + } + getRange(axis) { + return axis === 'x' ? this.width / 2 : this.height / 2; + } +} +BarElement.id = 'bar'; +BarElement.defaults = { + borderSkipped: 'start', + borderWidth: 0, + borderRadius: 0, + inflateAmount: 'auto', + pointStyle: undefined +}; +BarElement.defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' +}; + +var elements = /*#__PURE__*/Object.freeze({ +__proto__: null, +ArcElement: ArcElement, +LineElement: LineElement, +PointElement: PointElement, +BarElement: BarElement +}); + +function lttbDecimation(data, start, count, availableWidth, options) { + const samples = options.samples || availableWidth; + if (samples >= count) { + return data.slice(start, start + count); + } + const decimated = []; + const bucketWidth = (count - 2) / (samples - 2); + let sampledIndex = 0; + const endIndex = start + count - 1; + let a = start; + let i, maxAreaPoint, maxArea, area, nextA; + decimated[sampledIndex++] = data[a]; + for (i = 0; i < samples - 2; i++) { + let avgX = 0; + let avgY = 0; + let j; + const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start; + const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start; + const avgRangeLength = avgRangeEnd - avgRangeStart; + for (j = avgRangeStart; j < avgRangeEnd; j++) { + avgX += data[j].x; + avgY += data[j].y; + } + avgX /= avgRangeLength; + avgY /= avgRangeLength; + const rangeOffs = Math.floor(i * bucketWidth) + 1 + start; + const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start; + const {x: pointAx, y: pointAy} = data[a]; + maxArea = area = -1; + for (j = rangeOffs; j < rangeTo; j++) { + area = 0.5 * Math.abs( + (pointAx - avgX) * (data[j].y - pointAy) - + (pointAx - data[j].x) * (avgY - pointAy) + ); + if (area > maxArea) { + maxArea = area; + maxAreaPoint = data[j]; + nextA = j; + } + } + decimated[sampledIndex++] = maxAreaPoint; + a = nextA; + } + decimated[sampledIndex++] = data[endIndex]; + return decimated; +} +function minMaxDecimation(data, start, count, availableWidth) { + let avgX = 0; + let countX = 0; + let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY; + const decimated = []; + const endIndex = start + count - 1; + const xMin = data[start].x; + const xMax = data[endIndex].x; + const dx = xMax - xMin; + for (i = start; i < start + count; ++i) { + point = data[i]; + x = (point.x - xMin) / dx * availableWidth; + y = point.y; + const truncX = x | 0; + if (truncX === prevX) { + if (y < minY) { + minY = y; + minIndex = i; + } else if (y > maxY) { + maxY = y; + maxIndex = i; + } + avgX = (countX * avgX + point.x) / ++countX; + } else { + const lastIndex = i - 1; + if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) { + const intermediateIndex1 = Math.min(minIndex, maxIndex); + const intermediateIndex2 = Math.max(minIndex, maxIndex); + if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) { + decimated.push({ + ...data[intermediateIndex1], + x: avgX, + }); + } + if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) { + decimated.push({ + ...data[intermediateIndex2], + x: avgX + }); + } + } + if (i > 0 && lastIndex !== startIndex) { + decimated.push(data[lastIndex]); + } + decimated.push(point); + prevX = truncX; + countX = 0; + minY = maxY = y; + minIndex = maxIndex = startIndex = i; + } + } + return decimated; +} +function cleanDecimatedDataset(dataset) { + if (dataset._decimated) { + const data = dataset._data; + delete dataset._decimated; + delete dataset._data; + Object.defineProperty(dataset, 'data', {value: data}); + } +} +function cleanDecimatedData(chart) { + chart.data.datasets.forEach((dataset) => { + cleanDecimatedDataset(dataset); + }); +} +function getStartAndCountOfVisiblePointsSimplified(meta, points) { + const pointCount = points.length; + let start = 0; + let count; + const {iScale} = meta; + const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); + if (minDefined) { + start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1); + } + if (maxDefined) { + count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start; + } else { + count = pointCount - start; + } + return {start, count}; +} +var plugin_decimation = { + id: 'decimation', + defaults: { + algorithm: 'min-max', + enabled: false, + }, + beforeElementsUpdate: (chart, args, options) => { + if (!options.enabled) { + cleanDecimatedData(chart); + return; + } + const availableWidth = chart.width; + chart.data.datasets.forEach((dataset, datasetIndex) => { + const {_data, indexAxis} = dataset; + const meta = chart.getDatasetMeta(datasetIndex); + const data = _data || dataset.data; + if (resolve([indexAxis, chart.options.indexAxis]) === 'y') { + return; + } + if (meta.type !== 'line') { + return; + } + const xAxis = chart.scales[meta.xAxisID]; + if (xAxis.type !== 'linear' && xAxis.type !== 'time') { + return; + } + if (chart.options.parsing) { + return; + } + let {start, count} = getStartAndCountOfVisiblePointsSimplified(meta, data); + const threshold = options.threshold || 4 * availableWidth; + if (count <= threshold) { + cleanDecimatedDataset(dataset); + return; + } + if (isNullOrUndef(_data)) { + dataset._data = data; + delete dataset.data; + Object.defineProperty(dataset, 'data', { + configurable: true, + enumerable: true, + get: function() { + return this._decimated; + }, + set: function(d) { + this._data = d; + } + }); + } + let decimated; + switch (options.algorithm) { + case 'lttb': + decimated = lttbDecimation(data, start, count, availableWidth, options); + break; + case 'min-max': + decimated = minMaxDecimation(data, start, count, availableWidth); + break; + default: + throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`); + } + dataset._decimated = decimated; + }); + }, + destroy(chart) { + cleanDecimatedData(chart); + } +}; + +function getLineByIndex(chart, index) { + const meta = chart.getDatasetMeta(index); + const visible = meta && chart.isDatasetVisible(index); + return visible ? meta.dataset : null; +} +function parseFillOption(line) { + const options = line.options; + const fillOption = options.fill; + let fill = valueOrDefault(fillOption && fillOption.target, fillOption); + if (fill === undefined) { + fill = !!options.backgroundColor; + } + if (fill === false || fill === null) { + return false; + } + if (fill === true) { + return 'origin'; + } + return fill; +} +function decodeFill(line, index, count) { + const fill = parseFillOption(line); + if (isObject(fill)) { + return isNaN(fill.value) ? false : fill; + } + let target = parseFloat(fill); + if (isNumberFinite(target) && Math.floor(target) === target) { + if (fill[0] === '-' || fill[0] === '+') { + target = index + target; + } + if (target === index || target < 0 || target >= count) { + return false; + } + return target; + } + return ['origin', 'start', 'end', 'stack', 'shape'].indexOf(fill) >= 0 && fill; +} +function computeLinearBoundary(source) { + const {scale = {}, fill} = source; + let target = null; + let horizontal; + if (fill === 'start') { + target = scale.bottom; + } else if (fill === 'end') { + target = scale.top; + } else if (isObject(fill)) { + target = scale.getPixelForValue(fill.value); + } else if (scale.getBasePixel) { + target = scale.getBasePixel(); + } + if (isNumberFinite(target)) { + horizontal = scale.isHorizontal(); + return { + x: horizontal ? target : null, + y: horizontal ? null : target + }; + } + return null; +} +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 computeCircularBoundary(source) { + const {scale, fill} = source; + const options = scale.options; + const length = scale.getLabels().length; + const target = []; + const start = options.reverse ? scale.max : scale.min; + const end = options.reverse ? scale.min : scale.max; + let i, center, value; + if (fill === 'start') { + value = start; + } else if (fill === 'end') { + value = end; + } else if (isObject(fill)) { + value = fill.value; + } else { + value = scale.getBaseValue(); + } + if (options.grid.circular) { + center = scale.getPointPositionForValue(0, start); + return new simpleArc({ + x: center.x, + y: center.y, + radius: scale.getDistanceFromCenterForValue(value) + }); + } + for (i = 0; i < length; ++i) { + target.push(scale.getPointPositionForValue(i, value)); + } + return target; +} +function computeBoundary(source) { + const scale = source.scale || {}; + if (scale.getPointPositionForValue) { + return computeCircularBoundary(source); + } + return computeLinearBoundary(source); +} +function findSegmentEnd(start, end, points) { + for (;end > start; end--) { + const point = points[end]; + if (!isNaN(point.x) && !isNaN(point.y)) { + break; + } + } + return 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 buildStackLine(source) { + const {scale, index, line} = source; + const points = []; + const segments = line.segments; + const sourcePoints = line.points; + const linesBelow = getLinesBelow(scale, index); + 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, index) { + const below = []; + const metas = scale.getMatchingVisibleMetas('line'); + for (let i = 0; i < metas.length; i++) { + const meta = metas[i]; + if (meta.index === index) { + 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}; +} +function getTarget(source) { + const {chart, fill, line} = source; + if (isNumberFinite(fill)) { + return getLineByIndex(chart, fill); + } + if (fill === 'stack') { + return buildStackLine(source); + } + if (fill === 'shape') { + return true; + } + const boundary = computeBoundary(source); + if (boundary instanceof simpleArc) { + return boundary; + } + return createBoundaryLine(boundary, line); +} +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 resolveTarget(sources, index, propagate) { + const source = sources[index]; + let fill = source.fill; + const visited = [index]; + let target; + if (!propagate) { + return fill; + } + while (fill !== false && visited.indexOf(fill) === -1) { + if (!isNumberFinite(fill)) { + return fill; + } + target = sources[fill]; + if (!target) { + return false; + } + if (target.visible) { + return fill; + } + visited.push(fill); + fill = target.fill; + } + return false; +} +function _clip(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 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 _getEdge(a, b, prop, fn) { + if (a && b) { + return fn(a[prop], b[prop]); + } + return a ? a[prop] : b ? b[prop] : 0; +} +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 clipBounds(ctx, scale, bounds) { + const {top, bottom} = scale.chart.chartArea; + const {property, start, end} = bounds || {}; + if (property === 'x') { + ctx.beginPath(); + ctx.rect(start, top, end - start, bottom - top); + ctx.clip(); + } +} +function interpolatedLineTo(ctx, target, point, property) { + const interpolatedPoint = target.interpolate(point, property); + if (interpolatedPoint) { + ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y); + } +} +function _fill(ctx, cfg) { + const {line, target, property, color, scale} = cfg; + const segments = _segments(line, target, property); + for (const {source: src, target: tgt, start, end} of segments) { + const {style: {backgroundColor = color} = {}} = src; + const notShape = target !== true; + ctx.save(); + ctx.fillStyle = backgroundColor; + clipBounds(ctx, scale, 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 doFill(ctx, cfg) { + const {line, target, above, below, area, scale} = cfg; + const property = line._loop ? 'angle' : cfg.axis; + ctx.save(); + if (property === 'x' && below !== above) { + _clip(ctx, target, area.top); + _fill(ctx, {line, target, color: above, scale, property}); + ctx.restore(); + ctx.save(); + _clip(ctx, target, area.bottom); + } + _fill(ctx, {line, target, color: below, scale, property}); + ctx.restore(); +} +function drawfill(ctx, source, area) { + const target = getTarget(source); + const {line, scale, axis} = source; + const lineOpts = line.options; + const fillOption = lineOpts.fill; + const color = lineOpts.backgroundColor; + const {above = color, below = color} = fillOption || {}; + if (target && line.points.length) { + clipArea(ctx, area); + doFill(ctx, {line, target, above, below, area, scale, axis}); + unclipArea(ctx); + } +} +var plugin_filler = { + 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 draw = 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 (draw) { + 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 (source) { + drawfill(chart.ctx, source, chart.chartArea); + } + } + }, + beforeDatasetDraw(chart, args, options) { + const source = args.meta.$filler; + if (!source || source.fill === false || 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 = 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 = undefined; + this.columnSizes = undefined; + this.lineWidths = undefined; + this.maxHeight = undefined; + this.maxWidth = undefined; + this.top = undefined; + this.bottom = undefined; + this.left = undefined; + this.right = undefined; + this.height = undefined; + this.width = undefined; + this._margins = undefined; + this.position = undefined; + this.weight = undefined; + this.fullSize = undefined; + } + 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, fontSize, boxWidth, itemHeight) + 10; + } + this.width = Math.min(width, options.maxWidth || this.maxWidth); + this.height = Math.min(height, options.maxHeight || this.maxHeight); + } + _fitRows(titleHeight, fontSize, boxWidth, itemHeight) { + const {ctx, 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, fontSize, 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 = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + 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 {color: fontColor, 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: boxWidth * Math.SQRT2 / 2, + pointStyle: legendItem.pointStyle, + rotation: legendItem.rotation, + borderWidth: lineWidth + }; + const centerX = rtlHelper.xPlus(x, boxWidth / 2); + const centerY = y + halfFontSize; + drawPoint(ctx, drawOptions, centerX, centerY); + } 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 || fontColor; + ctx.fillStyle = legendItem.fontColor || 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 { + 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') { + 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 isListened(type, opts) { + if (type === 'mousemove' && (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: 1000, + onClick(e, legendItem, legend) { + const index = legendItem.datasetIndex; + const ci = legend.chart; + if (ci.isDatasetVisible(index)) { + ci.hide(index); + legendItem.hidden = true; + } else { + ci.show(index); + 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}} = chart.legend.options; + return chart._getSortedDatasetMetas().map((meta) => { + const style = meta.controller.getStyle(usePointStyle ? 0 : undefined); + const borderWidth = toPadding(style.borderWidth); + return { + text: datasets[meta.index].label, + fillStyle: style.backgroundColor, + fontColor: color, + hidden: !meta.visible, + lineCap: style.borderCapStyle, + lineDash: style.borderDash, + lineDashOffset: style.borderDashOffset, + lineJoin: style.borderJoinStyle, + lineWidth: (borderWidth.width + borderWidth.height) / 4, + strokeStyle: style.borderColor, + pointStyle: pointStyle || style.pointStyle, + rotation: style.rotation, + textAlign: textAlign || style.textAlign, + borderRadius: 0, + datasetIndex: meta.index + }; + }, this); + } + }, + title: { + color: (ctx) => ctx.chart.options.color, + display: false, + position: 'center', + text: '', + } + }, + descriptors: { + _scriptable: (name) => !name.startsWith('on'), + labels: { + _scriptable: (name) => !['generateLabels', 'filter', 'sort'].includes(name), + } + }, +}; + +class Title extends Element { + constructor(config) { + super(); + this.chart = config.chart; + this.options = config.options; + this.ctx = config.ctx; + this._padding = undefined; + this.top = undefined; + this.bottom = undefined; + this.left = undefined; + this.right = undefined; + this.width = undefined; + this.height = undefined; + this.position = undefined; + this.weight = undefined; + this.fullSize = undefined; + } + update(maxWidth, maxHeight) { + const opts = this.options; + this.left = 0; + this.top = 0; + if (!opts.display) { + this.width = this.height = this.right = this.bottom = 0; + return; + } + this.width = this.right = maxWidth; + this.height = this.bottom = maxHeight; + const lineCount = isArray(opts.text) ? opts.text.length : 1; + this._padding = toPadding(opts.padding); + const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height; + if (this.isHorizontal()) { + this.height = textSize; + } else { + this.width = textSize; + } + } + isHorizontal() { + const pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + } + _drawArgs(offset) { + const {top, left, bottom, right, options} = this; + const align = options.align; + let rotation = 0; + let maxWidth, titleX, titleY; + if (this.isHorizontal()) { + titleX = _alignStartEnd(align, left, right); + titleY = top + offset; + maxWidth = right - left; + } else { + if (options.position === 'left') { + titleX = left + offset; + titleY = _alignStartEnd(align, bottom, top); + rotation = PI * -0.5; + } else { + titleX = right - offset; + titleY = _alignStartEnd(align, top, bottom); + rotation = PI * 0.5; + } + maxWidth = bottom - top; + } + return {titleX, titleY, maxWidth, rotation}; + } + draw() { + const ctx = this.ctx; + const opts = this.options; + if (!opts.display) { + return; + } + const fontOpts = toFont(opts.font); + const lineHeight = fontOpts.lineHeight; + const offset = lineHeight / 2 + this._padding.top; + const {titleX, titleY, maxWidth, rotation} = this._drawArgs(offset); + renderText(ctx, opts.text, 0, 0, fontOpts, { + color: opts.color, + maxWidth, + rotation, + textAlign: _toLeftRightCenter(opts.align), + textBaseline: 'middle', + translation: [titleX, titleY], + }); + } +} +function createTitle(chart, titleOpts) { + const title = new Title({ + ctx: chart.ctx, + options: titleOpts, + chart + }); + layouts.configure(chart, title, titleOpts); + layouts.addBox(chart, title); + chart.titleBlock = title; +} +var plugin_title = { + id: 'title', + _element: Title, + start(chart, _args, options) { + createTitle(chart, options); + }, + stop(chart) { + const titleBlock = chart.titleBlock; + layouts.removeBox(chart, titleBlock); + delete chart.titleBlock; + }, + beforeUpdate(chart, _args, options) { + const title = chart.titleBlock; + layouts.configure(chart, title, options); + title.options = options; + }, + defaults: { + align: 'center', + display: false, + font: { + weight: 'bold', + }, + fullSize: true, + padding: 10, + position: 'top', + text: '', + weight: 2000 + }, + defaultRoutes: { + color: 'color' + }, + descriptors: { + _scriptable: true, + _indexable: false, + }, +}; + +const map = new WeakMap(); +var plugin_subtitle = { + id: 'subtitle', + start(chart, _args, options) { + const title = new Title({ + ctx: chart.ctx, + options, + chart + }); + layouts.configure(chart, title, options); + layouts.addBox(chart, title); + map.set(chart, title); + }, + stop(chart) { + layouts.removeBox(chart, map.get(chart)); + map.delete(chart); + }, + beforeUpdate(chart, _args, options) { + const title = map.get(chart); + layouts.configure(chart, title, options); + title.options = options; + }, + defaults: { + align: 'center', + display: false, + font: { + weight: 'normal', + }, + fullSize: true, + padding: 0, + position: 'top', + text: '', + weight: 1500 + }, + defaultRoutes: { + color: 'color' + }, + descriptors: { + _scriptable: true, + _indexable: false, + }, +}; + +const positioners = { + average(items) { + if (!items.length) { + return false; + } + let i, len; + let x = 0; + 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(); + x += pos.x; + y += pos.y; + ++count; + } + } + return { + x: x / count, + 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} = item; + const controller = chart.getDatasetMeta(datasetIndex).controller; + const {label, value} = controller.getLabelAndValue(index); + return { + chart, + label, + parsed: controller.getParsed(index), + raw: chart.data.datasets[datasetIndex].data[index], + formattedValue: value, + dataset: controller.getDataset(), + dataIndex: index, + 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(callback) { + return pushOrConcat([], splitNewlines(callback)); +} +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; +} +class Tooltip extends Element { + constructor(config) { + super(); + this.opacity = 0; + this._active = []; + this._eventPosition = undefined; + this._size = undefined; + this._cachedAnimations = undefined; + this._tooltipItems = []; + this.$animations = undefined; + this.$context = undefined; + this.chart = config.chart || config._chart; + this._chart = this.chart; + this.options = config.options; + this.dataPoints = undefined; + this.title = undefined; + this.beforeBody = undefined; + this.body = undefined; + this.afterBody = undefined; + this.footer = undefined; + this.xAlign = undefined; + this.yAlign = undefined; + this.x = undefined; + this.y = undefined; + this.height = undefined; + this.width = undefined; + this.caretX = undefined; + this.caretY = undefined; + this.labelColors = undefined; + this.labelPointStyles = undefined; + this.labelTextColors = undefined; + } + initialize(options) { + this.options = options; + this._cachedAnimations = undefined; + this.$context = undefined; + } + _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 = callbacks.beforeTitle.apply(this, [context]); + const title = callbacks.title.apply(this, [context]); + const afterTitle = callbacks.afterTitle.apply(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(options.callbacks.beforeBody.apply(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(scoped.beforeLabel.call(this, context))); + pushOrConcat(bodyItem.lines, scoped.label.call(this, context)); + pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(this, context))); + bodyItems.push(bodyItem); + }); + return bodyItems; + } + getAfterBody(tooltipItems, options) { + return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this, [tooltipItems])); + } + getFooter(tooltipItems, options) { + const {callbacks} = options; + const beforeFooter = callbacks.beforeFooter.apply(this, [tooltipItems]); + const footer = callbacks.footer.apply(this, [tooltipItems]); + const afterFooter = callbacks.afterFooter.apply(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, index, array) => options.filter(element, index, 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(scoped.labelColor.call(this, context)); + labelPointStyles.push(scoped.labelPointStyle.call(this, context)); + labelTextColors.push(scoped.labelTextColor.call(this, context)); + }); + this.labelColors = labelColors; + this.labelPointStyles = labelPointStyles; + this.labelTextColors = labelTextColors; + this.dataPoints = tooltipItems; + 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 = undefined; + 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 labelColors = this.labelColors[i]; + const labelPointStyle = this.labelPointStyles[i]; + const {boxHeight, boxWidth, boxPadding} = 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 = labelColors.borderColor; + ctx.fillStyle = labelColors.backgroundColor; + drawPoint(ctx, drawOptions, centerX, centerY); + } else { + ctx.lineWidth = labelColors.borderWidth || 1; + ctx.strokeStyle = labelColors.borderColor; + ctx.setLineDash(labelColors.borderDash || []); + ctx.lineDashOffset = labelColors.borderDashOffset || 0; + const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth - boxPadding); + const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - boxPadding - 2); + const borderRadius = toTRBLCorners(labelColors.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 = labelColors.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 = labelColors.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); + } + } + } + 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}) => { + const meta = this.chart.getDatasetMeta(datasetIndex); + if (!meta) { + throw new Error('Cannot find a dataset at index ' + datasetIndex); + } + return { + datasetIndex, + element: meta.data[index], + index, + }; + }); + 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; + } + 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); + } +} +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; + const args = { + tooltip + }; + if (chart.notifyPlugins('beforeTooltipDraw', args) === false) { + return; + } + if (tooltip) { + 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: { + 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 + } + }, + 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'] +}; + +var plugins = /*#__PURE__*/Object.freeze({ +__proto__: null, +Decimation: plugin_decimation, +Filler: plugin_filler, +Legend: plugin_legend, +SubTitle: plugin_subtitle, +Title: plugin_title, +Tooltip: plugin_tooltip +}); + +const addIfString = (labels, raw, index, addedLabels) => { + if (typeof raw === 'string') { + index = labels.push(raw) - 1; + addedLabels.unshift({index, label: raw}); + } else if (isNaN(raw)) { + index = null; + } + return index; +}; +function findOrAddLabel(labels, raw, index, addedLabels) { + const first = labels.indexOf(raw); + if (first === -1) { + return addIfString(labels, raw, index, addedLabels); + } + const last = labels.lastIndexOf(raw); + return first !== last ? index : first; +} +const validIndex = (index, max) => index === null ? null : _limitValue(Math.round(index), 0, max); +class CategoryScale extends Scale { + constructor(cfg) { + super(cfg); + this._startValue = undefined; + this._valueRange = 0; + this._addedLabels = []; + } + init(scaleOptions) { + const added = this._addedLabels; + if (added.length) { + const labels = this.getLabels(); + for (const {index, label} of added) { + if (labels[index] === label) { + labels.splice(index, 1); + } + } + this._addedLabels = []; + } + super.init(scaleOptions); + } + parse(raw, index) { + if (isNullOrUndef(raw)) { + return null; + } + const labels = this.getLabels(); + index = isFinite(index) && labels[index] === raw ? index + : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels); + return validIndex(index, 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) { + const labels = this.getLabels(); + if (value >= 0 && value < labels.length) { + return labels[value]; + } + return 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(index) { + const ticks = this.ticks; + if (index < 0 || index > ticks.length - 1) { + return null; + } + return this.getPixelForValue(ticks[index].value); + } + getValueForPixel(pixel) { + return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange); + } + getBasePixel() { + return this.bottom; + } +} +CategoryScale.id = 'category'; +CategoryScale.defaults = { + ticks: { + callback: CategoryScale.prototype.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 / 1000)) { + 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 / 1000)) { + 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) { + ticks.push({value: Math.round((niceMin + j * spacing) * factor) / factor}); + } + 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)) || 0.001; + const length = 0.75 * minSpacing * ('' + value).length; + return Math.min(minSpacing / ratio, length); +} +class LinearScaleBase extends Scale { + constructor(cfg) { + super(cfg); + this.start = undefined; + this.end = undefined; + this._startValue = undefined; + this._endValue = undefined; + this._valueRange = 0; + } + parse(raw, index) { + 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 = 1; + if (max >= Number.MAX_SAFE_INTEGER || min <= Number.MIN_SAFE_INTEGER) { + offset = 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 > 1000) { + console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`); + maxTicks = 1000; + } + } 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)) || 0.001; + 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; + } +} +LinearScale.id = 'linear'; +LinearScale.defaults = { + ticks: { + callback: Ticks.formatters.numeric + } +}; + +function isMajor(tickVal) { + const remain = tickVal / (Math.pow(10, Math.floor(log10(tickVal)))); + return remain === 1; +} +function generateTicks(generationOptions, dataRange) { + const endExp = Math.floor(log10(dataRange.max)); + const endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); + const ticks = []; + let tickVal = finiteOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); + let exp = Math.floor(log10(tickVal)); + let significand = Math.floor(tickVal / Math.pow(10, exp)); + let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; + do { + ticks.push({value: tickVal, major: isMajor(tickVal)}); + ++significand; + if (significand === 10) { + significand = 1; + ++exp; + precision = exp >= 0 ? 1 : precision; + } + tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; + } while (exp < endExp || (exp === endExp && significand < endSignificand)); + const lastTick = finiteOrDefault(generationOptions.max, tickVal); + ticks.push({value: lastTick, major: isMajor(tickVal)}); + return ticks; +} +class LogarithmicScale extends Scale { + constructor(cfg) { + super(cfg); + this.start = undefined; + this.end = undefined; + this._startValue = undefined; + this._valueRange = 0; + } + parse(raw, index) { + const value = LinearScaleBase.prototype.parse.apply(this, [raw, index]); + if (value === 0) { + this._zero = true; + return undefined; + } + return isNumberFinite(value) && value > 0 ? value : null; + } + determineDataLimits() { + const {min, max} = this.getMinMax(true); + this.min = isNumberFinite(min) ? Math.max(0, min) : null; + this.max = isNumberFinite(max) ? Math.max(0, max) : null; + if (this.options.beginAtZero) { + this._zero = true; + } + this.handleTickRangeOptions(); + } + handleTickRangeOptions() { + const {minDefined, maxDefined} = this.getUserBounds(); + let min = this.min; + let max = this.max; + const setMin = v => (min = minDefined ? min : v); + const setMax = v => (max = maxDefined ? max : v); + const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m); + if (min === max) { + if (min <= 0) { + setMin(1); + setMax(10); + } else { + setMin(exp(min, -1)); + setMax(exp(max, +1)); + } + } + if (min <= 0) { + setMin(exp(max, -1)); + } + if (max <= 0) { + setMax(exp(min, +1)); + } + if (this._zero && this.min !== this._suggestedMin && min === exp(this.min, 0)) { + setMin(exp(min, -1)); + } + this.min = min; + this.max = max; + } + buildTicks() { + const opts = this.options; + const generationOptions = { + min: this._userMin, + max: this._userMax + }; + const ticks = generateTicks(generationOptions, this); + 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; + } + getLabelForValue(value) { + return value === undefined + ? '0' + : formatNumber(value, this.chart.options.locale, this.options.ticks.format); + } + configure() { + const start = this.min; + super.configure(); + this._startValue = log10(start); + this._valueRange = log10(this.max) - log10(start); + } + getPixelForValue(value) { + if (value === undefined || value === 0) { + value = this.min; + } + if (value === null || isNaN(value)) { + return NaN; + } + return this.getPixelForDecimal(value === this.min + ? 0 + : (log10(value) - this._startValue) / this._valueRange); + } + getValueForPixel(pixel) { + const decimal = this.getDecimalForPixel(pixel); + return Math.pow(10, this._startValue + decimal * this._valueRange); + } +} +LogarithmicScale.id = 'logarithmic'; +LogarithmicScale.defaults = { + ticks: { + callback: Ticks.formatters.logarithmic, + major: { + enabled: true + } + } +}; + +function getTickBackdropHeight(opts) { + const tickOpts = opts.ticks; + if (tickOpts.display && opts.display) { + const padding = toPadding(tickOpts.backdropPadding); + return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height; + } + return 0; +} +function measureLabelSize(ctx, font, label) { + label = isArray(label) ? label : [label]; + return { + w: _longestText(ctx, font.string, label), + h: label.length * font.lineHeight + }; +} +function determineLimits(angle, pos, size, min, max) { + if (angle === min || angle === max) { + return { + start: pos - (size / 2), + end: pos + (size / 2) + }; + } else if (angle < min || angle > max) { + return { + start: pos - size, + end: pos + }; + } + return { + start: pos, + end: pos + size + }; +} +function fitWithPointLabels(scale) { + const orig = { + l: scale.left + scale._padding.left, + r: scale.right - scale._padding.right, + t: scale.top + scale._padding.top, + b: scale.bottom - scale._padding.bottom + }; + const limits = Object.assign({}, orig); + const labelSizes = []; + const padding = []; + const valueCount = scale._pointLabels.length; + const pointLabelOpts = scale.options.pointLabels; + const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0; + for (let i = 0; i < valueCount; i++) { + const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i)); + padding[i] = opts.padding; + const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle); + const plFont = toFont(opts.font); + const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]); + labelSizes[i] = textSize; + const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle); + const angle = Math.round(toDegrees(angleRadians)); + const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); + const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); + updateLimits(limits, orig, angleRadians, hLimits, vLimits); + } + scale.setCenterPoint( + orig.l - limits.l, + limits.r - orig.r, + orig.t - limits.t, + limits.b - orig.b + ); + scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding); +} +function updateLimits(limits, orig, angle, hLimits, vLimits) { + const sin = Math.abs(Math.sin(angle)); + const cos = Math.abs(Math.cos(angle)); + let x = 0; + let y = 0; + if (hLimits.start < orig.l) { + x = (orig.l - hLimits.start) / sin; + limits.l = Math.min(limits.l, orig.l - x); + } else if (hLimits.end > orig.r) { + x = (hLimits.end - orig.r) / sin; + limits.r = Math.max(limits.r, orig.r + x); + } + if (vLimits.start < orig.t) { + y = (orig.t - vLimits.start) / cos; + limits.t = Math.min(limits.t, orig.t - y); + } else if (vLimits.end > orig.b) { + y = (vLimits.end - orig.b) / cos; + limits.b = Math.max(limits.b, orig.b + y); + } +} +function buildPointLabelItems(scale, labelSizes, padding) { + const items = []; + const valueCount = scale._pointLabels.length; + const opts = scale.options; + const extra = getTickBackdropHeight(opts) / 2; + const outerDistance = scale.drawingArea; + const additionalAngle = opts.pointLabels.centerPointLabels ? PI / valueCount : 0; + for (let i = 0; i < valueCount; i++) { + const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], additionalAngle); + const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI))); + const size = labelSizes[i]; + const y = yForAngle(pointLabelPosition.y, size.h, angle); + const textAlign = getTextAlignForAngle(angle); + const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign); + items.push({ + x: pointLabelPosition.x, + y, + textAlign, + left, + top: y, + right: left + size.w, + bottom: y + size.h + }); + } + return items; +} +function getTextAlignForAngle(angle) { + if (angle === 0 || angle === 180) { + return 'center'; + } else if (angle < 180) { + return 'left'; + } + return 'right'; +} +function leftForTextAlign(x, w, align) { + if (align === 'right') { + x -= w; + } else if (align === 'center') { + x -= (w / 2); + } + return x; +} +function yForAngle(y, h, angle) { + if (angle === 90 || angle === 270) { + y -= (h / 2); + } else if (angle > 270 || angle < 90) { + y -= h; + } + return y; +} +function drawPointLabels(scale, labelCount) { + const {ctx, options: {pointLabels}} = scale; + for (let i = labelCount - 1; i >= 0; i--) { + const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i)); + const plFont = toFont(optsAtIndex.font); + const {x, y, textAlign, left, top, right, bottom} = scale._pointLabelItems[i]; + const {backdropColor} = optsAtIndex; + if (!isNullOrUndef(backdropColor)) { + const padding = toPadding(optsAtIndex.backdropPadding); + ctx.fillStyle = backdropColor; + ctx.fillRect(left - padding.left, top - padding.top, right - left + padding.width, bottom - top + padding.height); + } + renderText( + ctx, + scale._pointLabels[i], + x, + y + (plFont.lineHeight / 2), + plFont, + { + color: optsAtIndex.color, + textAlign: textAlign, + textBaseline: 'middle' + } + ); + } +} +function pathRadiusLine(scale, radius, circular, labelCount) { + const {ctx} = scale; + if (circular) { + ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU); + } else { + let pointPosition = scale.getPointPosition(0, radius); + ctx.moveTo(pointPosition.x, pointPosition.y); + for (let i = 1; i < labelCount; i++) { + pointPosition = scale.getPointPosition(i, radius); + ctx.lineTo(pointPosition.x, pointPosition.y); + } + } +} +function drawRadiusLine(scale, gridLineOpts, radius, labelCount) { + const ctx = scale.ctx; + const circular = gridLineOpts.circular; + const {color, lineWidth} = gridLineOpts; + if ((!circular && !labelCount) || !color || !lineWidth || radius < 0) { + return; + } + ctx.save(); + ctx.strokeStyle = color; + ctx.lineWidth = lineWidth; + ctx.setLineDash(gridLineOpts.borderDash); + ctx.lineDashOffset = gridLineOpts.borderDashOffset; + ctx.beginPath(); + pathRadiusLine(scale, radius, circular, labelCount); + ctx.closePath(); + ctx.stroke(); + ctx.restore(); +} +function createPointLabelContext(parent, index, label) { + return createContext(parent, { + label, + index, + type: 'pointLabel' + }); +} +class RadialLinearScale extends LinearScaleBase { + constructor(cfg) { + super(cfg); + this.xCenter = undefined; + this.yCenter = undefined; + this.drawingArea = undefined; + this._pointLabels = []; + this._pointLabelItems = []; + } + setDimensions() { + const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2); + const w = this.width = this.maxWidth - padding.width; + const h = this.height = this.maxHeight - padding.height; + this.xCenter = Math.floor(this.left + w / 2 + padding.left); + this.yCenter = Math.floor(this.top + h / 2 + padding.top); + this.drawingArea = Math.floor(Math.min(w, h) / 2); + } + determineDataLimits() { + const {min, max} = this.getMinMax(false); + this.min = isNumberFinite(min) && !isNaN(min) ? min : 0; + this.max = isNumberFinite(max) && !isNaN(max) ? max : 0; + this.handleTickRangeOptions(); + } + computeTickLimit() { + return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); + } + generateTickLabels(ticks) { + LinearScaleBase.prototype.generateTickLabels.call(this, ticks); + this._pointLabels = this.getLabels() + .map((value, index) => { + const label = callback(this.options.pointLabels.callback, [value, index], this); + return label || label === 0 ? label : ''; + }) + .filter((v, i) => this.chart.getDataVisibility(i)); + } + fit() { + const opts = this.options; + if (opts.display && opts.pointLabels.display) { + fitWithPointLabels(this); + } else { + this.setCenterPoint(0, 0, 0, 0); + } + } + setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) { + this.xCenter += Math.floor((leftMovement - rightMovement) / 2); + this.yCenter += Math.floor((topMovement - bottomMovement) / 2); + this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement)); + } + getIndexAngle(index) { + const angleMultiplier = TAU / (this._pointLabels.length || 1); + const startAngle = this.options.startAngle || 0; + return _normalizeAngle(index * angleMultiplier + toRadians(startAngle)); + } + getDistanceFromCenterForValue(value) { + if (isNullOrUndef(value)) { + return NaN; + } + const scalingFactor = this.drawingArea / (this.max - this.min); + if (this.options.reverse) { + return (this.max - value) * scalingFactor; + } + return (value - this.min) * scalingFactor; + } + getValueForDistanceFromCenter(distance) { + if (isNullOrUndef(distance)) { + return NaN; + } + const scaledDistance = distance / (this.drawingArea / (this.max - this.min)); + return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance; + } + getPointLabelContext(index) { + const pointLabels = this._pointLabels || []; + if (index >= 0 && index < pointLabels.length) { + const pointLabel = pointLabels[index]; + return createPointLabelContext(this.getContext(), index, pointLabel); + } + } + getPointPosition(index, distanceFromCenter, additionalAngle = 0) { + const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle; + return { + x: Math.cos(angle) * distanceFromCenter + this.xCenter, + y: Math.sin(angle) * distanceFromCenter + this.yCenter, + angle + }; + } + getPointPositionForValue(index, value) { + return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); + } + getBasePosition(index) { + return this.getPointPositionForValue(index || 0, this.getBaseValue()); + } + getPointLabelPosition(index) { + const {left, top, right, bottom} = this._pointLabelItems[index]; + return { + left, + top, + right, + bottom, + }; + } + drawBackground() { + const {backgroundColor, grid: {circular}} = this.options; + if (backgroundColor) { + const ctx = this.ctx; + ctx.save(); + ctx.beginPath(); + pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length); + ctx.closePath(); + ctx.fillStyle = backgroundColor; + ctx.fill(); + ctx.restore(); + } + } + drawGrid() { + const ctx = this.ctx; + const opts = this.options; + const {angleLines, grid} = opts; + const labelCount = this._pointLabels.length; + let i, offset, position; + if (opts.pointLabels.display) { + drawPointLabels(this, labelCount); + } + if (grid.display) { + this.ticks.forEach((tick, index) => { + if (index !== 0) { + offset = this.getDistanceFromCenterForValue(tick.value); + const optsAtIndex = grid.setContext(this.getContext(index - 1)); + drawRadiusLine(this, optsAtIndex, offset, labelCount); + } + }); + } + if (angleLines.display) { + ctx.save(); + for (i = labelCount - 1; i >= 0; i--) { + const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i)); + const {color, lineWidth} = optsAtIndex; + if (!lineWidth || !color) { + continue; + } + ctx.lineWidth = lineWidth; + ctx.strokeStyle = color; + ctx.setLineDash(optsAtIndex.borderDash); + ctx.lineDashOffset = optsAtIndex.borderDashOffset; + offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max); + position = this.getPointPosition(i, offset); + ctx.beginPath(); + ctx.moveTo(this.xCenter, this.yCenter); + ctx.lineTo(position.x, position.y); + ctx.stroke(); + } + ctx.restore(); + } + } + drawBorder() {} + drawLabels() { + const ctx = this.ctx; + const opts = this.options; + const tickOpts = opts.ticks; + if (!tickOpts.display) { + return; + } + const startAngle = this.getIndexAngle(0); + let offset, width; + ctx.save(); + ctx.translate(this.xCenter, this.yCenter); + ctx.rotate(startAngle); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + this.ticks.forEach((tick, index) => { + if (index === 0 && !opts.reverse) { + return; + } + const optsAtIndex = tickOpts.setContext(this.getContext(index)); + const tickFont = toFont(optsAtIndex.font); + offset = this.getDistanceFromCenterForValue(this.ticks[index].value); + if (optsAtIndex.showLabelBackdrop) { + ctx.font = tickFont.string; + width = ctx.measureText(tick.label).width; + ctx.fillStyle = optsAtIndex.backdropColor; + const padding = toPadding(optsAtIndex.backdropPadding); + ctx.fillRect( + -width / 2 - padding.left, + -offset - tickFont.size / 2 - padding.top, + width + padding.width, + tickFont.size + padding.height + ); + } + renderText(ctx, tick.label, 0, -offset, tickFont, { + color: optsAtIndex.color, + }); + }); + ctx.restore(); + } + drawTitle() {} +} +RadialLinearScale.id = 'radialLinear'; +RadialLinearScale.defaults = { + display: true, + animate: true, + position: 'chartArea', + angleLines: { + display: true, + lineWidth: 1, + borderDash: [], + borderDashOffset: 0.0 + }, + grid: { + circular: false + }, + startAngle: 0, + ticks: { + showLabelBackdrop: true, + callback: Ticks.formatters.numeric + }, + pointLabels: { + backdropColor: undefined, + backdropPadding: 2, + display: true, + font: { + size: 10 + }, + callback(label) { + return label; + }, + padding: 5, + centerPointLabels: false + } +}; +RadialLinearScale.defaultRoutes = { + 'angleLines.color': 'borderColor', + 'pointLabels.color': 'color', + 'ticks.color': 'color' +}; +RadialLinearScale.descriptors = { + angleLines: { + _fallback: 'grid' + } +}; + +const INTERVALS = { + millisecond: {common: true, size: 1, steps: 1000}, + second: {common: true, size: 1000, steps: 60}, + minute: {common: true, size: 60000, steps: 60}, + hour: {common: true, size: 3600000, steps: 24}, + day: {common: true, size: 86400000, steps: 30}, + week: {common: false, size: 604800000, steps: 4}, + month: {common: true, size: 2.628e9, steps: 12}, + quarter: {common: false, size: 7.884e9, steps: 4}, + year: {common: true, size: 3.154e10} +}; +const UNITS = (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, 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 (round) { + value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) + ? adapter.startOf(value, 'isoWeek', isoWeekday) + : adapter.startOf(value, round); + } + 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, map, majorUnit) { + const adapter = scale._adapter; + const first = +adapter.startOf(ticks[0].value, majorUnit); + const last = ticks[ticks.length - 1].value; + let major, index; + for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { + index = map[major]; + if (index >= 0) { + ticks[index].major = true; + } + } + return ticks; +} +function ticksFromTimestamps(scale, values, majorUnit) { + const ticks = []; + const map = {}; + const ilen = values.length; + let i, value; + for (i = 0; i < ilen; ++i) { + value = values[i]; + map[value] = i; + ticks.push({ + value, + major: false + }); + } + return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); +} +class TimeScale extends Scale { + constructor(props) { + super(props); + this._cache = { + data: [], + labels: [], + all: [] + }; + this._unit = 'day'; + this._majorUnit = undefined; + this._offsets = {}; + this._normalized = false; + this._parseOpts = undefined; + } + init(scaleOpts, opts) { + const time = scaleOpts.time || (scaleOpts.time = {}); + const adapter = this._adapter = new _adapters._date(scaleOpts.adapters.date); + mergeIf(time.displayFormats, adapter.formats()); + this._parseOpts = { + parser: time.parser, + round: time.round, + isoWeekday: time.isoWeekday + }; + super.init(scaleOpts); + this._normalized = opts.normalized; + } + parse(raw, index) { + if (raw === undefined) { + 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' ? undefined + : determineMajorUnit(this._unit); + this.initOffsets(timestamps); + if (options.reverse) { + ticks.reverse(); + } + return ticksFromTimestamps(this, ticks, this._majorUnit); + } + 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(timeOpts.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) > 100000 * 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((a, b) => a - b).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); + } + _tickFormatFunction(time, index, ticks, format) { + const options = this.options; + 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[index]; + const major = majorUnit && majorFormat && tick && tick.major; + const label = this._adapter.format(time, format || (major ? majorFormat : minorFormat)); + const formatter = options.ticks.callback; + return formatter ? callback(formatter, [label, index, ticks], this) : label; + } + generateTickLabels(ticks) { + let i, ilen, tick; + for (i = 0, ilen = ticks.length; 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)); + } +} +TimeScale.id = 'time'; +TimeScale.defaults = { + bounds: 'data', + adapters: {}, + time: { + parser: false, + unit: false, + round: false, + isoWeekday: false, + minUnit: 'millisecond', + displayFormats: {} + }, + ticks: { + source: 'auto', + 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 = undefined; + this._tableRange = undefined; + } + 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; + } + _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); + } +} +TimeSeriesScale.id = 'timeseries'; +TimeSeriesScale.defaults = TimeScale.defaults; + +var scales = /*#__PURE__*/Object.freeze({ +__proto__: null, +CategoryScale: CategoryScale, +LinearScale: LinearScale, +LogarithmicScale: LogarithmicScale, +RadialLinearScale: RadialLinearScale, +TimeScale: TimeScale, +TimeSeriesScale: TimeSeriesScale +}); + +Chart.register(controllers, scales, elements, plugins); +Chart.helpers = {...helpers}; +Chart._adapters = _adapters; +Chart.Animation = Animation; +Chart.Animations = Animations; +Chart.animator = animator; +Chart.controllers = registry.controllers.items; +Chart.DatasetController = DatasetController; +Chart.Element = Element; +Chart.elements = elements; +Chart.Interaction = Interaction; +Chart.layouts = layouts; +Chart.platforms = platforms; +Chart.Scale = Scale; +Chart.Ticks = Ticks; +Object.assign(Chart, controllers, scales, elements, plugins, platforms); +Chart.Chart = Chart; +if (typeof window !== 'undefined') { + window.Chart = Chart; +} + +return Chart; + +})); diff --git a/node_modules/chart.js/dist/chart.min.js b/node_modules/chart.js/dist/chart.min.js new file mode 100644 index 00000000..2b3e9984 --- /dev/null +++ b/node_modules/chart.js/dist/chart.min.js @@ -0,0 +1,13 @@ +/*! + * Chart.js v3.7.1 + * https://www.chartjs.org + * (c) 2022 Chart.js Contributors + * Released under the MIT License + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Chart=e()}(this,(function(){"use strict";const t="undefined"==typeof window?function(t){return t()}:window.requestAnimationFrame;function e(e,i,s){const n=s||(t=>Array.prototype.slice.call(t));let o=!1,a=[];return function(...s){a=n(s),o||(o=!0,t.call(window,(()=>{o=!1,e.apply(i,a)})))}}function i(t,e){let i;return function(...s){return e?(clearTimeout(i),i=setTimeout(t,e,s)):t.apply(this,s),e}}const s=t=>"start"===t?"left":"end"===t?"right":"center",n=(t,e,i)=>"start"===t?e:"end"===t?i:(e+i)/2,o=(t,e,i,s)=>t===(s?"left":"right")?i:"center"===t?(e+i)/2:e;var a=new class{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(t,e,i,s){const n=e.listeners[s],o=e.duration;n.forEach((s=>s({chart:t,initial:e.initial,numSteps:o,currentStep:Math.min(i-e.start,o)})))}_refresh(){this._request||(this._running=!0,this._request=t.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(t=Date.now()){let e=0;this._charts.forEach(((i,s)=>{if(!i.running||!i.items.length)return;const n=i.items;let o,a=n.length-1,r=!1;for(;a>=0;--a)o=n[a],o._active?(o._total>i.duration&&(i.duration=o._total),o.tick(t),r=!0):(n[a]=n[n.length-1],n.pop());r&&(s.draw(),this._notify(s,i,t,"progress")),n.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=n.length})),this._lastDate=t,0===e&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}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(((t,e)=>Math.max(t,e._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 i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}; +/*! + * @kurkle/color v0.1.9 + * https://github.com/kurkle/color#readme + * (c) 2020 Jukka Kurkela + * Released under the MIT License + */const r={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},l="0123456789ABCDEF",h=t=>l[15&t],c=t=>l[(240&t)>>4]+l[15&t],d=t=>(240&t)>>4==(15&t);function u(t){var e=function(t){return d(t.r)&&d(t.g)&&d(t.b)&&d(t.a)}(t)?h:c;return t?"#"+e(t.r)+e(t.g)+e(t.b)+(t.a<255?e(t.a):""):t}function f(t){return t+.5|0}const g=(t,e,i)=>Math.max(Math.min(t,i),e);function p(t){return g(f(2.55*t),0,255)}function m(t){return g(f(255*t),0,255)}function x(t){return g(f(t/2.55)/100,0,1)}function b(t){return g(f(100*t),0,100)}const _=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const y=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function v(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function w(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function M(t,e,i){const s=v(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function k(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=n===e?(i-s)/h+(i>16&255,o>>8&255,255&o]}return t}(),T.transparent=[0,0,0,0]);const e=T[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}function R(t,e,i){if(t){let s=k(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=P(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function E(t,e){return t?Object.assign(e||{},t):t}function I(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=m(t[3]))):(e=E(t,{r:0,g:0,b:0,a:1})).a=m(e.a),e}function z(t){return"r"===t.charAt(0)?function(t){const e=_.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=255&(e[8]?p(t):255*t)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?p(i):i),s=255&(e[4]?p(s):s),n=255&(e[6]?p(n):n),{r:i,g:s,b:n,a:o}}}(t):C(t)}class F{constructor(t){if(t instanceof F)return t;const e=typeof t;let i;var s,n,o;"object"===e?i=I(t):"string"===e&&(o=(s=t).length,"#"===s[0]&&(4===o||5===o?n={r:255&17*r[s[1]],g:255&17*r[s[2]],b:255&17*r[s[3]],a:5===o?17*r[s[4]]:255}:7!==o&&9!==o||(n={r:r[s[1]]<<4|r[s[2]],g:r[s[3]]<<4|r[s[4]],b:r[s[5]]<<4|r[s[6]],a:9===o?r[s[7]]<<4|r[s[8]]:255})),i=n||L(t)||z(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=E(this._rgb);return t&&(t.a=x(t.a)),t}set rgb(t){this._rgb=I(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${x(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):this._rgb;var t}hexString(){return this._valid?u(this._rgb):this._rgb}hslString(){return this._valid?function(t){if(!t)return;const e=k(t),i=e[0],s=b(e[1]),n=b(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${x(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):this._rgb}mix(t,e){const i=this;if(t){const s=i.rgb,n=t.rgb;let o;const a=e===o?.5:e,r=2*a-1,l=s.a-n.a,h=((r*l==-1?r:(r+l)/(1+r*l))+1)/2;o=1-h,s.r=255&h*s.r+o*n.r+.5,s.g=255&h*s.g+o*n.g+.5,s.b=255&h*s.b+o*n.b+.5,s.a=a*s.a+(1-a)*n.a,i.rgb=s}return i}clone(){return new F(this.rgb)}alpha(t){return this._rgb.a=m(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=f(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.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 R(this._rgb,2,t),this}darken(t){return R(this._rgb,2,-t),this}saturate(t){return R(this._rgb,1,t),this}desaturate(t){return R(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=k(t);i[0]=D(i[0]+e),i=P(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function B(t){return new F(t)}const V=t=>t instanceof CanvasGradient||t instanceof CanvasPattern;function W(t){return V(t)?t:B(t)}function N(t){return V(t)?t:B(t).saturate(.5).darken(.1).hexString()}function H(){}const j=function(){let t=0;return function(){return t++}}();function $(t){return null==t}function Y(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)}function U(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)}const X=t=>("number"==typeof t||t instanceof Number)&&isFinite(+t);function q(t,e){return X(t)?t:e}function K(t,e){return void 0===t?e:t}const G=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100:t/e,Z=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100*e:+t;function J(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)}function Q(t,e,i,s){let n,o,a;if(Y(t))if(o=t.length,s)for(n=o-1;n>=0;n--)e.call(i,t[n],n);else for(n=0;ni;)t=t[e.substr(i,s-i)],i=s+1,s=rt(e,i);return t}function ht(t){return t.charAt(0).toUpperCase()+t.slice(1)}const ct=t=>void 0!==t,dt=t=>"function"==typeof t,ut=(t,e)=>{if(t.size!==e.size)return!1;for(const i of t)if(!e.has(i))return!1;return!0};function ft(t){return"mouseup"===t.type||"click"===t.type||"contextmenu"===t.type}const gt=Object.create(null),pt=Object.create(null);function mt(t,e){if(!e)return t;const i=e.split(".");for(let e=0,s=i.length;et.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=(t,e)=>N(e.backgroundColor),this.hoverBorderColor=(t,e)=>N(e.borderColor),this.hoverColor=(t,e)=>N(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0},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)}set(t,e){return xt(this,t,e)}get(t){return mt(this,t)}describe(t,e){return xt(pt,t,e)}override(t,e){return xt(gt,t,e)}route(t,e,i,s){const n=mt(this,t),o=mt(this,i),a="_"+e;Object.defineProperties(n,{[a]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[a],e=o[s];return U(t)?Object.assign({},e,t):K(t,e)},set(t){this[a]=t}}})}}({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}});const _t=Math.PI,yt=2*_t,vt=yt+_t,wt=Number.POSITIVE_INFINITY,Mt=_t/180,kt=_t/2,St=_t/4,Pt=2*_t/3,Dt=Math.log10,Ct=Math.sign;function Ot(t){const e=Math.round(t);t=Lt(t,e,t/1e3)?e:t;const i=Math.pow(10,Math.floor(Dt(t))),s=t/i;return(s<=1?1:s<=2?2:s<=5?5:10)*i}function At(t){const e=[],i=Math.sqrt(t);let s;for(s=1;st-e)).pop(),e}function Tt(t){return!isNaN(parseFloat(t))&&isFinite(t)}function Lt(t,e,i){return Math.abs(t-e)=t}function Et(t,e,i){let s,n,o;for(s=0,n=t.length;sl&&h=Math.min(e,i)-s&&t<=Math.max(e,i)+s}function Ut(t){return!t||$(t.size)||$(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function Xt(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function qt(t,e,i,s){let n=(s=s||{}).data=s.data||{},o=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(n=s.data={},o=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let a=0;const r=i.length;let l,h,c,d,u;for(l=0;li.length){for(l=0;l0&&t.stroke()}}function Jt(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.xe.top-i&&t.y0&&""!==o.strokeColor;let l,h;for(t.save(),t.font=n.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]);$(e.rotation)||t.rotate(e.rotation);e.color&&(t.fillStyle=e.color);e.textAlign&&(t.textAlign=e.textAlign);e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,o),l=0;lt[i]1;)s=o+n>>1,i(s)?o=s:n=s;return{lo:o,hi:n}}const re=(t,e,i)=>ae(t,i,(s=>t[s][e]ae(t,i,(s=>t[s][e]>=i));function he(t,e,i){let s=0,n=t.length;for(;ss&&t[n-1]>i;)n--;return s>0||n{const i="_onData"+ht(e),s=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value(...e){const n=s.apply(this,e);return t._chartjs.listeners.forEach((t=>{"function"==typeof t[i]&&t[i](...e)})),n}})})))}function ue(t,e){const i=t._chartjs;if(!i)return;const s=i.listeners,n=s.indexOf(e);-1!==n&&s.splice(n,1),s.length>0||(ce.forEach((e=>{delete t[e]})),delete t._chartjs)}function fe(t){const e=new Set;let i,s;for(i=0,s=t.length;iwindow.getComputedStyle(t,null);function be(t,e){return xe(t).getPropertyValue(e)}const _e=["top","right","bottom","left"];function ye(t,e,i){const s={};i=i?"-"+i:"";for(let n=0;n<4;n++){const o=_e[n];s[o]=parseFloat(t[e+"-"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}function ve(t,e){const{canvas:i,currentDevicePixelRatio:s}=e,n=xe(i),o="border-box"===n.boxSizing,a=ye(n,"padding"),r=ye(n,"border","width"),{x:l,y:h,box:c}=function(t,e){const i=t.native||t,s=i.touches,n=s&&s.length?s[0]:i,{offsetX:o,offsetY:a}=n;let r,l,h=!1;if(((t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot))(o,a,i.target))r=o,l=a;else{const t=e.getBoundingClientRect();r=n.clientX-t.left,l=n.clientY-t.top,h=!0}return{x:r,y:l,box:h}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const we=t=>Math.round(10*t)/10;function Me(t,e,i,s){const n=xe(t),o=ye(n,"margin"),a=me(n.maxWidth,t,"clientWidth")||wt,r=me(n.maxHeight,t,"clientHeight")||wt,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=pe(t);if(o){const t=o.getBoundingClientRect(),a=xe(o),r=ye(a,"border","width"),l=ye(a,"padding");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=me(a.maxWidth,o,"clientWidth"),n=me(a.maxHeight,o,"clientHeight")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||wt,maxHeight:n||wt}}(t,e,i);let{width:h,height:c}=l;if("content-box"===n.boxSizing){const t=ye(n,"border","width"),e=ye(n,"padding");h-=e.width+t.width,c-=e.height+t.height}return h=Math.max(0,h-o.width),c=Math.max(0,s?Math.floor(h/s):c-o.height),h=we(Math.min(h,a,l.maxWidth)),c=we(Math.min(c,r,l.maxHeight)),h&&!c&&(c=we(h/2)),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Math.floor(t.height*s),o=Math.floor(t.width*s);t.height=n/s,t.width=o/s;const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};window.addEventListener("test",null,e),window.removeEventListener("test",null,e)}catch(t){}return t}();function Pe(t,e){const i=be(t,e),s=i&&i.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function De(t,e){return"native"in t?{x:t.x,y:t.y}:ve(t,e)}function Ce(t,e,i,s){const{controller:n,data:o,_sorted:a}=t,r=n._cachedMeta.iScale;if(r&&e===r.axis&&"r"!==e&&a&&o.length){const t=r._reversePixels?le:re;if(!s)return t(o,e,i);if(n._sharedOptions){const s=o[0],n="function"==typeof s.getRange&&s.getRange(e);if(n){const s=t(o,e,i-n),a=t(o,e,i+n);return{lo:s.lo,hi:a.hi}}}}return{lo:0,hi:o.length-1}}function Oe(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t{t[r](n[a],s)&&o.push({element:t,datasetIndex:e,index:i}),t.inRange(n.x,n.y,s)&&(l=!0)})),i.intersect&&!l?[]:o}var Ee={modes:{index(t,e,i,s){const n=De(e,t),o=i.axis||"x",a=i.intersect?Ae(t,n,o,s):Le(t,n,o,!1,s),r=[];return a.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=a[0].index,i=t.data[e];i&&!i.skip&&r.push({element:i,datasetIndex:t.index,index:e})})),r):[]},dataset(t,e,i,s){const n=De(e,t),o=i.axis||"xy";let a=i.intersect?Ae(t,n,o,s):Le(t,n,o,!1,s);if(a.length>0){const e=a[0].datasetIndex,i=t.getDatasetMeta(e).data;a=[];for(let t=0;tAe(t,De(e,t),i.axis||"xy",s),nearest:(t,e,i,s)=>Le(t,De(e,t),i.axis||"xy",i.intersect,s),x:(t,e,i,s)=>Re(t,e,{axis:"x",intersect:i.intersect},s),y:(t,e,i,s)=>Re(t,e,{axis:"y",intersect:i.intersect},s)}};const Ie=new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/),ze=new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/);function Fe(t,e){const i=(""+t).match(Ie);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t}function Be(t,e){const i={},s=U(e),n=s?Object.keys(e):e,o=U(t)?s?i=>K(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=+o(t)||0;return i}function Ve(t){return Be(t,{top:"y",right:"x",bottom:"y",left:"x"})}function We(t){return Be(t,["topLeft","topRight","bottomLeft","bottomRight"])}function Ne(t){const e=Ve(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function He(t,e){t=t||{},e=e||bt.font;let i=K(t.size,e.size);"string"==typeof i&&(i=parseInt(i,10));let s=K(t.style,e.style);s&&!(""+s).match(ze)&&(console.warn('Invalid font style specified: "'+s+'"'),s="");const n={family:K(t.family,e.family),lineHeight:Fe(K(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:K(t.weight,e.weight),string:""};return n.string=Ut(n),n}function je(t,e,i,s){let n,o,a,r=!0;for(n=0,o=t.length;ni&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ye(t,e){return Object.assign(Object.create(t),e)}const Ue=["left","top","right","bottom"];function Xe(t,e){return t.filter((t=>t.pos===e))}function qe(t,e){return t.filter((t=>-1===Ue.indexOf(t.pos)&&t.box.axis===e))}function Ke(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function Ge(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Ue.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o{s[t]=Math.max(e[t],i[t])})),s}return s(t?["left","right"]:["top","bottom"])}function ei(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;ot.box.fullSize)),!0),s=Ke(Xe(e,"left"),!0),n=Ke(Xe(e,"right")),o=Ke(Xe(e,"top"),!0),a=Ke(Xe(e,"bottom")),r=qe(e,"x"),l=qe(e,"y");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Xe(e,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;Q(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),u=Object.assign({},n);Je(u,Ne(s));const f=Object.assign({maxPadding:u,w:o,h:a,x:n.left,y:n.top},n),g=Ge(l.concat(h),d);ei(r.fullSize,f,d,g),ei(l,f,d,g),ei(h,f,d,g)&&ei(l,f,d,g),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i("top"),t.x+=i("left"),i("right"),i("bottom")}(f),si(r.leftAndTop,f,d,g),f.x+=f.w,f.y+=f.h,si(r.rightAndBottom,f,d,g),t.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},Q(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})}))}};function oi(t,e=[""],i=t,s,n=(()=>t[0])){ct(s)||(s=mi("_fallback",t));const o={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:i,_fallback:s,_getTarget:n,override:n=>oi([n,...t],e,i,s)};return new Proxy(o,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>ci(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=mi(li(o,t),i),ct(n))return hi(t,n)?gi(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>xi(t).includes(e),ownKeys:t=>xi(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function ai(t,e,i,s){const n={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:ri(t,s),setContext:e=>ai(t,e,i,s),override:n=>ai(t.override(n),e,i,s)};return new Proxy(n,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>ci(t,e,(()=>function(t,e,i){const{_proxy:s,_context:n,_subProxy:o,_descriptors:a}=t;let r=s[e];dt(r)&&a.isScriptable(e)&&(r=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+t);r.add(t),e=e(o,a||s),r.delete(t),hi(t,e)&&(e=gi(n._scopes,n,t,e));return e}(e,r,t,i));Y(r)&&r.length&&(r=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_descriptors:r}=i;if(ct(o.index)&&s(t))e=e[o.index%e.length];else if(U(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const l of i){const i=gi(s,n,t,l);e.push(ai(i,o,a&&a[t],r))}}return e}(e,r,t,a.isIndexable));hi(e,r)&&(r=ai(r,n,o&&o[e],a));return r}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function ri(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:dt(i)?i:()=>i,isIndexable:dt(s)?s:()=>s}}const li=(t,e)=>t?t+ht(e):e,hi=(t,e)=>U(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function ci(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e))return t[e];const s=i();return t[e]=s,s}function di(t,e,i){return dt(t)?t(e,i):t}const ui=(t,e)=>!0===t?e:"string"==typeof t?lt(e,t):void 0;function fi(t,e,i,s,n){for(const o of e){const e=ui(i,o);if(e){t.add(e);const o=di(e._fallback,i,n);if(ct(o)&&o!==i&&o!==s)return o}else if(!1===e&&ct(s)&&i!==s)return null}return!1}function gi(t,e,i,s){const n=e._rootScopes,o=di(e._fallback,i,s),a=[...t,...n],r=new Set;r.add(s);let l=pi(r,a,i,o||i,s);return null!==l&&((!ct(o)||o===i||(l=pi(r,a,o,l,s),null!==l))&&oi(Array.from(r),[""],n,o,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const n=s[e];if(Y(n)&&U(i))return i;return n}(e,i,s))))}function pi(t,e,i,s,n){for(;i;)i=fi(t,e,i,s,n);return i}function mi(t,e){for(const i of e){if(!i)continue;const e=i[t];if(ct(e))return e}}function xi(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}const bi=Number.EPSILON||1e-14,_i=(t,e)=>e"x"===t?"y":"x";function vi(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=Vt(o,n),l=Vt(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function wi(t,e="x"){const i=yi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=_i(t,0);for(a=0;a!t.skip))),"monotone"===e.cubicInterpolationMode)wi(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o0===t||1===t,Pi=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*yt/i),Di=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*yt/i)+1,Ci={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*kt),easeOutSine:t=>Math.sin(t*kt),easeInOutSine:t=>-.5*(Math.cos(_t*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>Si(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>Si(t)?t:Pi(t,.075,.3),easeOutElastic:t=>Si(t)?t:Di(t,.075,.3),easeInOutElastic(t){const e=.1125;return Si(t)?t:t<.5?.5*Pi(2*t,e,.45):.5+.5*Di(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-Ci.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*Ci.easeInBounce(2*t):.5*Ci.easeOutBounce(2*t-1)+.5};function Oi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function Ai(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:"middle"===s?i<.5?t.y:e.y:"after"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function Ti(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=Oi(t,n,i),r=Oi(n,o,i),l=Oi(o,e,i),h=Oi(a,r,i),c=Oi(r,l,i);return Oi(h,c,i)}const Li=new Map;function Ri(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=Li.get(i);return s||(s=new Intl.NumberFormat(t,e),Li.set(i,s)),s}(e,i).format(t)}function Ei(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ii(t,e){let i,s;"ltr"!==e&&"rtl"!==e||(i=t.canvas.style,s=[i.getPropertyValue("direction"),i.getPropertyPriority("direction")],i.setProperty("direction",e,"important"),t.prevTextDirection=s)}function zi(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function Fi(t){return"angle"===t?{between:Ht,compare:Wt,normalize:Nt}:{between:Yt,compare:(t,e)=>t-e,normalize:t=>t}}function Bi({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Vi(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Fi(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Fi(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;hb||l(n,x,p)&&0!==r(n,x),v=()=>!b||0===r(o,p)||l(o,x,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==x&&(b=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Bi({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,x=p));return null!==_&&g.push(Bi({start:_,end:d,loop:u,count:a,style:f})),g}function Wi(t,e){const i=[],s=t.segments;for(let n=0;nn&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Hi(t,[{start:a,end:r,loop:o}],i,e);return Hi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,rnull===t||""===t;const Gi=!!Se&&{passive:!0};function Zi(t,e,i){t.canvas.removeEventListener(e,i,Gi)}function Ji(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function Qi(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||Ji(i.addedNodes,s),e=e&&!Ji(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function ts(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||Ji(i.removedNodes,s),e=e&&!Ji(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const es=new Map;let is=0;function ss(){const t=window.devicePixelRatio;t!==is&&(is=t,es.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function ns(t,i,s){const n=t.canvas,o=n&&pe(n);if(!o)return;const a=e(((t,e)=>{const i=o.clientWidth;s(t,e),i{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||a(i,s)}));return r.observe(o),function(t,e){es.size||window.addEventListener("resize",ss),es.set(t,e)}(t,a),r}function os(t,e,i){i&&i.disconnect(),"resize"===e&&function(t){es.delete(t),es.size||window.removeEventListener("resize",ss)}(t)}function as(t,i,s){const n=t.canvas,o=e((e=>{null!==t.ctx&&s(function(t,e){const i=qi[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t,(t=>{const e=t[0];return[e,e.offsetX,e.offsetY]}));return function(t,e,i){t.addEventListener(e,i,Gi)}(n,i,o),o}class rs extends Ui{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute("height"),n=t.getAttribute("width");if(t.$chartjs={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",i.boxSizing=i.boxSizing||"border-box",Ki(n)){const e=Pe(t,"width");void 0!==e&&(t.width=e)}if(Ki(s))if(""===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,"height");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e.$chartjs)return!1;const i=e.$chartjs.initial;["height","width"].forEach((t=>{const s=i[t];$(s)?e.removeAttribute(t):e.setAttribute(t,s)}));const s=i.style||{};return Object.keys(s).forEach((t=>{e.style[t]=s[t]})),e.width=e.width,delete e.$chartjs,!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:Qi,detach:ts,resize:ns}[e]||as;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:os,detach:os,resize:os}[e]||Zi)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return Me(t,e,i,s)}isAttached(t){const e=pe(t);return!(!e||!e.isConnected)}}function ls(t){return!ge()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?Xi:rs}var hs=Object.freeze({__proto__:null,_detectPlatform:ls,BasePlatform:Ui,BasicPlatform:Xi,DomPlatform:rs});const cs="transparent",ds={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=W(t||cs),n=s.valid&&W(e||cs);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class us{constructor(t,e,i,s){const n=e[i];s=je([t.to,s,n,t.from]);const o=je([t.from,n,s]);this._active=!0,this._fn=t.fn||ds[t.type||typeof o],this._easing=Ci[t.easing]||Ci.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=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=je([t.to,e,s,t.from]),this._from=je([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let t=0;t"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),bt.set("animations",{colors:{type:"color",properties:["color","borderColor","backgroundColor"]},numbers:{type:"number",properties:["x","y","borderWidth","radius","tension"]}}),bt.describe("animations",{_fallback:"animation"}),bt.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=>0|t}}}});class gs{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!U(t))return;const e=this._properties;Object.getOwnPropertyNames(t).forEach((i=>{const s=t[i];if(!U(s))return;const n={};for(const t of fs)n[t]=s[t];(Y(s.properties)&&s.properties||[i]).forEach((t=>{t!==i&&e.has(t)||e.set(t,n)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if("$"===l.charAt(0))continue;if("options"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new us(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(a.add(this._chart,i),!0):void 0}}function ps(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function ms(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n0||!i&&e<0)return n.index}return null}function vs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;ti[t].axis===e)).shift()}function Ms(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i]}}}const ks=t=>"reset"===t||"none"===t,Ss=(t,e)=>e?t:Object.assign({},t);class Ps{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.$context=void 0,this._syncList=[],this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=bs(t.vScale,t),this.addElements()}updateIndex(t){this.index!==t&&Ms(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>"x"===t?e:"r"===t?s:i,n=e.xAxisID=K(i.xAxisID,ws(t,"x")),o=e.yAxisID=K(i.yAxisID,ws(t,"y")),a=e.rAxisID=K(i.rAxisID,ws(t,"r")),r=e.indexAxis,l=e.iAxisID=s(r,n,o,a),h=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(l),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&&ue(this._data,this),t._stacked&&Ms(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(U(e))this._data=function(t){const e=Object.keys(t),i=new Array(e.length);let s,n,o;for(s=0,n=e.length;s0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,h=s;else{h=Y(s[t])?this.parseArrayData(i,s,t,e):U(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const n=()=>null===l[a]||d&&l[a]t&&!e.hidden&&e._stacked&&{keys:ms(i,!0),values:null})(e,i,this.chart),l={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:h,max:c}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(a);let d,u;function f(){u=s[d];const e=u[a.axis];return!X(u[t.axis])||h>e||c=0;--d)if(!f()){this.updateRangeFromParsed(l,t,u,r);break}return l}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s=0&&tthis.getContext(i,s)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Ss(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new gs(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ks(t)||this.chart._animationsDisabled}updateElement(t,e,i,s){ks(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!ks(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"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,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}Ds.defaults={},Ds.defaultRoutes=void 0;const Cs={values:t=>Y(t)?t:""+t,numeric(t,e,i){if(0===t)return"0";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n="scientific"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=Dt(Math.abs(o)),r=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),Ri(t,s,l)},logarithmic(t,e,i){if(0===t)return"0";const s=t/Math.pow(10,Math.floor(Dt(t)));return 1===s||2===s||5===s?Cs.numeric.call(this,t,e,i):""}};var Os={formatters:Cs};function As(t,e){const i=t.options.ticks,s=i.maxTicksLimit||function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),n=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;is)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;nn)return e}return Math.max(n,1)}(n,e,s);if(o>0){let t,i;const s=o>1?Math.round((r-a)/(o-1)):null;for(Ts(e,l,h,$(s)?0:a-s,a),t=0,i=o-1;te.lineWidth,tickColor:(t,e)=>e.color,offset:!1,borderDash:[],borderDashOffset:0,borderWidth:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:Os.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),bt.route("scale.ticks","color","","color"),bt.route("scale.grid","color","","borderColor"),bt.route("scale.grid","borderColor","","borderColor"),bt.route("scale.title","color","","color"),bt.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t}),bt.describe("scales",{_fallback:"scale"}),bt.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t});const Ls=(t,e,i)=>"top"===e||"left"===e?t[e]+i:t[e]-i;function Rs(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;oa+r)))return h}function Is(t){return t.drawTicks?t.tickLength:0}function zs(t,e){if(!t.display)return 0;const i=He(t.font,e),s=Ne(t.padding);return(Y(t.text)?t.text.length:1)*i.lineHeight+s.height}function Fs(t,e,i){let n=s(t);return(i&&"right"!==e||!i&&"right"===e)&&(n=(t=>"left"===t?"right":"right"===t?"left":t)(n)),n}class Bs extends Ds{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=q(t,Number.POSITIVE_INFINITY),e=q(e,Number.NEGATIVE_INFINITY),i=q(i,Number.POSITIVE_INFINITY),s=q(s,Number.NEGATIVE_INFINITY),{min:q(t,i),max:q(e,s),minDefined:X(t),maxDefined:X(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;rs?s:i,s=n&&i>s?i:s,{min:q(i,q(s,i)),max:q(s,q(i,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||[]}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){J(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=$e(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=jt(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Is(t.grid)-e.padding-zs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=zt(Math.min(Math.asin(jt((h.highest.height+6)/o,-1,1)),Math.asin(jt(a/r,-1,1))-Math.asin(jt(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){J(this.options.afterCalculateLabelRotation,[this])}beforeFit(){J(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=zs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Is(n)+o):(t.height=this.maxHeight,t.width=Is(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=It(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}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,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l="top"!==a&&"x"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):"start"===n?d=e.width:"end"===n?c=t.width:(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;"start"===n?(i=0,s=t.height):"end"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_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(){J(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n({width:n[t]||0,height:o[t]||0});return{first:v(0),last:v(e-1),widest:v(_),highest:v(y),widths:n,heights:o}}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 $t(this._alignToPixels?Kt(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*s?a/i:r/s:r*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:o}=s,a=n.offset,r=this.isHorizontal(),l=this.ticks.length+(a?1:0),h=Is(n),c=[],d=n.setContext(this.getContext()),u=d.drawBorder?d.borderWidth:0,f=u/2,g=function(t){return Kt(i,t,u)};let p,m,x,b,_,y,v,w,M,k,S,P;if("top"===o)p=g(this.bottom),y=this.bottom-h,w=p-f,k=g(t.top)+f,P=t.bottom;else if("bottom"===o)p=g(this.top),k=t.top,P=g(t.bottom)-f,y=p+f,w=this.top+h;else if("left"===o)p=g(this.right),_=this.right-h,v=p-f,M=g(t.left)+f,S=t.right;else if("right"===o)p=g(this.left),M=t.left,S=g(t.right)-f,_=p+f,v=this.left+h;else if("x"===e){if("center"===o)p=g((t.top+t.bottom)/2+.5);else if(U(o)){const t=Object.keys(o)[0],e=o[t];p=g(this.chart.scales[t].getPixelForValue(e))}k=t.top,P=t.bottom,y=p+f,w=y+h}else if("y"===e){if("center"===o)p=g((t.left+t.right)/2);else if(U(o)){const t=Object.keys(o)[0],e=o[t];p=g(this.chart.scales[t].getPixelForValue(e))}_=p-f,v=_-h,M=t.left,S=t.right}const D=K(s.ticks.maxTicksLimit,l),C=Math.max(1,Math.ceil(l/D));for(m=0;me.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:i+1,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let n,o;for(n=0,o=e.length;n{const s=i.split("."),n=s.pop(),o=[t].concat(s).join("."),a=e[i].split("."),r=a.pop(),l=a.join(".");bt.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&bt.describe(e,t.descriptors)}(t,o,i),this.override&&bt.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in bt[s]&&(delete bt[s][i],this.override&&delete gt[i])}}var Ws=new class{constructor(){this.controllers=new Vs(Ps,"datasets",!0),this.elements=new Vs(Ds,"elements"),this.plugins=new Vs(Object,"plugins"),this.scales=new Vs(Bs,"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,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):Q(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=ht(t);J(i["before"+s],[],i),e[t](i),J(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;et.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function Hs(t,e){return e||!1!==t?!0===t?{}:t:null}function js(t,e,i,s){const n=t.pluginScopeKeys(e),o=t.getOptionScopes(i,n);return t.createResolver(o,s,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function $s(t,e){const i=bt.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||"x"}function Ys(t,e){return"x"===t||"y"===t?t:e.axis||("top"===(i=e.position)||"bottom"===i?"x":"left"===i||"right"===i?"y":void 0)||t.charAt(0).toLowerCase();var i}function Us(t){const e=t.options||(t.options={});e.plugins=K(e.plugins,{}),e.scales=function(t,e){const i=gt[t.type]||{scales:{}},s=e.scales||{},n=$s(t.type,e),o=Object.create(null),a=Object.create(null);return Object.keys(s).forEach((t=>{const e=s[t];if(!U(e))return console.error(`Invalid scale configuration for scale: ${t}`);if(e._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${t}`);const r=Ys(t,e),l=function(t,e){return t===e?"_index_":"_value_"}(r,n),h=i.scales||{};o[r]=o[r]||t,a[t]=ot(Object.create(null),[{axis:r},e,h[r],h[l]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,r=i.indexAxis||$s(n,e),l=(gt[n]||{}).scales||{};Object.keys(l).forEach((t=>{const e=function(t,e){let i=t;return"_index_"===t?i=e:"_value_"===t&&(i="x"===e?"y":"x"),i}(t,r),n=i[e+"AxisID"]||o[e]||e;a[n]=a[n]||Object.create(null),ot(a[n],[{axis:e},s[n],l[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];ot(e,[bt.scales[e.type],bt.scale])})),a}(t,e)}function Xs(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const qs=new Map,Ks=new Set;function Gs(t,e){let i=qs.get(t);return i||(i=e(),qs.set(t,i),Ks.add(i)),i}const Zs=(t,e,i)=>{const s=lt(e,i);void 0!==s&&t.add(s)};class Js{constructor(t){this._config=function(t){return(t=t||{}).data=Xs(t.data),Us(t),t}(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=Xs(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(),Us(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Gs(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return Gs(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return Gs(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return Gs(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>Zs(r,t,e)))),e.forEach((t=>Zs(r,s,t))),e.forEach((t=>Zs(r,gt[n]||{},t))),e.forEach((t=>Zs(r,bt,t))),e.forEach((t=>Zs(r,pt,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),Ks.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,gt[e]||{},bt.datasets[e]||{},{type:e},bt,pt]}resolveNamedOptions(t,e,i,s=[""]){const n={$shared:!0},{resolver:o,subPrefixes:a}=Qs(this._resolverCache,t,s);let r=o;if(function(t,e){const{isScriptable:i,isIndexable:s}=ri(t);for(const n of e){const e=i(n),o=s(n),a=(o||e)&&t[n];if(e&&(dt(a)||tn(a))||o&&Y(a))return!0}return!1}(o,e)){n.$shared=!1;r=ai(o,i=dt(i)?i():i,this.createResolver(t,i,a))}for(const t of e)n[t]=r[t];return n}createResolver(t,e,i=[""],s){const{resolver:n}=Qs(this._resolverCache,t,i);return U(e)?ai(n,e,void 0,s):n}}function Qs(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:oi(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes("hover")))},s.set(n,o)}return o}const tn=t=>U(t)&&Object.getOwnPropertyNames(t).reduce(((e,i)=>e||dt(t[i])),!1);const en=["top","bottom","left","right","chartArea"];function sn(t,e){return"top"===t||"bottom"===t||-1===en.indexOf(t)&&"x"===e}function nn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function on(t){const e=t.chart,i=e.options.animation;e.notifyPlugins("afterRender"),J(i&&i.onComplete,[t],e)}function an(t){const e=t.chart,i=e.options.animation;J(i&&i.onProgress,[t],e)}function rn(t){return ge()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const ln={},hn=t=>{const e=rn(t);return Object.values(ln).filter((t=>t.canvas===e)).pop()};function cn(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class dn{constructor(t,e){const s=this.config=new Js(e),n=rn(t),o=hn(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas can be reused.");const r=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ls(n)),this.platform.updateConfig(s);const l=this.platform.acquireContext(n,r.aspectRatio),h=l&&l.canvas,c=h&&h.height,d=h&&h.width;this.id=j(),this.ctx=l,this.canvas=h,this.width=d,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 Ns,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=i((t=>this.update(t)),r.resizeDelay||0),this._dataChanges=[],ln[this.id]=this,l&&h?(a.listen(this,"complete",on),a.listen(this,"progress",an),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:s,_aspectRatio:n}=this;return $(t)?e&&n?n:s?i/s: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}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Gt(this.canvas,this.ctx),this}stop(){return a.stop(this),this}resize(t,e){a.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins("resize",{size:o}),J(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){Q(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=Ys(t,i),n="r"===s,o="x"===s;return{options:i,dposition:n?"chartArea":o?"bottom":"left",dtype:n?"radialLinear":o?"category":"linear"}})))),Q(n,(e=>{const n=e.options,o=n.id,a=Ys(o,n),r=K(n.type,e.dtype);void 0!==n.position&&sn(n.position,a)===sn(e.dposition)||(n.position=e.dposition),s[o]=!0;let l=null;if(o in i&&i[o].type===r)l=i[o];else{l=new(Ws.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[l.id]=l}l.init(n,t)})),Q(s,((t,e)=>{t||delete i[e]})),Q(i,(t=>{ni.configure(this,t,t.options),ni.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;te.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(nn("z","_idx"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){Q(this.scales,(t=>{ni.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);ut(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){cn(t,s,"_removeElements"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),s=i(0);for(let t=1;tt.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;ni.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],Q(this.boxes,(t=>{i&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,n=this.chartArea,o={meta:t,index:t.index,cancelable:!0};!1!==this.notifyPlugins("beforeDatasetDraw",o)&&(s&&Qt(e,{left:!1===i.left?0:n.left-i.left,right:!1===i.right?this.width:n.right+i.right,top:!1===i.top?0:n.top-i.top,bottom:!1===i.bottom?this.height:n.bottom+i.bottom}),t.controller.draw(),s&&te(e),o.cancelable=!1,this.notifyPlugins("afterDatasetDraw",o))}getElementsAtEventForMode(t,e,i,s){const n=Ee.modes[e];return"function"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={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},i.push(s)),s}getContext(){return this.$context||(this.$context=Ye(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return"boolean"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);ct(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s: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(),a.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};Q(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",n),i("detach",o)};o=()=>{this.attached=!1,s("resize",n),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){Q(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},Q(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let n,o,a,r;for("dataset"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller["_"+s+"DatasetHoverStyle"]()),a=0,r=t.length;a{const i=this.getDatasetMeta(t);if(!i)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:i.data[e],index:e}}));!tt(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:Jt(t,this.chartArea,this._minPadding)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=ft(t),l=function(t,e,i,s){return i&&"mouseout"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,J(n.onHover,[t,a,this],this),r&&J(n.onClick,[t,a,this],this));const h=!tt(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if("mouseout"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}const un=()=>Q(dn.instances,(t=>t._plugins.invalidate())),fn=!0;function gn(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}Object.defineProperties(dn,{defaults:{enumerable:fn,value:bt},instances:{enumerable:fn,value:ln},overrides:{enumerable:fn,value:gt},registry:{enumerable:fn,value:Ws},version:{enumerable:fn,value:"3.7.1"},getChart:{enumerable:fn,value:hn},register:{enumerable:fn,value:(...t)=>{Ws.add(...t),un()}},unregister:{enumerable:fn,value:(...t)=>{Ws.remove(...t),un()}}});class pn{constructor(t){this.options=t||{}}formats(){return gn()}parse(t,e){return gn()}format(t,e){return gn()}add(t,e,i){return gn()}diff(t,e,i){return gn()}startOf(t,e,i){return gn()}endOf(t,e){return gn()}}pn.override=function(t){Object.assign(pn.prototype,t)};var mn={_date:pn};function xn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;et-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(ct(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;sMath.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function _n(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;ht.x,i="left",s="right"):(e=t.base=i?1:-1)}(c,e,o)*n,d===o&&(p-=c/2),h=p+c),p===e.getPixelForValue(o)){const t=Ct(c)*e.getLineWidthForValue(o)/2;p+=t,c-=t}return{size:c,base:p,head:h,center:h+c/2}}_calculateBarIndexPixels(t,e){const i=e.scale,s=this.options,n=s.skipNull,o=K(s.maxBarThickness,1/0);let a,r;if(e.grouped){const i=n?this._getStackCount(t):e.stackCount,l="flex"===s.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,{xScale:i,yScale:s}=e,n=this.getParsed(t),o=i.getLabelForValue(n.x),a=s.getLabelForValue(n.y),r=n._custom;return{label:e.label,value:"("+o+", "+a+(r?", "+r:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a}=this._cachedMeta,r=this.resolveDataElementOptions(e,s),l=this.getSharedOptions(r),h=this.includeOptions(s,l),c=o.axis,d=a.axis;for(let r=e;r""}}}};class Dn extends Ps{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,o,a=t=>+i[t];if(U(i[t])){const{key:t="value"}=this._parsing;a=e=>+lt(i[e],t)}for(n=t,o=t+e;nHt(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>Ht(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(kt,c,u),x=g(_t,h,d),b=g(_t+kt,c,u);s=(p-x)/2,n=(m-b)/2,o=-(p+x)/2,a=-(m+b)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(c,h,r),p=(i.width-o)/d,m=(i.height-o)/u,x=Math.max(Math.min(p,m)/2,0),b=Z(this.options.radius,x),_=(b-Math.max(b*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=f*b,this.offsetY=g*b,s.total=this.calculateTotal(),this.outerRadius=b-_*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-_*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/yt)}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,f=this.resolveDataElementOptions(e,s),g=this.getSharedOptions(f),p=this.includeOptions(s,g);let m,x=this._getRotation();for(m=0;m0&&!isNaN(t)?yt*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=Ri(e._parsed[t],i.options.locale);return{label:s[t]||"",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s"spacing"!==t,_indexable:t=>"spacing"!==t},Dn.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i}}=t.legend.options;return e.labels.map(((e,s)=>{const n=t.getDatasetMeta(0).controller.getStyle(s);return{text:e,fillStyle:n.backgroundColor,strokeStyle:n.borderColor,lineWidth:n.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(s),index:s}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}},tooltip:{callbacks:{title:()=>"",label(t){let e=t.label;const i=": "+t.formattedValue;return Y(e)?(e=e.slice(),e[0]+=i):e+=i,e}}}}};class Cn extends Ps{initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:n}=e,o=this.chart._animationsDisabled;let{start:a,count:r}=function(t,e,i){const s=e.length;let n=0,o=s;if(t._sorted){const{iScale:a,_parsed:r}=t,l=a.axis,{min:h,max:c,minDefined:d,maxDefined:u}=a.getUserBounds();d&&(n=jt(Math.min(re(r,a.axis,h).lo,i?s:re(e,l,a.getPixelForValue(h)).lo),0,s-1)),o=u?jt(Math.max(re(r,a.axis,c).hi+1,i?0:re(e,l,a.getPixelForValue(c)).hi+1),n,s)-n:s-n}return{start:n,count:o}}(e,s,o);this._drawStart=a,this._drawCount=r,function(t){const{xScale:e,yScale:i,_scaleRanges:s}=t,n={xmin:e.min,xmax:e.max,ymin:i.min,ymax:i.max};if(!s)return t._scaleRanges=n,!0;const o=s.xmin!==e.min||s.xmax!==e.max||s.ymin!==i.min||s.ymax!==i.max;return Object.assign(s,n),o}(e)&&(a=0,r=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!n._decimated,i.points=s;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(i,void 0,{animated:!o,options:l},t),this.updateElements(s,a,r,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a,_stacked:r,_dataset:l}=this._cachedMeta,h=this.resolveDataElementOptions(e,s),c=this.getSharedOptions(h),d=this.includeOptions(s,c),u=o.axis,f=a.axis,{spanGaps:g,segment:p}=this.options,m=Tt(g)?g:Number.POSITIVE_INFINITY,x=this.chart._animationsDisabled||n||"none"===s;let b=e>0&&this.getParsed(e-1);for(let h=e;h0&&i[u]-b[u]>m,p&&(g.parsed=i,g.raw=l.data[h]),d&&(g.options=c||this.resolveDataElementOptions(h,e.active?"active":s)),x||this.updateElement(e,h,g,s),b=i}this.updateSharedOptions(c,s,h)}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}Cn.id="line",Cn.defaults={datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1},Cn.overrides={scales:{_index_:{type:"category"},_value_:{type:"linear"}}};class On extends Ps{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=Ri(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:n}}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=this.getDataset(),r=o.options.animation,l=this._cachedMeta.rScale,h=l.xCenter,c=l.yCenter,d=l.getIndexAngle(0)-.5*_t;let u,f=d;const g=360/this.countVisibleElements();for(u=0;u{!isNaN(t.data[s])&&this.chart.getDataVisibility(s)&&i++})),i}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?It(this.resolveDataElementOptions(t,e).angle||i):0}}On.id="polarArea",On.defaults={dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0},On.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i}}=t.legend.options;return e.labels.map(((e,s)=>{const n=t.getDatasetMeta(0).controller.getStyle(s);return{text:e,fillStyle:n.backgroundColor,strokeStyle:n.borderColor,lineWidth:n.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(s),index:s}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}},tooltip:{callbacks:{title:()=>"",label:t=>t.chart.data.labels[t.dataIndex]+": "+t.formattedValue}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};class An extends Dn{}An.id="pie",An.defaults={cutout:0,rotation:0,circumference:360,radius:"100%"};class Tn extends Ps{getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(i[e.axis])}}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this.getDataset(),o=this._cachedMeta.rScale,a="reset"===s;for(let r=e;r"",label:t=>"("+t.label+", "+t.formattedValue+")"}}},scales:{x:{type:"linear"},y:{type:"linear"}}};var Rn=Object.freeze({__proto__:null,BarController:Sn,BubbleController:Pn,DoughnutController:Dn,LineController:Cn,PolarAreaController:On,PieController:An,RadarController:Tn,ScatterController:Ln});function En(t,e,i){const{startAngle:s,pixelMargin:n,x:o,y:a,outerRadius:r,innerRadius:l}=e;let h=n/r;t.beginPath(),t.arc(o,a,r,s-h,i+h),l>n?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+kt,s-kt),t.closePath(),t.clip()}function In(t,e,i,s){const n=Be(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return jt(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:jt(n.innerStart,0,a),innerEnd:jt(n.innerEnd,0,a)}}function zn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Fn(t,e,i,s,n){const{x:o,y:a,startAngle:r,pixelMargin:l,innerRadius:h}=e,c=Math.max(e.outerRadius+s+i-l,0),d=h>0?h+s+i+l:0;let u=0;const f=n-r;if(s){const t=((h>0?h-s:0)+(c>0?c-s:0))/2;u=(f-(0!==t?f*t/(t+s):f))/2}const g=(f-Math.max(.001,f*c-i/_t)/c)/2,p=r+g+u,m=n-g-u,{outerStart:x,outerEnd:b,innerStart:_,innerEnd:y}=In(e,d,c,m-p),v=c-x,w=c-b,M=p+x/v,k=m-b/w,S=d+_,P=d+y,D=p+_/S,C=m-y/P;if(t.beginPath(),t.arc(o,a,c,M,k),b>0){const e=zn(w,k,o,a);t.arc(e.x,e.y,b,k,m+kt)}const O=zn(P,m,o,a);if(t.lineTo(O.x,O.y),y>0){const e=zn(P,C,o,a);t.arc(e.x,e.y,y,m+kt,C+Math.PI)}if(t.arc(o,a,d,m-y/d,p+_/d,!0),_>0){const e=zn(S,D,o,a);t.arc(e.x,e.y,_,D+Math.PI,p-kt)}const A=zn(v,p,o,a);if(t.lineTo(A.x,A.y),x>0){const e=zn(v,M,o,a);t.arc(e.x,e.y,x,p-kt,M)}t.closePath()}function Bn(t,e,i,s,n){const{options:o}=e,{borderWidth:a,borderJoinStyle:r}=o,l="inner"===o.borderAlign;a&&(l?(t.lineWidth=2*a,t.lineJoin=r||"round"):(t.lineWidth=a,t.lineJoin=r||"bevel"),e.fullCircles&&function(t,e,i){const{x:s,y:n,startAngle:o,pixelMargin:a,fullCircles:r}=e,l=Math.max(e.outerRadius-a,0),h=e.innerRadius+a;let c;for(i&&En(t,e,o+yt),t.beginPath(),t.arc(s,n,h,o+yt,o,!0),c=0;c=yt||Ht(n,a,r),f=Yt(o,l+d,h+d);return u&&f}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius","circumference"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/2,n=(e.spacing||0)/2;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=i>yt?Math.floor(i/yt):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();let o=0;if(s){o=s/2;const e=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(e)*o,Math.sin(e)*o),this.circumference>=_t&&(o=s)}t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor;const a=function(t,e,i,s){const{fullCircles:n,startAngle:o,circumference:a}=e;let r=e.endAngle;if(n){Fn(t,e,i,s,o+yt);for(let e=0;er&&o>r;return{count:s,start:l,loop:e.loop,ilen:h(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[b(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[b(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(ig&&(g=i),m=(x*m+e)/++x):(_(),t.lineTo(e,i),u=s,x=0,f=g=i),p=i}_()}function Yn(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||i)?$n:jn}Vn.id="arc",Vn.defaults={borderAlign:"center",borderColor:"#fff",borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0},Vn.defaultRoutes={backgroundColor:"backgroundColor"};const Un="function"==typeof Path2D;function Xn(t,e,i,s){Un&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Wn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=Yn(e);for(const r of n)Wn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class qn extends Ds{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 i=this.options;if((i.tension||"monotone"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;ki(this._points,i,t,s,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=Ni(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,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Wi(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?Ai:t.tension||"monotone"===t.cubicInterpolationMode?Ti:Oi}(i);let l,h;for(l=0,h=o.length;l"borderDash"!==t&&"fill"!==t};class Gn extends Ds{constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.options,{x:n,y:o}=this.getProps(["x","y"],i);return Math.pow(t-n,2)+Math.pow(e-o,2){oo(t)}))}var ro={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void ao(t);const s=t.width;t.data.datasets.forEach(((e,n)=>{const{_data:o,indexAxis:a}=e,r=t.getDatasetMeta(n),l=o||e.data;if("y"===je([a,t.options.indexAxis]))return;if("line"!==r.type)return;const h=t.scales[r.xAxisID];if("linear"!==h.type&&"time"!==h.type)return;if(t.options.parsing)return;let{start:c,count:d}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=jt(re(e,o.axis,a).lo,0,i-1)),s=h?jt(re(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(r,l);if(d<=(i.threshold||4*s))return void oo(e);let u;switch($(o)&&(e._data=l,delete e.data,Object.defineProperty(e,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case"lttb":u=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;cu&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(l,c,d,s,i);break;case"min-max":u=function(t,e,i,s){let n,o,a,r,l,h,c,d,u,f,g=0,p=0;const m=[],x=e+i-1,b=t[e].x,_=t[x].x-b;for(n=e;nf&&(f=r,c=n),g=(p*g+o.x)/++p;else{const i=n-1;if(!$(h)&&!$(c)){const e=Math.min(h,c),s=Math.max(h,c);e!==d&&e!==i&&m.push({...t[e],x:g}),s!==d&&s!==i&&m.push({...t[s],x:g})}n>0&&i!==d&&m.push(t[i]),m.push(o),l=e,p=0,u=f=r,h=c=d=n}}return m}(l,c,d,s);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=u}))},destroy(t){ao(t)}};function lo(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=K(i&&i.target,i);return void 0===s&&(s=!!e.backgroundColor),!1!==s&&null!==s&&(!0===s?"origin":s)}(t);if(U(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return X(n)&&Math.floor(n)===n?("-"!==s[0]&&"+"!==s[0]||(n=e+n),!(n===e||n<0||n>=i)&&n):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}class ho{constructor(t){this.x=t.x,this.y=t.y,this.radius=t.radius}pathSegment(t,e,i){const{x:s,y:n,radius:o}=this;return e=e||{start:0,end:yt},t.arc(s,n,o,e.end,e.start,!0),!i.bounds}interpolate(t){const{x:e,y:i,radius:s}=this,n=t.angle;return{x:e+Math.cos(n)*s,y:i+Math.sin(n)*s,angle:n}}}function co(t){return(t.scale||{}).getPointPositionForValue?function(t){const{scale:e,fill:i}=t,s=e.options,n=e.getLabels().length,o=[],a=s.reverse?e.max:e.min,r=s.reverse?e.min:e.max;let l,h,c;if(c="start"===i?a:"end"===i?r:U(i)?i.value:e.getBaseValue(),s.grid.circular)return h=e.getPointPositionForValue(0,a),new ho({x:h.x,y:h.y,radius:e.getDistanceFromCenterForValue(c)});for(l=0;lt;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function fo(t,e,i){const s=[];for(let n=0;n{e=uo(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new qn({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function xo(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!X(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function bo(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[uo(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(h.x,i),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(c.x,i)}t.lineTo(e.first().x,i),t.closePath(),t.clip()}function _o(t,e,i,s){if(s)return;let n=e[t],o=i[t];return"angle"===t&&(n=Nt(n),o=Nt(o)),{property:t,start:n,end:o}}function yo(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function vo(t,e,i){const{top:s,bottom:n}=e.chart.chartArea,{property:o,start:a,end:r}=i||{};"x"===o&&(t.beginPath(),t.rect(a,s,r-a,n-s),t.clip())}function wo(t,e,i,s){const n=e.interpolate(i,s);n&&t.lineTo(n.x,n.y)}function Mo(t,e){const{line:i,target:s,property:n,color:o,scale:a}=e,r=function(t,e,i){const s=t.segments,n=t.points,o=e.points,a=[];for(const t of s){let{start:s,end:r}=t;r=uo(s,r,n);const l=_o(i,n[s],n[r],t.loop);if(!e.segments){a.push({source:t,target:l,start:n[s],end:n[r]});continue}const h=Wi(e,l);for(const e of h){const s=_o(i,o[e.start],o[e.end],e.loop),r=Vi(t,n,s);for(const t of r)a.push({source:t,target:e,start:{[i]:yo(l,s,"start",Math.max)},end:{[i]:yo(l,s,"end",Math.min)}})}}return a}(i,s,n);for(const{source:e,target:l,start:h,end:c}of r){const{style:{backgroundColor:r=o}={}}=e,d=!0!==s;t.save(),t.fillStyle=r,vo(t,a,d&&_o(n,h,c)),t.beginPath();const u=!!i.pathSegment(t,e);let f;if(d){u?t.closePath():wo(t,s,c,n);const e=!!s.pathSegment(t,l,{move:u,reverse:!0});f=u&&e,f||wo(t,s,h,n)}t.closePath(),t.fill(f?"evenodd":"nonzero"),t.restore()}}function ko(t,e,i){const s=po(e),{line:n,scale:o,axis:a}=e,r=n.options,l=r.fill,h=r.backgroundColor,{above:c=h,below:d=h}=l||{};s&&n.points.length&&(Qt(t,i),function(t,e){const{line:i,target:s,above:n,below:o,area:a,scale:r}=e,l=i._loop?"angle":e.axis;t.save(),"x"===l&&o!==n&&(bo(t,s,a.top),Mo(t,{line:i,target:s,color:n,scale:r,property:l}),t.restore(),t.save(),bo(t,s,a.bottom)),Mo(t,{line:i,target:s,color:o,scale:r,property:l}),t.restore()}(t,{line:n,target:s,above:c,below:d,area:i,scale:o,axis:a}),te(t))}var So={id:"filler",afterDatasetsUpdate(t,e,i){const s=(t.data.datasets||[]).length,n=[];let o,a,r,l;for(a=0;a=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&ko(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if("beforeDatasetsDraw"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;i&&ko(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;s&&!1!==s.fill&&"beforeDatasetDraw"===i.drawTime&&ko(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Po=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class Do extends Ds{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,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,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=J(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=He(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=Po(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,n,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign="left",n.textBaseline="middle";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const p=i+e/2+n.measureText(t.text).width;o>0&&u+s+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:s},d=Math.max(d,p),u+=s+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:o}}=this,a=Ei(o,this.left,this.width);if(this.isHorizontal()){let o=0,r=n(i,this.left+s,this.right-this.lineWidths[o]);for(const l of e)o!==l.row&&(o=l.row,r=n(i,this.left+s,this.right-this.lineWidths[o])),l.top+=this.top+t+s,l.left=a.leftForLtr(a.x(r),l.width),r+=l.width+s}else{let o=0,r=n(i,this.top+t+s,this.bottom-this.columnSizes[o].height);for(const l of e)l.col!==o&&(o=l.col,r=n(i,this.top+t+s,this.bottom-this.columnSizes[o].height)),l.top=r,l.left+=this.left+s,l.left=a.leftForLtr(a.x(l.left),l.width),r+=l.height+s}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Qt(t,this),this._draw(),te(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:a,labels:r}=t,l=bt.color,h=Ei(t.rtl,this.left,this.width),c=He(r.font),{color:d,padding:u}=r,f=c.size,g=f/2;let p;this.drawTitle(),s.textAlign=h.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:m,boxHeight:x,itemHeight:b}=Po(r,f),_=this.isHorizontal(),y=this._computeTitleHeight();p=_?{x:n(a,this.left+u,this.right-i[0]),y:this.top+u+y,line:0}:{x:this.left+u,y:n(a,this.top+y+u,this.bottom-e[0].height),line:0},Ii(this.ctx,t.textDirection);const v=b+u;this.legendItems.forEach(((w,M)=>{s.strokeStyle=w.fontColor||d,s.fillStyle=w.fontColor||d;const k=s.measureText(w.text).width,S=h.textAlign(w.textAlign||(w.textAlign=r.textAlign)),P=m+g+k;let D=p.x,C=p.y;h.setWidth(this.width),_?M>0&&D+P+u>this.right&&(C=p.y+=v,p.line++,D=p.x=n(a,this.left+u,this.right-i[p.line])):M>0&&C+v>this.bottom&&(D=p.x=D+e[p.line].width+u,p.line++,C=p.y=n(a,this.top+y+u,this.bottom-e[p.line].height));!function(t,e,i){if(isNaN(m)||m<=0||isNaN(x)||x<0)return;s.save();const n=K(i.lineWidth,1);if(s.fillStyle=K(i.fillStyle,l),s.lineCap=K(i.lineCap,"butt"),s.lineDashOffset=K(i.lineDashOffset,0),s.lineJoin=K(i.lineJoin,"miter"),s.lineWidth=n,s.strokeStyle=K(i.strokeStyle,l),s.setLineDash(K(i.lineDash,[])),r.usePointStyle){const o={radius:m*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},a=h.xPlus(t,m/2);Zt(s,o,a,e+g)}else{const o=e+Math.max((f-x)/2,0),a=h.leftForLtr(t,m),r=We(i.borderRadius);s.beginPath(),Object.values(r).some((t=>0!==t))?oe(s,{x:a,y:o,w:m,h:x,radius:r}):s.rect(a,o,m,x),s.fill(),0!==n&&s.stroke()}s.restore()}(h.x(D),C,w),D=o(S,D+m+g,_?D+P:this.right,t.rtl),function(t,e,i){se(s,i.text,t,e+b/2,c,{strikethrough:i.hidden,textAlign:h.textAlign(i.textAlign)})}(h.x(D),C,w),_?p.x+=P+u:p.y+=v})),zi(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=He(e.font),o=Ne(e.padding);if(!e.display)return;const a=Ei(t.rtl,this.left,this.width),r=this.ctx,l=e.position,h=i.size/2,c=o.top+h;let d,u=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),d=this.top+c,u=n(t.align,u,this.right-f);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);d=c+n(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const g=n(l,u,u+f);r.textAlign=a.textAlign(s(l)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=i.string,se(r,e.text,g,d,i)}_computeTitleHeight(){const t=this.options.title,e=He(t.font),i=Ne(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(Yt(t,this.left,this.right)&&Yt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;it.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const a=t.controller.getStyle(i?0:void 0),r=Ne(a.borderWidth);return{text:e[t.index].label,fillStyle:a.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:(r.width+r.height)/4,strokeStyle:a.borderColor,pointStyle:s||a.pointStyle,rotation:a.rotation,textAlign:n||a.textAlign,borderRadius:0,datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class Oo extends Ds{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=Y(i.text)?i.text.length:1;this._padding=Ne(i.padding);const n=s*He(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=n:this.width=n}isHorizontal(){const t=this.options.position;return"top"===t||"bottom"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:o,options:a}=this,r=a.align;let l,h,c,d=0;return this.isHorizontal()?(h=n(r,i,o),c=e+t,l=o-i):("left"===a.position?(h=i+t,c=n(r,s,e),d=-.5*_t):(h=o-t,c=n(r,e,s),d=.5*_t),l=s-e),{titleX:h,titleY:c,maxWidth:l,rotation:d}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=He(e.font),n=i.lineHeight/2+this._padding.top,{titleX:o,titleY:a,maxWidth:r,rotation:l}=this._drawArgs(n);se(t,e.text,0,0,i,{color:e.color,maxWidth:r,rotation:l,textAlign:s(e.align),textBaseline:"middle",translation:[o,a]})}}var Ao={id:"title",_element:Oo,start(t,e,i){!function(t,e){const i=new Oo({ctx:t.ctx,options:e,chart:t});ni.configure(t,i,e),ni.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ni.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ni.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const To=new WeakMap;var Lo={id:"subtitle",start(t,e,i){const s=new Oo({ctx:t.ctx,options:i,chart:t});ni.configure(t,s,i),ni.addBox(t,s),To.set(t,s)},stop(t){ni.removeBox(t,To.get(t)),To.delete(t)},beforeUpdate(t,e,i){const s=To.get(t);ni.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Ro={average(t){if(!t.length)return!1;let e,i,s=0,n=0,o=0;for(e=0,i=t.length;e-1?t.split("\n"):t}function zo(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Fo(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=He(e.bodyFont),h=He(e.titleFont),c=He(e.footerFont),d=o.length,u=n.length,f=s.length,g=Ne(e.padding);let p=g.height,m=0,x=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(x+=t.beforeBody.length+t.afterBody.length,d&&(p+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),x){p+=f*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(x-f)*l.lineHeight+(x-1)*e.bodySpacing}u&&(p+=e.footerMarginTop+u*c.lineHeight+(u-1)*e.footerSpacing);let b=0;const _=function(t){m=Math.max(m,i.measureText(t).width+b)};return i.save(),i.font=h.string,Q(t.title,_),i.font=l.string,Q(t.beforeBody.concat(t.afterBody),_),b=e.displayColors?a+2+e.boxPadding:0,Q(s,(t=>{Q(t.before,_),Q(t.lines,_),Q(t.after,_)})),b=0,i.font=c.string,Q(t.footer,_),i.restore(),m+=g.width,{width:m,height:p}}function Bo(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h="center";return"center"===s?h=n<=(r+l)/2?"left":"right":n<=o/2?h="left":n>=a-o/2&&(h="right"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return"left"===t&&n+o+a>e.width||"right"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h="center"),h}function Vo(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return it.height-s/2?"bottom":"center"}(t,i);return{xAlign:i.xAlign||e.xAlign||Bo(t,e,i,s),yAlign:s}}function Wo(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=We(a);let g=function(t,e){let{x:i,width:s}=t;return"right"===e?i-=s:"center"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return"top"===e?s+=i:s-="bottom"===e?n+i:n/2,s}(e,l,h);return"center"===l?"left"===r?g+=h:"right"===r&&(g-=h):"left"===r?g-=Math.max(c,u)+n:"right"===r&&(g+=Math.max(d,f)+n),{x:jt(g,0,s.width-e.width),y:jt(p,0,s.height-e.height)}}function No(t,e,i){const s=Ne(i.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-s.right:t.x+s.left}function Ho(t){return Eo([],Io(t))}function jo(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}class $o extends Ds{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||t._chart,this._chart=this.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,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,n=new gs(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(n)),n}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,i=this._tooltipItems,Ye(t,{tooltip:e,tooltipItems:i,type:"tooltip"})));var t,e,i}getTitle(t,e){const{callbacks:i}=e,s=i.beforeTitle.apply(this,[t]),n=i.title.apply(this,[t]),o=i.afterTitle.apply(this,[t]);let a=[];return a=Eo(a,Io(s)),a=Eo(a,Io(n)),a=Eo(a,Io(o)),a}getBeforeBody(t,e){return Ho(e.callbacks.beforeBody.apply(this,[t]))}getBody(t,e){const{callbacks:i}=e,s=[];return Q(t,(t=>{const e={before:[],lines:[],after:[]},n=jo(i,t);Eo(e.before,Io(n.beforeLabel.call(this,t))),Eo(e.lines,n.label.call(this,t)),Eo(e.after,Io(n.afterLabel.call(this,t))),s.push(e)})),s}getAfterBody(t,e){return Ho(e.callbacks.afterBody.apply(this,[t]))}getFooter(t,e){const{callbacks:i}=e,s=i.beforeFooter.apply(this,[t]),n=i.footer.apply(this,[t]),o=i.afterFooter.apply(this,[t]);let a=[];return a=Eo(a,Io(s)),a=Eo(a,Io(n)),a=Eo(a,Io(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;at.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),Q(l,(e=>{const i=jo(t.callbacks,e);s.push(i.labelColor.call(this,e)),n.push(i.labelPointStyle.call(this,e)),o.push(i.labelTextColor.call(this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=Ro[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Fo(this,i),a=Object.assign({},t,e),r=Vo(this.chart,i,a),l=Wo(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=We(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,x,b,_,y;return"center"===n?(_=u+g/2,"left"===s?(p=d,m=p-o,b=_+o,y=_-o):(p=d+f,m=p+o,b=_-o,y=_+o),x=p):(m="left"===s?d+Math.max(r,h)+o:"right"===s?d+f-Math.max(l,c)-o:this.caretX,"top"===n?(b=u,_=b-o,p=m-o,x=m+o):(b=u+g,_=b+o,p=m+o,x=m-o),y=b),{x1:p,x2:m,x3:x,y1:b,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Ei(i.rtl,this.x,this.width);for(t.x=No(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline="middle",o=He(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,oe(t,{x:e,y:g,w:l,h:r,radius:a}),t.fill(),t.stroke(),t.fillStyle=o.backgroundColor,t.beginPath(),oe(t,{x:i,y:g+1,w:l-2,h:r-2,radius:a}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,l,r),t.strokeRect(e,g,l,r),t.fillStyle=o.backgroundColor,t.fillRect(i,g+1,l-2,r-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=He(i.bodyFont);let d=c.lineHeight,u=0;const f=Ei(i.rtl,this.x,this.width),g=function(i){e.fillText(i,f.x(t.x+u),t.y+d/2),t.y+=d+n},p=f.textAlign(o);let m,x,b,_,y,v,w;for(e.textAlign=o,e.textBaseline="middle",e.font=c.string,t.x=No(this,p,i),e.fillStyle=i.bodyColor,Q(this.beforeBody,g),u=a&&"right"!==p?"center"===o?l/2+h:l+2+h:0,_=0,v=s.length;_0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=Ro[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Fo(this,t),a=Object.assign({},i,this._size),r=Vo(e,t,a),l=Wo(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=Ne(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=i,this.drawBackground(n,t,s,e),Ii(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),zi(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!tt(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!tt(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if("mouseout"===t.type)return[];if(!s)return e;const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=Ro[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}$o.positioners=Ro;var Yo={id:"tooltip",_element:$o,positioners:Ro,afterInit(t,e,i){i&&(t.tooltip=new $o({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip,i={tooltip:e};!1!==t.notifyPlugins("beforeTooltipDraw",i)&&(e&&e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",i))},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.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:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:{beforeTitle:H,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&"dataset"===this.options.mode)return e.dataset.label||"";if(e.label)return e.label;if(s>0&&e.dataIndex"filter"!==t&&"itemSort"!==t&&"external"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]},Uo=Object.freeze({__proto__:null,Decimation:ro,Filler:So,Legend:Co,SubTitle:Lo,Title:Ao,Tooltip:Yo});function Xo(t,e,i,s){const n=t.indexOf(e);if(-1===n)return((t,e,i,s)=>("string"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}class qo extends Bs{constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if($(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:jt(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:Xo(i,t,K(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){const e=this.getLabels();return t>=0&&te.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}function Ko(t,e,{horizontal:i,minRotation:s}){const n=It(s),o=(i?Math.sin(n):Math.cos(n))||.001,a=.75*e*(""+t).length;return Math.min(e/o,a)}qo.id="category",qo.defaults={ticks:{callback:qo.prototype.getLabelForValue}};class Go extends Bs{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)||("number"==typeof t||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:n}=this;const o=t=>s=e?s:t,a=t=>n=i?n:t;if(t){const t=Ct(s),e=Ct(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=1;(n>=Number.MAX_SAFE_INTEGER||s<=Number.MIN_SAFE_INTEGER)&&(e=Math.abs(.05*n)),a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const s=function(t,e){const i=[],{bounds:s,step:n,min:o,max:a,precision:r,count:l,maxTicks:h,maxDigits:c,includeBounds:d}=t,u=n||1,f=h-1,{min:g,max:p}=e,m=!$(o),x=!$(a),b=!$(l),_=(p-g)/(c+1);let y,v,w,M,k=Ot((p-g)/f/u)*u;if(k<1e-14&&!m&&!x)return[{value:g},{value:p}];M=Math.ceil(p/k)-Math.floor(g/k),M>f&&(k=Ot(M*k/f/u)*u),$(r)||(y=Math.pow(10,r),k=Math.ceil(k*y)/y),"ticks"===s?(v=Math.floor(g/k)*k,w=Math.ceil(p/k)*k):(v=g,w=p),m&&x&&n&&Rt((a-o)/n,k/1e3)?(M=Math.round(Math.min((a-o)/k,h)),k=(a-o)/M,v=o,w=a):b?(v=m?o:v,w=x?a:w,M=l-1,k=(w-v)/M):(M=(w-v)/k,M=Lt(M,Math.round(M),k/1e3)?Math.round(M):Math.ceil(M));const S=Math.max(Ft(k),Ft(v));y=Math.pow(10,$(r)?S:r),v=Math.round(v*y)/y,w=Math.round(w*y)/y;let P=0;for(m&&(d&&v!==o?(i.push({value:o}),v0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=X(t)?Math.max(0,t):null,this.max=X(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t,a=(t,e)=>Math.pow(10,Math.floor(Dt(t))+e);i===s&&(i<=0?(n(1),o(10)):(n(a(i,-1)),o(a(s,1)))),i<=0&&n(a(s,-1)),s<=0&&o(a(i,1)),this._zero&&this.min!==this._suggestedMin&&i===a(this.min,0)&&n(a(i,-1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=function(t,e){const i=Math.floor(Dt(e.max)),s=Math.ceil(e.max/Math.pow(10,i)),n=[];let o=q(t.min,Math.pow(10,Math.floor(Dt(e.min)))),a=Math.floor(Dt(o)),r=Math.floor(o/Math.pow(10,a)),l=a<0?Math.pow(10,Math.abs(a)):1;do{n.push({value:o,major:Jo(o)}),++r,10===r&&(r=1,++a,l=a>=0?1:l),o=Math.round(r*Math.pow(10,a)*l)/l}while(an?{start:e-i,end:e}:{start:e,end:e+i}}function ia(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],n=[],o=t._pointLabels.length,a=t.options.pointLabels,r=a.centerPointLabels?_t/o:0;for(let d=0;de.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.starte.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function na(t){return 0===t||180===t?"center":t<180?"left":"right"}function oa(t,e,i){return"right"===i?t-=e:"center"===i&&(t-=e/2),t}function aa(t,e,i){return 90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e),t}function ra(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,yt);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;o{const i=J(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?ia(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return Nt(t*(yt/(this._pointLabels.length||1))+It(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if($(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if($(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t=0;n--){const e=s.setContext(t.getPointLabelContext(n)),o=He(e.font),{x:a,y:r,textAlign:l,left:h,top:c,right:d,bottom:u}=t._pointLabelItems[n],{backdropColor:f}=e;if(!$(f)){const t=Ne(e.backdropPadding);i.fillStyle=f,i.fillRect(h-t.left,c-t.top,d-h+t.width,u-c+t.height)}se(i,t._pointLabels[n],a,r+o.lineHeight/2,o,{color:e.color,textAlign:l,textBaseline:"middle"})}}(this,n),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e){a=this.getDistanceFromCenterForValue(t.value);!function(t,e,i,s){const n=t.ctx,o=e.circular,{color:a,lineWidth:r}=e;!o&&!s||!a||!r||i<0||(n.save(),n.strokeStyle=a,n.lineWidth=r,n.setLineDash(e.borderDash),n.lineDashOffset=e.borderDashOffset,n.beginPath(),ra(t,i,o,s),n.closePath(),n.stroke(),n.restore())}(this,s.setContext(this.getContext(e-1)),a,n)}})),i.display){for(t.save(),o=n-1;o>=0;o--){const s=i.setContext(this.getPointLabelContext(o)),{color:n,lineWidth:l}=s;l&&n&&(t.lineWidth=l,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,a=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),r=this.getPointPosition(o,a),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(r.x,r.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((s,a)=>{if(0===a&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=He(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=Ne(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}se(t,s.label,0,-n,l,{color:r.color})})),t.restore()}drawTitle(){}}la.id="radialLinear",la.defaults={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:Os.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}},la.defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"},la.descriptors={angleLines:{_fallback:"grid"}};const ha={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}},ca=Object.keys(ha);function da(t,e){return t-e}function ua(t,e){if($(e))return null;const i=t._adapter,{parser:s,round:n,isoWeekday:o}=t._parseOpts;let a=e;return"function"==typeof s&&(a=s(a)),X(a)||(a="string"==typeof s?i.parse(a,s):i.parse(a)),null===a?null:(n&&(a="week"!==n||!Tt(o)&&!0!==o?i.startOf(a,n):i.startOf(a,"isoWeek",o)),+a)}function fa(t,e,i,s){const n=ca.length;for(let o=ca.indexOf(t);o=e?i[s]:i[n]]=!0}}else t[e]=!0}function pa(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a=0&&(e[l].major=!0);return e}(t,s,n,i):s}class ma extends Bs{constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e){const i=t.time||(t.time={}),s=this._adapter=new mn._date(t.adapters.date);ot(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:ua(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||"day";let{min:s,max:n,minDefined:o,maxDefined:a}=this.getUserBounds();function r(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),a||isNaN(t.max)||(n=Math.max(n,t.max))}o&&a||(r(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||r(this.getMinMax(!1))),s=X(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=X(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s="labels"===i.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=he(s,n,this.max);return this._unit=e.unit||(i.autoSkip?fa(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=ca.length-1;o>=ca.indexOf(i);o--){const i=ca[o];if(ha[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return ca[i?ca.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&"year"!==this._unit?function(t){for(let e=ca.indexOf(t)+1,i=ca.length;e1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+o);const f="data"===s.ticks.source&&this.getDataTimestamps();for(c=u,d=0;ct-e)).map((t=>+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.time.displayFormats,a=this._unit,r=this._majorUnit,l=a&&o[a],h=r&&o[r],c=i[e],d=r&&h&&c&&c.major,u=this._adapter.format(t,s||(d?h:l)),f=n.ticks.callback;return f?J(f,[u,e,i],this):u}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=re(t,"pos",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=re(t,"time",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}ma.id="time",ma.defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",major:{enabled:!1}}};class ba extends ma{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=xa(e,this.min),this._tableRange=xa(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;o Array.prototype.slice.call(args)); + let ticking = false; + let args = []; + return function(...rest) { + args = updateArgs(rest); + if (!ticking) { + ticking = true; + requestAnimFrame.call(window, () => { + ticking = false; + fn.apply(thisArg, args); + }); + } + }; +} +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 noop() {} +const uid = (function() { + let id = 0; + return function() { + return id++; + }; +}()); +function isNullOrUndef(value) { + return value === null || typeof value === 'undefined'; +} +function isArray(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + const type = Object.prototype.toString.call(value); + if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { + return true; + } + return false; +} +function isObject(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; +} +const isNumberFinite = (value) => (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 toPercentage = (value, dimension) => + typeof value === 'string' && value.endsWith('%') ? + parseFloat(value) / 100 + : value / dimension; +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; + if (reverse) { + for (i = len - 1; i >= 0; i--) { + fn.call(thisArg, loopable[i], i); + } + } else { + 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$1(source) { + if (isArray(source)) { + return source.map(clone$1); + } + if (isObject(source)) { + const target = Object.create(null); + const keys = Object.keys(source); + const klen = keys.length; + let k = 0; + for (; k < klen; ++k) { + target[keys[k]] = clone$1(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$1(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; + for (let i = 0; i < ilen; ++i) { + source = sources[i]; + if (!isObject(source)) { + continue; + } + const keys = Object.keys(source); + for (let k = 0, klen = keys.length; k < klen; ++k) { + merger(keys[k], target, source, 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$1(sval); + } +} +function _deprecated(scope, value, previous, current) { + if (value !== undefined) { + console.warn(scope + ': "' + previous + + '" is deprecated. Please use "' + current + '" instead'); + } +} +const emptyString = ''; +const dot = '.'; +function indexOfDotOrLength(key, start) { + const idx = key.indexOf(dot, start); + return idx === -1 ? key.length : idx; +} +function resolveObjectKey(obj, key) { + if (key === emptyString) { + return obj; + } + let pos = 0; + let idx = indexOfDotOrLength(key, pos); + while (obj && idx > pos) { + obj = obj[key.substr(pos, idx - pos)]; + pos = idx + 1; + idx = indexOfDotOrLength(key, pos); + } + return 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 niceNum(range) { + const roundedRange = Math.round(range); + range = almostEquals(range, roundedRange, range / 1000) ? 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 isNumber(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} +function almostEquals(x, y, epsilon) { + return Math.abs(x - y) < epsilon; +} +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; +} + +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, +}; + +/*! + * @kurkle/color v0.1.9 + * https://github.com/kurkle/color#readme + * (c) 2020 Jukka Kurkela + * Released under the MIT License + */ +const map = {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 & 0xF]; +const h2 = (b) => hex[(b & 0xF0) >> 4] + hex[b & 0xF]; +const eq = (b) => (((b & 0xF0) >> 4) === (b & 0xF)); +function isShort(v) { + return 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[str[1]] * 17, + g: 255 & map[str[2]] * 17, + b: 255 & map[str[3]] * 17, + a: len === 5 ? map[str[4]] * 17 : 255 + }; + } else if (len === 7 || len === 9) { + ret = { + r: map[str[1]] << 4 | map[str[2]], + g: map[str[3]] << 4 | map[str[4]], + b: map[str[5]] << 4 | map[str[6]], + a: len === 9 ? (map[str[7]] << 4 | map[str[8]]) : 255 + }; + } + } + return ret; +} +function hexString(v) { + var f = isShort(v) ? h1 : h2; + return v + ? '#' + f(v.r) + f(v.g) + f(v.b) + (v.a < 255 ? f(v.a) : '') + : v; +} +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 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 = 255 & (m[8] ? p2b(v) : v * 255); + } + r = +m[1]; + g = +m[3]; + b = +m[5]; + r = 255 & (m[2] ? p2b(r) : r); + g = 255 & (m[4] ? p2b(g) : g); + b = 255 & (m[6] ? p2b(b) : b); + return { + r: r, + g: g, + b: b, + a: 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 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 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 = max === r + ? ((g - b) / d) + (g < b ? 6 : 0) + : max === g + ? (b - r) / d + 2 + : (r - g) / d + 4; + 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: 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$1 = { + 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 = { + 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); + const tkeys = Object.keys(map$1); + 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$1[k]); + } + k = parseInt(names[ok], 16); + unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF]; + } + return unpacked; +} +let names$1; +function nameParse(str) { + if (!names$1) { + names$1 = unpack(); + names$1.transparent = [0, 0, 0, 0]; + } + const a = names$1[str.toLowerCase()]; + return a && { + r: a[0], + g: a[1], + b: a[2], + a: a.length === 4 ? a[3] : 255 + }; +} +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(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(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(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) : this._rgb; + } + hexString() { + return this._valid ? hexString(this._rgb) : this._rgb; + } + hslString() { + return this._valid ? hslString(this._rgb) : this._rgb; + } + mix(color, weight) { + const me = this; + if (color) { + const c1 = me.rgb; + const c2 = color.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.0; + w2 = 1 - w1; + c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5; + c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5; + c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5; + c1.a = p * c1.a + (1 - p) * c2.a; + me.rgb = c1; + } + return me; + } + 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; + } +} +function index_esm(input) { + return new Color(input); +} + +const isPatternOrGradient = (value) => value instanceof CanvasGradient || value instanceof CanvasPattern; +function color(value) { + return isPatternOrGradient(value) ? value : index_esm(value); +} +function getHoverColor(value) { + return isPatternOrGradient(value) + ? value + : index_esm(value).saturate(0.5).darken(0.1).hexString(); +} + +const overrides = Object.create(null); +const descriptors = 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] = 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(_descriptors) { + this.animation = undefined; + 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 + }; + this.maintainAspectRatio = true; + this.onHover = null; + this.onClick = null; + this.parsing = true; + this.plugins = {}; + this.responsive = true; + this.scale = undefined; + this.scales = {}; + this.showLine = true; + this.drawActiveElementsOnTop = true; + this.describe(_descriptors); + } + set(scope, values) { + return set(this, scope, values); + } + get(scope) { + return getScope$1(this, scope); + } + describe(scope, values) { + return set(descriptors, scope, values); + } + override(scope, values) { + return set(overrides, scope, values); + } + route(scope, name, targetScope, targetName) { + const scopeObject = getScope$1(this, scope); + 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; + } + } + }); + } +} +var defaults = new Defaults({ + _scriptable: (name) => !name.startsWith('on'), + _indexable: (name) => name !== 'events', + hover: { + _fallback: 'interaction' + }, + interaction: { + _scriptable: false, + _indexable: false, + } +}); + +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 _longestText(ctx, font, arrayOfThings, cache) { + cache = cache || {}; + let data = cache.data = cache.data || {}; + let gc = cache.garbageCollect = cache.garbageCollect || []; + if (cache.font !== font) { + data = cache.data = {}; + gc = cache.garbageCollect = []; + cache.font = font; + } + ctx.save(); + ctx.font = font; + let longest = 0; + const ilen = arrayOfThings.length; + let i, j, jlen, thing, nestedThing; + for (i = 0; i < ilen; i++) { + thing = arrayOfThings[i]; + if (thing !== undefined && thing !== null && isArray(thing) !== true) { + longest = _measureText(ctx, data, gc, longest, thing); + } else if (isArray(thing)) { + for (j = 0, jlen = thing.length; j < jlen; j++) { + nestedThing = thing[j]; + if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) { + longest = _measureText(ctx, data, gc, longest, nestedThing); + } + } + } + } + ctx.restore(); + const gcLen = gc.length / 2; + if (gcLen > arrayOfThings.length) { + for (i = 0; i < gcLen; i++) { + delete data[gc[i]]; + } + gc.splice(0, gcLen); + } + 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) { + 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) { + let type, xOffset, yOffset, size, cornerRadius; + 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: + ctx.arc(x, y, radius, 0, TAU); + ctx.closePath(); + break; + case 'triangle': + ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case 'rectRounded': + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + yOffset = Math.sin(rad + QUARTER_PI) * size; + ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case 'rect': + if (!rotation) { + size = Math.SQRT1_2 * radius; + ctx.rect(x - size, y - size, 2 * size, 2 * size); + break; + } + rad += QUARTER_PI; + case 'rectRot': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + yOffset, y - xOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.lineTo(x - yOffset, y + xOffset); + ctx.closePath(); + break; + case 'crossRot': + rad += QUARTER_PI; + case 'cross': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'star': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + rad += QUARTER_PI; + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'line': + xOffset = 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) * radius, y + Math.sin(rad) * radius); + 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.0; + 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 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 (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 += font.lineHeight; + } + ctx.restore(); +} +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 addRoundedRectPath(ctx, rect) { + const {x, y, w, h, radius} = rect; + ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_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 = new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); +const FONT_STYLE = new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/); +function toLineHeight(value, size) { + const matches = ('' + value).match(LINE_HEIGHT); + if (!matches || 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 = ''; + } + 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, index, info) { + let cacheable = true; + let i, ilen, value; + for (i = 0, ilen = inputs.length; i < ilen; ++i) { + value = inputs[i]; + if (value === undefined) { + continue; + } + if (context !== undefined && typeof value === 'function') { + value = value(context); + cacheable = false; + } + if (index !== undefined && isArray(value)) { + value = value[index % value.length]; + cacheable = false; + } + if (value !== undefined) { + if (info && !cacheable) { + info.cacheable = false; + } + 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 _lookup(table, value, cmp) { + cmp = cmp || ((index) => table[index] < 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) => + _lookup(table, value, index => table[index][key] < value); +const _rlookupByKey = (table, key, value) => + _lookup(table, value, index => table[index][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 index = listeners.indexOf(listener); + if (index !== -1) { + listeners.splice(index, 1); + } + if (listeners.length > 0) { + return; + } + arrayEvents.forEach((key) => { + delete array[key]; + }); + delete array._chartjs; +} +function _arrayUnique(items) { + const set = new Set(); + let i, ilen; + for (i = 0, ilen = items.length; i < ilen; ++i) { + set.add(items[i]); + } + if (set.size === ilen) { + return items; + } + return Array.from(set); +} + +function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback, getTarget = () => scopes[0]) { + if (!defined(fallback)) { + fallback = _resolve('_fallback', scopes); + } + const cache = { + [Symbol.toStringTag]: 'Object', + _cacheable: true, + _scopes: scopes, + _rootScopes: rootScopes, + _fallback: fallback, + _getTarget: getTarget, + override: (scope) => _createResolver([scope, ...scopes], prefixes, rootScopes, fallback), + }; + return new Proxy(cache, { + deleteProperty(target, prop) { + delete target[prop]; + delete target._keys; + delete scopes[0][prop]; + return true; + }, + get(target, prop) { + return _cached(target, prop, + () => _resolveWithPrefixes(prop, prefixes, scopes, target)); + }, + getOwnPropertyDescriptor(target, prop) { + return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop); + }, + getPrototypeOf() { + return Reflect.getPrototypeOf(scopes[0]); + }, + has(target, prop) { + return getKeysFromAllScopes(target).includes(prop); + }, + ownKeys(target) { + return getKeysFromAllScopes(target); + }, + 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: new Set(), + _descriptors: _descriptors(proxy, descriptorDefaults), + setContext: (ctx) => _attachContext(proxy, ctx, subProxy, descriptorDefaults), + override: (scope) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults) + }; + return new Proxy(cache, { + deleteProperty(target, prop) { + delete target[prop]; + delete proxy[prop]; + return true; + }, + get(target, prop, receiver) { + return _cached(target, prop, + () => _resolveWithContext(target, prop, receiver)); + }, + getOwnPropertyDescriptor(target, prop) { + return target._descriptors.allKeys + ? Reflect.has(proxy, prop) ? {enumerable: true, configurable: true} : undefined + : Reflect.getOwnPropertyDescriptor(proxy, prop); + }, + getPrototypeOf() { + return Reflect.getPrototypeOf(proxy); + }, + has(target, prop) { + return Reflect.has(proxy, prop); + }, + ownKeys() { + return Reflect.ownKeys(proxy); + }, + set(target, prop, value) { + proxy[prop] = value; + delete target[prop]; + return true; + } + }); +} +function _descriptors(proxy, defaults = {scriptable: true, indexable: true}) { + const {_scriptable = defaults.scriptable, _indexable = defaults.indexable, _allKeys = defaults.allKeys} = proxy; + return { + allKeys: _allKeys, + scriptable: _scriptable, + indexable: _indexable, + isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable, + isIndexable: isFunction(_indexable) ? _indexable : () => _indexable + }; +} +const readKey = (prefix, name) => prefix ? prefix + _capitalize(name) : name; +const needsSubResolver = (prop, value) => isObject(value) && prop !== 'adapters' && + (Object.getPrototypeOf(value) === null || value.constructor === Object); +function _cached(target, prop, resolve) { + if (Object.prototype.hasOwnProperty.call(target, prop)) { + return target[prop]; + } + const value = resolve(); + target[prop] = value; + return value; +} +function _resolveWithContext(target, prop, receiver) { + const {_proxy, _context, _subProxy, _descriptors: descriptors} = target; + let value = _proxy[prop]; + if (isFunction(value) && descriptors.isScriptable(prop)) { + value = _resolveScriptable(prop, value, target, receiver); + } + if (isArray(value) && value.length) { + value = _resolveArray(prop, value, target, descriptors.isIndexable); + } + if (needsSubResolver(prop, value)) { + value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors); + } + return value; +} +function _resolveScriptable(prop, value, 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); + value = value(_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: descriptors} = target; + if (defined(_context.index) && isIndexable(prop)) { + value = 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], descriptors)); + } + } + 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) : undefined; +function addScopes(set, parentScopes, key, parentFallback, value) { + for (const parent of parentScopes) { + const scope = getScope(key, parent); + if (scope) { + set.add(scope); + const fallback = resolveFallback(scope._fallback, key, value); + if (defined(fallback) && fallback !== key && fallback !== parentFallback) { + return fallback; + } + } else if (scope === false && defined(parentFallback) && 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 set = new Set(); + set.add(value); + let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value); + if (key === null) { + return false; + } + if (defined(fallback) && fallback !== prop) { + key = addScopesFromKey(set, allScopes, fallback, key, value); + if (key === null) { + return false; + } + } + return _createResolver(Array.from(set), [''], rootScopes, fallback, + () => subGetTarget(resolver, prop, value)); +} +function addScopesFromKey(set, allScopes, key, fallback, item) { + while (key) { + key = addScopes(set, 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 (defined(value)) { + return needsSubResolver(prop, value) + ? createSubResolver(scopes, proxy, prop, value) + : value; + } + } +} +function _resolve(key, scopes) { + for (const scope of scopes) { + if (!scope) { + continue; + } + const value = scope[key]; + if (defined(value)) { + return value; + } + } +} +function getKeysFromAllScopes(target) { + let keys = target._keys; + if (!keys) { + keys = target._keys = resolveKeysFromAllScopes(target._scopes); + } + return keys; +} +function resolveKeysFromAllScopes(scopes) { + const set = new Set(); + for (const scope of scopes) { + for (const key of Object.keys(scope).filter(k => !k.startsWith('_'))) { + set.add(key); + } + } + return Array.from(set); +} + +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) => window.getComputedStyle(element, null); +function getStyle(el, property) { + return getComputedStyle(el).getPropertyValue(property); +} +const positions = ['top', 'right', 'bottom', 'left']; +function getPositionedStyle(styles, style, suffix) { + const result = {}; + suffix = suffix ? '-' + suffix : ''; + for (let i = 0; i < 4; i++) { + const pos = positions[i]; + result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0; + } + 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(evt, canvas) { + const e = evt.native || evt; + 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(evt, chart) { + 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(evt, 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 === undefined || height === undefined) { + const container = _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 ? Math.floor(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); + } + return { + width, + height + }; +} +function retinaScale(chart, forceRatio, forceStyle) { + const pixelRatio = forceRatio || 1; + const deviceHeight = Math.floor(chart.height * pixelRatio); + const deviceWidth = Math.floor(chart.width * pixelRatio); + chart.height = deviceHeight / pixelRatio; + chart.width = deviceWidth / pixelRatio; + 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; + } + }; + 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] : undefined; +} + +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 intlCache = 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 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 !== undefined) { + 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) { + return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle); +} + +export { _toLeftRightCenter as $, _rlookupByKey as A, getAngleFromPoint as B, toPadding as C, each as D, getMaximumSize as E, _getParentNode as F, readUsedSize as G, HALF_PI as H, throttled as I, supportsEventListenerOptions as J, _isDomSupported as K, log10 as L, _factorize as M, finiteOrDefault as N, callback as O, PI as P, _addGrace as Q, toDegrees as R, _measureText as S, TAU as T, _int16Range as U, _alignPixel as V, clipArea as W, renderText as X, unclipArea as Y, toFont as Z, _arrayUnique as _, resolve as a, _angleDiff as a$, _alignStartEnd as a0, overrides as a1, merge as a2, _capitalize as a3, descriptors as a4, isFunction as a5, _attachContext as a6, _createResolver as a7, _descriptors as a8, mergeIf as a9, restoreTextDirection as aA, noop as aB, distanceBetweenPoints as aC, _setMinAndMaxByKey as aD, niceNum as aE, almostWhole as aF, almostEquals as aG, _decimalPlaces as aH, _longestText as aI, _filterBetween as aJ, _lookup as aK, getHoverColor as aL, clone$1 as aM, _merger as aN, _mergerIf as aO, _deprecated as aP, toFontString as aQ, splineCurve as aR, splineCurveMonotone as aS, getStyle as aT, fontString as aU, toLineHeight as aV, PITAU as aW, INFINITY as aX, RAD_PER_DEG as aY, QUARTER_PI as aZ, TWO_THIRDS_PI as a_, uid as aa, debounce as ab, retinaScale as ac, clearCanvas as ad, setsEqual as ae, _elementsEqual as af, _isClickEvent as ag, _isBetween as ah, _readValueToProps as ai, _updateBezierControlPoints as aj, _computeSegments as ak, _boundSegments as al, _steppedInterpolation as am, _bezierInterpolation as an, _pointInLine as ao, _steppedLineTo as ap, _bezierCurveTo as aq, drawPoint as ar, addRoundedRectPath as as, toTRBL as at, toTRBLCorners as au, _boundSegment as av, _normalizeAngle as aw, getRtlAdapter as ax, overrideTextDirection as ay, _textX as az, isArray as b, color as c, defaults as d, effects as e, resolveObjectKey as f, isNumberFinite as g, createContext as h, isObject as i, defined as j, isNullOrUndef as k, listenArrayEvents as l, toPercentage as m, toDimension as n, formatNumber as o, _angleBetween as p, isNumber as q, requestAnimFrame as r, sign as s, toRadians as t, unlistenArrayEvents as u, valueOrDefault as v, _limitValue as w, _lookupByKey as x, getRelativePosition as y, _isPointInArea as z }; diff --git a/node_modules/chart.js/dist/helpers.esm.js b/node_modules/chart.js/dist/helpers.esm.js new file mode 100644 index 00000000..399496db --- /dev/null +++ b/node_modules/chart.js/dist/helpers.esm.js @@ -0,0 +1,7 @@ +/*! + * Chart.js v3.7.1 + * https://www.chartjs.org + * (c) 2022 Chart.js Contributors + * Released under the MIT License + */ +export { H as HALF_PI, aX as INFINITY, P as PI, aW as PITAU, aZ as QUARTER_PI, aY as RAD_PER_DEG, T as TAU, a_ as TWO_THIRDS_PI, Q as _addGrace, V as _alignPixel, a0 as _alignStartEnd, p as _angleBetween, a$ as _angleDiff, _ as _arrayUnique, a6 as _attachContext, aq as _bezierCurveTo, an as _bezierInterpolation, av as _boundSegment, al as _boundSegments, a3 as _capitalize, ak as _computeSegments, a7 as _createResolver, aH as _decimalPlaces, aP as _deprecated, a8 as _descriptors, af as _elementsEqual, M as _factorize, aJ as _filterBetween, F as _getParentNode, U as _int16Range, ah as _isBetween, ag as _isClickEvent, K as _isDomSupported, z as _isPointInArea, w as _limitValue, aI as _longestText, aK as _lookup, x as _lookupByKey, S as _measureText, aN as _merger, aO as _mergerIf, aw as _normalizeAngle, ao as _pointInLine, ai as _readValueToProps, A as _rlookupByKey, aD as _setMinAndMaxByKey, am as _steppedInterpolation, ap as _steppedLineTo, az as _textX, $ as _toLeftRightCenter, aj as _updateBezierControlPoints, as as addRoundedRectPath, aG as almostEquals, aF as almostWhole, O as callback, ad as clearCanvas, W as clipArea, aM as clone, c as color, h as createContext, ab as debounce, j as defined, aC as distanceBetweenPoints, ar as drawPoint, D as each, e as easingEffects, N as finiteOrDefault, aU as fontString, o as formatNumber, B as getAngleFromPoint, aL as getHoverColor, E as getMaximumSize, y as getRelativePosition, ax as getRtlAdapter, aT as getStyle, b as isArray, g as isFinite, a5 as isFunction, k as isNullOrUndef, q as isNumber, i as isObject, l as listenArrayEvents, L as log10, a2 as merge, a9 as mergeIf, aE as niceNum, aB as noop, ay as overrideTextDirection, G as readUsedSize, X as renderText, r as requestAnimFrame, a as resolve, f as resolveObjectKey, aA as restoreTextDirection, ac as retinaScale, ae as setsEqual, s as sign, aR as splineCurve, aS as splineCurveMonotone, J as supportsEventListenerOptions, I as throttled, R as toDegrees, n as toDimension, Z as toFont, aQ as toFontString, aV as toLineHeight, C as toPadding, m as toPercentage, t as toRadians, at as toTRBL, au as toTRBLCorners, aa as uid, Y as unclipArea, u as unlistenArrayEvents, v as valueOrDefault } from './chunks/helpers.segment.js'; diff --git a/node_modules/chart.js/helpers/helpers.esm.d.ts b/node_modules/chart.js/helpers/helpers.esm.d.ts new file mode 100644 index 00000000..2c3468e7 --- /dev/null +++ b/node_modules/chart.js/helpers/helpers.esm.d.ts @@ -0,0 +1 @@ +export * from '../types/helpers'; diff --git a/node_modules/chart.js/helpers/helpers.esm.js b/node_modules/chart.js/helpers/helpers.esm.js new file mode 100644 index 00000000..ca4eee52 --- /dev/null +++ b/node_modules/chart.js/helpers/helpers.esm.js @@ -0,0 +1 @@ +export * from '../dist/helpers.esm'; diff --git a/node_modules/chart.js/helpers/helpers.js b/node_modules/chart.js/helpers/helpers.js new file mode 100644 index 00000000..a762f589 --- /dev/null +++ b/node_modules/chart.js/helpers/helpers.js @@ -0,0 +1 @@ +module.exports = require('..').helpers; diff --git a/node_modules/chart.js/helpers/package.json b/node_modules/chart.js/helpers/package.json new file mode 100644 index 00000000..d97b75cb --- /dev/null +++ b/node_modules/chart.js/helpers/package.json @@ -0,0 +1,8 @@ +{ + "name": "chart.js-helpers", + "private": true, + "description": "helper package", + "main": "helpers.js", + "module": "helpers.esm.js", + "types": "helpers.esm.d.ts" +} \ No newline at end of file diff --git a/node_modules/chart.js/package.json b/node_modules/chart.js/package.json new file mode 100644 index 00000000..24c99596 --- /dev/null +++ b/node_modules/chart.js/package.json @@ -0,0 +1,111 @@ +{ + "name": "chart.js", + "homepage": "https://www.chartjs.org", + "description": "Simple HTML5 charts using the canvas element.", + "version": "3.7.1", + "license": "MIT", + "jsdelivr": "dist/chart.min.js", + "unpkg": "dist/chart.min.js", + "main": "dist/chart.js", + "module": "dist/chart.esm.js", + "types": "types/index.esm.d.ts", + "keywords": [ + "canvas", + "charts", + "data", + "graphs", + "html5", + "responsive" + ], + "repository": { + "type": "git", + "url": "https://github.com/chartjs/Chart.js.git" + }, + "bugs": { + "url": "https://github.com/chartjs/Chart.js/issues" + }, + "files": [ + "auto/**/*.js", + "auto/**/*.d.ts", + "dist/*.js", + "dist/chunks/*.js", + "types/*.d.ts", + "types/helpers/*.d.ts", + "helpers/**/*.js", + "helpers/**/*.d.ts" + ], + "scripts": { + "autobuild": "rollup -c -w", + "build": "rollup -c", + "dev": "karma start --auto-watch --no-single-run --browsers chrome --grep", + "dev:ff": "karma start --auto-watch --no-single-run --browsers firefox --grep", + "docs": "npm run build && vuepress build docs --no-cache", + "docs:dev": "npm run build && vuepress dev docs --no-cache", + "lint-js": "eslint \"src/**/*.js\" \"test/**/*.js\" \"docs/**/*.js\"", + "lint-md": "eslint \"**/*.md\"", + "lint-tsc": "tsc", + "lint-types": "eslint \"types/**/*.ts\" && node -r esm types/tests/autogen.js && tsc -p types/tests/", + "lint": "concurrently \"npm:lint-*\"", + "test": "npm run lint && cross-env NODE_ENV=test karma start --auto-watch --single-run --coverage --grep" + }, + "devDependencies": { + "@kurkle/color": "^0.1.9", + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-inject": "^4.0.2", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.0.0", + "@simonbrunel/vuepress-plugin-versions": "^0.2.0", + "@types/offscreencanvas": "^2019.6.4", + "@typescript-eslint/eslint-plugin": "^5.8.0", + "@typescript-eslint/parser": "^5.8.0", + "@vuepress/plugin-google-analytics": "^1.8.3", + "@vuepress/plugin-html-redirect": "^0.1.2", + "chartjs-adapter-luxon": "^1.0.0", + "chartjs-adapter-moment": "^1.0.0", + "chartjs-test-utils": "^0.3.1", + "concurrently": "^6.0.1", + "coveralls": "^3.1.0", + "cross-env": "^7.0.3", + "eslint": "^8.5.0", + "eslint-config-chartjs": "^0.3.0", + "eslint-plugin-es": "^4.1.0", + "eslint-plugin-html": "^6.1.2", + "eslint-plugin-markdown": "^2.2.1", + "esm": "^3.2.25", + "glob": "^7.1.6", + "jasmine": "^3.7.0", + "jasmine-core": "^3.7.1", + "karma": "^6.3.2", + "karma-chrome-launcher": "^3.1.0", + "karma-coverage": "^2.0.3", + "karma-edge-launcher": "^0.4.2", + "karma-firefox-launcher": "^2.1.0", + "karma-jasmine": "^4.0.1", + "karma-jasmine-html-reporter": "^1.5.4", + "karma-rollup-preprocessor": "^7.0.7", + "karma-safari-private-launcher": "^1.0.0", + "karma-spec-reporter": "0.0.32", + "luxon": "^2.2.0", + "markdown-it-include": "^2.0.0", + "moment": "^2.29.1", + "moment-timezone": "^0.5.34", + "pixelmatch": "^5.2.1", + "rollup": "^2.44.0", + "rollup-plugin-analyzer": "^4.0.0", + "rollup-plugin-cleanup": "^3.2.1", + "rollup-plugin-istanbul": "^3.0.0", + "rollup-plugin-terser": "^7.0.2", + "typedoc": "^0.22.10", + "typedoc-plugin-markdown": "^3.6.1", + "typescript": "^4.3.5", + "vue-tabs-component": "^1.5.0", + "vuepress": "^1.8.2", + "vuepress-plugin-code-copy": "^1.0.6", + "vuepress-plugin-flexsearch": "^0.3.0", + "vuepress-plugin-redirect": "^1.2.5", + "vuepress-plugin-tabs": "^0.3.0", + "vuepress-plugin-typedoc": "^0.10.0", + "vuepress-theme-chartjs": "^0.2.0", + "yargs": "^17.0.1" + } +} diff --git a/node_modules/chart.js/types/adapters.d.ts b/node_modules/chart.js/types/adapters.d.ts new file mode 100644 index 00000000..f06c41b6 --- /dev/null +++ b/node_modules/chart.js/types/adapters.d.ts @@ -0,0 +1,63 @@ +export type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'; + +export interface DateAdapter { + // Override one or multiple of the methods to adjust to the logic of the current date library. + override(members: Partial): void; + readonly options: unknown; + + /** + * Returns a map of time formats for the supported formatting units defined + * in Unit as well as 'datetime' representing a detailed date/time string. + * @returns {{string: string}} + */ + formats(): { [key: string]: string }; + /** + * Parses the given `value` and return the associated timestamp. + * @param {unknown} value - the value to parse (usually comes from the data) + * @param {string} [format] - the expected data format + */ + parse(value: unknown, format?: TimeUnit): number | null; + /** + * Returns the formatted date in the specified `format` for a given `timestamp`. + * @param {number} timestamp - the timestamp to format + * @param {string} format - the date/time token + * @return {string} + */ + format(timestamp: number, format: TimeUnit): string; + /** + * Adds the specified `amount` of `unit` to the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {number} amount - the amount to add + * @param {Unit} unit - the unit as string + * @return {number} + */ + add(timestamp: number, amount: number, unit: TimeUnit): number; + /** + * Returns the number of `unit` between the given timestamps. + * @param {number} a - the input timestamp (reference) + * @param {number} b - the timestamp to subtract + * @param {Unit} unit - the unit as string + * @return {number} + */ + diff(a: number, b: number, unit: TimeUnit): number; + /** + * Returns start of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit|'isoWeek'} unit - the unit as string + * @param {number} [weekday] - the ISO day of the week with 1 being Monday + * and 7 being Sunday (only needed if param *unit* is `isoWeek`). + * @return {number} + */ + startOf(timestamp: number, unit: TimeUnit | 'isoWeek', weekday?: number): number; + /** + * Returns end of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit|'isoWeek'} unit - the unit as string + * @return {number} + */ + endOf(timestamp: number, unit: TimeUnit | 'isoWeek'): number; +} + +export const _adapters: { + _date: DateAdapter; +}; diff --git a/node_modules/chart.js/types/animation.d.ts b/node_modules/chart.js/types/animation.d.ts new file mode 100644 index 00000000..b8320412 --- /dev/null +++ b/node_modules/chart.js/types/animation.d.ts @@ -0,0 +1,33 @@ +import { Chart } from './index.esm'; +import { AnyObject } from './basic'; + +export class Animation { + constructor(cfg: AnyObject, target: AnyObject, prop: string, to?: unknown); + active(): boolean; + update(cfg: AnyObject, to: unknown, date: number): void; + cancel(): void; + tick(date: number): void; +} + +export interface AnimationEvent { + chart: Chart; + numSteps: number; + initial: boolean; + currentStep: number; +} + +export class Animator { + listen(chart: Chart, event: 'complete' | 'progress', cb: (event: AnimationEvent) => void): void; + add(chart: Chart, items: readonly Animation[]): void; + has(chart: Chart): boolean; + start(chart: Chart): void; + running(chart: Chart): boolean; + stop(chart: Chart): void; + remove(chart: Chart): boolean; +} + +export class Animations { + constructor(chart: Chart, animations: AnyObject); + configure(animations: AnyObject): void; + update(target: AnyObject, values: AnyObject): undefined | boolean; +} diff --git a/node_modules/chart.js/types/basic.d.ts b/node_modules/chart.js/types/basic.d.ts new file mode 100644 index 00000000..1692c9cb --- /dev/null +++ b/node_modules/chart.js/types/basic.d.ts @@ -0,0 +1,3 @@ + +export type AnyObject = Record; +export type EmptyObject = Record; diff --git a/node_modules/chart.js/types/color.d.ts b/node_modules/chart.js/types/color.d.ts new file mode 100644 index 00000000..4a68f98b --- /dev/null +++ b/node_modules/chart.js/types/color.d.ts @@ -0,0 +1 @@ +export type Color = string | CanvasGradient | CanvasPattern; diff --git a/node_modules/chart.js/types/element.d.ts b/node_modules/chart.js/types/element.d.ts new file mode 100644 index 00000000..3b9359b3 --- /dev/null +++ b/node_modules/chart.js/types/element.d.ts @@ -0,0 +1,17 @@ +import { AnyObject } from './basic'; +import { Point } from './geometric'; + +export interface Element { + readonly x: number; + readonly y: number; + readonly active: boolean; + readonly options: O; + + tooltipPosition(useFinalPosition?: boolean): Point; + hasValue(): boolean; + getProps

(props: P, final?: boolean): Pick; +} +export const Element: { + prototype: Element; + new (): Element; +}; diff --git a/node_modules/chart.js/types/geometric.d.ts b/node_modules/chart.js/types/geometric.d.ts new file mode 100644 index 00000000..0e1affda --- /dev/null +++ b/node_modules/chart.js/types/geometric.d.ts @@ -0,0 +1,37 @@ +export interface ChartArea { + top: number; + left: number; + right: number; + bottom: number; + width: number; + height: number; +} + +export interface Point { + x: number; + y: number; +} + +export type TRBL = { + top: number; + right: number; + bottom: number; + left: number; +} + +export type TRBLCorners = { + topLeft: number; + topRight: number; + bottomLeft: number; + bottomRight: number; +}; + +export type CornerRadius = number | Partial; + +export type RoundedRect = { + x: number; + y: number; + w: number; + h: number; + radius?: CornerRadius +} diff --git a/node_modules/chart.js/types/helpers/helpers.canvas.d.ts b/node_modules/chart.js/types/helpers/helpers.canvas.d.ts new file mode 100644 index 00000000..44e570e6 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.canvas.d.ts @@ -0,0 +1,101 @@ +import { PointStyle } from '../index.esm'; +import { Color } from '../color'; +import { ChartArea, RoundedRect } from '../geometric'; +import { CanvasFontSpec } from './helpers.options'; + +export function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void; + +export function clipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void; + +export function unclipArea(ctx: CanvasRenderingContext2D): void; + +export interface DrawPointOptions { + pointStyle: PointStyle; + rotation?: number; + radius: number; + borderWidth: number; +} + +export function drawPoint(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number): void; + +/** + * Converts the given font object into a CSS font string. + * @param font a font object + * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font + */ +export function toFontString(font: { size: number; family: string; style?: string; weight?: string }): string | null; + +export interface RenderTextOpts { + /** + * The fill color of the text. If unset, the existing + * fillStyle property of the canvas is unchanged. + */ + color?: Color; + + /** + * The width of the strikethrough / underline + * @default 2 + */ + decorationWidth?: number; + + /** + * The max width of the text in pixels + */ + maxWidth?: number; + + /** + * A rotation to be applied to the canvas + * This is applied after the translation is applied + */ + rotation?: number; + + /** + * Apply a strikethrough effect to the text + */ + strikethrough?: boolean; + + /** + * The color of the text stroke. If unset, the existing + * strokeStyle property of the context is unchanged + */ + strokeColor?: Color; + + /** + * The text stroke width. If unset, the existing + * lineWidth property of the context is unchanged + */ + strokeWidth?: number; + + /** + * The text alignment to use. If unset, the existing + * textAlign property of the context is unchanged + */ + textAlign: CanvasTextAlign; + + /** + * The text baseline to use. If unset, the existing + * textBaseline property of the context is unchanged + */ + textBaseline: CanvasTextBaseline; + + /** + * If specified, a translation to apply to the context + */ + translation?: [number, number]; + + /** + * Underline the text + */ + underline?: boolean; +} + +export function renderText( + ctx: CanvasRenderingContext2D, + text: string | string[], + x: number, + y: number, + font: CanvasFontSpec, + opts?: RenderTextOpts +): void; + +export function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: RoundedRect): void; diff --git a/node_modules/chart.js/types/helpers/helpers.collection.d.ts b/node_modules/chart.js/types/helpers/helpers.collection.d.ts new file mode 100644 index 00000000..6a51597c --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.collection.d.ts @@ -0,0 +1,20 @@ +export interface ArrayListener { + _onDataPush?(...item: T[]): void; + _onDataPop?(): void; + _onDataShift?(): void; + _onDataSplice?(index: number, deleteCount: number, ...items: T[]): void; + _onDataUnshift?(...item: T[]): void; +} + +/** + * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', + * 'unshift') and notify the listener AFTER the array has been altered. Listeners are + * called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments. + */ +export function listenArrayEvents(array: T[], listener: ArrayListener): void; + +/** + * Removes the given array event listener and cleanup extra attached properties (such as + * the _chartjs stub and overridden methods) if array doesn't have any more listeners. + */ +export function unlistenArrayEvents(array: T[], listener: ArrayListener): void; diff --git a/node_modules/chart.js/types/helpers/helpers.color.d.ts b/node_modules/chart.js/types/helpers/helpers.color.d.ts new file mode 100644 index 00000000..3cfc20ea --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.color.d.ts @@ -0,0 +1,33 @@ +export function color(value: CanvasGradient): CanvasGradient; +export function color(value: CanvasPattern): CanvasPattern; +export function color( + value: + | string + | { r: number; g: number; b: number; a: number } + | [number, number, number] + | [number, number, number, number] +): ColorModel; + +export interface ColorModel { + rgbString(): string; + hexString(): string; + hslString(): string; + rgb: { r: number; g: number; b: number; a: number }; + valid: boolean; + mix(color: ColorModel, weight: number): this; + clone(): ColorModel; + alpha(a: number): ColorModel; + clearer(ration: number): ColorModel; + greyscale(): ColorModel; + opaquer(ratio: number): ColorModel; + negate(): ColorModel; + lighten(ratio: number): ColorModel; + darken(ratio: number): ColorModel; + saturate(ratio: number): ColorModel; + desaturate(ratio: number): ColorModel; + rotate(deg: number): this; +} + +export function getHoverColor(value: CanvasGradient): CanvasGradient; +export function getHoverColor(value: CanvasPattern): CanvasPattern; +export function getHoverColor(value: string): string; diff --git a/node_modules/chart.js/types/helpers/helpers.core.d.ts b/node_modules/chart.js/types/helpers/helpers.core.d.ts new file mode 100644 index 00000000..bc376da0 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.core.d.ts @@ -0,0 +1,157 @@ +import { AnyObject } from '../basic'; + +/** + * An empty function that can be used, for example, for optional callback. + */ +export function noop(): void; + +/** + * Returns a unique id, sequentially generated from a global variable. + * @returns {number} + * @function + */ +export function uid(): number; +/** + * Returns true if `value` is neither null nor undefined, else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ +export function isNullOrUndef(value: unknown): value is null | undefined; +/** + * Returns true if `value` is an array (including typed arrays), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @function + */ +export function isArray(value: unknown): value is ArrayLike; +/** + * Returns true if `value` is an object (excluding null), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ +export function isObject(value: unknown): value is AnyObject; +/** + * Returns true if `value` is a finite number, else returns false + * @param {*} value - The value to test. + * @returns {boolean} + */ +export function isFinite(value: unknown): value is number; + +/** + * Returns `value` if finite, else returns `defaultValue`. + * @param {*} value - The value to return if defined. + * @param {*} defaultValue - The value to return if `value` is not finite. + * @returns {*} + */ +export function finiteOrDefault(value: unknown, defaultValue: number): number; + +/** + * Returns `value` if defined, else returns `defaultValue`. + * @param {*} value - The value to return if defined. + * @param {*} defaultValue - The value to return if `value` is undefined. + * @returns {*} + */ +export function valueOrDefault(value: T | undefined, defaultValue: T): T; + +export function toPercentage(value: number | string, dimesion: number): number; +export function toDimension(value: number | string, dimension: number): number; + +/** + * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the + * value returned by `fn`. If `fn` is not a function, this method returns undefined. + * @param fn - The function to call. + * @param args - The arguments with which `fn` should be called. + * @param [thisArg] - The value of `this` provided for the call to `fn`. + * @returns {*} + */ +export function callback R, TA, R>( + fn: T | undefined, + args: unknown[], + thisArg?: TA +): R | undefined; + +/** + * Note(SB) for performance sake, this method should only be used when loopable type + * is unknown or in none intensive code (not called often and small loopable). Else + * it's preferable to use a regular for() loop and save extra function calls. + * @param loopable - The object or array to be iterated. + * @param fn - The function to call for each item. + * @param [thisArg] - The value of `this` provided for the call to `fn`. + * @param [reverse] - If true, iterates backward on the loopable. + */ +export function each( + loopable: T[], + fn: (this: TA, v: T, i: number) => void, + thisArg?: TA, + reverse?: boolean +): void; +/** + * Note(SB) for performance sake, this method should only be used when loopable type + * is unknown or in none intensive code (not called often and small loopable). Else + * it's preferable to use a regular for() loop and save extra function calls. + * @param loopable - The object or array to be iterated. + * @param fn - The function to call for each item. + * @param [thisArg] - The value of `this` provided for the call to `fn`. + * @param [reverse] - If true, iterates backward on the loopable. + */ +export function each( + loopable: { [key: string]: T }, + fn: (this: TA, v: T, k: string) => void, + thisArg?: TA, + reverse?: boolean +): void; + +/** + * Returns a deep copy of `source` without keeping references on objects and arrays. + * @param source - The value to clone. + */ +export function clone(source: T): T; + +export interface MergeOptions { + merger?: (key: string, target: AnyObject, source: AnyObject, options: AnyObject) => AnyObject; +} +/** + * Recursively deep copies `source` properties into `target` with the given `options`. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param target - The target object in which all sources are merged into. + * @param source - Object(s) to merge into `target`. + * @param {object} [options] - Merging options: + * @param {function} [options.merger] - The merge method (key, target, source, options) + * @returns {object} The `target` object. + */ +export function merge(target: T, source: [], options?: MergeOptions): T; +export function merge(target: T, source: S1, options?: MergeOptions): T & S1; +export function merge(target: T, source: [S1], options?: MergeOptions): T & S1; +export function merge(target: T, source: [S1, S2], options?: MergeOptions): T & S1 & S2; +export function merge(target: T, source: [S1, S2, S3], options?: MergeOptions): T & S1 & S2 & S3; +export function merge( + target: T, + source: [S1, S2, S3, S4], + options?: MergeOptions +): T & S1 & S2 & S3 & S4; +export function merge(target: T, source: AnyObject[], options?: MergeOptions): AnyObject; + +/** + * Recursively deep copies `source` properties into `target` *only* if not defined in target. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param target - The target object in which all sources are merged into. + * @param source - Object(s) to merge into `target`. + * @returns The `target` object. + */ +export function mergeIf(target: T, source: []): T; +export function mergeIf(target: T, source: S1): T & S1; +export function mergeIf(target: T, source: [S1]): T & S1; +export function mergeIf(target: T, source: [S1, S2]): T & S1 & S2; +export function mergeIf(target: T, source: [S1, S2, S3]): T & S1 & S2 & S3; +export function mergeIf(target: T, source: [S1, S2, S3, S4]): T & S1 & S2 & S3 & S4; +export function mergeIf(target: T, source: AnyObject[]): AnyObject; + +export function resolveObjectKey(obj: AnyObject, key: string): AnyObject; + +export function defined(value: unknown): boolean; + +export function isFunction(value: unknown): boolean; + +export function setsEqual(a: Set, b: Set): boolean; diff --git a/node_modules/chart.js/types/helpers/helpers.curve.d.ts b/node_modules/chart.js/types/helpers/helpers.curve.d.ts new file mode 100644 index 00000000..28d9ee4a --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.curve.d.ts @@ -0,0 +1,34 @@ +export interface SplinePoint { + x: number; + y: number; +} + +/** + * Props to Rob Spencer at scaled innovation for his post on splining between points + * http://scaledinnovation.com/analytics/splines/aboutSplines.html + */ +export function splineCurve( + firstPoint: SplinePoint & { skip?: boolean }, + middlePoint: SplinePoint, + afterPoint: SplinePoint, + t: number +): { + previous: SplinePoint; + next: SplinePoint; +}; + +export interface MonotoneSplinePoint extends SplinePoint { + skip: boolean; + cp1x?: number; + cp1y?: number; + cp2x?: number; + cp2y?: number; +} + +/** + * This function calculates Bézier control points in a similar way than |splineCurve|, + * but preserves monotonicity of the provided data and ensures no local extremums are added + * between the dataset discrete points due to the interpolation. + * @see https://en.wikipedia.org/wiki/Monotone_cubic_interpolation + */ +export function splineCurveMonotone(points: readonly MonotoneSplinePoint[], indexAxis?: 'x' | 'y'): void; diff --git a/node_modules/chart.js/types/helpers/helpers.dom.d.ts b/node_modules/chart.js/types/helpers/helpers.dom.d.ts new file mode 100644 index 00000000..73864314 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.dom.d.ts @@ -0,0 +1,20 @@ +import { ChartEvent } from '../index.esm'; + +export function getMaximumSize(node: HTMLElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number }; +export function getRelativePosition( + evt: MouseEvent | ChartEvent, + chart: { readonly canvas: HTMLCanvasElement } +): { x: number; y: number }; +export function getStyle(el: HTMLElement, property: string): string; +export function retinaScale( + chart: { + currentDevicePixelRatio: number; + readonly canvas: HTMLCanvasElement; + readonly width: number; + readonly height: number; + readonly ctx: CanvasRenderingContext2D; + }, + forceRatio: number, + forceStyle?: boolean +): void; +export function readUsedSize(element: HTMLElement, property: 'width' | 'height'): number | undefined; diff --git a/node_modules/chart.js/types/helpers/helpers.easing.d.ts b/node_modules/chart.js/types/helpers/helpers.easing.d.ts new file mode 100644 index 00000000..b86d6532 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.easing.d.ts @@ -0,0 +1,5 @@ +import { EasingFunction } from '../index.esm'; + +export type EasingFunctionSignature = (t: number) => number; + +export const easingEffects: Record; diff --git a/node_modules/chart.js/types/helpers/helpers.extras.d.ts b/node_modules/chart.js/types/helpers/helpers.extras.d.ts new file mode 100644 index 00000000..cb445c32 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.extras.d.ts @@ -0,0 +1,23 @@ +export function fontString(pixelSize: number, fontStyle: string, fontFamily: string): string; + +/** + * Request animation polyfill + */ +export function requestAnimFrame(cb: () => void): void; + +/** + * Throttles calling `fn` once per animation frame + * Latest arguments are used on the actual call + * @param {function} fn + * @param {*} thisArg + * @param {function} [updateFn] + */ +export function throttled(fn: (...args: unknown[]) => void, thisArg: unknown, updateFn?: (...args: unknown[]) => unknown[]): (...args: unknown[]) => void; + +/** + * Debounces calling `fn` for `delay` ms + * @param {function} fn - Function to call. No arguments are passed. + * @param {number} delay - Delay in ms. 0 = immediate invocation. + * @returns {function} + */ +export function debounce(fn: () => void, delay: number): () => number; diff --git a/node_modules/chart.js/types/helpers/helpers.interpolation.d.ts b/node_modules/chart.js/types/helpers/helpers.interpolation.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.interpolation.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/chart.js/types/helpers/helpers.intl.d.ts b/node_modules/chart.js/types/helpers/helpers.intl.d.ts new file mode 100644 index 00000000..3a896f4a --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.intl.d.ts @@ -0,0 +1,7 @@ +/** + * Format a number using a localized number formatter. + * @param num The number to format + * @param locale The locale to pass to the Intl.NumberFormat constructor + * @param options Number format options + */ +export function formatNumber(num: number, locale: string, options: Intl.NumberFormatOptions): string; diff --git a/node_modules/chart.js/types/helpers/helpers.math.d.ts b/node_modules/chart.js/types/helpers/helpers.math.d.ts new file mode 100644 index 00000000..cc58b30e --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.math.d.ts @@ -0,0 +1,17 @@ +export function log10(x: number): number; +export function isNumber(v: unknown): boolean; +export function almostEquals(x: number, y: number, epsilon: number): boolean; +export function almostWhole(x: number, epsilon: number): number; +export function sign(x: number): number; +export function niceNum(range: number): number; +export function toRadians(degrees: number): number; +export function toDegrees(radians: number): number; +/** + * Gets the angle from vertical upright to the point about a centre. + */ +export function getAngleFromPoint( + centrePoint: { x: number; y: number }, + anglePoint: { x: number; y: number } +): { angle: number; distance: number }; + +export function distanceBetweenPoints(pt1: { x: number; y: number }, pt2: { x: number; y: number }): number; diff --git a/node_modules/chart.js/types/helpers/helpers.options.d.ts b/node_modules/chart.js/types/helpers/helpers.options.d.ts new file mode 100644 index 00000000..0bd783fa --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.options.d.ts @@ -0,0 +1,61 @@ +import { TRBL, TRBLCorners } from '../geometric'; +import { FontSpec } from '../index.esm'; + +export interface CanvasFontSpec extends FontSpec { + string: string; +} +/** + * Parses font options and returns the font object. + * @param {object} options - A object that contains font options to be parsed. + * @return {object} The font object. + */ +export function toFont(options: Partial): CanvasFontSpec; + +/** + * Converts the given line height `value` in pixels for a specific font `size`. + * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). + * @param {number} size - The font size (in pixels) used to resolve relative `value`. + * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height + * @since 2.7.0 + */ +export function toLineHeight(value: string, size: number): number; + +export function toTRBL(value: number | Partial): TRBL; +export function toTRBLCorners(value: number | Partial): TRBLCorners; + +/** + * Converts the given value into a padding object with pre-computed width/height. + * @param {number|object} value - If a number, set the value to all TRBL component; + * else, if an object, use defined properties and sets undefined ones to 0. + * @returns {object} The padding values (top, right, bottom, left, width, height) + * @since 2.7.0 + */ +export function toPadding( + value?: number | { top?: number; left?: number; right?: number; bottom?: number; x?:number, y?: number } +): { top: number; left: number; right: number; bottom: number; width: number; height: number }; + +/** + * Evaluates the given `inputs` sequentially and returns the first defined value. + * @param inputs - An array of values, falling back to the last value. + * @param [context] - If defined and the current value is a function, the value + * is called with `context` as first argument and the result becomes the new input. + * @param [index] - If defined and the current value is an array, the value + * at `index` become the new input. + * @param [info] - object to return information about resolution in + * @param [info.cacheable] - Will be set to `false` if option is not cacheable. + * @since 2.7.0 + */ +export function resolve( + inputs: undefined | T | ((c: C) => T) | readonly T[], + context?: C, + index?: number, + info?: { cacheable?: boolean } +): T | undefined; + + +/** + * Create a context inheriting parentContext + * @since 3.6.0 + */ +export function createContext(parentContext: P, context: T): P extends null ? T : P & T; diff --git a/node_modules/chart.js/types/helpers/helpers.rtl.d.ts b/node_modules/chart.js/types/helpers/helpers.rtl.d.ts new file mode 100644 index 00000000..ed0b9248 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.rtl.d.ts @@ -0,0 +1,12 @@ +export interface RTLAdapter { + x(x: number): number; + setWidth(w: number): void; + textAlign(align: 'center' | 'left' | 'right'): 'center' | 'left' | 'right'; + xPlus(x: number, value: number): number; + leftForLtr(x: number, itemWidth: number): number; +} +export function getRtlAdapter(rtl: boolean, rectX: number, width: number): RTLAdapter; + +export function overrideTextDirection(ctx: CanvasRenderingContext2D, direction: 'ltr' | 'rtl'): void; + +export function restoreTextDirection(ctx: CanvasRenderingContext2D, original?: [string, string]): void; diff --git a/node_modules/chart.js/types/helpers/helpers.segment.d.ts b/node_modules/chart.js/types/helpers/helpers.segment.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/node_modules/chart.js/types/helpers/helpers.segment.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/chart.js/types/helpers/index.d.ts b/node_modules/chart.js/types/helpers/index.d.ts new file mode 100644 index 00000000..01332692 --- /dev/null +++ b/node_modules/chart.js/types/helpers/index.d.ts @@ -0,0 +1,15 @@ +export * from './helpers.canvas'; +export * from './helpers.collection'; +export * from './helpers.color'; +export * from './helpers.core'; +export * from './helpers.curve'; +export * from './helpers.dom'; +export * from './helpers.easing'; +export * from './helpers.extras'; +export * from './helpers.interpolation'; +export * from './helpers.intl'; +export * from './helpers.math'; +export * from './helpers.options'; +export * from './helpers.canvas'; +export * from './helpers.rtl'; +export * from './helpers.segment'; diff --git a/node_modules/chart.js/types/index.esm.d.ts b/node_modules/chart.js/types/index.esm.d.ts new file mode 100644 index 00000000..ccea4128 --- /dev/null +++ b/node_modules/chart.js/types/index.esm.d.ts @@ -0,0 +1,3601 @@ +import { DeepPartial, DistributiveArray, UnionToIntersection } from './utils'; + +import { TimeUnit } from './adapters'; +import { AnimationEvent } from './animation'; +import { AnyObject, EmptyObject } from './basic'; +import { Color } from './color'; +import { Element } from './element'; +import { ChartArea, Point } from './geometric'; +import { LayoutItem, LayoutPosition } from './layout'; + +export { DateAdapter, TimeUnit, _adapters } from './adapters'; +export { Animation, Animations, Animator, AnimationEvent } from './animation'; +export { Color } from './color'; +export { Element } from './element'; +export { ChartArea, Point } from './geometric'; +export { LayoutItem, LayoutPosition } from './layout'; + +export interface ScriptableContext { + active: boolean; + chart: Chart; + dataIndex: number; + dataset: UnionToIntersection>; + datasetIndex: number; + parsed: UnionToIntersection>; + raw: unknown; +} + +export interface ScriptableLineSegmentContext { + type: 'segment', + p0: PointElement, + p1: PointElement, + p0DataIndex: number, + p1DataIndex: number, + datasetIndex: number +} + +export type Scriptable = T | ((ctx: TContext, options: AnyObject) => T | undefined); +export type ScriptableOptions = { [P in keyof T]: Scriptable }; +export type ScriptableAndArray = readonly T[] | Scriptable; +export type ScriptableAndArrayOptions = { [P in keyof T]: ScriptableAndArray }; + +export interface ParsingOptions { + /** + * How to parse the dataset. The parsing can be disabled by specifying parsing: false at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally. + */ + parsing: + { + [key: string]: string; + } + | false; + + /** + * Chart.js is fastest if you provide data with indices that are unique, sorted, and consistent across datasets and provide the normalized: true option to let Chart.js know that you have done so. + */ + normalized: boolean; +} + +export interface ControllerDatasetOptions extends ParsingOptions { + /** + * The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts. + * @default 'x' + */ + indexAxis: 'x' | 'y'; + /** + * How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. 0 = clip at chartArea. Clipping can also be configured per side: clip: {left: 5, top: false, right: -2, bottom: 0} + */ + clip: number | ChartArea; + /** + * The label for the dataset which appears in the legend and tooltips. + */ + label: string; + /** + * The drawing order of dataset. Also affects order for stacking, tooltip and legend. + */ + order: number; + + /** + * The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack). + */ + stack: string; + /** + * Configures the visibility state of the dataset. Set it to true, to hide the dataset from the chart. + * @default false + */ + hidden: boolean; +} + +export interface BarControllerDatasetOptions + extends ControllerDatasetOptions, + ScriptableAndArrayOptions>, + ScriptableAndArrayOptions>, + AnimationOptions<'bar'> { + /** + * The ID of the x axis to plot this dataset on. + */ + xAxisID: string; + /** + * The ID of the y axis to plot this dataset on. + */ + yAxisID: string; + + /** + * Percent (0-1) of the available width each bar should be within the category width. 1.0 will take the whole category width and put the bars right next to each other. + * @default 0.9 + */ + barPercentage: number; + /** + * Percent (0-1) of the available width each category should be within the sample width. + * @default 0.8 + */ + categoryPercentage: number; + + /** + * Manually set width of each bar in pixels. If set to 'flex', it computes "optimal" sample widths that globally arrange bars side by side. If not set (default), bars are equally sized based on the smallest interval. + */ + barThickness: number | 'flex'; + + /** + * Set this to ensure that bars are not sized thicker than this. + */ + maxBarThickness: number; + + /** + * Set this to ensure that bars have a minimum length in pixels. + */ + minBarLength: number; + + /** + * Point style for the legend + * @default 'circle; + */ + pointStyle: PointStyle; +} + +export interface BarControllerChartOptions { + /** + * Should null or undefined values be omitted from drawing + */ + skipNull?: boolean; +} + +export type BarController = DatasetController +export const BarController: ChartComponent & { + prototype: BarController; + new (chart: Chart, datasetIndex: number): BarController; +}; + +export interface BubbleControllerDatasetOptions + extends ControllerDatasetOptions, + ScriptableAndArrayOptions>, + ScriptableAndArrayOptions> {} + +export interface BubbleDataPoint { + /** + * X Value + */ + x: number; + + /** + * Y Value + */ + y: number; + + /** + * Bubble radius in pixels (not scaled). + */ + r: number; +} + +export type BubbleController = DatasetController +export const BubbleController: ChartComponent & { + prototype: BubbleController; + new (chart: Chart, datasetIndex: number): BubbleController; +}; + +export interface LineControllerDatasetOptions + extends ControllerDatasetOptions, + ScriptableAndArrayOptions>, + ScriptableAndArrayOptions>, + ScriptableOptions>, + ScriptableOptions>, + AnimationOptions<'line'> { + /** + * The ID of the x axis to plot this dataset on. + */ + xAxisID: string; + /** + * The ID of the y axis to plot this dataset on. + */ + yAxisID: string; + + /** + * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. + * @default false + */ + spanGaps: boolean | number; + + showLine: boolean; +} + +export interface LineControllerChartOptions { + /** + * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. + * @default false + */ + spanGaps: boolean | number; + /** + * If false, the lines between points are not drawn. + * @default true + */ + showLine: boolean; +} + +export type LineController = DatasetController +export const LineController: ChartComponent & { + prototype: LineController; + new (chart: Chart, datasetIndex: number): LineController; +}; + +export type ScatterControllerDatasetOptions = LineControllerDatasetOptions; + +export interface ScatterDataPoint { + x: number; + y: number; +} + +export type ScatterControllerChartOptions = LineControllerChartOptions; + +export type ScatterController = LineController +export const ScatterController: ChartComponent & { + prototype: ScatterController; + new (chart: Chart, datasetIndex: number): ScatterController; +}; + +export interface DoughnutControllerDatasetOptions + extends ControllerDatasetOptions, + ScriptableAndArrayOptions>, + ScriptableAndArrayOptions>, + AnimationOptions<'doughnut'> { + + /** + * Sweep to allow arcs to cover. + * @default 360 + */ + circumference: number; + + /** + * Arc offset (in pixels). + */ + offset: number; + + /** + * Starting angle to draw this dataset from. + * @default 0 + */ + rotation: number; + + /** + * The relative thickness of the dataset. Providing a value for weight will cause the pie or doughnut dataset to be drawn with a thickness relative to the sum of all the dataset weight values. + * @default 1 + */ + weight: number; + + /** + * Similar to the `offset` option, but applies to all arcs. This can be used to to add spaces + * between arcs + * @default 0 + */ + spacing: number; +} + +export interface DoughnutAnimationOptions { + /** + * If true, the chart will animate in with a rotation animation. This property is in the options.animation object. + * @default true + */ + animateRotate: boolean; + + /** + * If true, will animate scaling the chart from the center outwards. + * @default false + */ + animateScale: boolean; +} + +export interface DoughnutControllerChartOptions { + /** + * Sweep to allow arcs to cover. + * @default 360 + */ + circumference: number; + + /** + * The portion of the chart that is cut out of the middle. ('50%' - for doughnut, 0 - for pie) + * String ending with '%' means percentage, number means pixels. + * @default 50 + */ + cutout: Scriptable>; + + /** + * Arc offset (in pixels). + */ + offset: number; + + /** + * The outer radius of the chart. String ending with '%' means percentage of maximum radius, number means pixels. + * @default '100%' + */ + radius: Scriptable>; + + /** + * Starting angle to draw arcs from. + * @default 0 + */ + rotation: number; + + /** + * Spacing between the arcs + * @default 0 + */ + spacing: number; + + animation: false | DoughnutAnimationOptions; +} + +export type DoughnutDataPoint = number; + +export interface DoughnutController extends DatasetController { + readonly innerRadius: number; + readonly outerRadius: number; + readonly offsetX: number; + readonly offsetY: number; + + calculateTotal(): number; + calculateCircumference(value: number): number; +} + +export const DoughnutController: ChartComponent & { + prototype: DoughnutController; + new (chart: Chart, datasetIndex: number): DoughnutController; +}; + +export interface DoughnutMetaExtensions { + total: number; +} + +export type PieControllerDatasetOptions = DoughnutControllerDatasetOptions; +export type PieControllerChartOptions = DoughnutControllerChartOptions; +export type PieAnimationOptions = DoughnutAnimationOptions; + +export type PieDataPoint = DoughnutDataPoint; +export type PieMetaExtensions = DoughnutMetaExtensions; + +export type PieController = DoughnutController +export const PieController: ChartComponent & { + prototype: PieController; + new (chart: Chart, datasetIndex: number): PieController; +}; + +export interface PolarAreaControllerDatasetOptions extends DoughnutControllerDatasetOptions { + /** + * Arc angle to cover. - for polar only + * @default circumference / (arc count) + */ + angle: number; +} + +export type PolarAreaAnimationOptions = DoughnutAnimationOptions; + +export interface PolarAreaControllerChartOptions { + /** + * Starting angle to draw arcs for the first item in a dataset. In degrees, 0 is at top. + * @default 0 + */ + startAngle: number; + + animation: false | PolarAreaAnimationOptions; +} + +export interface PolarAreaController extends DoughnutController { + countVisibleElements(): number; +} +export const PolarAreaController: ChartComponent & { + prototype: PolarAreaController; + new (chart: Chart, datasetIndex: number): PolarAreaController; +}; + +export interface RadarControllerDatasetOptions + extends ControllerDatasetOptions, + ScriptableAndArrayOptions>, + ScriptableAndArrayOptions>, + AnimationOptions<'radar'> { + /** + * The ID of the x axis to plot this dataset on. + */ + xAxisID: string; + /** + * The ID of the y axis to plot this dataset on. + */ + yAxisID: string; + + /** + * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. + */ + spanGaps: boolean | number; + + /** + * If false, the line is not drawn for this dataset. + */ + showLine: boolean; +} + +export type RadarControllerChartOptions = LineControllerChartOptions; + +export type RadarController = DatasetController +export const RadarController: ChartComponent & { + prototype: RadarController; + new (chart: Chart, datasetIndex: number): RadarController; +}; +interface ChartMetaCommon { + type: string; + controller: DatasetController; + order: number; + + label: string; + index: number; + visible: boolean; + + stack: number; + + indexAxis: 'x' | 'y'; + + data: TElement[]; + dataset?: TDatasetElement; + + hidden: boolean; + + xAxisID?: string; + yAxisID?: string; + rAxisID?: string; + iAxisID: string; + vAxisID: string; + + xScale?: Scale; + yScale?: Scale; + rScale?: Scale; + iScale?: Scale; + vScale?: Scale; + + _sorted: boolean; + _stacked: boolean | 'single'; + _parsed: unknown[]; +} + +export type ChartMeta< + TElement extends Element = Element, + TDatasetElement extends Element = Element, + // TODO - V4, move this to the first parameter. + // When this was introduced, doing so was a breaking change + TType extends ChartType = ChartType, +> = DeepPartial< +{ [key in ChartType]: ChartTypeRegistry[key]['metaExtensions'] }[TType] +> & ChartMetaCommon; + +export interface ActiveDataPoint { + datasetIndex: number; + index: number; +} + +export interface ActiveElement extends ActiveDataPoint { + element: Element; +} + +export declare class Chart< + TType extends ChartType = ChartType, + TData = DefaultDataPoint, + TLabel = unknown +> { + readonly platform: BasePlatform; + readonly id: string; + readonly canvas: HTMLCanvasElement; + readonly ctx: CanvasRenderingContext2D; + readonly config: ChartConfiguration; + readonly width: number; + readonly height: number; + readonly aspectRatio: number; + readonly boxes: LayoutItem[]; + readonly currentDevicePixelRatio: number; + readonly chartArea: ChartArea; + readonly scales: { [key: string]: Scale }; + readonly attached: boolean; + + readonly legend?: LegendElement; // Only available if legend plugin is registered and enabled + readonly tooltip?: TooltipModel; // Only available if tooltip plugin is registered and enabled + + data: ChartData; + options: ChartOptions; + + constructor(item: ChartItem, config: ChartConfiguration); + + clear(): this; + stop(): this; + + resize(width?: number, height?: number): void; + ensureScalesHaveIDs(): void; + buildOrUpdateScales(): void; + buildOrUpdateControllers(): void; + reset(): void; + update(mode?: UpdateMode): void; + render(): void; + draw(): void; + + getElementsAtEventForMode(e: Event, mode: string, options: InteractionOptions, useFinalPosition: boolean): InteractionItem[]; + + getSortedVisibleDatasetMetas(): ChartMeta[]; + getDatasetMeta(datasetIndex: number): ChartMeta; + getVisibleDatasetCount(): number; + isDatasetVisible(datasetIndex: number): boolean; + setDatasetVisibility(datasetIndex: number, visible: boolean): void; + toggleDataVisibility(index: number): void; + getDataVisibility(index: number): boolean; + hide(datasetIndex: number, dataIndex?: number): void; + show(datasetIndex: number, dataIndex?: number): void; + + getActiveElements(): ActiveElement[]; + setActiveElements(active: ActiveDataPoint[]): void; + + destroy(): void; + toBase64Image(type?: string, quality?: unknown): string; + bindEvents(): void; + unbindEvents(): void; + updateHoverStyle(items: InteractionItem[], mode: 'dataset', enabled: boolean): void; + + notifyPlugins(hook: string, args?: AnyObject): boolean | void; + + static readonly defaults: Defaults; + static readonly overrides: Overrides; + static readonly version: string; + static readonly instances: { [key: string]: Chart }; + static readonly registry: Registry; + static getChart(key: string | CanvasRenderingContext2D | HTMLCanvasElement): Chart | undefined; + static register(...items: ChartComponentLike[]): void; + static unregister(...items: ChartComponentLike[]): void; +} + +export const registerables: readonly ChartComponentLike[]; + +export declare type ChartItem = + | string + | CanvasRenderingContext2D + | HTMLCanvasElement + | { canvas: HTMLCanvasElement } + | ArrayLike; + +export declare enum UpdateModeEnum { + resize = 'resize', + reset = 'reset', + none = 'none', + hide = 'hide', + show = 'show', + normal = 'normal', + active = 'active' +} + +export type UpdateMode = keyof typeof UpdateModeEnum; + +export class DatasetController< + TType extends ChartType = ChartType, + TElement extends Element = Element, + TDatasetElement extends Element = Element, + TParsedData = ParsedDataType, +> { + constructor(chart: Chart, datasetIndex: number); + + readonly chart: Chart; + readonly index: number; + readonly _cachedMeta: ChartMeta; + enableOptionSharing: boolean; + + linkScales(): void; + getAllParsedValues(scale: Scale): number[]; + protected getLabelAndValue(index: number): { label: string; value: string }; + updateElements(elements: TElement[], start: number, count: number, mode: UpdateMode): void; + update(mode: UpdateMode): void; + updateIndex(datasetIndex: number): void; + protected getMaxOverflow(): boolean | number; + draw(): void; + reset(): void; + getDataset(): ChartDataset; + getMeta(): ChartMeta; + getScaleForId(scaleID: string): Scale | undefined; + configure(): void; + initialize(): void; + addElements(): void; + buildOrUpdateElements(resetNewElements?: boolean): void; + + getStyle(index: number, active: boolean): AnyObject; + protected resolveDatasetElementOptions(mode: UpdateMode): AnyObject; + protected resolveDataElementOptions(index: number, mode: UpdateMode): AnyObject; + /** + * Utility for checking if the options are shared and should be animated separately. + * @protected + */ + protected getSharedOptions(options: AnyObject): undefined | AnyObject; + /** + * Utility for determining if `options` should be included in the updated properties + * @protected + */ + protected includeOptions(mode: UpdateMode, sharedOptions: AnyObject): boolean; + /** + * Utility for updating an element with new properties, using animations when appropriate. + * @protected + */ + + protected updateElement(element: TElement | TDatasetElement, index: number | undefined, properties: AnyObject, mode: UpdateMode): void; + /** + * Utility to animate the shared options, that are potentially affecting multiple elements. + * @protected + */ + + protected updateSharedOptions(sharedOptions: AnyObject, mode: UpdateMode, newOptions: AnyObject): void; + removeHoverStyle(element: TElement, datasetIndex: number, index: number): void; + setHoverStyle(element: TElement, datasetIndex: number, index: number): void; + + parse(start: number, count: number): void; + protected parsePrimitiveData(meta: ChartMeta, data: AnyObject[], start: number, count: number): AnyObject[]; + protected parseArrayData(meta: ChartMeta, data: AnyObject[], start: number, count: number): AnyObject[]; + protected parseObjectData(meta: ChartMeta, data: AnyObject[], start: number, count: number): AnyObject[]; + protected getParsed(index: number): TParsedData; + protected applyStack(scale: Scale, parsed: unknown[]): number; + protected updateRangeFromParsed( + range: { min: number; max: number }, + scale: Scale, + parsed: unknown[], + stack: boolean | string + ): void; + protected getMinMax(scale: Scale, canStack?: boolean): { min: number; max: number }; +} + +export interface DatasetControllerChartComponent extends ChartComponent { + defaults: { + datasetElementType?: string | null | false; + dataElementType?: string | null | false; + }; +} + +export interface Defaults extends CoreChartOptions, ElementChartOptions, PluginChartOptions { + + scale: ScaleOptionsByType; + scales: { + [key in ScaleType]: ScaleOptionsByType; + }; + + set(values: AnyObject): AnyObject; + set(scope: string, values: AnyObject): AnyObject; + get(scope: string): AnyObject; + + describe(scope: string, values: AnyObject): AnyObject; + override(scope: string, values: AnyObject): AnyObject; + + /** + * Routes the named defaults to fallback to another scope/name. + * This routing is useful when those target values, like defaults.color, are changed runtime. + * If the values would be copied, the runtime change would not take effect. By routing, the + * fallback is evaluated at each access, so its always up to date. + * + * Example: + * + * defaults.route('elements.arc', 'backgroundColor', '', 'color') + * - reads the backgroundColor from defaults.color when undefined locally + * + * @param scope Scope this route applies to. + * @param name Property name that should be routed to different namespace when not defined here. + * @param targetScope The namespace where those properties should be routed to. + * Empty string ('') is the root of defaults. + * @param targetName The target name in the target scope the property should be routed to. + */ + route(scope: string, name: string, targetScope: string, targetName: string): void; +} + +export type Overrides = { + [key in ChartType]: + CoreChartOptions & + ElementChartOptions & + PluginChartOptions & + DatasetChartOptions & + ScaleChartOptions & + ChartTypeRegistry[key]['chartOptions']; +} + +export const defaults: Defaults; +export interface InteractionOptions { + axis?: string; + intersect?: boolean; +} + +export interface InteractionItem { + element: Element; + datasetIndex: number; + index: number; +} + +export type InteractionModeFunction = ( + chart: Chart, + e: ChartEvent, + options: InteractionOptions, + useFinalPosition?: boolean +) => InteractionItem[]; + +export interface InteractionModeMap { + /** + * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item + */ + index: InteractionModeFunction; + + /** + * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect is false, we find the nearest item and return the items in that dataset + */ + dataset: InteractionModeFunction; + /** + * Point mode returns all elements that hit test based on the event position + * of the event + */ + point: InteractionModeFunction; + /** + * nearest mode returns the element closest to the point + */ + nearest: InteractionModeFunction; + /** + * x mode returns the elements that hit-test at the current x coordinate + */ + x: InteractionModeFunction; + /** + * y mode returns the elements that hit-test at the current y coordinate + */ + y: InteractionModeFunction; +} + +export type InteractionMode = keyof InteractionModeMap; + +export const Interaction: { + modes: InteractionModeMap; +}; + +export const layouts: { + /** + * Register a box to a chart. + * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. + * @param {Chart} chart - the chart to use + * @param {LayoutItem} item - the item to add to be laid out + */ + addBox(chart: Chart, item: LayoutItem): void; + + /** + * Remove a layoutItem from a chart + * @param {Chart} chart - the chart to remove the box from + * @param {LayoutItem} layoutItem - the item to remove from the layout + */ + removeBox(chart: Chart, layoutItem: LayoutItem): void; + + /** + * Sets (or updates) options on the given `item`. + * @param {Chart} chart - the chart in which the item lives (or will be added to) + * @param {LayoutItem} item - the item to configure with the given options + * @param options - the new item options. + */ + configure( + chart: Chart, + item: LayoutItem, + options: { fullSize?: number; position?: LayoutPosition; weight?: number } + ): void; + + /** + * Fits boxes of the given chart into the given size by having each box measure itself + * then running a fitting algorithm + * @param {Chart} chart - the chart + * @param {number} width - the width to fit into + * @param {number} height - the height to fit into + */ + update(chart: Chart, width: number, height: number): void; +}; + +export interface Plugin extends ExtendedPlugin { + id: string; + + /** + * @desc Called when plugin is installed for this chart instance. This hook is also invoked for disabled plugins (options === false). + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @since 3.0.0 + */ + install?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called when a plugin is starting. This happens when chart is created or plugin is enabled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @since 3.0.0 + */ + start?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called when a plugin stopping. This happens when chart is destroyed or plugin is disabled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @since 3.0.0 + */ + stop?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called before initializing `chart`. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + beforeInit?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called after `chart` has been initialized and before the first update. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + afterInit?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called before updating `chart`. If any plugin returns `false`, the update + * is cancelled (and thus subsequent render(s)) until another `update` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {UpdateMode} args.mode - The update mode + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart update. + */ + beforeUpdate?(chart: Chart, args: { mode: UpdateMode, cancelable: true }, options: O): boolean | void; + /** + * @desc Called after `chart` has been updated and before rendering. Note that this + * hook will not be called if the chart update has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {UpdateMode} args.mode - The update mode + * @param {object} options - The plugin options. + */ + afterUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): void; + /** + * @desc Called during the update process, before any chart elements have been created. + * This can be used for data decimation by changing the data array inside a dataset. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + beforeElementsUpdate?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called during chart reset + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @since version 3.0.0 + */ + reset?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called before updating the `chart` datasets. If any plugin returns `false`, + * the datasets update is cancelled until another `update` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {UpdateMode} args.mode - The update mode. + * @param {object} options - The plugin options. + * @returns {boolean} false to cancel the datasets update. + * @since version 2.1.5 + */ + beforeDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): boolean | void; + /** + * @desc Called after the `chart` datasets have been updated. Note that this hook + * will not be called if the datasets update has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {UpdateMode} args.mode - The update mode. + * @param {object} options - The plugin options. + * @since version 2.1.5 + */ + afterDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode, cancelable: true }, options: O): void; + /** + * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin + * returns `false`, the datasets update is cancelled until another `update` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {number} args.index - The dataset index. + * @param {object} args.meta - The dataset metadata. + * @param {UpdateMode} args.mode - The update mode. + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart datasets drawing. + */ + beforeDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: true }, options: O): boolean | void; + /** + * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note + * that this hook will not be called if the datasets update has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {number} args.index - The dataset index. + * @param {object} args.meta - The dataset metadata. + * @param {UpdateMode} args.mode - The update mode. + * @param {object} options - The plugin options. + */ + afterDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: false }, options: O): void; + /** + * @desc Called before laying out `chart`. If any plugin returns `false`, + * the layout update is cancelled until another `update` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart layout. + */ + beforeLayout?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; + /** + * @desc Called before scale data limits are calculated. This hook is called separately for each scale in the chart. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {Scale} args.scale - The scale. + * @param {object} options - The plugin options. + */ + beforeDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void; + /** + * @desc Called after scale data limits are calculated. This hook is called separately for each scale in the chart. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {Scale} args.scale - The scale. + * @param {object} options - The plugin options. + */ + afterDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void; + /** + * @desc Called before scale builds its ticks. This hook is called separately for each scale in the chart. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {Scale} args.scale - The scale. + * @param {object} options - The plugin options. + */ + beforeBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void; + /** + * @desc Called after scale has build its ticks. This hook is called separately for each scale in the chart. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {Scale} args.scale - The scale. + * @param {object} options - The plugin options. + */ + afterBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void; + /** + * @desc Called after the `chart` has been laid out. Note that this hook will not + * be called if the layout update has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + afterLayout?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called before rendering `chart`. If any plugin returns `false`, + * the rendering is cancelled until another `render` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart rendering. + */ + beforeRender?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; + /** + * @desc Called after the `chart` has been fully rendered (and animation completed). Note + * that this hook will not be called if the rendering has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + afterRender?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called before drawing `chart` at every animation frame. If any plugin returns `false`, + * the frame drawing is cancelled untilanother `render` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart drawing. + */ + beforeDraw?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; + /** + * @desc Called after the `chart` has been drawn. Note that this hook will not be called + * if the drawing has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + afterDraw?(chart: Chart, args: EmptyObject, options: O): void; + /** + * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, + * the datasets drawing is cancelled until another `render` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart datasets drawing. + */ + beforeDatasetsDraw?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; + /** + * @desc Called after the `chart` datasets have been drawn. Note that this hook + * will not be called if the datasets drawing has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + afterDatasetsDraw?(chart: Chart, args: EmptyObject, options: O, cancelable: false): void; + /** + * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets + * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing + * is cancelled until another `render` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {number} args.index - The dataset index. + * @param {object} args.meta - The dataset metadata. + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart datasets drawing. + */ + beforeDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): boolean | void; + /** + * @desc Called after the `chart` datasets at the given `args.index` have been drawn + * (datasets are drawn in the reverse order). Note that this hook will not be called + * if the datasets drawing has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {number} args.index - The dataset index. + * @param {object} args.meta - The dataset metadata. + * @param {object} options - The plugin options. + */ + afterDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): void; + /** + * @desc Called before processing the specified `event`. If any plugin returns `false`, + * the event will be discarded. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {ChartEvent} args.event - The event object. + * @param {boolean} args.replay - True if this event is replayed from `Chart.update` + * @param {boolean} args.inChartArea - The event position is inside chartArea + * @param {object} options - The plugin options. + */ + beforeEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean, cancelable: true, inChartArea: boolean }, options: O): boolean | void; + /** + * @desc Called after the `event` has been consumed. Note that this hook + * will not be called if the `event` has been previously discarded. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {ChartEvent} args.event - The event object. + * @param {boolean} args.replay - True if this event is replayed from `Chart.update` + * @param {boolean} args.inChartArea - The event position is inside chartArea + * @param {boolean} [args.changed] - Set to true if the plugin needs a render. Should only be changed to true, because this args object is passed through all plugins. + * @param {object} options - The plugin options. + */ + afterEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean, changed?: boolean, cancelable: false, inChartArea: boolean }, options: O): void; + /** + * @desc Called after the chart as been resized. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {number} args.size - The new canvas display size (eq. canvas.style width & height). + * @param {object} options - The plugin options. + */ + resize?(chart: Chart, args: { size: { width: number, height: number } }, options: O): void; + /** + * Called before the chart is being destroyed. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + beforeDestroy?(chart: Chart, args: EmptyObject, options: O): void; + /** + * Called after the chart has been destroyed. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @deprecated since version 3.7.0 in favour of afterDestroy + */ + destroy?(chart: Chart, args: EmptyObject, options: O): void; + /** + * Called after the chart has been destroyed. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + */ + afterDestroy?(chart: Chart, args: EmptyObject, options: O): void; + /** + * Called after chart is destroyed on all plugins that were installed for that chart. This hook is also invoked for disabled plugins (options === false). + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {object} options - The plugin options. + * @since 3.0.0 + */ + uninstall?(chart: Chart, args: EmptyObject, options: O): void; +} + +export declare type ChartComponentLike = ChartComponent | ChartComponent[] | { [key: string]: ChartComponent } | Plugin | Plugin[]; + +/** + * Please use the module's default export which provides a singleton instance + * Note: class is exported for typedoc + */ +export interface Registry { + readonly controllers: TypedRegistry; + readonly elements: TypedRegistry; + readonly plugins: TypedRegistry; + readonly scales: TypedRegistry; + + add(...args: ChartComponentLike[]): void; + remove(...args: ChartComponentLike[]): void; + + addControllers(...args: ChartComponentLike[]): void; + addElements(...args: ChartComponentLike[]): void; + addPlugins(...args: ChartComponentLike[]): void; + addScales(...args: ChartComponentLike[]): void; + + getController(id: string): DatasetController | undefined; + getElement(id: string): Element | undefined; + getPlugin(id: string): Plugin | undefined; + getScale(id: string): Scale | undefined; +} + +export const registry: Registry; + +export interface Tick { + value: number; + label?: string | string[]; + major?: boolean; +} + +export interface CoreScaleOptions { + /** + * Controls the axis global visibility (visible when true, hidden when false). When display: 'auto', the axis is visible only if at least one associated dataset is visible. + * @default true + */ + display: boolean | 'auto'; + /** + * Align pixel values to device pixels + */ + alignToPixels: boolean; + /** + * Reverse the scale. + * @default false + */ + reverse: boolean; + /** + * The weight used to sort the axis. Higher weights are further away from the chart area. + * @default true + */ + weight: number; + /** + * Callback called before the update process starts. + */ + beforeUpdate(axis: Scale): void; + /** + * Callback that runs before dimensions are set. + */ + beforeSetDimensions(axis: Scale): void; + /** + * Callback that runs after dimensions are set. + */ + afterSetDimensions(axis: Scale): void; + /** + * Callback that runs before data limits are determined. + */ + beforeDataLimits(axis: Scale): void; + /** + * Callback that runs after data limits are determined. + */ + afterDataLimits(axis: Scale): void; + /** + * Callback that runs before ticks are created. + */ + beforeBuildTicks(axis: Scale): void; + /** + * Callback that runs after ticks are created. Useful for filtering ticks. + */ + afterBuildTicks(axis: Scale): void; + /** + * Callback that runs before ticks are converted into strings. + */ + beforeTickToLabelConversion(axis: Scale): void; + /** + * Callback that runs after ticks are converted into strings. + */ + afterTickToLabelConversion(axis: Scale): void; + /** + * Callback that runs before tick rotation is determined. + */ + beforeCalculateLabelRotation(axis: Scale): void; + /** + * Callback that runs after tick rotation is determined. + */ + afterCalculateLabelRotation(axis: Scale): void; + /** + * Callback that runs before the scale fits to the canvas. + */ + beforeFit(axis: Scale): void; + /** + * Callback that runs after the scale fits to the canvas. + */ + afterFit(axis: Scale): void; + /** + * Callback that runs at the end of the update process. + */ + afterUpdate(axis: Scale): void; +} + +export interface Scale extends Element, LayoutItem { + readonly id: string; + readonly type: string; + readonly ctx: CanvasRenderingContext2D; + readonly chart: Chart; + + maxWidth: number; + maxHeight: number; + + paddingTop: number; + paddingBottom: number; + paddingLeft: number; + paddingRight: number; + + axis: string; + labelRotation: number; + min: number; + max: number; + ticks: Tick[]; + getMatchingVisibleMetas(type?: string): ChartMeta[]; + + drawTitle(chartArea: ChartArea): void; + drawLabels(chartArea: ChartArea): void; + drawGrid(chartArea: ChartArea): void; + + /** + * @param {number} pixel + * @return {number} + */ + getDecimalForPixel(pixel: number): number; + /** + * Utility for getting the pixel location of a percentage of scale + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param {number} decimal + * @return {number} + */ + getPixelForDecimal(decimal: number): number; + /** + * Returns the location of the tick at the given index + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param {number} index + * @return {number} + */ + getPixelForTick(index: number): number; + /** + * Used to get the label to display in the tooltip for the given value + * @param {*} value + * @return {string} + */ + getLabelForValue(value: number): string; + + /** + * Returns the grid line width at given value + */ + getLineWidthForValue(value: number): number; + + /** + * Returns the location of the given data point. Value can either be an index or a numerical value + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param {*} value + * @param {number} [index] + * @return {number} + */ + getPixelForValue(value: number, index?: number): number; + + /** + * Used to get the data value from a given pixel. This is the inverse of getPixelForValue + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param {number} pixel + * @return {*} + */ + getValueForPixel(pixel: number): number | undefined; + + getBaseValue(): number; + /** + * Returns the pixel for the minimum chart value + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @return {number} + */ + getBasePixel(): number; + + init(options: O): void; + parse(raw: unknown, index: number): unknown; + getUserBounds(): { min: number; max: number; minDefined: boolean; maxDefined: boolean }; + getMinMax(canStack: boolean): { min: number; max: number }; + getTicks(): Tick[]; + getLabels(): string[]; + beforeUpdate(): void; + configure(): void; + afterUpdate(): void; + beforeSetDimensions(): void; + setDimensions(): void; + afterSetDimensions(): void; + beforeDataLimits(): void; + determineDataLimits(): void; + afterDataLimits(): void; + beforeBuildTicks(): void; + buildTicks(): Tick[]; + afterBuildTicks(): void; + beforeTickToLabelConversion(): void; + generateTickLabels(ticks: Tick[]): void; + afterTickToLabelConversion(): void; + beforeCalculateLabelRotation(): void; + calculateLabelRotation(): void; + afterCalculateLabelRotation(): void; + beforeFit(): void; + fit(): void; + afterFit(): void; + + isFullSize(): boolean; +} +export declare class Scale { + constructor(cfg: {id: string, type: string, ctx: CanvasRenderingContext2D, chart: Chart}); +} + +export interface ScriptableScaleContext { + chart: Chart; + scale: Scale; + index: number; + tick: Tick; +} + +export interface ScriptableScalePointLabelContext { + chart: Chart; + scale: Scale; + index: number; + label: string; +} + + +export const Ticks: { + formatters: { + /** + * Formatter for value labels + * @param value the value to display + * @return {string|string[]} the label to display + */ + values(value: unknown): string | string[]; + /** + * Formatter for numeric ticks + * @param tickValue the value to be formatted + * @param index the position of the tickValue parameter in the ticks array + * @param ticks the list of ticks being converted + * @return string representation of the tickValue parameter + */ + numeric(tickValue: number, index: number, ticks: { value: number }[]): string; + /** + * Formatter for logarithmic ticks + * @param tickValue the value to be formatted + * @param index the position of the tickValue parameter in the ticks array + * @param ticks the list of ticks being converted + * @return string representation of the tickValue parameter + */ + logarithmic(tickValue: number, index: number, ticks: { value: number }[]): string; + }; +}; + +export interface TypedRegistry { + /** + * @param {ChartComponent} item + * @returns {string} The scope where items defaults were registered to. + */ + register(item: ChartComponent): string; + get(id: string): T | undefined; + unregister(item: ChartComponent): void; +} + +export interface ChartEvent { + type: + | 'contextmenu' + | 'mouseenter' + | 'mousedown' + | 'mousemove' + | 'mouseup' + | 'mouseout' + | 'click' + | 'dblclick' + | 'keydown' + | 'keypress' + | 'keyup' + | 'resize'; + native: Event | null; + x: number | null; + y: number | null; +} +export interface ChartComponent { + id: string; + defaults?: AnyObject; + defaultRoutes?: { [property: string]: string }; + + beforeRegister?(): void; + afterRegister?(): void; + beforeUnregister?(): void; + afterUnregister?(): void; +} + +export interface CoreInteractionOptions { + /** + * Sets which elements appear in the tooltip. See Interaction Modes for details. + * @default 'nearest' + */ + mode: InteractionMode; + /** + * if true, the hover mode only applies when the mouse position intersects an item on the chart. + * @default true + */ + intersect: boolean; + + /** + * Can be set to 'x', 'y', 'xy' or 'r' to define which directions are used in calculating distances. Defaults to 'x' for 'index' mode and 'xy' in dataset and 'nearest' modes. + */ + axis: 'x' | 'y' | 'xy' | 'r'; +} + +export interface CoreChartOptions extends ParsingOptions, AnimationOptions { + + datasets: { + [key in ChartType]: ChartTypeRegistry[key]['datasetOptions'] + } + + /** + * The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts. + * @default 'x' + */ + indexAxis: 'x' | 'y'; + + /** + * base color + * @see Defaults.color + */ + color: Scriptable>; + /** + * base background color + * @see Defaults.backgroundColor + */ + backgroundColor: Scriptable>; + /** + * base border color + * @see Defaults.borderColor + */ + borderColor: Scriptable>; + /** + * base font + * @see Defaults.font + */ + font: Partial; + /** + * Resizes the chart canvas when its container does (important note...). + * @default true + */ + responsive: boolean; + /** + * Maintain the original canvas aspect ratio (width / height) when resizing. + * @default true + */ + maintainAspectRatio: boolean; + /** + * Delay the resize update by give amount of milliseconds. This can ease the resize process by debouncing update of the elements. + * @default 0 + */ + resizeDelay: number; + + /** + * Canvas aspect ratio (i.e. width / height, a value of 1 representing a square canvas). Note that this option is ignored if the height is explicitly defined either as attribute or via the style. + * @default 2 + */ + aspectRatio: number; + + /** + * Locale used for number formatting (using `Intl.NumberFormat`). + * @default user's browser setting + */ + locale: string; + + /** + * Called when a resize occurs. Gets passed two arguments: the chart instance and the new size. + */ + onResize(chart: Chart, size: { width: number; height: number }): void; + + /** + * Override the window's default devicePixelRatio. + * @default window.devicePixelRatio + */ + devicePixelRatio: number; + + interaction: CoreInteractionOptions; + + hover: CoreInteractionOptions; + + /** + * The events option defines the browser events that the chart should listen to for tooltips and hovering. + * @default ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'] + */ + events: (keyof HTMLElementEventMap)[] + + /** + * Called when any of the events fire. Passed the event, an array of active elements (bars, points, etc), and the chart. + */ + onHover(event: ChartEvent, elements: ActiveElement[], chart: Chart): void; + + /** + * Called if the event is of type 'mouseup' or 'click'. Passed the event, an array of active elements, and the chart. + */ + onClick(event: ChartEvent, elements: ActiveElement[], chart: Chart): void; + + layout: Partial<{ + autoPadding: boolean; + padding: Scriptable, ScriptableContext>; + }>; +} + +export type EasingFunction = + | 'linear' + | 'easeInQuad' + | 'easeOutQuad' + | 'easeInOutQuad' + | 'easeInCubic' + | 'easeOutCubic' + | 'easeInOutCubic' + | 'easeInQuart' + | 'easeOutQuart' + | 'easeInOutQuart' + | 'easeInQuint' + | 'easeOutQuint' + | 'easeInOutQuint' + | 'easeInSine' + | 'easeOutSine' + | 'easeInOutSine' + | 'easeInExpo' + | 'easeOutExpo' + | 'easeInOutExpo' + | 'easeInCirc' + | 'easeOutCirc' + | 'easeInOutCirc' + | 'easeInElastic' + | 'easeOutElastic' + | 'easeInOutElastic' + | 'easeInBack' + | 'easeOutBack' + | 'easeInOutBack' + | 'easeInBounce' + | 'easeOutBounce' + | 'easeInOutBounce'; + +export type AnimationSpec = { + /** + * The number of milliseconds an animation takes. + * @default 1000 + */ + duration?: Scriptable>; + /** + * Easing function to use + * @default 'easeOutQuart' + */ + easing?: Scriptable>; + + /** + * Delay before starting the animations. + * @default 0 + */ + delay?: Scriptable>; + + /** + * If set to true, the animations loop endlessly. + * @default false + */ + loop?: Scriptable>; +} + +export type AnimationsSpec = { + [name: string]: false | AnimationSpec & { + properties: string[]; + + /** + * Type of property, determines the interpolator used. Possible values: 'number', 'color' and 'boolean'. Only really needed for 'color', because typeof does not get that right. + */ + type: 'color' | 'number' | 'boolean'; + + fn: (from: T, to: T, factor: number) => T; + + /** + * Start value for the animation. Current value is used when undefined + */ + from: Scriptable>; + /** + * + */ + to: Scriptable>; + } +} + +export type TransitionSpec = { + animation: AnimationSpec; + animations: AnimationsSpec; +} + +export type TransitionsSpec = { + [mode: string]: TransitionSpec +} + +export type AnimationOptions = { + animation: false | AnimationSpec & { + /** + * Callback called on each step of an animation. + */ + onProgress?: (this: Chart, event: AnimationEvent) => void; + /** + * Callback called when all animations are completed. + */ + onComplete?: (this: Chart, event: AnimationEvent) => void; + }; + animations: AnimationsSpec; + transitions: TransitionsSpec; +}; + +export interface FontSpec { + /** + * Default font family for all text, follows CSS font-family options. + * @default "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" + */ + family: string; + /** + * Default font size (in px) for text. Does not apply to radialLinear scale point labels. + * @default 12 + */ + size: number; + /** + * Default font style. Does not apply to tooltip title or footer. Does not apply to chart title. Follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit) + * @default 'normal' + */ + style: 'normal' | 'italic' | 'oblique' | 'initial' | 'inherit'; + /** + * Default font weight (boldness). (see MDN). + */ + weight: string | null; + /** + * Height of an individual line of text (see MDN). + * @default 1.2 + */ + lineHeight: number | string; +} + +export type TextAlign = 'left' | 'center' | 'right'; +export type Align = 'start' | 'center' | 'end'; + +export interface VisualElement { + draw(ctx: CanvasRenderingContext2D, area?: ChartArea): void; + inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean; + inXRange(mouseX: number, useFinalPosition?: boolean): boolean; + inYRange(mouseY: number, useFinalPosition?: boolean): boolean; + getCenterPoint(useFinalPosition?: boolean): { x: number; y: number }; + getRange?(axis: 'x' | 'y'): number; +} + +export interface CommonElementOptions { + borderWidth: number; + borderColor: Color; + backgroundColor: Color; +} + +export interface CommonHoverOptions { + hoverBorderWidth: number; + hoverBorderColor: Color; + hoverBackgroundColor: Color; +} + +export interface Segment { + start: number; + end: number; + loop: boolean; +} + +export interface ArcProps { + x: number; + y: number; + startAngle: number; + endAngle: number; + innerRadius: number; + outerRadius: number; + circumference: number; +} + +export interface ArcBorderRadius { + outerStart: number; + outerEnd: number; + innerStart: number; + innerEnd: number; +} + +export interface ArcOptions extends CommonElementOptions { + /** + * Arc stroke alignment. + */ + borderAlign: 'center' | 'inner'; + + /** + * Line join style. See MDN. Default is 'round' when `borderAlign` is 'inner', else 'bevel'. + */ + borderJoinStyle: CanvasLineJoin; + + /** + * Sets the border radius for arcs + * @default 0 + */ + borderRadius: number | ArcBorderRadius; + + /** + * Arc offset (in pixels). + */ + offset: number; +} + +export interface ArcHoverOptions extends CommonHoverOptions { + hoverOffset: number; +} + +export interface ArcElement + extends Element, + VisualElement {} + +export const ArcElement: ChartComponent & { + prototype: ArcElement; + new (cfg: AnyObject): ArcElement; +}; + +export interface LineProps { + points: Point[] +} + +export interface LineOptions extends CommonElementOptions { + /** + * Line cap style. See MDN. + * @default 'butt' + */ + borderCapStyle: CanvasLineCap; + /** + * Line dash. See MDN. + * @default [] + */ + borderDash: number[]; + /** + * Line dash offset. See MDN. + * @default 0.0 + */ + borderDashOffset: number; + /** + * Line join style. See MDN. + * @default 'miter' + */ + borderJoinStyle: CanvasLineJoin; + /** + * true to keep Bézier control inside the chart, false for no restriction. + * @default true + */ + capBezierPoints: boolean; + /** + * Interpolation mode to apply. + * @default 'default' + */ + cubicInterpolationMode: 'default' | 'monotone'; + /** + * Bézier curve tension (0 for no Bézier curves). + * @default 0 + */ + tension: number; + /** + * true to show the line as a stepped line (tension will be ignored). + * @default false + */ + stepped: 'before' | 'after' | 'middle' | boolean; + /** + * Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end + */ + fill: FillTarget | ComplexFillTarget; + /** + * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. + */ + spanGaps: boolean | number; + + segment: { + backgroundColor: Scriptable, + borderColor: Scriptable, + borderCapStyle: Scriptable; + borderDash: Scriptable; + borderDashOffset: Scriptable; + borderJoinStyle: Scriptable; + borderWidth: Scriptable; + }; +} + +export interface LineHoverOptions extends CommonHoverOptions { + hoverBorderCapStyle: CanvasLineCap; + hoverBorderDash: number[]; + hoverBorderDashOffset: number; + hoverBorderJoinStyle: CanvasLineJoin; +} + +export interface LineElement + extends Element, + VisualElement { + updateControlPoints(chartArea: ChartArea, indexAxis?: 'x' | 'y'): void; + points: Point[]; + readonly segments: Segment[]; + first(): Point | false; + last(): Point | false; + interpolate(point: Point, property: 'x' | 'y'): undefined | Point | Point[]; + pathSegment(ctx: CanvasRenderingContext2D, segment: Segment, params: AnyObject): undefined | boolean; + path(ctx: CanvasRenderingContext2D): boolean; +} + +export const LineElement: ChartComponent & { + prototype: LineElement; + new (cfg: AnyObject): LineElement; +}; + +export interface PointProps { + x: number; + y: number; +} + +export type PointStyle = + | 'circle' + | 'cross' + | 'crossRot' + | 'dash' + | 'line' + | 'rect' + | 'rectRounded' + | 'rectRot' + | 'star' + | 'triangle' + | HTMLImageElement + | HTMLCanvasElement; + +export interface PointOptions extends CommonElementOptions { + /** + * Point radius + * @default 3 + */ + radius: number; + /** + * Extra radius added to point radius for hit detection. + * @default 1 + */ + hitRadius: number; + /** + * Point style + * @default 'circle; + */ + pointStyle: PointStyle; + /** + * Point rotation (in degrees). + * @default 0 + */ + rotation: number; + /** + * Draw the active elements over the other elements of the dataset, + * @default true + */ + drawActiveElementsOnTop: boolean; +} + +export interface PointHoverOptions extends CommonHoverOptions { + /** + * Point radius when hovered. + * @default 4 + */ + hoverRadius: number; +} + +export interface PointPrefixedOptions { + /** + * The fill color for points. + */ + pointBackgroundColor: Color; + /** + * The border color for points. + */ + pointBorderColor: Color; + /** + * The width of the point border in pixels. + */ + pointBorderWidth: number; + /** + * The pixel size of the non-displayed point that reacts to mouse events. + */ + pointHitRadius: number; + /** + * The radius of the point shape. If set to 0, the point is not rendered. + */ + pointRadius: number; + /** + * The rotation of the point in degrees. + */ + pointRotation: number; + /** + * Style of the point. + */ + pointStyle: PointStyle; +} + +export interface PointPrefixedHoverOptions { + /** + * Point background color when hovered. + */ + pointHoverBackgroundColor: Color; + /** + * Point border color when hovered. + */ + pointHoverBorderColor: Color; + /** + * Border width of point when hovered. + */ + pointHoverBorderWidth: number; + /** + * The radius of the point when hovered. + */ + pointHoverRadius: number; +} + +export interface PointElement + extends Element, + VisualElement { + readonly skip: boolean; + readonly parsed: CartesianParsedData; +} + +export const PointElement: ChartComponent & { + prototype: PointElement; + new (cfg: AnyObject): PointElement; +}; + +export interface BarProps { + x: number; + y: number; + base: number; + horizontal: boolean; + width: number; + height: number; +} + +export interface BarOptions extends Omit { + /** + * The base value for the bar in data units along the value axis. + */ + base: number; + + /** + * Skipped (excluded) border: 'start', 'end', 'left', 'right', 'bottom', 'top' or false (none). + * @default 'start' + */ + borderSkipped: 'start' | 'end' | 'left' | 'right' | 'bottom' | 'top' | false; + + /** + * Border radius + * @default 0 + */ + borderRadius: number | BorderRadius; + + /** + * Amount to inflate the rectangle(s). This can be used to hide artifacts between bars. + * Unit is pixels. 'auto' translates to 0.33 pixels when barPercentage * categoryPercentage is 1, else 0. + * @default 'auto' + */ + inflateAmount: number | 'auto'; + + /** + * Width of the border, number for all sides, object to specify width for each side specifically + * @default 0 + */ + borderWidth: number | { top?: number, right?: number, bottom?: number, left?: number }; +} + +export interface BorderRadius { + topLeft: number; + topRight: number; + bottomLeft: number; + bottomRight: number; +} + +export interface BarHoverOptions extends CommonHoverOptions { + hoverBorderRadius: number | BorderRadius; +} + +export interface BarElement< + T extends BarProps = BarProps, + O extends BarOptions = BarOptions +> extends Element, VisualElement {} + +export const BarElement: ChartComponent & { + prototype: BarElement; + new (cfg: AnyObject): BarElement; +}; + +export interface ElementOptionsByType { + arc: ScriptableAndArrayOptions>; + bar: ScriptableAndArrayOptions>; + line: ScriptableAndArrayOptions>; + point: ScriptableAndArrayOptions>; +} + +export type ElementChartOptions = { + elements: ElementOptionsByType +}; + +export class BasePlatform { + /** + * Called at chart construction time, returns a context2d instance implementing + * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. + * @param {HTMLCanvasElement} canvas - The canvas from which to acquire context (platform specific) + * @param options - The chart options + */ + acquireContext( + canvas: HTMLCanvasElement, + options?: CanvasRenderingContext2DSettings + ): CanvasRenderingContext2D | null; + /** + * Called at chart destruction time, releases any resources associated to the context + * previously returned by the acquireContext() method. + * @param {CanvasRenderingContext2D} context - The context2d instance + * @returns {boolean} true if the method succeeded, else false + */ + releaseContext(context: CanvasRenderingContext2D): boolean; + /** + * Registers the specified listener on the given chart. + * @param {Chart} chart - Chart from which to listen for event + * @param {string} type - The ({@link ChartEvent}) type to listen for + * @param listener - Receives a notification (an object that implements + * the {@link ChartEvent} interface) when an event of the specified type occurs. + */ + addEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void; + /** + * Removes the specified listener previously registered with addEventListener. + * @param {Chart} chart - Chart from which to remove the listener + * @param {string} type - The ({@link ChartEvent}) type to remove + * @param listener - The listener function to remove from the event target. + */ + removeEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void; + /** + * @returns {number} the current devicePixelRatio of the device this platform is connected to. + */ + getDevicePixelRatio(): number; + /** + * @param {HTMLCanvasElement} canvas - The canvas for which to calculate the maximum size + * @param {number} [width] - Parent element's content width + * @param {number} [height] - Parent element's content height + * @param {number} [aspectRatio] - The aspect ratio to maintain + * @returns { width: number, height: number } the maximum size available. + */ + getMaximumSize(canvas: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number }; + /** + * @param {HTMLCanvasElement} canvas + * @returns {boolean} true if the canvas is attached to the platform, false if not. + */ + isAttached(canvas: HTMLCanvasElement): boolean; + /** + * Updates config with platform specific requirements + * @param {ChartConfiguration} config + */ + updateConfig(config: ChartConfiguration): void; +} + +export class BasicPlatform extends BasePlatform {} +export class DomPlatform extends BasePlatform {} + +export const Decimation: Plugin; + +export const enum DecimationAlgorithm { + lttb = 'lttb', + minmax = 'min-max', +} +interface BaseDecimationOptions { + enabled: boolean; + threshold?: number; +} + +interface LttbDecimationOptions extends BaseDecimationOptions { + algorithm: DecimationAlgorithm.lttb | 'lttb'; + samples?: number; +} + +interface MinMaxDecimationOptions extends BaseDecimationOptions { + algorithm: DecimationAlgorithm.minmax | 'min-max'; +} + +export type DecimationOptions = LttbDecimationOptions | MinMaxDecimationOptions; + +export const Filler: Plugin; +export interface FillerOptions { + drawTime: 'beforeDatasetDraw' | 'beforeDatasetsDraw'; + propagate: boolean; +} + +export type FillTarget = number | string | { value: number } | 'start' | 'end' | 'origin' | 'stack' | 'shape' | boolean; + +export interface ComplexFillTarget { + /** + * The accepted values are the same as the filling mode values, so you may use absolute and relative dataset indexes and/or boundaries. + */ + target: FillTarget; + /** + * If no color is set, the default color will be the background color of the chart. + */ + above: Color; + /** + * Same as the above. + */ + below: Color; +} + +export interface FillerControllerDatasetOptions { + /** + * Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end + */ + fill: FillTarget | ComplexFillTarget; +} + +export const Legend: Plugin; + +export interface LegendItem { + /** + * Label that will be displayed + */ + text: string; + + /** + * Border radius of the legend box + * @since 3.1.0 + */ + borderRadius?: number | BorderRadius; + + /** + * Index of the associated dataset + */ + datasetIndex: number; + + /** + * Fill style of the legend box + */ + fillStyle?: Color; + + /** + * Font color for the text + * Defaults to LegendOptions.labels.color + */ + fontColor?: Color; + + /** + * If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect + */ + hidden?: boolean; + + /** + * For box border. + * @see https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap + */ + lineCap?: CanvasLineCap; + + /** + * For box border. + * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash + */ + lineDash?: number[]; + + /** + * For box border. + * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset + */ + lineDashOffset?: number; + + /** + * For box border. + * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin + */ + lineJoin?: CanvasLineJoin; + + /** + * Width of box border + */ + lineWidth?: number; + + /** + * Stroke style of the legend box + */ + strokeStyle?: Color; + + /** + * Point style of the legend box (only used if usePointStyle is true) + */ + pointStyle?: PointStyle; + + /** + * Rotation of the point in degrees (only used if usePointStyle is true) + */ + rotation?: number; + + /** + * Text alignment + */ + textAlign?: TextAlign; +} + +export interface LegendElement extends Element>, LayoutItem { + chart: Chart; + ctx: CanvasRenderingContext2D; + legendItems?: LegendItem[]; + options: LegendOptions; +} + +export interface LegendOptions { + /** + * Is the legend shown? + * @default true + */ + display: boolean; + /** + * Position of the legend. + * @default 'top' + */ + position: LayoutPosition; + /** + * Alignment of the legend. + * @default 'center' + */ + align: Align; + /** + * Maximum height of the legend, in pixels + */ + maxHeight: number; + /** + * Maximum width of the legend, in pixels + */ + maxWidth: number; + /** + * Marks that this box should take the full width/height of the canvas (moving other boxes). This is unlikely to need to be changed in day-to-day use. + * @default true + */ + fullSize: boolean; + /** + * Legend will show datasets in reverse order. + * @default false + */ + reverse: boolean; + /** + * A callback that is called when a click event is registered on a label item. + */ + onClick(this: LegendElement, e: ChartEvent, legendItem: LegendItem, legend: LegendElement): void; + /** + * A callback that is called when a 'mousemove' event is registered on top of a label item + */ + onHover(this: LegendElement, e: ChartEvent, legendItem: LegendItem, legend: LegendElement): void; + /** + * A callback that is called when a 'mousemove' event is registered outside of a previously hovered label item. + */ + onLeave(this: LegendElement, e: ChartEvent, legendItem: LegendItem, legend: LegendElement): void; + + labels: { + /** + * Width of colored box. + * @default 40 + */ + boxWidth: number; + /** + * Height of the coloured box. + * @default fontSize + */ + boxHeight: number; + /** + * Padding between the color box and the text + * @default 1 + */ + boxPadding: number; + /** + * Color of label + * @see Defaults.color + */ + color: Color; + /** + * Font of label + * @see Defaults.font + */ + font: FontSpec; + /** + * Padding between rows of colored boxes. + * @default 10 + */ + padding: number; + /** + * Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See Legend Item for details. + */ + generateLabels(chart: Chart): LegendItem[]; + + /** + * Filters legend items out of the legend. Receives 2 parameters, a Legend Item and the chart data + */ + filter(item: LegendItem, data: ChartData): boolean; + + /** + * Sorts the legend items + */ + sort(a: LegendItem, b: LegendItem, data: ChartData): number; + + /** + * Override point style for the legend. Only applies if usePointStyle is true + */ + pointStyle: PointStyle; + + /** + * Text alignment + */ + textAlign?: TextAlign; + + /** + * Label style will match corresponding point style (size is based on the minimum value between boxWidth and font.size). + * @default false + */ + usePointStyle: boolean; + }; + /** + * true for rendering the legends from right to left. + */ + rtl: boolean; + /** + * This will force the text direction 'rtl' or 'ltr' on the canvas for rendering the legend, regardless of the css specified on the canvas + * @default canvas' default + */ + textDirection: string; + + title: { + /** + * Is the legend title displayed. + * @default false + */ + display: boolean; + /** + * Color of title + * @see Defaults.color + */ + color: Color; + /** + * see Fonts + */ + font: FontSpec; + position: 'center' | 'start' | 'end'; + padding?: number | ChartArea; + /** + * The string title. + */ + text: string; + }; +} + +export const SubTitle: Plugin; +export const Title: Plugin; + +export interface TitleOptions { + /** + * Alignment of the title. + * @default 'center' + */ + align: Align; + /** + * Is the title shown? + * @default false + */ + display: boolean; + /** + * Position of title + * @default 'top' + */ + position: 'top' | 'left' | 'bottom' | 'right'; + /** + * Color of text + * @see Defaults.color + */ + color: Color; + font: FontSpec; + + /** + * Marks that this box should take the full width/height of the canvas (moving other boxes). If set to `false`, places the box above/beside the + * chart area + * @default true + */ + fullSize: boolean; + /** + * Adds padding above and below the title text if a single number is specified. It is also possible to change top and bottom padding separately. + */ + padding: number | { top: number; bottom: number }; + /** + * Title text to display. If specified as an array, text is rendered on multiple lines. + */ + text: string | string[]; +} + +export type TooltipXAlignment = 'left' | 'center' | 'right'; +export type TooltipYAlignment = 'top' | 'center' | 'bottom'; +export interface TooltipLabelStyle { + borderColor: Color; + backgroundColor: Color; + + /** + * Width of border line + * @since 3.1.0 + */ + borderWidth?: number; + + /** + * Border dash + * @since 3.1.0 + */ + borderDash?: [number, number]; + + /** + * Border dash offset + * @since 3.1.0 + */ + borderDashOffset?: number; + + /** + * borderRadius + * @since 3.1.0 + */ + borderRadius?: number | BorderRadius; +} +export interface TooltipModel extends Element> { + readonly chart: Chart; + + // The items that we are rendering in the tooltip. See Tooltip Item Interface section + dataPoints: TooltipItem[]; + + // Positioning + xAlign: TooltipXAlignment; + yAlign: TooltipYAlignment; + + // X and Y properties are the top left of the tooltip + x: number; + y: number; + width: number; + height: number; + // Where the tooltip points to + caretX: number; + caretY: number; + + // Body + // The body lines that need to be rendered + // Each object contains 3 parameters + // before: string[] // lines of text before the line with the color square + // lines: string[]; // lines of text to render as the main item with color square + // after: string[]; // lines of text to render after the main lines + body: { before: string[]; lines: string[]; after: string[] }[]; + // lines of text that appear after the title but before the body + beforeBody: string[]; + // line of text that appear after the body and before the footer + afterBody: string[]; + + // Title + // lines of text that form the title + title: string[]; + + // Footer + // lines of text that form the footer + footer: string[]; + + // Styles to render for each item in body[]. This is the styling of the squares in the tooltip + labelColors: TooltipLabelStyle[]; + labelTextColors: Color[]; + labelPointStyles: { pointStyle: PointStyle; rotation: number }[]; + + // 0 opacity is a hidden tooltip + opacity: number; + + // tooltip options + options: TooltipOptions; + + getActiveElements(): ActiveElement[]; + setActiveElements(active: ActiveDataPoint[], eventPosition: Point): void; +} + +export interface TooltipPosition { + x: number; + y: number; + xAlign?: TooltipXAlignment; + yAlign?: TooltipYAlignment; +} + +export type TooltipPositionerFunction = ( + this: TooltipModel, + items: readonly ActiveElement[], + eventPosition: Point +) => TooltipPosition | false; + +export interface TooltipPositionerMap { + average: TooltipPositionerFunction; + nearest: TooltipPositionerFunction; +} + +export type TooltipPositioner = keyof TooltipPositionerMap; + +export interface Tooltip extends Plugin { + readonly positioners: TooltipPositionerMap; +} + +export const Tooltip: Tooltip; + +export interface TooltipCallbacks< + TType extends ChartType, + Model = TooltipModel, + Item = TooltipItem> { + + beforeTitle(this: Model, tooltipItems: Item[]): string | string[]; + title(this: Model, tooltipItems: Item[]): string | string[]; + afterTitle(this: Model, tooltipItems: Item[]): string | string[]; + + beforeBody(this: Model, tooltipItems: Item[]): string | string[]; + afterBody(this: Model, tooltipItems: Item[]): string | string[]; + + beforeLabel(this: Model, tooltipItem: Item): string | string[]; + label(this: Model, tooltipItem: Item): string | string[]; + afterLabel(this: Model, tooltipItem: Item): string | string[]; + + labelColor(this: Model, tooltipItem: Item): TooltipLabelStyle; + labelTextColor(this: Model, tooltipItem: Item): Color; + labelPointStyle(this: Model, tooltipItem: Item): { pointStyle: PointStyle; rotation: number }; + + beforeFooter(this: Model, tooltipItems: Item[]): string | string[]; + footer(this: Model, tooltipItems: Item[]): string | string[]; + afterFooter(this: Model, tooltipItems: Item[]): string | string[]; +} + +export interface ExtendedPlugin< + TType extends ChartType, + O = AnyObject, + Model = TooltipModel> { + /** + * @desc Called before drawing the `tooltip`. If any plugin returns `false`, + * the tooltip drawing is cancelled until another `render` is triggered. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {Tooltip} args.tooltip - The tooltip. + * @param {object} options - The plugin options. + * @returns {boolean} `false` to cancel the chart tooltip drawing. + */ + beforeTooltipDraw?(chart: Chart, args: { tooltip: Model }, options: O): boolean | void; + /** + * @desc Called after drawing the `tooltip`. Note that this hook will not + * be called if the tooltip drawing has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {object} args - The call arguments. + * @param {Tooltip} args.tooltip - The tooltip. + * @param {object} options - The plugin options. + */ + afterTooltipDraw?(chart: Chart, args: { tooltip: Model }, options: O): void; +} + +export interface ScriptableTooltipContext { + chart: UnionToIntersection>; + tooltip: UnionToIntersection>; + tooltipItems: TooltipItem[]; +} + +export interface TooltipOptions extends CoreInteractionOptions { + /** + * Are on-canvas tooltips enabled? + * @default true + */ + enabled: Scriptable>; + /** + * See external tooltip section. + */ + external(this: TooltipModel, args: { chart: Chart; tooltip: TooltipModel }): void; + /** + * The mode for positioning the tooltip + */ + position: Scriptable> + + /** + * Override the tooltip alignment calculations + */ + xAlign: Scriptable>; + yAlign: Scriptable>; + + /** + * Sort tooltip items. + */ + itemSort: (a: TooltipItem, b: TooltipItem, data: ChartData) => number; + + filter: (e: TooltipItem, index: number, array: TooltipItem[], data: ChartData) => boolean; + + /** + * Background color of the tooltip. + * @default 'rgba(0, 0, 0, 0.8)' + */ + backgroundColor: Scriptable>; + /** + * Padding between the color box and the text. + * @default 1 + */ + boxPadding: number; + /** + * Color of title + * @default '#fff' + */ + titleColor: Scriptable>; + /** + * See Fonts + * @default {weight: 'bold'} + */ + titleFont: Scriptable>; + /** + * Spacing to add to top and bottom of each title line. + * @default 2 + */ + titleSpacing: Scriptable>; + /** + * Margin to add on bottom of title section. + * @default 6 + */ + titleMarginBottom: Scriptable>; + /** + * Horizontal alignment of the title text lines. + * @default 'left' + */ + titleAlign: Scriptable>; + /** + * Spacing to add to top and bottom of each tooltip item. + * @default 2 + */ + bodySpacing: Scriptable>; + /** + * Color of body + * @default '#fff' + */ + bodyColor: Scriptable>; + /** + * See Fonts. + * @default {} + */ + bodyFont: Scriptable>; + /** + * Horizontal alignment of the body text lines. + * @default 'left' + */ + bodyAlign: Scriptable>; + /** + * Spacing to add to top and bottom of each footer line. + * @default 2 + */ + footerSpacing: Scriptable>; + /** + * Margin to add before drawing the footer. + * @default 6 + */ + footerMarginTop: Scriptable>; + /** + * Color of footer + * @default '#fff' + */ + footerColor: Scriptable>; + /** + * See Fonts + * @default {weight: 'bold'} + */ + footerFont: Scriptable>; + /** + * Horizontal alignment of the footer text lines. + * @default 'left' + */ + footerAlign: Scriptable>; + /** + * Padding to add to the tooltip + * @default 6 + */ + padding: Scriptable>; + /** + * Extra distance to move the end of the tooltip arrow away from the tooltip point. + * @default 2 + */ + caretPadding: Scriptable>; + /** + * Size, in px, of the tooltip arrow. + * @default 5 + */ + caretSize: Scriptable>; + /** + * Radius of tooltip corner curves. + * @default 6 + */ + cornerRadius: Scriptable>; + /** + * Color to draw behind the colored boxes when multiple items are in the tooltip. + * @default '#fff' + */ + multiKeyBackground: Scriptable>; + /** + * If true, color boxes are shown in the tooltip. + * @default true + */ + displayColors: Scriptable>; + /** + * Width of the color box if displayColors is true. + * @default bodyFont.size + */ + boxWidth: Scriptable>; + /** + * Height of the color box if displayColors is true. + * @default bodyFont.size + */ + boxHeight: Scriptable>; + /** + * Use the corresponding point style (from dataset options) instead of color boxes, ex: star, triangle etc. (size is based on the minimum value between boxWidth and boxHeight) + * @default false + */ + usePointStyle: Scriptable>; + /** + * Color of the border. + * @default 'rgba(0, 0, 0, 0)' + */ + borderColor: Scriptable>; + /** + * Size of the border. + * @default 0 + */ + borderWidth: Scriptable>; + /** + * true for rendering the legends from right to left. + */ + rtl: Scriptable>; + + /** + * This will force the text direction 'rtl' or 'ltr on the canvas for rendering the tooltips, regardless of the css specified on the canvas + * @default canvas's default + */ + textDirection: Scriptable>; + + animation: AnimationSpec; + animations: AnimationsSpec; + callbacks: TooltipCallbacks; +} + +export interface TooltipItem { + /** + * The chart the tooltip is being shown on + */ + chart: Chart; + + /** + * Label for the tooltip + */ + label: string; + + /** + * Parsed data values for the given `dataIndex` and `datasetIndex` + */ + parsed: UnionToIntersection>; + + /** + * Raw data values for the given `dataIndex` and `datasetIndex` + */ + raw: unknown; + + /** + * Formatted value for the tooltip + */ + formattedValue: string; + + /** + * The dataset the item comes from + */ + dataset: UnionToIntersection>; + + /** + * Index of the dataset the item comes from + */ + datasetIndex: number; + + /** + * Index of this data item in the dataset + */ + dataIndex: number; + + /** + * The chart element (point, arc, bar, etc.) for this tooltip item + */ + element: Element; +} + +export interface PluginOptionsByType { + decimation: DecimationOptions; + filler: FillerOptions; + legend: LegendOptions; + subtitle: TitleOptions; + title: TitleOptions; + tooltip: TooltipOptions; +} +export interface PluginChartOptions { + plugins: PluginOptionsByType; +} + +export interface GridLineOptions { + /** + * @default true + */ + display: boolean; + borderColor: Color; + borderWidth: number; + /** + * @default false + */ + circular: boolean; + /** + * @default 'rgba(0, 0, 0, 0.1)' + */ + color: ScriptableAndArray; + /** + * @default [] + */ + borderDash: number[]; + /** + * @default 0 + */ + borderDashOffset: Scriptable; + /** + * @default 1 + */ + lineWidth: ScriptableAndArray; + + /** + * @default true + */ + drawBorder: boolean; + /** + * @default true + */ + drawOnChartArea: boolean; + /** + * @default true + */ + drawTicks: boolean; + /** + * @default [] + */ + tickBorderDash: number[]; + /** + * @default 0 + */ + tickBorderDashOffset: Scriptable; + /** + * @default 'rgba(0, 0, 0, 0.1)' + */ + tickColor: ScriptableAndArray; + /** + * @default 10 + */ + tickLength: number; + /** + * @default 1 + */ + tickWidth: number; + /** + * @default false + */ + offset: boolean; + /** + * @default 0 + */ + z: number; +} + +export interface TickOptions { + /** + * Color of label backdrops. + * @default 'rgba(255, 255, 255, 0.75)' + */ + backdropColor: Scriptable; + /** + * Padding of tick backdrop. + * @default 2 + */ + backdropPadding: number | ChartArea; + + /** + * Returns the string representation of the tick value as it should be displayed on the chart. See callback. + */ + callback: (this: Scale, tickValue: number | string, index: number, ticks: Tick[]) => string | string[] | number | number[] | null | undefined; + /** + * If true, show tick labels. + * @default true + */ + display: boolean; + /** + * Color of tick + * @see Defaults.color + */ + color: ScriptableAndArray; + /** + * see Fonts + */ + font: Scriptable; + /** + * Sets the offset of the tick labels from the axis + */ + padding: number; + /** + * If true, draw a background behind the tick labels. + * @default false + */ + showLabelBackdrop: Scriptable; + /** + * The color of the stroke around the text. + * @default undefined + */ + textStrokeColor: Scriptable; + /** + * Stroke width around the text. + * @default 0 + */ + textStrokeWidth: Scriptable; + /** + * z-index of tick layer. Useful when ticks are drawn on chart area. Values <= 0 are drawn under datasets, > 0 on top. + * @default 0 + */ + z: number; + + major: { + /** + * If true, major ticks are generated. A major tick will affect autoskipping and major will be defined on ticks in the scriptable options context. + * @default false + */ + enabled: boolean; + }; +} + +export interface CartesianScaleOptions extends CoreScaleOptions { + /** + * Scale boundary strategy (bypassed by min/max time options) + * - `data`: make sure data are fully visible, ticks outside are removed + * - `ticks`: make sure ticks are fully visible, data outside are truncated + * @since 2.7.0 + * @default 'ticks' + */ + bounds: 'ticks' | 'data'; + + /** + * Position of the axis. + */ + position: 'left' | 'top' | 'right' | 'bottom' | 'center' | { [scale: string]: number }; + + /** + * Stack group. Axes at the same `position` with same `stack` are stacked. + */ + stack?: string; + + /** + * Weight of the scale in stack group. Used to determine the amount of allocated space for the scale within the group. + * @default 1 + */ + stackWeight?: number; + + /** + * Which type of axis this is. Possible values are: 'x', 'y'. If not set, this is inferred from the first character of the ID which should be 'x' or 'y'. + */ + axis: 'x' | 'y'; + + /** + * User defined minimum value for the scale, overrides minimum value from data. + */ + min: number; + + /** + * User defined maximum value for the scale, overrides maximum value from data. + */ + max: number; + + /** + * If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true for a bar chart by default. + * @default false + */ + offset: boolean; + + grid: GridLineOptions; + + /** Options for the scale title. */ + title: { + /** If true, displays the axis title. */ + display: boolean; + /** Alignment of the axis title. */ + align: Align; + /** The text for the title, e.g. "# of People" or "Response Choices". */ + text: string | string[]; + /** Color of the axis label. */ + color: Color; + /** Information about the axis title font. */ + font: FontSpec; + /** Padding to apply around scale labels. */ + padding: number | { + /** Padding on the (relative) top side of this axis label. */ + top: number; + /** Padding on the (relative) bottom side of this axis label. */ + bottom: number; + /** This is a shorthand for defining top/bottom to the same values. */ + y: number; + }; + }; + + /** + * If true, data will be comprised between datasets of data + * @default false + */ + stacked?: boolean | 'single'; + + ticks: TickOptions & { + /** + * The number of ticks to examine when deciding how many labels will fit. Setting a smaller value will be faster, but may be less accurate when there is large variability in label length. + * @default ticks.length + */ + sampleSize: number; + /** + * The label alignment + * @default 'center' + */ + align: Align; + /** + * If true, automatically calculates how many labels can be shown and hides labels accordingly. Labels will be rotated up to maxRotation before skipping any. Turn autoSkip off to show all labels no matter what. + * @default true + */ + autoSkip: boolean; + /** + * Padding between the ticks on the horizontal axis when autoSkip is enabled. + * @default 0 + */ + autoSkipPadding: number; + + /** + * How is the label positioned perpendicular to the axis direction. + * This only applies when the rotation is 0 and the axis position is one of "top", "left", "right", or "bottom" + * @default 'near' + */ + crossAlign: 'near' | 'center' | 'far'; + + /** + * Should the defined `min` and `max` values be presented as ticks even if they are not "nice". + * @default: true + */ + includeBounds: boolean; + + /** + * Distance in pixels to offset the label from the centre point of the tick (in the x direction for the x axis, and the y direction for the y axis). Note: this can cause labels at the edges to be cropped by the edge of the canvas + * @default 0 + */ + labelOffset: number; + + /** + * Minimum rotation for tick labels. Note: Only applicable to horizontal scales. + * @default 0 + */ + minRotation: number; + /** + * Maximum rotation for tick labels when rotating to condense labels. Note: Rotation doesn't occur until necessary. Note: Only applicable to horizontal scales. + * @default 50 + */ + maxRotation: number; + /** + * Flips tick labels around axis, displaying the labels inside the chart instead of outside. Note: Only applicable to vertical scales. + * @default false + */ + mirror: boolean; + /** + * Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction. + * @default 0 + */ + padding: number; + /** + * Maximum number of ticks and gridlines to show. + * @default 11 + */ + maxTicksLimit: number; + }; +} + +export type CategoryScaleOptions = Omit & { + min: string | number; + max: string | number; + labels: string[] | string[][]; +}; + +export type CategoryScale = Scale +export const CategoryScale: ChartComponent & { + prototype: CategoryScale; + new (cfg: AnyObject): CategoryScale; +}; + +export type LinearScaleOptions = CartesianScaleOptions & { + + /** + * if true, scale will include 0 if it is not already included. + * @default true + */ + beginAtZero: boolean; + /** + * Adjustment used when calculating the maximum data value. + */ + suggestedMin?: number; + /** + * Adjustment used when calculating the minimum data value. + */ + suggestedMax?: number; + /** + * Percentage (string ending with %) or amount (number) for added room in the scale range above and below data. + */ + grace?: string | number; + + ticks: { + /** + * The Intl.NumberFormat options used by the default label formatter + */ + format: Intl.NumberFormatOptions; + + /** + * if defined and stepSize is not specified, the step size will be rounded to this many decimal places. + */ + precision: number; + + /** + * User defined fixed step size for the scale + */ + stepSize: number; + + /** + * User defined count of ticks + */ + count: number; + }; +}; + +export type LinearScale = Scale +export const LinearScale: ChartComponent & { + prototype: LinearScale; + new (cfg: AnyObject): LinearScale; +}; + +export type LogarithmicScaleOptions = CartesianScaleOptions & { + /** + * Adjustment used when calculating the maximum data value. + */ + suggestedMin?: number; + /** + * Adjustment used when calculating the minimum data value. + */ + suggestedMax?: number; + + ticks: { + /** + * The Intl.NumberFormat options used by the default label formatter + */ + format: Intl.NumberFormatOptions; + }; +}; + +export type LogarithmicScale = Scale +export const LogarithmicScale: ChartComponent & { + prototype: LogarithmicScale; + new (cfg: AnyObject): LogarithmicScale; +}; + +export type TimeScaleOptions = Omit & { + min: string | number; + max: string | number; + suggestedMin: string | number; + suggestedMax: string | number; + /** + * Scale boundary strategy (bypassed by min/max time options) + * - `data`: make sure data are fully visible, ticks outside are removed + * - `ticks`: make sure ticks are fully visible, data outside are truncated + * @since 2.7.0 + * @default 'data' + */ + bounds: 'ticks' | 'data'; + + /** + * options for creating a new adapter instance + */ + adapters: { + date: unknown; + }; + + time: { + /** + * Custom parser for dates. + */ + parser: string | ((v: unknown) => number); + /** + * If defined, dates will be rounded to the start of this unit. See Time Units below for the allowed units. + */ + round: false | TimeUnit; + /** + * If boolean and true and the unit is set to 'week', then the first day of the week will be Monday. Otherwise, it will be Sunday. + * If `number`, the index of the first day of the week (0 - Sunday, 6 - Saturday). + * @default false + */ + isoWeekday: boolean | number; + /** + * Sets how different time units are displayed. + */ + displayFormats: { + [key: string]: string; + }; + /** + * The format string to use for the tooltip. + */ + tooltipFormat: string; + /** + * If defined, will force the unit to be a certain type. See Time Units section below for details. + * @default false + */ + unit: false | TimeUnit; + + /** + * The number of units between grid lines. + * @default 1 + */ + stepSize: number; + /** + * The minimum display format to be used for a time unit. + * @default 'millisecond' + */ + minUnit: TimeUnit; + }; + + ticks: { + /** + * Ticks generation input values: + * - 'auto': generates "optimal" ticks based on scale size and time options. + * - 'data': generates ticks from data (including labels from data {t|x|y} objects). + * - 'labels': generates ticks from user given `data.labels` values ONLY. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + * @default 'auto' + */ + source: 'labels' | 'auto' | 'data'; + }; +}; + +export interface TimeScale extends Scale { + getDataTimestamps(): number[]; + getLabelTimestamps(): string[]; + normalize(values: number[]): number[]; +} + +export const TimeScale: ChartComponent & { + prototype: TimeScale; + new (cfg: AnyObject): TimeScale; +}; + +export type TimeSeriesScale = TimeScale +export const TimeSeriesScale: ChartComponent & { + prototype: TimeSeriesScale; + new (cfg: AnyObject): TimeSeriesScale; +}; + +export type RadialLinearScaleOptions = CoreScaleOptions & { + animate: boolean; + + angleLines: { + /** + * if true, angle lines are shown. + * @default true + */ + display: boolean; + /** + * Color of angled lines. + * @default 'rgba(0, 0, 0, 0.1)' + */ + color: Scriptable; + /** + * Width of angled lines. + * @default 1 + */ + lineWidth: Scriptable; + /** + * Length and spacing of dashes on angled lines. See MDN. + * @default [] + */ + borderDash: Scriptable; + /** + * Offset for line dashes. See MDN. + * @default 0 + */ + borderDashOffset: Scriptable; + }; + + /** + * if true, scale will include 0 if it is not already included. + * @default false + */ + beginAtZero: boolean; + + grid: GridLineOptions; + + /** + * User defined minimum number for the scale, overrides minimum value from data. + */ + min: number; + /** + * User defined maximum number for the scale, overrides maximum value from data. + */ + max: number; + + pointLabels: { + /** + * Background color of the point label. + * @default undefined + */ + backdropColor: Scriptable; + /** + * Padding of label backdrop. + * @default 2 + */ + backdropPadding: Scriptable; + + /** + * if true, point labels are shown. + * @default true + */ + display: boolean; + /** + * Color of label + * @see Defaults.color + */ + color: Scriptable; + /** + */ + font: Scriptable; + + /** + * Callback function to transform data labels to point labels. The default implementation simply returns the current string. + */ + callback: (label: string, index: number) => string | string[] | number | number[]; + + /** + * if true, point labels are centered. + * @default false + */ + centerPointLabels: boolean; + }; + + /** + * Adjustment used when calculating the maximum data value. + */ + suggestedMax: number; + /** + * Adjustment used when calculating the minimum data value. + */ + suggestedMin: number; + + ticks: TickOptions & { + /** + * The Intl.NumberFormat options used by the default label formatter + */ + format: Intl.NumberFormatOptions; + + /** + * Maximum number of ticks and gridlines to show. + * @default 11 + */ + maxTicksLimit: number; + + /** + * if defined and stepSize is not specified, the step size will be rounded to this many decimal places. + */ + precision: number; + + /** + * User defined fixed step size for the scale. + */ + stepSize: number; + + /** + * User defined number of ticks + */ + count: number; + }; +}; + +export interface RadialLinearScale extends Scale { + setCenterPoint(leftMovement: number, rightMovement: number, topMovement: number, bottomMovement: number): void; + getIndexAngle(index: number): number; + getDistanceFromCenterForValue(value: number): number; + getValueForDistanceFromCenter(distance: number): number; + getPointPosition(index: number, distanceFromCenter: number): { x: number; y: number; angle: number }; + getPointPositionForValue(index: number, value: number): { x: number; y: number; angle: number }; + getPointLabelPosition(index: number): ChartArea; + getBasePosition(index: number): { x: number; y: number; angle: number }; +} +export const RadialLinearScale: ChartComponent & { + prototype: RadialLinearScale; + new (cfg: AnyObject): RadialLinearScale; +}; + +export interface CartesianScaleTypeRegistry { + linear: { + options: LinearScaleOptions; + }; + logarithmic: { + options: LogarithmicScaleOptions; + }; + category: { + options: CategoryScaleOptions; + }; + time: { + options: TimeScaleOptions; + }; + timeseries: { + options: TimeScaleOptions; + }; +} + +export interface RadialScaleTypeRegistry { + radialLinear: { + options: RadialLinearScaleOptions; + }; +} + +export interface ScaleTypeRegistry extends CartesianScaleTypeRegistry, RadialScaleTypeRegistry { +} + +export type ScaleType = keyof ScaleTypeRegistry; + +interface CartesianParsedData { + x: number; + y: number; + + // Only specified when stacked bars are enabled + _stacks?: { + // Key is the stack ID which is generally the axis ID + [key: string]: { + // Inner key is the datasetIndex + [key: number]: number; + } + } +} + +interface BarParsedData extends CartesianParsedData { + // Only specified if floating bars are show + _custom?: { + barStart: number; + barEnd: number; + start: number; + end: number; + min: number; + max: number; + } +} + +interface BubbleParsedData extends CartesianParsedData { + // The bubble radius value + _custom: number; +} + +interface RadialParsedData { + r: number; +} + +export interface ChartTypeRegistry { + bar: { + chartOptions: BarControllerChartOptions; + datasetOptions: BarControllerDatasetOptions; + defaultDataPoint: number; + metaExtensions: {}; + parsedDataType: BarParsedData, + scales: keyof CartesianScaleTypeRegistry; + }; + line: { + chartOptions: LineControllerChartOptions; + datasetOptions: LineControllerDatasetOptions & FillerControllerDatasetOptions; + defaultDataPoint: ScatterDataPoint | number | null; + metaExtensions: {}; + parsedDataType: CartesianParsedData; + scales: keyof CartesianScaleTypeRegistry; + }; + scatter: { + chartOptions: ScatterControllerChartOptions; + datasetOptions: ScatterControllerDatasetOptions; + defaultDataPoint: ScatterDataPoint | number | null; + metaExtensions: {}; + parsedDataType: CartesianParsedData; + scales: keyof CartesianScaleTypeRegistry; + }; + bubble: { + chartOptions: unknown; + datasetOptions: BubbleControllerDatasetOptions; + defaultDataPoint: BubbleDataPoint; + metaExtensions: {}; + parsedDataType: BubbleParsedData; + scales: keyof CartesianScaleTypeRegistry; + }; + pie: { + chartOptions: PieControllerChartOptions; + datasetOptions: PieControllerDatasetOptions; + defaultDataPoint: PieDataPoint; + metaExtensions: PieMetaExtensions; + parsedDataType: number; + scales: keyof CartesianScaleTypeRegistry; + }; + doughnut: { + chartOptions: DoughnutControllerChartOptions; + datasetOptions: DoughnutControllerDatasetOptions; + defaultDataPoint: DoughnutDataPoint; + metaExtensions: DoughnutMetaExtensions; + parsedDataType: number; + scales: keyof CartesianScaleTypeRegistry; + }; + polarArea: { + chartOptions: PolarAreaControllerChartOptions; + datasetOptions: PolarAreaControllerDatasetOptions; + defaultDataPoint: number; + metaExtensions: {}; + parsedDataType: RadialParsedData; + scales: keyof RadialScaleTypeRegistry; + }; + radar: { + chartOptions: RadarControllerChartOptions; + datasetOptions: RadarControllerDatasetOptions & FillerControllerDatasetOptions; + defaultDataPoint: number | null; + metaExtensions: {}; + parsedDataType: RadialParsedData; + scales: keyof RadialScaleTypeRegistry; + }; +} + +export type ChartType = keyof ChartTypeRegistry; + +export type ScaleOptionsByType = + { [key in ScaleType]: { type: key } & ScaleTypeRegistry[key]['options'] }[TScale] +; + +// Convenience alias for creating and manipulating scale options in user code +export type ScaleOptions = DeepPartial>; + +export type DatasetChartOptions = { + [key in TType]: { + datasets: ChartTypeRegistry[key]['datasetOptions']; + }; +}; + +export type ScaleChartOptions = { + scales: { + [key: string]: ScaleOptionsByType; + }; +}; + +export type ChartOptions = DeepPartial< +CoreChartOptions & +ElementChartOptions & +PluginChartOptions & +DatasetChartOptions & +ScaleChartOptions & +ChartTypeRegistry[TType]['chartOptions'] +>; + +export type DefaultDataPoint = DistributiveArray; + +export type ParsedDataType = ChartTypeRegistry[TType]['parsedDataType']; + +export interface ChartDatasetProperties { + type?: TType; + data: TData; +} + +export type ChartDataset< + TType extends ChartType = ChartType, + TData = DefaultDataPoint +> = DeepPartial< +{ [key in ChartType]: { type: key } & ChartTypeRegistry[key]['datasetOptions'] }[TType] +> & ChartDatasetProperties; + +/** + * TData represents the data point type. If unspecified, a default is provided + * based on the chart type. + * TLabel represents the label type + */ +export interface ChartData< + TType extends ChartType = ChartType, + TData = DefaultDataPoint, + TLabel = unknown +> { + labels?: TLabel[]; + datasets: ChartDataset[]; +} + +export interface ChartConfiguration< + TType extends ChartType = ChartType, + TData = DefaultDataPoint, + TLabel = unknown +> { + type: TType; + data: ChartData; + options?: ChartOptions; + plugins?: Plugin[]; +} diff --git a/node_modules/chart.js/types/layout.d.ts b/node_modules/chart.js/types/layout.d.ts new file mode 100644 index 00000000..4c770711 --- /dev/null +++ b/node_modules/chart.js/types/layout.d.ts @@ -0,0 +1,65 @@ +import { ChartArea } from './geometric'; + +export type LayoutPosition = 'left' | 'top' | 'right' | 'bottom' | 'center' | 'chartArea' | {[scaleId: string]: number}; + +export interface LayoutItem { + /** + * The position of the item in the chart layout. Possible values are + */ + position: LayoutPosition; + /** + * The weight used to sort the item. Higher weights are further away from the chart area + */ + weight: number; + /** + * if true, and the item is horizontal, then push vertical boxes down + */ + fullSize: boolean; + /** + * Width of item. Must be valid after update() + */ + width: number; + /** + * Height of item. Must be valid after update() + */ + height: number; + /** + * Left edge of the item. Set by layout system and cannot be used in update + */ + left: number; + /** + * Top edge of the item. Set by layout system and cannot be used in update + */ + top: number; + /** + * Right edge of the item. Set by layout system and cannot be used in update + */ + right: number; + /** + * Bottom edge of the item. Set by layout system and cannot be used in update + */ + bottom: number; + + /** + * Called before the layout process starts + */ + beforeLayout?(): void; + /** + * Draws the element + */ + draw(chartArea: ChartArea): void; + /** + * Returns an object with padding on the edges + */ + getPadding?(): ChartArea; + /** + * returns true if the layout item is horizontal (ie. top or bottom) + */ + isHorizontal(): boolean; + /** + * Takes two parameters: width and height. + * @param width + * @param height + */ + update(width: number, height: number, margins?: ChartArea): void; +} diff --git a/node_modules/chart.js/types/utils.d.ts b/node_modules/chart.js/types/utils.d.ts new file mode 100644 index 00000000..8313d9fa --- /dev/null +++ b/node_modules/chart.js/types/utils.d.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +// DeepPartial implementation taken from the utility-types NPM package, which is +// Copyright (c) 2016 Piotr Witek (http://piotrwitek.github.io) +// and used under the terms of the MIT license +export type DeepPartial = T extends Function + ? T + : T extends Array + ? _DeepPartialArray + : T extends object + ? _DeepPartialObject + : T | undefined; + +type _DeepPartialArray = Array> +type _DeepPartialObject = { [P in keyof T]?: DeepPartial }; + +export type DistributiveArray = [T] extends [unknown] ? Array : never + +// https://stackoverflow.com/a/50375286 +export type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..8627f416 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "project-github-tracker", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "chart.js": "^3.7.1" + } + }, + "node_modules/chart.js": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", + "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" + } + }, + "dependencies": { + "chart.js": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", + "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..832728d9 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "chart.js": "^3.7.1" + } +} From 15c4c3547d77fea91a4e07c2728bb123db660c72 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:18:49 +0100 Subject: [PATCH 05/31] trying the token again --- code/chart.js | 34 +++++++++++++++++++++++++++++++++- code/index.html | 3 ++- code/package-lock.json | 2 +- code/script.js | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/code/chart.js b/code/chart.js index d29b65a9..38c39a63 100644 --- a/code/chart.js +++ b/code/chart.js @@ -1,4 +1,36 @@ +const { Chart } = require("chart.js"); + //DOM-selector for the canvas 👇 -const ctx = document.getElementById('myChart').getContext('2d') +const ctx = document.getElementById('myChart') //"Draw" the chart here 👇 + +const labels = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', +]; + +const data = { +labels: labels, +datasets: [{ + label: 'My First dataset', + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgb(255, 99, 132)', + data: [0, 10, 5, 2, 20, 30, 45], +}] +}; + + const config = { + type: 'line', + data: data, + options: {} +}; + +const myChart = new Chart( +document.getElementById('myChart'), +config +); \ No newline at end of file diff --git a/code/index.html b/code/index.html index f92ab85d..1598f310 100644 --- a/code/index.html +++ b/code/index.html @@ -12,6 +12,7 @@ +

@@ -27,8 +28,8 @@

Technigo Projects:

+ - \ No newline at end of file diff --git a/code/package-lock.json b/code/package-lock.json index 8627f416..0121b50f 100644 --- a/code/package-lock.json +++ b/code/package-lock.json @@ -1,5 +1,5 @@ { - "name": "project-github-tracker", + "name": "code", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/code/script.js b/code/script.js index f95992f1..2913d7ed 100644 --- a/code/script.js +++ b/code/script.js @@ -7,7 +7,7 @@ const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` // GITHUB Authentication - TOKEN REVOKED AFTER COMMIT? -const GITHUB_TOKEN = TOKEN || process.env.API_KEY; +const GITHUB_TOKEN = API_TOKEN || process.env.API_KEY; const options = { method: 'GET', headers: { From 00f4be8b0b190b6548e1ebc6115aa44ee9b23f0c Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:20:40 +0100 Subject: [PATCH 06/31] trying the token again --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index 2913d7ed..b2dc952f 100644 --- a/code/script.js +++ b/code/script.js @@ -11,7 +11,7 @@ const GITHUB_TOKEN = API_TOKEN || process.env.API_KEY; const options = { method: 'GET', headers: { - Authorization: `token ${GITHUB_TOKEN}` + Authorization: `token ${API_TOKEN}` } } From fa46e8da1da5a75d08405bab8b72102063bc5b0d Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:22:43 +0100 Subject: [PATCH 07/31] trying authentication again --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index b2dc952f..8e157db9 100644 --- a/code/script.js +++ b/code/script.js @@ -7,7 +7,7 @@ const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` // GITHUB Authentication - TOKEN REVOKED AFTER COMMIT? -const GITHUB_TOKEN = API_TOKEN || process.env.API_KEY; +const API_TOKEN = API_TOKEN || process.env.API_KEY; const options = { method: 'GET', headers: { From 56211444cf4d7384553aa99612696c0250308b01 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:24:23 +0100 Subject: [PATCH 08/31] trying authentication again again... --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index 8e157db9..17eb6426 100644 --- a/code/script.js +++ b/code/script.js @@ -7,7 +7,7 @@ const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` // GITHUB Authentication - TOKEN REVOKED AFTER COMMIT? -const API_TOKEN = API_TOKEN || process.env.API_KEY; +// const API_TOKEN = API_TOKEN || process.env.API_KEY; const options = { method: 'GET', headers: { From bff6b9a31b51d9fd6d5f040339bdc99eac28db38 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:25:06 +0100 Subject: [PATCH 09/31] think i finally figured out the damn authentication --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index 17eb6426..5d6f2994 100644 --- a/code/script.js +++ b/code/script.js @@ -11,7 +11,7 @@ const GITHUB_API = `https://api.github.com/users/${username}/repos` const options = { method: 'GET', headers: { - Authorization: `token ${API_TOKEN}` + Authorization: `token ${API_KEY}` } } From 7abbf601ed639e149e1ed586683542c4215b8b1d Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:28:07 +0100 Subject: [PATCH 10/31] trying once more --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index 5d6f2994..bbd57b93 100644 --- a/code/script.js +++ b/code/script.js @@ -7,7 +7,7 @@ const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` // GITHUB Authentication - TOKEN REVOKED AFTER COMMIT? -// const API_TOKEN = API_TOKEN || process.env.API_KEY; +const API_KEY = API_KEY || process.env.API_KEY; const options = { method: 'GET', headers: { From 3e261f79d709f6f35148436623e0d571fbe5d183 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:41:13 +0100 Subject: [PATCH 11/31] trying again --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index bbd57b93..464eef34 100644 --- a/code/script.js +++ b/code/script.js @@ -7,7 +7,7 @@ const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` // GITHUB Authentication - TOKEN REVOKED AFTER COMMIT? -const API_KEY = API_KEY || process.env.API_KEY; +// const API_KEY = API_KEY || process.env.API_KEY; const options = { method: 'GET', headers: { From 823c29d58cf252a5d379ae92fdc1a1a4819e6049 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:45:21 +0100 Subject: [PATCH 12/31] delete files?? --- node_modules/.package-lock.json | 12 - node_modules/chart.js/LICENSE.md | 9 - node_modules/chart.js/README.md | 38 - node_modules/chart.js/auto/auto.esm.d.ts | 4 - node_modules/chart.js/auto/auto.esm.js | 5 - node_modules/chart.js/auto/auto.js | 1 - node_modules/chart.js/auto/package.json | 8 - node_modules/chart.js/dist/chart.esm.js | 10627 ------------- node_modules/chart.js/dist/chart.js | 13269 ---------------- node_modules/chart.js/dist/chart.min.js | 13 - .../chart.js/dist/chunks/helpers.segment.js | 2503 --- node_modules/chart.js/dist/helpers.esm.js | 7 - .../chart.js/helpers/helpers.esm.d.ts | 1 - node_modules/chart.js/helpers/helpers.esm.js | 1 - node_modules/chart.js/helpers/helpers.js | 1 - node_modules/chart.js/helpers/package.json | 8 - node_modules/chart.js/package.json | 111 - node_modules/chart.js/types/adapters.d.ts | 63 - node_modules/chart.js/types/animation.d.ts | 33 - node_modules/chart.js/types/basic.d.ts | 3 - node_modules/chart.js/types/color.d.ts | 1 - node_modules/chart.js/types/element.d.ts | 17 - node_modules/chart.js/types/geometric.d.ts | 37 - .../types/helpers/helpers.canvas.d.ts | 101 - .../types/helpers/helpers.collection.d.ts | 20 - .../chart.js/types/helpers/helpers.color.d.ts | 33 - .../chart.js/types/helpers/helpers.core.d.ts | 157 - .../chart.js/types/helpers/helpers.curve.d.ts | 34 - .../chart.js/types/helpers/helpers.dom.d.ts | 20 - .../types/helpers/helpers.easing.d.ts | 5 - .../types/helpers/helpers.extras.d.ts | 23 - .../types/helpers/helpers.interpolation.d.ts | 1 - .../chart.js/types/helpers/helpers.intl.d.ts | 7 - .../chart.js/types/helpers/helpers.math.d.ts | 17 - .../types/helpers/helpers.options.d.ts | 61 - .../chart.js/types/helpers/helpers.rtl.d.ts | 12 - .../types/helpers/helpers.segment.d.ts | 1 - .../chart.js/types/helpers/index.d.ts | 15 - node_modules/chart.js/types/index.esm.d.ts | 3601 ----- node_modules/chart.js/types/layout.d.ts | 65 - node_modules/chart.js/types/utils.d.ts | 21 - package-lock.json | 24 - package.json | 5 - 43 files changed, 30995 deletions(-) delete mode 100644 node_modules/.package-lock.json delete mode 100644 node_modules/chart.js/LICENSE.md delete mode 100644 node_modules/chart.js/README.md delete mode 100644 node_modules/chart.js/auto/auto.esm.d.ts delete mode 100644 node_modules/chart.js/auto/auto.esm.js delete mode 100644 node_modules/chart.js/auto/auto.js delete mode 100644 node_modules/chart.js/auto/package.json delete mode 100644 node_modules/chart.js/dist/chart.esm.js delete mode 100644 node_modules/chart.js/dist/chart.js delete mode 100644 node_modules/chart.js/dist/chart.min.js delete mode 100644 node_modules/chart.js/dist/chunks/helpers.segment.js delete mode 100644 node_modules/chart.js/dist/helpers.esm.js delete mode 100644 node_modules/chart.js/helpers/helpers.esm.d.ts delete mode 100644 node_modules/chart.js/helpers/helpers.esm.js delete mode 100644 node_modules/chart.js/helpers/helpers.js delete mode 100644 node_modules/chart.js/helpers/package.json delete mode 100644 node_modules/chart.js/package.json delete mode 100644 node_modules/chart.js/types/adapters.d.ts delete mode 100644 node_modules/chart.js/types/animation.d.ts delete mode 100644 node_modules/chart.js/types/basic.d.ts delete mode 100644 node_modules/chart.js/types/color.d.ts delete mode 100644 node_modules/chart.js/types/element.d.ts delete mode 100644 node_modules/chart.js/types/geometric.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.canvas.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.collection.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.color.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.core.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.curve.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.dom.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.easing.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.extras.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.interpolation.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.intl.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.math.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.options.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.rtl.d.ts delete mode 100644 node_modules/chart.js/types/helpers/helpers.segment.d.ts delete mode 100644 node_modules/chart.js/types/helpers/index.d.ts delete mode 100644 node_modules/chart.js/types/index.esm.d.ts delete mode 100644 node_modules/chart.js/types/layout.d.ts delete mode 100644 node_modules/chart.js/types/utils.d.ts delete mode 100644 package-lock.json delete mode 100644 package.json diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json deleted file mode 100644 index 28bd1f48..00000000 --- a/node_modules/.package-lock.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "project-github-tracker", - "lockfileVersion": 2, - "requires": true, - "packages": { - "node_modules/chart.js": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", - "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" - } - } -} diff --git a/node_modules/chart.js/LICENSE.md b/node_modules/chart.js/LICENSE.md deleted file mode 100644 index 5060fabc..00000000 --- a/node_modules/chart.js/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014-2021 Chart.js Contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/chart.js/README.md b/node_modules/chart.js/README.md deleted file mode 100644 index f485b4eb..00000000 --- a/node_modules/chart.js/README.md +++ /dev/null @@ -1,38 +0,0 @@ -

- - https://www.chartjs.org/
-
- Simple yet flexible JavaScript charting for designers & developers -

- -

- Downloads - GitHub Workflow Status - Coverage - Awesome - Slack -

- -## Documentation - -All the links point to the new version 3 of the lib. - -* [Introduction](https://www.chartjs.org/docs/latest/) -* [Getting Started](https://www.chartjs.org/docs/latest/getting-started/index) -* [General](https://www.chartjs.org/docs/latest/general/data-structures) -* [Configuration](https://www.chartjs.org/docs/latest/configuration/index) -* [Charts](https://www.chartjs.org/docs/latest/charts/line) -* [Axes](https://www.chartjs.org/docs/latest/axes/index) -* [Developers](https://www.chartjs.org/docs/latest/developers/index) -* [Popular Extensions](https://github.com/chartjs/awesome) -* [Samples](https://www.chartjs.org/samples/) - -In case you are looking for the docs of version 2, you will have to specify the specific version in the url like this: [https://www.chartjs.org/docs/2.9.4/](https://www.chartjs.org/docs/2.9.4/) - -## Contributing - -Instructions on building and testing Chart.js can be found in [the documentation](https://www.chartjs.org/docs/master/developers/contributing.html#building-and-testing). Before submitting an issue or a pull request, please take a moment to look over the [contributing guidelines](https://www.chartjs.org/docs/master/developers/contributing) first. For support, please post questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/chartjs) with the `chartjs` tag. - -## License - -Chart.js is available under the [MIT license](LICENSE.md). diff --git a/node_modules/chart.js/auto/auto.esm.d.ts b/node_modules/chart.js/auto/auto.esm.d.ts deleted file mode 100644 index f0bc3805..00000000 --- a/node_modules/chart.js/auto/auto.esm.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Chart } from '../types/index.esm'; - -export * from '../types/index.esm'; -export default Chart; diff --git a/node_modules/chart.js/auto/auto.esm.js b/node_modules/chart.js/auto/auto.esm.js deleted file mode 100644 index 42626764..00000000 --- a/node_modules/chart.js/auto/auto.esm.js +++ /dev/null @@ -1,5 +0,0 @@ -import {Chart, registerables} from '../dist/chart.esm'; - -Chart.register(...registerables); - -export default Chart; diff --git a/node_modules/chart.js/auto/auto.js b/node_modules/chart.js/auto/auto.js deleted file mode 100644 index 235580fe..00000000 --- a/node_modules/chart.js/auto/auto.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../dist/chart'); diff --git a/node_modules/chart.js/auto/package.json b/node_modules/chart.js/auto/package.json deleted file mode 100644 index c2ad5b2c..00000000 --- a/node_modules/chart.js/auto/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "chart.js-auto", - "private": true, - "description": "auto registering package", - "main": "auto.js", - "module": "auto.esm.js", - "types": "auto.esm.d.ts" -} diff --git a/node_modules/chart.js/dist/chart.esm.js b/node_modules/chart.js/dist/chart.esm.js deleted file mode 100644 index 2306fa88..00000000 --- a/node_modules/chart.js/dist/chart.esm.js +++ /dev/null @@ -1,10627 +0,0 @@ -/*! - * Chart.js v3.7.1 - * https://www.chartjs.org - * (c) 2022 Chart.js Contributors - * Released under the MIT License - */ -import { r as requestAnimFrame, a as resolve, e as effects, c as color, d as defaults, i as isObject, b as isArray, v as valueOrDefault, u as unlistenArrayEvents, l as listenArrayEvents, f as resolveObjectKey, g as isNumberFinite, h as createContext, j as defined, s as sign, k as isNullOrUndef, _ as _arrayUnique, t as toRadians, m as toPercentage, n as toDimension, T as TAU, o as formatNumber, p as _angleBetween, H as HALF_PI, P as PI, q as isNumber, w as _limitValue, x as _lookupByKey, y as getRelativePosition$1, z as _isPointInArea, A as _rlookupByKey, B as getAngleFromPoint, C as toPadding, D as each, E as getMaximumSize, F as _getParentNode, G as readUsedSize, I as throttled, J as supportsEventListenerOptions, K as _isDomSupported, L as log10, M as _factorize, N as finiteOrDefault, O as callback, Q as _addGrace, R as toDegrees, S as _measureText, U as _int16Range, V as _alignPixel, W as clipArea, X as renderText, Y as unclipArea, Z as toFont, $ as _toLeftRightCenter, a0 as _alignStartEnd, a1 as overrides, a2 as merge, a3 as _capitalize, a4 as descriptors, a5 as isFunction, a6 as _attachContext, a7 as _createResolver, a8 as _descriptors, a9 as mergeIf, aa as uid, ab as debounce, ac as retinaScale, ad as clearCanvas, ae as setsEqual, af as _elementsEqual, ag as _isClickEvent, ah as _isBetween, ai as _readValueToProps, aj as _updateBezierControlPoints, ak as _computeSegments, al as _boundSegments, am as _steppedInterpolation, an as _bezierInterpolation, ao as _pointInLine, ap as _steppedLineTo, aq as _bezierCurveTo, ar as drawPoint, as as addRoundedRectPath, at as toTRBL, au as toTRBLCorners, av as _boundSegment, aw as _normalizeAngle, ax as getRtlAdapter, ay as overrideTextDirection, az as _textX, aA as restoreTextDirection, aB as noop, aC as distanceBetweenPoints, aD as _setMinAndMaxByKey, aE as niceNum, aF as almostWhole, aG as almostEquals, aH as _decimalPlaces, aI as _longestText, aJ as _filterBetween, aK as _lookup } from './chunks/helpers.segment.js'; -export { d as defaults } from './chunks/helpers.segment.js'; - -class Animator { - constructor() { - this._request = null; - this._charts = new Map(); - this._running = false; - this._lastDate = undefined; - } - _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 draw = 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); - draw = true; - } else { - items[i] = items[items.length - 1]; - items.pop(); - } - } - if (draw) { - 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 = new Animator(); - -const transparent = 'transparent'; -const interpolators = { - boolean(from, to, factor) { - return factor > 0.5 ? to : from; - }, - color(from, to, factor) { - const c0 = color(from || transparent); - const c1 = c0.valid && color(to || transparent); - return c1 && c1.valid - ? c1.mix(c0, factor).hexString() - : to; - }, - number(from, to, factor) { - return from + (to - from) * factor; - } -}; -class Animation { - constructor(cfg, target, prop, to) { - const currentValue = target[prop]; - to = resolve([cfg.to, to, currentValue, cfg.from]); - const from = resolve([cfg.from, currentValue, to]); - this._active = true; - this._fn = cfg.fn || interpolators[cfg.type || typeof from]; - this._easing = effects[cfg.easing] || effects.linear; - this._start = Math.floor(Date.now() + (cfg.delay || 0)); - this._duration = this._total = Math.floor(cfg.duration); - this._loop = !!cfg.loop; - this._target = target; - this._prop = prop; - this._from = from; - this._to = to; - this._promises = undefined; - } - active() { - return this._active; - } - update(cfg, to, 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, to, currentValue, cfg.from]); - this._from = resolve([cfg.from, currentValue, to]); - } - } - 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 from = this._from; - const loop = this._loop; - const to = this._to; - let factor; - this._active = from !== to && (loop || (elapsed < duration)); - if (!this._active) { - this._target[prop] = to; - this._notify(true); - return; - } - if (elapsed < 0) { - this._target[prop] = from; - 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(from, to, 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](); - } - } -} - -const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension']; -const colors = ['color', 'borderColor', 'backgroundColor']; -defaults.set('animation', { - delay: undefined, - duration: 1000, - easing: 'easeOutQuart', - fn: undefined, - from: undefined, - loop: undefined, - to: undefined, - type: undefined, -}); -const animationOptions = Object.keys(defaults.animation); -defaults.describe('animation', { - _fallback: false, - _indexable: false, - _scriptable: (name) => name !== 'onProgress' && name !== 'onComplete' && name !== 'fn', -}); -defaults.set('animations', { - colors: { - type: 'color', - properties: colors - }, - numbers: { - type: 'number', - properties: numbers - }, -}); -defaults.describe('animations', { - _fallback: 'animation', -}); -defaults.set('transitions', { - active: { - animation: { - duration: 400 - } - }, - resize: { - animation: { - duration: 0 - } - }, - show: { - animations: { - colors: { - from: 'transparent' - }, - visible: { - type: 'boolean', - duration: 0 - }, - } - }, - hide: { - animations: { - colors: { - to: 'transparent' - }, - visible: { - type: 'boolean', - easing: 'linear', - fn: v => v | 0 - }, - } - } -}); -class Animations { - constructor(chart, config) { - this._chart = chart; - this._properties = new Map(); - this.configure(config); - } - configure(config) { - if (!isObject(config)) { - return; - } - const animatedProps = this._properties; - Object.getOwnPropertyNames(config).forEach(key => { - const cfg = config[key]; - if (!isObject(cfg)) { - return; - } - const resolved = {}; - for (const option of animationOptions) { - resolved[option] = cfg[option]; - } - (isArray(cfg.properties) && cfg.properties || [key]).forEach((prop) => { - 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 === undefined ? allowedOverflow : 0; - const max = opts.max === undefined ? 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; - } - for (i = 0, ilen = keys.length; i < ilen; ++i) { - datasetIndex = +keys[i]; - if (datasetIndex === dsIndex) { - if (options.all) { - continue; - } - break; - } - otherValue = stack.values[datasetIndex]; - if (isNumberFinite(otherValue) && (singleMode || (value === 0 || sign(value) === sign(otherValue)))) { - value += otherValue; - } - } - return value; -} -function convertObjectDataToArray(data) { - 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] = { - x: key, - y: data[key] - }; - } - return adata; -} -function isStacked(scale, meta) { - const stacked = scale && scale.options.stacked; - return stacked || (stacked === undefined && meta.stack !== undefined); -} -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]: index, [vAxis]: value} = item; - const itemStacks = item._stacks || (item._stacks = {}); - stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index); - stack[datasetIndex] = value; - stack._top = getLastIndexInStack(stack, vScale, true, meta.type); - stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type); - } -} -function getFirstScaleId(chart, axis) { - const scales = chart.scales; - return Object.keys(scales).filter(key => scales[key].axis === axis).shift(); -} -function createDatasetContext(parent, index) { - return createContext(parent, - { - active: false, - dataset: undefined, - datasetIndex: index, - index, - mode: 'default', - type: 'dataset' - } - ); -} -function createDataContext(parent, index, element) { - return createContext(parent, { - active: false, - dataIndex: index, - parsed: undefined, - raw: undefined, - element, - index, - 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] === undefined || stacks[axis][datasetIndex] === undefined) { - return; - } - delete stacks[axis][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 = undefined; - this._parsing = false; - this._data = undefined; - this._objectData = undefined; - this._sharedOptions = undefined; - this._drawStart = undefined; - this._drawCount = undefined; - this.enableOptionSharing = false; - this.$context = undefined; - this._syncList = []; - this.initialize(); - } - initialize() { - const meta = this._cachedMeta; - this.configure(); - this.linkScales(); - meta._stacked = isStacked(meta.vScale, meta); - this.addElements(); - } - 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)) { - this._data = convertObjectDataToArray(data); - } 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); - } - } - 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, index; - for (i = 0, ilen = count; i < ilen; ++i) { - index = i + start; - parsed[i] = { - [iAxis]: singleScale || iScale.parse(labels[index], index), - [vAxis]: vScale.parse(data[index], index) - }; - } - return parsed; - } - parseArrayData(meta, data, start, count) { - const {xScale, yScale} = meta; - const parsed = new Array(count); - let i, ilen, index, item; - for (i = 0, ilen = count; i < ilen; ++i) { - index = i + start; - item = data[index]; - parsed[i] = { - x: xScale.parse(item[0], index), - y: yScale.parse(item[1], index) - }; - } - 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, index, item; - for (i = 0, ilen = count; i < ilen; ++i) { - index = i + start; - item = data[index]; - parsed[i] = { - x: xScale.parse(resolveObjectKey(item, xAxisKey), index), - y: yScale.parse(resolveObjectKey(item, yAxisKey), index) - }; - } - return parsed; - } - getParsed(index) { - return this._cachedMeta._parsed[index]; - } - getDataElement(index) { - return this._cachedMeta.data[index]; - } - 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] - }; - 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(index) { - const meta = this._cachedMeta; - const iScale = meta.iScale; - const vScale = meta.vScale; - const parsed = this.getParsed(index); - 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(index, active) { - const mode = active ? 'active' : 'default'; - return index === undefined && this._cachedMeta.dataset - ? this.resolveDatasetElementOptions(mode) - : this.resolveDataElementOptions(index || 0, mode); - } - getContext(index, active, mode) { - const dataset = this.getDataset(); - let context; - if (index >= 0 && index < this._cachedMeta.data.length) { - const element = this._cachedMeta.data[index]; - context = element.$context || - (element.$context = createDataContext(this.getContext(), index, element)); - context.parsed = this.getParsed(index); - context.raw = dataset.data[index]; - context.index = context.dataIndex = index; - } 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(index, mode) { - return this._resolveElementOptions(this.dataElementType.id, mode, index); - } - _resolveElementOptions(elementType, mode = 'default', index) { - const active = mode === 'active'; - const cache = this._cachedDataOpts; - const cacheKey = elementType + '-' + mode; - const cached = cache[cacheKey]; - const sharing = this.enableOptionSharing && defined(index); - 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 names = Object.keys(defaults.elements[elementType]); - const context = () => this.getContext(index, active); - const values = config.resolveNamedOptions(scopes, names, context, prefixes); - if (values.$shared) { - values.$shared = sharing; - cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing)); - } - return values; - } - _resolveAnimations(index, 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(index, 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; - } - updateElement(element, index, properties, mode) { - if (isDirectUpdateMode(mode)) { - Object.assign(element, properties); - } else { - this._resolveAnimations(index, mode).update(element, properties); - } - } - updateSharedOptions(sharedOptions, mode, newOptions) { - if (sharedOptions && !isDirectUpdateMode(mode)) { - this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions); - } - } - _setStyle(element, index, mode, active) { - element.active = active; - const options = this.getStyle(index, active); - this._resolveAnimations(index, mode, active).update(element, { - options: (!active && this.getSharedOptions(options)) || options - }); - } - removeHoverStyle(element, datasetIndex, index) { - this._setStyle(element, index, 'active', false); - } - setHoverStyle(element, datasetIndex, index) { - this._setStyle(element, index, 'active', true); - } - _removeDatasetHoverStyle() { - const element = this._cachedMeta.dataset; - if (element) { - this._setStyle(element, undefined, 'active', false); - } - } - _setDatasetHoverStyle() { - const element = this._cachedMeta.dataset; - if (element) { - this._setStyle(element, undefined, '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]); - } -} -DatasetController.defaults = {}; -DatasetController.prototype.datasetElementType = null; -DatasetController.prototype.dataElementType = null; - -function getAllScaleValues(scale, type) { - if (!scale._cache.$bar) { - const visibleMetas = scale.getMatchingVisibleMetas(type); - let values = []; - for (let i = 0, ilen = visibleMetas.length; i < ilen; i++) { - values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale)); - } - scale._cache.$bar = _arrayUnique(values.sort((a, b) => a - b)); - } - return scale._cache.$bar; -} -function computeMinSampleSize(meta) { - const scale = meta.iScale; - const values = getAllScaleValues(scale, meta.type); - let min = scale._length; - let i, ilen, curr, prev; - const updateMinAndPrev = () => { - if (curr === 32767 || curr === -32768) { - return; - } - if (defined(prev)) { - min = Math.min(min, Math.abs(curr - prev) || min); - } - prev = curr; - }; - for (i = 0, ilen = values.length; i < ilen; ++i) { - curr = scale.getPixelForValue(values[i]); - updateMinAndPrev(); - } - prev = undefined; - for (i = 0, ilen = scale.ticks.length; i < ilen; ++i) { - curr = scale.getPixelForTick(i); - updateMinAndPrev(); - } - return min; -} -function computeFitCategoryTraits(index, ruler, options, stackCount) { - const thickness = options.barThickness; - let size, ratio; - if (isNullOrUndef(thickness)) { - size = ruler.min * options.categoryPercentage; - ratio = options.barPercentage; - } else { - size = thickness * stackCount; - ratio = 1; - } - return { - chunk: size / stackCount, - ratio, - start: ruler.pixels[index] - (size / 2) - }; -} -function computeFlexCategoryTraits(index, ruler, options, stackCount) { - const pixels = ruler.pixels; - const curr = pixels[index]; - let prev = index > 0 ? pixels[index - 1] : null; - let next = index < pixels.length - 1 ? pixels[index + 1] : null; - const percent = options.categoryPercentage; - if (prev === null) { - prev = curr - (next === null ? ruler.end - ruler.start : next - curr); - } - if (next === null) { - next = curr + curr - prev; - } - const start = curr - (curr - Math.min(prev, next)) / 2 * percent; - const size = Math.abs(next - prev) / 2 * percent; - return { - chunk: size / stackCount, - ratio: options.barPercentage, - start - }; -} -function parseFloatBar(entry, item, vScale, i) { - const startValue = vScale.parse(entry[0], i); - const endValue = vScale.parse(entry[1], i); - const min = Math.min(startValue, endValue); - const max = Math.max(startValue, endValue); - let barStart = min; - let barEnd = max; - if (Math.abs(min) > Math.abs(max)) { - barStart = max; - barEnd = min; - } - item[vScale.axis] = barEnd; - item._custom = { - barStart, - barEnd, - start: startValue, - end: endValue, - min, - max - }; -} -function parseValue(entry, item, vScale, i) { - if (isArray(entry)) { - parseFloatBar(entry, item, vScale, i); - } else { - item[vScale.axis] = vScale.parse(entry, i); - } - return item; -} -function parseArrayOrPrimitive(meta, data, start, count) { - const iScale = meta.iScale; - const vScale = meta.vScale; - const labels = iScale.getLabels(); - const singleScale = iScale === vScale; - const parsed = []; - let i, ilen, item, entry; - for (i = start, ilen = start + count; i < ilen; ++i) { - entry = data[i]; - item = {}; - item[iScale.axis] = singleScale || iScale.parse(labels[i], i); - parsed.push(parseValue(entry, item, vScale, i)); - } - return parsed; -} -function isFloatBar(custom) { - return custom && custom.barStart !== undefined && custom.barEnd !== undefined; -} -function barSign(size, vScale, actualBase) { - if (size !== 0) { - return sign(size); - } - return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1); -} -function borderProps(properties) { - let reverse, start, end, top, bottom; - if (properties.horizontal) { - reverse = properties.base > properties.x; - start = 'left'; - end = 'right'; - } else { - reverse = properties.base < properties.y; - start = 'bottom'; - end = 'top'; - } - if (reverse) { - top = 'end'; - bottom = 'start'; - } else { - top = 'start'; - bottom = 'end'; - } - return {start, end, reverse, top, bottom}; -} -function setBorderSkipped(properties, options, stack, index) { - let edge = options.borderSkipped; - const res = {}; - if (!edge) { - properties.borderSkipped = res; - return; - } - const {start, end, reverse, top, bottom} = borderProps(properties); - if (edge === 'middle' && stack) { - properties.enableBorderRadius = true; - if ((stack._top || 0) === index) { - edge = top; - } else if ((stack._bottom || 0) === index) { - edge = bottom; - } else { - res[parseEdge(bottom, start, end, reverse)] = true; - edge = top; - } - } - res[parseEdge(edge, start, end, reverse)] = true; - properties.borderSkipped = res; -} -function parseEdge(edge, a, b, reverse) { - if (reverse) { - edge = swap(edge, a, b); - edge = startEnd(edge, b, a); - } else { - edge = startEnd(edge, a, b); - } - return edge; -} -function swap(orig, v1, v2) { - return orig === v1 ? v2 : orig === v2 ? v1 : orig; -} -function startEnd(v, start, end) { - return v === 'start' ? start : v === 'end' ? end : v; -} -function setInflateAmount(properties, {inflateAmount}, ratio) { - properties.inflateAmount = inflateAmount === 'auto' - ? ratio === 1 ? 0.33 : 0 - : inflateAmount; -} -class BarController extends DatasetController { - parsePrimitiveData(meta, data, start, count) { - return parseArrayOrPrimitive(meta, data, start, count); - } - parseArrayData(meta, data, start, count) { - return parseArrayOrPrimitive(meta, data, start, count); - } - parseObjectData(meta, data, start, count) { - const {iScale, vScale} = meta; - const {xAxisKey = 'x', yAxisKey = 'y'} = this._parsing; - const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey; - const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey; - const parsed = []; - let i, ilen, item, obj; - for (i = start, ilen = start + count; i < ilen; ++i) { - obj = data[i]; - item = {}; - item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i); - parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i)); - } - return parsed; - } - updateRangeFromParsed(range, scale, parsed, stack) { - super.updateRangeFromParsed(range, scale, parsed, stack); - const custom = parsed._custom; - if (custom && scale === this._cachedMeta.vScale) { - range.min = Math.min(range.min, custom.min); - range.max = Math.max(range.max, custom.max); - } - } - getMaxOverflow() { - return 0; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const {iScale, vScale} = meta; - const parsed = this.getParsed(index); - const custom = parsed._custom; - const value = isFloatBar(custom) - ? '[' + custom.start + ', ' + custom.end + ']' - : '' + vScale.getLabelForValue(parsed[vScale.axis]); - return { - label: '' + iScale.getLabelForValue(parsed[iScale.axis]), - value - }; - } - initialize() { - this.enableOptionSharing = true; - super.initialize(); - const meta = this._cachedMeta; - meta.stack = this.getDataset().stack; - } - update(mode) { - const meta = this._cachedMeta; - this.updateElements(meta.data, 0, meta.data.length, mode); - } - updateElements(bars, start, count, mode) { - const reset = mode === 'reset'; - const {index, _cachedMeta: {vScale}} = this; - const base = vScale.getBasePixel(); - const horizontal = vScale.isHorizontal(); - const ruler = this._getRuler(); - const firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - this.updateSharedOptions(sharedOptions, mode, firstOpts); - for (let i = start; i < start + count; i++) { - const parsed = this.getParsed(i); - const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : this._calculateBarValuePixels(i); - const ipixels = this._calculateBarIndexPixels(i, ruler); - const stack = (parsed._stacks || {})[vScale.axis]; - const properties = { - horizontal, - base: vpixels.base, - enableBorderRadius: !stack || isFloatBar(parsed._custom) || (index === stack._top || index === stack._bottom), - x: horizontal ? vpixels.head : ipixels.center, - y: horizontal ? ipixels.center : vpixels.head, - height: horizontal ? ipixels.size : Math.abs(vpixels.size), - width: horizontal ? Math.abs(vpixels.size) : ipixels.size - }; - if (includeOptions) { - properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode); - } - const options = properties.options || bars[i].options; - setBorderSkipped(properties, options, stack, index); - setInflateAmount(properties, options, ruler.ratio); - this.updateElement(bars[i], i, properties, mode); - } - } - _getStacks(last, dataIndex) { - const meta = this._cachedMeta; - const iScale = meta.iScale; - const metasets = iScale.getMatchingVisibleMetas(this._type); - const stacked = iScale.options.stacked; - const ilen = metasets.length; - const stacks = []; - let i, item; - for (i = 0; i < ilen; ++i) { - item = metasets[i]; - if (!item.controller.options.grouped) { - continue; - } - if (typeof dataIndex !== 'undefined') { - const val = item.controller.getParsed(dataIndex)[ - item.controller._cachedMeta.vScale.axis - ]; - if (isNullOrUndef(val) || isNaN(val)) { - continue; - } - } - if (stacked === false || stacks.indexOf(item.stack) === -1 || - (stacked === undefined && item.stack === undefined)) { - stacks.push(item.stack); - } - if (item.index === last) { - break; - } - } - if (!stacks.length) { - stacks.push(undefined); - } - return stacks; - } - _getStackCount(index) { - return this._getStacks(undefined, index).length; - } - _getStackIndex(datasetIndex, name, dataIndex) { - const stacks = this._getStacks(datasetIndex, dataIndex); - const index = (name !== undefined) - ? stacks.indexOf(name) - : -1; - return (index === -1) - ? stacks.length - 1 - : index; - } - _getRuler() { - const opts = this.options; - const meta = this._cachedMeta; - const iScale = meta.iScale; - const pixels = []; - let i, ilen; - for (i = 0, ilen = meta.data.length; i < ilen; ++i) { - pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i)); - } - const barThickness = opts.barThickness; - const min = barThickness || computeMinSampleSize(meta); - return { - min, - pixels, - start: iScale._startPixel, - end: iScale._endPixel, - stackCount: this._getStackCount(), - scale: iScale, - grouped: opts.grouped, - ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage - }; - } - _calculateBarValuePixels(index) { - const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = this; - const actualBase = baseValue || 0; - const parsed = this.getParsed(index); - const custom = parsed._custom; - const floating = isFloatBar(custom); - let value = parsed[vScale.axis]; - let start = 0; - let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value; - let head, size; - if (length !== value) { - start = length - value; - length = value; - } - if (floating) { - value = custom.barStart; - length = custom.barEnd - custom.barStart; - if (value !== 0 && sign(value) !== sign(custom.barEnd)) { - start = 0; - } - start += value; - } - const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start; - let base = vScale.getPixelForValue(startValue); - if (this.chart.getDataVisibility(index)) { - head = vScale.getPixelForValue(start + length); - } else { - head = base; - } - size = head - base; - if (Math.abs(size) < minBarLength) { - size = barSign(size, vScale, actualBase) * minBarLength; - if (value === actualBase) { - base -= size / 2; - } - head = base + size; - } - if (base === vScale.getPixelForValue(actualBase)) { - const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2; - base += halfGrid; - size -= halfGrid; - } - return { - size, - base, - head, - center: head + size / 2 - }; - } - _calculateBarIndexPixels(index, ruler) { - const scale = ruler.scale; - const options = this.options; - const skipNull = options.skipNull; - const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity); - let center, size; - if (ruler.grouped) { - const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount; - const range = options.barThickness === 'flex' - ? computeFlexCategoryTraits(index, ruler, options, stackCount) - : computeFitCategoryTraits(index, ruler, options, stackCount); - const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined); - center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); - size = Math.min(maxBarThickness, range.chunk * range.ratio); - } else { - center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index); - size = Math.min(maxBarThickness, ruler.min * ruler.ratio); - } - return { - base: center - size / 2, - head: center + size / 2, - center, - size - }; - } - draw() { - const meta = this._cachedMeta; - const vScale = meta.vScale; - const rects = meta.data; - const ilen = rects.length; - let i = 0; - for (; i < ilen; ++i) { - if (this.getParsed(i)[vScale.axis] !== null) { - rects[i].draw(this._ctx); - } - } - } -} -BarController.id = 'bar'; -BarController.defaults = { - datasetElementType: false, - dataElementType: 'bar', - categoryPercentage: 0.8, - barPercentage: 0.9, - grouped: true, - animations: { - numbers: { - type: 'number', - properties: ['x', 'y', 'base', 'width', 'height'] - } - } -}; -BarController.overrides = { - scales: { - _index_: { - type: 'category', - offset: true, - grid: { - offset: true - } - }, - _value_: { - type: 'linear', - beginAtZero: true, - } - } -}; - -class BubbleController extends DatasetController { - initialize() { - this.enableOptionSharing = true; - super.initialize(); - } - parsePrimitiveData(meta, data, start, count) { - const parsed = super.parsePrimitiveData(meta, data, start, count); - for (let i = 0; i < parsed.length; i++) { - parsed[i]._custom = this.resolveDataElementOptions(i + start).radius; - } - return parsed; - } - parseArrayData(meta, data, start, count) { - const parsed = super.parseArrayData(meta, data, start, count); - for (let i = 0; i < parsed.length; i++) { - const item = data[start + i]; - parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius); - } - return parsed; - } - parseObjectData(meta, data, start, count) { - const parsed = super.parseObjectData(meta, data, start, count); - for (let i = 0; i < parsed.length; i++) { - const item = data[start + i]; - parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius); - } - return parsed; - } - getMaxOverflow() { - const data = this._cachedMeta.data; - let max = 0; - for (let i = data.length - 1; i >= 0; --i) { - max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2); - } - return max > 0 && max; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const {xScale, yScale} = meta; - const parsed = this.getParsed(index); - const x = xScale.getLabelForValue(parsed.x); - const y = yScale.getLabelForValue(parsed.y); - const r = parsed._custom; - return { - label: meta.label, - value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')' - }; - } - update(mode) { - const points = this._cachedMeta.data; - this.updateElements(points, 0, points.length, mode); - } - updateElements(points, start, count, mode) { - const reset = mode === 'reset'; - const {iScale, vScale} = this._cachedMeta; - const firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - const iAxis = iScale.axis; - const vAxis = vScale.axis; - for (let i = start; i < start + count; i++) { - const point = points[i]; - const parsed = !reset && this.getParsed(i); - const properties = {}; - const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]); - const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]); - properties.skip = isNaN(iPixel) || isNaN(vPixel); - if (includeOptions) { - properties.options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); - if (reset) { - properties.options.radius = 0; - } - } - this.updateElement(point, i, properties, mode); - } - this.updateSharedOptions(sharedOptions, mode, firstOpts); - } - resolveDataElementOptions(index, mode) { - const parsed = this.getParsed(index); - let values = super.resolveDataElementOptions(index, mode); - if (values.$shared) { - values = Object.assign({}, values, {$shared: false}); - } - const radius = values.radius; - if (mode !== 'active') { - values.radius = 0; - } - values.radius += valueOrDefault(parsed && parsed._custom, radius); - return values; - } -} -BubbleController.id = 'bubble'; -BubbleController.defaults = { - datasetElementType: false, - dataElementType: 'point', - animations: { - numbers: { - type: 'number', - properties: ['x', 'y', 'borderWidth', 'radius'] - } - } -}; -BubbleController.overrides = { - scales: { - x: { - type: 'linear' - }, - y: { - type: 'linear' - } - }, - plugins: { - tooltip: { - callbacks: { - title() { - return ''; - } - } - } - } -}; - -function getRatioAndOffset(rotation, circumference, cutout) { - let ratioX = 1; - let ratioY = 1; - let offsetX = 0; - let offsetY = 0; - if (circumference < TAU) { - const startAngle = rotation; - const endAngle = startAngle + circumference; - const startX = Math.cos(startAngle); - const startY = Math.sin(startAngle); - const endX = Math.cos(endAngle); - const endY = Math.sin(endAngle); - const calcMax = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout); - const calcMin = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout); - const maxX = calcMax(0, startX, endX); - const maxY = calcMax(HALF_PI, startY, endY); - const minX = calcMin(PI, startX, endX); - const minY = calcMin(PI + HALF_PI, startY, endY); - ratioX = (maxX - minX) / 2; - ratioY = (maxY - minY) / 2; - offsetX = -(maxX + minX) / 2; - offsetY = -(maxY + minY) / 2; - } - return {ratioX, ratioY, offsetX, offsetY}; -} -class DoughnutController extends DatasetController { - constructor(chart, datasetIndex) { - super(chart, datasetIndex); - this.enableOptionSharing = true; - this.innerRadius = undefined; - this.outerRadius = undefined; - this.offsetX = undefined; - this.offsetY = undefined; - } - linkScales() {} - parse(start, count) { - const data = this.getDataset().data; - const meta = this._cachedMeta; - if (this._parsing === false) { - meta._parsed = data; - } else { - let getter = (i) => +data[i]; - if (isObject(data[start])) { - const {key = 'value'} = this._parsing; - getter = (i) => +resolveObjectKey(data[i], key); - } - let i, ilen; - for (i = start, ilen = start + count; i < ilen; ++i) { - meta._parsed[i] = getter(i); - } - } - } - _getRotation() { - return toRadians(this.options.rotation - 90); - } - _getCircumference() { - return toRadians(this.options.circumference); - } - _getRotationExtents() { - let min = TAU; - let max = -TAU; - for (let i = 0; i < this.chart.data.datasets.length; ++i) { - if (this.chart.isDatasetVisible(i)) { - const controller = this.chart.getDatasetMeta(i).controller; - const rotation = controller._getRotation(); - const circumference = controller._getCircumference(); - min = Math.min(min, rotation); - max = Math.max(max, rotation + circumference); - } - } - return { - rotation: min, - circumference: max - min, - }; - } - update(mode) { - const chart = this.chart; - const {chartArea} = chart; - const meta = this._cachedMeta; - const arcs = meta.data; - const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing; - const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0); - const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1); - const chartWeight = this._getRingWeight(this.index); - const {circumference, rotation} = this._getRotationExtents(); - const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout); - const maxWidth = (chartArea.width - spacing) / ratioX; - const maxHeight = (chartArea.height - spacing) / ratioY; - const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); - const outerRadius = toDimension(this.options.radius, maxRadius); - const innerRadius = Math.max(outerRadius * cutout, 0); - const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal(); - this.offsetX = offsetX * outerRadius; - this.offsetY = offsetY * outerRadius; - meta.total = this.calculateTotal(); - this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index); - this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0); - this.updateElements(arcs, 0, arcs.length, mode); - } - _circumference(i, reset) { - const opts = this.options; - const meta = this._cachedMeta; - const circumference = this._getCircumference(); - if ((reset && opts.animation.animateRotate) || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) { - return 0; - } - return this.calculateCircumference(meta._parsed[i] * circumference / TAU); - } - updateElements(arcs, start, count, mode) { - const reset = mode === 'reset'; - const chart = this.chart; - const chartArea = chart.chartArea; - const opts = chart.options; - const animationOpts = opts.animation; - const centerX = (chartArea.left + chartArea.right) / 2; - const centerY = (chartArea.top + chartArea.bottom) / 2; - const animateScale = reset && animationOpts.animateScale; - const innerRadius = animateScale ? 0 : this.innerRadius; - const outerRadius = animateScale ? 0 : this.outerRadius; - const firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - let startAngle = this._getRotation(); - let i; - for (i = 0; i < start; ++i) { - startAngle += this._circumference(i, reset); - } - for (i = start; i < start + count; ++i) { - const circumference = this._circumference(i, reset); - const arc = arcs[i]; - const properties = { - x: centerX + this.offsetX, - y: centerY + this.offsetY, - startAngle, - endAngle: startAngle + circumference, - circumference, - outerRadius, - innerRadius - }; - if (includeOptions) { - properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode); - } - startAngle += circumference; - this.updateElement(arc, i, properties, mode); - } - this.updateSharedOptions(sharedOptions, mode, firstOpts); - } - calculateTotal() { - const meta = this._cachedMeta; - const metaData = meta.data; - let total = 0; - let i; - for (i = 0; i < metaData.length; i++) { - const value = meta._parsed[i]; - if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) { - total += Math.abs(value); - } - } - return total; - } - calculateCircumference(value) { - const total = this._cachedMeta.total; - if (total > 0 && !isNaN(value)) { - return TAU * (Math.abs(value) / total); - } - return 0; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const chart = this.chart; - const labels = chart.data.labels || []; - const value = formatNumber(meta._parsed[index], chart.options.locale); - return { - label: labels[index] || '', - value, - }; - } - getMaxBorderWidth(arcs) { - let max = 0; - const chart = this.chart; - let i, ilen, meta, controller, options; - if (!arcs) { - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - arcs = meta.data; - controller = meta.controller; - break; - } - } - } - if (!arcs) { - return 0; - } - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - options = controller.resolveDataElementOptions(i); - if (options.borderAlign !== 'inner') { - max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0); - } - } - return max; - } - getMaxOffset(arcs) { - let max = 0; - for (let i = 0, ilen = arcs.length; i < ilen; ++i) { - const options = this.resolveDataElementOptions(i); - max = Math.max(max, options.offset || 0, options.hoverOffset || 0); - } - return max; - } - _getRingWeightOffset(datasetIndex) { - let ringWeightOffset = 0; - for (let i = 0; i < datasetIndex; ++i) { - if (this.chart.isDatasetVisible(i)) { - ringWeightOffset += this._getRingWeight(i); - } - } - return ringWeightOffset; - } - _getRingWeight(datasetIndex) { - return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0); - } - _getVisibleDatasetWeightTotal() { - return this._getRingWeightOffset(this.chart.data.datasets.length) || 1; - } -} -DoughnutController.id = 'doughnut'; -DoughnutController.defaults = { - datasetElementType: false, - dataElementType: 'arc', - animation: { - animateRotate: true, - animateScale: false - }, - animations: { - numbers: { - type: 'number', - properties: ['circumference', 'endAngle', 'innerRadius', 'outerRadius', 'startAngle', 'x', 'y', 'offset', 'borderWidth', 'spacing'] - }, - }, - cutout: '50%', - rotation: 0, - circumference: 360, - radius: '100%', - spacing: 0, - indexAxis: 'r', -}; -DoughnutController.descriptors = { - _scriptable: (name) => name !== 'spacing', - _indexable: (name) => name !== 'spacing', -}; -DoughnutController.overrides = { - aspectRatio: 1, - plugins: { - legend: { - labels: { - generateLabels(chart) { - const data = chart.data; - if (data.labels.length && data.datasets.length) { - const {labels: {pointStyle}} = chart.legend.options; - return data.labels.map((label, i) => { - const meta = chart.getDatasetMeta(0); - const style = meta.controller.getStyle(i); - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - pointStyle: pointStyle, - hidden: !chart.getDataVisibility(i), - index: i - }; - }); - } - return []; - } - }, - onClick(e, legendItem, legend) { - legend.chart.toggleDataVisibility(legendItem.index); - legend.chart.update(); - } - }, - tooltip: { - callbacks: { - title() { - return ''; - }, - label(tooltipItem) { - let dataLabel = tooltipItem.label; - const value = ': ' + tooltipItem.formattedValue; - if (isArray(dataLabel)) { - dataLabel = dataLabel.slice(); - dataLabel[0] += value; - } else { - dataLabel += value; - } - return dataLabel; - } - } - } - } -}; - -class LineController extends DatasetController { - initialize() { - this.enableOptionSharing = 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, undefined, { - 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 firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - 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'; - let prevParsed = start > 0 && this.getParsed(start - 1); - for (let i = start; i < start + count; ++i) { - const point = points[i]; - const parsed = this.getParsed(i); - const properties = directUpdate ? point : {}; - 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 && (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; - } - this.updateSharedOptions(sharedOptions, mode, firstOpts); - } - 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(); - } -} -LineController.id = 'line'; -LineController.defaults = { - datasetElementType: 'line', - dataElementType: 'point', - showLine: true, - spanGaps: false, -}; -LineController.overrides = { - scales: { - _index_: { - type: 'category', - }, - _value_: { - type: 'linear', - }, - } -}; -function getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) { - const pointCount = points.length; - let start = 0; - let count = pointCount; - if (meta._sorted) { - const {iScale, _parsed} = meta; - const axis = iScale.axis; - const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); - if (minDefined) { - start = _limitValue(Math.min( - _lookupByKey(_parsed, iScale.axis, min).lo, - animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), - 0, pointCount - 1); - } - if (maxDefined) { - count = _limitValue(Math.max( - _lookupByKey(_parsed, iScale.axis, max).hi + 1, - animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max)).hi + 1), - 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; -} - -class PolarAreaController extends DatasetController { - constructor(chart, datasetIndex) { - super(chart, datasetIndex); - this.innerRadius = undefined; - this.outerRadius = undefined; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const chart = this.chart; - const labels = chart.data.labels || []; - const value = formatNumber(meta._parsed[index].r, chart.options.locale); - return { - label: labels[index] || '', - value, - }; - } - update(mode) { - const arcs = this._cachedMeta.data; - this._updateRadius(); - this.updateElements(arcs, 0, arcs.length, mode); - } - _updateRadius() { - const chart = this.chart; - const chartArea = chart.chartArea; - const opts = chart.options; - const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); - const outerRadius = Math.max(minSize / 2, 0); - const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); - const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount(); - this.outerRadius = outerRadius - (radiusLength * this.index); - this.innerRadius = this.outerRadius - radiusLength; - } - updateElements(arcs, start, count, mode) { - const reset = mode === 'reset'; - const chart = this.chart; - const dataset = this.getDataset(); - const opts = chart.options; - const animationOpts = opts.animation; - const scale = this._cachedMeta.rScale; - const centerX = scale.xCenter; - const centerY = scale.yCenter; - const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI; - let angle = datasetStartAngle; - let i; - const defaultAngle = 360 / this.countVisibleElements(); - for (i = 0; i < start; ++i) { - angle += this._computeAngle(i, mode, defaultAngle); - } - for (i = start; i < start + count; i++) { - const arc = arcs[i]; - let startAngle = angle; - let endAngle = angle + this._computeAngle(i, mode, defaultAngle); - let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0; - angle = endAngle; - if (reset) { - if (animationOpts.animateScale) { - outerRadius = 0; - } - if (animationOpts.animateRotate) { - startAngle = endAngle = datasetStartAngle; - } - } - const properties = { - x: centerX, - y: centerY, - innerRadius: 0, - outerRadius, - startAngle, - endAngle, - options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode) - }; - this.updateElement(arc, i, properties, mode); - } - } - countVisibleElements() { - const dataset = this.getDataset(); - const meta = this._cachedMeta; - let count = 0; - meta.data.forEach((element, index) => { - if (!isNaN(dataset.data[index]) && this.chart.getDataVisibility(index)) { - count++; - } - }); - return count; - } - _computeAngle(index, mode, defaultAngle) { - return this.chart.getDataVisibility(index) - ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) - : 0; - } -} -PolarAreaController.id = 'polarArea'; -PolarAreaController.defaults = { - dataElementType: 'arc', - animation: { - animateRotate: true, - animateScale: true - }, - animations: { - numbers: { - type: 'number', - properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius'] - }, - }, - indexAxis: 'r', - startAngle: 0, -}; -PolarAreaController.overrides = { - aspectRatio: 1, - plugins: { - legend: { - labels: { - generateLabels(chart) { - const data = chart.data; - if (data.labels.length && data.datasets.length) { - const {labels: {pointStyle}} = chart.legend.options; - return data.labels.map((label, i) => { - const meta = chart.getDatasetMeta(0); - const style = meta.controller.getStyle(i); - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - pointStyle: pointStyle, - hidden: !chart.getDataVisibility(i), - index: i - }; - }); - } - return []; - } - }, - onClick(e, legendItem, legend) { - legend.chart.toggleDataVisibility(legendItem.index); - legend.chart.update(); - } - }, - tooltip: { - callbacks: { - title() { - return ''; - }, - label(context) { - return context.chart.data.labels[context.dataIndex] + ': ' + context.formattedValue; - } - } - } - }, - scales: { - r: { - type: 'radialLinear', - angleLines: { - display: false - }, - beginAtZero: true, - grid: { - circular: true - }, - pointLabels: { - display: false - }, - startAngle: 0 - } - } -}; - -class PieController extends DoughnutController { -} -PieController.id = 'pie'; -PieController.defaults = { - cutout: 0, - rotation: 0, - circumference: 360, - radius: '100%' -}; - -class RadarController extends DatasetController { - getLabelAndValue(index) { - const vScale = this._cachedMeta.vScale; - const parsed = this.getParsed(index); - return { - label: vScale.getLabels()[index], - value: '' + vScale.getLabelForValue(parsed[vScale.axis]) - }; - } - update(mode) { - const meta = this._cachedMeta; - const line = meta.dataset; - const points = meta.data || []; - const labels = meta.iScale.getLabels(); - line.points = points; - if (mode !== 'resize') { - const options = this.resolveDatasetElementOptions(mode); - if (!this.options.showLine) { - options.borderWidth = 0; - } - const properties = { - _loop: true, - _fullLoop: labels.length === points.length, - options - }; - this.updateElement(line, undefined, properties, mode); - } - this.updateElements(points, 0, points.length, mode); - } - updateElements(points, start, count, mode) { - const dataset = this.getDataset(); - const scale = this._cachedMeta.rScale; - const reset = mode === 'reset'; - for (let i = start; i < start + count; i++) { - const point = points[i]; - const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); - const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]); - const x = reset ? scale.xCenter : pointPosition.x; - const y = reset ? scale.yCenter : pointPosition.y; - const properties = { - x, - y, - angle: pointPosition.angle, - skip: isNaN(x) || isNaN(y), - options - }; - this.updateElement(point, i, properties, mode); - } - } -} -RadarController.id = 'radar'; -RadarController.defaults = { - datasetElementType: 'line', - dataElementType: 'point', - indexAxis: 'r', - showLine: true, - elements: { - line: { - fill: 'start' - } - }, -}; -RadarController.overrides = { - aspectRatio: 1, - scales: { - r: { - type: 'radialLinear', - } - } -}; - -class ScatterController extends LineController { -} -ScatterController.id = 'scatter'; -ScatterController.defaults = { - showLine: false, - fill: false -}; -ScatterController.overrides = { - interaction: { - mode: 'point' - }, - plugins: { - tooltip: { - callbacks: { - title() { - return ''; - }, - label(item) { - return '(' + item.label + ', ' + item.formattedValue + ')'; - } - } - } - }, - scales: { - x: { - type: 'linear' - }, - y: { - type: 'linear' - } - } -}; - -var controllers = /*#__PURE__*/Object.freeze({ -__proto__: null, -BarController: BarController, -BubbleController: BubbleController, -DoughnutController: DoughnutController, -LineController: LineController, -PolarAreaController: PolarAreaController, -PieController: PieController, -RadarController: RadarController, -ScatterController: ScatterController -}); - -function abstract() { - throw new Error('This method is not implemented: Check that a complete date adapter is provided.'); -} -class DateAdapter { - constructor(options) { - this.options = options || {}; - } - formats() { - return abstract(); - } - parse(value, format) { - return abstract(); - } - format(timestamp, format) { - return abstract(); - } - add(timestamp, amount, unit) { - return abstract(); - } - diff(a, b, unit) { - return abstract(); - } - startOf(timestamp, unit, weekday) { - return abstract(); - } - endOf(timestamp, unit) { - return abstract(); - } -} -DateAdapter.override = function(members) { - Object.assign(DateAdapter.prototype, members); -}; -var adapters = { - _date: DateAdapter -}; - -function getRelativePosition(e, chart) { - if ('native' in e) { - return { - x: e.x, - y: e.y - }; - } - return getRelativePosition$1(e, chart); -} -function evaluateAllVisibleItems(chart, handler) { - const metasets = chart.getSortedVisibleDatasetMetas(); - let index, data, element; - for (let i = 0, ilen = metasets.length; i < ilen; ++i) { - ({index, data} = metasets[i]); - for (let j = 0, jlen = data.length; j < jlen; ++j) { - element = data[j]; - if (!element.skip) { - handler(element, index, j); - } - } - } -} -function binarySearch(metaset, axis, value, intersect) { - const {controller, data, _sorted} = metaset; - const iScale = controller._cachedMeta.iScale; - if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) { - const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey; - if (!intersect) { - return lookupMethod(data, axis, value); - } 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 optimizedEvaluateItems(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, 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, index, 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) { - const items = []; - if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { - return items; - } - const evaluationFunc = function(element, datasetIndex, index) { - if (element.inRange(position.x, position.y, useFinalPosition)) { - items.push({element, datasetIndex, index}); - } - }; - optimizedEvaluateItems(chart, axis, position, evaluationFunc, true); - return items; -} -function getNearestRadialItems(chart, position, axis, useFinalPosition) { - let items = []; - function evaluationFunc(element, datasetIndex, index) { - 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}); - } - } - optimizedEvaluateItems(chart, axis, position, evaluationFunc); - return items; -} -function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition) { - let items = []; - const distanceMetric = getDistanceMetricForAxis(axis); - let minDistance = Number.POSITIVE_INFINITY; - function evaluationFunc(element, datasetIndex, index) { - const inRange = element.inRange(position.x, position.y, useFinalPosition); - if (intersect && !inRange) { - return; - } - const center = element.getCenterPoint(useFinalPosition); - const pointInArea = _isPointInArea(center, chart.chartArea, chart._minPadding); - if (!pointInArea && !inRange) { - return; - } - const distance = distanceMetric(position, center); - if (distance < minDistance) { - items = [{element, datasetIndex, index}]; - minDistance = distance; - } else if (distance === minDistance) { - items.push({element, datasetIndex, index}); - } - } - optimizedEvaluateItems(chart, axis, position, evaluationFunc); - return items; -} -function getNearestItems(chart, position, axis, intersect, useFinalPosition) { - if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { - return []; - } - return axis === 'r' && !intersect - ? getNearestRadialItems(chart, position, axis, useFinalPosition) - : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition); -} -function getAxisItems(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const items = []; - const axis = options.axis; - const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange'; - let intersectsItem = false; - evaluateAllVisibleItems(chart, (element, datasetIndex, index) => { - if (element[rangeMethod](position[axis], useFinalPosition)) { - items.push({element, datasetIndex, index}); - } - if (element.inRange(position.x, position.y, useFinalPosition)) { - intersectsItem = true; - } - }); - if (options.intersect && !intersectsItem) { - return []; - } - return items; -} -var Interaction = { - modes: { - index(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const axis = options.axis || 'x'; - const items = options.intersect - ? getIntersectItems(chart, position, axis, useFinalPosition) - : getNearestItems(chart, position, axis, false, useFinalPosition); - const elements = []; - if (!items.length) { - return []; - } - chart.getSortedVisibleDatasetMetas().forEach((meta) => { - const index = items[0].index; - const element = meta.data[index]; - if (element && !element.skip) { - elements.push({element, datasetIndex: meta.index, index}); - } - }); - return elements; - }, - dataset(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const axis = options.axis || 'xy'; - let items = options.intersect - ? getIntersectItems(chart, position, axis, useFinalPosition) : - getNearestItems(chart, position, axis, false, useFinalPosition); - 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'; - return getIntersectItems(chart, position, axis, useFinalPosition); - }, - nearest(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const axis = options.axis || 'xy'; - return getNearestItems(chart, position, axis, options.intersect, useFinalPosition); - }, - x(chart, e, options, useFinalPosition) { - return getAxisItems(chart, e, {axis: 'x', intersect: options.intersect}, useFinalPosition); - }, - y(chart, e, options, useFinalPosition) { - return getAxisItems(chart, e, {axis: 'y', intersect: 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(layouts) { - const stacks = {}; - for (const wrap of layouts) { - 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(layouts, params) { - const stacks = buildStacks(layouts); - const {vBoxMaxWidth, hBoxMaxHeight} = params; - let i, ilen, layout; - for (i = 0, ilen = layouts.length; i < ilen; ++i) { - layout = layouts[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(positions) { - const margin = {left: 0, top: 0, right: 0, bottom: 0}; - positions.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] || {count: 1, 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; -} -defaults.set('layout', { - autoPadding: true, - padding: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } -}); -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 index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; - if (index !== -1) { - chart.boxes.splice(index, 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 !== undefined) { - canvas.width = displayWidth; - } - } - if (isNullOrEmpty(renderHeight)) { - if (canvas.style.height === '') { - canvas.height = canvas.width / (aspectRatio || 2); - } else { - const displayHeight = readUsedSize(canvas, 'height'); - if (displayHeight !== undefined) { - canvas.height = displayHeight; - } - } - } - return canvas; -} -const eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; -function addListener(node, type, listener) { - node.addEventListener(type, listener, eventListenerOptions); -} -function removeListener(chart, type, listener) { - chart.canvas.removeEventListener(type, listener, eventListenerOptions); -} -function fromNativeEvent(event, chart) { - const type = EVENT_TYPES[event.type] || event.type; - const {x, y} = getRelativePosition$1(event, chart); - return { - type, - chart, - native: event, - x: x !== undefined ? x : null, - y: y !== undefined ? 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 = 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, (args) => { - const event = args[0]; - return [event, event.offsetX, event.offsetY]; - }); - 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] = undefined; - } - getDevicePixelRatio() { - return window.devicePixelRatio; - } - getMaximumSize(canvas, width, height, aspectRatio) { - return getMaximumSize(canvas, width, height, aspectRatio); - } - isAttached(canvas) { - const container = _getParentNode(canvas); - return !!(container && container.isConnected); - } -} - -function _detectPlatform(canvas) { - if (!_isDomSupported() || (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas)) { - return BasicPlatform; - } - return DomPlatform; -} - -class Element { - constructor() { - this.x = undefined; - this.y = undefined; - this.active = false; - this.options = undefined; - this.$animations = undefined; - } - 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; - } -} -Element.defaults = {}; -Element.defaultRoutes = undefined; - -const formatters = { - values(value) { - return isArray(value) ? value : '' + value; - }, - numeric(tickValue, index, 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 > 1e+15) { - notation = 'scientific'; - } - delta = calculateDelta(tickValue, ticks); - } - const logDelta = log10(Math.abs(delta)); - const numDecimal = 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); - }, - logarithmic(tickValue, index, ticks) { - if (tickValue === 0) { - return '0'; - } - const remain = tickValue / (Math.pow(10, Math.floor(log10(tickValue)))); - if (remain === 1 || remain === 2 || remain === 5) { - return formatters.numeric.call(this, tickValue, index, ticks); - } - return ''; - } -}; -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}; - -defaults.set('scale', { - display: true, - offset: false, - reverse: false, - beginAtZero: false, - bounds: 'ticks', - grace: 0, - grid: { - display: true, - lineWidth: 1, - drawBorder: true, - drawOnChartArea: true, - drawTicks: true, - tickLength: 8, - tickWidth: (_ctx, options) => options.lineWidth, - tickColor: (_ctx, options) => options.color, - offset: false, - borderDash: [], - borderDashOffset: 0.0, - borderWidth: 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, - } -}); -defaults.route('scale.ticks', 'color', '', 'color'); -defaults.route('scale.grid', 'color', '', 'borderColor'); -defaults.route('scale.grid', 'borderColor', '', 'borderColor'); -defaults.route('scale.title', 'color', '', 'color'); -defaults.describe('scale', { - _fallback: false, - _scriptable: (name) => !name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser', - _indexable: (name) => name !== 'borderDash' && name !== 'tickBorderDash', -}); -defaults.describe('scales', { - _fallback: 'scale', -}); -defaults.describe('scale.ticks', { - _scriptable: (name) => name !== 'backdropPadding' && name !== 'callback', - _indexable: (name) => name !== 'backdropPadding', -}); - -function autoSkip(scale, ticks) { - const tickOpts = scale.options.ticks; - const ticksLimit = tickOpts.maxTicksLimit || determineMaxTicks(scale); - 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; -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, index, offsetGridLines) { - const length = scale.ticks.length; - const validIndex = Math.min(index, length - 1); - const start = scale._startPixel; - const end = scale._endPixel; - const epsilon = 1e-6; - let lineValue = scale.getPixelForTick(validIndex); - let offset; - if (offsetGridLines) { - if (length === 1) { - offset = Math.max(lineValue - start, end - lineValue); - } else if (index === 0) { - offset = (scale.getPixelForTick(1) - lineValue) / 2; - } else { - offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; - } - lineValue += validIndex < index ? 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, index, tick) { - return createContext(parent, { - tick, - index, - 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 = undefined; - this.ctx = cfg.ctx; - this.chart = cfg.chart; - this.top = undefined; - this.bottom = undefined; - this.left = undefined; - this.right = undefined; - this.width = undefined; - this.height = undefined; - this._margins = { - left: 0, - right: 0, - top: 0, - bottom: 0 - }; - this.maxWidth = undefined; - this.maxHeight = undefined; - this.paddingTop = undefined; - this.paddingBottom = undefined; - this.paddingLeft = undefined; - this.paddingRight = undefined; - this.axis = undefined; - this.labelRotation = undefined; - this.min = undefined; - this.max = undefined; - this._range = undefined; - this.ticks = []; - this._gridLineItems = null; - this._labelItems = null; - this._labelSizes = null; - this._length = 0; - this._maxLength = 0; - this._longestTextCache = {}; - this._startPixel = undefined; - this._endPixel = undefined; - this._reversePixels = false; - this._userMax = undefined; - this._userMin = undefined; - this._suggestedMax = undefined; - this._suggestedMin = undefined; - this._ticksLength = 0; - this._borderValue = 0; - this._cache = {}; - this._dataLimitsCached = false; - this.$context = undefined; - } - init(options) { - this.options = options.setContext(this.getContext()); - this.axis = options.axis; - this._userMin = this.parse(options.min); - this._userMax = this.parse(options.max); - this._suggestedMin = this.parse(options.suggestedMin); - this._suggestedMax = this.parse(options.suggestedMax); - } - parse(raw, index) { - return raw; - } - getUserBounds() { - let {_userMin, _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 || []; - } - 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; - } - 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 = this.ticks.length; - 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]); - } - 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 { - 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); - } - return labelSizes; - } - _computeLabelSizes(ticks, length) { - const {ctx, _longestTextCache: caches} = this; - const widths = []; - const heights = []; - let widestLabelSize = 0; - let highestLabelSize = 0; - let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel; - for (i = 0; i < length; ++i) { - 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, index) { - return NaN; - } - getValueForPixel(pixel) {} - getPixelForTick(index) { - const ticks = this.ticks; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index].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(index) { - const ticks = this.ticks || []; - if (index >= 0 && index < ticks.length) { - const tick = ticks[index]; - return tick.$context || - (tick.$context = createTickContext(this.getContext(), index, 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} = 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 = grid.setContext(this.getContext()); - const axisWidth = borderOpts.drawBorder ? borderOpts.borderWidth : 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 optsAtIndex = grid.setContext(this.getContext(i)); - const lineWidth = optsAtIndex.lineWidth; - const lineColor = optsAtIndex.color; - const borderDash = grid.borderDash || []; - const borderDashOffset = optsAtIndex.borderDashOffset; - const tickWidth = optsAtIndex.tickWidth; - const tickColor = optsAtIndex.tickColor; - const tickBorderDash = optsAtIndex.tickBorderDash || []; - const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset; - lineValue = getPixelForGridLine(this, i, offset); - if (lineValue === undefined) { - 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 color = optsAtIndex.color; - const strokeColor = optsAtIndex.textStrokeColor; - const strokeWidth = optsAtIndex.textStrokeWidth; - if (isHorizontal) { - x = pixel; - 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; - } - } 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 = y + textOffset - labelPadding.top; - let left = x - 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; - } - backdrop = { - left, - top, - width: width + labelPadding.width, - height: height + labelPadding.height, - color: optsAtIndex.backdropColor, - }; - } - items.push({ - rotation, - label, - font, - color, - strokeColor, - strokeWidth, - textOffset, - textAlign, - 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'; - } - 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 index = ticks.findIndex(t => t.value === value); - if (index >= 0) { - const opts = grid.setContext(this.getContext(index)); - 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: {grid}} = this; - const borderOpts = grid.setContext(this.getContext()); - const axisWidth = grid.drawBorder ? borderOpts.borderWidth : 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.borderWidth; - ctx.strokeStyle = borderOpts.borderColor; - 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._labelItems || (this._labelItems = this._computeLabelItems(chartArea)); - let i, ilen; - for (i = 0, ilen = items.length; i < ilen; ++i) { - const item = items[i]; - const tickFont = item.font; - const label = item.label; - if (item.backdrop) { - ctx.fillStyle = item.backdrop.color; - ctx.fillRect(item.backdrop.left, item.backdrop.top, item.backdrop.width, item.backdrop.height); - } - let y = item.textOffset; - renderText(ctx, label, 0, y, tickFont, item); - } - 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); - 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: gz + 1, - 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(index) { - const opts = this.options.ticks.setContext(this.getContext(index)); - 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 = 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(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, registry, component) { - const camelMethod = _capitalize(method); - callback(component['before' + camelMethod], [], component); - registry[method](component); - callback(component['after' + camelMethod], [], component); - } - _getRegistryForType(type) { - for (let i = 0; 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 === undefined) { - throw new Error('"' + id + '" is not a registered ' + type + '.'); - } - return item; - } -} -var registry = new Registry(); - -class PluginService { - constructor() { - this._init = []; - } - notify(chart, hook, args, filter) { - if (hook === 'beforeInit') { - this._init = this._createDescriptors(chart, true); - this._notify(this._init, chart, 'install'); - } - const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart); - const result = this._notify(descriptors, chart, hook, args); - if (hook === 'afterDestroy') { - this._notify(descriptors, chart, 'stop'); - this._notify(this._init, chart, 'uninstall'); - } - return result; - } - _notify(descriptors, chart, hook, args) { - args = args || {}; - for (const descriptor of descriptors) { - 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 = undefined; - } - } - _descriptors(chart) { - if (this._cache) { - return this._cache; - } - const descriptors = this._cache = this._createDescriptors(chart); - this._notifyStateChanges(chart); - return descriptors; - } - _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 descriptors = this._cache; - const diff = (a, b) => a.filter(x => !b.some(y => x.plugin.id === y.plugin.id)); - this._notify(diff(previousDescriptors, descriptors), chart, 'stop'); - this._notify(diff(descriptors, previousDescriptors), chart, 'start'); - } -} -function allPlugins(config) { - 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); - } - } - return plugins; -} -function getOpts(options, all) { - if (!all && options === false) { - return null; - } - if (options === true) { - return {}; - } - return options; -} -function createDescriptors(chart, plugins, options, all) { - const result = []; - const context = chart.getContext(); - for (let i = 0; i < plugins.length; i++) { - const plugin = plugins[i]; - const id = plugin.id; - const opts = getOpts(options[id], all); - if (opts === null) { - continue; - } - result.push({ - plugin, - options: pluginOpts(chart.config, plugin, opts, context) - }); - } - return result; -} -function pluginOpts(config, plugin, opts, context) { - const keys = config.pluginScopeKeys(plugin); - const scopes = config.getOptionScopes(opts, keys); - 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 axisFromPosition(position) { - if (position === 'top' || position === 'bottom') { - return 'x'; - } - if (position === 'left' || position === 'right') { - return 'y'; - } -} -function determineAxis(id, scaleOptions) { - if (id === 'x' || id === 'y') { - return id; - } - return scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.charAt(0).toLowerCase(); -} -function mergeScaleConfig(config, options) { - const chartDefaults = overrides[config.type] || {scales: {}}; - const configScales = options.scales || {}; - const chartIndexAxis = getIndexAxis(config.type, options); - const firstIDs = Object.create(null); - const scales = 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); - const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis); - const defaultScaleOptions = chartDefaults.scales || {}; - firstIDs[axis] = firstIDs[axis] || id; - scales[id] = mergeIf(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'] || firstIDs[axis] || axis; - scales[id] = scales[id] || 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 = new Map(); -const keysCached = 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 = (set, obj, key) => { - const opts = resolveObjectKey(obj, key); - if (opts !== undefined) { - set.add(opts); - } -}; -class Config { - constructor(config) { - this._config = initConfig(config); - this._scopeCache = new Map(); - this._resolverCache = new Map(); - } - get platform() { - return this._config.platform; - } - get type() { - return this._config.type; - } - set type(type) { - this._config.type = type; - } - get data() { - return this._config.data; - } - set data(data) { - this._config.data = initData(data); - } - get options() { - return this._config.options; - } - set options(options) { - this._config.options = options; - } - get plugins() { - return this._config.plugins; - } - update() { - const config = this._config; - this.clearCache(); - initOptions(config); - } - clearCache() { - this._scopeCache.clear(); - this._resolverCache.clear(); - } - datasetScopeKeys(datasetType) { - return cachedKeys(datasetType, - () => [[ - `datasets.${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 = 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 = 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(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, names, context, prefixes = ['']) { - const result = {$shared: true}; - const {resolver, subPrefixes} = getResolver(this._resolverCache, scopes, prefixes); - let options = resolver; - if (needContext(resolver, names)) { - result.$shared = false; - context = isFunction(context) ? context() : context; - const subResolver = this.createResolver(scopes, context, subPrefixes); - options = _attachContext(resolver, context, subResolver); - } - for (const prop of names) { - result[prop] = options[prop]; - } - return result; - } - createResolver(scopes, context, prefixes = [''], descriptorDefaults) { - const {resolver} = getResolver(this._resolverCache, scopes, prefixes); - return isObject(context) - ? _attachContext(resolver, context, undefined, descriptorDefaults) - : resolver; - } -} -function getResolver(resolverCache, scopes, prefixes) { - let cache = resolverCache.get(scopes); - if (!cache) { - cache = 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).reduce((acc, key) => acc || isFunction(value[key]), false); -function needContext(proxy, names) { - const {isScriptable, isIndexable} = _descriptors(proxy); - for (const prop of names) { - 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 = "3.7.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 { - 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 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 = undefined; - this.boxes = []; - this.currentDevicePixelRatio = undefined; - this.chartArea = undefined; - this._active = []; - this._lastEvent = undefined; - this._listeners = {}; - this._responsiveListeners = undefined; - this._sortedMetasets = []; - this.scales = {}; - this._plugins = new PluginService(); - this.$proxies = {}; - this._hiddenIndices = {}; - this.attached = false; - this._animationsDisabled = undefined; - this.$context = undefined; - 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; - } - _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 === undefined || 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, index) => { - if (datasets.filter(x => x === meta._dataset).length === 0) { - this._destroyDatasetMeta(index); - } - }); - } - 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.prototype, { - 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, index) => { - item._idx = index; - }); - 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(index, mode) { - const meta = this.getDatasetMeta(index); - const args = {meta, index, 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._resize(width, height); - this._resizeBeforeDraw = null; - } - 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 clip = meta._clip; - const useClip = !clip.disabled; - const area = this.chartArea; - const args = { - meta, - index: meta.index, - cancelable: true - }; - if (this.notifyPlugins('beforeDatasetDraw', args) === false) { - return; - } - if (useClip) { - clipArea(ctx, { - left: clip.left === false ? 0 : area.left - clip.left, - right: clip.right === false ? this.width : area.right + clip.right, - top: clip.top === false ? 0 : area.top - clip.top, - bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom - }); - } - meta.controller.draw(); - if (useClip) { - unclipArea(ctx); - } - args.cancelable = false; - this.notifyPlugins('afterDatasetDraw', args); - } - 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(index) { - this._hiddenIndices[index] = !this._hiddenIndices[index]; - } - getDataVisibility(index) { - return !this._hiddenIndices[index]; - } - _updateVisibility(datasetIndex, dataIndex, visible) { - const mode = visible ? 'show' : 'hide'; - const meta = this.getDatasetMeta(datasetIndex); - const anims = meta.controller._resolveAnimations(undefined, 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 : undefined); - } - } - 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; - } - this.notifyPlugins('destroy'); - 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, listener) => { - platform.addEventListener(this, type, listener); - listeners[type] = listener; - }; - 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, listener) => { - platform.addEventListener(this, type, listener); - listeners[type] = listener; - }; - const _remove = (type, listener) => { - if (listeners[type]) { - platform.removeEventListener(this, type, listener); - 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 = undefined; - } - 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}) => { - const meta = this.getDatasetMeta(datasetIndex); - if (!meta) { - throw new Error('No dataset found at index ' + datasetIndex); - } - return { - datasetIndex, - element: meta.data[index], - index, - }; - }); - 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); - } - _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: _isPointInArea(e, this.chartArea, this._minPadding) - }; - 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); - } -} -const invalidatePlugins = () => each(Chart.instances, (chart) => chart._plugins.invalidate()); -const enumerable = true; -Object.defineProperties(Chart, { - defaults: { - enumerable, - value: defaults - }, - instances: { - enumerable, - value: instances - }, - overrides: { - enumerable, - value: overrides - }, - registry: { - enumerable, - value: registry - }, - version: { - enumerable, - value: version - }, - getChart: { - enumerable, - value: getChart - }, - register: { - enumerable, - value: (...items) => { - registry.add(...items); - invalidatePlugins(); - } - }, - unregister: { - enumerable, - value: (...items) => { - registry.remove(...items); - invalidatePlugins(); - } - } -}); - -function clipArc(ctx, element, endAngle) { - const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element; - let angleMargin = pixelMargin / outerRadius; - ctx.beginPath(); - ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin); - if (innerRadius > pixelMargin) { - angleMargin = pixelMargin / innerRadius; - ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true); - } else { - ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI); - } - ctx.closePath(); - ctx.clip(); -} -function toRadiusCorners(value) { - return _readValueToProps(value, ['outerStart', 'outerEnd', 'innerStart', 'innerEnd']); -} -function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) { - const o = toRadiusCorners(arc.options.borderRadius); - const halfThickness = (outerRadius - innerRadius) / 2; - const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2); - const computeOuterLimit = (val) => { - const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2; - return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit)); - }; - return { - outerStart: computeOuterLimit(o.outerStart), - outerEnd: computeOuterLimit(o.outerEnd), - innerStart: _limitValue(o.innerStart, 0, innerLimit), - innerEnd: _limitValue(o.innerEnd, 0, innerLimit), - }; -} -function rThetaToXY(r, theta, x, y) { - return { - x: x + r * Math.cos(theta), - y: y + r * Math.sin(theta), - }; -} -function pathArc(ctx, element, offset, spacing, end) { - const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element; - const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0); - const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0; - let spacingOffset = 0; - const alpha = end - start; - if (spacing) { - const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0; - const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0; - const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2; - const adjustedAngle = avNogSpacingRadius !== 0 ? (alpha * avNogSpacingRadius) / (avNogSpacingRadius + spacing) : alpha; - spacingOffset = (alpha - adjustedAngle) / 2; - } - const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius; - const angleOffset = (alpha - beta) / 2; - const startAngle = start + angleOffset + spacingOffset; - const endAngle = end - angleOffset - spacingOffset; - const {outerStart, outerEnd, innerStart, innerEnd} = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle); - const outerStartAdjustedRadius = outerRadius - outerStart; - const outerEndAdjustedRadius = outerRadius - outerEnd; - const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius; - const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius; - const innerStartAdjustedRadius = innerRadius + innerStart; - const innerEndAdjustedRadius = innerRadius + innerEnd; - const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius; - const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius; - ctx.beginPath(); - ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle); - if (outerEnd > 0) { - const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI); - } - const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y); - ctx.lineTo(p4.x, p4.y); - if (innerEnd > 0) { - const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI); - } - ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true); - if (innerStart > 0) { - const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI); - } - const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y); - ctx.lineTo(p8.x, p8.y); - if (outerStart > 0) { - const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle); - } - ctx.closePath(); -} -function drawArc(ctx, element, offset, spacing) { - const {fullCircles, startAngle, circumference} = element; - let endAngle = element.endAngle; - if (fullCircles) { - pathArc(ctx, element, offset, spacing, startAngle + TAU); - for (let i = 0; i < fullCircles; ++i) { - ctx.fill(); - } - if (!isNaN(circumference)) { - endAngle = startAngle + circumference % TAU; - if (circumference % TAU === 0) { - endAngle += TAU; - } - } - } - pathArc(ctx, element, offset, spacing, endAngle); - ctx.fill(); - return endAngle; -} -function drawFullCircleBorders(ctx, element, inner) { - const {x, y, startAngle, pixelMargin, fullCircles} = element; - const outerRadius = Math.max(element.outerRadius - pixelMargin, 0); - const innerRadius = element.innerRadius + pixelMargin; - let i; - if (inner) { - clipArc(ctx, element, startAngle + TAU); - } - ctx.beginPath(); - ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true); - for (i = 0; i < fullCircles; ++i) { - ctx.stroke(); - } - ctx.beginPath(); - ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU); - for (i = 0; i < fullCircles; ++i) { - ctx.stroke(); - } -} -function drawBorder(ctx, element, offset, spacing, endAngle) { - const {options} = element; - const {borderWidth, borderJoinStyle} = options; - const inner = options.borderAlign === 'inner'; - if (!borderWidth) { - return; - } - if (inner) { - ctx.lineWidth = borderWidth * 2; - ctx.lineJoin = borderJoinStyle || 'round'; - } else { - ctx.lineWidth = borderWidth; - ctx.lineJoin = borderJoinStyle || 'bevel'; - } - if (element.fullCircles) { - drawFullCircleBorders(ctx, element, inner); - } - if (inner) { - clipArc(ctx, element, endAngle); - } - pathArc(ctx, element, offset, spacing, endAngle); - ctx.stroke(); -} -class ArcElement extends Element { - constructor(cfg) { - super(); - this.options = undefined; - this.circumference = undefined; - this.startAngle = undefined; - this.endAngle = undefined; - this.innerRadius = undefined; - this.outerRadius = undefined; - this.pixelMargin = 0; - this.fullCircles = 0; - if (cfg) { - Object.assign(this, cfg); - } - } - inRange(chartX, chartY, useFinalPosition) { - const point = this.getProps(['x', 'y'], useFinalPosition); - const {angle, distance} = getAngleFromPoint(point, {x: chartX, y: chartY}); - const {startAngle, endAngle, innerRadius, outerRadius, circumference} = this.getProps([ - 'startAngle', - 'endAngle', - 'innerRadius', - 'outerRadius', - 'circumference' - ], useFinalPosition); - const rAdjust = this.options.spacing / 2; - const _circumference = valueOrDefault(circumference, endAngle - startAngle); - const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle); - const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust); - return (betweenAngles && withinRadius); - } - getCenterPoint(useFinalPosition) { - const {x, y, startAngle, endAngle, innerRadius, outerRadius} = this.getProps([ - 'x', - 'y', - 'startAngle', - 'endAngle', - 'innerRadius', - 'outerRadius', - 'circumference', - ], useFinalPosition); - const {offset, spacing} = this.options; - const halfAngle = (startAngle + endAngle) / 2; - const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2; - return { - x: x + Math.cos(halfAngle) * halfRadius, - y: y + Math.sin(halfAngle) * halfRadius - }; - } - tooltipPosition(useFinalPosition) { - return this.getCenterPoint(useFinalPosition); - } - draw(ctx) { - const {options, circumference} = this; - const offset = (options.offset || 0) / 2; - const spacing = (options.spacing || 0) / 2; - this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0; - this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0; - if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) { - return; - } - ctx.save(); - let radiusOffset = 0; - if (offset) { - radiusOffset = offset / 2; - const halfAngle = (this.startAngle + this.endAngle) / 2; - ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset); - if (this.circumference >= PI) { - radiusOffset = offset; - } - } - ctx.fillStyle = options.backgroundColor; - ctx.strokeStyle = options.borderColor; - const endAngle = drawArc(ctx, this, radiusOffset, spacing); - drawBorder(ctx, this, radiusOffset, spacing, endAngle); - ctx.restore(); - } -} -ArcElement.id = 'arc'; -ArcElement.defaults = { - borderAlign: 'center', - borderColor: '#fff', - borderJoinStyle: undefined, - borderRadius: 0, - borderWidth: 2, - offset: 0, - spacing: 0, - angle: undefined, -}; -ArcElement.defaultRoutes = { - backgroundColor: 'backgroundColor' -}; - -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 = (index) => (start + (reverse ? ilen - index : index)) % 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 = undefined; - this._chart = undefined; - this._loop = undefined; - this._fullLoop = undefined; - this._path = undefined; - this._points = undefined; - this._segments = undefined; - this._decimated = false; - this._pointsUpdated = false; - this._datasetIndex = undefined; - 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 = undefined; - } - } -} -LineElement.id = 'line'; -LineElement.defaults = { - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0, - borderJoinStyle: 'miter', - borderWidth: 3, - capBezierPoints: true, - cubicInterpolationMode: 'default', - fill: false, - spanGaps: false, - stepped: false, - tension: 0, -}; -LineElement.defaultRoutes = { - backgroundColor: 'backgroundColor', - borderColor: 'borderColor' -}; -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(); - this.options = undefined; - this.parsed = undefined; - this.skip = undefined; - this.stop = undefined; - 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; - } -} -PointElement.id = 'point'; -PointElement.defaults = { - borderWidth: 1, - hitRadius: 1, - hoverBorderWidth: 1, - hoverRadius: 4, - pointStyle: 'circle', - radius: 3, - rotation: 0 -}; -PointElement.defaultRoutes = { - backgroundColor: 'backgroundColor', - borderColor: 'borderColor' -}; - -function getBarBounds(bar, useFinalPosition) { - const {x, y, base, width, height} = bar.getProps(['x', 'y', 'base', 'width', 'height'], useFinalPosition); - let left, right, top, bottom, half; - if (bar.horizontal) { - half = height / 2; - left = Math.min(x, base); - right = Math.max(x, base); - top = y - half; - bottom = y + half; - } else { - half = width / 2; - left = x - half; - right = x + half; - top = Math.min(y, base); - bottom = Math.max(y, base); - } - return {left, top, right, bottom}; -} -function skipOrLimit(skip, value, min, max) { - return skip ? 0 : _limitValue(value, min, max); -} -function parseBorderWidth(bar, maxW, maxH) { - const value = bar.options.borderWidth; - const skip = bar.borderSkipped; - const o = toTRBL(value); - return { - t: skipOrLimit(skip.top, o.top, 0, maxH), - r: skipOrLimit(skip.right, o.right, 0, maxW), - b: skipOrLimit(skip.bottom, o.bottom, 0, maxH), - l: skipOrLimit(skip.left, o.left, 0, maxW) - }; -} -function parseBorderRadius(bar, maxW, maxH) { - const {enableBorderRadius} = bar.getProps(['enableBorderRadius']); - const value = bar.options.borderRadius; - const o = toTRBLCorners(value); - const maxR = Math.min(maxW, maxH); - const skip = bar.borderSkipped; - const enableBorder = enableBorderRadius || isObject(value); - return { - topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR), - topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR), - bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR), - bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR) - }; -} -function boundingRects(bar) { - const bounds = getBarBounds(bar); - const width = bounds.right - bounds.left; - const height = bounds.bottom - bounds.top; - const border = parseBorderWidth(bar, width / 2, height / 2); - const radius = parseBorderRadius(bar, width / 2, height / 2); - return { - outer: { - x: bounds.left, - y: bounds.top, - w: width, - h: height, - radius - }, - inner: { - x: bounds.left + border.l, - y: bounds.top + border.t, - w: width - border.l - border.r, - h: height - border.t - border.b, - radius: { - topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)), - topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)), - bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)), - bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)), - } - } - }; -} -function inRange(bar, x, y, useFinalPosition) { - const skipX = x === null; - const skipY = y === null; - const skipBoth = skipX && skipY; - const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition); - return bounds - && (skipX || _isBetween(x, bounds.left, bounds.right)) - && (skipY || _isBetween(y, bounds.top, bounds.bottom)); -} -function hasRadius(radius) { - return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight; -} -function addNormalRectPath(ctx, rect) { - ctx.rect(rect.x, rect.y, rect.w, rect.h); -} -function inflateRect(rect, amount, refRect = {}) { - const x = rect.x !== refRect.x ? -amount : 0; - const y = rect.y !== refRect.y ? -amount : 0; - const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x; - const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y; - return { - x: rect.x + x, - y: rect.y + y, - w: rect.w + w, - h: rect.h + h, - radius: rect.radius - }; -} -class BarElement extends Element { - constructor(cfg) { - super(); - this.options = undefined; - this.horizontal = undefined; - this.base = undefined; - this.width = undefined; - this.height = undefined; - this.inflateAmount = undefined; - if (cfg) { - Object.assign(this, cfg); - } - } - draw(ctx) { - const {inflateAmount, options: {borderColor, backgroundColor}} = this; - const {inner, outer} = boundingRects(this); - const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath; - ctx.save(); - if (outer.w !== inner.w || outer.h !== inner.h) { - ctx.beginPath(); - addRectPath(ctx, inflateRect(outer, inflateAmount, inner)); - ctx.clip(); - addRectPath(ctx, inflateRect(inner, -inflateAmount, outer)); - ctx.fillStyle = borderColor; - ctx.fill('evenodd'); - } - ctx.beginPath(); - addRectPath(ctx, inflateRect(inner, inflateAmount)); - ctx.fillStyle = backgroundColor; - ctx.fill(); - ctx.restore(); - } - inRange(mouseX, mouseY, useFinalPosition) { - return inRange(this, mouseX, mouseY, useFinalPosition); - } - inXRange(mouseX, useFinalPosition) { - return inRange(this, mouseX, null, useFinalPosition); - } - inYRange(mouseY, useFinalPosition) { - return inRange(this, null, mouseY, useFinalPosition); - } - getCenterPoint(useFinalPosition) { - const {x, y, base, horizontal} = this.getProps(['x', 'y', 'base', 'horizontal'], useFinalPosition); - return { - x: horizontal ? (x + base) / 2 : x, - y: horizontal ? y : (y + base) / 2 - }; - } - getRange(axis) { - return axis === 'x' ? this.width / 2 : this.height / 2; - } -} -BarElement.id = 'bar'; -BarElement.defaults = { - borderSkipped: 'start', - borderWidth: 0, - borderRadius: 0, - inflateAmount: 'auto', - pointStyle: undefined -}; -BarElement.defaultRoutes = { - backgroundColor: 'backgroundColor', - borderColor: 'borderColor' -}; - -var elements = /*#__PURE__*/Object.freeze({ -__proto__: null, -ArcElement: ArcElement, -LineElement: LineElement, -PointElement: PointElement, -BarElement: BarElement -}); - -function lttbDecimation(data, start, count, availableWidth, options) { - const samples = options.samples || availableWidth; - if (samples >= count) { - return data.slice(start, start + count); - } - const decimated = []; - const bucketWidth = (count - 2) / (samples - 2); - let sampledIndex = 0; - const endIndex = start + count - 1; - let a = start; - let i, maxAreaPoint, maxArea, area, nextA; - decimated[sampledIndex++] = data[a]; - for (i = 0; i < samples - 2; i++) { - let avgX = 0; - let avgY = 0; - let j; - const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start; - const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start; - const avgRangeLength = avgRangeEnd - avgRangeStart; - for (j = avgRangeStart; j < avgRangeEnd; j++) { - avgX += data[j].x; - avgY += data[j].y; - } - avgX /= avgRangeLength; - avgY /= avgRangeLength; - const rangeOffs = Math.floor(i * bucketWidth) + 1 + start; - const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start; - const {x: pointAx, y: pointAy} = data[a]; - maxArea = area = -1; - for (j = rangeOffs; j < rangeTo; j++) { - area = 0.5 * Math.abs( - (pointAx - avgX) * (data[j].y - pointAy) - - (pointAx - data[j].x) * (avgY - pointAy) - ); - if (area > maxArea) { - maxArea = area; - maxAreaPoint = data[j]; - nextA = j; - } - } - decimated[sampledIndex++] = maxAreaPoint; - a = nextA; - } - decimated[sampledIndex++] = data[endIndex]; - return decimated; -} -function minMaxDecimation(data, start, count, availableWidth) { - let avgX = 0; - let countX = 0; - let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY; - const decimated = []; - const endIndex = start + count - 1; - const xMin = data[start].x; - const xMax = data[endIndex].x; - const dx = xMax - xMin; - for (i = start; i < start + count; ++i) { - point = data[i]; - x = (point.x - xMin) / dx * availableWidth; - y = point.y; - const truncX = x | 0; - if (truncX === prevX) { - if (y < minY) { - minY = y; - minIndex = i; - } else if (y > maxY) { - maxY = y; - maxIndex = i; - } - avgX = (countX * avgX + point.x) / ++countX; - } else { - const lastIndex = i - 1; - if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) { - const intermediateIndex1 = Math.min(minIndex, maxIndex); - const intermediateIndex2 = Math.max(minIndex, maxIndex); - if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) { - decimated.push({ - ...data[intermediateIndex1], - x: avgX, - }); - } - if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) { - decimated.push({ - ...data[intermediateIndex2], - x: avgX - }); - } - } - if (i > 0 && lastIndex !== startIndex) { - decimated.push(data[lastIndex]); - } - decimated.push(point); - prevX = truncX; - countX = 0; - minY = maxY = y; - minIndex = maxIndex = startIndex = i; - } - } - return decimated; -} -function cleanDecimatedDataset(dataset) { - if (dataset._decimated) { - const data = dataset._data; - delete dataset._decimated; - delete dataset._data; - Object.defineProperty(dataset, 'data', {value: data}); - } -} -function cleanDecimatedData(chart) { - chart.data.datasets.forEach((dataset) => { - cleanDecimatedDataset(dataset); - }); -} -function getStartAndCountOfVisiblePointsSimplified(meta, points) { - const pointCount = points.length; - let start = 0; - let count; - const {iScale} = meta; - const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); - if (minDefined) { - start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1); - } - if (maxDefined) { - count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start; - } else { - count = pointCount - start; - } - return {start, count}; -} -var plugin_decimation = { - id: 'decimation', - defaults: { - algorithm: 'min-max', - enabled: false, - }, - beforeElementsUpdate: (chart, args, options) => { - if (!options.enabled) { - cleanDecimatedData(chart); - return; - } - const availableWidth = chart.width; - chart.data.datasets.forEach((dataset, datasetIndex) => { - const {_data, indexAxis} = dataset; - const meta = chart.getDatasetMeta(datasetIndex); - const data = _data || dataset.data; - if (resolve([indexAxis, chart.options.indexAxis]) === 'y') { - return; - } - if (meta.type !== 'line') { - return; - } - const xAxis = chart.scales[meta.xAxisID]; - if (xAxis.type !== 'linear' && xAxis.type !== 'time') { - return; - } - if (chart.options.parsing) { - return; - } - let {start, count} = getStartAndCountOfVisiblePointsSimplified(meta, data); - const threshold = options.threshold || 4 * availableWidth; - if (count <= threshold) { - cleanDecimatedDataset(dataset); - return; - } - if (isNullOrUndef(_data)) { - dataset._data = data; - delete dataset.data; - Object.defineProperty(dataset, 'data', { - configurable: true, - enumerable: true, - get: function() { - return this._decimated; - }, - set: function(d) { - this._data = d; - } - }); - } - let decimated; - switch (options.algorithm) { - case 'lttb': - decimated = lttbDecimation(data, start, count, availableWidth, options); - break; - case 'min-max': - decimated = minMaxDecimation(data, start, count, availableWidth); - break; - default: - throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`); - } - dataset._decimated = decimated; - }); - }, - destroy(chart) { - cleanDecimatedData(chart); - } -}; - -function getLineByIndex(chart, index) { - const meta = chart.getDatasetMeta(index); - const visible = meta && chart.isDatasetVisible(index); - return visible ? meta.dataset : null; -} -function parseFillOption(line) { - const options = line.options; - const fillOption = options.fill; - let fill = valueOrDefault(fillOption && fillOption.target, fillOption); - if (fill === undefined) { - fill = !!options.backgroundColor; - } - if (fill === false || fill === null) { - return false; - } - if (fill === true) { - return 'origin'; - } - return fill; -} -function decodeFill(line, index, count) { - const fill = parseFillOption(line); - if (isObject(fill)) { - return isNaN(fill.value) ? false : fill; - } - let target = parseFloat(fill); - if (isNumberFinite(target) && Math.floor(target) === target) { - if (fill[0] === '-' || fill[0] === '+') { - target = index + target; - } - if (target === index || target < 0 || target >= count) { - return false; - } - return target; - } - return ['origin', 'start', 'end', 'stack', 'shape'].indexOf(fill) >= 0 && fill; -} -function computeLinearBoundary(source) { - const {scale = {}, fill} = source; - let target = null; - let horizontal; - if (fill === 'start') { - target = scale.bottom; - } else if (fill === 'end') { - target = scale.top; - } else if (isObject(fill)) { - target = scale.getPixelForValue(fill.value); - } else if (scale.getBasePixel) { - target = scale.getBasePixel(); - } - if (isNumberFinite(target)) { - horizontal = scale.isHorizontal(); - return { - x: horizontal ? target : null, - y: horizontal ? null : target - }; - } - return null; -} -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 computeCircularBoundary(source) { - const {scale, fill} = source; - const options = scale.options; - const length = scale.getLabels().length; - const target = []; - const start = options.reverse ? scale.max : scale.min; - const end = options.reverse ? scale.min : scale.max; - let i, center, value; - if (fill === 'start') { - value = start; - } else if (fill === 'end') { - value = end; - } else if (isObject(fill)) { - value = fill.value; - } else { - value = scale.getBaseValue(); - } - if (options.grid.circular) { - center = scale.getPointPositionForValue(0, start); - return new simpleArc({ - x: center.x, - y: center.y, - radius: scale.getDistanceFromCenterForValue(value) - }); - } - for (i = 0; i < length; ++i) { - target.push(scale.getPointPositionForValue(i, value)); - } - return target; -} -function computeBoundary(source) { - const scale = source.scale || {}; - if (scale.getPointPositionForValue) { - return computeCircularBoundary(source); - } - return computeLinearBoundary(source); -} -function findSegmentEnd(start, end, points) { - for (;end > start; end--) { - const point = points[end]; - if (!isNaN(point.x) && !isNaN(point.y)) { - break; - } - } - return 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 buildStackLine(source) { - const {scale, index, line} = source; - const points = []; - const segments = line.segments; - const sourcePoints = line.points; - const linesBelow = getLinesBelow(scale, index); - 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, index) { - const below = []; - const metas = scale.getMatchingVisibleMetas('line'); - for (let i = 0; i < metas.length; i++) { - const meta = metas[i]; - if (meta.index === index) { - 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}; -} -function getTarget(source) { - const {chart, fill, line} = source; - if (isNumberFinite(fill)) { - return getLineByIndex(chart, fill); - } - if (fill === 'stack') { - return buildStackLine(source); - } - if (fill === 'shape') { - return true; - } - const boundary = computeBoundary(source); - if (boundary instanceof simpleArc) { - return boundary; - } - return createBoundaryLine(boundary, line); -} -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 resolveTarget(sources, index, propagate) { - const source = sources[index]; - let fill = source.fill; - const visited = [index]; - let target; - if (!propagate) { - return fill; - } - while (fill !== false && visited.indexOf(fill) === -1) { - if (!isNumberFinite(fill)) { - return fill; - } - target = sources[fill]; - if (!target) { - return false; - } - if (target.visible) { - return fill; - } - visited.push(fill); - fill = target.fill; - } - return false; -} -function _clip(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 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 _getEdge(a, b, prop, fn) { - if (a && b) { - return fn(a[prop], b[prop]); - } - return a ? a[prop] : b ? b[prop] : 0; -} -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 clipBounds(ctx, scale, bounds) { - const {top, bottom} = scale.chart.chartArea; - const {property, start, end} = bounds || {}; - if (property === 'x') { - ctx.beginPath(); - ctx.rect(start, top, end - start, bottom - top); - ctx.clip(); - } -} -function interpolatedLineTo(ctx, target, point, property) { - const interpolatedPoint = target.interpolate(point, property); - if (interpolatedPoint) { - ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y); - } -} -function _fill(ctx, cfg) { - const {line, target, property, color, scale} = cfg; - const segments = _segments(line, target, property); - for (const {source: src, target: tgt, start, end} of segments) { - const {style: {backgroundColor = color} = {}} = src; - const notShape = target !== true; - ctx.save(); - ctx.fillStyle = backgroundColor; - clipBounds(ctx, scale, 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 doFill(ctx, cfg) { - const {line, target, above, below, area, scale} = cfg; - const property = line._loop ? 'angle' : cfg.axis; - ctx.save(); - if (property === 'x' && below !== above) { - _clip(ctx, target, area.top); - _fill(ctx, {line, target, color: above, scale, property}); - ctx.restore(); - ctx.save(); - _clip(ctx, target, area.bottom); - } - _fill(ctx, {line, target, color: below, scale, property}); - ctx.restore(); -} -function drawfill(ctx, source, area) { - const target = getTarget(source); - const {line, scale, axis} = source; - const lineOpts = line.options; - const fillOption = lineOpts.fill; - const color = lineOpts.backgroundColor; - const {above = color, below = color} = fillOption || {}; - if (target && line.points.length) { - clipArea(ctx, area); - doFill(ctx, {line, target, above, below, area, scale, axis}); - unclipArea(ctx); - } -} -var plugin_filler = { - 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 draw = 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 (draw) { - 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 (source) { - drawfill(chart.ctx, source, chart.chartArea); - } - } - }, - beforeDatasetDraw(chart, args, options) { - const source = args.meta.$filler; - if (!source || source.fill === false || 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 = 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 = undefined; - this.columnSizes = undefined; - this.lineWidths = undefined; - this.maxHeight = undefined; - this.maxWidth = undefined; - this.top = undefined; - this.bottom = undefined; - this.left = undefined; - this.right = undefined; - this.height = undefined; - this.width = undefined; - this._margins = undefined; - this.position = undefined; - this.weight = undefined; - this.fullSize = undefined; - } - 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, fontSize, boxWidth, itemHeight) + 10; - } - this.width = Math.min(width, options.maxWidth || this.maxWidth); - this.height = Math.min(height, options.maxHeight || this.maxHeight); - } - _fitRows(titleHeight, fontSize, boxWidth, itemHeight) { - const {ctx, 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, fontSize, 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 = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - 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 {color: fontColor, 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: boxWidth * Math.SQRT2 / 2, - pointStyle: legendItem.pointStyle, - rotation: legendItem.rotation, - borderWidth: lineWidth - }; - const centerX = rtlHelper.xPlus(x, boxWidth / 2); - const centerY = y + halfFontSize; - drawPoint(ctx, drawOptions, centerX, centerY); - } 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 || fontColor; - ctx.fillStyle = legendItem.fontColor || 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 { - 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') { - 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 isListened(type, opts) { - if (type === 'mousemove' && (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: 1000, - onClick(e, legendItem, legend) { - const index = legendItem.datasetIndex; - const ci = legend.chart; - if (ci.isDatasetVisible(index)) { - ci.hide(index); - legendItem.hidden = true; - } else { - ci.show(index); - 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}} = chart.legend.options; - return chart._getSortedDatasetMetas().map((meta) => { - const style = meta.controller.getStyle(usePointStyle ? 0 : undefined); - const borderWidth = toPadding(style.borderWidth); - return { - text: datasets[meta.index].label, - fillStyle: style.backgroundColor, - fontColor: color, - hidden: !meta.visible, - lineCap: style.borderCapStyle, - lineDash: style.borderDash, - lineDashOffset: style.borderDashOffset, - lineJoin: style.borderJoinStyle, - lineWidth: (borderWidth.width + borderWidth.height) / 4, - strokeStyle: style.borderColor, - pointStyle: pointStyle || style.pointStyle, - rotation: style.rotation, - textAlign: textAlign || style.textAlign, - borderRadius: 0, - datasetIndex: meta.index - }; - }, this); - } - }, - title: { - color: (ctx) => ctx.chart.options.color, - display: false, - position: 'center', - text: '', - } - }, - descriptors: { - _scriptable: (name) => !name.startsWith('on'), - labels: { - _scriptable: (name) => !['generateLabels', 'filter', 'sort'].includes(name), - } - }, -}; - -class Title extends Element { - constructor(config) { - super(); - this.chart = config.chart; - this.options = config.options; - this.ctx = config.ctx; - this._padding = undefined; - this.top = undefined; - this.bottom = undefined; - this.left = undefined; - this.right = undefined; - this.width = undefined; - this.height = undefined; - this.position = undefined; - this.weight = undefined; - this.fullSize = undefined; - } - update(maxWidth, maxHeight) { - const opts = this.options; - this.left = 0; - this.top = 0; - if (!opts.display) { - this.width = this.height = this.right = this.bottom = 0; - return; - } - this.width = this.right = maxWidth; - this.height = this.bottom = maxHeight; - const lineCount = isArray(opts.text) ? opts.text.length : 1; - this._padding = toPadding(opts.padding); - const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height; - if (this.isHorizontal()) { - this.height = textSize; - } else { - this.width = textSize; - } - } - isHorizontal() { - const pos = this.options.position; - return pos === 'top' || pos === 'bottom'; - } - _drawArgs(offset) { - const {top, left, bottom, right, options} = this; - const align = options.align; - let rotation = 0; - let maxWidth, titleX, titleY; - if (this.isHorizontal()) { - titleX = _alignStartEnd(align, left, right); - titleY = top + offset; - maxWidth = right - left; - } else { - if (options.position === 'left') { - titleX = left + offset; - titleY = _alignStartEnd(align, bottom, top); - rotation = PI * -0.5; - } else { - titleX = right - offset; - titleY = _alignStartEnd(align, top, bottom); - rotation = PI * 0.5; - } - maxWidth = bottom - top; - } - return {titleX, titleY, maxWidth, rotation}; - } - draw() { - const ctx = this.ctx; - const opts = this.options; - if (!opts.display) { - return; - } - const fontOpts = toFont(opts.font); - const lineHeight = fontOpts.lineHeight; - const offset = lineHeight / 2 + this._padding.top; - const {titleX, titleY, maxWidth, rotation} = this._drawArgs(offset); - renderText(ctx, opts.text, 0, 0, fontOpts, { - color: opts.color, - maxWidth, - rotation, - textAlign: _toLeftRightCenter(opts.align), - textBaseline: 'middle', - translation: [titleX, titleY], - }); - } -} -function createTitle(chart, titleOpts) { - const title = new Title({ - ctx: chart.ctx, - options: titleOpts, - chart - }); - layouts.configure(chart, title, titleOpts); - layouts.addBox(chart, title); - chart.titleBlock = title; -} -var plugin_title = { - id: 'title', - _element: Title, - start(chart, _args, options) { - createTitle(chart, options); - }, - stop(chart) { - const titleBlock = chart.titleBlock; - layouts.removeBox(chart, titleBlock); - delete chart.titleBlock; - }, - beforeUpdate(chart, _args, options) { - const title = chart.titleBlock; - layouts.configure(chart, title, options); - title.options = options; - }, - defaults: { - align: 'center', - display: false, - font: { - weight: 'bold', - }, - fullSize: true, - padding: 10, - position: 'top', - text: '', - weight: 2000 - }, - defaultRoutes: { - color: 'color' - }, - descriptors: { - _scriptable: true, - _indexable: false, - }, -}; - -const map = new WeakMap(); -var plugin_subtitle = { - id: 'subtitle', - start(chart, _args, options) { - const title = new Title({ - ctx: chart.ctx, - options, - chart - }); - layouts.configure(chart, title, options); - layouts.addBox(chart, title); - map.set(chart, title); - }, - stop(chart) { - layouts.removeBox(chart, map.get(chart)); - map.delete(chart); - }, - beforeUpdate(chart, _args, options) { - const title = map.get(chart); - layouts.configure(chart, title, options); - title.options = options; - }, - defaults: { - align: 'center', - display: false, - font: { - weight: 'normal', - }, - fullSize: true, - padding: 0, - position: 'top', - text: '', - weight: 1500 - }, - defaultRoutes: { - color: 'color' - }, - descriptors: { - _scriptable: true, - _indexable: false, - }, -}; - -const positioners = { - average(items) { - if (!items.length) { - return false; - } - let i, len; - let x = 0; - 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(); - x += pos.x; - y += pos.y; - ++count; - } - } - return { - x: x / count, - 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} = item; - const controller = chart.getDatasetMeta(datasetIndex).controller; - const {label, value} = controller.getLabelAndValue(index); - return { - chart, - label, - parsed: controller.getParsed(index), - raw: chart.data.datasets[datasetIndex].data[index], - formattedValue: value, - dataset: controller.getDataset(), - dataIndex: index, - 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(callback) { - return pushOrConcat([], splitNewlines(callback)); -} -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; -} -class Tooltip extends Element { - constructor(config) { - super(); - this.opacity = 0; - this._active = []; - this._eventPosition = undefined; - this._size = undefined; - this._cachedAnimations = undefined; - this._tooltipItems = []; - this.$animations = undefined; - this.$context = undefined; - this.chart = config.chart || config._chart; - this._chart = this.chart; - this.options = config.options; - this.dataPoints = undefined; - this.title = undefined; - this.beforeBody = undefined; - this.body = undefined; - this.afterBody = undefined; - this.footer = undefined; - this.xAlign = undefined; - this.yAlign = undefined; - this.x = undefined; - this.y = undefined; - this.height = undefined; - this.width = undefined; - this.caretX = undefined; - this.caretY = undefined; - this.labelColors = undefined; - this.labelPointStyles = undefined; - this.labelTextColors = undefined; - } - initialize(options) { - this.options = options; - this._cachedAnimations = undefined; - this.$context = undefined; - } - _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 = callbacks.beforeTitle.apply(this, [context]); - const title = callbacks.title.apply(this, [context]); - const afterTitle = callbacks.afterTitle.apply(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(options.callbacks.beforeBody.apply(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(scoped.beforeLabel.call(this, context))); - pushOrConcat(bodyItem.lines, scoped.label.call(this, context)); - pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(this, context))); - bodyItems.push(bodyItem); - }); - return bodyItems; - } - getAfterBody(tooltipItems, options) { - return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this, [tooltipItems])); - } - getFooter(tooltipItems, options) { - const {callbacks} = options; - const beforeFooter = callbacks.beforeFooter.apply(this, [tooltipItems]); - const footer = callbacks.footer.apply(this, [tooltipItems]); - const afterFooter = callbacks.afterFooter.apply(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, index, array) => options.filter(element, index, 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(scoped.labelColor.call(this, context)); - labelPointStyles.push(scoped.labelPointStyle.call(this, context)); - labelTextColors.push(scoped.labelTextColor.call(this, context)); - }); - this.labelColors = labelColors; - this.labelPointStyles = labelPointStyles; - this.labelTextColors = labelTextColors; - this.dataPoints = tooltipItems; - 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 = undefined; - 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 labelColors = this.labelColors[i]; - const labelPointStyle = this.labelPointStyles[i]; - const {boxHeight, boxWidth, boxPadding} = 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 = labelColors.borderColor; - ctx.fillStyle = labelColors.backgroundColor; - drawPoint(ctx, drawOptions, centerX, centerY); - } else { - ctx.lineWidth = labelColors.borderWidth || 1; - ctx.strokeStyle = labelColors.borderColor; - ctx.setLineDash(labelColors.borderDash || []); - ctx.lineDashOffset = labelColors.borderDashOffset || 0; - const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth - boxPadding); - const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - boxPadding - 2); - const borderRadius = toTRBLCorners(labelColors.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 = labelColors.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 = labelColors.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); - } - } - } - 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}) => { - const meta = this.chart.getDatasetMeta(datasetIndex); - if (!meta) { - throw new Error('Cannot find a dataset at index ' + datasetIndex); - } - return { - datasetIndex, - element: meta.data[index], - index, - }; - }); - 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; - } - 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); - } -} -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; - const args = { - tooltip - }; - if (chart.notifyPlugins('beforeTooltipDraw', args) === false) { - return; - } - if (tooltip) { - 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: { - 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 - } - }, - 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'] -}; - -var plugins = /*#__PURE__*/Object.freeze({ -__proto__: null, -Decimation: plugin_decimation, -Filler: plugin_filler, -Legend: plugin_legend, -SubTitle: plugin_subtitle, -Title: plugin_title, -Tooltip: plugin_tooltip -}); - -const addIfString = (labels, raw, index, addedLabels) => { - if (typeof raw === 'string') { - index = labels.push(raw) - 1; - addedLabels.unshift({index, label: raw}); - } else if (isNaN(raw)) { - index = null; - } - return index; -}; -function findOrAddLabel(labels, raw, index, addedLabels) { - const first = labels.indexOf(raw); - if (first === -1) { - return addIfString(labels, raw, index, addedLabels); - } - const last = labels.lastIndexOf(raw); - return first !== last ? index : first; -} -const validIndex = (index, max) => index === null ? null : _limitValue(Math.round(index), 0, max); -class CategoryScale extends Scale { - constructor(cfg) { - super(cfg); - this._startValue = undefined; - this._valueRange = 0; - this._addedLabels = []; - } - init(scaleOptions) { - const added = this._addedLabels; - if (added.length) { - const labels = this.getLabels(); - for (const {index, label} of added) { - if (labels[index] === label) { - labels.splice(index, 1); - } - } - this._addedLabels = []; - } - super.init(scaleOptions); - } - parse(raw, index) { - if (isNullOrUndef(raw)) { - return null; - } - const labels = this.getLabels(); - index = isFinite(index) && labels[index] === raw ? index - : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels); - return validIndex(index, 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) { - const labels = this.getLabels(); - if (value >= 0 && value < labels.length) { - return labels[value]; - } - return 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(index) { - const ticks = this.ticks; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index].value); - } - getValueForPixel(pixel) { - return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange); - } - getBasePixel() { - return this.bottom; - } -} -CategoryScale.id = 'category'; -CategoryScale.defaults = { - ticks: { - callback: CategoryScale.prototype.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 / 1000)) { - 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 / 1000)) { - 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) { - ticks.push({value: Math.round((niceMin + j * spacing) * factor) / factor}); - } - 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)) || 0.001; - const length = 0.75 * minSpacing * ('' + value).length; - return Math.min(minSpacing / ratio, length); -} -class LinearScaleBase extends Scale { - constructor(cfg) { - super(cfg); - this.start = undefined; - this.end = undefined; - this._startValue = undefined; - this._endValue = undefined; - this._valueRange = 0; - } - parse(raw, index) { - 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 = 1; - if (max >= Number.MAX_SAFE_INTEGER || min <= Number.MIN_SAFE_INTEGER) { - offset = 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 > 1000) { - console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`); - maxTicks = 1000; - } - } 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)) || 0.001; - 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; - } -} -LinearScale.id = 'linear'; -LinearScale.defaults = { - ticks: { - callback: Ticks.formatters.numeric - } -}; - -function isMajor(tickVal) { - const remain = tickVal / (Math.pow(10, Math.floor(log10(tickVal)))); - return remain === 1; -} -function generateTicks(generationOptions, dataRange) { - const endExp = Math.floor(log10(dataRange.max)); - const endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); - const ticks = []; - let tickVal = finiteOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); - let exp = Math.floor(log10(tickVal)); - let significand = Math.floor(tickVal / Math.pow(10, exp)); - let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; - do { - ticks.push({value: tickVal, major: isMajor(tickVal)}); - ++significand; - if (significand === 10) { - significand = 1; - ++exp; - precision = exp >= 0 ? 1 : precision; - } - tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; - } while (exp < endExp || (exp === endExp && significand < endSignificand)); - const lastTick = finiteOrDefault(generationOptions.max, tickVal); - ticks.push({value: lastTick, major: isMajor(tickVal)}); - return ticks; -} -class LogarithmicScale extends Scale { - constructor(cfg) { - super(cfg); - this.start = undefined; - this.end = undefined; - this._startValue = undefined; - this._valueRange = 0; - } - parse(raw, index) { - const value = LinearScaleBase.prototype.parse.apply(this, [raw, index]); - if (value === 0) { - this._zero = true; - return undefined; - } - return isNumberFinite(value) && value > 0 ? value : null; - } - determineDataLimits() { - const {min, max} = this.getMinMax(true); - this.min = isNumberFinite(min) ? Math.max(0, min) : null; - this.max = isNumberFinite(max) ? Math.max(0, max) : null; - if (this.options.beginAtZero) { - this._zero = true; - } - this.handleTickRangeOptions(); - } - handleTickRangeOptions() { - const {minDefined, maxDefined} = this.getUserBounds(); - let min = this.min; - let max = this.max; - const setMin = v => (min = minDefined ? min : v); - const setMax = v => (max = maxDefined ? max : v); - const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m); - if (min === max) { - if (min <= 0) { - setMin(1); - setMax(10); - } else { - setMin(exp(min, -1)); - setMax(exp(max, +1)); - } - } - if (min <= 0) { - setMin(exp(max, -1)); - } - if (max <= 0) { - setMax(exp(min, +1)); - } - if (this._zero && this.min !== this._suggestedMin && min === exp(this.min, 0)) { - setMin(exp(min, -1)); - } - this.min = min; - this.max = max; - } - buildTicks() { - const opts = this.options; - const generationOptions = { - min: this._userMin, - max: this._userMax - }; - const ticks = generateTicks(generationOptions, this); - 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; - } - getLabelForValue(value) { - return value === undefined - ? '0' - : formatNumber(value, this.chart.options.locale, this.options.ticks.format); - } - configure() { - const start = this.min; - super.configure(); - this._startValue = log10(start); - this._valueRange = log10(this.max) - log10(start); - } - getPixelForValue(value) { - if (value === undefined || value === 0) { - value = this.min; - } - if (value === null || isNaN(value)) { - return NaN; - } - return this.getPixelForDecimal(value === this.min - ? 0 - : (log10(value) - this._startValue) / this._valueRange); - } - getValueForPixel(pixel) { - const decimal = this.getDecimalForPixel(pixel); - return Math.pow(10, this._startValue + decimal * this._valueRange); - } -} -LogarithmicScale.id = 'logarithmic'; -LogarithmicScale.defaults = { - ticks: { - callback: Ticks.formatters.logarithmic, - major: { - enabled: true - } - } -}; - -function getTickBackdropHeight(opts) { - const tickOpts = opts.ticks; - if (tickOpts.display && opts.display) { - const padding = toPadding(tickOpts.backdropPadding); - return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height; - } - return 0; -} -function measureLabelSize(ctx, font, label) { - label = isArray(label) ? label : [label]; - return { - w: _longestText(ctx, font.string, label), - h: label.length * font.lineHeight - }; -} -function determineLimits(angle, pos, size, min, max) { - if (angle === min || angle === max) { - return { - start: pos - (size / 2), - end: pos + (size / 2) - }; - } else if (angle < min || angle > max) { - return { - start: pos - size, - end: pos - }; - } - return { - start: pos, - end: pos + size - }; -} -function fitWithPointLabels(scale) { - const orig = { - l: scale.left + scale._padding.left, - r: scale.right - scale._padding.right, - t: scale.top + scale._padding.top, - b: scale.bottom - scale._padding.bottom - }; - const limits = Object.assign({}, orig); - const labelSizes = []; - const padding = []; - const valueCount = scale._pointLabels.length; - const pointLabelOpts = scale.options.pointLabels; - const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0; - for (let i = 0; i < valueCount; i++) { - const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i)); - padding[i] = opts.padding; - const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle); - const plFont = toFont(opts.font); - const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]); - labelSizes[i] = textSize; - const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle); - const angle = Math.round(toDegrees(angleRadians)); - const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); - const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); - updateLimits(limits, orig, angleRadians, hLimits, vLimits); - } - scale.setCenterPoint( - orig.l - limits.l, - limits.r - orig.r, - orig.t - limits.t, - limits.b - orig.b - ); - scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding); -} -function updateLimits(limits, orig, angle, hLimits, vLimits) { - const sin = Math.abs(Math.sin(angle)); - const cos = Math.abs(Math.cos(angle)); - let x = 0; - let y = 0; - if (hLimits.start < orig.l) { - x = (orig.l - hLimits.start) / sin; - limits.l = Math.min(limits.l, orig.l - x); - } else if (hLimits.end > orig.r) { - x = (hLimits.end - orig.r) / sin; - limits.r = Math.max(limits.r, orig.r + x); - } - if (vLimits.start < orig.t) { - y = (orig.t - vLimits.start) / cos; - limits.t = Math.min(limits.t, orig.t - y); - } else if (vLimits.end > orig.b) { - y = (vLimits.end - orig.b) / cos; - limits.b = Math.max(limits.b, orig.b + y); - } -} -function buildPointLabelItems(scale, labelSizes, padding) { - const items = []; - const valueCount = scale._pointLabels.length; - const opts = scale.options; - const extra = getTickBackdropHeight(opts) / 2; - const outerDistance = scale.drawingArea; - const additionalAngle = opts.pointLabels.centerPointLabels ? PI / valueCount : 0; - for (let i = 0; i < valueCount; i++) { - const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], additionalAngle); - const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI))); - const size = labelSizes[i]; - const y = yForAngle(pointLabelPosition.y, size.h, angle); - const textAlign = getTextAlignForAngle(angle); - const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign); - items.push({ - x: pointLabelPosition.x, - y, - textAlign, - left, - top: y, - right: left + size.w, - bottom: y + size.h - }); - } - return items; -} -function getTextAlignForAngle(angle) { - if (angle === 0 || angle === 180) { - return 'center'; - } else if (angle < 180) { - return 'left'; - } - return 'right'; -} -function leftForTextAlign(x, w, align) { - if (align === 'right') { - x -= w; - } else if (align === 'center') { - x -= (w / 2); - } - return x; -} -function yForAngle(y, h, angle) { - if (angle === 90 || angle === 270) { - y -= (h / 2); - } else if (angle > 270 || angle < 90) { - y -= h; - } - return y; -} -function drawPointLabels(scale, labelCount) { - const {ctx, options: {pointLabels}} = scale; - for (let i = labelCount - 1; i >= 0; i--) { - const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i)); - const plFont = toFont(optsAtIndex.font); - const {x, y, textAlign, left, top, right, bottom} = scale._pointLabelItems[i]; - const {backdropColor} = optsAtIndex; - if (!isNullOrUndef(backdropColor)) { - const padding = toPadding(optsAtIndex.backdropPadding); - ctx.fillStyle = backdropColor; - ctx.fillRect(left - padding.left, top - padding.top, right - left + padding.width, bottom - top + padding.height); - } - renderText( - ctx, - scale._pointLabels[i], - x, - y + (plFont.lineHeight / 2), - plFont, - { - color: optsAtIndex.color, - textAlign: textAlign, - textBaseline: 'middle' - } - ); - } -} -function pathRadiusLine(scale, radius, circular, labelCount) { - const {ctx} = scale; - if (circular) { - ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU); - } else { - let pointPosition = scale.getPointPosition(0, radius); - ctx.moveTo(pointPosition.x, pointPosition.y); - for (let i = 1; i < labelCount; i++) { - pointPosition = scale.getPointPosition(i, radius); - ctx.lineTo(pointPosition.x, pointPosition.y); - } - } -} -function drawRadiusLine(scale, gridLineOpts, radius, labelCount) { - const ctx = scale.ctx; - const circular = gridLineOpts.circular; - const {color, lineWidth} = gridLineOpts; - if ((!circular && !labelCount) || !color || !lineWidth || radius < 0) { - return; - } - ctx.save(); - ctx.strokeStyle = color; - ctx.lineWidth = lineWidth; - ctx.setLineDash(gridLineOpts.borderDash); - ctx.lineDashOffset = gridLineOpts.borderDashOffset; - ctx.beginPath(); - pathRadiusLine(scale, radius, circular, labelCount); - ctx.closePath(); - ctx.stroke(); - ctx.restore(); -} -function createPointLabelContext(parent, index, label) { - return createContext(parent, { - label, - index, - type: 'pointLabel' - }); -} -class RadialLinearScale extends LinearScaleBase { - constructor(cfg) { - super(cfg); - this.xCenter = undefined; - this.yCenter = undefined; - this.drawingArea = undefined; - this._pointLabels = []; - this._pointLabelItems = []; - } - setDimensions() { - const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2); - const w = this.width = this.maxWidth - padding.width; - const h = this.height = this.maxHeight - padding.height; - this.xCenter = Math.floor(this.left + w / 2 + padding.left); - this.yCenter = Math.floor(this.top + h / 2 + padding.top); - this.drawingArea = Math.floor(Math.min(w, h) / 2); - } - determineDataLimits() { - const {min, max} = this.getMinMax(false); - this.min = isNumberFinite(min) && !isNaN(min) ? min : 0; - this.max = isNumberFinite(max) && !isNaN(max) ? max : 0; - this.handleTickRangeOptions(); - } - computeTickLimit() { - return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); - } - generateTickLabels(ticks) { - LinearScaleBase.prototype.generateTickLabels.call(this, ticks); - this._pointLabels = this.getLabels() - .map((value, index) => { - const label = callback(this.options.pointLabels.callback, [value, index], this); - return label || label === 0 ? label : ''; - }) - .filter((v, i) => this.chart.getDataVisibility(i)); - } - fit() { - const opts = this.options; - if (opts.display && opts.pointLabels.display) { - fitWithPointLabels(this); - } else { - this.setCenterPoint(0, 0, 0, 0); - } - } - setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) { - this.xCenter += Math.floor((leftMovement - rightMovement) / 2); - this.yCenter += Math.floor((topMovement - bottomMovement) / 2); - this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement)); - } - getIndexAngle(index) { - const angleMultiplier = TAU / (this._pointLabels.length || 1); - const startAngle = this.options.startAngle || 0; - return _normalizeAngle(index * angleMultiplier + toRadians(startAngle)); - } - getDistanceFromCenterForValue(value) { - if (isNullOrUndef(value)) { - return NaN; - } - const scalingFactor = this.drawingArea / (this.max - this.min); - if (this.options.reverse) { - return (this.max - value) * scalingFactor; - } - return (value - this.min) * scalingFactor; - } - getValueForDistanceFromCenter(distance) { - if (isNullOrUndef(distance)) { - return NaN; - } - const scaledDistance = distance / (this.drawingArea / (this.max - this.min)); - return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance; - } - getPointLabelContext(index) { - const pointLabels = this._pointLabels || []; - if (index >= 0 && index < pointLabels.length) { - const pointLabel = pointLabels[index]; - return createPointLabelContext(this.getContext(), index, pointLabel); - } - } - getPointPosition(index, distanceFromCenter, additionalAngle = 0) { - const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle; - return { - x: Math.cos(angle) * distanceFromCenter + this.xCenter, - y: Math.sin(angle) * distanceFromCenter + this.yCenter, - angle - }; - } - getPointPositionForValue(index, value) { - return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); - } - getBasePosition(index) { - return this.getPointPositionForValue(index || 0, this.getBaseValue()); - } - getPointLabelPosition(index) { - const {left, top, right, bottom} = this._pointLabelItems[index]; - return { - left, - top, - right, - bottom, - }; - } - drawBackground() { - const {backgroundColor, grid: {circular}} = this.options; - if (backgroundColor) { - const ctx = this.ctx; - ctx.save(); - ctx.beginPath(); - pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length); - ctx.closePath(); - ctx.fillStyle = backgroundColor; - ctx.fill(); - ctx.restore(); - } - } - drawGrid() { - const ctx = this.ctx; - const opts = this.options; - const {angleLines, grid} = opts; - const labelCount = this._pointLabels.length; - let i, offset, position; - if (opts.pointLabels.display) { - drawPointLabels(this, labelCount); - } - if (grid.display) { - this.ticks.forEach((tick, index) => { - if (index !== 0) { - offset = this.getDistanceFromCenterForValue(tick.value); - const optsAtIndex = grid.setContext(this.getContext(index - 1)); - drawRadiusLine(this, optsAtIndex, offset, labelCount); - } - }); - } - if (angleLines.display) { - ctx.save(); - for (i = labelCount - 1; i >= 0; i--) { - const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i)); - const {color, lineWidth} = optsAtIndex; - if (!lineWidth || !color) { - continue; - } - ctx.lineWidth = lineWidth; - ctx.strokeStyle = color; - ctx.setLineDash(optsAtIndex.borderDash); - ctx.lineDashOffset = optsAtIndex.borderDashOffset; - offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max); - position = this.getPointPosition(i, offset); - ctx.beginPath(); - ctx.moveTo(this.xCenter, this.yCenter); - ctx.lineTo(position.x, position.y); - ctx.stroke(); - } - ctx.restore(); - } - } - drawBorder() {} - drawLabels() { - const ctx = this.ctx; - const opts = this.options; - const tickOpts = opts.ticks; - if (!tickOpts.display) { - return; - } - const startAngle = this.getIndexAngle(0); - let offset, width; - ctx.save(); - ctx.translate(this.xCenter, this.yCenter); - ctx.rotate(startAngle); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - this.ticks.forEach((tick, index) => { - if (index === 0 && !opts.reverse) { - return; - } - const optsAtIndex = tickOpts.setContext(this.getContext(index)); - const tickFont = toFont(optsAtIndex.font); - offset = this.getDistanceFromCenterForValue(this.ticks[index].value); - if (optsAtIndex.showLabelBackdrop) { - ctx.font = tickFont.string; - width = ctx.measureText(tick.label).width; - ctx.fillStyle = optsAtIndex.backdropColor; - const padding = toPadding(optsAtIndex.backdropPadding); - ctx.fillRect( - -width / 2 - padding.left, - -offset - tickFont.size / 2 - padding.top, - width + padding.width, - tickFont.size + padding.height - ); - } - renderText(ctx, tick.label, 0, -offset, tickFont, { - color: optsAtIndex.color, - }); - }); - ctx.restore(); - } - drawTitle() {} -} -RadialLinearScale.id = 'radialLinear'; -RadialLinearScale.defaults = { - display: true, - animate: true, - position: 'chartArea', - angleLines: { - display: true, - lineWidth: 1, - borderDash: [], - borderDashOffset: 0.0 - }, - grid: { - circular: false - }, - startAngle: 0, - ticks: { - showLabelBackdrop: true, - callback: Ticks.formatters.numeric - }, - pointLabels: { - backdropColor: undefined, - backdropPadding: 2, - display: true, - font: { - size: 10 - }, - callback(label) { - return label; - }, - padding: 5, - centerPointLabels: false - } -}; -RadialLinearScale.defaultRoutes = { - 'angleLines.color': 'borderColor', - 'pointLabels.color': 'color', - 'ticks.color': 'color' -}; -RadialLinearScale.descriptors = { - angleLines: { - _fallback: 'grid' - } -}; - -const INTERVALS = { - millisecond: {common: true, size: 1, steps: 1000}, - second: {common: true, size: 1000, steps: 60}, - minute: {common: true, size: 60000, steps: 60}, - hour: {common: true, size: 3600000, steps: 24}, - day: {common: true, size: 86400000, steps: 30}, - week: {common: false, size: 604800000, steps: 4}, - month: {common: true, size: 2.628e9, steps: 12}, - quarter: {common: false, size: 7.884e9, steps: 4}, - year: {common: true, size: 3.154e10} -}; -const UNITS = (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, 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 (round) { - value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) - ? adapter.startOf(value, 'isoWeek', isoWeekday) - : adapter.startOf(value, round); - } - 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, map, majorUnit) { - const adapter = scale._adapter; - const first = +adapter.startOf(ticks[0].value, majorUnit); - const last = ticks[ticks.length - 1].value; - let major, index; - for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { - index = map[major]; - if (index >= 0) { - ticks[index].major = true; - } - } - return ticks; -} -function ticksFromTimestamps(scale, values, majorUnit) { - const ticks = []; - const map = {}; - const ilen = values.length; - let i, value; - for (i = 0; i < ilen; ++i) { - value = values[i]; - map[value] = i; - ticks.push({ - value, - major: false - }); - } - return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); -} -class TimeScale extends Scale { - constructor(props) { - super(props); - this._cache = { - data: [], - labels: [], - all: [] - }; - this._unit = 'day'; - this._majorUnit = undefined; - this._offsets = {}; - this._normalized = false; - this._parseOpts = undefined; - } - init(scaleOpts, opts) { - const time = scaleOpts.time || (scaleOpts.time = {}); - const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date); - mergeIf(time.displayFormats, adapter.formats()); - this._parseOpts = { - parser: time.parser, - round: time.round, - isoWeekday: time.isoWeekday - }; - super.init(scaleOpts); - this._normalized = opts.normalized; - } - parse(raw, index) { - if (raw === undefined) { - 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' ? undefined - : determineMajorUnit(this._unit); - this.initOffsets(timestamps); - if (options.reverse) { - ticks.reverse(); - } - return ticksFromTimestamps(this, ticks, this._majorUnit); - } - 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(timeOpts.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) > 100000 * 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((a, b) => a - b).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); - } - _tickFormatFunction(time, index, ticks, format) { - const options = this.options; - 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[index]; - const major = majorUnit && majorFormat && tick && tick.major; - const label = this._adapter.format(time, format || (major ? majorFormat : minorFormat)); - const formatter = options.ticks.callback; - return formatter ? callback(formatter, [label, index, ticks], this) : label; - } - generateTickLabels(ticks) { - let i, ilen, tick; - for (i = 0, ilen = ticks.length; 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)); - } -} -TimeScale.id = 'time'; -TimeScale.defaults = { - bounds: 'data', - adapters: {}, - time: { - parser: false, - unit: false, - round: false, - isoWeekday: false, - minUnit: 'millisecond', - displayFormats: {} - }, - ticks: { - source: 'auto', - 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 = undefined; - this._tableRange = undefined; - } - 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; - } - _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); - } -} -TimeSeriesScale.id = 'timeseries'; -TimeSeriesScale.defaults = TimeScale.defaults; - -var scales = /*#__PURE__*/Object.freeze({ -__proto__: null, -CategoryScale: CategoryScale, -LinearScale: LinearScale, -LogarithmicScale: LogarithmicScale, -RadialLinearScale: RadialLinearScale, -TimeScale: TimeScale, -TimeSeriesScale: TimeSeriesScale -}); - -const registerables = [ - controllers, - elements, - plugins, - scales, -]; - -export { Animation, Animations, ArcElement, BarController, BarElement, BasePlatform, BasicPlatform, BubbleController, CategoryScale, Chart, DatasetController, plugin_decimation as Decimation, DomPlatform, DoughnutController, Element, plugin_filler as Filler, Interaction, plugin_legend as Legend, LineController, LineElement, LinearScale, LogarithmicScale, PieController, PointElement, PolarAreaController, RadarController, RadialLinearScale, Scale, ScatterController, plugin_subtitle as SubTitle, Ticks, TimeScale, TimeSeriesScale, plugin_title as Title, plugin_tooltip as Tooltip, adapters as _adapters, _detectPlatform, animator, controllers, elements, layouts, plugins, registerables, registry, scales }; diff --git a/node_modules/chart.js/dist/chart.js b/node_modules/chart.js/dist/chart.js deleted file mode 100644 index 58990616..00000000 --- a/node_modules/chart.js/dist/chart.js +++ /dev/null @@ -1,13269 +0,0 @@ -/*! - * Chart.js v3.7.1 - * https://www.chartjs.org - * (c) 2022 Chart.js Contributors - * Released under the MIT License - */ -(function (global, factory) { -typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : -typeof define === 'function' && define.amd ? define(factory) : -(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory()); -})(this, (function () { 'use strict'; - -function fontString(pixelSize, fontStyle, fontFamily) { - return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; -} -const requestAnimFrame = (function() { - if (typeof window === 'undefined') { - return function(callback) { - return callback(); - }; - } - return window.requestAnimationFrame; -}()); -function throttled(fn, thisArg, updateFn) { - const updateArgs = updateFn || ((args) => Array.prototype.slice.call(args)); - let ticking = false; - let args = []; - return function(...rest) { - args = updateArgs(rest); - if (!ticking) { - ticking = true; - requestAnimFrame.call(window, () => { - ticking = false; - fn.apply(thisArg, args); - }); - } - }; -} -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; -}; - -class Animator { - constructor() { - this._request = null; - this._charts = new Map(); - this._running = false; - this._lastDate = undefined; - } - _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 draw = 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); - draw = true; - } else { - items[i] = items[items.length - 1]; - items.pop(); - } - } - if (draw) { - 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 = new Animator(); - -/*! - * @kurkle/color v0.1.9 - * https://github.com/kurkle/color#readme - * (c) 2020 Jukka Kurkela - * Released under the MIT License - */ -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 & 0xF]; -const h2 = (b) => hex[(b & 0xF0) >> 4] + hex[b & 0xF]; -const eq = (b) => (((b & 0xF0) >> 4) === (b & 0xF)); -function isShort(v) { - return 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; -} -function hexString(v) { - var f = isShort(v) ? h1 : h2; - return v - ? '#' + f(v.r) + f(v.g) + f(v.b) + (v.a < 255 ? f(v.a) : '') - : v; -} -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 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 = 255 & (m[8] ? p2b(v) : v * 255); - } - r = +m[1]; - g = +m[3]; - b = +m[5]; - r = 255 & (m[2] ? p2b(r) : r); - g = 255 & (m[4] ? p2b(g) : g); - b = 255 & (m[6] ? p2b(b) : b); - return { - r: r, - g: g, - b: b, - a: 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 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 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 = max === r - ? ((g - b) / d) + (g < b ? 6 : 0) - : max === g - ? (b - r) / d + 2 - : (r - g) / d + 4; - 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: 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$1$1 = { - 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 = { - 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); - const tkeys = Object.keys(map$1$1); - 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$1$1[k]); - } - k = parseInt(names[ok], 16); - unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF]; - } - return unpacked; -} -let names$1; -function nameParse(str) { - if (!names$1) { - names$1 = unpack(); - names$1.transparent = [0, 0, 0, 0]; - } - const a = names$1[str.toLowerCase()]; - return a && { - r: a[0], - g: a[1], - b: a[2], - a: a.length === 4 ? a[3] : 255 - }; -} -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) : this._rgb; - } - hexString() { - return this._valid ? hexString(this._rgb) : this._rgb; - } - hslString() { - return this._valid ? hslString(this._rgb) : this._rgb; - } - mix(color, weight) { - const me = this; - if (color) { - const c1 = me.rgb; - const c2 = color.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.0; - w2 = 1 - w1; - c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5; - c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5; - c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5; - c1.a = p * c1.a + (1 - p) * c2.a; - me.rgb = c1; - } - return me; - } - 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; - } -} -function index_esm(input) { - return new Color(input); -} - -const isPatternOrGradient = (value) => value instanceof CanvasGradient || value instanceof CanvasPattern; -function color(value) { - return isPatternOrGradient(value) ? value : index_esm(value); -} -function getHoverColor(value) { - return isPatternOrGradient(value) - ? value - : index_esm(value).saturate(0.5).darken(0.1).hexString(); -} - -function noop() {} -const uid = (function() { - let id = 0; - return function() { - return id++; - }; -}()); -function isNullOrUndef(value) { - return value === null || typeof value === 'undefined'; -} -function isArray(value) { - if (Array.isArray && Array.isArray(value)) { - return true; - } - const type = Object.prototype.toString.call(value); - if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { - return true; - } - return false; -} -function isObject(value) { - return value !== null && Object.prototype.toString.call(value) === '[object Object]'; -} -const isNumberFinite = (value) => (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 toPercentage = (value, dimension) => - typeof value === 'string' && value.endsWith('%') ? - parseFloat(value) / 100 - : value / dimension; -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; - if (reverse) { - for (i = len - 1; i >= 0; i--) { - fn.call(thisArg, loopable[i], i); - } - } else { - 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 = 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; - for (let i = 0; i < ilen; ++i) { - source = sources[i]; - if (!isObject(source)) { - continue; - } - const keys = Object.keys(source); - for (let k = 0, klen = keys.length; k < klen; ++k) { - merger(keys[k], target, source, 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); - } -} -function _deprecated(scope, value, previous, current) { - if (value !== undefined) { - console.warn(scope + ': "' + previous + - '" is deprecated. Please use "' + current + '" instead'); - } -} -const emptyString = ''; -const dot = '.'; -function indexOfDotOrLength(key, start) { - const idx = key.indexOf(dot, start); - return idx === -1 ? key.length : idx; -} -function resolveObjectKey(obj, key) { - if (key === emptyString) { - return obj; - } - let pos = 0; - let idx = indexOfDotOrLength(key, pos); - while (obj && idx > pos) { - obj = obj[key.substr(pos, idx - pos)]; - pos = idx + 1; - idx = indexOfDotOrLength(key, pos); - } - return 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 overrides = Object.create(null); -const descriptors = 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] = 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(_descriptors) { - this.animation = undefined; - 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 - }; - this.maintainAspectRatio = true; - this.onHover = null; - this.onClick = null; - this.parsing = true; - this.plugins = {}; - this.responsive = true; - this.scale = undefined; - this.scales = {}; - this.showLine = true; - this.drawActiveElementsOnTop = true; - this.describe(_descriptors); - } - set(scope, values) { - return set(this, scope, values); - } - get(scope) { - return getScope$1(this, scope); - } - describe(scope, values) { - return set(descriptors, scope, values); - } - override(scope, values) { - return set(overrides, scope, values); - } - route(scope, name, targetScope, targetName) { - const scopeObject = getScope$1(this, scope); - 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; - } - } - }); - } -} -var defaults = new Defaults({ - _scriptable: (name) => !name.startsWith('on'), - _indexable: (name) => name !== 'events', - hover: { - _fallback: 'interaction' - }, - interaction: { - _scriptable: false, - _indexable: false, - } -}); - -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 niceNum(range) { - const roundedRange = Math.round(range); - range = almostEquals(range, roundedRange, range / 1000) ? 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 isNumber(n) { - return !isNaN(parseFloat(n)) && isFinite(n); -} -function almostEquals(x, y, epsilon) { - return Math.abs(x - y) < epsilon; -} -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 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 _longestText(ctx, font, arrayOfThings, cache) { - cache = cache || {}; - let data = cache.data = cache.data || {}; - let gc = cache.garbageCollect = cache.garbageCollect || []; - if (cache.font !== font) { - data = cache.data = {}; - gc = cache.garbageCollect = []; - cache.font = font; - } - ctx.save(); - ctx.font = font; - let longest = 0; - const ilen = arrayOfThings.length; - let i, j, jlen, thing, nestedThing; - for (i = 0; i < ilen; i++) { - thing = arrayOfThings[i]; - if (thing !== undefined && thing !== null && isArray(thing) !== true) { - longest = _measureText(ctx, data, gc, longest, thing); - } else if (isArray(thing)) { - for (j = 0, jlen = thing.length; j < jlen; j++) { - nestedThing = thing[j]; - if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) { - longest = _measureText(ctx, data, gc, longest, nestedThing); - } - } - } - } - ctx.restore(); - const gcLen = gc.length / 2; - if (gcLen > arrayOfThings.length) { - for (i = 0; i < gcLen; i++) { - delete data[gc[i]]; - } - gc.splice(0, gcLen); - } - 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) { - 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) { - let type, xOffset, yOffset, size, cornerRadius; - 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: - ctx.arc(x, y, radius, 0, TAU); - ctx.closePath(); - break; - case 'triangle': - ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - ctx.closePath(); - break; - case 'rectRounded': - cornerRadius = radius * 0.516; - size = radius - cornerRadius; - xOffset = Math.cos(rad + QUARTER_PI) * size; - yOffset = Math.sin(rad + QUARTER_PI) * size; - ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); - ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); - ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); - ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); - ctx.closePath(); - break; - case 'rect': - if (!rotation) { - size = Math.SQRT1_2 * radius; - ctx.rect(x - size, y - size, 2 * size, 2 * size); - break; - } - rad += QUARTER_PI; - case 'rectRot': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + yOffset, y - xOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.lineTo(x - yOffset, y + xOffset); - ctx.closePath(); - break; - case 'crossRot': - rad += QUARTER_PI; - case 'cross': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'star': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - rad += QUARTER_PI; - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'line': - xOffset = 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) * radius, y + Math.sin(rad) * radius); - 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.0; - 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 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 (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 += font.lineHeight; - } - ctx.restore(); -} -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 addRoundedRectPath(ctx, rect) { - const {x, y, w, h, radius} = rect; - ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_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); -} - -function _lookup(table, value, cmp) { - cmp = cmp || ((index) => table[index] < 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) => - _lookup(table, value, index => table[index][key] < value); -const _rlookupByKey = (table, key, value) => - _lookup(table, value, index => table[index][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 index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - if (listeners.length > 0) { - return; - } - arrayEvents.forEach((key) => { - delete array[key]; - }); - delete array._chartjs; -} -function _arrayUnique(items) { - const set = new Set(); - let i, ilen; - for (i = 0, ilen = items.length; i < ilen; ++i) { - set.add(items[i]); - } - if (set.size === ilen) { - return items; - } - return Array.from(set); -} - -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) => window.getComputedStyle(element, null); -function getStyle(el, property) { - return getComputedStyle(el).getPropertyValue(property); -} -const positions = ['top', 'right', 'bottom', 'left']; -function getPositionedStyle(styles, style, suffix) { - const result = {}; - suffix = suffix ? '-' + suffix : ''; - for (let i = 0; i < 4; i++) { - const pos = positions[i]; - result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0; - } - 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(evt, canvas) { - const e = evt.native || evt; - 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$1(evt, chart) { - 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(evt, 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 === undefined || height === undefined) { - const container = _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 ? Math.floor(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); - } - return { - width, - height - }; -} -function retinaScale(chart, forceRatio, forceStyle) { - const pixelRatio = forceRatio || 1; - const deviceHeight = Math.floor(chart.height * pixelRatio); - const deviceWidth = Math.floor(chart.width * pixelRatio); - chart.height = deviceHeight / pixelRatio; - chart.width = deviceWidth / pixelRatio; - 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; - } - }; - 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] : undefined; -} - -function getRelativePosition(e, chart) { - if ('native' in e) { - return { - x: e.x, - y: e.y - }; - } - return getRelativePosition$1(e, chart); -} -function evaluateAllVisibleItems(chart, handler) { - const metasets = chart.getSortedVisibleDatasetMetas(); - let index, data, element; - for (let i = 0, ilen = metasets.length; i < ilen; ++i) { - ({index, data} = metasets[i]); - for (let j = 0, jlen = data.length; j < jlen; ++j) { - element = data[j]; - if (!element.skip) { - handler(element, index, j); - } - } - } -} -function binarySearch(metaset, axis, value, intersect) { - const {controller, data, _sorted} = metaset; - const iScale = controller._cachedMeta.iScale; - if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) { - const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey; - if (!intersect) { - return lookupMethod(data, axis, value); - } 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 optimizedEvaluateItems(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, 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, index, 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) { - const items = []; - if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { - return items; - } - const evaluationFunc = function(element, datasetIndex, index) { - if (element.inRange(position.x, position.y, useFinalPosition)) { - items.push({element, datasetIndex, index}); - } - }; - optimizedEvaluateItems(chart, axis, position, evaluationFunc, true); - return items; -} -function getNearestRadialItems(chart, position, axis, useFinalPosition) { - let items = []; - function evaluationFunc(element, datasetIndex, index) { - 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}); - } - } - optimizedEvaluateItems(chart, axis, position, evaluationFunc); - return items; -} -function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition) { - let items = []; - const distanceMetric = getDistanceMetricForAxis(axis); - let minDistance = Number.POSITIVE_INFINITY; - function evaluationFunc(element, datasetIndex, index) { - const inRange = element.inRange(position.x, position.y, useFinalPosition); - if (intersect && !inRange) { - return; - } - const center = element.getCenterPoint(useFinalPosition); - const pointInArea = _isPointInArea(center, chart.chartArea, chart._minPadding); - if (!pointInArea && !inRange) { - return; - } - const distance = distanceMetric(position, center); - if (distance < minDistance) { - items = [{element, datasetIndex, index}]; - minDistance = distance; - } else if (distance === minDistance) { - items.push({element, datasetIndex, index}); - } - } - optimizedEvaluateItems(chart, axis, position, evaluationFunc); - return items; -} -function getNearestItems(chart, position, axis, intersect, useFinalPosition) { - if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) { - return []; - } - return axis === 'r' && !intersect - ? getNearestRadialItems(chart, position, axis, useFinalPosition) - : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition); -} -function getAxisItems(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const items = []; - const axis = options.axis; - const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange'; - let intersectsItem = false; - evaluateAllVisibleItems(chart, (element, datasetIndex, index) => { - if (element[rangeMethod](position[axis], useFinalPosition)) { - items.push({element, datasetIndex, index}); - } - if (element.inRange(position.x, position.y, useFinalPosition)) { - intersectsItem = true; - } - }); - if (options.intersect && !intersectsItem) { - return []; - } - return items; -} -var Interaction = { - modes: { - index(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const axis = options.axis || 'x'; - const items = options.intersect - ? getIntersectItems(chart, position, axis, useFinalPosition) - : getNearestItems(chart, position, axis, false, useFinalPosition); - const elements = []; - if (!items.length) { - return []; - } - chart.getSortedVisibleDatasetMetas().forEach((meta) => { - const index = items[0].index; - const element = meta.data[index]; - if (element && !element.skip) { - elements.push({element, datasetIndex: meta.index, index}); - } - }); - return elements; - }, - dataset(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const axis = options.axis || 'xy'; - let items = options.intersect - ? getIntersectItems(chart, position, axis, useFinalPosition) : - getNearestItems(chart, position, axis, false, useFinalPosition); - 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'; - return getIntersectItems(chart, position, axis, useFinalPosition); - }, - nearest(chart, e, options, useFinalPosition) { - const position = getRelativePosition(e, chart); - const axis = options.axis || 'xy'; - return getNearestItems(chart, position, axis, options.intersect, useFinalPosition); - }, - x(chart, e, options, useFinalPosition) { - return getAxisItems(chart, e, {axis: 'x', intersect: options.intersect}, useFinalPosition); - }, - y(chart, e, options, useFinalPosition) { - return getAxisItems(chart, e, {axis: 'y', intersect: options.intersect}, useFinalPosition); - } - } -}; - -const LINE_HEIGHT = new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); -const FONT_STYLE = new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/); -function toLineHeight(value, size) { - const matches = ('' + value).match(LINE_HEIGHT); - if (!matches || 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 = ''; - } - 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, index, info) { - let cacheable = true; - let i, ilen, value; - for (i = 0, ilen = inputs.length; i < ilen; ++i) { - value = inputs[i]; - if (value === undefined) { - continue; - } - if (context !== undefined && typeof value === 'function') { - value = value(context); - cacheable = false; - } - if (index !== undefined && isArray(value)) { - value = value[index % value.length]; - cacheable = false; - } - if (value !== undefined) { - if (info && !cacheable) { - info.cacheable = false; - } - 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); -} - -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(layouts) { - const stacks = {}; - for (const wrap of layouts) { - 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(layouts, params) { - const stacks = buildStacks(layouts); - const {vBoxMaxWidth, hBoxMaxHeight} = params; - let i, ilen, layout; - for (i = 0, ilen = layouts.length; i < ilen; ++i) { - layout = layouts[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(positions) { - const margin = {left: 0, top: 0, right: 0, bottom: 0}; - positions.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] || {count: 1, 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; -} -defaults.set('layout', { - autoPadding: true, - padding: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } -}); -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 index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; - if (index !== -1) { - chart.boxes.splice(index, 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}); - }); - } -}; - -function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback, getTarget = () => scopes[0]) { - if (!defined(fallback)) { - fallback = _resolve('_fallback', scopes); - } - const cache = { - [Symbol.toStringTag]: 'Object', - _cacheable: true, - _scopes: scopes, - _rootScopes: rootScopes, - _fallback: fallback, - _getTarget: getTarget, - override: (scope) => _createResolver([scope, ...scopes], prefixes, rootScopes, fallback), - }; - return new Proxy(cache, { - deleteProperty(target, prop) { - delete target[prop]; - delete target._keys; - delete scopes[0][prop]; - return true; - }, - get(target, prop) { - return _cached(target, prop, - () => _resolveWithPrefixes(prop, prefixes, scopes, target)); - }, - getOwnPropertyDescriptor(target, prop) { - return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop); - }, - getPrototypeOf() { - return Reflect.getPrototypeOf(scopes[0]); - }, - has(target, prop) { - return getKeysFromAllScopes(target).includes(prop); - }, - ownKeys(target) { - return getKeysFromAllScopes(target); - }, - 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: new Set(), - _descriptors: _descriptors(proxy, descriptorDefaults), - setContext: (ctx) => _attachContext(proxy, ctx, subProxy, descriptorDefaults), - override: (scope) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults) - }; - return new Proxy(cache, { - deleteProperty(target, prop) { - delete target[prop]; - delete proxy[prop]; - return true; - }, - get(target, prop, receiver) { - return _cached(target, prop, - () => _resolveWithContext(target, prop, receiver)); - }, - getOwnPropertyDescriptor(target, prop) { - return target._descriptors.allKeys - ? Reflect.has(proxy, prop) ? {enumerable: true, configurable: true} : undefined - : Reflect.getOwnPropertyDescriptor(proxy, prop); - }, - getPrototypeOf() { - return Reflect.getPrototypeOf(proxy); - }, - has(target, prop) { - return Reflect.has(proxy, prop); - }, - ownKeys() { - return Reflect.ownKeys(proxy); - }, - set(target, prop, value) { - proxy[prop] = value; - delete target[prop]; - return true; - } - }); -} -function _descriptors(proxy, defaults = {scriptable: true, indexable: true}) { - const {_scriptable = defaults.scriptable, _indexable = defaults.indexable, _allKeys = defaults.allKeys} = proxy; - return { - allKeys: _allKeys, - scriptable: _scriptable, - indexable: _indexable, - isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable, - isIndexable: isFunction(_indexable) ? _indexable : () => _indexable - }; -} -const readKey = (prefix, name) => prefix ? prefix + _capitalize(name) : name; -const needsSubResolver = (prop, value) => isObject(value) && prop !== 'adapters' && - (Object.getPrototypeOf(value) === null || value.constructor === Object); -function _cached(target, prop, resolve) { - if (Object.prototype.hasOwnProperty.call(target, prop)) { - return target[prop]; - } - const value = resolve(); - target[prop] = value; - return value; -} -function _resolveWithContext(target, prop, receiver) { - const {_proxy, _context, _subProxy, _descriptors: descriptors} = target; - let value = _proxy[prop]; - if (isFunction(value) && descriptors.isScriptable(prop)) { - value = _resolveScriptable(prop, value, target, receiver); - } - if (isArray(value) && value.length) { - value = _resolveArray(prop, value, target, descriptors.isIndexable); - } - if (needsSubResolver(prop, value)) { - value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors); - } - return value; -} -function _resolveScriptable(prop, value, 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); - value = value(_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: descriptors} = target; - if (defined(_context.index) && isIndexable(prop)) { - value = 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], descriptors)); - } - } - 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) : undefined; -function addScopes(set, parentScopes, key, parentFallback, value) { - for (const parent of parentScopes) { - const scope = getScope(key, parent); - if (scope) { - set.add(scope); - const fallback = resolveFallback(scope._fallback, key, value); - if (defined(fallback) && fallback !== key && fallback !== parentFallback) { - return fallback; - } - } else if (scope === false && defined(parentFallback) && 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 set = new Set(); - set.add(value); - let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value); - if (key === null) { - return false; - } - if (defined(fallback) && fallback !== prop) { - key = addScopesFromKey(set, allScopes, fallback, key, value); - if (key === null) { - return false; - } - } - return _createResolver(Array.from(set), [''], rootScopes, fallback, - () => subGetTarget(resolver, prop, value)); -} -function addScopesFromKey(set, allScopes, key, fallback, item) { - while (key) { - key = addScopes(set, 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 (defined(value)) { - return needsSubResolver(prop, value) - ? createSubResolver(scopes, proxy, prop, value) - : value; - } - } -} -function _resolve(key, scopes) { - for (const scope of scopes) { - if (!scope) { - continue; - } - const value = scope[key]; - if (defined(value)) { - return value; - } - } -} -function getKeysFromAllScopes(target) { - let keys = target._keys; - if (!keys) { - keys = target._keys = resolveKeysFromAllScopes(target._scopes); - } - return keys; -} -function resolveKeysFromAllScopes(scopes) { - const set = new Set(); - for (const scope of scopes) { - for (const key of Object.keys(scope).filter(k => !k.startsWith('_'))) { - set.add(key); - } - } - return Array.from(set); -} - -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); - } -} - -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 _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 intlCache = 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 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 !== undefined) { - 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) { - return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle); -} - -var helpers = /*#__PURE__*/Object.freeze({ -__proto__: null, -easingEffects: effects, -color: color, -getHoverColor: getHoverColor, -noop: noop, -uid: uid, -isNullOrUndef: isNullOrUndef, -isArray: isArray, -isObject: isObject, -isFinite: isNumberFinite, -finiteOrDefault: finiteOrDefault, -valueOrDefault: valueOrDefault, -toPercentage: toPercentage, -toDimension: toDimension, -callback: callback, -each: each, -_elementsEqual: _elementsEqual, -clone: clone, -_merger: _merger, -merge: merge, -mergeIf: mergeIf, -_mergerIf: _mergerIf, -_deprecated: _deprecated, -resolveObjectKey: resolveObjectKey, -_capitalize: _capitalize, -defined: defined, -isFunction: isFunction, -setsEqual: setsEqual, -_isClickEvent: _isClickEvent, -toFontString: toFontString, -_measureText: _measureText, -_longestText: _longestText, -_alignPixel: _alignPixel, -clearCanvas: clearCanvas, -drawPoint: drawPoint, -_isPointInArea: _isPointInArea, -clipArea: clipArea, -unclipArea: unclipArea, -_steppedLineTo: _steppedLineTo, -_bezierCurveTo: _bezierCurveTo, -renderText: renderText, -addRoundedRectPath: addRoundedRectPath, -_lookup: _lookup, -_lookupByKey: _lookupByKey, -_rlookupByKey: _rlookupByKey, -_filterBetween: _filterBetween, -listenArrayEvents: listenArrayEvents, -unlistenArrayEvents: unlistenArrayEvents, -_arrayUnique: _arrayUnique, -_createResolver: _createResolver, -_attachContext: _attachContext, -_descriptors: _descriptors, -splineCurve: splineCurve, -splineCurveMonotone: splineCurveMonotone, -_updateBezierControlPoints: _updateBezierControlPoints, -_isDomSupported: _isDomSupported, -_getParentNode: _getParentNode, -getStyle: getStyle, -getRelativePosition: getRelativePosition$1, -getMaximumSize: getMaximumSize, -retinaScale: retinaScale, -supportsEventListenerOptions: supportsEventListenerOptions, -readUsedSize: readUsedSize, -fontString: fontString, -requestAnimFrame: requestAnimFrame, -throttled: throttled, -debounce: debounce, -_toLeftRightCenter: _toLeftRightCenter, -_alignStartEnd: _alignStartEnd, -_textX: _textX, -_pointInLine: _pointInLine, -_steppedInterpolation: _steppedInterpolation, -_bezierInterpolation: _bezierInterpolation, -formatNumber: formatNumber, -toLineHeight: toLineHeight, -_readValueToProps: _readValueToProps, -toTRBL: toTRBL, -toTRBLCorners: toTRBLCorners, -toPadding: toPadding, -toFont: toFont, -resolve: resolve, -_addGrace: _addGrace, -createContext: createContext, -PI: PI, -TAU: TAU, -PITAU: PITAU, -INFINITY: INFINITY, -RAD_PER_DEG: RAD_PER_DEG, -HALF_PI: HALF_PI, -QUARTER_PI: QUARTER_PI, -TWO_THIRDS_PI: TWO_THIRDS_PI, -log10: log10, -sign: sign, -niceNum: niceNum, -_factorize: _factorize, -isNumber: isNumber, -almostEquals: almostEquals, -almostWhole: almostWhole, -_setMinAndMaxByKey: _setMinAndMaxByKey, -toRadians: toRadians, -toDegrees: toDegrees, -_decimalPlaces: _decimalPlaces, -getAngleFromPoint: getAngleFromPoint, -distanceBetweenPoints: distanceBetweenPoints, -_angleDiff: _angleDiff, -_normalizeAngle: _normalizeAngle, -_angleBetween: _angleBetween, -_limitValue: _limitValue, -_int16Range: _int16Range, -_isBetween: _isBetween, -getRtlAdapter: getRtlAdapter, -overrideTextDirection: overrideTextDirection, -restoreTextDirection: restoreTextDirection, -_boundSegment: _boundSegment, -_boundSegments: _boundSegments, -_computeSegments: _computeSegments -}); - -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 !== undefined) { - canvas.width = displayWidth; - } - } - if (isNullOrEmpty(renderHeight)) { - if (canvas.style.height === '') { - canvas.height = canvas.width / (aspectRatio || 2); - } else { - const displayHeight = readUsedSize(canvas, 'height'); - if (displayHeight !== undefined) { - canvas.height = displayHeight; - } - } - } - return canvas; -} -const eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; -function addListener(node, type, listener) { - node.addEventListener(type, listener, eventListenerOptions); -} -function removeListener(chart, type, listener) { - chart.canvas.removeEventListener(type, listener, eventListenerOptions); -} -function fromNativeEvent(event, chart) { - const type = EVENT_TYPES[event.type] || event.type; - const {x, y} = getRelativePosition$1(event, chart); - return { - type, - chart, - native: event, - x: x !== undefined ? x : null, - y: y !== undefined ? 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 = 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, (args) => { - const event = args[0]; - return [event, event.offsetX, event.offsetY]; - }); - 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] = undefined; - } - getDevicePixelRatio() { - return window.devicePixelRatio; - } - getMaximumSize(canvas, width, height, aspectRatio) { - return getMaximumSize(canvas, width, height, aspectRatio); - } - isAttached(canvas) { - const container = _getParentNode(canvas); - return !!(container && container.isConnected); - } -} - -function _detectPlatform(canvas) { - if (!_isDomSupported() || (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas)) { - return BasicPlatform; - } - return DomPlatform; -} - -var platforms = /*#__PURE__*/Object.freeze({ -__proto__: null, -_detectPlatform: _detectPlatform, -BasePlatform: BasePlatform, -BasicPlatform: BasicPlatform, -DomPlatform: DomPlatform -}); - -const transparent = 'transparent'; -const interpolators = { - boolean(from, to, factor) { - return factor > 0.5 ? to : from; - }, - color(from, to, factor) { - const c0 = color(from || transparent); - const c1 = c0.valid && color(to || transparent); - return c1 && c1.valid - ? c1.mix(c0, factor).hexString() - : to; - }, - number(from, to, factor) { - return from + (to - from) * factor; - } -}; -class Animation { - constructor(cfg, target, prop, to) { - const currentValue = target[prop]; - to = resolve([cfg.to, to, currentValue, cfg.from]); - const from = resolve([cfg.from, currentValue, to]); - this._active = true; - this._fn = cfg.fn || interpolators[cfg.type || typeof from]; - this._easing = effects[cfg.easing] || effects.linear; - this._start = Math.floor(Date.now() + (cfg.delay || 0)); - this._duration = this._total = Math.floor(cfg.duration); - this._loop = !!cfg.loop; - this._target = target; - this._prop = prop; - this._from = from; - this._to = to; - this._promises = undefined; - } - active() { - return this._active; - } - update(cfg, to, 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, to, currentValue, cfg.from]); - this._from = resolve([cfg.from, currentValue, to]); - } - } - 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 from = this._from; - const loop = this._loop; - const to = this._to; - let factor; - this._active = from !== to && (loop || (elapsed < duration)); - if (!this._active) { - this._target[prop] = to; - this._notify(true); - return; - } - if (elapsed < 0) { - this._target[prop] = from; - 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(from, to, 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](); - } - } -} - -const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension']; -const colors = ['color', 'borderColor', 'backgroundColor']; -defaults.set('animation', { - delay: undefined, - duration: 1000, - easing: 'easeOutQuart', - fn: undefined, - from: undefined, - loop: undefined, - to: undefined, - type: undefined, -}); -const animationOptions = Object.keys(defaults.animation); -defaults.describe('animation', { - _fallback: false, - _indexable: false, - _scriptable: (name) => name !== 'onProgress' && name !== 'onComplete' && name !== 'fn', -}); -defaults.set('animations', { - colors: { - type: 'color', - properties: colors - }, - numbers: { - type: 'number', - properties: numbers - }, -}); -defaults.describe('animations', { - _fallback: 'animation', -}); -defaults.set('transitions', { - active: { - animation: { - duration: 400 - } - }, - resize: { - animation: { - duration: 0 - } - }, - show: { - animations: { - colors: { - from: 'transparent' - }, - visible: { - type: 'boolean', - duration: 0 - }, - } - }, - hide: { - animations: { - colors: { - to: 'transparent' - }, - visible: { - type: 'boolean', - easing: 'linear', - fn: v => v | 0 - }, - } - } -}); -class Animations { - constructor(chart, config) { - this._chart = chart; - this._properties = new Map(); - this.configure(config); - } - configure(config) { - if (!isObject(config)) { - return; - } - const animatedProps = this._properties; - Object.getOwnPropertyNames(config).forEach(key => { - const cfg = config[key]; - if (!isObject(cfg)) { - return; - } - const resolved = {}; - for (const option of animationOptions) { - resolved[option] = cfg[option]; - } - (isArray(cfg.properties) && cfg.properties || [key]).forEach((prop) => { - 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 === undefined ? allowedOverflow : 0; - const max = opts.max === undefined ? 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; - } - for (i = 0, ilen = keys.length; i < ilen; ++i) { - datasetIndex = +keys[i]; - if (datasetIndex === dsIndex) { - if (options.all) { - continue; - } - break; - } - otherValue = stack.values[datasetIndex]; - if (isNumberFinite(otherValue) && (singleMode || (value === 0 || sign(value) === sign(otherValue)))) { - value += otherValue; - } - } - return value; -} -function convertObjectDataToArray(data) { - 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] = { - x: key, - y: data[key] - }; - } - return adata; -} -function isStacked(scale, meta) { - const stacked = scale && scale.options.stacked; - return stacked || (stacked === undefined && meta.stack !== undefined); -} -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]: index, [vAxis]: value} = item; - const itemStacks = item._stacks || (item._stacks = {}); - stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index); - stack[datasetIndex] = value; - stack._top = getLastIndexInStack(stack, vScale, true, meta.type); - stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type); - } -} -function getFirstScaleId(chart, axis) { - const scales = chart.scales; - return Object.keys(scales).filter(key => scales[key].axis === axis).shift(); -} -function createDatasetContext(parent, index) { - return createContext(parent, - { - active: false, - dataset: undefined, - datasetIndex: index, - index, - mode: 'default', - type: 'dataset' - } - ); -} -function createDataContext(parent, index, element) { - return createContext(parent, { - active: false, - dataIndex: index, - parsed: undefined, - raw: undefined, - element, - index, - 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] === undefined || stacks[axis][datasetIndex] === undefined) { - return; - } - delete stacks[axis][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 = undefined; - this._parsing = false; - this._data = undefined; - this._objectData = undefined; - this._sharedOptions = undefined; - this._drawStart = undefined; - this._drawCount = undefined; - this.enableOptionSharing = false; - this.$context = undefined; - this._syncList = []; - this.initialize(); - } - initialize() { - const meta = this._cachedMeta; - this.configure(); - this.linkScales(); - meta._stacked = isStacked(meta.vScale, meta); - this.addElements(); - } - 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)) { - this._data = convertObjectDataToArray(data); - } 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); - } - } - 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, index; - for (i = 0, ilen = count; i < ilen; ++i) { - index = i + start; - parsed[i] = { - [iAxis]: singleScale || iScale.parse(labels[index], index), - [vAxis]: vScale.parse(data[index], index) - }; - } - return parsed; - } - parseArrayData(meta, data, start, count) { - const {xScale, yScale} = meta; - const parsed = new Array(count); - let i, ilen, index, item; - for (i = 0, ilen = count; i < ilen; ++i) { - index = i + start; - item = data[index]; - parsed[i] = { - x: xScale.parse(item[0], index), - y: yScale.parse(item[1], index) - }; - } - 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, index, item; - for (i = 0, ilen = count; i < ilen; ++i) { - index = i + start; - item = data[index]; - parsed[i] = { - x: xScale.parse(resolveObjectKey(item, xAxisKey), index), - y: yScale.parse(resolveObjectKey(item, yAxisKey), index) - }; - } - return parsed; - } - getParsed(index) { - return this._cachedMeta._parsed[index]; - } - getDataElement(index) { - return this._cachedMeta.data[index]; - } - 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] - }; - 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(index) { - const meta = this._cachedMeta; - const iScale = meta.iScale; - const vScale = meta.vScale; - const parsed = this.getParsed(index); - 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(index, active) { - const mode = active ? 'active' : 'default'; - return index === undefined && this._cachedMeta.dataset - ? this.resolveDatasetElementOptions(mode) - : this.resolveDataElementOptions(index || 0, mode); - } - getContext(index, active, mode) { - const dataset = this.getDataset(); - let context; - if (index >= 0 && index < this._cachedMeta.data.length) { - const element = this._cachedMeta.data[index]; - context = element.$context || - (element.$context = createDataContext(this.getContext(), index, element)); - context.parsed = this.getParsed(index); - context.raw = dataset.data[index]; - context.index = context.dataIndex = index; - } 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(index, mode) { - return this._resolveElementOptions(this.dataElementType.id, mode, index); - } - _resolveElementOptions(elementType, mode = 'default', index) { - const active = mode === 'active'; - const cache = this._cachedDataOpts; - const cacheKey = elementType + '-' + mode; - const cached = cache[cacheKey]; - const sharing = this.enableOptionSharing && defined(index); - 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 names = Object.keys(defaults.elements[elementType]); - const context = () => this.getContext(index, active); - const values = config.resolveNamedOptions(scopes, names, context, prefixes); - if (values.$shared) { - values.$shared = sharing; - cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing)); - } - return values; - } - _resolveAnimations(index, 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(index, 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; - } - updateElement(element, index, properties, mode) { - if (isDirectUpdateMode(mode)) { - Object.assign(element, properties); - } else { - this._resolveAnimations(index, mode).update(element, properties); - } - } - updateSharedOptions(sharedOptions, mode, newOptions) { - if (sharedOptions && !isDirectUpdateMode(mode)) { - this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions); - } - } - _setStyle(element, index, mode, active) { - element.active = active; - const options = this.getStyle(index, active); - this._resolveAnimations(index, mode, active).update(element, { - options: (!active && this.getSharedOptions(options)) || options - }); - } - removeHoverStyle(element, datasetIndex, index) { - this._setStyle(element, index, 'active', false); - } - setHoverStyle(element, datasetIndex, index) { - this._setStyle(element, index, 'active', true); - } - _removeDatasetHoverStyle() { - const element = this._cachedMeta.dataset; - if (element) { - this._setStyle(element, undefined, 'active', false); - } - } - _setDatasetHoverStyle() { - const element = this._cachedMeta.dataset; - if (element) { - this._setStyle(element, undefined, '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]); - } -} -DatasetController.defaults = {}; -DatasetController.prototype.datasetElementType = null; -DatasetController.prototype.dataElementType = null; - -class Element { - constructor() { - this.x = undefined; - this.y = undefined; - this.active = false; - this.options = undefined; - this.$animations = undefined; - } - 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; - } -} -Element.defaults = {}; -Element.defaultRoutes = undefined; - -const formatters = { - values(value) { - return isArray(value) ? value : '' + value; - }, - numeric(tickValue, index, 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 > 1e+15) { - notation = 'scientific'; - } - delta = calculateDelta(tickValue, ticks); - } - const logDelta = log10(Math.abs(delta)); - const numDecimal = 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); - }, - logarithmic(tickValue, index, ticks) { - if (tickValue === 0) { - return '0'; - } - const remain = tickValue / (Math.pow(10, Math.floor(log10(tickValue)))); - if (remain === 1 || remain === 2 || remain === 5) { - return formatters.numeric.call(this, tickValue, index, ticks); - } - return ''; - } -}; -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}; - -defaults.set('scale', { - display: true, - offset: false, - reverse: false, - beginAtZero: false, - bounds: 'ticks', - grace: 0, - grid: { - display: true, - lineWidth: 1, - drawBorder: true, - drawOnChartArea: true, - drawTicks: true, - tickLength: 8, - tickWidth: (_ctx, options) => options.lineWidth, - tickColor: (_ctx, options) => options.color, - offset: false, - borderDash: [], - borderDashOffset: 0.0, - borderWidth: 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, - } -}); -defaults.route('scale.ticks', 'color', '', 'color'); -defaults.route('scale.grid', 'color', '', 'borderColor'); -defaults.route('scale.grid', 'borderColor', '', 'borderColor'); -defaults.route('scale.title', 'color', '', 'color'); -defaults.describe('scale', { - _fallback: false, - _scriptable: (name) => !name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser', - _indexable: (name) => name !== 'borderDash' && name !== 'tickBorderDash', -}); -defaults.describe('scales', { - _fallback: 'scale', -}); -defaults.describe('scale.ticks', { - _scriptable: (name) => name !== 'backdropPadding' && name !== 'callback', - _indexable: (name) => name !== 'backdropPadding', -}); - -function autoSkip(scale, ticks) { - const tickOpts = scale.options.ticks; - const ticksLimit = tickOpts.maxTicksLimit || determineMaxTicks(scale); - 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; -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, index, offsetGridLines) { - const length = scale.ticks.length; - const validIndex = Math.min(index, length - 1); - const start = scale._startPixel; - const end = scale._endPixel; - const epsilon = 1e-6; - let lineValue = scale.getPixelForTick(validIndex); - let offset; - if (offsetGridLines) { - if (length === 1) { - offset = Math.max(lineValue - start, end - lineValue); - } else if (index === 0) { - offset = (scale.getPixelForTick(1) - lineValue) / 2; - } else { - offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2; - } - lineValue += validIndex < index ? 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, index, tick) { - return createContext(parent, { - tick, - index, - 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 = undefined; - this.ctx = cfg.ctx; - this.chart = cfg.chart; - this.top = undefined; - this.bottom = undefined; - this.left = undefined; - this.right = undefined; - this.width = undefined; - this.height = undefined; - this._margins = { - left: 0, - right: 0, - top: 0, - bottom: 0 - }; - this.maxWidth = undefined; - this.maxHeight = undefined; - this.paddingTop = undefined; - this.paddingBottom = undefined; - this.paddingLeft = undefined; - this.paddingRight = undefined; - this.axis = undefined; - this.labelRotation = undefined; - this.min = undefined; - this.max = undefined; - this._range = undefined; - this.ticks = []; - this._gridLineItems = null; - this._labelItems = null; - this._labelSizes = null; - this._length = 0; - this._maxLength = 0; - this._longestTextCache = {}; - this._startPixel = undefined; - this._endPixel = undefined; - this._reversePixels = false; - this._userMax = undefined; - this._userMin = undefined; - this._suggestedMax = undefined; - this._suggestedMin = undefined; - this._ticksLength = 0; - this._borderValue = 0; - this._cache = {}; - this._dataLimitsCached = false; - this.$context = undefined; - } - init(options) { - this.options = options.setContext(this.getContext()); - this.axis = options.axis; - this._userMin = this.parse(options.min); - this._userMax = this.parse(options.max); - this._suggestedMin = this.parse(options.suggestedMin); - this._suggestedMax = this.parse(options.suggestedMax); - } - parse(raw, index) { - return raw; - } - getUserBounds() { - let {_userMin, _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 || []; - } - 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; - } - 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 = this.ticks.length; - 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]); - } - 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 { - 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); - } - return labelSizes; - } - _computeLabelSizes(ticks, length) { - const {ctx, _longestTextCache: caches} = this; - const widths = []; - const heights = []; - let widestLabelSize = 0; - let highestLabelSize = 0; - let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel; - for (i = 0; i < length; ++i) { - 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, index) { - return NaN; - } - getValueForPixel(pixel) {} - getPixelForTick(index) { - const ticks = this.ticks; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index].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(index) { - const ticks = this.ticks || []; - if (index >= 0 && index < ticks.length) { - const tick = ticks[index]; - return tick.$context || - (tick.$context = createTickContext(this.getContext(), index, 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} = 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 = grid.setContext(this.getContext()); - const axisWidth = borderOpts.drawBorder ? borderOpts.borderWidth : 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 optsAtIndex = grid.setContext(this.getContext(i)); - const lineWidth = optsAtIndex.lineWidth; - const lineColor = optsAtIndex.color; - const borderDash = grid.borderDash || []; - const borderDashOffset = optsAtIndex.borderDashOffset; - const tickWidth = optsAtIndex.tickWidth; - const tickColor = optsAtIndex.tickColor; - const tickBorderDash = optsAtIndex.tickBorderDash || []; - const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset; - lineValue = getPixelForGridLine(this, i, offset); - if (lineValue === undefined) { - 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 color = optsAtIndex.color; - const strokeColor = optsAtIndex.textStrokeColor; - const strokeWidth = optsAtIndex.textStrokeWidth; - if (isHorizontal) { - x = pixel; - 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; - } - } 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 = y + textOffset - labelPadding.top; - let left = x - 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; - } - backdrop = { - left, - top, - width: width + labelPadding.width, - height: height + labelPadding.height, - color: optsAtIndex.backdropColor, - }; - } - items.push({ - rotation, - label, - font, - color, - strokeColor, - strokeWidth, - textOffset, - textAlign, - 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'; - } - 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 index = ticks.findIndex(t => t.value === value); - if (index >= 0) { - const opts = grid.setContext(this.getContext(index)); - 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: {grid}} = this; - const borderOpts = grid.setContext(this.getContext()); - const axisWidth = grid.drawBorder ? borderOpts.borderWidth : 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.borderWidth; - ctx.strokeStyle = borderOpts.borderColor; - 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._labelItems || (this._labelItems = this._computeLabelItems(chartArea)); - let i, ilen; - for (i = 0, ilen = items.length; i < ilen; ++i) { - const item = items[i]; - const tickFont = item.font; - const label = item.label; - if (item.backdrop) { - ctx.fillStyle = item.backdrop.color; - ctx.fillRect(item.backdrop.left, item.backdrop.top, item.backdrop.width, item.backdrop.height); - } - let y = item.textOffset; - renderText(ctx, label, 0, y, tickFont, item); - } - 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); - 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: gz + 1, - 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(index) { - const opts = this.options.ticks.setContext(this.getContext(index)); - 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 = 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(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, registry, component) { - const camelMethod = _capitalize(method); - callback(component['before' + camelMethod], [], component); - registry[method](component); - callback(component['after' + camelMethod], [], component); - } - _getRegistryForType(type) { - for (let i = 0; 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 === undefined) { - throw new Error('"' + id + '" is not a registered ' + type + '.'); - } - return item; - } -} -var registry = new Registry(); - -class PluginService { - constructor() { - this._init = []; - } - notify(chart, hook, args, filter) { - if (hook === 'beforeInit') { - this._init = this._createDescriptors(chart, true); - this._notify(this._init, chart, 'install'); - } - const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart); - const result = this._notify(descriptors, chart, hook, args); - if (hook === 'afterDestroy') { - this._notify(descriptors, chart, 'stop'); - this._notify(this._init, chart, 'uninstall'); - } - return result; - } - _notify(descriptors, chart, hook, args) { - args = args || {}; - for (const descriptor of descriptors) { - 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 = undefined; - } - } - _descriptors(chart) { - if (this._cache) { - return this._cache; - } - const descriptors = this._cache = this._createDescriptors(chart); - this._notifyStateChanges(chart); - return descriptors; - } - _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 descriptors = this._cache; - const diff = (a, b) => a.filter(x => !b.some(y => x.plugin.id === y.plugin.id)); - this._notify(diff(previousDescriptors, descriptors), chart, 'stop'); - this._notify(diff(descriptors, previousDescriptors), chart, 'start'); - } -} -function allPlugins(config) { - 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); - } - } - return plugins; -} -function getOpts(options, all) { - if (!all && options === false) { - return null; - } - if (options === true) { - return {}; - } - return options; -} -function createDescriptors(chart, plugins, options, all) { - const result = []; - const context = chart.getContext(); - for (let i = 0; i < plugins.length; i++) { - const plugin = plugins[i]; - const id = plugin.id; - const opts = getOpts(options[id], all); - if (opts === null) { - continue; - } - result.push({ - plugin, - options: pluginOpts(chart.config, plugin, opts, context) - }); - } - return result; -} -function pluginOpts(config, plugin, opts, context) { - const keys = config.pluginScopeKeys(plugin); - const scopes = config.getOptionScopes(opts, keys); - 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 axisFromPosition(position) { - if (position === 'top' || position === 'bottom') { - return 'x'; - } - if (position === 'left' || position === 'right') { - return 'y'; - } -} -function determineAxis(id, scaleOptions) { - if (id === 'x' || id === 'y') { - return id; - } - return scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.charAt(0).toLowerCase(); -} -function mergeScaleConfig(config, options) { - const chartDefaults = overrides[config.type] || {scales: {}}; - const configScales = options.scales || {}; - const chartIndexAxis = getIndexAxis(config.type, options); - const firstIDs = Object.create(null); - const scales = 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); - const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis); - const defaultScaleOptions = chartDefaults.scales || {}; - firstIDs[axis] = firstIDs[axis] || id; - scales[id] = mergeIf(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'] || firstIDs[axis] || axis; - scales[id] = scales[id] || 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 = new Map(); -const keysCached = 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 = (set, obj, key) => { - const opts = resolveObjectKey(obj, key); - if (opts !== undefined) { - set.add(opts); - } -}; -class Config { - constructor(config) { - this._config = initConfig(config); - this._scopeCache = new Map(); - this._resolverCache = new Map(); - } - get platform() { - return this._config.platform; - } - get type() { - return this._config.type; - } - set type(type) { - this._config.type = type; - } - get data() { - return this._config.data; - } - set data(data) { - this._config.data = initData(data); - } - get options() { - return this._config.options; - } - set options(options) { - this._config.options = options; - } - get plugins() { - return this._config.plugins; - } - update() { - const config = this._config; - this.clearCache(); - initOptions(config); - } - clearCache() { - this._scopeCache.clear(); - this._resolverCache.clear(); - } - datasetScopeKeys(datasetType) { - return cachedKeys(datasetType, - () => [[ - `datasets.${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 = 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 = 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(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, names, context, prefixes = ['']) { - const result = {$shared: true}; - const {resolver, subPrefixes} = getResolver(this._resolverCache, scopes, prefixes); - let options = resolver; - if (needContext(resolver, names)) { - result.$shared = false; - context = isFunction(context) ? context() : context; - const subResolver = this.createResolver(scopes, context, subPrefixes); - options = _attachContext(resolver, context, subResolver); - } - for (const prop of names) { - result[prop] = options[prop]; - } - return result; - } - createResolver(scopes, context, prefixes = [''], descriptorDefaults) { - const {resolver} = getResolver(this._resolverCache, scopes, prefixes); - return isObject(context) - ? _attachContext(resolver, context, undefined, descriptorDefaults) - : resolver; - } -} -function getResolver(resolverCache, scopes, prefixes) { - let cache = resolverCache.get(scopes); - if (!cache) { - cache = 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).reduce((acc, key) => acc || isFunction(value[key]), false); -function needContext(proxy, names) { - const {isScriptable, isIndexable} = _descriptors(proxy); - for (const prop of names) { - 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 = "3.7.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 { - 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 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 = undefined; - this.boxes = []; - this.currentDevicePixelRatio = undefined; - this.chartArea = undefined; - this._active = []; - this._lastEvent = undefined; - this._listeners = {}; - this._responsiveListeners = undefined; - this._sortedMetasets = []; - this.scales = {}; - this._plugins = new PluginService(); - this.$proxies = {}; - this._hiddenIndices = {}; - this.attached = false; - this._animationsDisabled = undefined; - this.$context = undefined; - 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; - } - _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 === undefined || 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, index) => { - if (datasets.filter(x => x === meta._dataset).length === 0) { - this._destroyDatasetMeta(index); - } - }); - } - 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.prototype, { - 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, index) => { - item._idx = index; - }); - 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(index, mode) { - const meta = this.getDatasetMeta(index); - const args = {meta, index, 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._resize(width, height); - this._resizeBeforeDraw = null; - } - 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 clip = meta._clip; - const useClip = !clip.disabled; - const area = this.chartArea; - const args = { - meta, - index: meta.index, - cancelable: true - }; - if (this.notifyPlugins('beforeDatasetDraw', args) === false) { - return; - } - if (useClip) { - clipArea(ctx, { - left: clip.left === false ? 0 : area.left - clip.left, - right: clip.right === false ? this.width : area.right + clip.right, - top: clip.top === false ? 0 : area.top - clip.top, - bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom - }); - } - meta.controller.draw(); - if (useClip) { - unclipArea(ctx); - } - args.cancelable = false; - this.notifyPlugins('afterDatasetDraw', args); - } - 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(index) { - this._hiddenIndices[index] = !this._hiddenIndices[index]; - } - getDataVisibility(index) { - return !this._hiddenIndices[index]; - } - _updateVisibility(datasetIndex, dataIndex, visible) { - const mode = visible ? 'show' : 'hide'; - const meta = this.getDatasetMeta(datasetIndex); - const anims = meta.controller._resolveAnimations(undefined, 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 : undefined); - } - } - 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; - } - this.notifyPlugins('destroy'); - 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, listener) => { - platform.addEventListener(this, type, listener); - listeners[type] = listener; - }; - 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, listener) => { - platform.addEventListener(this, type, listener); - listeners[type] = listener; - }; - const _remove = (type, listener) => { - if (listeners[type]) { - platform.removeEventListener(this, type, listener); - 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 = undefined; - } - 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}) => { - const meta = this.getDatasetMeta(datasetIndex); - if (!meta) { - throw new Error('No dataset found at index ' + datasetIndex); - } - return { - datasetIndex, - element: meta.data[index], - index, - }; - }); - 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); - } - _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: _isPointInArea(e, this.chartArea, this._minPadding) - }; - 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); - } -} -const invalidatePlugins = () => each(Chart.instances, (chart) => chart._plugins.invalidate()); -const enumerable = true; -Object.defineProperties(Chart, { - defaults: { - enumerable, - value: defaults - }, - instances: { - enumerable, - value: instances - }, - overrides: { - enumerable, - value: overrides - }, - registry: { - enumerable, - value: registry - }, - version: { - enumerable, - value: version - }, - getChart: { - enumerable, - value: getChart - }, - register: { - enumerable, - value: (...items) => { - registry.add(...items); - invalidatePlugins(); - } - }, - unregister: { - enumerable, - value: (...items) => { - registry.remove(...items); - invalidatePlugins(); - } - } -}); - -function abstract() { - throw new Error('This method is not implemented: Check that a complete date adapter is provided.'); -} -class DateAdapter { - constructor(options) { - this.options = options || {}; - } - formats() { - return abstract(); - } - parse(value, format) { - return abstract(); - } - format(timestamp, format) { - return abstract(); - } - add(timestamp, amount, unit) { - return abstract(); - } - diff(a, b, unit) { - return abstract(); - } - startOf(timestamp, unit, weekday) { - return abstract(); - } - endOf(timestamp, unit) { - return abstract(); - } -} -DateAdapter.override = function(members) { - Object.assign(DateAdapter.prototype, members); -}; -var _adapters = { - _date: DateAdapter -}; - -function getAllScaleValues(scale, type) { - if (!scale._cache.$bar) { - const visibleMetas = scale.getMatchingVisibleMetas(type); - let values = []; - for (let i = 0, ilen = visibleMetas.length; i < ilen; i++) { - values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale)); - } - scale._cache.$bar = _arrayUnique(values.sort((a, b) => a - b)); - } - return scale._cache.$bar; -} -function computeMinSampleSize(meta) { - const scale = meta.iScale; - const values = getAllScaleValues(scale, meta.type); - let min = scale._length; - let i, ilen, curr, prev; - const updateMinAndPrev = () => { - if (curr === 32767 || curr === -32768) { - return; - } - if (defined(prev)) { - min = Math.min(min, Math.abs(curr - prev) || min); - } - prev = curr; - }; - for (i = 0, ilen = values.length; i < ilen; ++i) { - curr = scale.getPixelForValue(values[i]); - updateMinAndPrev(); - } - prev = undefined; - for (i = 0, ilen = scale.ticks.length; i < ilen; ++i) { - curr = scale.getPixelForTick(i); - updateMinAndPrev(); - } - return min; -} -function computeFitCategoryTraits(index, ruler, options, stackCount) { - const thickness = options.barThickness; - let size, ratio; - if (isNullOrUndef(thickness)) { - size = ruler.min * options.categoryPercentage; - ratio = options.barPercentage; - } else { - size = thickness * stackCount; - ratio = 1; - } - return { - chunk: size / stackCount, - ratio, - start: ruler.pixels[index] - (size / 2) - }; -} -function computeFlexCategoryTraits(index, ruler, options, stackCount) { - const pixels = ruler.pixels; - const curr = pixels[index]; - let prev = index > 0 ? pixels[index - 1] : null; - let next = index < pixels.length - 1 ? pixels[index + 1] : null; - const percent = options.categoryPercentage; - if (prev === null) { - prev = curr - (next === null ? ruler.end - ruler.start : next - curr); - } - if (next === null) { - next = curr + curr - prev; - } - const start = curr - (curr - Math.min(prev, next)) / 2 * percent; - const size = Math.abs(next - prev) / 2 * percent; - return { - chunk: size / stackCount, - ratio: options.barPercentage, - start - }; -} -function parseFloatBar(entry, item, vScale, i) { - const startValue = vScale.parse(entry[0], i); - const endValue = vScale.parse(entry[1], i); - const min = Math.min(startValue, endValue); - const max = Math.max(startValue, endValue); - let barStart = min; - let barEnd = max; - if (Math.abs(min) > Math.abs(max)) { - barStart = max; - barEnd = min; - } - item[vScale.axis] = barEnd; - item._custom = { - barStart, - barEnd, - start: startValue, - end: endValue, - min, - max - }; -} -function parseValue(entry, item, vScale, i) { - if (isArray(entry)) { - parseFloatBar(entry, item, vScale, i); - } else { - item[vScale.axis] = vScale.parse(entry, i); - } - return item; -} -function parseArrayOrPrimitive(meta, data, start, count) { - const iScale = meta.iScale; - const vScale = meta.vScale; - const labels = iScale.getLabels(); - const singleScale = iScale === vScale; - const parsed = []; - let i, ilen, item, entry; - for (i = start, ilen = start + count; i < ilen; ++i) { - entry = data[i]; - item = {}; - item[iScale.axis] = singleScale || iScale.parse(labels[i], i); - parsed.push(parseValue(entry, item, vScale, i)); - } - return parsed; -} -function isFloatBar(custom) { - return custom && custom.barStart !== undefined && custom.barEnd !== undefined; -} -function barSign(size, vScale, actualBase) { - if (size !== 0) { - return sign(size); - } - return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1); -} -function borderProps(properties) { - let reverse, start, end, top, bottom; - if (properties.horizontal) { - reverse = properties.base > properties.x; - start = 'left'; - end = 'right'; - } else { - reverse = properties.base < properties.y; - start = 'bottom'; - end = 'top'; - } - if (reverse) { - top = 'end'; - bottom = 'start'; - } else { - top = 'start'; - bottom = 'end'; - } - return {start, end, reverse, top, bottom}; -} -function setBorderSkipped(properties, options, stack, index) { - let edge = options.borderSkipped; - const res = {}; - if (!edge) { - properties.borderSkipped = res; - return; - } - const {start, end, reverse, top, bottom} = borderProps(properties); - if (edge === 'middle' && stack) { - properties.enableBorderRadius = true; - if ((stack._top || 0) === index) { - edge = top; - } else if ((stack._bottom || 0) === index) { - edge = bottom; - } else { - res[parseEdge(bottom, start, end, reverse)] = true; - edge = top; - } - } - res[parseEdge(edge, start, end, reverse)] = true; - properties.borderSkipped = res; -} -function parseEdge(edge, a, b, reverse) { - if (reverse) { - edge = swap(edge, a, b); - edge = startEnd(edge, b, a); - } else { - edge = startEnd(edge, a, b); - } - return edge; -} -function swap(orig, v1, v2) { - return orig === v1 ? v2 : orig === v2 ? v1 : orig; -} -function startEnd(v, start, end) { - return v === 'start' ? start : v === 'end' ? end : v; -} -function setInflateAmount(properties, {inflateAmount}, ratio) { - properties.inflateAmount = inflateAmount === 'auto' - ? ratio === 1 ? 0.33 : 0 - : inflateAmount; -} -class BarController extends DatasetController { - parsePrimitiveData(meta, data, start, count) { - return parseArrayOrPrimitive(meta, data, start, count); - } - parseArrayData(meta, data, start, count) { - return parseArrayOrPrimitive(meta, data, start, count); - } - parseObjectData(meta, data, start, count) { - const {iScale, vScale} = meta; - const {xAxisKey = 'x', yAxisKey = 'y'} = this._parsing; - const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey; - const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey; - const parsed = []; - let i, ilen, item, obj; - for (i = start, ilen = start + count; i < ilen; ++i) { - obj = data[i]; - item = {}; - item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i); - parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i)); - } - return parsed; - } - updateRangeFromParsed(range, scale, parsed, stack) { - super.updateRangeFromParsed(range, scale, parsed, stack); - const custom = parsed._custom; - if (custom && scale === this._cachedMeta.vScale) { - range.min = Math.min(range.min, custom.min); - range.max = Math.max(range.max, custom.max); - } - } - getMaxOverflow() { - return 0; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const {iScale, vScale} = meta; - const parsed = this.getParsed(index); - const custom = parsed._custom; - const value = isFloatBar(custom) - ? '[' + custom.start + ', ' + custom.end + ']' - : '' + vScale.getLabelForValue(parsed[vScale.axis]); - return { - label: '' + iScale.getLabelForValue(parsed[iScale.axis]), - value - }; - } - initialize() { - this.enableOptionSharing = true; - super.initialize(); - const meta = this._cachedMeta; - meta.stack = this.getDataset().stack; - } - update(mode) { - const meta = this._cachedMeta; - this.updateElements(meta.data, 0, meta.data.length, mode); - } - updateElements(bars, start, count, mode) { - const reset = mode === 'reset'; - const {index, _cachedMeta: {vScale}} = this; - const base = vScale.getBasePixel(); - const horizontal = vScale.isHorizontal(); - const ruler = this._getRuler(); - const firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - this.updateSharedOptions(sharedOptions, mode, firstOpts); - for (let i = start; i < start + count; i++) { - const parsed = this.getParsed(i); - const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : this._calculateBarValuePixels(i); - const ipixels = this._calculateBarIndexPixels(i, ruler); - const stack = (parsed._stacks || {})[vScale.axis]; - const properties = { - horizontal, - base: vpixels.base, - enableBorderRadius: !stack || isFloatBar(parsed._custom) || (index === stack._top || index === stack._bottom), - x: horizontal ? vpixels.head : ipixels.center, - y: horizontal ? ipixels.center : vpixels.head, - height: horizontal ? ipixels.size : Math.abs(vpixels.size), - width: horizontal ? Math.abs(vpixels.size) : ipixels.size - }; - if (includeOptions) { - properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode); - } - const options = properties.options || bars[i].options; - setBorderSkipped(properties, options, stack, index); - setInflateAmount(properties, options, ruler.ratio); - this.updateElement(bars[i], i, properties, mode); - } - } - _getStacks(last, dataIndex) { - const meta = this._cachedMeta; - const iScale = meta.iScale; - const metasets = iScale.getMatchingVisibleMetas(this._type); - const stacked = iScale.options.stacked; - const ilen = metasets.length; - const stacks = []; - let i, item; - for (i = 0; i < ilen; ++i) { - item = metasets[i]; - if (!item.controller.options.grouped) { - continue; - } - if (typeof dataIndex !== 'undefined') { - const val = item.controller.getParsed(dataIndex)[ - item.controller._cachedMeta.vScale.axis - ]; - if (isNullOrUndef(val) || isNaN(val)) { - continue; - } - } - if (stacked === false || stacks.indexOf(item.stack) === -1 || - (stacked === undefined && item.stack === undefined)) { - stacks.push(item.stack); - } - if (item.index === last) { - break; - } - } - if (!stacks.length) { - stacks.push(undefined); - } - return stacks; - } - _getStackCount(index) { - return this._getStacks(undefined, index).length; - } - _getStackIndex(datasetIndex, name, dataIndex) { - const stacks = this._getStacks(datasetIndex, dataIndex); - const index = (name !== undefined) - ? stacks.indexOf(name) - : -1; - return (index === -1) - ? stacks.length - 1 - : index; - } - _getRuler() { - const opts = this.options; - const meta = this._cachedMeta; - const iScale = meta.iScale; - const pixels = []; - let i, ilen; - for (i = 0, ilen = meta.data.length; i < ilen; ++i) { - pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i)); - } - const barThickness = opts.barThickness; - const min = barThickness || computeMinSampleSize(meta); - return { - min, - pixels, - start: iScale._startPixel, - end: iScale._endPixel, - stackCount: this._getStackCount(), - scale: iScale, - grouped: opts.grouped, - ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage - }; - } - _calculateBarValuePixels(index) { - const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = this; - const actualBase = baseValue || 0; - const parsed = this.getParsed(index); - const custom = parsed._custom; - const floating = isFloatBar(custom); - let value = parsed[vScale.axis]; - let start = 0; - let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value; - let head, size; - if (length !== value) { - start = length - value; - length = value; - } - if (floating) { - value = custom.barStart; - length = custom.barEnd - custom.barStart; - if (value !== 0 && sign(value) !== sign(custom.barEnd)) { - start = 0; - } - start += value; - } - const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start; - let base = vScale.getPixelForValue(startValue); - if (this.chart.getDataVisibility(index)) { - head = vScale.getPixelForValue(start + length); - } else { - head = base; - } - size = head - base; - if (Math.abs(size) < minBarLength) { - size = barSign(size, vScale, actualBase) * minBarLength; - if (value === actualBase) { - base -= size / 2; - } - head = base + size; - } - if (base === vScale.getPixelForValue(actualBase)) { - const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2; - base += halfGrid; - size -= halfGrid; - } - return { - size, - base, - head, - center: head + size / 2 - }; - } - _calculateBarIndexPixels(index, ruler) { - const scale = ruler.scale; - const options = this.options; - const skipNull = options.skipNull; - const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity); - let center, size; - if (ruler.grouped) { - const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount; - const range = options.barThickness === 'flex' - ? computeFlexCategoryTraits(index, ruler, options, stackCount) - : computeFitCategoryTraits(index, ruler, options, stackCount); - const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined); - center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); - size = Math.min(maxBarThickness, range.chunk * range.ratio); - } else { - center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index); - size = Math.min(maxBarThickness, ruler.min * ruler.ratio); - } - return { - base: center - size / 2, - head: center + size / 2, - center, - size - }; - } - draw() { - const meta = this._cachedMeta; - const vScale = meta.vScale; - const rects = meta.data; - const ilen = rects.length; - let i = 0; - for (; i < ilen; ++i) { - if (this.getParsed(i)[vScale.axis] !== null) { - rects[i].draw(this._ctx); - } - } - } -} -BarController.id = 'bar'; -BarController.defaults = { - datasetElementType: false, - dataElementType: 'bar', - categoryPercentage: 0.8, - barPercentage: 0.9, - grouped: true, - animations: { - numbers: { - type: 'number', - properties: ['x', 'y', 'base', 'width', 'height'] - } - } -}; -BarController.overrides = { - scales: { - _index_: { - type: 'category', - offset: true, - grid: { - offset: true - } - }, - _value_: { - type: 'linear', - beginAtZero: true, - } - } -}; - -class BubbleController extends DatasetController { - initialize() { - this.enableOptionSharing = true; - super.initialize(); - } - parsePrimitiveData(meta, data, start, count) { - const parsed = super.parsePrimitiveData(meta, data, start, count); - for (let i = 0; i < parsed.length; i++) { - parsed[i]._custom = this.resolveDataElementOptions(i + start).radius; - } - return parsed; - } - parseArrayData(meta, data, start, count) { - const parsed = super.parseArrayData(meta, data, start, count); - for (let i = 0; i < parsed.length; i++) { - const item = data[start + i]; - parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius); - } - return parsed; - } - parseObjectData(meta, data, start, count) { - const parsed = super.parseObjectData(meta, data, start, count); - for (let i = 0; i < parsed.length; i++) { - const item = data[start + i]; - parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius); - } - return parsed; - } - getMaxOverflow() { - const data = this._cachedMeta.data; - let max = 0; - for (let i = data.length - 1; i >= 0; --i) { - max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2); - } - return max > 0 && max; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const {xScale, yScale} = meta; - const parsed = this.getParsed(index); - const x = xScale.getLabelForValue(parsed.x); - const y = yScale.getLabelForValue(parsed.y); - const r = parsed._custom; - return { - label: meta.label, - value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')' - }; - } - update(mode) { - const points = this._cachedMeta.data; - this.updateElements(points, 0, points.length, mode); - } - updateElements(points, start, count, mode) { - const reset = mode === 'reset'; - const {iScale, vScale} = this._cachedMeta; - const firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - const iAxis = iScale.axis; - const vAxis = vScale.axis; - for (let i = start; i < start + count; i++) { - const point = points[i]; - const parsed = !reset && this.getParsed(i); - const properties = {}; - const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]); - const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]); - properties.skip = isNaN(iPixel) || isNaN(vPixel); - if (includeOptions) { - properties.options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); - if (reset) { - properties.options.radius = 0; - } - } - this.updateElement(point, i, properties, mode); - } - this.updateSharedOptions(sharedOptions, mode, firstOpts); - } - resolveDataElementOptions(index, mode) { - const parsed = this.getParsed(index); - let values = super.resolveDataElementOptions(index, mode); - if (values.$shared) { - values = Object.assign({}, values, {$shared: false}); - } - const radius = values.radius; - if (mode !== 'active') { - values.radius = 0; - } - values.radius += valueOrDefault(parsed && parsed._custom, radius); - return values; - } -} -BubbleController.id = 'bubble'; -BubbleController.defaults = { - datasetElementType: false, - dataElementType: 'point', - animations: { - numbers: { - type: 'number', - properties: ['x', 'y', 'borderWidth', 'radius'] - } - } -}; -BubbleController.overrides = { - scales: { - x: { - type: 'linear' - }, - y: { - type: 'linear' - } - }, - plugins: { - tooltip: { - callbacks: { - title() { - return ''; - } - } - } - } -}; - -function getRatioAndOffset(rotation, circumference, cutout) { - let ratioX = 1; - let ratioY = 1; - let offsetX = 0; - let offsetY = 0; - if (circumference < TAU) { - const startAngle = rotation; - const endAngle = startAngle + circumference; - const startX = Math.cos(startAngle); - const startY = Math.sin(startAngle); - const endX = Math.cos(endAngle); - const endY = Math.sin(endAngle); - const calcMax = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout); - const calcMin = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout); - const maxX = calcMax(0, startX, endX); - const maxY = calcMax(HALF_PI, startY, endY); - const minX = calcMin(PI, startX, endX); - const minY = calcMin(PI + HALF_PI, startY, endY); - ratioX = (maxX - minX) / 2; - ratioY = (maxY - minY) / 2; - offsetX = -(maxX + minX) / 2; - offsetY = -(maxY + minY) / 2; - } - return {ratioX, ratioY, offsetX, offsetY}; -} -class DoughnutController extends DatasetController { - constructor(chart, datasetIndex) { - super(chart, datasetIndex); - this.enableOptionSharing = true; - this.innerRadius = undefined; - this.outerRadius = undefined; - this.offsetX = undefined; - this.offsetY = undefined; - } - linkScales() {} - parse(start, count) { - const data = this.getDataset().data; - const meta = this._cachedMeta; - if (this._parsing === false) { - meta._parsed = data; - } else { - let getter = (i) => +data[i]; - if (isObject(data[start])) { - const {key = 'value'} = this._parsing; - getter = (i) => +resolveObjectKey(data[i], key); - } - let i, ilen; - for (i = start, ilen = start + count; i < ilen; ++i) { - meta._parsed[i] = getter(i); - } - } - } - _getRotation() { - return toRadians(this.options.rotation - 90); - } - _getCircumference() { - return toRadians(this.options.circumference); - } - _getRotationExtents() { - let min = TAU; - let max = -TAU; - for (let i = 0; i < this.chart.data.datasets.length; ++i) { - if (this.chart.isDatasetVisible(i)) { - const controller = this.chart.getDatasetMeta(i).controller; - const rotation = controller._getRotation(); - const circumference = controller._getCircumference(); - min = Math.min(min, rotation); - max = Math.max(max, rotation + circumference); - } - } - return { - rotation: min, - circumference: max - min, - }; - } - update(mode) { - const chart = this.chart; - const {chartArea} = chart; - const meta = this._cachedMeta; - const arcs = meta.data; - const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing; - const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0); - const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1); - const chartWeight = this._getRingWeight(this.index); - const {circumference, rotation} = this._getRotationExtents(); - const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout); - const maxWidth = (chartArea.width - spacing) / ratioX; - const maxHeight = (chartArea.height - spacing) / ratioY; - const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); - const outerRadius = toDimension(this.options.radius, maxRadius); - const innerRadius = Math.max(outerRadius * cutout, 0); - const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal(); - this.offsetX = offsetX * outerRadius; - this.offsetY = offsetY * outerRadius; - meta.total = this.calculateTotal(); - this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index); - this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0); - this.updateElements(arcs, 0, arcs.length, mode); - } - _circumference(i, reset) { - const opts = this.options; - const meta = this._cachedMeta; - const circumference = this._getCircumference(); - if ((reset && opts.animation.animateRotate) || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) { - return 0; - } - return this.calculateCircumference(meta._parsed[i] * circumference / TAU); - } - updateElements(arcs, start, count, mode) { - const reset = mode === 'reset'; - const chart = this.chart; - const chartArea = chart.chartArea; - const opts = chart.options; - const animationOpts = opts.animation; - const centerX = (chartArea.left + chartArea.right) / 2; - const centerY = (chartArea.top + chartArea.bottom) / 2; - const animateScale = reset && animationOpts.animateScale; - const innerRadius = animateScale ? 0 : this.innerRadius; - const outerRadius = animateScale ? 0 : this.outerRadius; - const firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - let startAngle = this._getRotation(); - let i; - for (i = 0; i < start; ++i) { - startAngle += this._circumference(i, reset); - } - for (i = start; i < start + count; ++i) { - const circumference = this._circumference(i, reset); - const arc = arcs[i]; - const properties = { - x: centerX + this.offsetX, - y: centerY + this.offsetY, - startAngle, - endAngle: startAngle + circumference, - circumference, - outerRadius, - innerRadius - }; - if (includeOptions) { - properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode); - } - startAngle += circumference; - this.updateElement(arc, i, properties, mode); - } - this.updateSharedOptions(sharedOptions, mode, firstOpts); - } - calculateTotal() { - const meta = this._cachedMeta; - const metaData = meta.data; - let total = 0; - let i; - for (i = 0; i < metaData.length; i++) { - const value = meta._parsed[i]; - if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) { - total += Math.abs(value); - } - } - return total; - } - calculateCircumference(value) { - const total = this._cachedMeta.total; - if (total > 0 && !isNaN(value)) { - return TAU * (Math.abs(value) / total); - } - return 0; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const chart = this.chart; - const labels = chart.data.labels || []; - const value = formatNumber(meta._parsed[index], chart.options.locale); - return { - label: labels[index] || '', - value, - }; - } - getMaxBorderWidth(arcs) { - let max = 0; - const chart = this.chart; - let i, ilen, meta, controller, options; - if (!arcs) { - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - arcs = meta.data; - controller = meta.controller; - break; - } - } - } - if (!arcs) { - return 0; - } - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - options = controller.resolveDataElementOptions(i); - if (options.borderAlign !== 'inner') { - max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0); - } - } - return max; - } - getMaxOffset(arcs) { - let max = 0; - for (let i = 0, ilen = arcs.length; i < ilen; ++i) { - const options = this.resolveDataElementOptions(i); - max = Math.max(max, options.offset || 0, options.hoverOffset || 0); - } - return max; - } - _getRingWeightOffset(datasetIndex) { - let ringWeightOffset = 0; - for (let i = 0; i < datasetIndex; ++i) { - if (this.chart.isDatasetVisible(i)) { - ringWeightOffset += this._getRingWeight(i); - } - } - return ringWeightOffset; - } - _getRingWeight(datasetIndex) { - return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0); - } - _getVisibleDatasetWeightTotal() { - return this._getRingWeightOffset(this.chart.data.datasets.length) || 1; - } -} -DoughnutController.id = 'doughnut'; -DoughnutController.defaults = { - datasetElementType: false, - dataElementType: 'arc', - animation: { - animateRotate: true, - animateScale: false - }, - animations: { - numbers: { - type: 'number', - properties: ['circumference', 'endAngle', 'innerRadius', 'outerRadius', 'startAngle', 'x', 'y', 'offset', 'borderWidth', 'spacing'] - }, - }, - cutout: '50%', - rotation: 0, - circumference: 360, - radius: '100%', - spacing: 0, - indexAxis: 'r', -}; -DoughnutController.descriptors = { - _scriptable: (name) => name !== 'spacing', - _indexable: (name) => name !== 'spacing', -}; -DoughnutController.overrides = { - aspectRatio: 1, - plugins: { - legend: { - labels: { - generateLabels(chart) { - const data = chart.data; - if (data.labels.length && data.datasets.length) { - const {labels: {pointStyle}} = chart.legend.options; - return data.labels.map((label, i) => { - const meta = chart.getDatasetMeta(0); - const style = meta.controller.getStyle(i); - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - pointStyle: pointStyle, - hidden: !chart.getDataVisibility(i), - index: i - }; - }); - } - return []; - } - }, - onClick(e, legendItem, legend) { - legend.chart.toggleDataVisibility(legendItem.index); - legend.chart.update(); - } - }, - tooltip: { - callbacks: { - title() { - return ''; - }, - label(tooltipItem) { - let dataLabel = tooltipItem.label; - const value = ': ' + tooltipItem.formattedValue; - if (isArray(dataLabel)) { - dataLabel = dataLabel.slice(); - dataLabel[0] += value; - } else { - dataLabel += value; - } - return dataLabel; - } - } - } - } -}; - -class LineController extends DatasetController { - initialize() { - this.enableOptionSharing = 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, undefined, { - 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 firstOpts = this.resolveDataElementOptions(start, mode); - const sharedOptions = this.getSharedOptions(firstOpts); - const includeOptions = this.includeOptions(mode, sharedOptions); - 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'; - let prevParsed = start > 0 && this.getParsed(start - 1); - for (let i = start; i < start + count; ++i) { - const point = points[i]; - const parsed = this.getParsed(i); - const properties = directUpdate ? point : {}; - 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 && (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; - } - this.updateSharedOptions(sharedOptions, mode, firstOpts); - } - 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(); - } -} -LineController.id = 'line'; -LineController.defaults = { - datasetElementType: 'line', - dataElementType: 'point', - showLine: true, - spanGaps: false, -}; -LineController.overrides = { - scales: { - _index_: { - type: 'category', - }, - _value_: { - type: 'linear', - }, - } -}; -function getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) { - const pointCount = points.length; - let start = 0; - let count = pointCount; - if (meta._sorted) { - const {iScale, _parsed} = meta; - const axis = iScale.axis; - const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); - if (minDefined) { - start = _limitValue(Math.min( - _lookupByKey(_parsed, iScale.axis, min).lo, - animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), - 0, pointCount - 1); - } - if (maxDefined) { - count = _limitValue(Math.max( - _lookupByKey(_parsed, iScale.axis, max).hi + 1, - animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max)).hi + 1), - 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; -} - -class PolarAreaController extends DatasetController { - constructor(chart, datasetIndex) { - super(chart, datasetIndex); - this.innerRadius = undefined; - this.outerRadius = undefined; - } - getLabelAndValue(index) { - const meta = this._cachedMeta; - const chart = this.chart; - const labels = chart.data.labels || []; - const value = formatNumber(meta._parsed[index].r, chart.options.locale); - return { - label: labels[index] || '', - value, - }; - } - update(mode) { - const arcs = this._cachedMeta.data; - this._updateRadius(); - this.updateElements(arcs, 0, arcs.length, mode); - } - _updateRadius() { - const chart = this.chart; - const chartArea = chart.chartArea; - const opts = chart.options; - const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); - const outerRadius = Math.max(minSize / 2, 0); - const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); - const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount(); - this.outerRadius = outerRadius - (radiusLength * this.index); - this.innerRadius = this.outerRadius - radiusLength; - } - updateElements(arcs, start, count, mode) { - const reset = mode === 'reset'; - const chart = this.chart; - const dataset = this.getDataset(); - const opts = chart.options; - const animationOpts = opts.animation; - const scale = this._cachedMeta.rScale; - const centerX = scale.xCenter; - const centerY = scale.yCenter; - const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI; - let angle = datasetStartAngle; - let i; - const defaultAngle = 360 / this.countVisibleElements(); - for (i = 0; i < start; ++i) { - angle += this._computeAngle(i, mode, defaultAngle); - } - for (i = start; i < start + count; i++) { - const arc = arcs[i]; - let startAngle = angle; - let endAngle = angle + this._computeAngle(i, mode, defaultAngle); - let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0; - angle = endAngle; - if (reset) { - if (animationOpts.animateScale) { - outerRadius = 0; - } - if (animationOpts.animateRotate) { - startAngle = endAngle = datasetStartAngle; - } - } - const properties = { - x: centerX, - y: centerY, - innerRadius: 0, - outerRadius, - startAngle, - endAngle, - options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode) - }; - this.updateElement(arc, i, properties, mode); - } - } - countVisibleElements() { - const dataset = this.getDataset(); - const meta = this._cachedMeta; - let count = 0; - meta.data.forEach((element, index) => { - if (!isNaN(dataset.data[index]) && this.chart.getDataVisibility(index)) { - count++; - } - }); - return count; - } - _computeAngle(index, mode, defaultAngle) { - return this.chart.getDataVisibility(index) - ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) - : 0; - } -} -PolarAreaController.id = 'polarArea'; -PolarAreaController.defaults = { - dataElementType: 'arc', - animation: { - animateRotate: true, - animateScale: true - }, - animations: { - numbers: { - type: 'number', - properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius'] - }, - }, - indexAxis: 'r', - startAngle: 0, -}; -PolarAreaController.overrides = { - aspectRatio: 1, - plugins: { - legend: { - labels: { - generateLabels(chart) { - const data = chart.data; - if (data.labels.length && data.datasets.length) { - const {labels: {pointStyle}} = chart.legend.options; - return data.labels.map((label, i) => { - const meta = chart.getDatasetMeta(0); - const style = meta.controller.getStyle(i); - return { - text: label, - fillStyle: style.backgroundColor, - strokeStyle: style.borderColor, - lineWidth: style.borderWidth, - pointStyle: pointStyle, - hidden: !chart.getDataVisibility(i), - index: i - }; - }); - } - return []; - } - }, - onClick(e, legendItem, legend) { - legend.chart.toggleDataVisibility(legendItem.index); - legend.chart.update(); - } - }, - tooltip: { - callbacks: { - title() { - return ''; - }, - label(context) { - return context.chart.data.labels[context.dataIndex] + ': ' + context.formattedValue; - } - } - } - }, - scales: { - r: { - type: 'radialLinear', - angleLines: { - display: false - }, - beginAtZero: true, - grid: { - circular: true - }, - pointLabels: { - display: false - }, - startAngle: 0 - } - } -}; - -class PieController extends DoughnutController { -} -PieController.id = 'pie'; -PieController.defaults = { - cutout: 0, - rotation: 0, - circumference: 360, - radius: '100%' -}; - -class RadarController extends DatasetController { - getLabelAndValue(index) { - const vScale = this._cachedMeta.vScale; - const parsed = this.getParsed(index); - return { - label: vScale.getLabels()[index], - value: '' + vScale.getLabelForValue(parsed[vScale.axis]) - }; - } - update(mode) { - const meta = this._cachedMeta; - const line = meta.dataset; - const points = meta.data || []; - const labels = meta.iScale.getLabels(); - line.points = points; - if (mode !== 'resize') { - const options = this.resolveDatasetElementOptions(mode); - if (!this.options.showLine) { - options.borderWidth = 0; - } - const properties = { - _loop: true, - _fullLoop: labels.length === points.length, - options - }; - this.updateElement(line, undefined, properties, mode); - } - this.updateElements(points, 0, points.length, mode); - } - updateElements(points, start, count, mode) { - const dataset = this.getDataset(); - const scale = this._cachedMeta.rScale; - const reset = mode === 'reset'; - for (let i = start; i < start + count; i++) { - const point = points[i]; - const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode); - const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]); - const x = reset ? scale.xCenter : pointPosition.x; - const y = reset ? scale.yCenter : pointPosition.y; - const properties = { - x, - y, - angle: pointPosition.angle, - skip: isNaN(x) || isNaN(y), - options - }; - this.updateElement(point, i, properties, mode); - } - } -} -RadarController.id = 'radar'; -RadarController.defaults = { - datasetElementType: 'line', - dataElementType: 'point', - indexAxis: 'r', - showLine: true, - elements: { - line: { - fill: 'start' - } - }, -}; -RadarController.overrides = { - aspectRatio: 1, - scales: { - r: { - type: 'radialLinear', - } - } -}; - -class ScatterController extends LineController { -} -ScatterController.id = 'scatter'; -ScatterController.defaults = { - showLine: false, - fill: false -}; -ScatterController.overrides = { - interaction: { - mode: 'point' - }, - plugins: { - tooltip: { - callbacks: { - title() { - return ''; - }, - label(item) { - return '(' + item.label + ', ' + item.formattedValue + ')'; - } - } - } - }, - scales: { - x: { - type: 'linear' - }, - y: { - type: 'linear' - } - } -}; - -var controllers = /*#__PURE__*/Object.freeze({ -__proto__: null, -BarController: BarController, -BubbleController: BubbleController, -DoughnutController: DoughnutController, -LineController: LineController, -PolarAreaController: PolarAreaController, -PieController: PieController, -RadarController: RadarController, -ScatterController: ScatterController -}); - -function clipArc(ctx, element, endAngle) { - const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element; - let angleMargin = pixelMargin / outerRadius; - ctx.beginPath(); - ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin); - if (innerRadius > pixelMargin) { - angleMargin = pixelMargin / innerRadius; - ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true); - } else { - ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI); - } - ctx.closePath(); - ctx.clip(); -} -function toRadiusCorners(value) { - return _readValueToProps(value, ['outerStart', 'outerEnd', 'innerStart', 'innerEnd']); -} -function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) { - const o = toRadiusCorners(arc.options.borderRadius); - const halfThickness = (outerRadius - innerRadius) / 2; - const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2); - const computeOuterLimit = (val) => { - const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2; - return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit)); - }; - return { - outerStart: computeOuterLimit(o.outerStart), - outerEnd: computeOuterLimit(o.outerEnd), - innerStart: _limitValue(o.innerStart, 0, innerLimit), - innerEnd: _limitValue(o.innerEnd, 0, innerLimit), - }; -} -function rThetaToXY(r, theta, x, y) { - return { - x: x + r * Math.cos(theta), - y: y + r * Math.sin(theta), - }; -} -function pathArc(ctx, element, offset, spacing, end) { - const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element; - const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0); - const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0; - let spacingOffset = 0; - const alpha = end - start; - if (spacing) { - const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0; - const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0; - const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2; - const adjustedAngle = avNogSpacingRadius !== 0 ? (alpha * avNogSpacingRadius) / (avNogSpacingRadius + spacing) : alpha; - spacingOffset = (alpha - adjustedAngle) / 2; - } - const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius; - const angleOffset = (alpha - beta) / 2; - const startAngle = start + angleOffset + spacingOffset; - const endAngle = end - angleOffset - spacingOffset; - const {outerStart, outerEnd, innerStart, innerEnd} = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle); - const outerStartAdjustedRadius = outerRadius - outerStart; - const outerEndAdjustedRadius = outerRadius - outerEnd; - const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius; - const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius; - const innerStartAdjustedRadius = innerRadius + innerStart; - const innerEndAdjustedRadius = innerRadius + innerEnd; - const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius; - const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius; - ctx.beginPath(); - ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle); - if (outerEnd > 0) { - const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI); - } - const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y); - ctx.lineTo(p4.x, p4.y); - if (innerEnd > 0) { - const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI); - } - ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true); - if (innerStart > 0) { - const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI); - } - const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y); - ctx.lineTo(p8.x, p8.y); - if (outerStart > 0) { - const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y); - ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle); - } - ctx.closePath(); -} -function drawArc(ctx, element, offset, spacing) { - const {fullCircles, startAngle, circumference} = element; - let endAngle = element.endAngle; - if (fullCircles) { - pathArc(ctx, element, offset, spacing, startAngle + TAU); - for (let i = 0; i < fullCircles; ++i) { - ctx.fill(); - } - if (!isNaN(circumference)) { - endAngle = startAngle + circumference % TAU; - if (circumference % TAU === 0) { - endAngle += TAU; - } - } - } - pathArc(ctx, element, offset, spacing, endAngle); - ctx.fill(); - return endAngle; -} -function drawFullCircleBorders(ctx, element, inner) { - const {x, y, startAngle, pixelMargin, fullCircles} = element; - const outerRadius = Math.max(element.outerRadius - pixelMargin, 0); - const innerRadius = element.innerRadius + pixelMargin; - let i; - if (inner) { - clipArc(ctx, element, startAngle + TAU); - } - ctx.beginPath(); - ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true); - for (i = 0; i < fullCircles; ++i) { - ctx.stroke(); - } - ctx.beginPath(); - ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU); - for (i = 0; i < fullCircles; ++i) { - ctx.stroke(); - } -} -function drawBorder(ctx, element, offset, spacing, endAngle) { - const {options} = element; - const {borderWidth, borderJoinStyle} = options; - const inner = options.borderAlign === 'inner'; - if (!borderWidth) { - return; - } - if (inner) { - ctx.lineWidth = borderWidth * 2; - ctx.lineJoin = borderJoinStyle || 'round'; - } else { - ctx.lineWidth = borderWidth; - ctx.lineJoin = borderJoinStyle || 'bevel'; - } - if (element.fullCircles) { - drawFullCircleBorders(ctx, element, inner); - } - if (inner) { - clipArc(ctx, element, endAngle); - } - pathArc(ctx, element, offset, spacing, endAngle); - ctx.stroke(); -} -class ArcElement extends Element { - constructor(cfg) { - super(); - this.options = undefined; - this.circumference = undefined; - this.startAngle = undefined; - this.endAngle = undefined; - this.innerRadius = undefined; - this.outerRadius = undefined; - this.pixelMargin = 0; - this.fullCircles = 0; - if (cfg) { - Object.assign(this, cfg); - } - } - inRange(chartX, chartY, useFinalPosition) { - const point = this.getProps(['x', 'y'], useFinalPosition); - const {angle, distance} = getAngleFromPoint(point, {x: chartX, y: chartY}); - const {startAngle, endAngle, innerRadius, outerRadius, circumference} = this.getProps([ - 'startAngle', - 'endAngle', - 'innerRadius', - 'outerRadius', - 'circumference' - ], useFinalPosition); - const rAdjust = this.options.spacing / 2; - const _circumference = valueOrDefault(circumference, endAngle - startAngle); - const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle); - const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust); - return (betweenAngles && withinRadius); - } - getCenterPoint(useFinalPosition) { - const {x, y, startAngle, endAngle, innerRadius, outerRadius} = this.getProps([ - 'x', - 'y', - 'startAngle', - 'endAngle', - 'innerRadius', - 'outerRadius', - 'circumference', - ], useFinalPosition); - const {offset, spacing} = this.options; - const halfAngle = (startAngle + endAngle) / 2; - const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2; - return { - x: x + Math.cos(halfAngle) * halfRadius, - y: y + Math.sin(halfAngle) * halfRadius - }; - } - tooltipPosition(useFinalPosition) { - return this.getCenterPoint(useFinalPosition); - } - draw(ctx) { - const {options, circumference} = this; - const offset = (options.offset || 0) / 2; - const spacing = (options.spacing || 0) / 2; - this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0; - this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0; - if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) { - return; - } - ctx.save(); - let radiusOffset = 0; - if (offset) { - radiusOffset = offset / 2; - const halfAngle = (this.startAngle + this.endAngle) / 2; - ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset); - if (this.circumference >= PI) { - radiusOffset = offset; - } - } - ctx.fillStyle = options.backgroundColor; - ctx.strokeStyle = options.borderColor; - const endAngle = drawArc(ctx, this, radiusOffset, spacing); - drawBorder(ctx, this, radiusOffset, spacing, endAngle); - ctx.restore(); - } -} -ArcElement.id = 'arc'; -ArcElement.defaults = { - borderAlign: 'center', - borderColor: '#fff', - borderJoinStyle: undefined, - borderRadius: 0, - borderWidth: 2, - offset: 0, - spacing: 0, - angle: undefined, -}; -ArcElement.defaultRoutes = { - backgroundColor: 'backgroundColor' -}; - -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 = (index) => (start + (reverse ? ilen - index : index)) % 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 = undefined; - this._chart = undefined; - this._loop = undefined; - this._fullLoop = undefined; - this._path = undefined; - this._points = undefined; - this._segments = undefined; - this._decimated = false; - this._pointsUpdated = false; - this._datasetIndex = undefined; - 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 = undefined; - } - } -} -LineElement.id = 'line'; -LineElement.defaults = { - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0, - borderJoinStyle: 'miter', - borderWidth: 3, - capBezierPoints: true, - cubicInterpolationMode: 'default', - fill: false, - spanGaps: false, - stepped: false, - tension: 0, -}; -LineElement.defaultRoutes = { - backgroundColor: 'backgroundColor', - borderColor: 'borderColor' -}; -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(); - this.options = undefined; - this.parsed = undefined; - this.skip = undefined; - this.stop = undefined; - 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; - } -} -PointElement.id = 'point'; -PointElement.defaults = { - borderWidth: 1, - hitRadius: 1, - hoverBorderWidth: 1, - hoverRadius: 4, - pointStyle: 'circle', - radius: 3, - rotation: 0 -}; -PointElement.defaultRoutes = { - backgroundColor: 'backgroundColor', - borderColor: 'borderColor' -}; - -function getBarBounds(bar, useFinalPosition) { - const {x, y, base, width, height} = bar.getProps(['x', 'y', 'base', 'width', 'height'], useFinalPosition); - let left, right, top, bottom, half; - if (bar.horizontal) { - half = height / 2; - left = Math.min(x, base); - right = Math.max(x, base); - top = y - half; - bottom = y + half; - } else { - half = width / 2; - left = x - half; - right = x + half; - top = Math.min(y, base); - bottom = Math.max(y, base); - } - return {left, top, right, bottom}; -} -function skipOrLimit(skip, value, min, max) { - return skip ? 0 : _limitValue(value, min, max); -} -function parseBorderWidth(bar, maxW, maxH) { - const value = bar.options.borderWidth; - const skip = bar.borderSkipped; - const o = toTRBL(value); - return { - t: skipOrLimit(skip.top, o.top, 0, maxH), - r: skipOrLimit(skip.right, o.right, 0, maxW), - b: skipOrLimit(skip.bottom, o.bottom, 0, maxH), - l: skipOrLimit(skip.left, o.left, 0, maxW) - }; -} -function parseBorderRadius(bar, maxW, maxH) { - const {enableBorderRadius} = bar.getProps(['enableBorderRadius']); - const value = bar.options.borderRadius; - const o = toTRBLCorners(value); - const maxR = Math.min(maxW, maxH); - const skip = bar.borderSkipped; - const enableBorder = enableBorderRadius || isObject(value); - return { - topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR), - topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR), - bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR), - bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR) - }; -} -function boundingRects(bar) { - const bounds = getBarBounds(bar); - const width = bounds.right - bounds.left; - const height = bounds.bottom - bounds.top; - const border = parseBorderWidth(bar, width / 2, height / 2); - const radius = parseBorderRadius(bar, width / 2, height / 2); - return { - outer: { - x: bounds.left, - y: bounds.top, - w: width, - h: height, - radius - }, - inner: { - x: bounds.left + border.l, - y: bounds.top + border.t, - w: width - border.l - border.r, - h: height - border.t - border.b, - radius: { - topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)), - topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)), - bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)), - bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)), - } - } - }; -} -function inRange(bar, x, y, useFinalPosition) { - const skipX = x === null; - const skipY = y === null; - const skipBoth = skipX && skipY; - const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition); - return bounds - && (skipX || _isBetween(x, bounds.left, bounds.right)) - && (skipY || _isBetween(y, bounds.top, bounds.bottom)); -} -function hasRadius(radius) { - return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight; -} -function addNormalRectPath(ctx, rect) { - ctx.rect(rect.x, rect.y, rect.w, rect.h); -} -function inflateRect(rect, amount, refRect = {}) { - const x = rect.x !== refRect.x ? -amount : 0; - const y = rect.y !== refRect.y ? -amount : 0; - const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x; - const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y; - return { - x: rect.x + x, - y: rect.y + y, - w: rect.w + w, - h: rect.h + h, - radius: rect.radius - }; -} -class BarElement extends Element { - constructor(cfg) { - super(); - this.options = undefined; - this.horizontal = undefined; - this.base = undefined; - this.width = undefined; - this.height = undefined; - this.inflateAmount = undefined; - if (cfg) { - Object.assign(this, cfg); - } - } - draw(ctx) { - const {inflateAmount, options: {borderColor, backgroundColor}} = this; - const {inner, outer} = boundingRects(this); - const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath; - ctx.save(); - if (outer.w !== inner.w || outer.h !== inner.h) { - ctx.beginPath(); - addRectPath(ctx, inflateRect(outer, inflateAmount, inner)); - ctx.clip(); - addRectPath(ctx, inflateRect(inner, -inflateAmount, outer)); - ctx.fillStyle = borderColor; - ctx.fill('evenodd'); - } - ctx.beginPath(); - addRectPath(ctx, inflateRect(inner, inflateAmount)); - ctx.fillStyle = backgroundColor; - ctx.fill(); - ctx.restore(); - } - inRange(mouseX, mouseY, useFinalPosition) { - return inRange(this, mouseX, mouseY, useFinalPosition); - } - inXRange(mouseX, useFinalPosition) { - return inRange(this, mouseX, null, useFinalPosition); - } - inYRange(mouseY, useFinalPosition) { - return inRange(this, null, mouseY, useFinalPosition); - } - getCenterPoint(useFinalPosition) { - const {x, y, base, horizontal} = this.getProps(['x', 'y', 'base', 'horizontal'], useFinalPosition); - return { - x: horizontal ? (x + base) / 2 : x, - y: horizontal ? y : (y + base) / 2 - }; - } - getRange(axis) { - return axis === 'x' ? this.width / 2 : this.height / 2; - } -} -BarElement.id = 'bar'; -BarElement.defaults = { - borderSkipped: 'start', - borderWidth: 0, - borderRadius: 0, - inflateAmount: 'auto', - pointStyle: undefined -}; -BarElement.defaultRoutes = { - backgroundColor: 'backgroundColor', - borderColor: 'borderColor' -}; - -var elements = /*#__PURE__*/Object.freeze({ -__proto__: null, -ArcElement: ArcElement, -LineElement: LineElement, -PointElement: PointElement, -BarElement: BarElement -}); - -function lttbDecimation(data, start, count, availableWidth, options) { - const samples = options.samples || availableWidth; - if (samples >= count) { - return data.slice(start, start + count); - } - const decimated = []; - const bucketWidth = (count - 2) / (samples - 2); - let sampledIndex = 0; - const endIndex = start + count - 1; - let a = start; - let i, maxAreaPoint, maxArea, area, nextA; - decimated[sampledIndex++] = data[a]; - for (i = 0; i < samples - 2; i++) { - let avgX = 0; - let avgY = 0; - let j; - const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start; - const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start; - const avgRangeLength = avgRangeEnd - avgRangeStart; - for (j = avgRangeStart; j < avgRangeEnd; j++) { - avgX += data[j].x; - avgY += data[j].y; - } - avgX /= avgRangeLength; - avgY /= avgRangeLength; - const rangeOffs = Math.floor(i * bucketWidth) + 1 + start; - const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start; - const {x: pointAx, y: pointAy} = data[a]; - maxArea = area = -1; - for (j = rangeOffs; j < rangeTo; j++) { - area = 0.5 * Math.abs( - (pointAx - avgX) * (data[j].y - pointAy) - - (pointAx - data[j].x) * (avgY - pointAy) - ); - if (area > maxArea) { - maxArea = area; - maxAreaPoint = data[j]; - nextA = j; - } - } - decimated[sampledIndex++] = maxAreaPoint; - a = nextA; - } - decimated[sampledIndex++] = data[endIndex]; - return decimated; -} -function minMaxDecimation(data, start, count, availableWidth) { - let avgX = 0; - let countX = 0; - let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY; - const decimated = []; - const endIndex = start + count - 1; - const xMin = data[start].x; - const xMax = data[endIndex].x; - const dx = xMax - xMin; - for (i = start; i < start + count; ++i) { - point = data[i]; - x = (point.x - xMin) / dx * availableWidth; - y = point.y; - const truncX = x | 0; - if (truncX === prevX) { - if (y < minY) { - minY = y; - minIndex = i; - } else if (y > maxY) { - maxY = y; - maxIndex = i; - } - avgX = (countX * avgX + point.x) / ++countX; - } else { - const lastIndex = i - 1; - if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) { - const intermediateIndex1 = Math.min(minIndex, maxIndex); - const intermediateIndex2 = Math.max(minIndex, maxIndex); - if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) { - decimated.push({ - ...data[intermediateIndex1], - x: avgX, - }); - } - if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) { - decimated.push({ - ...data[intermediateIndex2], - x: avgX - }); - } - } - if (i > 0 && lastIndex !== startIndex) { - decimated.push(data[lastIndex]); - } - decimated.push(point); - prevX = truncX; - countX = 0; - minY = maxY = y; - minIndex = maxIndex = startIndex = i; - } - } - return decimated; -} -function cleanDecimatedDataset(dataset) { - if (dataset._decimated) { - const data = dataset._data; - delete dataset._decimated; - delete dataset._data; - Object.defineProperty(dataset, 'data', {value: data}); - } -} -function cleanDecimatedData(chart) { - chart.data.datasets.forEach((dataset) => { - cleanDecimatedDataset(dataset); - }); -} -function getStartAndCountOfVisiblePointsSimplified(meta, points) { - const pointCount = points.length; - let start = 0; - let count; - const {iScale} = meta; - const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); - if (minDefined) { - start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1); - } - if (maxDefined) { - count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start; - } else { - count = pointCount - start; - } - return {start, count}; -} -var plugin_decimation = { - id: 'decimation', - defaults: { - algorithm: 'min-max', - enabled: false, - }, - beforeElementsUpdate: (chart, args, options) => { - if (!options.enabled) { - cleanDecimatedData(chart); - return; - } - const availableWidth = chart.width; - chart.data.datasets.forEach((dataset, datasetIndex) => { - const {_data, indexAxis} = dataset; - const meta = chart.getDatasetMeta(datasetIndex); - const data = _data || dataset.data; - if (resolve([indexAxis, chart.options.indexAxis]) === 'y') { - return; - } - if (meta.type !== 'line') { - return; - } - const xAxis = chart.scales[meta.xAxisID]; - if (xAxis.type !== 'linear' && xAxis.type !== 'time') { - return; - } - if (chart.options.parsing) { - return; - } - let {start, count} = getStartAndCountOfVisiblePointsSimplified(meta, data); - const threshold = options.threshold || 4 * availableWidth; - if (count <= threshold) { - cleanDecimatedDataset(dataset); - return; - } - if (isNullOrUndef(_data)) { - dataset._data = data; - delete dataset.data; - Object.defineProperty(dataset, 'data', { - configurable: true, - enumerable: true, - get: function() { - return this._decimated; - }, - set: function(d) { - this._data = d; - } - }); - } - let decimated; - switch (options.algorithm) { - case 'lttb': - decimated = lttbDecimation(data, start, count, availableWidth, options); - break; - case 'min-max': - decimated = minMaxDecimation(data, start, count, availableWidth); - break; - default: - throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`); - } - dataset._decimated = decimated; - }); - }, - destroy(chart) { - cleanDecimatedData(chart); - } -}; - -function getLineByIndex(chart, index) { - const meta = chart.getDatasetMeta(index); - const visible = meta && chart.isDatasetVisible(index); - return visible ? meta.dataset : null; -} -function parseFillOption(line) { - const options = line.options; - const fillOption = options.fill; - let fill = valueOrDefault(fillOption && fillOption.target, fillOption); - if (fill === undefined) { - fill = !!options.backgroundColor; - } - if (fill === false || fill === null) { - return false; - } - if (fill === true) { - return 'origin'; - } - return fill; -} -function decodeFill(line, index, count) { - const fill = parseFillOption(line); - if (isObject(fill)) { - return isNaN(fill.value) ? false : fill; - } - let target = parseFloat(fill); - if (isNumberFinite(target) && Math.floor(target) === target) { - if (fill[0] === '-' || fill[0] === '+') { - target = index + target; - } - if (target === index || target < 0 || target >= count) { - return false; - } - return target; - } - return ['origin', 'start', 'end', 'stack', 'shape'].indexOf(fill) >= 0 && fill; -} -function computeLinearBoundary(source) { - const {scale = {}, fill} = source; - let target = null; - let horizontal; - if (fill === 'start') { - target = scale.bottom; - } else if (fill === 'end') { - target = scale.top; - } else if (isObject(fill)) { - target = scale.getPixelForValue(fill.value); - } else if (scale.getBasePixel) { - target = scale.getBasePixel(); - } - if (isNumberFinite(target)) { - horizontal = scale.isHorizontal(); - return { - x: horizontal ? target : null, - y: horizontal ? null : target - }; - } - return null; -} -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 computeCircularBoundary(source) { - const {scale, fill} = source; - const options = scale.options; - const length = scale.getLabels().length; - const target = []; - const start = options.reverse ? scale.max : scale.min; - const end = options.reverse ? scale.min : scale.max; - let i, center, value; - if (fill === 'start') { - value = start; - } else if (fill === 'end') { - value = end; - } else if (isObject(fill)) { - value = fill.value; - } else { - value = scale.getBaseValue(); - } - if (options.grid.circular) { - center = scale.getPointPositionForValue(0, start); - return new simpleArc({ - x: center.x, - y: center.y, - radius: scale.getDistanceFromCenterForValue(value) - }); - } - for (i = 0; i < length; ++i) { - target.push(scale.getPointPositionForValue(i, value)); - } - return target; -} -function computeBoundary(source) { - const scale = source.scale || {}; - if (scale.getPointPositionForValue) { - return computeCircularBoundary(source); - } - return computeLinearBoundary(source); -} -function findSegmentEnd(start, end, points) { - for (;end > start; end--) { - const point = points[end]; - if (!isNaN(point.x) && !isNaN(point.y)) { - break; - } - } - return 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 buildStackLine(source) { - const {scale, index, line} = source; - const points = []; - const segments = line.segments; - const sourcePoints = line.points; - const linesBelow = getLinesBelow(scale, index); - 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, index) { - const below = []; - const metas = scale.getMatchingVisibleMetas('line'); - for (let i = 0; i < metas.length; i++) { - const meta = metas[i]; - if (meta.index === index) { - 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}; -} -function getTarget(source) { - const {chart, fill, line} = source; - if (isNumberFinite(fill)) { - return getLineByIndex(chart, fill); - } - if (fill === 'stack') { - return buildStackLine(source); - } - if (fill === 'shape') { - return true; - } - const boundary = computeBoundary(source); - if (boundary instanceof simpleArc) { - return boundary; - } - return createBoundaryLine(boundary, line); -} -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 resolveTarget(sources, index, propagate) { - const source = sources[index]; - let fill = source.fill; - const visited = [index]; - let target; - if (!propagate) { - return fill; - } - while (fill !== false && visited.indexOf(fill) === -1) { - if (!isNumberFinite(fill)) { - return fill; - } - target = sources[fill]; - if (!target) { - return false; - } - if (target.visible) { - return fill; - } - visited.push(fill); - fill = target.fill; - } - return false; -} -function _clip(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 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 _getEdge(a, b, prop, fn) { - if (a && b) { - return fn(a[prop], b[prop]); - } - return a ? a[prop] : b ? b[prop] : 0; -} -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 clipBounds(ctx, scale, bounds) { - const {top, bottom} = scale.chart.chartArea; - const {property, start, end} = bounds || {}; - if (property === 'x') { - ctx.beginPath(); - ctx.rect(start, top, end - start, bottom - top); - ctx.clip(); - } -} -function interpolatedLineTo(ctx, target, point, property) { - const interpolatedPoint = target.interpolate(point, property); - if (interpolatedPoint) { - ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y); - } -} -function _fill(ctx, cfg) { - const {line, target, property, color, scale} = cfg; - const segments = _segments(line, target, property); - for (const {source: src, target: tgt, start, end} of segments) { - const {style: {backgroundColor = color} = {}} = src; - const notShape = target !== true; - ctx.save(); - ctx.fillStyle = backgroundColor; - clipBounds(ctx, scale, 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 doFill(ctx, cfg) { - const {line, target, above, below, area, scale} = cfg; - const property = line._loop ? 'angle' : cfg.axis; - ctx.save(); - if (property === 'x' && below !== above) { - _clip(ctx, target, area.top); - _fill(ctx, {line, target, color: above, scale, property}); - ctx.restore(); - ctx.save(); - _clip(ctx, target, area.bottom); - } - _fill(ctx, {line, target, color: below, scale, property}); - ctx.restore(); -} -function drawfill(ctx, source, area) { - const target = getTarget(source); - const {line, scale, axis} = source; - const lineOpts = line.options; - const fillOption = lineOpts.fill; - const color = lineOpts.backgroundColor; - const {above = color, below = color} = fillOption || {}; - if (target && line.points.length) { - clipArea(ctx, area); - doFill(ctx, {line, target, above, below, area, scale, axis}); - unclipArea(ctx); - } -} -var plugin_filler = { - 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 draw = 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 (draw) { - 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 (source) { - drawfill(chart.ctx, source, chart.chartArea); - } - } - }, - beforeDatasetDraw(chart, args, options) { - const source = args.meta.$filler; - if (!source || source.fill === false || 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 = 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 = undefined; - this.columnSizes = undefined; - this.lineWidths = undefined; - this.maxHeight = undefined; - this.maxWidth = undefined; - this.top = undefined; - this.bottom = undefined; - this.left = undefined; - this.right = undefined; - this.height = undefined; - this.width = undefined; - this._margins = undefined; - this.position = undefined; - this.weight = undefined; - this.fullSize = undefined; - } - 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, fontSize, boxWidth, itemHeight) + 10; - } - this.width = Math.min(width, options.maxWidth || this.maxWidth); - this.height = Math.min(height, options.maxHeight || this.maxHeight); - } - _fitRows(titleHeight, fontSize, boxWidth, itemHeight) { - const {ctx, 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, fontSize, 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 = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - 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 {color: fontColor, 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: boxWidth * Math.SQRT2 / 2, - pointStyle: legendItem.pointStyle, - rotation: legendItem.rotation, - borderWidth: lineWidth - }; - const centerX = rtlHelper.xPlus(x, boxWidth / 2); - const centerY = y + halfFontSize; - drawPoint(ctx, drawOptions, centerX, centerY); - } 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 || fontColor; - ctx.fillStyle = legendItem.fontColor || 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 { - 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') { - 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 isListened(type, opts) { - if (type === 'mousemove' && (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: 1000, - onClick(e, legendItem, legend) { - const index = legendItem.datasetIndex; - const ci = legend.chart; - if (ci.isDatasetVisible(index)) { - ci.hide(index); - legendItem.hidden = true; - } else { - ci.show(index); - 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}} = chart.legend.options; - return chart._getSortedDatasetMetas().map((meta) => { - const style = meta.controller.getStyle(usePointStyle ? 0 : undefined); - const borderWidth = toPadding(style.borderWidth); - return { - text: datasets[meta.index].label, - fillStyle: style.backgroundColor, - fontColor: color, - hidden: !meta.visible, - lineCap: style.borderCapStyle, - lineDash: style.borderDash, - lineDashOffset: style.borderDashOffset, - lineJoin: style.borderJoinStyle, - lineWidth: (borderWidth.width + borderWidth.height) / 4, - strokeStyle: style.borderColor, - pointStyle: pointStyle || style.pointStyle, - rotation: style.rotation, - textAlign: textAlign || style.textAlign, - borderRadius: 0, - datasetIndex: meta.index - }; - }, this); - } - }, - title: { - color: (ctx) => ctx.chart.options.color, - display: false, - position: 'center', - text: '', - } - }, - descriptors: { - _scriptable: (name) => !name.startsWith('on'), - labels: { - _scriptable: (name) => !['generateLabels', 'filter', 'sort'].includes(name), - } - }, -}; - -class Title extends Element { - constructor(config) { - super(); - this.chart = config.chart; - this.options = config.options; - this.ctx = config.ctx; - this._padding = undefined; - this.top = undefined; - this.bottom = undefined; - this.left = undefined; - this.right = undefined; - this.width = undefined; - this.height = undefined; - this.position = undefined; - this.weight = undefined; - this.fullSize = undefined; - } - update(maxWidth, maxHeight) { - const opts = this.options; - this.left = 0; - this.top = 0; - if (!opts.display) { - this.width = this.height = this.right = this.bottom = 0; - return; - } - this.width = this.right = maxWidth; - this.height = this.bottom = maxHeight; - const lineCount = isArray(opts.text) ? opts.text.length : 1; - this._padding = toPadding(opts.padding); - const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height; - if (this.isHorizontal()) { - this.height = textSize; - } else { - this.width = textSize; - } - } - isHorizontal() { - const pos = this.options.position; - return pos === 'top' || pos === 'bottom'; - } - _drawArgs(offset) { - const {top, left, bottom, right, options} = this; - const align = options.align; - let rotation = 0; - let maxWidth, titleX, titleY; - if (this.isHorizontal()) { - titleX = _alignStartEnd(align, left, right); - titleY = top + offset; - maxWidth = right - left; - } else { - if (options.position === 'left') { - titleX = left + offset; - titleY = _alignStartEnd(align, bottom, top); - rotation = PI * -0.5; - } else { - titleX = right - offset; - titleY = _alignStartEnd(align, top, bottom); - rotation = PI * 0.5; - } - maxWidth = bottom - top; - } - return {titleX, titleY, maxWidth, rotation}; - } - draw() { - const ctx = this.ctx; - const opts = this.options; - if (!opts.display) { - return; - } - const fontOpts = toFont(opts.font); - const lineHeight = fontOpts.lineHeight; - const offset = lineHeight / 2 + this._padding.top; - const {titleX, titleY, maxWidth, rotation} = this._drawArgs(offset); - renderText(ctx, opts.text, 0, 0, fontOpts, { - color: opts.color, - maxWidth, - rotation, - textAlign: _toLeftRightCenter(opts.align), - textBaseline: 'middle', - translation: [titleX, titleY], - }); - } -} -function createTitle(chart, titleOpts) { - const title = new Title({ - ctx: chart.ctx, - options: titleOpts, - chart - }); - layouts.configure(chart, title, titleOpts); - layouts.addBox(chart, title); - chart.titleBlock = title; -} -var plugin_title = { - id: 'title', - _element: Title, - start(chart, _args, options) { - createTitle(chart, options); - }, - stop(chart) { - const titleBlock = chart.titleBlock; - layouts.removeBox(chart, titleBlock); - delete chart.titleBlock; - }, - beforeUpdate(chart, _args, options) { - const title = chart.titleBlock; - layouts.configure(chart, title, options); - title.options = options; - }, - defaults: { - align: 'center', - display: false, - font: { - weight: 'bold', - }, - fullSize: true, - padding: 10, - position: 'top', - text: '', - weight: 2000 - }, - defaultRoutes: { - color: 'color' - }, - descriptors: { - _scriptable: true, - _indexable: false, - }, -}; - -const map = new WeakMap(); -var plugin_subtitle = { - id: 'subtitle', - start(chart, _args, options) { - const title = new Title({ - ctx: chart.ctx, - options, - chart - }); - layouts.configure(chart, title, options); - layouts.addBox(chart, title); - map.set(chart, title); - }, - stop(chart) { - layouts.removeBox(chart, map.get(chart)); - map.delete(chart); - }, - beforeUpdate(chart, _args, options) { - const title = map.get(chart); - layouts.configure(chart, title, options); - title.options = options; - }, - defaults: { - align: 'center', - display: false, - font: { - weight: 'normal', - }, - fullSize: true, - padding: 0, - position: 'top', - text: '', - weight: 1500 - }, - defaultRoutes: { - color: 'color' - }, - descriptors: { - _scriptable: true, - _indexable: false, - }, -}; - -const positioners = { - average(items) { - if (!items.length) { - return false; - } - let i, len; - let x = 0; - 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(); - x += pos.x; - y += pos.y; - ++count; - } - } - return { - x: x / count, - 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} = item; - const controller = chart.getDatasetMeta(datasetIndex).controller; - const {label, value} = controller.getLabelAndValue(index); - return { - chart, - label, - parsed: controller.getParsed(index), - raw: chart.data.datasets[datasetIndex].data[index], - formattedValue: value, - dataset: controller.getDataset(), - dataIndex: index, - 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(callback) { - return pushOrConcat([], splitNewlines(callback)); -} -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; -} -class Tooltip extends Element { - constructor(config) { - super(); - this.opacity = 0; - this._active = []; - this._eventPosition = undefined; - this._size = undefined; - this._cachedAnimations = undefined; - this._tooltipItems = []; - this.$animations = undefined; - this.$context = undefined; - this.chart = config.chart || config._chart; - this._chart = this.chart; - this.options = config.options; - this.dataPoints = undefined; - this.title = undefined; - this.beforeBody = undefined; - this.body = undefined; - this.afterBody = undefined; - this.footer = undefined; - this.xAlign = undefined; - this.yAlign = undefined; - this.x = undefined; - this.y = undefined; - this.height = undefined; - this.width = undefined; - this.caretX = undefined; - this.caretY = undefined; - this.labelColors = undefined; - this.labelPointStyles = undefined; - this.labelTextColors = undefined; - } - initialize(options) { - this.options = options; - this._cachedAnimations = undefined; - this.$context = undefined; - } - _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 = callbacks.beforeTitle.apply(this, [context]); - const title = callbacks.title.apply(this, [context]); - const afterTitle = callbacks.afterTitle.apply(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(options.callbacks.beforeBody.apply(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(scoped.beforeLabel.call(this, context))); - pushOrConcat(bodyItem.lines, scoped.label.call(this, context)); - pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(this, context))); - bodyItems.push(bodyItem); - }); - return bodyItems; - } - getAfterBody(tooltipItems, options) { - return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this, [tooltipItems])); - } - getFooter(tooltipItems, options) { - const {callbacks} = options; - const beforeFooter = callbacks.beforeFooter.apply(this, [tooltipItems]); - const footer = callbacks.footer.apply(this, [tooltipItems]); - const afterFooter = callbacks.afterFooter.apply(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, index, array) => options.filter(element, index, 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(scoped.labelColor.call(this, context)); - labelPointStyles.push(scoped.labelPointStyle.call(this, context)); - labelTextColors.push(scoped.labelTextColor.call(this, context)); - }); - this.labelColors = labelColors; - this.labelPointStyles = labelPointStyles; - this.labelTextColors = labelTextColors; - this.dataPoints = tooltipItems; - 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 = undefined; - 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 labelColors = this.labelColors[i]; - const labelPointStyle = this.labelPointStyles[i]; - const {boxHeight, boxWidth, boxPadding} = 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 = labelColors.borderColor; - ctx.fillStyle = labelColors.backgroundColor; - drawPoint(ctx, drawOptions, centerX, centerY); - } else { - ctx.lineWidth = labelColors.borderWidth || 1; - ctx.strokeStyle = labelColors.borderColor; - ctx.setLineDash(labelColors.borderDash || []); - ctx.lineDashOffset = labelColors.borderDashOffset || 0; - const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth - boxPadding); - const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - boxPadding - 2); - const borderRadius = toTRBLCorners(labelColors.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 = labelColors.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 = labelColors.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); - } - } - } - 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}) => { - const meta = this.chart.getDatasetMeta(datasetIndex); - if (!meta) { - throw new Error('Cannot find a dataset at index ' + datasetIndex); - } - return { - datasetIndex, - element: meta.data[index], - index, - }; - }); - 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; - } - 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); - } -} -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; - const args = { - tooltip - }; - if (chart.notifyPlugins('beforeTooltipDraw', args) === false) { - return; - } - if (tooltip) { - 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: { - 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 - } - }, - 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'] -}; - -var plugins = /*#__PURE__*/Object.freeze({ -__proto__: null, -Decimation: plugin_decimation, -Filler: plugin_filler, -Legend: plugin_legend, -SubTitle: plugin_subtitle, -Title: plugin_title, -Tooltip: plugin_tooltip -}); - -const addIfString = (labels, raw, index, addedLabels) => { - if (typeof raw === 'string') { - index = labels.push(raw) - 1; - addedLabels.unshift({index, label: raw}); - } else if (isNaN(raw)) { - index = null; - } - return index; -}; -function findOrAddLabel(labels, raw, index, addedLabels) { - const first = labels.indexOf(raw); - if (first === -1) { - return addIfString(labels, raw, index, addedLabels); - } - const last = labels.lastIndexOf(raw); - return first !== last ? index : first; -} -const validIndex = (index, max) => index === null ? null : _limitValue(Math.round(index), 0, max); -class CategoryScale extends Scale { - constructor(cfg) { - super(cfg); - this._startValue = undefined; - this._valueRange = 0; - this._addedLabels = []; - } - init(scaleOptions) { - const added = this._addedLabels; - if (added.length) { - const labels = this.getLabels(); - for (const {index, label} of added) { - if (labels[index] === label) { - labels.splice(index, 1); - } - } - this._addedLabels = []; - } - super.init(scaleOptions); - } - parse(raw, index) { - if (isNullOrUndef(raw)) { - return null; - } - const labels = this.getLabels(); - index = isFinite(index) && labels[index] === raw ? index - : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels); - return validIndex(index, 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) { - const labels = this.getLabels(); - if (value >= 0 && value < labels.length) { - return labels[value]; - } - return 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(index) { - const ticks = this.ticks; - if (index < 0 || index > ticks.length - 1) { - return null; - } - return this.getPixelForValue(ticks[index].value); - } - getValueForPixel(pixel) { - return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange); - } - getBasePixel() { - return this.bottom; - } -} -CategoryScale.id = 'category'; -CategoryScale.defaults = { - ticks: { - callback: CategoryScale.prototype.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 / 1000)) { - 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 / 1000)) { - 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) { - ticks.push({value: Math.round((niceMin + j * spacing) * factor) / factor}); - } - 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)) || 0.001; - const length = 0.75 * minSpacing * ('' + value).length; - return Math.min(minSpacing / ratio, length); -} -class LinearScaleBase extends Scale { - constructor(cfg) { - super(cfg); - this.start = undefined; - this.end = undefined; - this._startValue = undefined; - this._endValue = undefined; - this._valueRange = 0; - } - parse(raw, index) { - 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 = 1; - if (max >= Number.MAX_SAFE_INTEGER || min <= Number.MIN_SAFE_INTEGER) { - offset = 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 > 1000) { - console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`); - maxTicks = 1000; - } - } 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)) || 0.001; - 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; - } -} -LinearScale.id = 'linear'; -LinearScale.defaults = { - ticks: { - callback: Ticks.formatters.numeric - } -}; - -function isMajor(tickVal) { - const remain = tickVal / (Math.pow(10, Math.floor(log10(tickVal)))); - return remain === 1; -} -function generateTicks(generationOptions, dataRange) { - const endExp = Math.floor(log10(dataRange.max)); - const endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); - const ticks = []; - let tickVal = finiteOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min)))); - let exp = Math.floor(log10(tickVal)); - let significand = Math.floor(tickVal / Math.pow(10, exp)); - let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; - do { - ticks.push({value: tickVal, major: isMajor(tickVal)}); - ++significand; - if (significand === 10) { - significand = 1; - ++exp; - precision = exp >= 0 ? 1 : precision; - } - tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; - } while (exp < endExp || (exp === endExp && significand < endSignificand)); - const lastTick = finiteOrDefault(generationOptions.max, tickVal); - ticks.push({value: lastTick, major: isMajor(tickVal)}); - return ticks; -} -class LogarithmicScale extends Scale { - constructor(cfg) { - super(cfg); - this.start = undefined; - this.end = undefined; - this._startValue = undefined; - this._valueRange = 0; - } - parse(raw, index) { - const value = LinearScaleBase.prototype.parse.apply(this, [raw, index]); - if (value === 0) { - this._zero = true; - return undefined; - } - return isNumberFinite(value) && value > 0 ? value : null; - } - determineDataLimits() { - const {min, max} = this.getMinMax(true); - this.min = isNumberFinite(min) ? Math.max(0, min) : null; - this.max = isNumberFinite(max) ? Math.max(0, max) : null; - if (this.options.beginAtZero) { - this._zero = true; - } - this.handleTickRangeOptions(); - } - handleTickRangeOptions() { - const {minDefined, maxDefined} = this.getUserBounds(); - let min = this.min; - let max = this.max; - const setMin = v => (min = minDefined ? min : v); - const setMax = v => (max = maxDefined ? max : v); - const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m); - if (min === max) { - if (min <= 0) { - setMin(1); - setMax(10); - } else { - setMin(exp(min, -1)); - setMax(exp(max, +1)); - } - } - if (min <= 0) { - setMin(exp(max, -1)); - } - if (max <= 0) { - setMax(exp(min, +1)); - } - if (this._zero && this.min !== this._suggestedMin && min === exp(this.min, 0)) { - setMin(exp(min, -1)); - } - this.min = min; - this.max = max; - } - buildTicks() { - const opts = this.options; - const generationOptions = { - min: this._userMin, - max: this._userMax - }; - const ticks = generateTicks(generationOptions, this); - 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; - } - getLabelForValue(value) { - return value === undefined - ? '0' - : formatNumber(value, this.chart.options.locale, this.options.ticks.format); - } - configure() { - const start = this.min; - super.configure(); - this._startValue = log10(start); - this._valueRange = log10(this.max) - log10(start); - } - getPixelForValue(value) { - if (value === undefined || value === 0) { - value = this.min; - } - if (value === null || isNaN(value)) { - return NaN; - } - return this.getPixelForDecimal(value === this.min - ? 0 - : (log10(value) - this._startValue) / this._valueRange); - } - getValueForPixel(pixel) { - const decimal = this.getDecimalForPixel(pixel); - return Math.pow(10, this._startValue + decimal * this._valueRange); - } -} -LogarithmicScale.id = 'logarithmic'; -LogarithmicScale.defaults = { - ticks: { - callback: Ticks.formatters.logarithmic, - major: { - enabled: true - } - } -}; - -function getTickBackdropHeight(opts) { - const tickOpts = opts.ticks; - if (tickOpts.display && opts.display) { - const padding = toPadding(tickOpts.backdropPadding); - return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height; - } - return 0; -} -function measureLabelSize(ctx, font, label) { - label = isArray(label) ? label : [label]; - return { - w: _longestText(ctx, font.string, label), - h: label.length * font.lineHeight - }; -} -function determineLimits(angle, pos, size, min, max) { - if (angle === min || angle === max) { - return { - start: pos - (size / 2), - end: pos + (size / 2) - }; - } else if (angle < min || angle > max) { - return { - start: pos - size, - end: pos - }; - } - return { - start: pos, - end: pos + size - }; -} -function fitWithPointLabels(scale) { - const orig = { - l: scale.left + scale._padding.left, - r: scale.right - scale._padding.right, - t: scale.top + scale._padding.top, - b: scale.bottom - scale._padding.bottom - }; - const limits = Object.assign({}, orig); - const labelSizes = []; - const padding = []; - const valueCount = scale._pointLabels.length; - const pointLabelOpts = scale.options.pointLabels; - const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0; - for (let i = 0; i < valueCount; i++) { - const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i)); - padding[i] = opts.padding; - const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle); - const plFont = toFont(opts.font); - const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]); - labelSizes[i] = textSize; - const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle); - const angle = Math.round(toDegrees(angleRadians)); - const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); - const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); - updateLimits(limits, orig, angleRadians, hLimits, vLimits); - } - scale.setCenterPoint( - orig.l - limits.l, - limits.r - orig.r, - orig.t - limits.t, - limits.b - orig.b - ); - scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding); -} -function updateLimits(limits, orig, angle, hLimits, vLimits) { - const sin = Math.abs(Math.sin(angle)); - const cos = Math.abs(Math.cos(angle)); - let x = 0; - let y = 0; - if (hLimits.start < orig.l) { - x = (orig.l - hLimits.start) / sin; - limits.l = Math.min(limits.l, orig.l - x); - } else if (hLimits.end > orig.r) { - x = (hLimits.end - orig.r) / sin; - limits.r = Math.max(limits.r, orig.r + x); - } - if (vLimits.start < orig.t) { - y = (orig.t - vLimits.start) / cos; - limits.t = Math.min(limits.t, orig.t - y); - } else if (vLimits.end > orig.b) { - y = (vLimits.end - orig.b) / cos; - limits.b = Math.max(limits.b, orig.b + y); - } -} -function buildPointLabelItems(scale, labelSizes, padding) { - const items = []; - const valueCount = scale._pointLabels.length; - const opts = scale.options; - const extra = getTickBackdropHeight(opts) / 2; - const outerDistance = scale.drawingArea; - const additionalAngle = opts.pointLabels.centerPointLabels ? PI / valueCount : 0; - for (let i = 0; i < valueCount; i++) { - const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], additionalAngle); - const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI))); - const size = labelSizes[i]; - const y = yForAngle(pointLabelPosition.y, size.h, angle); - const textAlign = getTextAlignForAngle(angle); - const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign); - items.push({ - x: pointLabelPosition.x, - y, - textAlign, - left, - top: y, - right: left + size.w, - bottom: y + size.h - }); - } - return items; -} -function getTextAlignForAngle(angle) { - if (angle === 0 || angle === 180) { - return 'center'; - } else if (angle < 180) { - return 'left'; - } - return 'right'; -} -function leftForTextAlign(x, w, align) { - if (align === 'right') { - x -= w; - } else if (align === 'center') { - x -= (w / 2); - } - return x; -} -function yForAngle(y, h, angle) { - if (angle === 90 || angle === 270) { - y -= (h / 2); - } else if (angle > 270 || angle < 90) { - y -= h; - } - return y; -} -function drawPointLabels(scale, labelCount) { - const {ctx, options: {pointLabels}} = scale; - for (let i = labelCount - 1; i >= 0; i--) { - const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i)); - const plFont = toFont(optsAtIndex.font); - const {x, y, textAlign, left, top, right, bottom} = scale._pointLabelItems[i]; - const {backdropColor} = optsAtIndex; - if (!isNullOrUndef(backdropColor)) { - const padding = toPadding(optsAtIndex.backdropPadding); - ctx.fillStyle = backdropColor; - ctx.fillRect(left - padding.left, top - padding.top, right - left + padding.width, bottom - top + padding.height); - } - renderText( - ctx, - scale._pointLabels[i], - x, - y + (plFont.lineHeight / 2), - plFont, - { - color: optsAtIndex.color, - textAlign: textAlign, - textBaseline: 'middle' - } - ); - } -} -function pathRadiusLine(scale, radius, circular, labelCount) { - const {ctx} = scale; - if (circular) { - ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU); - } else { - let pointPosition = scale.getPointPosition(0, radius); - ctx.moveTo(pointPosition.x, pointPosition.y); - for (let i = 1; i < labelCount; i++) { - pointPosition = scale.getPointPosition(i, radius); - ctx.lineTo(pointPosition.x, pointPosition.y); - } - } -} -function drawRadiusLine(scale, gridLineOpts, radius, labelCount) { - const ctx = scale.ctx; - const circular = gridLineOpts.circular; - const {color, lineWidth} = gridLineOpts; - if ((!circular && !labelCount) || !color || !lineWidth || radius < 0) { - return; - } - ctx.save(); - ctx.strokeStyle = color; - ctx.lineWidth = lineWidth; - ctx.setLineDash(gridLineOpts.borderDash); - ctx.lineDashOffset = gridLineOpts.borderDashOffset; - ctx.beginPath(); - pathRadiusLine(scale, radius, circular, labelCount); - ctx.closePath(); - ctx.stroke(); - ctx.restore(); -} -function createPointLabelContext(parent, index, label) { - return createContext(parent, { - label, - index, - type: 'pointLabel' - }); -} -class RadialLinearScale extends LinearScaleBase { - constructor(cfg) { - super(cfg); - this.xCenter = undefined; - this.yCenter = undefined; - this.drawingArea = undefined; - this._pointLabels = []; - this._pointLabelItems = []; - } - setDimensions() { - const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2); - const w = this.width = this.maxWidth - padding.width; - const h = this.height = this.maxHeight - padding.height; - this.xCenter = Math.floor(this.left + w / 2 + padding.left); - this.yCenter = Math.floor(this.top + h / 2 + padding.top); - this.drawingArea = Math.floor(Math.min(w, h) / 2); - } - determineDataLimits() { - const {min, max} = this.getMinMax(false); - this.min = isNumberFinite(min) && !isNaN(min) ? min : 0; - this.max = isNumberFinite(max) && !isNaN(max) ? max : 0; - this.handleTickRangeOptions(); - } - computeTickLimit() { - return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); - } - generateTickLabels(ticks) { - LinearScaleBase.prototype.generateTickLabels.call(this, ticks); - this._pointLabels = this.getLabels() - .map((value, index) => { - const label = callback(this.options.pointLabels.callback, [value, index], this); - return label || label === 0 ? label : ''; - }) - .filter((v, i) => this.chart.getDataVisibility(i)); - } - fit() { - const opts = this.options; - if (opts.display && opts.pointLabels.display) { - fitWithPointLabels(this); - } else { - this.setCenterPoint(0, 0, 0, 0); - } - } - setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) { - this.xCenter += Math.floor((leftMovement - rightMovement) / 2); - this.yCenter += Math.floor((topMovement - bottomMovement) / 2); - this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement)); - } - getIndexAngle(index) { - const angleMultiplier = TAU / (this._pointLabels.length || 1); - const startAngle = this.options.startAngle || 0; - return _normalizeAngle(index * angleMultiplier + toRadians(startAngle)); - } - getDistanceFromCenterForValue(value) { - if (isNullOrUndef(value)) { - return NaN; - } - const scalingFactor = this.drawingArea / (this.max - this.min); - if (this.options.reverse) { - return (this.max - value) * scalingFactor; - } - return (value - this.min) * scalingFactor; - } - getValueForDistanceFromCenter(distance) { - if (isNullOrUndef(distance)) { - return NaN; - } - const scaledDistance = distance / (this.drawingArea / (this.max - this.min)); - return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance; - } - getPointLabelContext(index) { - const pointLabels = this._pointLabels || []; - if (index >= 0 && index < pointLabels.length) { - const pointLabel = pointLabels[index]; - return createPointLabelContext(this.getContext(), index, pointLabel); - } - } - getPointPosition(index, distanceFromCenter, additionalAngle = 0) { - const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle; - return { - x: Math.cos(angle) * distanceFromCenter + this.xCenter, - y: Math.sin(angle) * distanceFromCenter + this.yCenter, - angle - }; - } - getPointPositionForValue(index, value) { - return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); - } - getBasePosition(index) { - return this.getPointPositionForValue(index || 0, this.getBaseValue()); - } - getPointLabelPosition(index) { - const {left, top, right, bottom} = this._pointLabelItems[index]; - return { - left, - top, - right, - bottom, - }; - } - drawBackground() { - const {backgroundColor, grid: {circular}} = this.options; - if (backgroundColor) { - const ctx = this.ctx; - ctx.save(); - ctx.beginPath(); - pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length); - ctx.closePath(); - ctx.fillStyle = backgroundColor; - ctx.fill(); - ctx.restore(); - } - } - drawGrid() { - const ctx = this.ctx; - const opts = this.options; - const {angleLines, grid} = opts; - const labelCount = this._pointLabels.length; - let i, offset, position; - if (opts.pointLabels.display) { - drawPointLabels(this, labelCount); - } - if (grid.display) { - this.ticks.forEach((tick, index) => { - if (index !== 0) { - offset = this.getDistanceFromCenterForValue(tick.value); - const optsAtIndex = grid.setContext(this.getContext(index - 1)); - drawRadiusLine(this, optsAtIndex, offset, labelCount); - } - }); - } - if (angleLines.display) { - ctx.save(); - for (i = labelCount - 1; i >= 0; i--) { - const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i)); - const {color, lineWidth} = optsAtIndex; - if (!lineWidth || !color) { - continue; - } - ctx.lineWidth = lineWidth; - ctx.strokeStyle = color; - ctx.setLineDash(optsAtIndex.borderDash); - ctx.lineDashOffset = optsAtIndex.borderDashOffset; - offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max); - position = this.getPointPosition(i, offset); - ctx.beginPath(); - ctx.moveTo(this.xCenter, this.yCenter); - ctx.lineTo(position.x, position.y); - ctx.stroke(); - } - ctx.restore(); - } - } - drawBorder() {} - drawLabels() { - const ctx = this.ctx; - const opts = this.options; - const tickOpts = opts.ticks; - if (!tickOpts.display) { - return; - } - const startAngle = this.getIndexAngle(0); - let offset, width; - ctx.save(); - ctx.translate(this.xCenter, this.yCenter); - ctx.rotate(startAngle); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - this.ticks.forEach((tick, index) => { - if (index === 0 && !opts.reverse) { - return; - } - const optsAtIndex = tickOpts.setContext(this.getContext(index)); - const tickFont = toFont(optsAtIndex.font); - offset = this.getDistanceFromCenterForValue(this.ticks[index].value); - if (optsAtIndex.showLabelBackdrop) { - ctx.font = tickFont.string; - width = ctx.measureText(tick.label).width; - ctx.fillStyle = optsAtIndex.backdropColor; - const padding = toPadding(optsAtIndex.backdropPadding); - ctx.fillRect( - -width / 2 - padding.left, - -offset - tickFont.size / 2 - padding.top, - width + padding.width, - tickFont.size + padding.height - ); - } - renderText(ctx, tick.label, 0, -offset, tickFont, { - color: optsAtIndex.color, - }); - }); - ctx.restore(); - } - drawTitle() {} -} -RadialLinearScale.id = 'radialLinear'; -RadialLinearScale.defaults = { - display: true, - animate: true, - position: 'chartArea', - angleLines: { - display: true, - lineWidth: 1, - borderDash: [], - borderDashOffset: 0.0 - }, - grid: { - circular: false - }, - startAngle: 0, - ticks: { - showLabelBackdrop: true, - callback: Ticks.formatters.numeric - }, - pointLabels: { - backdropColor: undefined, - backdropPadding: 2, - display: true, - font: { - size: 10 - }, - callback(label) { - return label; - }, - padding: 5, - centerPointLabels: false - } -}; -RadialLinearScale.defaultRoutes = { - 'angleLines.color': 'borderColor', - 'pointLabels.color': 'color', - 'ticks.color': 'color' -}; -RadialLinearScale.descriptors = { - angleLines: { - _fallback: 'grid' - } -}; - -const INTERVALS = { - millisecond: {common: true, size: 1, steps: 1000}, - second: {common: true, size: 1000, steps: 60}, - minute: {common: true, size: 60000, steps: 60}, - hour: {common: true, size: 3600000, steps: 24}, - day: {common: true, size: 86400000, steps: 30}, - week: {common: false, size: 604800000, steps: 4}, - month: {common: true, size: 2.628e9, steps: 12}, - quarter: {common: false, size: 7.884e9, steps: 4}, - year: {common: true, size: 3.154e10} -}; -const UNITS = (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, 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 (round) { - value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) - ? adapter.startOf(value, 'isoWeek', isoWeekday) - : adapter.startOf(value, round); - } - 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, map, majorUnit) { - const adapter = scale._adapter; - const first = +adapter.startOf(ticks[0].value, majorUnit); - const last = ticks[ticks.length - 1].value; - let major, index; - for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { - index = map[major]; - if (index >= 0) { - ticks[index].major = true; - } - } - return ticks; -} -function ticksFromTimestamps(scale, values, majorUnit) { - const ticks = []; - const map = {}; - const ilen = values.length; - let i, value; - for (i = 0; i < ilen; ++i) { - value = values[i]; - map[value] = i; - ticks.push({ - value, - major: false - }); - } - return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); -} -class TimeScale extends Scale { - constructor(props) { - super(props); - this._cache = { - data: [], - labels: [], - all: [] - }; - this._unit = 'day'; - this._majorUnit = undefined; - this._offsets = {}; - this._normalized = false; - this._parseOpts = undefined; - } - init(scaleOpts, opts) { - const time = scaleOpts.time || (scaleOpts.time = {}); - const adapter = this._adapter = new _adapters._date(scaleOpts.adapters.date); - mergeIf(time.displayFormats, adapter.formats()); - this._parseOpts = { - parser: time.parser, - round: time.round, - isoWeekday: time.isoWeekday - }; - super.init(scaleOpts); - this._normalized = opts.normalized; - } - parse(raw, index) { - if (raw === undefined) { - 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' ? undefined - : determineMajorUnit(this._unit); - this.initOffsets(timestamps); - if (options.reverse) { - ticks.reverse(); - } - return ticksFromTimestamps(this, ticks, this._majorUnit); - } - 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(timeOpts.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) > 100000 * 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((a, b) => a - b).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); - } - _tickFormatFunction(time, index, ticks, format) { - const options = this.options; - 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[index]; - const major = majorUnit && majorFormat && tick && tick.major; - const label = this._adapter.format(time, format || (major ? majorFormat : minorFormat)); - const formatter = options.ticks.callback; - return formatter ? callback(formatter, [label, index, ticks], this) : label; - } - generateTickLabels(ticks) { - let i, ilen, tick; - for (i = 0, ilen = ticks.length; 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)); - } -} -TimeScale.id = 'time'; -TimeScale.defaults = { - bounds: 'data', - adapters: {}, - time: { - parser: false, - unit: false, - round: false, - isoWeekday: false, - minUnit: 'millisecond', - displayFormats: {} - }, - ticks: { - source: 'auto', - 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 = undefined; - this._tableRange = undefined; - } - 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; - } - _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); - } -} -TimeSeriesScale.id = 'timeseries'; -TimeSeriesScale.defaults = TimeScale.defaults; - -var scales = /*#__PURE__*/Object.freeze({ -__proto__: null, -CategoryScale: CategoryScale, -LinearScale: LinearScale, -LogarithmicScale: LogarithmicScale, -RadialLinearScale: RadialLinearScale, -TimeScale: TimeScale, -TimeSeriesScale: TimeSeriesScale -}); - -Chart.register(controllers, scales, elements, plugins); -Chart.helpers = {...helpers}; -Chart._adapters = _adapters; -Chart.Animation = Animation; -Chart.Animations = Animations; -Chart.animator = animator; -Chart.controllers = registry.controllers.items; -Chart.DatasetController = DatasetController; -Chart.Element = Element; -Chart.elements = elements; -Chart.Interaction = Interaction; -Chart.layouts = layouts; -Chart.platforms = platforms; -Chart.Scale = Scale; -Chart.Ticks = Ticks; -Object.assign(Chart, controllers, scales, elements, plugins, platforms); -Chart.Chart = Chart; -if (typeof window !== 'undefined') { - window.Chart = Chart; -} - -return Chart; - -})); diff --git a/node_modules/chart.js/dist/chart.min.js b/node_modules/chart.js/dist/chart.min.js deleted file mode 100644 index 2b3e9984..00000000 --- a/node_modules/chart.js/dist/chart.min.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * Chart.js v3.7.1 - * https://www.chartjs.org - * (c) 2022 Chart.js Contributors - * Released under the MIT License - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Chart=e()}(this,(function(){"use strict";const t="undefined"==typeof window?function(t){return t()}:window.requestAnimationFrame;function e(e,i,s){const n=s||(t=>Array.prototype.slice.call(t));let o=!1,a=[];return function(...s){a=n(s),o||(o=!0,t.call(window,(()=>{o=!1,e.apply(i,a)})))}}function i(t,e){let i;return function(...s){return e?(clearTimeout(i),i=setTimeout(t,e,s)):t.apply(this,s),e}}const s=t=>"start"===t?"left":"end"===t?"right":"center",n=(t,e,i)=>"start"===t?e:"end"===t?i:(e+i)/2,o=(t,e,i,s)=>t===(s?"left":"right")?i:"center"===t?(e+i)/2:e;var a=new class{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(t,e,i,s){const n=e.listeners[s],o=e.duration;n.forEach((s=>s({chart:t,initial:e.initial,numSteps:o,currentStep:Math.min(i-e.start,o)})))}_refresh(){this._request||(this._running=!0,this._request=t.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(t=Date.now()){let e=0;this._charts.forEach(((i,s)=>{if(!i.running||!i.items.length)return;const n=i.items;let o,a=n.length-1,r=!1;for(;a>=0;--a)o=n[a],o._active?(o._total>i.duration&&(i.duration=o._total),o.tick(t),r=!0):(n[a]=n[n.length-1],n.pop());r&&(s.draw(),this._notify(s,i,t,"progress")),n.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=n.length})),this._lastDate=t,0===e&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}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(((t,e)=>Math.max(t,e._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 i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}; -/*! - * @kurkle/color v0.1.9 - * https://github.com/kurkle/color#readme - * (c) 2020 Jukka Kurkela - * Released under the MIT License - */const r={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},l="0123456789ABCDEF",h=t=>l[15&t],c=t=>l[(240&t)>>4]+l[15&t],d=t=>(240&t)>>4==(15&t);function u(t){var e=function(t){return d(t.r)&&d(t.g)&&d(t.b)&&d(t.a)}(t)?h:c;return t?"#"+e(t.r)+e(t.g)+e(t.b)+(t.a<255?e(t.a):""):t}function f(t){return t+.5|0}const g=(t,e,i)=>Math.max(Math.min(t,i),e);function p(t){return g(f(2.55*t),0,255)}function m(t){return g(f(255*t),0,255)}function x(t){return g(f(t/2.55)/100,0,1)}function b(t){return g(f(100*t),0,100)}const _=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const y=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function v(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function w(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function M(t,e,i){const s=v(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function k(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=n===e?(i-s)/h+(i>16&255,o>>8&255,255&o]}return t}(),T.transparent=[0,0,0,0]);const e=T[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}function R(t,e,i){if(t){let s=k(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=P(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function E(t,e){return t?Object.assign(e||{},t):t}function I(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=m(t[3]))):(e=E(t,{r:0,g:0,b:0,a:1})).a=m(e.a),e}function z(t){return"r"===t.charAt(0)?function(t){const e=_.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=255&(e[8]?p(t):255*t)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?p(i):i),s=255&(e[4]?p(s):s),n=255&(e[6]?p(n):n),{r:i,g:s,b:n,a:o}}}(t):C(t)}class F{constructor(t){if(t instanceof F)return t;const e=typeof t;let i;var s,n,o;"object"===e?i=I(t):"string"===e&&(o=(s=t).length,"#"===s[0]&&(4===o||5===o?n={r:255&17*r[s[1]],g:255&17*r[s[2]],b:255&17*r[s[3]],a:5===o?17*r[s[4]]:255}:7!==o&&9!==o||(n={r:r[s[1]]<<4|r[s[2]],g:r[s[3]]<<4|r[s[4]],b:r[s[5]]<<4|r[s[6]],a:9===o?r[s[7]]<<4|r[s[8]]:255})),i=n||L(t)||z(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=E(this._rgb);return t&&(t.a=x(t.a)),t}set rgb(t){this._rgb=I(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${x(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):this._rgb;var t}hexString(){return this._valid?u(this._rgb):this._rgb}hslString(){return this._valid?function(t){if(!t)return;const e=k(t),i=e[0],s=b(e[1]),n=b(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${x(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):this._rgb}mix(t,e){const i=this;if(t){const s=i.rgb,n=t.rgb;let o;const a=e===o?.5:e,r=2*a-1,l=s.a-n.a,h=((r*l==-1?r:(r+l)/(1+r*l))+1)/2;o=1-h,s.r=255&h*s.r+o*n.r+.5,s.g=255&h*s.g+o*n.g+.5,s.b=255&h*s.b+o*n.b+.5,s.a=a*s.a+(1-a)*n.a,i.rgb=s}return i}clone(){return new F(this.rgb)}alpha(t){return this._rgb.a=m(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=f(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.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 R(this._rgb,2,t),this}darken(t){return R(this._rgb,2,-t),this}saturate(t){return R(this._rgb,1,t),this}desaturate(t){return R(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=k(t);i[0]=D(i[0]+e),i=P(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function B(t){return new F(t)}const V=t=>t instanceof CanvasGradient||t instanceof CanvasPattern;function W(t){return V(t)?t:B(t)}function N(t){return V(t)?t:B(t).saturate(.5).darken(.1).hexString()}function H(){}const j=function(){let t=0;return function(){return t++}}();function $(t){return null==t}function Y(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)}function U(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)}const X=t=>("number"==typeof t||t instanceof Number)&&isFinite(+t);function q(t,e){return X(t)?t:e}function K(t,e){return void 0===t?e:t}const G=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100:t/e,Z=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100*e:+t;function J(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)}function Q(t,e,i,s){let n,o,a;if(Y(t))if(o=t.length,s)for(n=o-1;n>=0;n--)e.call(i,t[n],n);else for(n=0;ni;)t=t[e.substr(i,s-i)],i=s+1,s=rt(e,i);return t}function ht(t){return t.charAt(0).toUpperCase()+t.slice(1)}const ct=t=>void 0!==t,dt=t=>"function"==typeof t,ut=(t,e)=>{if(t.size!==e.size)return!1;for(const i of t)if(!e.has(i))return!1;return!0};function ft(t){return"mouseup"===t.type||"click"===t.type||"contextmenu"===t.type}const gt=Object.create(null),pt=Object.create(null);function mt(t,e){if(!e)return t;const i=e.split(".");for(let e=0,s=i.length;et.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=(t,e)=>N(e.backgroundColor),this.hoverBorderColor=(t,e)=>N(e.borderColor),this.hoverColor=(t,e)=>N(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0},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)}set(t,e){return xt(this,t,e)}get(t){return mt(this,t)}describe(t,e){return xt(pt,t,e)}override(t,e){return xt(gt,t,e)}route(t,e,i,s){const n=mt(this,t),o=mt(this,i),a="_"+e;Object.defineProperties(n,{[a]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[a],e=o[s];return U(t)?Object.assign({},e,t):K(t,e)},set(t){this[a]=t}}})}}({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}});const _t=Math.PI,yt=2*_t,vt=yt+_t,wt=Number.POSITIVE_INFINITY,Mt=_t/180,kt=_t/2,St=_t/4,Pt=2*_t/3,Dt=Math.log10,Ct=Math.sign;function Ot(t){const e=Math.round(t);t=Lt(t,e,t/1e3)?e:t;const i=Math.pow(10,Math.floor(Dt(t))),s=t/i;return(s<=1?1:s<=2?2:s<=5?5:10)*i}function At(t){const e=[],i=Math.sqrt(t);let s;for(s=1;st-e)).pop(),e}function Tt(t){return!isNaN(parseFloat(t))&&isFinite(t)}function Lt(t,e,i){return Math.abs(t-e)=t}function Et(t,e,i){let s,n,o;for(s=0,n=t.length;sl&&h=Math.min(e,i)-s&&t<=Math.max(e,i)+s}function Ut(t){return!t||$(t.size)||$(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function Xt(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function qt(t,e,i,s){let n=(s=s||{}).data=s.data||{},o=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(n=s.data={},o=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let a=0;const r=i.length;let l,h,c,d,u;for(l=0;li.length){for(l=0;l0&&t.stroke()}}function Jt(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.xe.top-i&&t.y0&&""!==o.strokeColor;let l,h;for(t.save(),t.font=n.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]);$(e.rotation)||t.rotate(e.rotation);e.color&&(t.fillStyle=e.color);e.textAlign&&(t.textAlign=e.textAlign);e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,o),l=0;lt[i]1;)s=o+n>>1,i(s)?o=s:n=s;return{lo:o,hi:n}}const re=(t,e,i)=>ae(t,i,(s=>t[s][e]ae(t,i,(s=>t[s][e]>=i));function he(t,e,i){let s=0,n=t.length;for(;ss&&t[n-1]>i;)n--;return s>0||n{const i="_onData"+ht(e),s=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value(...e){const n=s.apply(this,e);return t._chartjs.listeners.forEach((t=>{"function"==typeof t[i]&&t[i](...e)})),n}})})))}function ue(t,e){const i=t._chartjs;if(!i)return;const s=i.listeners,n=s.indexOf(e);-1!==n&&s.splice(n,1),s.length>0||(ce.forEach((e=>{delete t[e]})),delete t._chartjs)}function fe(t){const e=new Set;let i,s;for(i=0,s=t.length;iwindow.getComputedStyle(t,null);function be(t,e){return xe(t).getPropertyValue(e)}const _e=["top","right","bottom","left"];function ye(t,e,i){const s={};i=i?"-"+i:"";for(let n=0;n<4;n++){const o=_e[n];s[o]=parseFloat(t[e+"-"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}function ve(t,e){const{canvas:i,currentDevicePixelRatio:s}=e,n=xe(i),o="border-box"===n.boxSizing,a=ye(n,"padding"),r=ye(n,"border","width"),{x:l,y:h,box:c}=function(t,e){const i=t.native||t,s=i.touches,n=s&&s.length?s[0]:i,{offsetX:o,offsetY:a}=n;let r,l,h=!1;if(((t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot))(o,a,i.target))r=o,l=a;else{const t=e.getBoundingClientRect();r=n.clientX-t.left,l=n.clientY-t.top,h=!0}return{x:r,y:l,box:h}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const we=t=>Math.round(10*t)/10;function Me(t,e,i,s){const n=xe(t),o=ye(n,"margin"),a=me(n.maxWidth,t,"clientWidth")||wt,r=me(n.maxHeight,t,"clientHeight")||wt,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=pe(t);if(o){const t=o.getBoundingClientRect(),a=xe(o),r=ye(a,"border","width"),l=ye(a,"padding");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=me(a.maxWidth,o,"clientWidth"),n=me(a.maxHeight,o,"clientHeight")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||wt,maxHeight:n||wt}}(t,e,i);let{width:h,height:c}=l;if("content-box"===n.boxSizing){const t=ye(n,"border","width"),e=ye(n,"padding");h-=e.width+t.width,c-=e.height+t.height}return h=Math.max(0,h-o.width),c=Math.max(0,s?Math.floor(h/s):c-o.height),h=we(Math.min(h,a,l.maxWidth)),c=we(Math.min(c,r,l.maxHeight)),h&&!c&&(c=we(h/2)),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Math.floor(t.height*s),o=Math.floor(t.width*s);t.height=n/s,t.width=o/s;const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};window.addEventListener("test",null,e),window.removeEventListener("test",null,e)}catch(t){}return t}();function Pe(t,e){const i=be(t,e),s=i&&i.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function De(t,e){return"native"in t?{x:t.x,y:t.y}:ve(t,e)}function Ce(t,e,i,s){const{controller:n,data:o,_sorted:a}=t,r=n._cachedMeta.iScale;if(r&&e===r.axis&&"r"!==e&&a&&o.length){const t=r._reversePixels?le:re;if(!s)return t(o,e,i);if(n._sharedOptions){const s=o[0],n="function"==typeof s.getRange&&s.getRange(e);if(n){const s=t(o,e,i-n),a=t(o,e,i+n);return{lo:s.lo,hi:a.hi}}}}return{lo:0,hi:o.length-1}}function Oe(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t{t[r](n[a],s)&&o.push({element:t,datasetIndex:e,index:i}),t.inRange(n.x,n.y,s)&&(l=!0)})),i.intersect&&!l?[]:o}var Ee={modes:{index(t,e,i,s){const n=De(e,t),o=i.axis||"x",a=i.intersect?Ae(t,n,o,s):Le(t,n,o,!1,s),r=[];return a.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=a[0].index,i=t.data[e];i&&!i.skip&&r.push({element:i,datasetIndex:t.index,index:e})})),r):[]},dataset(t,e,i,s){const n=De(e,t),o=i.axis||"xy";let a=i.intersect?Ae(t,n,o,s):Le(t,n,o,!1,s);if(a.length>0){const e=a[0].datasetIndex,i=t.getDatasetMeta(e).data;a=[];for(let t=0;tAe(t,De(e,t),i.axis||"xy",s),nearest:(t,e,i,s)=>Le(t,De(e,t),i.axis||"xy",i.intersect,s),x:(t,e,i,s)=>Re(t,e,{axis:"x",intersect:i.intersect},s),y:(t,e,i,s)=>Re(t,e,{axis:"y",intersect:i.intersect},s)}};const Ie=new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/),ze=new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/);function Fe(t,e){const i=(""+t).match(Ie);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t}function Be(t,e){const i={},s=U(e),n=s?Object.keys(e):e,o=U(t)?s?i=>K(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=+o(t)||0;return i}function Ve(t){return Be(t,{top:"y",right:"x",bottom:"y",left:"x"})}function We(t){return Be(t,["topLeft","topRight","bottomLeft","bottomRight"])}function Ne(t){const e=Ve(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function He(t,e){t=t||{},e=e||bt.font;let i=K(t.size,e.size);"string"==typeof i&&(i=parseInt(i,10));let s=K(t.style,e.style);s&&!(""+s).match(ze)&&(console.warn('Invalid font style specified: "'+s+'"'),s="");const n={family:K(t.family,e.family),lineHeight:Fe(K(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:K(t.weight,e.weight),string:""};return n.string=Ut(n),n}function je(t,e,i,s){let n,o,a,r=!0;for(n=0,o=t.length;ni&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ye(t,e){return Object.assign(Object.create(t),e)}const Ue=["left","top","right","bottom"];function Xe(t,e){return t.filter((t=>t.pos===e))}function qe(t,e){return t.filter((t=>-1===Ue.indexOf(t.pos)&&t.box.axis===e))}function Ke(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function Ge(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Ue.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o{s[t]=Math.max(e[t],i[t])})),s}return s(t?["left","right"]:["top","bottom"])}function ei(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;ot.box.fullSize)),!0),s=Ke(Xe(e,"left"),!0),n=Ke(Xe(e,"right")),o=Ke(Xe(e,"top"),!0),a=Ke(Xe(e,"bottom")),r=qe(e,"x"),l=qe(e,"y");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Xe(e,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;Q(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),u=Object.assign({},n);Je(u,Ne(s));const f=Object.assign({maxPadding:u,w:o,h:a,x:n.left,y:n.top},n),g=Ge(l.concat(h),d);ei(r.fullSize,f,d,g),ei(l,f,d,g),ei(h,f,d,g)&&ei(l,f,d,g),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i("top"),t.x+=i("left"),i("right"),i("bottom")}(f),si(r.leftAndTop,f,d,g),f.x+=f.w,f.y+=f.h,si(r.rightAndBottom,f,d,g),t.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},Q(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})}))}};function oi(t,e=[""],i=t,s,n=(()=>t[0])){ct(s)||(s=mi("_fallback",t));const o={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:i,_fallback:s,_getTarget:n,override:n=>oi([n,...t],e,i,s)};return new Proxy(o,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>ci(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=mi(li(o,t),i),ct(n))return hi(t,n)?gi(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>xi(t).includes(e),ownKeys:t=>xi(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function ai(t,e,i,s){const n={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:ri(t,s),setContext:e=>ai(t,e,i,s),override:n=>ai(t.override(n),e,i,s)};return new Proxy(n,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>ci(t,e,(()=>function(t,e,i){const{_proxy:s,_context:n,_subProxy:o,_descriptors:a}=t;let r=s[e];dt(r)&&a.isScriptable(e)&&(r=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+t);r.add(t),e=e(o,a||s),r.delete(t),hi(t,e)&&(e=gi(n._scopes,n,t,e));return e}(e,r,t,i));Y(r)&&r.length&&(r=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_descriptors:r}=i;if(ct(o.index)&&s(t))e=e[o.index%e.length];else if(U(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const l of i){const i=gi(s,n,t,l);e.push(ai(i,o,a&&a[t],r))}}return e}(e,r,t,a.isIndexable));hi(e,r)&&(r=ai(r,n,o&&o[e],a));return r}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function ri(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:dt(i)?i:()=>i,isIndexable:dt(s)?s:()=>s}}const li=(t,e)=>t?t+ht(e):e,hi=(t,e)=>U(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function ci(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e))return t[e];const s=i();return t[e]=s,s}function di(t,e,i){return dt(t)?t(e,i):t}const ui=(t,e)=>!0===t?e:"string"==typeof t?lt(e,t):void 0;function fi(t,e,i,s,n){for(const o of e){const e=ui(i,o);if(e){t.add(e);const o=di(e._fallback,i,n);if(ct(o)&&o!==i&&o!==s)return o}else if(!1===e&&ct(s)&&i!==s)return null}return!1}function gi(t,e,i,s){const n=e._rootScopes,o=di(e._fallback,i,s),a=[...t,...n],r=new Set;r.add(s);let l=pi(r,a,i,o||i,s);return null!==l&&((!ct(o)||o===i||(l=pi(r,a,o,l,s),null!==l))&&oi(Array.from(r),[""],n,o,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const n=s[e];if(Y(n)&&U(i))return i;return n}(e,i,s))))}function pi(t,e,i,s,n){for(;i;)i=fi(t,e,i,s,n);return i}function mi(t,e){for(const i of e){if(!i)continue;const e=i[t];if(ct(e))return e}}function xi(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}const bi=Number.EPSILON||1e-14,_i=(t,e)=>e"x"===t?"y":"x";function vi(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=Vt(o,n),l=Vt(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function wi(t,e="x"){const i=yi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=_i(t,0);for(a=0;a!t.skip))),"monotone"===e.cubicInterpolationMode)wi(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o0===t||1===t,Pi=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*yt/i),Di=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*yt/i)+1,Ci={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*kt),easeOutSine:t=>Math.sin(t*kt),easeInOutSine:t=>-.5*(Math.cos(_t*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>Si(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>Si(t)?t:Pi(t,.075,.3),easeOutElastic:t=>Si(t)?t:Di(t,.075,.3),easeInOutElastic(t){const e=.1125;return Si(t)?t:t<.5?.5*Pi(2*t,e,.45):.5+.5*Di(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-Ci.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*Ci.easeInBounce(2*t):.5*Ci.easeOutBounce(2*t-1)+.5};function Oi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function Ai(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:"middle"===s?i<.5?t.y:e.y:"after"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function Ti(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=Oi(t,n,i),r=Oi(n,o,i),l=Oi(o,e,i),h=Oi(a,r,i),c=Oi(r,l,i);return Oi(h,c,i)}const Li=new Map;function Ri(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=Li.get(i);return s||(s=new Intl.NumberFormat(t,e),Li.set(i,s)),s}(e,i).format(t)}function Ei(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ii(t,e){let i,s;"ltr"!==e&&"rtl"!==e||(i=t.canvas.style,s=[i.getPropertyValue("direction"),i.getPropertyPriority("direction")],i.setProperty("direction",e,"important"),t.prevTextDirection=s)}function zi(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function Fi(t){return"angle"===t?{between:Ht,compare:Wt,normalize:Nt}:{between:Yt,compare:(t,e)=>t-e,normalize:t=>t}}function Bi({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Vi(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Fi(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Fi(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;hb||l(n,x,p)&&0!==r(n,x),v=()=>!b||0===r(o,p)||l(o,x,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==x&&(b=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Bi({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,x=p));return null!==_&&g.push(Bi({start:_,end:d,loop:u,count:a,style:f})),g}function Wi(t,e){const i=[],s=t.segments;for(let n=0;nn&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Hi(t,[{start:a,end:r,loop:o}],i,e);return Hi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,rnull===t||""===t;const Gi=!!Se&&{passive:!0};function Zi(t,e,i){t.canvas.removeEventListener(e,i,Gi)}function Ji(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function Qi(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||Ji(i.addedNodes,s),e=e&&!Ji(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function ts(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||Ji(i.removedNodes,s),e=e&&!Ji(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const es=new Map;let is=0;function ss(){const t=window.devicePixelRatio;t!==is&&(is=t,es.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function ns(t,i,s){const n=t.canvas,o=n&&pe(n);if(!o)return;const a=e(((t,e)=>{const i=o.clientWidth;s(t,e),i{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||a(i,s)}));return r.observe(o),function(t,e){es.size||window.addEventListener("resize",ss),es.set(t,e)}(t,a),r}function os(t,e,i){i&&i.disconnect(),"resize"===e&&function(t){es.delete(t),es.size||window.removeEventListener("resize",ss)}(t)}function as(t,i,s){const n=t.canvas,o=e((e=>{null!==t.ctx&&s(function(t,e){const i=qi[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t,(t=>{const e=t[0];return[e,e.offsetX,e.offsetY]}));return function(t,e,i){t.addEventListener(e,i,Gi)}(n,i,o),o}class rs extends Ui{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute("height"),n=t.getAttribute("width");if(t.$chartjs={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",i.boxSizing=i.boxSizing||"border-box",Ki(n)){const e=Pe(t,"width");void 0!==e&&(t.width=e)}if(Ki(s))if(""===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,"height");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e.$chartjs)return!1;const i=e.$chartjs.initial;["height","width"].forEach((t=>{const s=i[t];$(s)?e.removeAttribute(t):e.setAttribute(t,s)}));const s=i.style||{};return Object.keys(s).forEach((t=>{e.style[t]=s[t]})),e.width=e.width,delete e.$chartjs,!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:Qi,detach:ts,resize:ns}[e]||as;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:os,detach:os,resize:os}[e]||Zi)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return Me(t,e,i,s)}isAttached(t){const e=pe(t);return!(!e||!e.isConnected)}}function ls(t){return!ge()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?Xi:rs}var hs=Object.freeze({__proto__:null,_detectPlatform:ls,BasePlatform:Ui,BasicPlatform:Xi,DomPlatform:rs});const cs="transparent",ds={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=W(t||cs),n=s.valid&&W(e||cs);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class us{constructor(t,e,i,s){const n=e[i];s=je([t.to,s,n,t.from]);const o=je([t.from,n,s]);this._active=!0,this._fn=t.fn||ds[t.type||typeof o],this._easing=Ci[t.easing]||Ci.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=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=je([t.to,e,s,t.from]),this._from=je([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let t=0;t"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),bt.set("animations",{colors:{type:"color",properties:["color","borderColor","backgroundColor"]},numbers:{type:"number",properties:["x","y","borderWidth","radius","tension"]}}),bt.describe("animations",{_fallback:"animation"}),bt.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=>0|t}}}});class gs{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!U(t))return;const e=this._properties;Object.getOwnPropertyNames(t).forEach((i=>{const s=t[i];if(!U(s))return;const n={};for(const t of fs)n[t]=s[t];(Y(s.properties)&&s.properties||[i]).forEach((t=>{t!==i&&e.has(t)||e.set(t,n)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if("$"===l.charAt(0))continue;if("options"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new us(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(a.add(this._chart,i),!0):void 0}}function ps(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function ms(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n0||!i&&e<0)return n.index}return null}function vs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;ti[t].axis===e)).shift()}function Ms(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i]}}}const ks=t=>"reset"===t||"none"===t,Ss=(t,e)=>e?t:Object.assign({},t);class Ps{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.$context=void 0,this._syncList=[],this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=bs(t.vScale,t),this.addElements()}updateIndex(t){this.index!==t&&Ms(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>"x"===t?e:"r"===t?s:i,n=e.xAxisID=K(i.xAxisID,ws(t,"x")),o=e.yAxisID=K(i.yAxisID,ws(t,"y")),a=e.rAxisID=K(i.rAxisID,ws(t,"r")),r=e.indexAxis,l=e.iAxisID=s(r,n,o,a),h=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(l),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&&ue(this._data,this),t._stacked&&Ms(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(U(e))this._data=function(t){const e=Object.keys(t),i=new Array(e.length);let s,n,o;for(s=0,n=e.length;s0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,h=s;else{h=Y(s[t])?this.parseArrayData(i,s,t,e):U(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const n=()=>null===l[a]||d&&l[a]t&&!e.hidden&&e._stacked&&{keys:ms(i,!0),values:null})(e,i,this.chart),l={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:h,max:c}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(a);let d,u;function f(){u=s[d];const e=u[a.axis];return!X(u[t.axis])||h>e||c=0;--d)if(!f()){this.updateRangeFromParsed(l,t,u,r);break}return l}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s=0&&tthis.getContext(i,s)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Ss(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new gs(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ks(t)||this.chart._animationsDisabled}updateElement(t,e,i,s){ks(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!ks(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"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,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}Ds.defaults={},Ds.defaultRoutes=void 0;const Cs={values:t=>Y(t)?t:""+t,numeric(t,e,i){if(0===t)return"0";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n="scientific"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=Dt(Math.abs(o)),r=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),Ri(t,s,l)},logarithmic(t,e,i){if(0===t)return"0";const s=t/Math.pow(10,Math.floor(Dt(t)));return 1===s||2===s||5===s?Cs.numeric.call(this,t,e,i):""}};var Os={formatters:Cs};function As(t,e){const i=t.options.ticks,s=i.maxTicksLimit||function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),n=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;is)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;nn)return e}return Math.max(n,1)}(n,e,s);if(o>0){let t,i;const s=o>1?Math.round((r-a)/(o-1)):null;for(Ts(e,l,h,$(s)?0:a-s,a),t=0,i=o-1;te.lineWidth,tickColor:(t,e)=>e.color,offset:!1,borderDash:[],borderDashOffset:0,borderWidth:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:Os.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),bt.route("scale.ticks","color","","color"),bt.route("scale.grid","color","","borderColor"),bt.route("scale.grid","borderColor","","borderColor"),bt.route("scale.title","color","","color"),bt.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t}),bt.describe("scales",{_fallback:"scale"}),bt.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t});const Ls=(t,e,i)=>"top"===e||"left"===e?t[e]+i:t[e]-i;function Rs(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;oa+r)))return h}function Is(t){return t.drawTicks?t.tickLength:0}function zs(t,e){if(!t.display)return 0;const i=He(t.font,e),s=Ne(t.padding);return(Y(t.text)?t.text.length:1)*i.lineHeight+s.height}function Fs(t,e,i){let n=s(t);return(i&&"right"!==e||!i&&"right"===e)&&(n=(t=>"left"===t?"right":"right"===t?"left":t)(n)),n}class Bs extends Ds{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=q(t,Number.POSITIVE_INFINITY),e=q(e,Number.NEGATIVE_INFINITY),i=q(i,Number.POSITIVE_INFINITY),s=q(s,Number.NEGATIVE_INFINITY),{min:q(t,i),max:q(e,s),minDefined:X(t),maxDefined:X(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;rs?s:i,s=n&&i>s?i:s,{min:q(i,q(s,i)),max:q(s,q(i,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||[]}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){J(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=$e(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=jt(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Is(t.grid)-e.padding-zs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=zt(Math.min(Math.asin(jt((h.highest.height+6)/o,-1,1)),Math.asin(jt(a/r,-1,1))-Math.asin(jt(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){J(this.options.afterCalculateLabelRotation,[this])}beforeFit(){J(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=zs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Is(n)+o):(t.height=this.maxHeight,t.width=Is(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=It(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}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,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l="top"!==a&&"x"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):"start"===n?d=e.width:"end"===n?c=t.width:(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;"start"===n?(i=0,s=t.height):"end"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_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(){J(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n({width:n[t]||0,height:o[t]||0});return{first:v(0),last:v(e-1),widest:v(_),highest:v(y),widths:n,heights:o}}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 $t(this._alignToPixels?Kt(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*s?a/i:r/s:r*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:o}=s,a=n.offset,r=this.isHorizontal(),l=this.ticks.length+(a?1:0),h=Is(n),c=[],d=n.setContext(this.getContext()),u=d.drawBorder?d.borderWidth:0,f=u/2,g=function(t){return Kt(i,t,u)};let p,m,x,b,_,y,v,w,M,k,S,P;if("top"===o)p=g(this.bottom),y=this.bottom-h,w=p-f,k=g(t.top)+f,P=t.bottom;else if("bottom"===o)p=g(this.top),k=t.top,P=g(t.bottom)-f,y=p+f,w=this.top+h;else if("left"===o)p=g(this.right),_=this.right-h,v=p-f,M=g(t.left)+f,S=t.right;else if("right"===o)p=g(this.left),M=t.left,S=g(t.right)-f,_=p+f,v=this.left+h;else if("x"===e){if("center"===o)p=g((t.top+t.bottom)/2+.5);else if(U(o)){const t=Object.keys(o)[0],e=o[t];p=g(this.chart.scales[t].getPixelForValue(e))}k=t.top,P=t.bottom,y=p+f,w=y+h}else if("y"===e){if("center"===o)p=g((t.left+t.right)/2);else if(U(o)){const t=Object.keys(o)[0],e=o[t];p=g(this.chart.scales[t].getPixelForValue(e))}_=p-f,v=_-h,M=t.left,S=t.right}const D=K(s.ticks.maxTicksLimit,l),C=Math.max(1,Math.ceil(l/D));for(m=0;me.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:i+1,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let n,o;for(n=0,o=e.length;n{const s=i.split("."),n=s.pop(),o=[t].concat(s).join("."),a=e[i].split("."),r=a.pop(),l=a.join(".");bt.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&bt.describe(e,t.descriptors)}(t,o,i),this.override&&bt.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in bt[s]&&(delete bt[s][i],this.override&&delete gt[i])}}var Ws=new class{constructor(){this.controllers=new Vs(Ps,"datasets",!0),this.elements=new Vs(Ds,"elements"),this.plugins=new Vs(Object,"plugins"),this.scales=new Vs(Bs,"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,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):Q(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=ht(t);J(i["before"+s],[],i),e[t](i),J(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;et.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function Hs(t,e){return e||!1!==t?!0===t?{}:t:null}function js(t,e,i,s){const n=t.pluginScopeKeys(e),o=t.getOptionScopes(i,n);return t.createResolver(o,s,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function $s(t,e){const i=bt.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||"x"}function Ys(t,e){return"x"===t||"y"===t?t:e.axis||("top"===(i=e.position)||"bottom"===i?"x":"left"===i||"right"===i?"y":void 0)||t.charAt(0).toLowerCase();var i}function Us(t){const e=t.options||(t.options={});e.plugins=K(e.plugins,{}),e.scales=function(t,e){const i=gt[t.type]||{scales:{}},s=e.scales||{},n=$s(t.type,e),o=Object.create(null),a=Object.create(null);return Object.keys(s).forEach((t=>{const e=s[t];if(!U(e))return console.error(`Invalid scale configuration for scale: ${t}`);if(e._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${t}`);const r=Ys(t,e),l=function(t,e){return t===e?"_index_":"_value_"}(r,n),h=i.scales||{};o[r]=o[r]||t,a[t]=ot(Object.create(null),[{axis:r},e,h[r],h[l]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,r=i.indexAxis||$s(n,e),l=(gt[n]||{}).scales||{};Object.keys(l).forEach((t=>{const e=function(t,e){let i=t;return"_index_"===t?i=e:"_value_"===t&&(i="x"===e?"y":"x"),i}(t,r),n=i[e+"AxisID"]||o[e]||e;a[n]=a[n]||Object.create(null),ot(a[n],[{axis:e},s[n],l[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];ot(e,[bt.scales[e.type],bt.scale])})),a}(t,e)}function Xs(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const qs=new Map,Ks=new Set;function Gs(t,e){let i=qs.get(t);return i||(i=e(),qs.set(t,i),Ks.add(i)),i}const Zs=(t,e,i)=>{const s=lt(e,i);void 0!==s&&t.add(s)};class Js{constructor(t){this._config=function(t){return(t=t||{}).data=Xs(t.data),Us(t),t}(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=Xs(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(),Us(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Gs(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return Gs(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return Gs(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return Gs(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>Zs(r,t,e)))),e.forEach((t=>Zs(r,s,t))),e.forEach((t=>Zs(r,gt[n]||{},t))),e.forEach((t=>Zs(r,bt,t))),e.forEach((t=>Zs(r,pt,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),Ks.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,gt[e]||{},bt.datasets[e]||{},{type:e},bt,pt]}resolveNamedOptions(t,e,i,s=[""]){const n={$shared:!0},{resolver:o,subPrefixes:a}=Qs(this._resolverCache,t,s);let r=o;if(function(t,e){const{isScriptable:i,isIndexable:s}=ri(t);for(const n of e){const e=i(n),o=s(n),a=(o||e)&&t[n];if(e&&(dt(a)||tn(a))||o&&Y(a))return!0}return!1}(o,e)){n.$shared=!1;r=ai(o,i=dt(i)?i():i,this.createResolver(t,i,a))}for(const t of e)n[t]=r[t];return n}createResolver(t,e,i=[""],s){const{resolver:n}=Qs(this._resolverCache,t,i);return U(e)?ai(n,e,void 0,s):n}}function Qs(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:oi(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes("hover")))},s.set(n,o)}return o}const tn=t=>U(t)&&Object.getOwnPropertyNames(t).reduce(((e,i)=>e||dt(t[i])),!1);const en=["top","bottom","left","right","chartArea"];function sn(t,e){return"top"===t||"bottom"===t||-1===en.indexOf(t)&&"x"===e}function nn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function on(t){const e=t.chart,i=e.options.animation;e.notifyPlugins("afterRender"),J(i&&i.onComplete,[t],e)}function an(t){const e=t.chart,i=e.options.animation;J(i&&i.onProgress,[t],e)}function rn(t){return ge()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const ln={},hn=t=>{const e=rn(t);return Object.values(ln).filter((t=>t.canvas===e)).pop()};function cn(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class dn{constructor(t,e){const s=this.config=new Js(e),n=rn(t),o=hn(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas can be reused.");const r=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ls(n)),this.platform.updateConfig(s);const l=this.platform.acquireContext(n,r.aspectRatio),h=l&&l.canvas,c=h&&h.height,d=h&&h.width;this.id=j(),this.ctx=l,this.canvas=h,this.width=d,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 Ns,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=i((t=>this.update(t)),r.resizeDelay||0),this._dataChanges=[],ln[this.id]=this,l&&h?(a.listen(this,"complete",on),a.listen(this,"progress",an),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:s,_aspectRatio:n}=this;return $(t)?e&&n?n:s?i/s: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}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Gt(this.canvas,this.ctx),this}stop(){return a.stop(this),this}resize(t,e){a.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins("resize",{size:o}),J(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){Q(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=Ys(t,i),n="r"===s,o="x"===s;return{options:i,dposition:n?"chartArea":o?"bottom":"left",dtype:n?"radialLinear":o?"category":"linear"}})))),Q(n,(e=>{const n=e.options,o=n.id,a=Ys(o,n),r=K(n.type,e.dtype);void 0!==n.position&&sn(n.position,a)===sn(e.dposition)||(n.position=e.dposition),s[o]=!0;let l=null;if(o in i&&i[o].type===r)l=i[o];else{l=new(Ws.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[l.id]=l}l.init(n,t)})),Q(s,((t,e)=>{t||delete i[e]})),Q(i,(t=>{ni.configure(this,t,t.options),ni.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;te.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(nn("z","_idx"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){Q(this.scales,(t=>{ni.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);ut(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){cn(t,s,"_removeElements"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),s=i(0);for(let t=1;tt.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;ni.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],Q(this.boxes,(t=>{i&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,n=this.chartArea,o={meta:t,index:t.index,cancelable:!0};!1!==this.notifyPlugins("beforeDatasetDraw",o)&&(s&&Qt(e,{left:!1===i.left?0:n.left-i.left,right:!1===i.right?this.width:n.right+i.right,top:!1===i.top?0:n.top-i.top,bottom:!1===i.bottom?this.height:n.bottom+i.bottom}),t.controller.draw(),s&&te(e),o.cancelable=!1,this.notifyPlugins("afterDatasetDraw",o))}getElementsAtEventForMode(t,e,i,s){const n=Ee.modes[e];return"function"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={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},i.push(s)),s}getContext(){return this.$context||(this.$context=Ye(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return"boolean"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);ct(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s: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(),a.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};Q(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",n),i("detach",o)};o=()=>{this.attached=!1,s("resize",n),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){Q(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},Q(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let n,o,a,r;for("dataset"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller["_"+s+"DatasetHoverStyle"]()),a=0,r=t.length;a{const i=this.getDatasetMeta(t);if(!i)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:i.data[e],index:e}}));!tt(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:Jt(t,this.chartArea,this._minPadding)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=ft(t),l=function(t,e,i,s){return i&&"mouseout"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,J(n.onHover,[t,a,this],this),r&&J(n.onClick,[t,a,this],this));const h=!tt(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if("mouseout"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}const un=()=>Q(dn.instances,(t=>t._plugins.invalidate())),fn=!0;function gn(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}Object.defineProperties(dn,{defaults:{enumerable:fn,value:bt},instances:{enumerable:fn,value:ln},overrides:{enumerable:fn,value:gt},registry:{enumerable:fn,value:Ws},version:{enumerable:fn,value:"3.7.1"},getChart:{enumerable:fn,value:hn},register:{enumerable:fn,value:(...t)=>{Ws.add(...t),un()}},unregister:{enumerable:fn,value:(...t)=>{Ws.remove(...t),un()}}});class pn{constructor(t){this.options=t||{}}formats(){return gn()}parse(t,e){return gn()}format(t,e){return gn()}add(t,e,i){return gn()}diff(t,e,i){return gn()}startOf(t,e,i){return gn()}endOf(t,e){return gn()}}pn.override=function(t){Object.assign(pn.prototype,t)};var mn={_date:pn};function xn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;et-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(ct(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;sMath.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function _n(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;ht.x,i="left",s="right"):(e=t.base=i?1:-1)}(c,e,o)*n,d===o&&(p-=c/2),h=p+c),p===e.getPixelForValue(o)){const t=Ct(c)*e.getLineWidthForValue(o)/2;p+=t,c-=t}return{size:c,base:p,head:h,center:h+c/2}}_calculateBarIndexPixels(t,e){const i=e.scale,s=this.options,n=s.skipNull,o=K(s.maxBarThickness,1/0);let a,r;if(e.grouped){const i=n?this._getStackCount(t):e.stackCount,l="flex"===s.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,{xScale:i,yScale:s}=e,n=this.getParsed(t),o=i.getLabelForValue(n.x),a=s.getLabelForValue(n.y),r=n._custom;return{label:e.label,value:"("+o+", "+a+(r?", "+r:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a}=this._cachedMeta,r=this.resolveDataElementOptions(e,s),l=this.getSharedOptions(r),h=this.includeOptions(s,l),c=o.axis,d=a.axis;for(let r=e;r""}}}};class Dn extends Ps{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,o,a=t=>+i[t];if(U(i[t])){const{key:t="value"}=this._parsing;a=e=>+lt(i[e],t)}for(n=t,o=t+e;nHt(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>Ht(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(kt,c,u),x=g(_t,h,d),b=g(_t+kt,c,u);s=(p-x)/2,n=(m-b)/2,o=-(p+x)/2,a=-(m+b)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(c,h,r),p=(i.width-o)/d,m=(i.height-o)/u,x=Math.max(Math.min(p,m)/2,0),b=Z(this.options.radius,x),_=(b-Math.max(b*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=f*b,this.offsetY=g*b,s.total=this.calculateTotal(),this.outerRadius=b-_*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-_*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/yt)}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,f=this.resolveDataElementOptions(e,s),g=this.getSharedOptions(f),p=this.includeOptions(s,g);let m,x=this._getRotation();for(m=0;m0&&!isNaN(t)?yt*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=Ri(e._parsed[t],i.options.locale);return{label:s[t]||"",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s"spacing"!==t,_indexable:t=>"spacing"!==t},Dn.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i}}=t.legend.options;return e.labels.map(((e,s)=>{const n=t.getDatasetMeta(0).controller.getStyle(s);return{text:e,fillStyle:n.backgroundColor,strokeStyle:n.borderColor,lineWidth:n.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(s),index:s}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}},tooltip:{callbacks:{title:()=>"",label(t){let e=t.label;const i=": "+t.formattedValue;return Y(e)?(e=e.slice(),e[0]+=i):e+=i,e}}}}};class Cn extends Ps{initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:n}=e,o=this.chart._animationsDisabled;let{start:a,count:r}=function(t,e,i){const s=e.length;let n=0,o=s;if(t._sorted){const{iScale:a,_parsed:r}=t,l=a.axis,{min:h,max:c,minDefined:d,maxDefined:u}=a.getUserBounds();d&&(n=jt(Math.min(re(r,a.axis,h).lo,i?s:re(e,l,a.getPixelForValue(h)).lo),0,s-1)),o=u?jt(Math.max(re(r,a.axis,c).hi+1,i?0:re(e,l,a.getPixelForValue(c)).hi+1),n,s)-n:s-n}return{start:n,count:o}}(e,s,o);this._drawStart=a,this._drawCount=r,function(t){const{xScale:e,yScale:i,_scaleRanges:s}=t,n={xmin:e.min,xmax:e.max,ymin:i.min,ymax:i.max};if(!s)return t._scaleRanges=n,!0;const o=s.xmin!==e.min||s.xmax!==e.max||s.ymin!==i.min||s.ymax!==i.max;return Object.assign(s,n),o}(e)&&(a=0,r=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!n._decimated,i.points=s;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(i,void 0,{animated:!o,options:l},t),this.updateElements(s,a,r,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a,_stacked:r,_dataset:l}=this._cachedMeta,h=this.resolveDataElementOptions(e,s),c=this.getSharedOptions(h),d=this.includeOptions(s,c),u=o.axis,f=a.axis,{spanGaps:g,segment:p}=this.options,m=Tt(g)?g:Number.POSITIVE_INFINITY,x=this.chart._animationsDisabled||n||"none"===s;let b=e>0&&this.getParsed(e-1);for(let h=e;h0&&i[u]-b[u]>m,p&&(g.parsed=i,g.raw=l.data[h]),d&&(g.options=c||this.resolveDataElementOptions(h,e.active?"active":s)),x||this.updateElement(e,h,g,s),b=i}this.updateSharedOptions(c,s,h)}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}Cn.id="line",Cn.defaults={datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1},Cn.overrides={scales:{_index_:{type:"category"},_value_:{type:"linear"}}};class On extends Ps{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=Ri(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:n}}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=this.getDataset(),r=o.options.animation,l=this._cachedMeta.rScale,h=l.xCenter,c=l.yCenter,d=l.getIndexAngle(0)-.5*_t;let u,f=d;const g=360/this.countVisibleElements();for(u=0;u{!isNaN(t.data[s])&&this.chart.getDataVisibility(s)&&i++})),i}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?It(this.resolveDataElementOptions(t,e).angle||i):0}}On.id="polarArea",On.defaults={dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0},On.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i}}=t.legend.options;return e.labels.map(((e,s)=>{const n=t.getDatasetMeta(0).controller.getStyle(s);return{text:e,fillStyle:n.backgroundColor,strokeStyle:n.borderColor,lineWidth:n.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(s),index:s}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}},tooltip:{callbacks:{title:()=>"",label:t=>t.chart.data.labels[t.dataIndex]+": "+t.formattedValue}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};class An extends Dn{}An.id="pie",An.defaults={cutout:0,rotation:0,circumference:360,radius:"100%"};class Tn extends Ps{getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(i[e.axis])}}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this.getDataset(),o=this._cachedMeta.rScale,a="reset"===s;for(let r=e;r"",label:t=>"("+t.label+", "+t.formattedValue+")"}}},scales:{x:{type:"linear"},y:{type:"linear"}}};var Rn=Object.freeze({__proto__:null,BarController:Sn,BubbleController:Pn,DoughnutController:Dn,LineController:Cn,PolarAreaController:On,PieController:An,RadarController:Tn,ScatterController:Ln});function En(t,e,i){const{startAngle:s,pixelMargin:n,x:o,y:a,outerRadius:r,innerRadius:l}=e;let h=n/r;t.beginPath(),t.arc(o,a,r,s-h,i+h),l>n?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+kt,s-kt),t.closePath(),t.clip()}function In(t,e,i,s){const n=Be(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return jt(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:jt(n.innerStart,0,a),innerEnd:jt(n.innerEnd,0,a)}}function zn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Fn(t,e,i,s,n){const{x:o,y:a,startAngle:r,pixelMargin:l,innerRadius:h}=e,c=Math.max(e.outerRadius+s+i-l,0),d=h>0?h+s+i+l:0;let u=0;const f=n-r;if(s){const t=((h>0?h-s:0)+(c>0?c-s:0))/2;u=(f-(0!==t?f*t/(t+s):f))/2}const g=(f-Math.max(.001,f*c-i/_t)/c)/2,p=r+g+u,m=n-g-u,{outerStart:x,outerEnd:b,innerStart:_,innerEnd:y}=In(e,d,c,m-p),v=c-x,w=c-b,M=p+x/v,k=m-b/w,S=d+_,P=d+y,D=p+_/S,C=m-y/P;if(t.beginPath(),t.arc(o,a,c,M,k),b>0){const e=zn(w,k,o,a);t.arc(e.x,e.y,b,k,m+kt)}const O=zn(P,m,o,a);if(t.lineTo(O.x,O.y),y>0){const e=zn(P,C,o,a);t.arc(e.x,e.y,y,m+kt,C+Math.PI)}if(t.arc(o,a,d,m-y/d,p+_/d,!0),_>0){const e=zn(S,D,o,a);t.arc(e.x,e.y,_,D+Math.PI,p-kt)}const A=zn(v,p,o,a);if(t.lineTo(A.x,A.y),x>0){const e=zn(v,M,o,a);t.arc(e.x,e.y,x,p-kt,M)}t.closePath()}function Bn(t,e,i,s,n){const{options:o}=e,{borderWidth:a,borderJoinStyle:r}=o,l="inner"===o.borderAlign;a&&(l?(t.lineWidth=2*a,t.lineJoin=r||"round"):(t.lineWidth=a,t.lineJoin=r||"bevel"),e.fullCircles&&function(t,e,i){const{x:s,y:n,startAngle:o,pixelMargin:a,fullCircles:r}=e,l=Math.max(e.outerRadius-a,0),h=e.innerRadius+a;let c;for(i&&En(t,e,o+yt),t.beginPath(),t.arc(s,n,h,o+yt,o,!0),c=0;c=yt||Ht(n,a,r),f=Yt(o,l+d,h+d);return u&&f}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius","circumference"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/2,n=(e.spacing||0)/2;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=i>yt?Math.floor(i/yt):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();let o=0;if(s){o=s/2;const e=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(e)*o,Math.sin(e)*o),this.circumference>=_t&&(o=s)}t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor;const a=function(t,e,i,s){const{fullCircles:n,startAngle:o,circumference:a}=e;let r=e.endAngle;if(n){Fn(t,e,i,s,o+yt);for(let e=0;er&&o>r;return{count:s,start:l,loop:e.loop,ilen:h(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[b(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[b(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(ig&&(g=i),m=(x*m+e)/++x):(_(),t.lineTo(e,i),u=s,x=0,f=g=i),p=i}_()}function Yn(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||i)?$n:jn}Vn.id="arc",Vn.defaults={borderAlign:"center",borderColor:"#fff",borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0},Vn.defaultRoutes={backgroundColor:"backgroundColor"};const Un="function"==typeof Path2D;function Xn(t,e,i,s){Un&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Wn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=Yn(e);for(const r of n)Wn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class qn extends Ds{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 i=this.options;if((i.tension||"monotone"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;ki(this._points,i,t,s,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=Ni(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,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Wi(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?Ai:t.tension||"monotone"===t.cubicInterpolationMode?Ti:Oi}(i);let l,h;for(l=0,h=o.length;l"borderDash"!==t&&"fill"!==t};class Gn extends Ds{constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.options,{x:n,y:o}=this.getProps(["x","y"],i);return Math.pow(t-n,2)+Math.pow(e-o,2){oo(t)}))}var ro={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void ao(t);const s=t.width;t.data.datasets.forEach(((e,n)=>{const{_data:o,indexAxis:a}=e,r=t.getDatasetMeta(n),l=o||e.data;if("y"===je([a,t.options.indexAxis]))return;if("line"!==r.type)return;const h=t.scales[r.xAxisID];if("linear"!==h.type&&"time"!==h.type)return;if(t.options.parsing)return;let{start:c,count:d}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=jt(re(e,o.axis,a).lo,0,i-1)),s=h?jt(re(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(r,l);if(d<=(i.threshold||4*s))return void oo(e);let u;switch($(o)&&(e._data=l,delete e.data,Object.defineProperty(e,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case"lttb":u=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;cu&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(l,c,d,s,i);break;case"min-max":u=function(t,e,i,s){let n,o,a,r,l,h,c,d,u,f,g=0,p=0;const m=[],x=e+i-1,b=t[e].x,_=t[x].x-b;for(n=e;nf&&(f=r,c=n),g=(p*g+o.x)/++p;else{const i=n-1;if(!$(h)&&!$(c)){const e=Math.min(h,c),s=Math.max(h,c);e!==d&&e!==i&&m.push({...t[e],x:g}),s!==d&&s!==i&&m.push({...t[s],x:g})}n>0&&i!==d&&m.push(t[i]),m.push(o),l=e,p=0,u=f=r,h=c=d=n}}return m}(l,c,d,s);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=u}))},destroy(t){ao(t)}};function lo(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=K(i&&i.target,i);return void 0===s&&(s=!!e.backgroundColor),!1!==s&&null!==s&&(!0===s?"origin":s)}(t);if(U(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return X(n)&&Math.floor(n)===n?("-"!==s[0]&&"+"!==s[0]||(n=e+n),!(n===e||n<0||n>=i)&&n):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}class ho{constructor(t){this.x=t.x,this.y=t.y,this.radius=t.radius}pathSegment(t,e,i){const{x:s,y:n,radius:o}=this;return e=e||{start:0,end:yt},t.arc(s,n,o,e.end,e.start,!0),!i.bounds}interpolate(t){const{x:e,y:i,radius:s}=this,n=t.angle;return{x:e+Math.cos(n)*s,y:i+Math.sin(n)*s,angle:n}}}function co(t){return(t.scale||{}).getPointPositionForValue?function(t){const{scale:e,fill:i}=t,s=e.options,n=e.getLabels().length,o=[],a=s.reverse?e.max:e.min,r=s.reverse?e.min:e.max;let l,h,c;if(c="start"===i?a:"end"===i?r:U(i)?i.value:e.getBaseValue(),s.grid.circular)return h=e.getPointPositionForValue(0,a),new ho({x:h.x,y:h.y,radius:e.getDistanceFromCenterForValue(c)});for(l=0;lt;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function fo(t,e,i){const s=[];for(let n=0;n{e=uo(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new qn({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function xo(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!X(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function bo(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[uo(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(h.x,i),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(c.x,i)}t.lineTo(e.first().x,i),t.closePath(),t.clip()}function _o(t,e,i,s){if(s)return;let n=e[t],o=i[t];return"angle"===t&&(n=Nt(n),o=Nt(o)),{property:t,start:n,end:o}}function yo(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function vo(t,e,i){const{top:s,bottom:n}=e.chart.chartArea,{property:o,start:a,end:r}=i||{};"x"===o&&(t.beginPath(),t.rect(a,s,r-a,n-s),t.clip())}function wo(t,e,i,s){const n=e.interpolate(i,s);n&&t.lineTo(n.x,n.y)}function Mo(t,e){const{line:i,target:s,property:n,color:o,scale:a}=e,r=function(t,e,i){const s=t.segments,n=t.points,o=e.points,a=[];for(const t of s){let{start:s,end:r}=t;r=uo(s,r,n);const l=_o(i,n[s],n[r],t.loop);if(!e.segments){a.push({source:t,target:l,start:n[s],end:n[r]});continue}const h=Wi(e,l);for(const e of h){const s=_o(i,o[e.start],o[e.end],e.loop),r=Vi(t,n,s);for(const t of r)a.push({source:t,target:e,start:{[i]:yo(l,s,"start",Math.max)},end:{[i]:yo(l,s,"end",Math.min)}})}}return a}(i,s,n);for(const{source:e,target:l,start:h,end:c}of r){const{style:{backgroundColor:r=o}={}}=e,d=!0!==s;t.save(),t.fillStyle=r,vo(t,a,d&&_o(n,h,c)),t.beginPath();const u=!!i.pathSegment(t,e);let f;if(d){u?t.closePath():wo(t,s,c,n);const e=!!s.pathSegment(t,l,{move:u,reverse:!0});f=u&&e,f||wo(t,s,h,n)}t.closePath(),t.fill(f?"evenodd":"nonzero"),t.restore()}}function ko(t,e,i){const s=po(e),{line:n,scale:o,axis:a}=e,r=n.options,l=r.fill,h=r.backgroundColor,{above:c=h,below:d=h}=l||{};s&&n.points.length&&(Qt(t,i),function(t,e){const{line:i,target:s,above:n,below:o,area:a,scale:r}=e,l=i._loop?"angle":e.axis;t.save(),"x"===l&&o!==n&&(bo(t,s,a.top),Mo(t,{line:i,target:s,color:n,scale:r,property:l}),t.restore(),t.save(),bo(t,s,a.bottom)),Mo(t,{line:i,target:s,color:o,scale:r,property:l}),t.restore()}(t,{line:n,target:s,above:c,below:d,area:i,scale:o,axis:a}),te(t))}var So={id:"filler",afterDatasetsUpdate(t,e,i){const s=(t.data.datasets||[]).length,n=[];let o,a,r,l;for(a=0;a=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&ko(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if("beforeDatasetsDraw"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;i&&ko(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;s&&!1!==s.fill&&"beforeDatasetDraw"===i.drawTime&&ko(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Po=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class Do extends Ds{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,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,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=J(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=He(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=Po(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,n,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign="left",n.textBaseline="middle";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const p=i+e/2+n.measureText(t.text).width;o>0&&u+s+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:s},d=Math.max(d,p),u+=s+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:o}}=this,a=Ei(o,this.left,this.width);if(this.isHorizontal()){let o=0,r=n(i,this.left+s,this.right-this.lineWidths[o]);for(const l of e)o!==l.row&&(o=l.row,r=n(i,this.left+s,this.right-this.lineWidths[o])),l.top+=this.top+t+s,l.left=a.leftForLtr(a.x(r),l.width),r+=l.width+s}else{let o=0,r=n(i,this.top+t+s,this.bottom-this.columnSizes[o].height);for(const l of e)l.col!==o&&(o=l.col,r=n(i,this.top+t+s,this.bottom-this.columnSizes[o].height)),l.top=r,l.left+=this.left+s,l.left=a.leftForLtr(a.x(l.left),l.width),r+=l.height+s}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Qt(t,this),this._draw(),te(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:a,labels:r}=t,l=bt.color,h=Ei(t.rtl,this.left,this.width),c=He(r.font),{color:d,padding:u}=r,f=c.size,g=f/2;let p;this.drawTitle(),s.textAlign=h.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:m,boxHeight:x,itemHeight:b}=Po(r,f),_=this.isHorizontal(),y=this._computeTitleHeight();p=_?{x:n(a,this.left+u,this.right-i[0]),y:this.top+u+y,line:0}:{x:this.left+u,y:n(a,this.top+y+u,this.bottom-e[0].height),line:0},Ii(this.ctx,t.textDirection);const v=b+u;this.legendItems.forEach(((w,M)=>{s.strokeStyle=w.fontColor||d,s.fillStyle=w.fontColor||d;const k=s.measureText(w.text).width,S=h.textAlign(w.textAlign||(w.textAlign=r.textAlign)),P=m+g+k;let D=p.x,C=p.y;h.setWidth(this.width),_?M>0&&D+P+u>this.right&&(C=p.y+=v,p.line++,D=p.x=n(a,this.left+u,this.right-i[p.line])):M>0&&C+v>this.bottom&&(D=p.x=D+e[p.line].width+u,p.line++,C=p.y=n(a,this.top+y+u,this.bottom-e[p.line].height));!function(t,e,i){if(isNaN(m)||m<=0||isNaN(x)||x<0)return;s.save();const n=K(i.lineWidth,1);if(s.fillStyle=K(i.fillStyle,l),s.lineCap=K(i.lineCap,"butt"),s.lineDashOffset=K(i.lineDashOffset,0),s.lineJoin=K(i.lineJoin,"miter"),s.lineWidth=n,s.strokeStyle=K(i.strokeStyle,l),s.setLineDash(K(i.lineDash,[])),r.usePointStyle){const o={radius:m*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},a=h.xPlus(t,m/2);Zt(s,o,a,e+g)}else{const o=e+Math.max((f-x)/2,0),a=h.leftForLtr(t,m),r=We(i.borderRadius);s.beginPath(),Object.values(r).some((t=>0!==t))?oe(s,{x:a,y:o,w:m,h:x,radius:r}):s.rect(a,o,m,x),s.fill(),0!==n&&s.stroke()}s.restore()}(h.x(D),C,w),D=o(S,D+m+g,_?D+P:this.right,t.rtl),function(t,e,i){se(s,i.text,t,e+b/2,c,{strikethrough:i.hidden,textAlign:h.textAlign(i.textAlign)})}(h.x(D),C,w),_?p.x+=P+u:p.y+=v})),zi(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=He(e.font),o=Ne(e.padding);if(!e.display)return;const a=Ei(t.rtl,this.left,this.width),r=this.ctx,l=e.position,h=i.size/2,c=o.top+h;let d,u=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),d=this.top+c,u=n(t.align,u,this.right-f);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);d=c+n(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const g=n(l,u,u+f);r.textAlign=a.textAlign(s(l)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=i.string,se(r,e.text,g,d,i)}_computeTitleHeight(){const t=this.options.title,e=He(t.font),i=Ne(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(Yt(t,this.left,this.right)&&Yt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;it.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const a=t.controller.getStyle(i?0:void 0),r=Ne(a.borderWidth);return{text:e[t.index].label,fillStyle:a.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:(r.width+r.height)/4,strokeStyle:a.borderColor,pointStyle:s||a.pointStyle,rotation:a.rotation,textAlign:n||a.textAlign,borderRadius:0,datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class Oo extends Ds{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=Y(i.text)?i.text.length:1;this._padding=Ne(i.padding);const n=s*He(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=n:this.width=n}isHorizontal(){const t=this.options.position;return"top"===t||"bottom"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:o,options:a}=this,r=a.align;let l,h,c,d=0;return this.isHorizontal()?(h=n(r,i,o),c=e+t,l=o-i):("left"===a.position?(h=i+t,c=n(r,s,e),d=-.5*_t):(h=o-t,c=n(r,e,s),d=.5*_t),l=s-e),{titleX:h,titleY:c,maxWidth:l,rotation:d}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=He(e.font),n=i.lineHeight/2+this._padding.top,{titleX:o,titleY:a,maxWidth:r,rotation:l}=this._drawArgs(n);se(t,e.text,0,0,i,{color:e.color,maxWidth:r,rotation:l,textAlign:s(e.align),textBaseline:"middle",translation:[o,a]})}}var Ao={id:"title",_element:Oo,start(t,e,i){!function(t,e){const i=new Oo({ctx:t.ctx,options:e,chart:t});ni.configure(t,i,e),ni.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ni.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ni.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const To=new WeakMap;var Lo={id:"subtitle",start(t,e,i){const s=new Oo({ctx:t.ctx,options:i,chart:t});ni.configure(t,s,i),ni.addBox(t,s),To.set(t,s)},stop(t){ni.removeBox(t,To.get(t)),To.delete(t)},beforeUpdate(t,e,i){const s=To.get(t);ni.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Ro={average(t){if(!t.length)return!1;let e,i,s=0,n=0,o=0;for(e=0,i=t.length;e-1?t.split("\n"):t}function zo(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Fo(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=He(e.bodyFont),h=He(e.titleFont),c=He(e.footerFont),d=o.length,u=n.length,f=s.length,g=Ne(e.padding);let p=g.height,m=0,x=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(x+=t.beforeBody.length+t.afterBody.length,d&&(p+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),x){p+=f*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(x-f)*l.lineHeight+(x-1)*e.bodySpacing}u&&(p+=e.footerMarginTop+u*c.lineHeight+(u-1)*e.footerSpacing);let b=0;const _=function(t){m=Math.max(m,i.measureText(t).width+b)};return i.save(),i.font=h.string,Q(t.title,_),i.font=l.string,Q(t.beforeBody.concat(t.afterBody),_),b=e.displayColors?a+2+e.boxPadding:0,Q(s,(t=>{Q(t.before,_),Q(t.lines,_),Q(t.after,_)})),b=0,i.font=c.string,Q(t.footer,_),i.restore(),m+=g.width,{width:m,height:p}}function Bo(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h="center";return"center"===s?h=n<=(r+l)/2?"left":"right":n<=o/2?h="left":n>=a-o/2&&(h="right"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return"left"===t&&n+o+a>e.width||"right"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h="center"),h}function Vo(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return it.height-s/2?"bottom":"center"}(t,i);return{xAlign:i.xAlign||e.xAlign||Bo(t,e,i,s),yAlign:s}}function Wo(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=We(a);let g=function(t,e){let{x:i,width:s}=t;return"right"===e?i-=s:"center"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return"top"===e?s+=i:s-="bottom"===e?n+i:n/2,s}(e,l,h);return"center"===l?"left"===r?g+=h:"right"===r&&(g-=h):"left"===r?g-=Math.max(c,u)+n:"right"===r&&(g+=Math.max(d,f)+n),{x:jt(g,0,s.width-e.width),y:jt(p,0,s.height-e.height)}}function No(t,e,i){const s=Ne(i.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-s.right:t.x+s.left}function Ho(t){return Eo([],Io(t))}function jo(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}class $o extends Ds{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||t._chart,this._chart=this.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,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,n=new gs(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(n)),n}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,i=this._tooltipItems,Ye(t,{tooltip:e,tooltipItems:i,type:"tooltip"})));var t,e,i}getTitle(t,e){const{callbacks:i}=e,s=i.beforeTitle.apply(this,[t]),n=i.title.apply(this,[t]),o=i.afterTitle.apply(this,[t]);let a=[];return a=Eo(a,Io(s)),a=Eo(a,Io(n)),a=Eo(a,Io(o)),a}getBeforeBody(t,e){return Ho(e.callbacks.beforeBody.apply(this,[t]))}getBody(t,e){const{callbacks:i}=e,s=[];return Q(t,(t=>{const e={before:[],lines:[],after:[]},n=jo(i,t);Eo(e.before,Io(n.beforeLabel.call(this,t))),Eo(e.lines,n.label.call(this,t)),Eo(e.after,Io(n.afterLabel.call(this,t))),s.push(e)})),s}getAfterBody(t,e){return Ho(e.callbacks.afterBody.apply(this,[t]))}getFooter(t,e){const{callbacks:i}=e,s=i.beforeFooter.apply(this,[t]),n=i.footer.apply(this,[t]),o=i.afterFooter.apply(this,[t]);let a=[];return a=Eo(a,Io(s)),a=Eo(a,Io(n)),a=Eo(a,Io(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;at.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),Q(l,(e=>{const i=jo(t.callbacks,e);s.push(i.labelColor.call(this,e)),n.push(i.labelPointStyle.call(this,e)),o.push(i.labelTextColor.call(this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=Ro[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Fo(this,i),a=Object.assign({},t,e),r=Vo(this.chart,i,a),l=Wo(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=We(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,x,b,_,y;return"center"===n?(_=u+g/2,"left"===s?(p=d,m=p-o,b=_+o,y=_-o):(p=d+f,m=p+o,b=_-o,y=_+o),x=p):(m="left"===s?d+Math.max(r,h)+o:"right"===s?d+f-Math.max(l,c)-o:this.caretX,"top"===n?(b=u,_=b-o,p=m-o,x=m+o):(b=u+g,_=b+o,p=m+o,x=m-o),y=b),{x1:p,x2:m,x3:x,y1:b,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Ei(i.rtl,this.x,this.width);for(t.x=No(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline="middle",o=He(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,oe(t,{x:e,y:g,w:l,h:r,radius:a}),t.fill(),t.stroke(),t.fillStyle=o.backgroundColor,t.beginPath(),oe(t,{x:i,y:g+1,w:l-2,h:r-2,radius:a}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,l,r),t.strokeRect(e,g,l,r),t.fillStyle=o.backgroundColor,t.fillRect(i,g+1,l-2,r-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=He(i.bodyFont);let d=c.lineHeight,u=0;const f=Ei(i.rtl,this.x,this.width),g=function(i){e.fillText(i,f.x(t.x+u),t.y+d/2),t.y+=d+n},p=f.textAlign(o);let m,x,b,_,y,v,w;for(e.textAlign=o,e.textBaseline="middle",e.font=c.string,t.x=No(this,p,i),e.fillStyle=i.bodyColor,Q(this.beforeBody,g),u=a&&"right"!==p?"center"===o?l/2+h:l+2+h:0,_=0,v=s.length;_0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=Ro[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Fo(this,t),a=Object.assign({},i,this._size),r=Vo(e,t,a),l=Wo(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=Ne(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=i,this.drawBackground(n,t,s,e),Ii(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),zi(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!tt(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!tt(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if("mouseout"===t.type)return[];if(!s)return e;const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=Ro[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}$o.positioners=Ro;var Yo={id:"tooltip",_element:$o,positioners:Ro,afterInit(t,e,i){i&&(t.tooltip=new $o({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip,i={tooltip:e};!1!==t.notifyPlugins("beforeTooltipDraw",i)&&(e&&e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",i))},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.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:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:{beforeTitle:H,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&"dataset"===this.options.mode)return e.dataset.label||"";if(e.label)return e.label;if(s>0&&e.dataIndex"filter"!==t&&"itemSort"!==t&&"external"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]},Uo=Object.freeze({__proto__:null,Decimation:ro,Filler:So,Legend:Co,SubTitle:Lo,Title:Ao,Tooltip:Yo});function Xo(t,e,i,s){const n=t.indexOf(e);if(-1===n)return((t,e,i,s)=>("string"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}class qo extends Bs{constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if($(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:jt(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:Xo(i,t,K(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){const e=this.getLabels();return t>=0&&te.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}function Ko(t,e,{horizontal:i,minRotation:s}){const n=It(s),o=(i?Math.sin(n):Math.cos(n))||.001,a=.75*e*(""+t).length;return Math.min(e/o,a)}qo.id="category",qo.defaults={ticks:{callback:qo.prototype.getLabelForValue}};class Go extends Bs{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)||("number"==typeof t||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:n}=this;const o=t=>s=e?s:t,a=t=>n=i?n:t;if(t){const t=Ct(s),e=Ct(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=1;(n>=Number.MAX_SAFE_INTEGER||s<=Number.MIN_SAFE_INTEGER)&&(e=Math.abs(.05*n)),a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const s=function(t,e){const i=[],{bounds:s,step:n,min:o,max:a,precision:r,count:l,maxTicks:h,maxDigits:c,includeBounds:d}=t,u=n||1,f=h-1,{min:g,max:p}=e,m=!$(o),x=!$(a),b=!$(l),_=(p-g)/(c+1);let y,v,w,M,k=Ot((p-g)/f/u)*u;if(k<1e-14&&!m&&!x)return[{value:g},{value:p}];M=Math.ceil(p/k)-Math.floor(g/k),M>f&&(k=Ot(M*k/f/u)*u),$(r)||(y=Math.pow(10,r),k=Math.ceil(k*y)/y),"ticks"===s?(v=Math.floor(g/k)*k,w=Math.ceil(p/k)*k):(v=g,w=p),m&&x&&n&&Rt((a-o)/n,k/1e3)?(M=Math.round(Math.min((a-o)/k,h)),k=(a-o)/M,v=o,w=a):b?(v=m?o:v,w=x?a:w,M=l-1,k=(w-v)/M):(M=(w-v)/k,M=Lt(M,Math.round(M),k/1e3)?Math.round(M):Math.ceil(M));const S=Math.max(Ft(k),Ft(v));y=Math.pow(10,$(r)?S:r),v=Math.round(v*y)/y,w=Math.round(w*y)/y;let P=0;for(m&&(d&&v!==o?(i.push({value:o}),v0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=X(t)?Math.max(0,t):null,this.max=X(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t,a=(t,e)=>Math.pow(10,Math.floor(Dt(t))+e);i===s&&(i<=0?(n(1),o(10)):(n(a(i,-1)),o(a(s,1)))),i<=0&&n(a(s,-1)),s<=0&&o(a(i,1)),this._zero&&this.min!==this._suggestedMin&&i===a(this.min,0)&&n(a(i,-1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=function(t,e){const i=Math.floor(Dt(e.max)),s=Math.ceil(e.max/Math.pow(10,i)),n=[];let o=q(t.min,Math.pow(10,Math.floor(Dt(e.min)))),a=Math.floor(Dt(o)),r=Math.floor(o/Math.pow(10,a)),l=a<0?Math.pow(10,Math.abs(a)):1;do{n.push({value:o,major:Jo(o)}),++r,10===r&&(r=1,++a,l=a>=0?1:l),o=Math.round(r*Math.pow(10,a)*l)/l}while(an?{start:e-i,end:e}:{start:e,end:e+i}}function ia(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],n=[],o=t._pointLabels.length,a=t.options.pointLabels,r=a.centerPointLabels?_t/o:0;for(let d=0;de.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.starte.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function na(t){return 0===t||180===t?"center":t<180?"left":"right"}function oa(t,e,i){return"right"===i?t-=e:"center"===i&&(t-=e/2),t}function aa(t,e,i){return 90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e),t}function ra(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,yt);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;o{const i=J(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?ia(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return Nt(t*(yt/(this._pointLabels.length||1))+It(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if($(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if($(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t=0;n--){const e=s.setContext(t.getPointLabelContext(n)),o=He(e.font),{x:a,y:r,textAlign:l,left:h,top:c,right:d,bottom:u}=t._pointLabelItems[n],{backdropColor:f}=e;if(!$(f)){const t=Ne(e.backdropPadding);i.fillStyle=f,i.fillRect(h-t.left,c-t.top,d-h+t.width,u-c+t.height)}se(i,t._pointLabels[n],a,r+o.lineHeight/2,o,{color:e.color,textAlign:l,textBaseline:"middle"})}}(this,n),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e){a=this.getDistanceFromCenterForValue(t.value);!function(t,e,i,s){const n=t.ctx,o=e.circular,{color:a,lineWidth:r}=e;!o&&!s||!a||!r||i<0||(n.save(),n.strokeStyle=a,n.lineWidth=r,n.setLineDash(e.borderDash),n.lineDashOffset=e.borderDashOffset,n.beginPath(),ra(t,i,o,s),n.closePath(),n.stroke(),n.restore())}(this,s.setContext(this.getContext(e-1)),a,n)}})),i.display){for(t.save(),o=n-1;o>=0;o--){const s=i.setContext(this.getPointLabelContext(o)),{color:n,lineWidth:l}=s;l&&n&&(t.lineWidth=l,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,a=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),r=this.getPointPosition(o,a),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(r.x,r.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((s,a)=>{if(0===a&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=He(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=Ne(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}se(t,s.label,0,-n,l,{color:r.color})})),t.restore()}drawTitle(){}}la.id="radialLinear",la.defaults={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:Os.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}},la.defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"},la.descriptors={angleLines:{_fallback:"grid"}};const ha={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}},ca=Object.keys(ha);function da(t,e){return t-e}function ua(t,e){if($(e))return null;const i=t._adapter,{parser:s,round:n,isoWeekday:o}=t._parseOpts;let a=e;return"function"==typeof s&&(a=s(a)),X(a)||(a="string"==typeof s?i.parse(a,s):i.parse(a)),null===a?null:(n&&(a="week"!==n||!Tt(o)&&!0!==o?i.startOf(a,n):i.startOf(a,"isoWeek",o)),+a)}function fa(t,e,i,s){const n=ca.length;for(let o=ca.indexOf(t);o=e?i[s]:i[n]]=!0}}else t[e]=!0}function pa(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a=0&&(e[l].major=!0);return e}(t,s,n,i):s}class ma extends Bs{constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e){const i=t.time||(t.time={}),s=this._adapter=new mn._date(t.adapters.date);ot(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:ua(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||"day";let{min:s,max:n,minDefined:o,maxDefined:a}=this.getUserBounds();function r(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),a||isNaN(t.max)||(n=Math.max(n,t.max))}o&&a||(r(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||r(this.getMinMax(!1))),s=X(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=X(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s="labels"===i.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=he(s,n,this.max);return this._unit=e.unit||(i.autoSkip?fa(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=ca.length-1;o>=ca.indexOf(i);o--){const i=ca[o];if(ha[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return ca[i?ca.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&"year"!==this._unit?function(t){for(let e=ca.indexOf(t)+1,i=ca.length;e1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+o);const f="data"===s.ticks.source&&this.getDataTimestamps();for(c=u,d=0;ct-e)).map((t=>+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.time.displayFormats,a=this._unit,r=this._majorUnit,l=a&&o[a],h=r&&o[r],c=i[e],d=r&&h&&c&&c.major,u=this._adapter.format(t,s||(d?h:l)),f=n.ticks.callback;return f?J(f,[u,e,i],this):u}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=re(t,"pos",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=re(t,"time",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}ma.id="time",ma.defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",major:{enabled:!1}}};class ba extends ma{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=xa(e,this.min),this._tableRange=xa(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;o Array.prototype.slice.call(args)); - let ticking = false; - let args = []; - return function(...rest) { - args = updateArgs(rest); - if (!ticking) { - ticking = true; - requestAnimFrame.call(window, () => { - ticking = false; - fn.apply(thisArg, args); - }); - } - }; -} -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 noop() {} -const uid = (function() { - let id = 0; - return function() { - return id++; - }; -}()); -function isNullOrUndef(value) { - return value === null || typeof value === 'undefined'; -} -function isArray(value) { - if (Array.isArray && Array.isArray(value)) { - return true; - } - const type = Object.prototype.toString.call(value); - if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { - return true; - } - return false; -} -function isObject(value) { - return value !== null && Object.prototype.toString.call(value) === '[object Object]'; -} -const isNumberFinite = (value) => (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 toPercentage = (value, dimension) => - typeof value === 'string' && value.endsWith('%') ? - parseFloat(value) / 100 - : value / dimension; -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; - if (reverse) { - for (i = len - 1; i >= 0; i--) { - fn.call(thisArg, loopable[i], i); - } - } else { - 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$1(source) { - if (isArray(source)) { - return source.map(clone$1); - } - if (isObject(source)) { - const target = Object.create(null); - const keys = Object.keys(source); - const klen = keys.length; - let k = 0; - for (; k < klen; ++k) { - target[keys[k]] = clone$1(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$1(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; - for (let i = 0; i < ilen; ++i) { - source = sources[i]; - if (!isObject(source)) { - continue; - } - const keys = Object.keys(source); - for (let k = 0, klen = keys.length; k < klen; ++k) { - merger(keys[k], target, source, 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$1(sval); - } -} -function _deprecated(scope, value, previous, current) { - if (value !== undefined) { - console.warn(scope + ': "' + previous + - '" is deprecated. Please use "' + current + '" instead'); - } -} -const emptyString = ''; -const dot = '.'; -function indexOfDotOrLength(key, start) { - const idx = key.indexOf(dot, start); - return idx === -1 ? key.length : idx; -} -function resolveObjectKey(obj, key) { - if (key === emptyString) { - return obj; - } - let pos = 0; - let idx = indexOfDotOrLength(key, pos); - while (obj && idx > pos) { - obj = obj[key.substr(pos, idx - pos)]; - pos = idx + 1; - idx = indexOfDotOrLength(key, pos); - } - return 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 niceNum(range) { - const roundedRange = Math.round(range); - range = almostEquals(range, roundedRange, range / 1000) ? 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 isNumber(n) { - return !isNaN(parseFloat(n)) && isFinite(n); -} -function almostEquals(x, y, epsilon) { - return Math.abs(x - y) < epsilon; -} -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; -} - -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, -}; - -/*! - * @kurkle/color v0.1.9 - * https://github.com/kurkle/color#readme - * (c) 2020 Jukka Kurkela - * Released under the MIT License - */ -const map = {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 & 0xF]; -const h2 = (b) => hex[(b & 0xF0) >> 4] + hex[b & 0xF]; -const eq = (b) => (((b & 0xF0) >> 4) === (b & 0xF)); -function isShort(v) { - return 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[str[1]] * 17, - g: 255 & map[str[2]] * 17, - b: 255 & map[str[3]] * 17, - a: len === 5 ? map[str[4]] * 17 : 255 - }; - } else if (len === 7 || len === 9) { - ret = { - r: map[str[1]] << 4 | map[str[2]], - g: map[str[3]] << 4 | map[str[4]], - b: map[str[5]] << 4 | map[str[6]], - a: len === 9 ? (map[str[7]] << 4 | map[str[8]]) : 255 - }; - } - } - return ret; -} -function hexString(v) { - var f = isShort(v) ? h1 : h2; - return v - ? '#' + f(v.r) + f(v.g) + f(v.b) + (v.a < 255 ? f(v.a) : '') - : v; -} -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 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 = 255 & (m[8] ? p2b(v) : v * 255); - } - r = +m[1]; - g = +m[3]; - b = +m[5]; - r = 255 & (m[2] ? p2b(r) : r); - g = 255 & (m[4] ? p2b(g) : g); - b = 255 & (m[6] ? p2b(b) : b); - return { - r: r, - g: g, - b: b, - a: 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 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 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 = max === r - ? ((g - b) / d) + (g < b ? 6 : 0) - : max === g - ? (b - r) / d + 2 - : (r - g) / d + 4; - 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: 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$1 = { - 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 = { - 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); - const tkeys = Object.keys(map$1); - 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$1[k]); - } - k = parseInt(names[ok], 16); - unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF]; - } - return unpacked; -} -let names$1; -function nameParse(str) { - if (!names$1) { - names$1 = unpack(); - names$1.transparent = [0, 0, 0, 0]; - } - const a = names$1[str.toLowerCase()]; - return a && { - r: a[0], - g: a[1], - b: a[2], - a: a.length === 4 ? a[3] : 255 - }; -} -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(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(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(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) : this._rgb; - } - hexString() { - return this._valid ? hexString(this._rgb) : this._rgb; - } - hslString() { - return this._valid ? hslString(this._rgb) : this._rgb; - } - mix(color, weight) { - const me = this; - if (color) { - const c1 = me.rgb; - const c2 = color.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.0; - w2 = 1 - w1; - c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5; - c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5; - c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5; - c1.a = p * c1.a + (1 - p) * c2.a; - me.rgb = c1; - } - return me; - } - 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; - } -} -function index_esm(input) { - return new Color(input); -} - -const isPatternOrGradient = (value) => value instanceof CanvasGradient || value instanceof CanvasPattern; -function color(value) { - return isPatternOrGradient(value) ? value : index_esm(value); -} -function getHoverColor(value) { - return isPatternOrGradient(value) - ? value - : index_esm(value).saturate(0.5).darken(0.1).hexString(); -} - -const overrides = Object.create(null); -const descriptors = 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] = 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(_descriptors) { - this.animation = undefined; - 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 - }; - this.maintainAspectRatio = true; - this.onHover = null; - this.onClick = null; - this.parsing = true; - this.plugins = {}; - this.responsive = true; - this.scale = undefined; - this.scales = {}; - this.showLine = true; - this.drawActiveElementsOnTop = true; - this.describe(_descriptors); - } - set(scope, values) { - return set(this, scope, values); - } - get(scope) { - return getScope$1(this, scope); - } - describe(scope, values) { - return set(descriptors, scope, values); - } - override(scope, values) { - return set(overrides, scope, values); - } - route(scope, name, targetScope, targetName) { - const scopeObject = getScope$1(this, scope); - 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; - } - } - }); - } -} -var defaults = new Defaults({ - _scriptable: (name) => !name.startsWith('on'), - _indexable: (name) => name !== 'events', - hover: { - _fallback: 'interaction' - }, - interaction: { - _scriptable: false, - _indexable: false, - } -}); - -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 _longestText(ctx, font, arrayOfThings, cache) { - cache = cache || {}; - let data = cache.data = cache.data || {}; - let gc = cache.garbageCollect = cache.garbageCollect || []; - if (cache.font !== font) { - data = cache.data = {}; - gc = cache.garbageCollect = []; - cache.font = font; - } - ctx.save(); - ctx.font = font; - let longest = 0; - const ilen = arrayOfThings.length; - let i, j, jlen, thing, nestedThing; - for (i = 0; i < ilen; i++) { - thing = arrayOfThings[i]; - if (thing !== undefined && thing !== null && isArray(thing) !== true) { - longest = _measureText(ctx, data, gc, longest, thing); - } else if (isArray(thing)) { - for (j = 0, jlen = thing.length; j < jlen; j++) { - nestedThing = thing[j]; - if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) { - longest = _measureText(ctx, data, gc, longest, nestedThing); - } - } - } - } - ctx.restore(); - const gcLen = gc.length / 2; - if (gcLen > arrayOfThings.length) { - for (i = 0; i < gcLen; i++) { - delete data[gc[i]]; - } - gc.splice(0, gcLen); - } - 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) { - 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) { - let type, xOffset, yOffset, size, cornerRadius; - 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: - ctx.arc(x, y, radius, 0, TAU); - ctx.closePath(); - break; - case 'triangle': - ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - ctx.closePath(); - break; - case 'rectRounded': - cornerRadius = radius * 0.516; - size = radius - cornerRadius; - xOffset = Math.cos(rad + QUARTER_PI) * size; - yOffset = Math.sin(rad + QUARTER_PI) * size; - ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); - ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); - ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); - ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); - ctx.closePath(); - break; - case 'rect': - if (!rotation) { - size = Math.SQRT1_2 * radius; - ctx.rect(x - size, y - size, 2 * size, 2 * size); - break; - } - rad += QUARTER_PI; - case 'rectRot': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + yOffset, y - xOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.lineTo(x - yOffset, y + xOffset); - ctx.closePath(); - break; - case 'crossRot': - rad += QUARTER_PI; - case 'cross': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'star': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - rad += QUARTER_PI; - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'line': - xOffset = 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) * radius, y + Math.sin(rad) * radius); - 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.0; - 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 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 (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 += font.lineHeight; - } - ctx.restore(); -} -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 addRoundedRectPath(ctx, rect) { - const {x, y, w, h, radius} = rect; - ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_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 = new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); -const FONT_STYLE = new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/); -function toLineHeight(value, size) { - const matches = ('' + value).match(LINE_HEIGHT); - if (!matches || 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 = ''; - } - 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, index, info) { - let cacheable = true; - let i, ilen, value; - for (i = 0, ilen = inputs.length; i < ilen; ++i) { - value = inputs[i]; - if (value === undefined) { - continue; - } - if (context !== undefined && typeof value === 'function') { - value = value(context); - cacheable = false; - } - if (index !== undefined && isArray(value)) { - value = value[index % value.length]; - cacheable = false; - } - if (value !== undefined) { - if (info && !cacheable) { - info.cacheable = false; - } - 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 _lookup(table, value, cmp) { - cmp = cmp || ((index) => table[index] < 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) => - _lookup(table, value, index => table[index][key] < value); -const _rlookupByKey = (table, key, value) => - _lookup(table, value, index => table[index][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 index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - if (listeners.length > 0) { - return; - } - arrayEvents.forEach((key) => { - delete array[key]; - }); - delete array._chartjs; -} -function _arrayUnique(items) { - const set = new Set(); - let i, ilen; - for (i = 0, ilen = items.length; i < ilen; ++i) { - set.add(items[i]); - } - if (set.size === ilen) { - return items; - } - return Array.from(set); -} - -function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback, getTarget = () => scopes[0]) { - if (!defined(fallback)) { - fallback = _resolve('_fallback', scopes); - } - const cache = { - [Symbol.toStringTag]: 'Object', - _cacheable: true, - _scopes: scopes, - _rootScopes: rootScopes, - _fallback: fallback, - _getTarget: getTarget, - override: (scope) => _createResolver([scope, ...scopes], prefixes, rootScopes, fallback), - }; - return new Proxy(cache, { - deleteProperty(target, prop) { - delete target[prop]; - delete target._keys; - delete scopes[0][prop]; - return true; - }, - get(target, prop) { - return _cached(target, prop, - () => _resolveWithPrefixes(prop, prefixes, scopes, target)); - }, - getOwnPropertyDescriptor(target, prop) { - return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop); - }, - getPrototypeOf() { - return Reflect.getPrototypeOf(scopes[0]); - }, - has(target, prop) { - return getKeysFromAllScopes(target).includes(prop); - }, - ownKeys(target) { - return getKeysFromAllScopes(target); - }, - 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: new Set(), - _descriptors: _descriptors(proxy, descriptorDefaults), - setContext: (ctx) => _attachContext(proxy, ctx, subProxy, descriptorDefaults), - override: (scope) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults) - }; - return new Proxy(cache, { - deleteProperty(target, prop) { - delete target[prop]; - delete proxy[prop]; - return true; - }, - get(target, prop, receiver) { - return _cached(target, prop, - () => _resolveWithContext(target, prop, receiver)); - }, - getOwnPropertyDescriptor(target, prop) { - return target._descriptors.allKeys - ? Reflect.has(proxy, prop) ? {enumerable: true, configurable: true} : undefined - : Reflect.getOwnPropertyDescriptor(proxy, prop); - }, - getPrototypeOf() { - return Reflect.getPrototypeOf(proxy); - }, - has(target, prop) { - return Reflect.has(proxy, prop); - }, - ownKeys() { - return Reflect.ownKeys(proxy); - }, - set(target, prop, value) { - proxy[prop] = value; - delete target[prop]; - return true; - } - }); -} -function _descriptors(proxy, defaults = {scriptable: true, indexable: true}) { - const {_scriptable = defaults.scriptable, _indexable = defaults.indexable, _allKeys = defaults.allKeys} = proxy; - return { - allKeys: _allKeys, - scriptable: _scriptable, - indexable: _indexable, - isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable, - isIndexable: isFunction(_indexable) ? _indexable : () => _indexable - }; -} -const readKey = (prefix, name) => prefix ? prefix + _capitalize(name) : name; -const needsSubResolver = (prop, value) => isObject(value) && prop !== 'adapters' && - (Object.getPrototypeOf(value) === null || value.constructor === Object); -function _cached(target, prop, resolve) { - if (Object.prototype.hasOwnProperty.call(target, prop)) { - return target[prop]; - } - const value = resolve(); - target[prop] = value; - return value; -} -function _resolveWithContext(target, prop, receiver) { - const {_proxy, _context, _subProxy, _descriptors: descriptors} = target; - let value = _proxy[prop]; - if (isFunction(value) && descriptors.isScriptable(prop)) { - value = _resolveScriptable(prop, value, target, receiver); - } - if (isArray(value) && value.length) { - value = _resolveArray(prop, value, target, descriptors.isIndexable); - } - if (needsSubResolver(prop, value)) { - value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors); - } - return value; -} -function _resolveScriptable(prop, value, 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); - value = value(_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: descriptors} = target; - if (defined(_context.index) && isIndexable(prop)) { - value = 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], descriptors)); - } - } - 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) : undefined; -function addScopes(set, parentScopes, key, parentFallback, value) { - for (const parent of parentScopes) { - const scope = getScope(key, parent); - if (scope) { - set.add(scope); - const fallback = resolveFallback(scope._fallback, key, value); - if (defined(fallback) && fallback !== key && fallback !== parentFallback) { - return fallback; - } - } else if (scope === false && defined(parentFallback) && 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 set = new Set(); - set.add(value); - let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value); - if (key === null) { - return false; - } - if (defined(fallback) && fallback !== prop) { - key = addScopesFromKey(set, allScopes, fallback, key, value); - if (key === null) { - return false; - } - } - return _createResolver(Array.from(set), [''], rootScopes, fallback, - () => subGetTarget(resolver, prop, value)); -} -function addScopesFromKey(set, allScopes, key, fallback, item) { - while (key) { - key = addScopes(set, 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 (defined(value)) { - return needsSubResolver(prop, value) - ? createSubResolver(scopes, proxy, prop, value) - : value; - } - } -} -function _resolve(key, scopes) { - for (const scope of scopes) { - if (!scope) { - continue; - } - const value = scope[key]; - if (defined(value)) { - return value; - } - } -} -function getKeysFromAllScopes(target) { - let keys = target._keys; - if (!keys) { - keys = target._keys = resolveKeysFromAllScopes(target._scopes); - } - return keys; -} -function resolveKeysFromAllScopes(scopes) { - const set = new Set(); - for (const scope of scopes) { - for (const key of Object.keys(scope).filter(k => !k.startsWith('_'))) { - set.add(key); - } - } - return Array.from(set); -} - -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) => window.getComputedStyle(element, null); -function getStyle(el, property) { - return getComputedStyle(el).getPropertyValue(property); -} -const positions = ['top', 'right', 'bottom', 'left']; -function getPositionedStyle(styles, style, suffix) { - const result = {}; - suffix = suffix ? '-' + suffix : ''; - for (let i = 0; i < 4; i++) { - const pos = positions[i]; - result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0; - } - 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(evt, canvas) { - const e = evt.native || evt; - 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(evt, chart) { - 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(evt, 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 === undefined || height === undefined) { - const container = _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 ? Math.floor(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); - } - return { - width, - height - }; -} -function retinaScale(chart, forceRatio, forceStyle) { - const pixelRatio = forceRatio || 1; - const deviceHeight = Math.floor(chart.height * pixelRatio); - const deviceWidth = Math.floor(chart.width * pixelRatio); - chart.height = deviceHeight / pixelRatio; - chart.width = deviceWidth / pixelRatio; - 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; - } - }; - 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] : undefined; -} - -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 intlCache = 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 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 !== undefined) { - 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) { - return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle); -} - -export { _toLeftRightCenter as $, _rlookupByKey as A, getAngleFromPoint as B, toPadding as C, each as D, getMaximumSize as E, _getParentNode as F, readUsedSize as G, HALF_PI as H, throttled as I, supportsEventListenerOptions as J, _isDomSupported as K, log10 as L, _factorize as M, finiteOrDefault as N, callback as O, PI as P, _addGrace as Q, toDegrees as R, _measureText as S, TAU as T, _int16Range as U, _alignPixel as V, clipArea as W, renderText as X, unclipArea as Y, toFont as Z, _arrayUnique as _, resolve as a, _angleDiff as a$, _alignStartEnd as a0, overrides as a1, merge as a2, _capitalize as a3, descriptors as a4, isFunction as a5, _attachContext as a6, _createResolver as a7, _descriptors as a8, mergeIf as a9, restoreTextDirection as aA, noop as aB, distanceBetweenPoints as aC, _setMinAndMaxByKey as aD, niceNum as aE, almostWhole as aF, almostEquals as aG, _decimalPlaces as aH, _longestText as aI, _filterBetween as aJ, _lookup as aK, getHoverColor as aL, clone$1 as aM, _merger as aN, _mergerIf as aO, _deprecated as aP, toFontString as aQ, splineCurve as aR, splineCurveMonotone as aS, getStyle as aT, fontString as aU, toLineHeight as aV, PITAU as aW, INFINITY as aX, RAD_PER_DEG as aY, QUARTER_PI as aZ, TWO_THIRDS_PI as a_, uid as aa, debounce as ab, retinaScale as ac, clearCanvas as ad, setsEqual as ae, _elementsEqual as af, _isClickEvent as ag, _isBetween as ah, _readValueToProps as ai, _updateBezierControlPoints as aj, _computeSegments as ak, _boundSegments as al, _steppedInterpolation as am, _bezierInterpolation as an, _pointInLine as ao, _steppedLineTo as ap, _bezierCurveTo as aq, drawPoint as ar, addRoundedRectPath as as, toTRBL as at, toTRBLCorners as au, _boundSegment as av, _normalizeAngle as aw, getRtlAdapter as ax, overrideTextDirection as ay, _textX as az, isArray as b, color as c, defaults as d, effects as e, resolveObjectKey as f, isNumberFinite as g, createContext as h, isObject as i, defined as j, isNullOrUndef as k, listenArrayEvents as l, toPercentage as m, toDimension as n, formatNumber as o, _angleBetween as p, isNumber as q, requestAnimFrame as r, sign as s, toRadians as t, unlistenArrayEvents as u, valueOrDefault as v, _limitValue as w, _lookupByKey as x, getRelativePosition as y, _isPointInArea as z }; diff --git a/node_modules/chart.js/dist/helpers.esm.js b/node_modules/chart.js/dist/helpers.esm.js deleted file mode 100644 index 399496db..00000000 --- a/node_modules/chart.js/dist/helpers.esm.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Chart.js v3.7.1 - * https://www.chartjs.org - * (c) 2022 Chart.js Contributors - * Released under the MIT License - */ -export { H as HALF_PI, aX as INFINITY, P as PI, aW as PITAU, aZ as QUARTER_PI, aY as RAD_PER_DEG, T as TAU, a_ as TWO_THIRDS_PI, Q as _addGrace, V as _alignPixel, a0 as _alignStartEnd, p as _angleBetween, a$ as _angleDiff, _ as _arrayUnique, a6 as _attachContext, aq as _bezierCurveTo, an as _bezierInterpolation, av as _boundSegment, al as _boundSegments, a3 as _capitalize, ak as _computeSegments, a7 as _createResolver, aH as _decimalPlaces, aP as _deprecated, a8 as _descriptors, af as _elementsEqual, M as _factorize, aJ as _filterBetween, F as _getParentNode, U as _int16Range, ah as _isBetween, ag as _isClickEvent, K as _isDomSupported, z as _isPointInArea, w as _limitValue, aI as _longestText, aK as _lookup, x as _lookupByKey, S as _measureText, aN as _merger, aO as _mergerIf, aw as _normalizeAngle, ao as _pointInLine, ai as _readValueToProps, A as _rlookupByKey, aD as _setMinAndMaxByKey, am as _steppedInterpolation, ap as _steppedLineTo, az as _textX, $ as _toLeftRightCenter, aj as _updateBezierControlPoints, as as addRoundedRectPath, aG as almostEquals, aF as almostWhole, O as callback, ad as clearCanvas, W as clipArea, aM as clone, c as color, h as createContext, ab as debounce, j as defined, aC as distanceBetweenPoints, ar as drawPoint, D as each, e as easingEffects, N as finiteOrDefault, aU as fontString, o as formatNumber, B as getAngleFromPoint, aL as getHoverColor, E as getMaximumSize, y as getRelativePosition, ax as getRtlAdapter, aT as getStyle, b as isArray, g as isFinite, a5 as isFunction, k as isNullOrUndef, q as isNumber, i as isObject, l as listenArrayEvents, L as log10, a2 as merge, a9 as mergeIf, aE as niceNum, aB as noop, ay as overrideTextDirection, G as readUsedSize, X as renderText, r as requestAnimFrame, a as resolve, f as resolveObjectKey, aA as restoreTextDirection, ac as retinaScale, ae as setsEqual, s as sign, aR as splineCurve, aS as splineCurveMonotone, J as supportsEventListenerOptions, I as throttled, R as toDegrees, n as toDimension, Z as toFont, aQ as toFontString, aV as toLineHeight, C as toPadding, m as toPercentage, t as toRadians, at as toTRBL, au as toTRBLCorners, aa as uid, Y as unclipArea, u as unlistenArrayEvents, v as valueOrDefault } from './chunks/helpers.segment.js'; diff --git a/node_modules/chart.js/helpers/helpers.esm.d.ts b/node_modules/chart.js/helpers/helpers.esm.d.ts deleted file mode 100644 index 2c3468e7..00000000 --- a/node_modules/chart.js/helpers/helpers.esm.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '../types/helpers'; diff --git a/node_modules/chart.js/helpers/helpers.esm.js b/node_modules/chart.js/helpers/helpers.esm.js deleted file mode 100644 index ca4eee52..00000000 --- a/node_modules/chart.js/helpers/helpers.esm.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../dist/helpers.esm'; diff --git a/node_modules/chart.js/helpers/helpers.js b/node_modules/chart.js/helpers/helpers.js deleted file mode 100644 index a762f589..00000000 --- a/node_modules/chart.js/helpers/helpers.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('..').helpers; diff --git a/node_modules/chart.js/helpers/package.json b/node_modules/chart.js/helpers/package.json deleted file mode 100644 index d97b75cb..00000000 --- a/node_modules/chart.js/helpers/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "chart.js-helpers", - "private": true, - "description": "helper package", - "main": "helpers.js", - "module": "helpers.esm.js", - "types": "helpers.esm.d.ts" -} \ No newline at end of file diff --git a/node_modules/chart.js/package.json b/node_modules/chart.js/package.json deleted file mode 100644 index 24c99596..00000000 --- a/node_modules/chart.js/package.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "name": "chart.js", - "homepage": "https://www.chartjs.org", - "description": "Simple HTML5 charts using the canvas element.", - "version": "3.7.1", - "license": "MIT", - "jsdelivr": "dist/chart.min.js", - "unpkg": "dist/chart.min.js", - "main": "dist/chart.js", - "module": "dist/chart.esm.js", - "types": "types/index.esm.d.ts", - "keywords": [ - "canvas", - "charts", - "data", - "graphs", - "html5", - "responsive" - ], - "repository": { - "type": "git", - "url": "https://github.com/chartjs/Chart.js.git" - }, - "bugs": { - "url": "https://github.com/chartjs/Chart.js/issues" - }, - "files": [ - "auto/**/*.js", - "auto/**/*.d.ts", - "dist/*.js", - "dist/chunks/*.js", - "types/*.d.ts", - "types/helpers/*.d.ts", - "helpers/**/*.js", - "helpers/**/*.d.ts" - ], - "scripts": { - "autobuild": "rollup -c -w", - "build": "rollup -c", - "dev": "karma start --auto-watch --no-single-run --browsers chrome --grep", - "dev:ff": "karma start --auto-watch --no-single-run --browsers firefox --grep", - "docs": "npm run build && vuepress build docs --no-cache", - "docs:dev": "npm run build && vuepress dev docs --no-cache", - "lint-js": "eslint \"src/**/*.js\" \"test/**/*.js\" \"docs/**/*.js\"", - "lint-md": "eslint \"**/*.md\"", - "lint-tsc": "tsc", - "lint-types": "eslint \"types/**/*.ts\" && node -r esm types/tests/autogen.js && tsc -p types/tests/", - "lint": "concurrently \"npm:lint-*\"", - "test": "npm run lint && cross-env NODE_ENV=test karma start --auto-watch --single-run --coverage --grep" - }, - "devDependencies": { - "@kurkle/color": "^0.1.9", - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-inject": "^4.0.2", - "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^13.0.0", - "@simonbrunel/vuepress-plugin-versions": "^0.2.0", - "@types/offscreencanvas": "^2019.6.4", - "@typescript-eslint/eslint-plugin": "^5.8.0", - "@typescript-eslint/parser": "^5.8.0", - "@vuepress/plugin-google-analytics": "^1.8.3", - "@vuepress/plugin-html-redirect": "^0.1.2", - "chartjs-adapter-luxon": "^1.0.0", - "chartjs-adapter-moment": "^1.0.0", - "chartjs-test-utils": "^0.3.1", - "concurrently": "^6.0.1", - "coveralls": "^3.1.0", - "cross-env": "^7.0.3", - "eslint": "^8.5.0", - "eslint-config-chartjs": "^0.3.0", - "eslint-plugin-es": "^4.1.0", - "eslint-plugin-html": "^6.1.2", - "eslint-plugin-markdown": "^2.2.1", - "esm": "^3.2.25", - "glob": "^7.1.6", - "jasmine": "^3.7.0", - "jasmine-core": "^3.7.1", - "karma": "^6.3.2", - "karma-chrome-launcher": "^3.1.0", - "karma-coverage": "^2.0.3", - "karma-edge-launcher": "^0.4.2", - "karma-firefox-launcher": "^2.1.0", - "karma-jasmine": "^4.0.1", - "karma-jasmine-html-reporter": "^1.5.4", - "karma-rollup-preprocessor": "^7.0.7", - "karma-safari-private-launcher": "^1.0.0", - "karma-spec-reporter": "0.0.32", - "luxon": "^2.2.0", - "markdown-it-include": "^2.0.0", - "moment": "^2.29.1", - "moment-timezone": "^0.5.34", - "pixelmatch": "^5.2.1", - "rollup": "^2.44.0", - "rollup-plugin-analyzer": "^4.0.0", - "rollup-plugin-cleanup": "^3.2.1", - "rollup-plugin-istanbul": "^3.0.0", - "rollup-plugin-terser": "^7.0.2", - "typedoc": "^0.22.10", - "typedoc-plugin-markdown": "^3.6.1", - "typescript": "^4.3.5", - "vue-tabs-component": "^1.5.0", - "vuepress": "^1.8.2", - "vuepress-plugin-code-copy": "^1.0.6", - "vuepress-plugin-flexsearch": "^0.3.0", - "vuepress-plugin-redirect": "^1.2.5", - "vuepress-plugin-tabs": "^0.3.0", - "vuepress-plugin-typedoc": "^0.10.0", - "vuepress-theme-chartjs": "^0.2.0", - "yargs": "^17.0.1" - } -} diff --git a/node_modules/chart.js/types/adapters.d.ts b/node_modules/chart.js/types/adapters.d.ts deleted file mode 100644 index f06c41b6..00000000 --- a/node_modules/chart.js/types/adapters.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -export type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'; - -export interface DateAdapter { - // Override one or multiple of the methods to adjust to the logic of the current date library. - override(members: Partial): void; - readonly options: unknown; - - /** - * Returns a map of time formats for the supported formatting units defined - * in Unit as well as 'datetime' representing a detailed date/time string. - * @returns {{string: string}} - */ - formats(): { [key: string]: string }; - /** - * Parses the given `value` and return the associated timestamp. - * @param {unknown} value - the value to parse (usually comes from the data) - * @param {string} [format] - the expected data format - */ - parse(value: unknown, format?: TimeUnit): number | null; - /** - * Returns the formatted date in the specified `format` for a given `timestamp`. - * @param {number} timestamp - the timestamp to format - * @param {string} format - the date/time token - * @return {string} - */ - format(timestamp: number, format: TimeUnit): string; - /** - * Adds the specified `amount` of `unit` to the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {number} amount - the amount to add - * @param {Unit} unit - the unit as string - * @return {number} - */ - add(timestamp: number, amount: number, unit: TimeUnit): number; - /** - * Returns the number of `unit` between the given timestamps. - * @param {number} a - the input timestamp (reference) - * @param {number} b - the timestamp to subtract - * @param {Unit} unit - the unit as string - * @return {number} - */ - diff(a: number, b: number, unit: TimeUnit): number; - /** - * Returns start of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit|'isoWeek'} unit - the unit as string - * @param {number} [weekday] - the ISO day of the week with 1 being Monday - * and 7 being Sunday (only needed if param *unit* is `isoWeek`). - * @return {number} - */ - startOf(timestamp: number, unit: TimeUnit | 'isoWeek', weekday?: number): number; - /** - * Returns end of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit|'isoWeek'} unit - the unit as string - * @return {number} - */ - endOf(timestamp: number, unit: TimeUnit | 'isoWeek'): number; -} - -export const _adapters: { - _date: DateAdapter; -}; diff --git a/node_modules/chart.js/types/animation.d.ts b/node_modules/chart.js/types/animation.d.ts deleted file mode 100644 index b8320412..00000000 --- a/node_modules/chart.js/types/animation.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Chart } from './index.esm'; -import { AnyObject } from './basic'; - -export class Animation { - constructor(cfg: AnyObject, target: AnyObject, prop: string, to?: unknown); - active(): boolean; - update(cfg: AnyObject, to: unknown, date: number): void; - cancel(): void; - tick(date: number): void; -} - -export interface AnimationEvent { - chart: Chart; - numSteps: number; - initial: boolean; - currentStep: number; -} - -export class Animator { - listen(chart: Chart, event: 'complete' | 'progress', cb: (event: AnimationEvent) => void): void; - add(chart: Chart, items: readonly Animation[]): void; - has(chart: Chart): boolean; - start(chart: Chart): void; - running(chart: Chart): boolean; - stop(chart: Chart): void; - remove(chart: Chart): boolean; -} - -export class Animations { - constructor(chart: Chart, animations: AnyObject); - configure(animations: AnyObject): void; - update(target: AnyObject, values: AnyObject): undefined | boolean; -} diff --git a/node_modules/chart.js/types/basic.d.ts b/node_modules/chart.js/types/basic.d.ts deleted file mode 100644 index 1692c9cb..00000000 --- a/node_modules/chart.js/types/basic.d.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export type AnyObject = Record; -export type EmptyObject = Record; diff --git a/node_modules/chart.js/types/color.d.ts b/node_modules/chart.js/types/color.d.ts deleted file mode 100644 index 4a68f98b..00000000 --- a/node_modules/chart.js/types/color.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type Color = string | CanvasGradient | CanvasPattern; diff --git a/node_modules/chart.js/types/element.d.ts b/node_modules/chart.js/types/element.d.ts deleted file mode 100644 index 3b9359b3..00000000 --- a/node_modules/chart.js/types/element.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { AnyObject } from './basic'; -import { Point } from './geometric'; - -export interface Element { - readonly x: number; - readonly y: number; - readonly active: boolean; - readonly options: O; - - tooltipPosition(useFinalPosition?: boolean): Point; - hasValue(): boolean; - getProps

(props: P, final?: boolean): Pick; -} -export const Element: { - prototype: Element; - new (): Element; -}; diff --git a/node_modules/chart.js/types/geometric.d.ts b/node_modules/chart.js/types/geometric.d.ts deleted file mode 100644 index 0e1affda..00000000 --- a/node_modules/chart.js/types/geometric.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -export interface ChartArea { - top: number; - left: number; - right: number; - bottom: number; - width: number; - height: number; -} - -export interface Point { - x: number; - y: number; -} - -export type TRBL = { - top: number; - right: number; - bottom: number; - left: number; -} - -export type TRBLCorners = { - topLeft: number; - topRight: number; - bottomLeft: number; - bottomRight: number; -}; - -export type CornerRadius = number | Partial; - -export type RoundedRect = { - x: number; - y: number; - w: number; - h: number; - radius?: CornerRadius -} diff --git a/node_modules/chart.js/types/helpers/helpers.canvas.d.ts b/node_modules/chart.js/types/helpers/helpers.canvas.d.ts deleted file mode 100644 index 44e570e6..00000000 --- a/node_modules/chart.js/types/helpers/helpers.canvas.d.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { PointStyle } from '../index.esm'; -import { Color } from '../color'; -import { ChartArea, RoundedRect } from '../geometric'; -import { CanvasFontSpec } from './helpers.options'; - -export function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void; - -export function clipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void; - -export function unclipArea(ctx: CanvasRenderingContext2D): void; - -export interface DrawPointOptions { - pointStyle: PointStyle; - rotation?: number; - radius: number; - borderWidth: number; -} - -export function drawPoint(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number): void; - -/** - * Converts the given font object into a CSS font string. - * @param font a font object - * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font - */ -export function toFontString(font: { size: number; family: string; style?: string; weight?: string }): string | null; - -export interface RenderTextOpts { - /** - * The fill color of the text. If unset, the existing - * fillStyle property of the canvas is unchanged. - */ - color?: Color; - - /** - * The width of the strikethrough / underline - * @default 2 - */ - decorationWidth?: number; - - /** - * The max width of the text in pixels - */ - maxWidth?: number; - - /** - * A rotation to be applied to the canvas - * This is applied after the translation is applied - */ - rotation?: number; - - /** - * Apply a strikethrough effect to the text - */ - strikethrough?: boolean; - - /** - * The color of the text stroke. If unset, the existing - * strokeStyle property of the context is unchanged - */ - strokeColor?: Color; - - /** - * The text stroke width. If unset, the existing - * lineWidth property of the context is unchanged - */ - strokeWidth?: number; - - /** - * The text alignment to use. If unset, the existing - * textAlign property of the context is unchanged - */ - textAlign: CanvasTextAlign; - - /** - * The text baseline to use. If unset, the existing - * textBaseline property of the context is unchanged - */ - textBaseline: CanvasTextBaseline; - - /** - * If specified, a translation to apply to the context - */ - translation?: [number, number]; - - /** - * Underline the text - */ - underline?: boolean; -} - -export function renderText( - ctx: CanvasRenderingContext2D, - text: string | string[], - x: number, - y: number, - font: CanvasFontSpec, - opts?: RenderTextOpts -): void; - -export function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: RoundedRect): void; diff --git a/node_modules/chart.js/types/helpers/helpers.collection.d.ts b/node_modules/chart.js/types/helpers/helpers.collection.d.ts deleted file mode 100644 index 6a51597c..00000000 --- a/node_modules/chart.js/types/helpers/helpers.collection.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface ArrayListener { - _onDataPush?(...item: T[]): void; - _onDataPop?(): void; - _onDataShift?(): void; - _onDataSplice?(index: number, deleteCount: number, ...items: T[]): void; - _onDataUnshift?(...item: T[]): void; -} - -/** - * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', - * 'unshift') and notify the listener AFTER the array has been altered. Listeners are - * called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments. - */ -export function listenArrayEvents(array: T[], listener: ArrayListener): void; - -/** - * Removes the given array event listener and cleanup extra attached properties (such as - * the _chartjs stub and overridden methods) if array doesn't have any more listeners. - */ -export function unlistenArrayEvents(array: T[], listener: ArrayListener): void; diff --git a/node_modules/chart.js/types/helpers/helpers.color.d.ts b/node_modules/chart.js/types/helpers/helpers.color.d.ts deleted file mode 100644 index 3cfc20ea..00000000 --- a/node_modules/chart.js/types/helpers/helpers.color.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -export function color(value: CanvasGradient): CanvasGradient; -export function color(value: CanvasPattern): CanvasPattern; -export function color( - value: - | string - | { r: number; g: number; b: number; a: number } - | [number, number, number] - | [number, number, number, number] -): ColorModel; - -export interface ColorModel { - rgbString(): string; - hexString(): string; - hslString(): string; - rgb: { r: number; g: number; b: number; a: number }; - valid: boolean; - mix(color: ColorModel, weight: number): this; - clone(): ColorModel; - alpha(a: number): ColorModel; - clearer(ration: number): ColorModel; - greyscale(): ColorModel; - opaquer(ratio: number): ColorModel; - negate(): ColorModel; - lighten(ratio: number): ColorModel; - darken(ratio: number): ColorModel; - saturate(ratio: number): ColorModel; - desaturate(ratio: number): ColorModel; - rotate(deg: number): this; -} - -export function getHoverColor(value: CanvasGradient): CanvasGradient; -export function getHoverColor(value: CanvasPattern): CanvasPattern; -export function getHoverColor(value: string): string; diff --git a/node_modules/chart.js/types/helpers/helpers.core.d.ts b/node_modules/chart.js/types/helpers/helpers.core.d.ts deleted file mode 100644 index bc376da0..00000000 --- a/node_modules/chart.js/types/helpers/helpers.core.d.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { AnyObject } from '../basic'; - -/** - * An empty function that can be used, for example, for optional callback. - */ -export function noop(): void; - -/** - * Returns a unique id, sequentially generated from a global variable. - * @returns {number} - * @function - */ -export function uid(): number; -/** - * Returns true if `value` is neither null nor undefined, else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ -export function isNullOrUndef(value: unknown): value is null | undefined; -/** - * Returns true if `value` is an array (including typed arrays), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @function - */ -export function isArray(value: unknown): value is ArrayLike; -/** - * Returns true if `value` is an object (excluding null), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ -export function isObject(value: unknown): value is AnyObject; -/** - * Returns true if `value` is a finite number, else returns false - * @param {*} value - The value to test. - * @returns {boolean} - */ -export function isFinite(value: unknown): value is number; - -/** - * Returns `value` if finite, else returns `defaultValue`. - * @param {*} value - The value to return if defined. - * @param {*} defaultValue - The value to return if `value` is not finite. - * @returns {*} - */ -export function finiteOrDefault(value: unknown, defaultValue: number): number; - -/** - * Returns `value` if defined, else returns `defaultValue`. - * @param {*} value - The value to return if defined. - * @param {*} defaultValue - The value to return if `value` is undefined. - * @returns {*} - */ -export function valueOrDefault(value: T | undefined, defaultValue: T): T; - -export function toPercentage(value: number | string, dimesion: number): number; -export function toDimension(value: number | string, dimension: number): number; - -/** - * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the - * value returned by `fn`. If `fn` is not a function, this method returns undefined. - * @param fn - The function to call. - * @param args - The arguments with which `fn` should be called. - * @param [thisArg] - The value of `this` provided for the call to `fn`. - * @returns {*} - */ -export function callback R, TA, R>( - fn: T | undefined, - args: unknown[], - thisArg?: TA -): R | undefined; - -/** - * Note(SB) for performance sake, this method should only be used when loopable type - * is unknown or in none intensive code (not called often and small loopable). Else - * it's preferable to use a regular for() loop and save extra function calls. - * @param loopable - The object or array to be iterated. - * @param fn - The function to call for each item. - * @param [thisArg] - The value of `this` provided for the call to `fn`. - * @param [reverse] - If true, iterates backward on the loopable. - */ -export function each( - loopable: T[], - fn: (this: TA, v: T, i: number) => void, - thisArg?: TA, - reverse?: boolean -): void; -/** - * Note(SB) for performance sake, this method should only be used when loopable type - * is unknown or in none intensive code (not called often and small loopable). Else - * it's preferable to use a regular for() loop and save extra function calls. - * @param loopable - The object or array to be iterated. - * @param fn - The function to call for each item. - * @param [thisArg] - The value of `this` provided for the call to `fn`. - * @param [reverse] - If true, iterates backward on the loopable. - */ -export function each( - loopable: { [key: string]: T }, - fn: (this: TA, v: T, k: string) => void, - thisArg?: TA, - reverse?: boolean -): void; - -/** - * Returns a deep copy of `source` without keeping references on objects and arrays. - * @param source - The value to clone. - */ -export function clone(source: T): T; - -export interface MergeOptions { - merger?: (key: string, target: AnyObject, source: AnyObject, options: AnyObject) => AnyObject; -} -/** - * Recursively deep copies `source` properties into `target` with the given `options`. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param target - The target object in which all sources are merged into. - * @param source - Object(s) to merge into `target`. - * @param {object} [options] - Merging options: - * @param {function} [options.merger] - The merge method (key, target, source, options) - * @returns {object} The `target` object. - */ -export function merge(target: T, source: [], options?: MergeOptions): T; -export function merge(target: T, source: S1, options?: MergeOptions): T & S1; -export function merge(target: T, source: [S1], options?: MergeOptions): T & S1; -export function merge(target: T, source: [S1, S2], options?: MergeOptions): T & S1 & S2; -export function merge(target: T, source: [S1, S2, S3], options?: MergeOptions): T & S1 & S2 & S3; -export function merge( - target: T, - source: [S1, S2, S3, S4], - options?: MergeOptions -): T & S1 & S2 & S3 & S4; -export function merge(target: T, source: AnyObject[], options?: MergeOptions): AnyObject; - -/** - * Recursively deep copies `source` properties into `target` *only* if not defined in target. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param target - The target object in which all sources are merged into. - * @param source - Object(s) to merge into `target`. - * @returns The `target` object. - */ -export function mergeIf(target: T, source: []): T; -export function mergeIf(target: T, source: S1): T & S1; -export function mergeIf(target: T, source: [S1]): T & S1; -export function mergeIf(target: T, source: [S1, S2]): T & S1 & S2; -export function mergeIf(target: T, source: [S1, S2, S3]): T & S1 & S2 & S3; -export function mergeIf(target: T, source: [S1, S2, S3, S4]): T & S1 & S2 & S3 & S4; -export function mergeIf(target: T, source: AnyObject[]): AnyObject; - -export function resolveObjectKey(obj: AnyObject, key: string): AnyObject; - -export function defined(value: unknown): boolean; - -export function isFunction(value: unknown): boolean; - -export function setsEqual(a: Set, b: Set): boolean; diff --git a/node_modules/chart.js/types/helpers/helpers.curve.d.ts b/node_modules/chart.js/types/helpers/helpers.curve.d.ts deleted file mode 100644 index 28d9ee4a..00000000 --- a/node_modules/chart.js/types/helpers/helpers.curve.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface SplinePoint { - x: number; - y: number; -} - -/** - * Props to Rob Spencer at scaled innovation for his post on splining between points - * http://scaledinnovation.com/analytics/splines/aboutSplines.html - */ -export function splineCurve( - firstPoint: SplinePoint & { skip?: boolean }, - middlePoint: SplinePoint, - afterPoint: SplinePoint, - t: number -): { - previous: SplinePoint; - next: SplinePoint; -}; - -export interface MonotoneSplinePoint extends SplinePoint { - skip: boolean; - cp1x?: number; - cp1y?: number; - cp2x?: number; - cp2y?: number; -} - -/** - * This function calculates Bézier control points in a similar way than |splineCurve|, - * but preserves monotonicity of the provided data and ensures no local extremums are added - * between the dataset discrete points due to the interpolation. - * @see https://en.wikipedia.org/wiki/Monotone_cubic_interpolation - */ -export function splineCurveMonotone(points: readonly MonotoneSplinePoint[], indexAxis?: 'x' | 'y'): void; diff --git a/node_modules/chart.js/types/helpers/helpers.dom.d.ts b/node_modules/chart.js/types/helpers/helpers.dom.d.ts deleted file mode 100644 index 73864314..00000000 --- a/node_modules/chart.js/types/helpers/helpers.dom.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ChartEvent } from '../index.esm'; - -export function getMaximumSize(node: HTMLElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number }; -export function getRelativePosition( - evt: MouseEvent | ChartEvent, - chart: { readonly canvas: HTMLCanvasElement } -): { x: number; y: number }; -export function getStyle(el: HTMLElement, property: string): string; -export function retinaScale( - chart: { - currentDevicePixelRatio: number; - readonly canvas: HTMLCanvasElement; - readonly width: number; - readonly height: number; - readonly ctx: CanvasRenderingContext2D; - }, - forceRatio: number, - forceStyle?: boolean -): void; -export function readUsedSize(element: HTMLElement, property: 'width' | 'height'): number | undefined; diff --git a/node_modules/chart.js/types/helpers/helpers.easing.d.ts b/node_modules/chart.js/types/helpers/helpers.easing.d.ts deleted file mode 100644 index b86d6532..00000000 --- a/node_modules/chart.js/types/helpers/helpers.easing.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { EasingFunction } from '../index.esm'; - -export type EasingFunctionSignature = (t: number) => number; - -export const easingEffects: Record; diff --git a/node_modules/chart.js/types/helpers/helpers.extras.d.ts b/node_modules/chart.js/types/helpers/helpers.extras.d.ts deleted file mode 100644 index cb445c32..00000000 --- a/node_modules/chart.js/types/helpers/helpers.extras.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -export function fontString(pixelSize: number, fontStyle: string, fontFamily: string): string; - -/** - * Request animation polyfill - */ -export function requestAnimFrame(cb: () => void): void; - -/** - * Throttles calling `fn` once per animation frame - * Latest arguments are used on the actual call - * @param {function} fn - * @param {*} thisArg - * @param {function} [updateFn] - */ -export function throttled(fn: (...args: unknown[]) => void, thisArg: unknown, updateFn?: (...args: unknown[]) => unknown[]): (...args: unknown[]) => void; - -/** - * Debounces calling `fn` for `delay` ms - * @param {function} fn - Function to call. No arguments are passed. - * @param {number} delay - Delay in ms. 0 = immediate invocation. - * @returns {function} - */ -export function debounce(fn: () => void, delay: number): () => number; diff --git a/node_modules/chart.js/types/helpers/helpers.interpolation.d.ts b/node_modules/chart.js/types/helpers/helpers.interpolation.d.ts deleted file mode 100644 index cb0ff5c3..00000000 --- a/node_modules/chart.js/types/helpers/helpers.interpolation.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/node_modules/chart.js/types/helpers/helpers.intl.d.ts b/node_modules/chart.js/types/helpers/helpers.intl.d.ts deleted file mode 100644 index 3a896f4a..00000000 --- a/node_modules/chart.js/types/helpers/helpers.intl.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Format a number using a localized number formatter. - * @param num The number to format - * @param locale The locale to pass to the Intl.NumberFormat constructor - * @param options Number format options - */ -export function formatNumber(num: number, locale: string, options: Intl.NumberFormatOptions): string; diff --git a/node_modules/chart.js/types/helpers/helpers.math.d.ts b/node_modules/chart.js/types/helpers/helpers.math.d.ts deleted file mode 100644 index cc58b30e..00000000 --- a/node_modules/chart.js/types/helpers/helpers.math.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export function log10(x: number): number; -export function isNumber(v: unknown): boolean; -export function almostEquals(x: number, y: number, epsilon: number): boolean; -export function almostWhole(x: number, epsilon: number): number; -export function sign(x: number): number; -export function niceNum(range: number): number; -export function toRadians(degrees: number): number; -export function toDegrees(radians: number): number; -/** - * Gets the angle from vertical upright to the point about a centre. - */ -export function getAngleFromPoint( - centrePoint: { x: number; y: number }, - anglePoint: { x: number; y: number } -): { angle: number; distance: number }; - -export function distanceBetweenPoints(pt1: { x: number; y: number }, pt2: { x: number; y: number }): number; diff --git a/node_modules/chart.js/types/helpers/helpers.options.d.ts b/node_modules/chart.js/types/helpers/helpers.options.d.ts deleted file mode 100644 index 0bd783fa..00000000 --- a/node_modules/chart.js/types/helpers/helpers.options.d.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { TRBL, TRBLCorners } from '../geometric'; -import { FontSpec } from '../index.esm'; - -export interface CanvasFontSpec extends FontSpec { - string: string; -} -/** - * Parses font options and returns the font object. - * @param {object} options - A object that contains font options to be parsed. - * @return {object} The font object. - */ -export function toFont(options: Partial): CanvasFontSpec; - -/** - * Converts the given line height `value` in pixels for a specific font `size`. - * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). - * @param {number} size - The font size (in pixels) used to resolve relative `value`. - * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height - * @since 2.7.0 - */ -export function toLineHeight(value: string, size: number): number; - -export function toTRBL(value: number | Partial): TRBL; -export function toTRBLCorners(value: number | Partial): TRBLCorners; - -/** - * Converts the given value into a padding object with pre-computed width/height. - * @param {number|object} value - If a number, set the value to all TRBL component; - * else, if an object, use defined properties and sets undefined ones to 0. - * @returns {object} The padding values (top, right, bottom, left, width, height) - * @since 2.7.0 - */ -export function toPadding( - value?: number | { top?: number; left?: number; right?: number; bottom?: number; x?:number, y?: number } -): { top: number; left: number; right: number; bottom: number; width: number; height: number }; - -/** - * Evaluates the given `inputs` sequentially and returns the first defined value. - * @param inputs - An array of values, falling back to the last value. - * @param [context] - If defined and the current value is a function, the value - * is called with `context` as first argument and the result becomes the new input. - * @param [index] - If defined and the current value is an array, the value - * at `index` become the new input. - * @param [info] - object to return information about resolution in - * @param [info.cacheable] - Will be set to `false` if option is not cacheable. - * @since 2.7.0 - */ -export function resolve( - inputs: undefined | T | ((c: C) => T) | readonly T[], - context?: C, - index?: number, - info?: { cacheable?: boolean } -): T | undefined; - - -/** - * Create a context inheriting parentContext - * @since 3.6.0 - */ -export function createContext(parentContext: P, context: T): P extends null ? T : P & T; diff --git a/node_modules/chart.js/types/helpers/helpers.rtl.d.ts b/node_modules/chart.js/types/helpers/helpers.rtl.d.ts deleted file mode 100644 index ed0b9248..00000000 --- a/node_modules/chart.js/types/helpers/helpers.rtl.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export interface RTLAdapter { - x(x: number): number; - setWidth(w: number): void; - textAlign(align: 'center' | 'left' | 'right'): 'center' | 'left' | 'right'; - xPlus(x: number, value: number): number; - leftForLtr(x: number, itemWidth: number): number; -} -export function getRtlAdapter(rtl: boolean, rectX: number, width: number): RTLAdapter; - -export function overrideTextDirection(ctx: CanvasRenderingContext2D, direction: 'ltr' | 'rtl'): void; - -export function restoreTextDirection(ctx: CanvasRenderingContext2D, original?: [string, string]): void; diff --git a/node_modules/chart.js/types/helpers/helpers.segment.d.ts b/node_modules/chart.js/types/helpers/helpers.segment.d.ts deleted file mode 100644 index cb0ff5c3..00000000 --- a/node_modules/chart.js/types/helpers/helpers.segment.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/node_modules/chart.js/types/helpers/index.d.ts b/node_modules/chart.js/types/helpers/index.d.ts deleted file mode 100644 index 01332692..00000000 --- a/node_modules/chart.js/types/helpers/index.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -export * from './helpers.canvas'; -export * from './helpers.collection'; -export * from './helpers.color'; -export * from './helpers.core'; -export * from './helpers.curve'; -export * from './helpers.dom'; -export * from './helpers.easing'; -export * from './helpers.extras'; -export * from './helpers.interpolation'; -export * from './helpers.intl'; -export * from './helpers.math'; -export * from './helpers.options'; -export * from './helpers.canvas'; -export * from './helpers.rtl'; -export * from './helpers.segment'; diff --git a/node_modules/chart.js/types/index.esm.d.ts b/node_modules/chart.js/types/index.esm.d.ts deleted file mode 100644 index ccea4128..00000000 --- a/node_modules/chart.js/types/index.esm.d.ts +++ /dev/null @@ -1,3601 +0,0 @@ -import { DeepPartial, DistributiveArray, UnionToIntersection } from './utils'; - -import { TimeUnit } from './adapters'; -import { AnimationEvent } from './animation'; -import { AnyObject, EmptyObject } from './basic'; -import { Color } from './color'; -import { Element } from './element'; -import { ChartArea, Point } from './geometric'; -import { LayoutItem, LayoutPosition } from './layout'; - -export { DateAdapter, TimeUnit, _adapters } from './adapters'; -export { Animation, Animations, Animator, AnimationEvent } from './animation'; -export { Color } from './color'; -export { Element } from './element'; -export { ChartArea, Point } from './geometric'; -export { LayoutItem, LayoutPosition } from './layout'; - -export interface ScriptableContext { - active: boolean; - chart: Chart; - dataIndex: number; - dataset: UnionToIntersection>; - datasetIndex: number; - parsed: UnionToIntersection>; - raw: unknown; -} - -export interface ScriptableLineSegmentContext { - type: 'segment', - p0: PointElement, - p1: PointElement, - p0DataIndex: number, - p1DataIndex: number, - datasetIndex: number -} - -export type Scriptable = T | ((ctx: TContext, options: AnyObject) => T | undefined); -export type ScriptableOptions = { [P in keyof T]: Scriptable }; -export type ScriptableAndArray = readonly T[] | Scriptable; -export type ScriptableAndArrayOptions = { [P in keyof T]: ScriptableAndArray }; - -export interface ParsingOptions { - /** - * How to parse the dataset. The parsing can be disabled by specifying parsing: false at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally. - */ - parsing: - { - [key: string]: string; - } - | false; - - /** - * Chart.js is fastest if you provide data with indices that are unique, sorted, and consistent across datasets and provide the normalized: true option to let Chart.js know that you have done so. - */ - normalized: boolean; -} - -export interface ControllerDatasetOptions extends ParsingOptions { - /** - * The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts. - * @default 'x' - */ - indexAxis: 'x' | 'y'; - /** - * How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. 0 = clip at chartArea. Clipping can also be configured per side: clip: {left: 5, top: false, right: -2, bottom: 0} - */ - clip: number | ChartArea; - /** - * The label for the dataset which appears in the legend and tooltips. - */ - label: string; - /** - * The drawing order of dataset. Also affects order for stacking, tooltip and legend. - */ - order: number; - - /** - * The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack). - */ - stack: string; - /** - * Configures the visibility state of the dataset. Set it to true, to hide the dataset from the chart. - * @default false - */ - hidden: boolean; -} - -export interface BarControllerDatasetOptions - extends ControllerDatasetOptions, - ScriptableAndArrayOptions>, - ScriptableAndArrayOptions>, - AnimationOptions<'bar'> { - /** - * The ID of the x axis to plot this dataset on. - */ - xAxisID: string; - /** - * The ID of the y axis to plot this dataset on. - */ - yAxisID: string; - - /** - * Percent (0-1) of the available width each bar should be within the category width. 1.0 will take the whole category width and put the bars right next to each other. - * @default 0.9 - */ - barPercentage: number; - /** - * Percent (0-1) of the available width each category should be within the sample width. - * @default 0.8 - */ - categoryPercentage: number; - - /** - * Manually set width of each bar in pixels. If set to 'flex', it computes "optimal" sample widths that globally arrange bars side by side. If not set (default), bars are equally sized based on the smallest interval. - */ - barThickness: number | 'flex'; - - /** - * Set this to ensure that bars are not sized thicker than this. - */ - maxBarThickness: number; - - /** - * Set this to ensure that bars have a minimum length in pixels. - */ - minBarLength: number; - - /** - * Point style for the legend - * @default 'circle; - */ - pointStyle: PointStyle; -} - -export interface BarControllerChartOptions { - /** - * Should null or undefined values be omitted from drawing - */ - skipNull?: boolean; -} - -export type BarController = DatasetController -export const BarController: ChartComponent & { - prototype: BarController; - new (chart: Chart, datasetIndex: number): BarController; -}; - -export interface BubbleControllerDatasetOptions - extends ControllerDatasetOptions, - ScriptableAndArrayOptions>, - ScriptableAndArrayOptions> {} - -export interface BubbleDataPoint { - /** - * X Value - */ - x: number; - - /** - * Y Value - */ - y: number; - - /** - * Bubble radius in pixels (not scaled). - */ - r: number; -} - -export type BubbleController = DatasetController -export const BubbleController: ChartComponent & { - prototype: BubbleController; - new (chart: Chart, datasetIndex: number): BubbleController; -}; - -export interface LineControllerDatasetOptions - extends ControllerDatasetOptions, - ScriptableAndArrayOptions>, - ScriptableAndArrayOptions>, - ScriptableOptions>, - ScriptableOptions>, - AnimationOptions<'line'> { - /** - * The ID of the x axis to plot this dataset on. - */ - xAxisID: string; - /** - * The ID of the y axis to plot this dataset on. - */ - yAxisID: string; - - /** - * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. - * @default false - */ - spanGaps: boolean | number; - - showLine: boolean; -} - -export interface LineControllerChartOptions { - /** - * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. - * @default false - */ - spanGaps: boolean | number; - /** - * If false, the lines between points are not drawn. - * @default true - */ - showLine: boolean; -} - -export type LineController = DatasetController -export const LineController: ChartComponent & { - prototype: LineController; - new (chart: Chart, datasetIndex: number): LineController; -}; - -export type ScatterControllerDatasetOptions = LineControllerDatasetOptions; - -export interface ScatterDataPoint { - x: number; - y: number; -} - -export type ScatterControllerChartOptions = LineControllerChartOptions; - -export type ScatterController = LineController -export const ScatterController: ChartComponent & { - prototype: ScatterController; - new (chart: Chart, datasetIndex: number): ScatterController; -}; - -export interface DoughnutControllerDatasetOptions - extends ControllerDatasetOptions, - ScriptableAndArrayOptions>, - ScriptableAndArrayOptions>, - AnimationOptions<'doughnut'> { - - /** - * Sweep to allow arcs to cover. - * @default 360 - */ - circumference: number; - - /** - * Arc offset (in pixels). - */ - offset: number; - - /** - * Starting angle to draw this dataset from. - * @default 0 - */ - rotation: number; - - /** - * The relative thickness of the dataset. Providing a value for weight will cause the pie or doughnut dataset to be drawn with a thickness relative to the sum of all the dataset weight values. - * @default 1 - */ - weight: number; - - /** - * Similar to the `offset` option, but applies to all arcs. This can be used to to add spaces - * between arcs - * @default 0 - */ - spacing: number; -} - -export interface DoughnutAnimationOptions { - /** - * If true, the chart will animate in with a rotation animation. This property is in the options.animation object. - * @default true - */ - animateRotate: boolean; - - /** - * If true, will animate scaling the chart from the center outwards. - * @default false - */ - animateScale: boolean; -} - -export interface DoughnutControllerChartOptions { - /** - * Sweep to allow arcs to cover. - * @default 360 - */ - circumference: number; - - /** - * The portion of the chart that is cut out of the middle. ('50%' - for doughnut, 0 - for pie) - * String ending with '%' means percentage, number means pixels. - * @default 50 - */ - cutout: Scriptable>; - - /** - * Arc offset (in pixels). - */ - offset: number; - - /** - * The outer radius of the chart. String ending with '%' means percentage of maximum radius, number means pixels. - * @default '100%' - */ - radius: Scriptable>; - - /** - * Starting angle to draw arcs from. - * @default 0 - */ - rotation: number; - - /** - * Spacing between the arcs - * @default 0 - */ - spacing: number; - - animation: false | DoughnutAnimationOptions; -} - -export type DoughnutDataPoint = number; - -export interface DoughnutController extends DatasetController { - readonly innerRadius: number; - readonly outerRadius: number; - readonly offsetX: number; - readonly offsetY: number; - - calculateTotal(): number; - calculateCircumference(value: number): number; -} - -export const DoughnutController: ChartComponent & { - prototype: DoughnutController; - new (chart: Chart, datasetIndex: number): DoughnutController; -}; - -export interface DoughnutMetaExtensions { - total: number; -} - -export type PieControllerDatasetOptions = DoughnutControllerDatasetOptions; -export type PieControllerChartOptions = DoughnutControllerChartOptions; -export type PieAnimationOptions = DoughnutAnimationOptions; - -export type PieDataPoint = DoughnutDataPoint; -export type PieMetaExtensions = DoughnutMetaExtensions; - -export type PieController = DoughnutController -export const PieController: ChartComponent & { - prototype: PieController; - new (chart: Chart, datasetIndex: number): PieController; -}; - -export interface PolarAreaControllerDatasetOptions extends DoughnutControllerDatasetOptions { - /** - * Arc angle to cover. - for polar only - * @default circumference / (arc count) - */ - angle: number; -} - -export type PolarAreaAnimationOptions = DoughnutAnimationOptions; - -export interface PolarAreaControllerChartOptions { - /** - * Starting angle to draw arcs for the first item in a dataset. In degrees, 0 is at top. - * @default 0 - */ - startAngle: number; - - animation: false | PolarAreaAnimationOptions; -} - -export interface PolarAreaController extends DoughnutController { - countVisibleElements(): number; -} -export const PolarAreaController: ChartComponent & { - prototype: PolarAreaController; - new (chart: Chart, datasetIndex: number): PolarAreaController; -}; - -export interface RadarControllerDatasetOptions - extends ControllerDatasetOptions, - ScriptableAndArrayOptions>, - ScriptableAndArrayOptions>, - AnimationOptions<'radar'> { - /** - * The ID of the x axis to plot this dataset on. - */ - xAxisID: string; - /** - * The ID of the y axis to plot this dataset on. - */ - yAxisID: string; - - /** - * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. - */ - spanGaps: boolean | number; - - /** - * If false, the line is not drawn for this dataset. - */ - showLine: boolean; -} - -export type RadarControllerChartOptions = LineControllerChartOptions; - -export type RadarController = DatasetController -export const RadarController: ChartComponent & { - prototype: RadarController; - new (chart: Chart, datasetIndex: number): RadarController; -}; -interface ChartMetaCommon { - type: string; - controller: DatasetController; - order: number; - - label: string; - index: number; - visible: boolean; - - stack: number; - - indexAxis: 'x' | 'y'; - - data: TElement[]; - dataset?: TDatasetElement; - - hidden: boolean; - - xAxisID?: string; - yAxisID?: string; - rAxisID?: string; - iAxisID: string; - vAxisID: string; - - xScale?: Scale; - yScale?: Scale; - rScale?: Scale; - iScale?: Scale; - vScale?: Scale; - - _sorted: boolean; - _stacked: boolean | 'single'; - _parsed: unknown[]; -} - -export type ChartMeta< - TElement extends Element = Element, - TDatasetElement extends Element = Element, - // TODO - V4, move this to the first parameter. - // When this was introduced, doing so was a breaking change - TType extends ChartType = ChartType, -> = DeepPartial< -{ [key in ChartType]: ChartTypeRegistry[key]['metaExtensions'] }[TType] -> & ChartMetaCommon; - -export interface ActiveDataPoint { - datasetIndex: number; - index: number; -} - -export interface ActiveElement extends ActiveDataPoint { - element: Element; -} - -export declare class Chart< - TType extends ChartType = ChartType, - TData = DefaultDataPoint, - TLabel = unknown -> { - readonly platform: BasePlatform; - readonly id: string; - readonly canvas: HTMLCanvasElement; - readonly ctx: CanvasRenderingContext2D; - readonly config: ChartConfiguration; - readonly width: number; - readonly height: number; - readonly aspectRatio: number; - readonly boxes: LayoutItem[]; - readonly currentDevicePixelRatio: number; - readonly chartArea: ChartArea; - readonly scales: { [key: string]: Scale }; - readonly attached: boolean; - - readonly legend?: LegendElement; // Only available if legend plugin is registered and enabled - readonly tooltip?: TooltipModel; // Only available if tooltip plugin is registered and enabled - - data: ChartData; - options: ChartOptions; - - constructor(item: ChartItem, config: ChartConfiguration); - - clear(): this; - stop(): this; - - resize(width?: number, height?: number): void; - ensureScalesHaveIDs(): void; - buildOrUpdateScales(): void; - buildOrUpdateControllers(): void; - reset(): void; - update(mode?: UpdateMode): void; - render(): void; - draw(): void; - - getElementsAtEventForMode(e: Event, mode: string, options: InteractionOptions, useFinalPosition: boolean): InteractionItem[]; - - getSortedVisibleDatasetMetas(): ChartMeta[]; - getDatasetMeta(datasetIndex: number): ChartMeta; - getVisibleDatasetCount(): number; - isDatasetVisible(datasetIndex: number): boolean; - setDatasetVisibility(datasetIndex: number, visible: boolean): void; - toggleDataVisibility(index: number): void; - getDataVisibility(index: number): boolean; - hide(datasetIndex: number, dataIndex?: number): void; - show(datasetIndex: number, dataIndex?: number): void; - - getActiveElements(): ActiveElement[]; - setActiveElements(active: ActiveDataPoint[]): void; - - destroy(): void; - toBase64Image(type?: string, quality?: unknown): string; - bindEvents(): void; - unbindEvents(): void; - updateHoverStyle(items: InteractionItem[], mode: 'dataset', enabled: boolean): void; - - notifyPlugins(hook: string, args?: AnyObject): boolean | void; - - static readonly defaults: Defaults; - static readonly overrides: Overrides; - static readonly version: string; - static readonly instances: { [key: string]: Chart }; - static readonly registry: Registry; - static getChart(key: string | CanvasRenderingContext2D | HTMLCanvasElement): Chart | undefined; - static register(...items: ChartComponentLike[]): void; - static unregister(...items: ChartComponentLike[]): void; -} - -export const registerables: readonly ChartComponentLike[]; - -export declare type ChartItem = - | string - | CanvasRenderingContext2D - | HTMLCanvasElement - | { canvas: HTMLCanvasElement } - | ArrayLike; - -export declare enum UpdateModeEnum { - resize = 'resize', - reset = 'reset', - none = 'none', - hide = 'hide', - show = 'show', - normal = 'normal', - active = 'active' -} - -export type UpdateMode = keyof typeof UpdateModeEnum; - -export class DatasetController< - TType extends ChartType = ChartType, - TElement extends Element = Element, - TDatasetElement extends Element = Element, - TParsedData = ParsedDataType, -> { - constructor(chart: Chart, datasetIndex: number); - - readonly chart: Chart; - readonly index: number; - readonly _cachedMeta: ChartMeta; - enableOptionSharing: boolean; - - linkScales(): void; - getAllParsedValues(scale: Scale): number[]; - protected getLabelAndValue(index: number): { label: string; value: string }; - updateElements(elements: TElement[], start: number, count: number, mode: UpdateMode): void; - update(mode: UpdateMode): void; - updateIndex(datasetIndex: number): void; - protected getMaxOverflow(): boolean | number; - draw(): void; - reset(): void; - getDataset(): ChartDataset; - getMeta(): ChartMeta; - getScaleForId(scaleID: string): Scale | undefined; - configure(): void; - initialize(): void; - addElements(): void; - buildOrUpdateElements(resetNewElements?: boolean): void; - - getStyle(index: number, active: boolean): AnyObject; - protected resolveDatasetElementOptions(mode: UpdateMode): AnyObject; - protected resolveDataElementOptions(index: number, mode: UpdateMode): AnyObject; - /** - * Utility for checking if the options are shared and should be animated separately. - * @protected - */ - protected getSharedOptions(options: AnyObject): undefined | AnyObject; - /** - * Utility for determining if `options` should be included in the updated properties - * @protected - */ - protected includeOptions(mode: UpdateMode, sharedOptions: AnyObject): boolean; - /** - * Utility for updating an element with new properties, using animations when appropriate. - * @protected - */ - - protected updateElement(element: TElement | TDatasetElement, index: number | undefined, properties: AnyObject, mode: UpdateMode): void; - /** - * Utility to animate the shared options, that are potentially affecting multiple elements. - * @protected - */ - - protected updateSharedOptions(sharedOptions: AnyObject, mode: UpdateMode, newOptions: AnyObject): void; - removeHoverStyle(element: TElement, datasetIndex: number, index: number): void; - setHoverStyle(element: TElement, datasetIndex: number, index: number): void; - - parse(start: number, count: number): void; - protected parsePrimitiveData(meta: ChartMeta, data: AnyObject[], start: number, count: number): AnyObject[]; - protected parseArrayData(meta: ChartMeta, data: AnyObject[], start: number, count: number): AnyObject[]; - protected parseObjectData(meta: ChartMeta, data: AnyObject[], start: number, count: number): AnyObject[]; - protected getParsed(index: number): TParsedData; - protected applyStack(scale: Scale, parsed: unknown[]): number; - protected updateRangeFromParsed( - range: { min: number; max: number }, - scale: Scale, - parsed: unknown[], - stack: boolean | string - ): void; - protected getMinMax(scale: Scale, canStack?: boolean): { min: number; max: number }; -} - -export interface DatasetControllerChartComponent extends ChartComponent { - defaults: { - datasetElementType?: string | null | false; - dataElementType?: string | null | false; - }; -} - -export interface Defaults extends CoreChartOptions, ElementChartOptions, PluginChartOptions { - - scale: ScaleOptionsByType; - scales: { - [key in ScaleType]: ScaleOptionsByType; - }; - - set(values: AnyObject): AnyObject; - set(scope: string, values: AnyObject): AnyObject; - get(scope: string): AnyObject; - - describe(scope: string, values: AnyObject): AnyObject; - override(scope: string, values: AnyObject): AnyObject; - - /** - * Routes the named defaults to fallback to another scope/name. - * This routing is useful when those target values, like defaults.color, are changed runtime. - * If the values would be copied, the runtime change would not take effect. By routing, the - * fallback is evaluated at each access, so its always up to date. - * - * Example: - * - * defaults.route('elements.arc', 'backgroundColor', '', 'color') - * - reads the backgroundColor from defaults.color when undefined locally - * - * @param scope Scope this route applies to. - * @param name Property name that should be routed to different namespace when not defined here. - * @param targetScope The namespace where those properties should be routed to. - * Empty string ('') is the root of defaults. - * @param targetName The target name in the target scope the property should be routed to. - */ - route(scope: string, name: string, targetScope: string, targetName: string): void; -} - -export type Overrides = { - [key in ChartType]: - CoreChartOptions & - ElementChartOptions & - PluginChartOptions & - DatasetChartOptions & - ScaleChartOptions & - ChartTypeRegistry[key]['chartOptions']; -} - -export const defaults: Defaults; -export interface InteractionOptions { - axis?: string; - intersect?: boolean; -} - -export interface InteractionItem { - element: Element; - datasetIndex: number; - index: number; -} - -export type InteractionModeFunction = ( - chart: Chart, - e: ChartEvent, - options: InteractionOptions, - useFinalPosition?: boolean -) => InteractionItem[]; - -export interface InteractionModeMap { - /** - * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item - */ - index: InteractionModeFunction; - - /** - * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect is false, we find the nearest item and return the items in that dataset - */ - dataset: InteractionModeFunction; - /** - * Point mode returns all elements that hit test based on the event position - * of the event - */ - point: InteractionModeFunction; - /** - * nearest mode returns the element closest to the point - */ - nearest: InteractionModeFunction; - /** - * x mode returns the elements that hit-test at the current x coordinate - */ - x: InteractionModeFunction; - /** - * y mode returns the elements that hit-test at the current y coordinate - */ - y: InteractionModeFunction; -} - -export type InteractionMode = keyof InteractionModeMap; - -export const Interaction: { - modes: InteractionModeMap; -}; - -export const layouts: { - /** - * Register a box to a chart. - * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. - * @param {Chart} chart - the chart to use - * @param {LayoutItem} item - the item to add to be laid out - */ - addBox(chart: Chart, item: LayoutItem): void; - - /** - * Remove a layoutItem from a chart - * @param {Chart} chart - the chart to remove the box from - * @param {LayoutItem} layoutItem - the item to remove from the layout - */ - removeBox(chart: Chart, layoutItem: LayoutItem): void; - - /** - * Sets (or updates) options on the given `item`. - * @param {Chart} chart - the chart in which the item lives (or will be added to) - * @param {LayoutItem} item - the item to configure with the given options - * @param options - the new item options. - */ - configure( - chart: Chart, - item: LayoutItem, - options: { fullSize?: number; position?: LayoutPosition; weight?: number } - ): void; - - /** - * Fits boxes of the given chart into the given size by having each box measure itself - * then running a fitting algorithm - * @param {Chart} chart - the chart - * @param {number} width - the width to fit into - * @param {number} height - the height to fit into - */ - update(chart: Chart, width: number, height: number): void; -}; - -export interface Plugin extends ExtendedPlugin { - id: string; - - /** - * @desc Called when plugin is installed for this chart instance. This hook is also invoked for disabled plugins (options === false). - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @since 3.0.0 - */ - install?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called when a plugin is starting. This happens when chart is created or plugin is enabled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @since 3.0.0 - */ - start?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called when a plugin stopping. This happens when chart is destroyed or plugin is disabled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @since 3.0.0 - */ - stop?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called before initializing `chart`. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - beforeInit?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called after `chart` has been initialized and before the first update. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - afterInit?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called before updating `chart`. If any plugin returns `false`, the update - * is cancelled (and thus subsequent render(s)) until another `update` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {UpdateMode} args.mode - The update mode - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart update. - */ - beforeUpdate?(chart: Chart, args: { mode: UpdateMode, cancelable: true }, options: O): boolean | void; - /** - * @desc Called after `chart` has been updated and before rendering. Note that this - * hook will not be called if the chart update has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {UpdateMode} args.mode - The update mode - * @param {object} options - The plugin options. - */ - afterUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): void; - /** - * @desc Called during the update process, before any chart elements have been created. - * This can be used for data decimation by changing the data array inside a dataset. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - beforeElementsUpdate?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called during chart reset - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @since version 3.0.0 - */ - reset?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called before updating the `chart` datasets. If any plugin returns `false`, - * the datasets update is cancelled until another `update` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {UpdateMode} args.mode - The update mode. - * @param {object} options - The plugin options. - * @returns {boolean} false to cancel the datasets update. - * @since version 2.1.5 - */ - beforeDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): boolean | void; - /** - * @desc Called after the `chart` datasets have been updated. Note that this hook - * will not be called if the datasets update has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {UpdateMode} args.mode - The update mode. - * @param {object} options - The plugin options. - * @since version 2.1.5 - */ - afterDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode, cancelable: true }, options: O): void; - /** - * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin - * returns `false`, the datasets update is cancelled until another `update` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {number} args.index - The dataset index. - * @param {object} args.meta - The dataset metadata. - * @param {UpdateMode} args.mode - The update mode. - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart datasets drawing. - */ - beforeDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: true }, options: O): boolean | void; - /** - * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note - * that this hook will not be called if the datasets update has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {number} args.index - The dataset index. - * @param {object} args.meta - The dataset metadata. - * @param {UpdateMode} args.mode - The update mode. - * @param {object} options - The plugin options. - */ - afterDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: false }, options: O): void; - /** - * @desc Called before laying out `chart`. If any plugin returns `false`, - * the layout update is cancelled until another `update` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart layout. - */ - beforeLayout?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; - /** - * @desc Called before scale data limits are calculated. This hook is called separately for each scale in the chart. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {Scale} args.scale - The scale. - * @param {object} options - The plugin options. - */ - beforeDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void; - /** - * @desc Called after scale data limits are calculated. This hook is called separately for each scale in the chart. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {Scale} args.scale - The scale. - * @param {object} options - The plugin options. - */ - afterDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void; - /** - * @desc Called before scale builds its ticks. This hook is called separately for each scale in the chart. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {Scale} args.scale - The scale. - * @param {object} options - The plugin options. - */ - beforeBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void; - /** - * @desc Called after scale has build its ticks. This hook is called separately for each scale in the chart. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {Scale} args.scale - The scale. - * @param {object} options - The plugin options. - */ - afterBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void; - /** - * @desc Called after the `chart` has been laid out. Note that this hook will not - * be called if the layout update has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - afterLayout?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called before rendering `chart`. If any plugin returns `false`, - * the rendering is cancelled until another `render` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart rendering. - */ - beforeRender?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; - /** - * @desc Called after the `chart` has been fully rendered (and animation completed). Note - * that this hook will not be called if the rendering has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - afterRender?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called before drawing `chart` at every animation frame. If any plugin returns `false`, - * the frame drawing is cancelled untilanother `render` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart drawing. - */ - beforeDraw?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; - /** - * @desc Called after the `chart` has been drawn. Note that this hook will not be called - * if the drawing has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - afterDraw?(chart: Chart, args: EmptyObject, options: O): void; - /** - * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, - * the datasets drawing is cancelled until another `render` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart datasets drawing. - */ - beforeDatasetsDraw?(chart: Chart, args: { cancelable: true }, options: O): boolean | void; - /** - * @desc Called after the `chart` datasets have been drawn. Note that this hook - * will not be called if the datasets drawing has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - afterDatasetsDraw?(chart: Chart, args: EmptyObject, options: O, cancelable: false): void; - /** - * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets - * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing - * is cancelled until another `render` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {number} args.index - The dataset index. - * @param {object} args.meta - The dataset metadata. - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart datasets drawing. - */ - beforeDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): boolean | void; - /** - * @desc Called after the `chart` datasets at the given `args.index` have been drawn - * (datasets are drawn in the reverse order). Note that this hook will not be called - * if the datasets drawing has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {number} args.index - The dataset index. - * @param {object} args.meta - The dataset metadata. - * @param {object} options - The plugin options. - */ - afterDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): void; - /** - * @desc Called before processing the specified `event`. If any plugin returns `false`, - * the event will be discarded. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {ChartEvent} args.event - The event object. - * @param {boolean} args.replay - True if this event is replayed from `Chart.update` - * @param {boolean} args.inChartArea - The event position is inside chartArea - * @param {object} options - The plugin options. - */ - beforeEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean, cancelable: true, inChartArea: boolean }, options: O): boolean | void; - /** - * @desc Called after the `event` has been consumed. Note that this hook - * will not be called if the `event` has been previously discarded. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {ChartEvent} args.event - The event object. - * @param {boolean} args.replay - True if this event is replayed from `Chart.update` - * @param {boolean} args.inChartArea - The event position is inside chartArea - * @param {boolean} [args.changed] - Set to true if the plugin needs a render. Should only be changed to true, because this args object is passed through all plugins. - * @param {object} options - The plugin options. - */ - afterEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean, changed?: boolean, cancelable: false, inChartArea: boolean }, options: O): void; - /** - * @desc Called after the chart as been resized. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {number} args.size - The new canvas display size (eq. canvas.style width & height). - * @param {object} options - The plugin options. - */ - resize?(chart: Chart, args: { size: { width: number, height: number } }, options: O): void; - /** - * Called before the chart is being destroyed. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - beforeDestroy?(chart: Chart, args: EmptyObject, options: O): void; - /** - * Called after the chart has been destroyed. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @deprecated since version 3.7.0 in favour of afterDestroy - */ - destroy?(chart: Chart, args: EmptyObject, options: O): void; - /** - * Called after the chart has been destroyed. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - */ - afterDestroy?(chart: Chart, args: EmptyObject, options: O): void; - /** - * Called after chart is destroyed on all plugins that were installed for that chart. This hook is also invoked for disabled plugins (options === false). - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {object} options - The plugin options. - * @since 3.0.0 - */ - uninstall?(chart: Chart, args: EmptyObject, options: O): void; -} - -export declare type ChartComponentLike = ChartComponent | ChartComponent[] | { [key: string]: ChartComponent } | Plugin | Plugin[]; - -/** - * Please use the module's default export which provides a singleton instance - * Note: class is exported for typedoc - */ -export interface Registry { - readonly controllers: TypedRegistry; - readonly elements: TypedRegistry; - readonly plugins: TypedRegistry; - readonly scales: TypedRegistry; - - add(...args: ChartComponentLike[]): void; - remove(...args: ChartComponentLike[]): void; - - addControllers(...args: ChartComponentLike[]): void; - addElements(...args: ChartComponentLike[]): void; - addPlugins(...args: ChartComponentLike[]): void; - addScales(...args: ChartComponentLike[]): void; - - getController(id: string): DatasetController | undefined; - getElement(id: string): Element | undefined; - getPlugin(id: string): Plugin | undefined; - getScale(id: string): Scale | undefined; -} - -export const registry: Registry; - -export interface Tick { - value: number; - label?: string | string[]; - major?: boolean; -} - -export interface CoreScaleOptions { - /** - * Controls the axis global visibility (visible when true, hidden when false). When display: 'auto', the axis is visible only if at least one associated dataset is visible. - * @default true - */ - display: boolean | 'auto'; - /** - * Align pixel values to device pixels - */ - alignToPixels: boolean; - /** - * Reverse the scale. - * @default false - */ - reverse: boolean; - /** - * The weight used to sort the axis. Higher weights are further away from the chart area. - * @default true - */ - weight: number; - /** - * Callback called before the update process starts. - */ - beforeUpdate(axis: Scale): void; - /** - * Callback that runs before dimensions are set. - */ - beforeSetDimensions(axis: Scale): void; - /** - * Callback that runs after dimensions are set. - */ - afterSetDimensions(axis: Scale): void; - /** - * Callback that runs before data limits are determined. - */ - beforeDataLimits(axis: Scale): void; - /** - * Callback that runs after data limits are determined. - */ - afterDataLimits(axis: Scale): void; - /** - * Callback that runs before ticks are created. - */ - beforeBuildTicks(axis: Scale): void; - /** - * Callback that runs after ticks are created. Useful for filtering ticks. - */ - afterBuildTicks(axis: Scale): void; - /** - * Callback that runs before ticks are converted into strings. - */ - beforeTickToLabelConversion(axis: Scale): void; - /** - * Callback that runs after ticks are converted into strings. - */ - afterTickToLabelConversion(axis: Scale): void; - /** - * Callback that runs before tick rotation is determined. - */ - beforeCalculateLabelRotation(axis: Scale): void; - /** - * Callback that runs after tick rotation is determined. - */ - afterCalculateLabelRotation(axis: Scale): void; - /** - * Callback that runs before the scale fits to the canvas. - */ - beforeFit(axis: Scale): void; - /** - * Callback that runs after the scale fits to the canvas. - */ - afterFit(axis: Scale): void; - /** - * Callback that runs at the end of the update process. - */ - afterUpdate(axis: Scale): void; -} - -export interface Scale extends Element, LayoutItem { - readonly id: string; - readonly type: string; - readonly ctx: CanvasRenderingContext2D; - readonly chart: Chart; - - maxWidth: number; - maxHeight: number; - - paddingTop: number; - paddingBottom: number; - paddingLeft: number; - paddingRight: number; - - axis: string; - labelRotation: number; - min: number; - max: number; - ticks: Tick[]; - getMatchingVisibleMetas(type?: string): ChartMeta[]; - - drawTitle(chartArea: ChartArea): void; - drawLabels(chartArea: ChartArea): void; - drawGrid(chartArea: ChartArea): void; - - /** - * @param {number} pixel - * @return {number} - */ - getDecimalForPixel(pixel: number): number; - /** - * Utility for getting the pixel location of a percentage of scale - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param {number} decimal - * @return {number} - */ - getPixelForDecimal(decimal: number): number; - /** - * Returns the location of the tick at the given index - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param {number} index - * @return {number} - */ - getPixelForTick(index: number): number; - /** - * Used to get the label to display in the tooltip for the given value - * @param {*} value - * @return {string} - */ - getLabelForValue(value: number): string; - - /** - * Returns the grid line width at given value - */ - getLineWidthForValue(value: number): number; - - /** - * Returns the location of the given data point. Value can either be an index or a numerical value - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param {*} value - * @param {number} [index] - * @return {number} - */ - getPixelForValue(value: number, index?: number): number; - - /** - * Used to get the data value from a given pixel. This is the inverse of getPixelForValue - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param {number} pixel - * @return {*} - */ - getValueForPixel(pixel: number): number | undefined; - - getBaseValue(): number; - /** - * Returns the pixel for the minimum chart value - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @return {number} - */ - getBasePixel(): number; - - init(options: O): void; - parse(raw: unknown, index: number): unknown; - getUserBounds(): { min: number; max: number; minDefined: boolean; maxDefined: boolean }; - getMinMax(canStack: boolean): { min: number; max: number }; - getTicks(): Tick[]; - getLabels(): string[]; - beforeUpdate(): void; - configure(): void; - afterUpdate(): void; - beforeSetDimensions(): void; - setDimensions(): void; - afterSetDimensions(): void; - beforeDataLimits(): void; - determineDataLimits(): void; - afterDataLimits(): void; - beforeBuildTicks(): void; - buildTicks(): Tick[]; - afterBuildTicks(): void; - beforeTickToLabelConversion(): void; - generateTickLabels(ticks: Tick[]): void; - afterTickToLabelConversion(): void; - beforeCalculateLabelRotation(): void; - calculateLabelRotation(): void; - afterCalculateLabelRotation(): void; - beforeFit(): void; - fit(): void; - afterFit(): void; - - isFullSize(): boolean; -} -export declare class Scale { - constructor(cfg: {id: string, type: string, ctx: CanvasRenderingContext2D, chart: Chart}); -} - -export interface ScriptableScaleContext { - chart: Chart; - scale: Scale; - index: number; - tick: Tick; -} - -export interface ScriptableScalePointLabelContext { - chart: Chart; - scale: Scale; - index: number; - label: string; -} - - -export const Ticks: { - formatters: { - /** - * Formatter for value labels - * @param value the value to display - * @return {string|string[]} the label to display - */ - values(value: unknown): string | string[]; - /** - * Formatter for numeric ticks - * @param tickValue the value to be formatted - * @param index the position of the tickValue parameter in the ticks array - * @param ticks the list of ticks being converted - * @return string representation of the tickValue parameter - */ - numeric(tickValue: number, index: number, ticks: { value: number }[]): string; - /** - * Formatter for logarithmic ticks - * @param tickValue the value to be formatted - * @param index the position of the tickValue parameter in the ticks array - * @param ticks the list of ticks being converted - * @return string representation of the tickValue parameter - */ - logarithmic(tickValue: number, index: number, ticks: { value: number }[]): string; - }; -}; - -export interface TypedRegistry { - /** - * @param {ChartComponent} item - * @returns {string} The scope where items defaults were registered to. - */ - register(item: ChartComponent): string; - get(id: string): T | undefined; - unregister(item: ChartComponent): void; -} - -export interface ChartEvent { - type: - | 'contextmenu' - | 'mouseenter' - | 'mousedown' - | 'mousemove' - | 'mouseup' - | 'mouseout' - | 'click' - | 'dblclick' - | 'keydown' - | 'keypress' - | 'keyup' - | 'resize'; - native: Event | null; - x: number | null; - y: number | null; -} -export interface ChartComponent { - id: string; - defaults?: AnyObject; - defaultRoutes?: { [property: string]: string }; - - beforeRegister?(): void; - afterRegister?(): void; - beforeUnregister?(): void; - afterUnregister?(): void; -} - -export interface CoreInteractionOptions { - /** - * Sets which elements appear in the tooltip. See Interaction Modes for details. - * @default 'nearest' - */ - mode: InteractionMode; - /** - * if true, the hover mode only applies when the mouse position intersects an item on the chart. - * @default true - */ - intersect: boolean; - - /** - * Can be set to 'x', 'y', 'xy' or 'r' to define which directions are used in calculating distances. Defaults to 'x' for 'index' mode and 'xy' in dataset and 'nearest' modes. - */ - axis: 'x' | 'y' | 'xy' | 'r'; -} - -export interface CoreChartOptions extends ParsingOptions, AnimationOptions { - - datasets: { - [key in ChartType]: ChartTypeRegistry[key]['datasetOptions'] - } - - /** - * The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts. - * @default 'x' - */ - indexAxis: 'x' | 'y'; - - /** - * base color - * @see Defaults.color - */ - color: Scriptable>; - /** - * base background color - * @see Defaults.backgroundColor - */ - backgroundColor: Scriptable>; - /** - * base border color - * @see Defaults.borderColor - */ - borderColor: Scriptable>; - /** - * base font - * @see Defaults.font - */ - font: Partial; - /** - * Resizes the chart canvas when its container does (important note...). - * @default true - */ - responsive: boolean; - /** - * Maintain the original canvas aspect ratio (width / height) when resizing. - * @default true - */ - maintainAspectRatio: boolean; - /** - * Delay the resize update by give amount of milliseconds. This can ease the resize process by debouncing update of the elements. - * @default 0 - */ - resizeDelay: number; - - /** - * Canvas aspect ratio (i.e. width / height, a value of 1 representing a square canvas). Note that this option is ignored if the height is explicitly defined either as attribute or via the style. - * @default 2 - */ - aspectRatio: number; - - /** - * Locale used for number formatting (using `Intl.NumberFormat`). - * @default user's browser setting - */ - locale: string; - - /** - * Called when a resize occurs. Gets passed two arguments: the chart instance and the new size. - */ - onResize(chart: Chart, size: { width: number; height: number }): void; - - /** - * Override the window's default devicePixelRatio. - * @default window.devicePixelRatio - */ - devicePixelRatio: number; - - interaction: CoreInteractionOptions; - - hover: CoreInteractionOptions; - - /** - * The events option defines the browser events that the chart should listen to for tooltips and hovering. - * @default ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'] - */ - events: (keyof HTMLElementEventMap)[] - - /** - * Called when any of the events fire. Passed the event, an array of active elements (bars, points, etc), and the chart. - */ - onHover(event: ChartEvent, elements: ActiveElement[], chart: Chart): void; - - /** - * Called if the event is of type 'mouseup' or 'click'. Passed the event, an array of active elements, and the chart. - */ - onClick(event: ChartEvent, elements: ActiveElement[], chart: Chart): void; - - layout: Partial<{ - autoPadding: boolean; - padding: Scriptable, ScriptableContext>; - }>; -} - -export type EasingFunction = - | 'linear' - | 'easeInQuad' - | 'easeOutQuad' - | 'easeInOutQuad' - | 'easeInCubic' - | 'easeOutCubic' - | 'easeInOutCubic' - | 'easeInQuart' - | 'easeOutQuart' - | 'easeInOutQuart' - | 'easeInQuint' - | 'easeOutQuint' - | 'easeInOutQuint' - | 'easeInSine' - | 'easeOutSine' - | 'easeInOutSine' - | 'easeInExpo' - | 'easeOutExpo' - | 'easeInOutExpo' - | 'easeInCirc' - | 'easeOutCirc' - | 'easeInOutCirc' - | 'easeInElastic' - | 'easeOutElastic' - | 'easeInOutElastic' - | 'easeInBack' - | 'easeOutBack' - | 'easeInOutBack' - | 'easeInBounce' - | 'easeOutBounce' - | 'easeInOutBounce'; - -export type AnimationSpec = { - /** - * The number of milliseconds an animation takes. - * @default 1000 - */ - duration?: Scriptable>; - /** - * Easing function to use - * @default 'easeOutQuart' - */ - easing?: Scriptable>; - - /** - * Delay before starting the animations. - * @default 0 - */ - delay?: Scriptable>; - - /** - * If set to true, the animations loop endlessly. - * @default false - */ - loop?: Scriptable>; -} - -export type AnimationsSpec = { - [name: string]: false | AnimationSpec & { - properties: string[]; - - /** - * Type of property, determines the interpolator used. Possible values: 'number', 'color' and 'boolean'. Only really needed for 'color', because typeof does not get that right. - */ - type: 'color' | 'number' | 'boolean'; - - fn: (from: T, to: T, factor: number) => T; - - /** - * Start value for the animation. Current value is used when undefined - */ - from: Scriptable>; - /** - * - */ - to: Scriptable>; - } -} - -export type TransitionSpec = { - animation: AnimationSpec; - animations: AnimationsSpec; -} - -export type TransitionsSpec = { - [mode: string]: TransitionSpec -} - -export type AnimationOptions = { - animation: false | AnimationSpec & { - /** - * Callback called on each step of an animation. - */ - onProgress?: (this: Chart, event: AnimationEvent) => void; - /** - * Callback called when all animations are completed. - */ - onComplete?: (this: Chart, event: AnimationEvent) => void; - }; - animations: AnimationsSpec; - transitions: TransitionsSpec; -}; - -export interface FontSpec { - /** - * Default font family for all text, follows CSS font-family options. - * @default "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" - */ - family: string; - /** - * Default font size (in px) for text. Does not apply to radialLinear scale point labels. - * @default 12 - */ - size: number; - /** - * Default font style. Does not apply to tooltip title or footer. Does not apply to chart title. Follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit) - * @default 'normal' - */ - style: 'normal' | 'italic' | 'oblique' | 'initial' | 'inherit'; - /** - * Default font weight (boldness). (see MDN). - */ - weight: string | null; - /** - * Height of an individual line of text (see MDN). - * @default 1.2 - */ - lineHeight: number | string; -} - -export type TextAlign = 'left' | 'center' | 'right'; -export type Align = 'start' | 'center' | 'end'; - -export interface VisualElement { - draw(ctx: CanvasRenderingContext2D, area?: ChartArea): void; - inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean; - inXRange(mouseX: number, useFinalPosition?: boolean): boolean; - inYRange(mouseY: number, useFinalPosition?: boolean): boolean; - getCenterPoint(useFinalPosition?: boolean): { x: number; y: number }; - getRange?(axis: 'x' | 'y'): number; -} - -export interface CommonElementOptions { - borderWidth: number; - borderColor: Color; - backgroundColor: Color; -} - -export interface CommonHoverOptions { - hoverBorderWidth: number; - hoverBorderColor: Color; - hoverBackgroundColor: Color; -} - -export interface Segment { - start: number; - end: number; - loop: boolean; -} - -export interface ArcProps { - x: number; - y: number; - startAngle: number; - endAngle: number; - innerRadius: number; - outerRadius: number; - circumference: number; -} - -export interface ArcBorderRadius { - outerStart: number; - outerEnd: number; - innerStart: number; - innerEnd: number; -} - -export interface ArcOptions extends CommonElementOptions { - /** - * Arc stroke alignment. - */ - borderAlign: 'center' | 'inner'; - - /** - * Line join style. See MDN. Default is 'round' when `borderAlign` is 'inner', else 'bevel'. - */ - borderJoinStyle: CanvasLineJoin; - - /** - * Sets the border radius for arcs - * @default 0 - */ - borderRadius: number | ArcBorderRadius; - - /** - * Arc offset (in pixels). - */ - offset: number; -} - -export interface ArcHoverOptions extends CommonHoverOptions { - hoverOffset: number; -} - -export interface ArcElement - extends Element, - VisualElement {} - -export const ArcElement: ChartComponent & { - prototype: ArcElement; - new (cfg: AnyObject): ArcElement; -}; - -export interface LineProps { - points: Point[] -} - -export interface LineOptions extends CommonElementOptions { - /** - * Line cap style. See MDN. - * @default 'butt' - */ - borderCapStyle: CanvasLineCap; - /** - * Line dash. See MDN. - * @default [] - */ - borderDash: number[]; - /** - * Line dash offset. See MDN. - * @default 0.0 - */ - borderDashOffset: number; - /** - * Line join style. See MDN. - * @default 'miter' - */ - borderJoinStyle: CanvasLineJoin; - /** - * true to keep Bézier control inside the chart, false for no restriction. - * @default true - */ - capBezierPoints: boolean; - /** - * Interpolation mode to apply. - * @default 'default' - */ - cubicInterpolationMode: 'default' | 'monotone'; - /** - * Bézier curve tension (0 for no Bézier curves). - * @default 0 - */ - tension: number; - /** - * true to show the line as a stepped line (tension will be ignored). - * @default false - */ - stepped: 'before' | 'after' | 'middle' | boolean; - /** - * Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end - */ - fill: FillTarget | ComplexFillTarget; - /** - * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used. - */ - spanGaps: boolean | number; - - segment: { - backgroundColor: Scriptable, - borderColor: Scriptable, - borderCapStyle: Scriptable; - borderDash: Scriptable; - borderDashOffset: Scriptable; - borderJoinStyle: Scriptable; - borderWidth: Scriptable; - }; -} - -export interface LineHoverOptions extends CommonHoverOptions { - hoverBorderCapStyle: CanvasLineCap; - hoverBorderDash: number[]; - hoverBorderDashOffset: number; - hoverBorderJoinStyle: CanvasLineJoin; -} - -export interface LineElement - extends Element, - VisualElement { - updateControlPoints(chartArea: ChartArea, indexAxis?: 'x' | 'y'): void; - points: Point[]; - readonly segments: Segment[]; - first(): Point | false; - last(): Point | false; - interpolate(point: Point, property: 'x' | 'y'): undefined | Point | Point[]; - pathSegment(ctx: CanvasRenderingContext2D, segment: Segment, params: AnyObject): undefined | boolean; - path(ctx: CanvasRenderingContext2D): boolean; -} - -export const LineElement: ChartComponent & { - prototype: LineElement; - new (cfg: AnyObject): LineElement; -}; - -export interface PointProps { - x: number; - y: number; -} - -export type PointStyle = - | 'circle' - | 'cross' - | 'crossRot' - | 'dash' - | 'line' - | 'rect' - | 'rectRounded' - | 'rectRot' - | 'star' - | 'triangle' - | HTMLImageElement - | HTMLCanvasElement; - -export interface PointOptions extends CommonElementOptions { - /** - * Point radius - * @default 3 - */ - radius: number; - /** - * Extra radius added to point radius for hit detection. - * @default 1 - */ - hitRadius: number; - /** - * Point style - * @default 'circle; - */ - pointStyle: PointStyle; - /** - * Point rotation (in degrees). - * @default 0 - */ - rotation: number; - /** - * Draw the active elements over the other elements of the dataset, - * @default true - */ - drawActiveElementsOnTop: boolean; -} - -export interface PointHoverOptions extends CommonHoverOptions { - /** - * Point radius when hovered. - * @default 4 - */ - hoverRadius: number; -} - -export interface PointPrefixedOptions { - /** - * The fill color for points. - */ - pointBackgroundColor: Color; - /** - * The border color for points. - */ - pointBorderColor: Color; - /** - * The width of the point border in pixels. - */ - pointBorderWidth: number; - /** - * The pixel size of the non-displayed point that reacts to mouse events. - */ - pointHitRadius: number; - /** - * The radius of the point shape. If set to 0, the point is not rendered. - */ - pointRadius: number; - /** - * The rotation of the point in degrees. - */ - pointRotation: number; - /** - * Style of the point. - */ - pointStyle: PointStyle; -} - -export interface PointPrefixedHoverOptions { - /** - * Point background color when hovered. - */ - pointHoverBackgroundColor: Color; - /** - * Point border color when hovered. - */ - pointHoverBorderColor: Color; - /** - * Border width of point when hovered. - */ - pointHoverBorderWidth: number; - /** - * The radius of the point when hovered. - */ - pointHoverRadius: number; -} - -export interface PointElement - extends Element, - VisualElement { - readonly skip: boolean; - readonly parsed: CartesianParsedData; -} - -export const PointElement: ChartComponent & { - prototype: PointElement; - new (cfg: AnyObject): PointElement; -}; - -export interface BarProps { - x: number; - y: number; - base: number; - horizontal: boolean; - width: number; - height: number; -} - -export interface BarOptions extends Omit { - /** - * The base value for the bar in data units along the value axis. - */ - base: number; - - /** - * Skipped (excluded) border: 'start', 'end', 'left', 'right', 'bottom', 'top' or false (none). - * @default 'start' - */ - borderSkipped: 'start' | 'end' | 'left' | 'right' | 'bottom' | 'top' | false; - - /** - * Border radius - * @default 0 - */ - borderRadius: number | BorderRadius; - - /** - * Amount to inflate the rectangle(s). This can be used to hide artifacts between bars. - * Unit is pixels. 'auto' translates to 0.33 pixels when barPercentage * categoryPercentage is 1, else 0. - * @default 'auto' - */ - inflateAmount: number | 'auto'; - - /** - * Width of the border, number for all sides, object to specify width for each side specifically - * @default 0 - */ - borderWidth: number | { top?: number, right?: number, bottom?: number, left?: number }; -} - -export interface BorderRadius { - topLeft: number; - topRight: number; - bottomLeft: number; - bottomRight: number; -} - -export interface BarHoverOptions extends CommonHoverOptions { - hoverBorderRadius: number | BorderRadius; -} - -export interface BarElement< - T extends BarProps = BarProps, - O extends BarOptions = BarOptions -> extends Element, VisualElement {} - -export const BarElement: ChartComponent & { - prototype: BarElement; - new (cfg: AnyObject): BarElement; -}; - -export interface ElementOptionsByType { - arc: ScriptableAndArrayOptions>; - bar: ScriptableAndArrayOptions>; - line: ScriptableAndArrayOptions>; - point: ScriptableAndArrayOptions>; -} - -export type ElementChartOptions = { - elements: ElementOptionsByType -}; - -export class BasePlatform { - /** - * Called at chart construction time, returns a context2d instance implementing - * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. - * @param {HTMLCanvasElement} canvas - The canvas from which to acquire context (platform specific) - * @param options - The chart options - */ - acquireContext( - canvas: HTMLCanvasElement, - options?: CanvasRenderingContext2DSettings - ): CanvasRenderingContext2D | null; - /** - * Called at chart destruction time, releases any resources associated to the context - * previously returned by the acquireContext() method. - * @param {CanvasRenderingContext2D} context - The context2d instance - * @returns {boolean} true if the method succeeded, else false - */ - releaseContext(context: CanvasRenderingContext2D): boolean; - /** - * Registers the specified listener on the given chart. - * @param {Chart} chart - Chart from which to listen for event - * @param {string} type - The ({@link ChartEvent}) type to listen for - * @param listener - Receives a notification (an object that implements - * the {@link ChartEvent} interface) when an event of the specified type occurs. - */ - addEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void; - /** - * Removes the specified listener previously registered with addEventListener. - * @param {Chart} chart - Chart from which to remove the listener - * @param {string} type - The ({@link ChartEvent}) type to remove - * @param listener - The listener function to remove from the event target. - */ - removeEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void; - /** - * @returns {number} the current devicePixelRatio of the device this platform is connected to. - */ - getDevicePixelRatio(): number; - /** - * @param {HTMLCanvasElement} canvas - The canvas for which to calculate the maximum size - * @param {number} [width] - Parent element's content width - * @param {number} [height] - Parent element's content height - * @param {number} [aspectRatio] - The aspect ratio to maintain - * @returns { width: number, height: number } the maximum size available. - */ - getMaximumSize(canvas: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number }; - /** - * @param {HTMLCanvasElement} canvas - * @returns {boolean} true if the canvas is attached to the platform, false if not. - */ - isAttached(canvas: HTMLCanvasElement): boolean; - /** - * Updates config with platform specific requirements - * @param {ChartConfiguration} config - */ - updateConfig(config: ChartConfiguration): void; -} - -export class BasicPlatform extends BasePlatform {} -export class DomPlatform extends BasePlatform {} - -export const Decimation: Plugin; - -export const enum DecimationAlgorithm { - lttb = 'lttb', - minmax = 'min-max', -} -interface BaseDecimationOptions { - enabled: boolean; - threshold?: number; -} - -interface LttbDecimationOptions extends BaseDecimationOptions { - algorithm: DecimationAlgorithm.lttb | 'lttb'; - samples?: number; -} - -interface MinMaxDecimationOptions extends BaseDecimationOptions { - algorithm: DecimationAlgorithm.minmax | 'min-max'; -} - -export type DecimationOptions = LttbDecimationOptions | MinMaxDecimationOptions; - -export const Filler: Plugin; -export interface FillerOptions { - drawTime: 'beforeDatasetDraw' | 'beforeDatasetsDraw'; - propagate: boolean; -} - -export type FillTarget = number | string | { value: number } | 'start' | 'end' | 'origin' | 'stack' | 'shape' | boolean; - -export interface ComplexFillTarget { - /** - * The accepted values are the same as the filling mode values, so you may use absolute and relative dataset indexes and/or boundaries. - */ - target: FillTarget; - /** - * If no color is set, the default color will be the background color of the chart. - */ - above: Color; - /** - * Same as the above. - */ - below: Color; -} - -export interface FillerControllerDatasetOptions { - /** - * Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end - */ - fill: FillTarget | ComplexFillTarget; -} - -export const Legend: Plugin; - -export interface LegendItem { - /** - * Label that will be displayed - */ - text: string; - - /** - * Border radius of the legend box - * @since 3.1.0 - */ - borderRadius?: number | BorderRadius; - - /** - * Index of the associated dataset - */ - datasetIndex: number; - - /** - * Fill style of the legend box - */ - fillStyle?: Color; - - /** - * Font color for the text - * Defaults to LegendOptions.labels.color - */ - fontColor?: Color; - - /** - * If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect - */ - hidden?: boolean; - - /** - * For box border. - * @see https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap - */ - lineCap?: CanvasLineCap; - - /** - * For box border. - * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash - */ - lineDash?: number[]; - - /** - * For box border. - * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset - */ - lineDashOffset?: number; - - /** - * For box border. - * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin - */ - lineJoin?: CanvasLineJoin; - - /** - * Width of box border - */ - lineWidth?: number; - - /** - * Stroke style of the legend box - */ - strokeStyle?: Color; - - /** - * Point style of the legend box (only used if usePointStyle is true) - */ - pointStyle?: PointStyle; - - /** - * Rotation of the point in degrees (only used if usePointStyle is true) - */ - rotation?: number; - - /** - * Text alignment - */ - textAlign?: TextAlign; -} - -export interface LegendElement extends Element>, LayoutItem { - chart: Chart; - ctx: CanvasRenderingContext2D; - legendItems?: LegendItem[]; - options: LegendOptions; -} - -export interface LegendOptions { - /** - * Is the legend shown? - * @default true - */ - display: boolean; - /** - * Position of the legend. - * @default 'top' - */ - position: LayoutPosition; - /** - * Alignment of the legend. - * @default 'center' - */ - align: Align; - /** - * Maximum height of the legend, in pixels - */ - maxHeight: number; - /** - * Maximum width of the legend, in pixels - */ - maxWidth: number; - /** - * Marks that this box should take the full width/height of the canvas (moving other boxes). This is unlikely to need to be changed in day-to-day use. - * @default true - */ - fullSize: boolean; - /** - * Legend will show datasets in reverse order. - * @default false - */ - reverse: boolean; - /** - * A callback that is called when a click event is registered on a label item. - */ - onClick(this: LegendElement, e: ChartEvent, legendItem: LegendItem, legend: LegendElement): void; - /** - * A callback that is called when a 'mousemove' event is registered on top of a label item - */ - onHover(this: LegendElement, e: ChartEvent, legendItem: LegendItem, legend: LegendElement): void; - /** - * A callback that is called when a 'mousemove' event is registered outside of a previously hovered label item. - */ - onLeave(this: LegendElement, e: ChartEvent, legendItem: LegendItem, legend: LegendElement): void; - - labels: { - /** - * Width of colored box. - * @default 40 - */ - boxWidth: number; - /** - * Height of the coloured box. - * @default fontSize - */ - boxHeight: number; - /** - * Padding between the color box and the text - * @default 1 - */ - boxPadding: number; - /** - * Color of label - * @see Defaults.color - */ - color: Color; - /** - * Font of label - * @see Defaults.font - */ - font: FontSpec; - /** - * Padding between rows of colored boxes. - * @default 10 - */ - padding: number; - /** - * Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See Legend Item for details. - */ - generateLabels(chart: Chart): LegendItem[]; - - /** - * Filters legend items out of the legend. Receives 2 parameters, a Legend Item and the chart data - */ - filter(item: LegendItem, data: ChartData): boolean; - - /** - * Sorts the legend items - */ - sort(a: LegendItem, b: LegendItem, data: ChartData): number; - - /** - * Override point style for the legend. Only applies if usePointStyle is true - */ - pointStyle: PointStyle; - - /** - * Text alignment - */ - textAlign?: TextAlign; - - /** - * Label style will match corresponding point style (size is based on the minimum value between boxWidth and font.size). - * @default false - */ - usePointStyle: boolean; - }; - /** - * true for rendering the legends from right to left. - */ - rtl: boolean; - /** - * This will force the text direction 'rtl' or 'ltr' on the canvas for rendering the legend, regardless of the css specified on the canvas - * @default canvas' default - */ - textDirection: string; - - title: { - /** - * Is the legend title displayed. - * @default false - */ - display: boolean; - /** - * Color of title - * @see Defaults.color - */ - color: Color; - /** - * see Fonts - */ - font: FontSpec; - position: 'center' | 'start' | 'end'; - padding?: number | ChartArea; - /** - * The string title. - */ - text: string; - }; -} - -export const SubTitle: Plugin; -export const Title: Plugin; - -export interface TitleOptions { - /** - * Alignment of the title. - * @default 'center' - */ - align: Align; - /** - * Is the title shown? - * @default false - */ - display: boolean; - /** - * Position of title - * @default 'top' - */ - position: 'top' | 'left' | 'bottom' | 'right'; - /** - * Color of text - * @see Defaults.color - */ - color: Color; - font: FontSpec; - - /** - * Marks that this box should take the full width/height of the canvas (moving other boxes). If set to `false`, places the box above/beside the - * chart area - * @default true - */ - fullSize: boolean; - /** - * Adds padding above and below the title text if a single number is specified. It is also possible to change top and bottom padding separately. - */ - padding: number | { top: number; bottom: number }; - /** - * Title text to display. If specified as an array, text is rendered on multiple lines. - */ - text: string | string[]; -} - -export type TooltipXAlignment = 'left' | 'center' | 'right'; -export type TooltipYAlignment = 'top' | 'center' | 'bottom'; -export interface TooltipLabelStyle { - borderColor: Color; - backgroundColor: Color; - - /** - * Width of border line - * @since 3.1.0 - */ - borderWidth?: number; - - /** - * Border dash - * @since 3.1.0 - */ - borderDash?: [number, number]; - - /** - * Border dash offset - * @since 3.1.0 - */ - borderDashOffset?: number; - - /** - * borderRadius - * @since 3.1.0 - */ - borderRadius?: number | BorderRadius; -} -export interface TooltipModel extends Element> { - readonly chart: Chart; - - // The items that we are rendering in the tooltip. See Tooltip Item Interface section - dataPoints: TooltipItem[]; - - // Positioning - xAlign: TooltipXAlignment; - yAlign: TooltipYAlignment; - - // X and Y properties are the top left of the tooltip - x: number; - y: number; - width: number; - height: number; - // Where the tooltip points to - caretX: number; - caretY: number; - - // Body - // The body lines that need to be rendered - // Each object contains 3 parameters - // before: string[] // lines of text before the line with the color square - // lines: string[]; // lines of text to render as the main item with color square - // after: string[]; // lines of text to render after the main lines - body: { before: string[]; lines: string[]; after: string[] }[]; - // lines of text that appear after the title but before the body - beforeBody: string[]; - // line of text that appear after the body and before the footer - afterBody: string[]; - - // Title - // lines of text that form the title - title: string[]; - - // Footer - // lines of text that form the footer - footer: string[]; - - // Styles to render for each item in body[]. This is the styling of the squares in the tooltip - labelColors: TooltipLabelStyle[]; - labelTextColors: Color[]; - labelPointStyles: { pointStyle: PointStyle; rotation: number }[]; - - // 0 opacity is a hidden tooltip - opacity: number; - - // tooltip options - options: TooltipOptions; - - getActiveElements(): ActiveElement[]; - setActiveElements(active: ActiveDataPoint[], eventPosition: Point): void; -} - -export interface TooltipPosition { - x: number; - y: number; - xAlign?: TooltipXAlignment; - yAlign?: TooltipYAlignment; -} - -export type TooltipPositionerFunction = ( - this: TooltipModel, - items: readonly ActiveElement[], - eventPosition: Point -) => TooltipPosition | false; - -export interface TooltipPositionerMap { - average: TooltipPositionerFunction; - nearest: TooltipPositionerFunction; -} - -export type TooltipPositioner = keyof TooltipPositionerMap; - -export interface Tooltip extends Plugin { - readonly positioners: TooltipPositionerMap; -} - -export const Tooltip: Tooltip; - -export interface TooltipCallbacks< - TType extends ChartType, - Model = TooltipModel, - Item = TooltipItem> { - - beforeTitle(this: Model, tooltipItems: Item[]): string | string[]; - title(this: Model, tooltipItems: Item[]): string | string[]; - afterTitle(this: Model, tooltipItems: Item[]): string | string[]; - - beforeBody(this: Model, tooltipItems: Item[]): string | string[]; - afterBody(this: Model, tooltipItems: Item[]): string | string[]; - - beforeLabel(this: Model, tooltipItem: Item): string | string[]; - label(this: Model, tooltipItem: Item): string | string[]; - afterLabel(this: Model, tooltipItem: Item): string | string[]; - - labelColor(this: Model, tooltipItem: Item): TooltipLabelStyle; - labelTextColor(this: Model, tooltipItem: Item): Color; - labelPointStyle(this: Model, tooltipItem: Item): { pointStyle: PointStyle; rotation: number }; - - beforeFooter(this: Model, tooltipItems: Item[]): string | string[]; - footer(this: Model, tooltipItems: Item[]): string | string[]; - afterFooter(this: Model, tooltipItems: Item[]): string | string[]; -} - -export interface ExtendedPlugin< - TType extends ChartType, - O = AnyObject, - Model = TooltipModel> { - /** - * @desc Called before drawing the `tooltip`. If any plugin returns `false`, - * the tooltip drawing is cancelled until another `render` is triggered. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {Tooltip} args.tooltip - The tooltip. - * @param {object} options - The plugin options. - * @returns {boolean} `false` to cancel the chart tooltip drawing. - */ - beforeTooltipDraw?(chart: Chart, args: { tooltip: Model }, options: O): boolean | void; - /** - * @desc Called after drawing the `tooltip`. Note that this hook will not - * be called if the tooltip drawing has been previously cancelled. - * @param {Chart} chart - The chart instance. - * @param {object} args - The call arguments. - * @param {Tooltip} args.tooltip - The tooltip. - * @param {object} options - The plugin options. - */ - afterTooltipDraw?(chart: Chart, args: { tooltip: Model }, options: O): void; -} - -export interface ScriptableTooltipContext { - chart: UnionToIntersection>; - tooltip: UnionToIntersection>; - tooltipItems: TooltipItem[]; -} - -export interface TooltipOptions extends CoreInteractionOptions { - /** - * Are on-canvas tooltips enabled? - * @default true - */ - enabled: Scriptable>; - /** - * See external tooltip section. - */ - external(this: TooltipModel, args: { chart: Chart; tooltip: TooltipModel }): void; - /** - * The mode for positioning the tooltip - */ - position: Scriptable> - - /** - * Override the tooltip alignment calculations - */ - xAlign: Scriptable>; - yAlign: Scriptable>; - - /** - * Sort tooltip items. - */ - itemSort: (a: TooltipItem, b: TooltipItem, data: ChartData) => number; - - filter: (e: TooltipItem, index: number, array: TooltipItem[], data: ChartData) => boolean; - - /** - * Background color of the tooltip. - * @default 'rgba(0, 0, 0, 0.8)' - */ - backgroundColor: Scriptable>; - /** - * Padding between the color box and the text. - * @default 1 - */ - boxPadding: number; - /** - * Color of title - * @default '#fff' - */ - titleColor: Scriptable>; - /** - * See Fonts - * @default {weight: 'bold'} - */ - titleFont: Scriptable>; - /** - * Spacing to add to top and bottom of each title line. - * @default 2 - */ - titleSpacing: Scriptable>; - /** - * Margin to add on bottom of title section. - * @default 6 - */ - titleMarginBottom: Scriptable>; - /** - * Horizontal alignment of the title text lines. - * @default 'left' - */ - titleAlign: Scriptable>; - /** - * Spacing to add to top and bottom of each tooltip item. - * @default 2 - */ - bodySpacing: Scriptable>; - /** - * Color of body - * @default '#fff' - */ - bodyColor: Scriptable>; - /** - * See Fonts. - * @default {} - */ - bodyFont: Scriptable>; - /** - * Horizontal alignment of the body text lines. - * @default 'left' - */ - bodyAlign: Scriptable>; - /** - * Spacing to add to top and bottom of each footer line. - * @default 2 - */ - footerSpacing: Scriptable>; - /** - * Margin to add before drawing the footer. - * @default 6 - */ - footerMarginTop: Scriptable>; - /** - * Color of footer - * @default '#fff' - */ - footerColor: Scriptable>; - /** - * See Fonts - * @default {weight: 'bold'} - */ - footerFont: Scriptable>; - /** - * Horizontal alignment of the footer text lines. - * @default 'left' - */ - footerAlign: Scriptable>; - /** - * Padding to add to the tooltip - * @default 6 - */ - padding: Scriptable>; - /** - * Extra distance to move the end of the tooltip arrow away from the tooltip point. - * @default 2 - */ - caretPadding: Scriptable>; - /** - * Size, in px, of the tooltip arrow. - * @default 5 - */ - caretSize: Scriptable>; - /** - * Radius of tooltip corner curves. - * @default 6 - */ - cornerRadius: Scriptable>; - /** - * Color to draw behind the colored boxes when multiple items are in the tooltip. - * @default '#fff' - */ - multiKeyBackground: Scriptable>; - /** - * If true, color boxes are shown in the tooltip. - * @default true - */ - displayColors: Scriptable>; - /** - * Width of the color box if displayColors is true. - * @default bodyFont.size - */ - boxWidth: Scriptable>; - /** - * Height of the color box if displayColors is true. - * @default bodyFont.size - */ - boxHeight: Scriptable>; - /** - * Use the corresponding point style (from dataset options) instead of color boxes, ex: star, triangle etc. (size is based on the minimum value between boxWidth and boxHeight) - * @default false - */ - usePointStyle: Scriptable>; - /** - * Color of the border. - * @default 'rgba(0, 0, 0, 0)' - */ - borderColor: Scriptable>; - /** - * Size of the border. - * @default 0 - */ - borderWidth: Scriptable>; - /** - * true for rendering the legends from right to left. - */ - rtl: Scriptable>; - - /** - * This will force the text direction 'rtl' or 'ltr on the canvas for rendering the tooltips, regardless of the css specified on the canvas - * @default canvas's default - */ - textDirection: Scriptable>; - - animation: AnimationSpec; - animations: AnimationsSpec; - callbacks: TooltipCallbacks; -} - -export interface TooltipItem { - /** - * The chart the tooltip is being shown on - */ - chart: Chart; - - /** - * Label for the tooltip - */ - label: string; - - /** - * Parsed data values for the given `dataIndex` and `datasetIndex` - */ - parsed: UnionToIntersection>; - - /** - * Raw data values for the given `dataIndex` and `datasetIndex` - */ - raw: unknown; - - /** - * Formatted value for the tooltip - */ - formattedValue: string; - - /** - * The dataset the item comes from - */ - dataset: UnionToIntersection>; - - /** - * Index of the dataset the item comes from - */ - datasetIndex: number; - - /** - * Index of this data item in the dataset - */ - dataIndex: number; - - /** - * The chart element (point, arc, bar, etc.) for this tooltip item - */ - element: Element; -} - -export interface PluginOptionsByType { - decimation: DecimationOptions; - filler: FillerOptions; - legend: LegendOptions; - subtitle: TitleOptions; - title: TitleOptions; - tooltip: TooltipOptions; -} -export interface PluginChartOptions { - plugins: PluginOptionsByType; -} - -export interface GridLineOptions { - /** - * @default true - */ - display: boolean; - borderColor: Color; - borderWidth: number; - /** - * @default false - */ - circular: boolean; - /** - * @default 'rgba(0, 0, 0, 0.1)' - */ - color: ScriptableAndArray; - /** - * @default [] - */ - borderDash: number[]; - /** - * @default 0 - */ - borderDashOffset: Scriptable; - /** - * @default 1 - */ - lineWidth: ScriptableAndArray; - - /** - * @default true - */ - drawBorder: boolean; - /** - * @default true - */ - drawOnChartArea: boolean; - /** - * @default true - */ - drawTicks: boolean; - /** - * @default [] - */ - tickBorderDash: number[]; - /** - * @default 0 - */ - tickBorderDashOffset: Scriptable; - /** - * @default 'rgba(0, 0, 0, 0.1)' - */ - tickColor: ScriptableAndArray; - /** - * @default 10 - */ - tickLength: number; - /** - * @default 1 - */ - tickWidth: number; - /** - * @default false - */ - offset: boolean; - /** - * @default 0 - */ - z: number; -} - -export interface TickOptions { - /** - * Color of label backdrops. - * @default 'rgba(255, 255, 255, 0.75)' - */ - backdropColor: Scriptable; - /** - * Padding of tick backdrop. - * @default 2 - */ - backdropPadding: number | ChartArea; - - /** - * Returns the string representation of the tick value as it should be displayed on the chart. See callback. - */ - callback: (this: Scale, tickValue: number | string, index: number, ticks: Tick[]) => string | string[] | number | number[] | null | undefined; - /** - * If true, show tick labels. - * @default true - */ - display: boolean; - /** - * Color of tick - * @see Defaults.color - */ - color: ScriptableAndArray; - /** - * see Fonts - */ - font: Scriptable; - /** - * Sets the offset of the tick labels from the axis - */ - padding: number; - /** - * If true, draw a background behind the tick labels. - * @default false - */ - showLabelBackdrop: Scriptable; - /** - * The color of the stroke around the text. - * @default undefined - */ - textStrokeColor: Scriptable; - /** - * Stroke width around the text. - * @default 0 - */ - textStrokeWidth: Scriptable; - /** - * z-index of tick layer. Useful when ticks are drawn on chart area. Values <= 0 are drawn under datasets, > 0 on top. - * @default 0 - */ - z: number; - - major: { - /** - * If true, major ticks are generated. A major tick will affect autoskipping and major will be defined on ticks in the scriptable options context. - * @default false - */ - enabled: boolean; - }; -} - -export interface CartesianScaleOptions extends CoreScaleOptions { - /** - * Scale boundary strategy (bypassed by min/max time options) - * - `data`: make sure data are fully visible, ticks outside are removed - * - `ticks`: make sure ticks are fully visible, data outside are truncated - * @since 2.7.0 - * @default 'ticks' - */ - bounds: 'ticks' | 'data'; - - /** - * Position of the axis. - */ - position: 'left' | 'top' | 'right' | 'bottom' | 'center' | { [scale: string]: number }; - - /** - * Stack group. Axes at the same `position` with same `stack` are stacked. - */ - stack?: string; - - /** - * Weight of the scale in stack group. Used to determine the amount of allocated space for the scale within the group. - * @default 1 - */ - stackWeight?: number; - - /** - * Which type of axis this is. Possible values are: 'x', 'y'. If not set, this is inferred from the first character of the ID which should be 'x' or 'y'. - */ - axis: 'x' | 'y'; - - /** - * User defined minimum value for the scale, overrides minimum value from data. - */ - min: number; - - /** - * User defined maximum value for the scale, overrides maximum value from data. - */ - max: number; - - /** - * If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true for a bar chart by default. - * @default false - */ - offset: boolean; - - grid: GridLineOptions; - - /** Options for the scale title. */ - title: { - /** If true, displays the axis title. */ - display: boolean; - /** Alignment of the axis title. */ - align: Align; - /** The text for the title, e.g. "# of People" or "Response Choices". */ - text: string | string[]; - /** Color of the axis label. */ - color: Color; - /** Information about the axis title font. */ - font: FontSpec; - /** Padding to apply around scale labels. */ - padding: number | { - /** Padding on the (relative) top side of this axis label. */ - top: number; - /** Padding on the (relative) bottom side of this axis label. */ - bottom: number; - /** This is a shorthand for defining top/bottom to the same values. */ - y: number; - }; - }; - - /** - * If true, data will be comprised between datasets of data - * @default false - */ - stacked?: boolean | 'single'; - - ticks: TickOptions & { - /** - * The number of ticks to examine when deciding how many labels will fit. Setting a smaller value will be faster, but may be less accurate when there is large variability in label length. - * @default ticks.length - */ - sampleSize: number; - /** - * The label alignment - * @default 'center' - */ - align: Align; - /** - * If true, automatically calculates how many labels can be shown and hides labels accordingly. Labels will be rotated up to maxRotation before skipping any. Turn autoSkip off to show all labels no matter what. - * @default true - */ - autoSkip: boolean; - /** - * Padding between the ticks on the horizontal axis when autoSkip is enabled. - * @default 0 - */ - autoSkipPadding: number; - - /** - * How is the label positioned perpendicular to the axis direction. - * This only applies when the rotation is 0 and the axis position is one of "top", "left", "right", or "bottom" - * @default 'near' - */ - crossAlign: 'near' | 'center' | 'far'; - - /** - * Should the defined `min` and `max` values be presented as ticks even if they are not "nice". - * @default: true - */ - includeBounds: boolean; - - /** - * Distance in pixels to offset the label from the centre point of the tick (in the x direction for the x axis, and the y direction for the y axis). Note: this can cause labels at the edges to be cropped by the edge of the canvas - * @default 0 - */ - labelOffset: number; - - /** - * Minimum rotation for tick labels. Note: Only applicable to horizontal scales. - * @default 0 - */ - minRotation: number; - /** - * Maximum rotation for tick labels when rotating to condense labels. Note: Rotation doesn't occur until necessary. Note: Only applicable to horizontal scales. - * @default 50 - */ - maxRotation: number; - /** - * Flips tick labels around axis, displaying the labels inside the chart instead of outside. Note: Only applicable to vertical scales. - * @default false - */ - mirror: boolean; - /** - * Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction. - * @default 0 - */ - padding: number; - /** - * Maximum number of ticks and gridlines to show. - * @default 11 - */ - maxTicksLimit: number; - }; -} - -export type CategoryScaleOptions = Omit & { - min: string | number; - max: string | number; - labels: string[] | string[][]; -}; - -export type CategoryScale = Scale -export const CategoryScale: ChartComponent & { - prototype: CategoryScale; - new (cfg: AnyObject): CategoryScale; -}; - -export type LinearScaleOptions = CartesianScaleOptions & { - - /** - * if true, scale will include 0 if it is not already included. - * @default true - */ - beginAtZero: boolean; - /** - * Adjustment used when calculating the maximum data value. - */ - suggestedMin?: number; - /** - * Adjustment used when calculating the minimum data value. - */ - suggestedMax?: number; - /** - * Percentage (string ending with %) or amount (number) for added room in the scale range above and below data. - */ - grace?: string | number; - - ticks: { - /** - * The Intl.NumberFormat options used by the default label formatter - */ - format: Intl.NumberFormatOptions; - - /** - * if defined and stepSize is not specified, the step size will be rounded to this many decimal places. - */ - precision: number; - - /** - * User defined fixed step size for the scale - */ - stepSize: number; - - /** - * User defined count of ticks - */ - count: number; - }; -}; - -export type LinearScale = Scale -export const LinearScale: ChartComponent & { - prototype: LinearScale; - new (cfg: AnyObject): LinearScale; -}; - -export type LogarithmicScaleOptions = CartesianScaleOptions & { - /** - * Adjustment used when calculating the maximum data value. - */ - suggestedMin?: number; - /** - * Adjustment used when calculating the minimum data value. - */ - suggestedMax?: number; - - ticks: { - /** - * The Intl.NumberFormat options used by the default label formatter - */ - format: Intl.NumberFormatOptions; - }; -}; - -export type LogarithmicScale = Scale -export const LogarithmicScale: ChartComponent & { - prototype: LogarithmicScale; - new (cfg: AnyObject): LogarithmicScale; -}; - -export type TimeScaleOptions = Omit & { - min: string | number; - max: string | number; - suggestedMin: string | number; - suggestedMax: string | number; - /** - * Scale boundary strategy (bypassed by min/max time options) - * - `data`: make sure data are fully visible, ticks outside are removed - * - `ticks`: make sure ticks are fully visible, data outside are truncated - * @since 2.7.0 - * @default 'data' - */ - bounds: 'ticks' | 'data'; - - /** - * options for creating a new adapter instance - */ - adapters: { - date: unknown; - }; - - time: { - /** - * Custom parser for dates. - */ - parser: string | ((v: unknown) => number); - /** - * If defined, dates will be rounded to the start of this unit. See Time Units below for the allowed units. - */ - round: false | TimeUnit; - /** - * If boolean and true and the unit is set to 'week', then the first day of the week will be Monday. Otherwise, it will be Sunday. - * If `number`, the index of the first day of the week (0 - Sunday, 6 - Saturday). - * @default false - */ - isoWeekday: boolean | number; - /** - * Sets how different time units are displayed. - */ - displayFormats: { - [key: string]: string; - }; - /** - * The format string to use for the tooltip. - */ - tooltipFormat: string; - /** - * If defined, will force the unit to be a certain type. See Time Units section below for details. - * @default false - */ - unit: false | TimeUnit; - - /** - * The number of units between grid lines. - * @default 1 - */ - stepSize: number; - /** - * The minimum display format to be used for a time unit. - * @default 'millisecond' - */ - minUnit: TimeUnit; - }; - - ticks: { - /** - * Ticks generation input values: - * - 'auto': generates "optimal" ticks based on scale size and time options. - * - 'data': generates ticks from data (including labels from data {t|x|y} objects). - * - 'labels': generates ticks from user given `data.labels` values ONLY. - * @see https://github.com/chartjs/Chart.js/pull/4507 - * @since 2.7.0 - * @default 'auto' - */ - source: 'labels' | 'auto' | 'data'; - }; -}; - -export interface TimeScale extends Scale { - getDataTimestamps(): number[]; - getLabelTimestamps(): string[]; - normalize(values: number[]): number[]; -} - -export const TimeScale: ChartComponent & { - prototype: TimeScale; - new (cfg: AnyObject): TimeScale; -}; - -export type TimeSeriesScale = TimeScale -export const TimeSeriesScale: ChartComponent & { - prototype: TimeSeriesScale; - new (cfg: AnyObject): TimeSeriesScale; -}; - -export type RadialLinearScaleOptions = CoreScaleOptions & { - animate: boolean; - - angleLines: { - /** - * if true, angle lines are shown. - * @default true - */ - display: boolean; - /** - * Color of angled lines. - * @default 'rgba(0, 0, 0, 0.1)' - */ - color: Scriptable; - /** - * Width of angled lines. - * @default 1 - */ - lineWidth: Scriptable; - /** - * Length and spacing of dashes on angled lines. See MDN. - * @default [] - */ - borderDash: Scriptable; - /** - * Offset for line dashes. See MDN. - * @default 0 - */ - borderDashOffset: Scriptable; - }; - - /** - * if true, scale will include 0 if it is not already included. - * @default false - */ - beginAtZero: boolean; - - grid: GridLineOptions; - - /** - * User defined minimum number for the scale, overrides minimum value from data. - */ - min: number; - /** - * User defined maximum number for the scale, overrides maximum value from data. - */ - max: number; - - pointLabels: { - /** - * Background color of the point label. - * @default undefined - */ - backdropColor: Scriptable; - /** - * Padding of label backdrop. - * @default 2 - */ - backdropPadding: Scriptable; - - /** - * if true, point labels are shown. - * @default true - */ - display: boolean; - /** - * Color of label - * @see Defaults.color - */ - color: Scriptable; - /** - */ - font: Scriptable; - - /** - * Callback function to transform data labels to point labels. The default implementation simply returns the current string. - */ - callback: (label: string, index: number) => string | string[] | number | number[]; - - /** - * if true, point labels are centered. - * @default false - */ - centerPointLabels: boolean; - }; - - /** - * Adjustment used when calculating the maximum data value. - */ - suggestedMax: number; - /** - * Adjustment used when calculating the minimum data value. - */ - suggestedMin: number; - - ticks: TickOptions & { - /** - * The Intl.NumberFormat options used by the default label formatter - */ - format: Intl.NumberFormatOptions; - - /** - * Maximum number of ticks and gridlines to show. - * @default 11 - */ - maxTicksLimit: number; - - /** - * if defined and stepSize is not specified, the step size will be rounded to this many decimal places. - */ - precision: number; - - /** - * User defined fixed step size for the scale. - */ - stepSize: number; - - /** - * User defined number of ticks - */ - count: number; - }; -}; - -export interface RadialLinearScale extends Scale { - setCenterPoint(leftMovement: number, rightMovement: number, topMovement: number, bottomMovement: number): void; - getIndexAngle(index: number): number; - getDistanceFromCenterForValue(value: number): number; - getValueForDistanceFromCenter(distance: number): number; - getPointPosition(index: number, distanceFromCenter: number): { x: number; y: number; angle: number }; - getPointPositionForValue(index: number, value: number): { x: number; y: number; angle: number }; - getPointLabelPosition(index: number): ChartArea; - getBasePosition(index: number): { x: number; y: number; angle: number }; -} -export const RadialLinearScale: ChartComponent & { - prototype: RadialLinearScale; - new (cfg: AnyObject): RadialLinearScale; -}; - -export interface CartesianScaleTypeRegistry { - linear: { - options: LinearScaleOptions; - }; - logarithmic: { - options: LogarithmicScaleOptions; - }; - category: { - options: CategoryScaleOptions; - }; - time: { - options: TimeScaleOptions; - }; - timeseries: { - options: TimeScaleOptions; - }; -} - -export interface RadialScaleTypeRegistry { - radialLinear: { - options: RadialLinearScaleOptions; - }; -} - -export interface ScaleTypeRegistry extends CartesianScaleTypeRegistry, RadialScaleTypeRegistry { -} - -export type ScaleType = keyof ScaleTypeRegistry; - -interface CartesianParsedData { - x: number; - y: number; - - // Only specified when stacked bars are enabled - _stacks?: { - // Key is the stack ID which is generally the axis ID - [key: string]: { - // Inner key is the datasetIndex - [key: number]: number; - } - } -} - -interface BarParsedData extends CartesianParsedData { - // Only specified if floating bars are show - _custom?: { - barStart: number; - barEnd: number; - start: number; - end: number; - min: number; - max: number; - } -} - -interface BubbleParsedData extends CartesianParsedData { - // The bubble radius value - _custom: number; -} - -interface RadialParsedData { - r: number; -} - -export interface ChartTypeRegistry { - bar: { - chartOptions: BarControllerChartOptions; - datasetOptions: BarControllerDatasetOptions; - defaultDataPoint: number; - metaExtensions: {}; - parsedDataType: BarParsedData, - scales: keyof CartesianScaleTypeRegistry; - }; - line: { - chartOptions: LineControllerChartOptions; - datasetOptions: LineControllerDatasetOptions & FillerControllerDatasetOptions; - defaultDataPoint: ScatterDataPoint | number | null; - metaExtensions: {}; - parsedDataType: CartesianParsedData; - scales: keyof CartesianScaleTypeRegistry; - }; - scatter: { - chartOptions: ScatterControllerChartOptions; - datasetOptions: ScatterControllerDatasetOptions; - defaultDataPoint: ScatterDataPoint | number | null; - metaExtensions: {}; - parsedDataType: CartesianParsedData; - scales: keyof CartesianScaleTypeRegistry; - }; - bubble: { - chartOptions: unknown; - datasetOptions: BubbleControllerDatasetOptions; - defaultDataPoint: BubbleDataPoint; - metaExtensions: {}; - parsedDataType: BubbleParsedData; - scales: keyof CartesianScaleTypeRegistry; - }; - pie: { - chartOptions: PieControllerChartOptions; - datasetOptions: PieControllerDatasetOptions; - defaultDataPoint: PieDataPoint; - metaExtensions: PieMetaExtensions; - parsedDataType: number; - scales: keyof CartesianScaleTypeRegistry; - }; - doughnut: { - chartOptions: DoughnutControllerChartOptions; - datasetOptions: DoughnutControllerDatasetOptions; - defaultDataPoint: DoughnutDataPoint; - metaExtensions: DoughnutMetaExtensions; - parsedDataType: number; - scales: keyof CartesianScaleTypeRegistry; - }; - polarArea: { - chartOptions: PolarAreaControllerChartOptions; - datasetOptions: PolarAreaControllerDatasetOptions; - defaultDataPoint: number; - metaExtensions: {}; - parsedDataType: RadialParsedData; - scales: keyof RadialScaleTypeRegistry; - }; - radar: { - chartOptions: RadarControllerChartOptions; - datasetOptions: RadarControllerDatasetOptions & FillerControllerDatasetOptions; - defaultDataPoint: number | null; - metaExtensions: {}; - parsedDataType: RadialParsedData; - scales: keyof RadialScaleTypeRegistry; - }; -} - -export type ChartType = keyof ChartTypeRegistry; - -export type ScaleOptionsByType = - { [key in ScaleType]: { type: key } & ScaleTypeRegistry[key]['options'] }[TScale] -; - -// Convenience alias for creating and manipulating scale options in user code -export type ScaleOptions = DeepPartial>; - -export type DatasetChartOptions = { - [key in TType]: { - datasets: ChartTypeRegistry[key]['datasetOptions']; - }; -}; - -export type ScaleChartOptions = { - scales: { - [key: string]: ScaleOptionsByType; - }; -}; - -export type ChartOptions = DeepPartial< -CoreChartOptions & -ElementChartOptions & -PluginChartOptions & -DatasetChartOptions & -ScaleChartOptions & -ChartTypeRegistry[TType]['chartOptions'] ->; - -export type DefaultDataPoint = DistributiveArray; - -export type ParsedDataType = ChartTypeRegistry[TType]['parsedDataType']; - -export interface ChartDatasetProperties { - type?: TType; - data: TData; -} - -export type ChartDataset< - TType extends ChartType = ChartType, - TData = DefaultDataPoint -> = DeepPartial< -{ [key in ChartType]: { type: key } & ChartTypeRegistry[key]['datasetOptions'] }[TType] -> & ChartDatasetProperties; - -/** - * TData represents the data point type. If unspecified, a default is provided - * based on the chart type. - * TLabel represents the label type - */ -export interface ChartData< - TType extends ChartType = ChartType, - TData = DefaultDataPoint, - TLabel = unknown -> { - labels?: TLabel[]; - datasets: ChartDataset[]; -} - -export interface ChartConfiguration< - TType extends ChartType = ChartType, - TData = DefaultDataPoint, - TLabel = unknown -> { - type: TType; - data: ChartData; - options?: ChartOptions; - plugins?: Plugin[]; -} diff --git a/node_modules/chart.js/types/layout.d.ts b/node_modules/chart.js/types/layout.d.ts deleted file mode 100644 index 4c770711..00000000 --- a/node_modules/chart.js/types/layout.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ChartArea } from './geometric'; - -export type LayoutPosition = 'left' | 'top' | 'right' | 'bottom' | 'center' | 'chartArea' | {[scaleId: string]: number}; - -export interface LayoutItem { - /** - * The position of the item in the chart layout. Possible values are - */ - position: LayoutPosition; - /** - * The weight used to sort the item. Higher weights are further away from the chart area - */ - weight: number; - /** - * if true, and the item is horizontal, then push vertical boxes down - */ - fullSize: boolean; - /** - * Width of item. Must be valid after update() - */ - width: number; - /** - * Height of item. Must be valid after update() - */ - height: number; - /** - * Left edge of the item. Set by layout system and cannot be used in update - */ - left: number; - /** - * Top edge of the item. Set by layout system and cannot be used in update - */ - top: number; - /** - * Right edge of the item. Set by layout system and cannot be used in update - */ - right: number; - /** - * Bottom edge of the item. Set by layout system and cannot be used in update - */ - bottom: number; - - /** - * Called before the layout process starts - */ - beforeLayout?(): void; - /** - * Draws the element - */ - draw(chartArea: ChartArea): void; - /** - * Returns an object with padding on the edges - */ - getPadding?(): ChartArea; - /** - * returns true if the layout item is horizontal (ie. top or bottom) - */ - isHorizontal(): boolean; - /** - * Takes two parameters: width and height. - * @param width - * @param height - */ - update(width: number, height: number, margins?: ChartArea): void; -} diff --git a/node_modules/chart.js/types/utils.d.ts b/node_modules/chart.js/types/utils.d.ts deleted file mode 100644 index 8313d9fa..00000000 --- a/node_modules/chart.js/types/utils.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-types */ - -// DeepPartial implementation taken from the utility-types NPM package, which is -// Copyright (c) 2016 Piotr Witek (http://piotrwitek.github.io) -// and used under the terms of the MIT license -export type DeepPartial = T extends Function - ? T - : T extends Array - ? _DeepPartialArray - : T extends object - ? _DeepPartialObject - : T | undefined; - -type _DeepPartialArray = Array> -type _DeepPartialObject = { [P in keyof T]?: DeepPartial }; - -export type DistributiveArray = [T] extends [unknown] ? Array : never - -// https://stackoverflow.com/a/50375286 -export type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; - diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 8627f416..00000000 --- a/package-lock.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "project-github-tracker", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "chart.js": "^3.7.1" - } - }, - "node_modules/chart.js": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", - "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" - } - }, - "dependencies": { - "chart.js": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", - "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 832728d9..00000000 --- a/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "chart.js": "^3.7.1" - } -} From 960218a586547086d5140d4964ebd94b7de3c45a Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:46:54 +0100 Subject: [PATCH 13/31] trying again --- code/.gitignore | 2 +- code/index.html | 2 +- code/secret.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 code/secret.js diff --git a/code/.gitignore b/code/.gitignore index ae92fb00..83678f36 100644 --- a/code/.gitignore +++ b/code/.gitignore @@ -1,5 +1,5 @@ #other -token.js +secret.js # Ignore node_modules folder node_modules diff --git a/code/index.html b/code/index.html index 1598f310..47fa8f92 100644 --- a/code/index.html +++ b/code/index.html @@ -29,7 +29,7 @@

Technigo Projects:

- + \ No newline at end of file diff --git a/code/secret.js b/code/secret.js new file mode 100644 index 00000000..859f9306 --- /dev/null +++ b/code/secret.js @@ -0,0 +1 @@ +const API_KEY = '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' \ No newline at end of file From 9f9544c8c82816c13350155ce8af860d35c190c0 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Wed, 23 Feb 2022 13:57:37 +0100 Subject: [PATCH 14/31] trying authentication again --- code/.gitignore => .gitignore | 4 ++-- code/index.html | 2 +- code/secret.js | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) rename code/.gitignore => .gitignore (78%) delete mode 100644 code/secret.js diff --git a/code/.gitignore b/.gitignore similarity index 78% rename from code/.gitignore rename to .gitignore index 83678f36..606ad8fc 100644 --- a/code/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -#other -secret.js +#API KEY +code/token.js # Ignore node_modules folder node_modules diff --git a/code/index.html b/code/index.html index 47fa8f92..1598f310 100644 --- a/code/index.html +++ b/code/index.html @@ -29,7 +29,7 @@

Technigo Projects:

- + \ No newline at end of file diff --git a/code/secret.js b/code/secret.js deleted file mode 100644 index 859f9306..00000000 --- a/code/secret.js +++ /dev/null @@ -1 +0,0 @@ -const API_KEY = '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' \ No newline at end of file From 6e9ab30860e70b4e9a0032ef1c3759daf07bdb6c Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 14:19:13 +0100 Subject: [PATCH 15/31] added a second chart & refactored javascript --- code/chart.js | 82 +++++++------ code/chart2.js | 239 +++++++++++++++++++++++++++++++++++++ code/index.html | 18 ++- code/package-lock.json | 24 ---- code/package.json | 5 - code/script.js | 259 +++++++++++++++++++++++++---------------- code/style.css | 178 ++++++++++++++++++++++++++-- 7 files changed, 629 insertions(+), 176 deletions(-) create mode 100644 code/chart2.js delete mode 100644 code/package-lock.json delete mode 100644 code/package.json diff --git a/code/chart.js b/code/chart.js index 38c39a63..0592c4de 100644 --- a/code/chart.js +++ b/code/chart.js @@ -1,36 +1,50 @@ -const { Chart } = require("chart.js"); - -//DOM-selector for the canvas 👇 +//DOM-selector const ctx = document.getElementById('myChart') -//"Draw" the chart here 👇 - -const labels = [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', -]; - -const data = { -labels: labels, -datasets: [{ - label: 'My First dataset', - backgroundColor: 'rgb(255, 99, 132)', - borderColor: 'rgb(255, 99, 132)', - data: [0, 10, 5, 2, 20, 30, 45], -}] -}; - - const config = { - type: 'line', - data: data, - options: {} -}; - -const myChart = new Chart( -document.getElementById('myChart'), -config -); \ No newline at end of file +//Drawing the doughnut chart +const completedProjects = (complete) => { + const data = { + labels: [ + `Completed Projects`, + `Incomplete Projects`, + ], + // Pulls complete from script.js + datasets: [{ + data: [complete, (19 - complete)], + backgroundColor: [ + '#2d2e2f', + '#DCD8DC' + ], + hoverOffset: 8, + hoverBorderColor: [ + '#FFFFFF' + ] + }] + }; + + const config = { + type: 'doughnut', + data: data, + options: { + plugins: { + legend: { + // display: false, + position: 'top', + // align: 'start', + labels: { + font: { + family: 'Roboto Mono, monospace', + size: 16, + }, + // padding: 20 + } + }, + } + } + }; + + const myChart = new Chart( + ctx, + config, + ); +} diff --git a/code/chart2.js b/code/chart2.js new file mode 100644 index 00000000..20be96a0 --- /dev/null +++ b/code/chart2.js @@ -0,0 +1,239 @@ +//DOM-selector for sprint chart +const sprint = document.getElementById('sprintChart') + +// Date Consts (In Epoch - Milliseconds) +const feb21 = 1645401600000 +const feb28 = 1646006400000 +const mar7 = 1646611200000 +const mar14 = 1647216000000 +const mar21 = 1647820800000 +const mar28 = 1648425600000 +const apr4 = 1649030400000 +const apr11 = 1649635200000 +const apr18 = 1650240000000 +const apr25 = 1650844800000 +const may2 = 1651449600000 +const may9 = 1652054400000 +const may16 = 1652659200000 +const may23 = 1653264000000 +const may30 = 1653868800000 +const jun6 = 1654473600000 +const jun13 = 1655078400000 +const jun20 = 1655683200000 + +// Let declarations +let sprint1 +let sprint2 +let sprint3 +let sprint4 +let sprint5 +let sprint6 +let colors = [] +let now = Date.now() + +// Function for determining date & setting sprint data and colors on bar chart +const dataColors = () => { + // If statements for sprints + if (now > feb21 && now < feb28) { + sprint1 = 4; + sprint2 = 3; + sprint3 = 0; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > feb28 && now < mar7) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 0; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar7 && now < mar14) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 1; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar14 && now < mar21) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 2; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar21 && now < mar28) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 3; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar28 && now < apr4) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr4 && now < apr11) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 1; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr11 && now < apr18) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 2; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr18 && now < apr25) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 3; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr25 && now < may2) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may2 && now < may9) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 1; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may9 && now < may16) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 2; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may16 && now < may23) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 3; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may23 && now < may30) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } if (now > may30 && now < jun6) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 1; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } if (now > jun6 && now < jun13) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 2; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } if (now > jun13 && now < jun20) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 3; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } else if (now > jun20) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 4; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f'] + } +} + +//Chart function +const sprintProgress = () => { + + // Run dataColors for determining sprint progress + dataColors(); + + const data = { + labels: [ + 'Sprint 1', + 'Sprint 2', + 'Sprint 3', + 'Sprint 4', + 'Sprint 5', + 'Sprint 6' + ], + datasets: [{ + data: [sprint1, sprint2, sprint3, sprint4, sprint5, sprint6], + backgroundColor: colors, + hoverOffset: 8, + hoverBorderColor: [ + '#FFFFFF' + ], + // xAxisID = 'xAxis' + }] + }; + + const config = { + type: 'bar', + data: data, + options: { + indexAxis: 'y', + // xAxisID: 'Weeks', + plugins: { + legend: { + display: false, + position: 'top', + }, + scales: { + xAxis: { + ticks: { + precision: 0, + // stepSize: 1, + } + } + } + } + } + }; + + const sprintChart = new Chart( + sprint, + config, + ); +} + +sprintProgress(); \ No newline at end of file diff --git a/code/index.html b/code/index.html index 1598f310..f336dbd9 100644 --- a/code/index.html +++ b/code/index.html @@ -3,7 +3,7 @@ - + Technigo Projects GitHub Tracker | michaelchangdk @@ -19,17 +19,25 @@
-

Technigo Projects:

+

Technigo Projects:

-
- +
+

Bootcamp Progress:

+ + +
+ +
+

Sprint Progress:

+
- + + \ No newline at end of file diff --git a/code/package-lock.json b/code/package-lock.json deleted file mode 100644 index 0121b50f..00000000 --- a/code/package-lock.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "code", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "chart.js": "^3.7.1" - } - }, - "node_modules/chart.js": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", - "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" - } - }, - "dependencies": { - "chart.js": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", - "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" - } - } -} diff --git a/code/package.json b/code/package.json deleted file mode 100644 index 832728d9..00000000 --- a/code/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "chart.js": "^3.7.1" - } -} diff --git a/code/script.js b/code/script.js index 464eef34..b025183e 100644 --- a/code/script.js +++ b/code/script.js @@ -1,4 +1,4 @@ -//Global Dom Selectors +//Global DOM Selectors const header = document.querySelector('header') const projects = document.getElementById('projects') @@ -6,8 +6,7 @@ const projects = document.getElementById('projects') const username = 'michaelchangdk' const GITHUB_API = `https://api.github.com/users/${username}/repos` -// GITHUB Authentication - TOKEN REVOKED AFTER COMMIT? -// const API_KEY = API_KEY || process.env.API_KEY; +// GITHUB Authentication const options = { method: 'GET', headers: { @@ -25,13 +24,29 @@ compareCreateDate = (a, b) => { return 0; }; +// Function for opening and closing the projects items +const openSesame = (projectID) => { + let projectHeader = document.getElementById(projectID); + let projectBody = projectHeader.nextElementSibling; + if (getComputedStyle(projectBody).display === "none") { + projectBody.style.display = "block"; + projectHeader.classList.add("project-header--active") + } else { + projectBody.style.display = "none"; + projectHeader.classList.remove("project-header--active") + } +} + const getProjects = async () => { const projectsWait = await fetch(GITHUB_API, options); const data = await projectsWait.json(); // Filter Technigo Projects Only & Sort by Date Created const technigoProjects = data.filter(item => item.archive_url.includes('project') === true).sort(compareCreateDate); - console.log(technigoProjects); + + // Length of completed projects and sending the number to chart.js + const completedNumber = data.filter(item => item.archive_url.includes('project') === true).length + completedProjects(completedNumber) // Create Header Section header.innerHTML = ` @@ -39,107 +54,149 @@ const getProjects = async () => {

${technigoProjects[0].owner.login}

` - // Create Main Project Elements & Fetch Number of Commits + // For Loop to Create Main Project Elements || & Fetch Number of Commits as well as Pull Requests for (let i = 0; i < technigoProjects.length; i++) { - fetch(technigoProjects[i].commits_url.replace("{/sha}", ""), options) - .then(res => res.json()) - .then(data => { - // console.log('commit data', data) - - // Number of Commits - let numberOfCommits = data.length; - - // Last Commit Message - Committed Netlify Link to add to live link. - let lastCommitLink = data[0].commit.message - - // Last Commit Date which is NOT the Netlify commit (Index 1 instead of 0) - // Formatting dates to look nice for the tracker - let lastCommitDateRaw = data[1].commit.committer.date.substring(0,10) - // Removing the 0 from dates before the 10th for DAYS - let lastCommitDayRaw = lastCommitDateRaw.substring(8,10) - let lastCommitDay - if (lastCommitDayRaw < 10) { - lastCommitDay = lastCommitDayRaw.replace("0", "") - } else { - lastCommitDay = lastCommitDayRaw; - } - // Formatting MONTHS - const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - let lastCommitMonthNumber = lastCommitDateRaw.substring(5,7).replace("0","") - let lastCommitMonth = months[lastCommitMonthNumber - 1] - - // Extracting YEAR - let lastCommitYear = lastCommitDateRaw.substring(0,4) - - // Creating Project ID - let projectName = technigoProjects[i].name.replaceAll("-",""); - - // Creating the Project Elements - projects.innerHTML += ` -
- -
-

Number of commits: ${numberOfCommits}

-

Last commit date: ${lastCommitMonth} ${lastCommitDay}, ${lastCommitYear}

-

Repo can be found here. -

View it live here. -

-
- ` - - // Fetching the pull requests - fetch('https://api.github.com/repos/technigo/' + technigoProjects[i].name + '/pulls?per_page=100', options) - .then(res => res.json()) - .then(data => { - // console.log(data); - const filteredPR = data.filter(function (el) { - return el.user.login === "michaelchangdk" - }) - document.getElementById(`${projectName}2`).innerHTML += ` -

Pull request with comments here. - ` - // console.log(filteredPR[0].review_comments_url) - - // Fetch Comments - // fetch(filteredPR[0].review_comments_url, options) - // .then(res => res.json()) - // .then(data => { - // // console.log(data); - // document.getElementById(`${projectName}2`).innerHTML += ` - //
- //

Review comments:

- // ` - // for (let i = 0; i < data.length; i++) { - // if (data[i].user.login !== username) { - // // console.log(data[i].body); - // document.getElementById(`${projectName}2`).innerHTML += ` - //
    - //
  • ${data[i].body}
  • - //
- // ` - // } - // } - // }) - - }) - }) + + // Creating Project classname & project ID + let projectName = technigoProjects[i].name.replaceAll("-",""); + let projectID = technigoProjects[i].id + + // Create DIV in the correct sorted order + projects.innerHTML += ` +
+
+ ` + + // Creating the Project Elements + document.getElementById(projectID).innerHTML += ` + +
+

Repo can be found here.

+
+ ` + + // Fetching Commit Data + commitFetch(technigoProjects[i], projectID); + + // Fetching Pull Requests + pullFetch(technigoProjects[i].name, projectID) + } } + +// Function for Fetching Commits and Live Link +const commitFetch = (projects, projectsID) => { + const GIT_COMMIT_API = projects.commits_url.replace("{/sha}", "") + fetch(GIT_COMMIT_API, options) + .then(res => res.json()) + .then(data => { + + // Defining number of commits + let numberOfCommits = data.length; + + // Last Commit Message - Committed Netlify Link to add to live link. + let lastCommitLink = data[0].commit.message + + // Formatting the DATES + // Last Commit Date which is NOT the Netlify commit (Index 1 instead of 0) + // Formatting DAYS + let lastCommitDateRaw = data[1].commit.committer.date.substring(0,10) + // Removing the 0 from dates before the 10th for DAYS + let lastCommitDayRaw = lastCommitDateRaw.substring(8,10) + let lastCommitDay + if (lastCommitDayRaw < 10) { + lastCommitDay = lastCommitDayRaw.replace("0", "") + } else { + lastCommitDay = lastCommitDayRaw; + } + // Formatting MONTHS + const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + let lastCommitMonthNumber = lastCommitDateRaw.substring(5,7).replace("0","") + let lastCommitMonth = months[lastCommitMonthNumber - 1] + // Extracting YEAR + let lastCommitYear = lastCommitDateRaw.substring(0,4) + + let commitDate = `${lastCommitMonth} ${lastCommitDay}, ${lastCommitYear}` + + // Creating the Project Elements + document.getElementById(`${projectsID}2`).innerHTML += ` +

Number of commits: ${numberOfCommits}

+

Last commit date: ${commitDate}

+

Repo can be found here.

+ ` + + // If statement to check if last commit includes netlify link + if (lastCommitLink.includes('netlify') === true) { + document.getElementById(`${projectsID}2`).innerHTML += ` +

View it live here. + ` + } else { + document.getElementById(`${projectsID}2`).innerHTML += ` +

No live link available.

+ ` + } + }) } -// Function for opening and closing the projects list -const openSesame = (projectID) => { - // console.log('you got clicked! bitch') - let projectHeader = document.getElementById(`${projectID}`); - let projectBody = projectHeader.nextElementSibling; - if (projectBody.style.display === "none") { - projectBody.style.display = "block"; - projectHeader.classList.add("project-header--active") - } else { - projectBody.style.display = "none"; - projectHeader.classList.remove("project-header--active") - } +// Fetching the pull requests +const pullFetch = (projectName, projectsID) => { + const GIT_FETCH_API = `https://api.github.com/repos/technigo/${projectName}/pulls?per_page=100` + fetch(GIT_FETCH_API, options) + .then(res => res.json()) + .then(data => { + + // Filter Pull Requests by my username + const filteredPR = data.filter(function (pr) { + return pr.user.login === username + }) + + // Check if there is a pull request with my username + let exist = false + for (let i = 0; i < data.length; i++) { + if (data[i].user.login === username) { + exist = true + } + } + + // Conditional for pull request innerHTML + if (exist === true) { + document.getElementById(`${projectsID}2`).innerHTML += ` +

Pull request with comments here.

+ ` + } if (exist === false) { + document.getElementById(`${projectsID}2`).innerHTML += ` +

Pull request unavailable.

+ ` + } + + // Fetch Comments - Commented out because I didn't want them, but left it here to show how to do it + // pullComments(filteredPR, projectsID); + + }) } +// Fetch Comments - Commented out because I didn't want them, but left it here to show how to do it +// const pullComments = (pullRequests, projectsID) => { +// for (let i = 0; i < pullRequests.length; i++) { +// fetch(pullRequests[0].review_comments_url, options) +// .then(res => res.json()) +// .then(data => { +// document.getElementById(`${projectsID}2`).innerHTML += ` +//
+//

Review comments:

+// ` +// for (let i = 0; i < data.length; i++) { +// if (data[i].user.login !== username) { +// document.getElementById(`${projectsID}2`).innerHTML += ` +//
    +//
  • ${data[i].body}
  • +//
+// ` +// } +// } +// }) +// } +// } + getProjects(); diff --git a/code/style.css b/code/style.css index 39daf8e0..619e5504 100644 --- a/code/style.css +++ b/code/style.css @@ -13,7 +13,7 @@ body { background: white; font-family: 'Roboto Mono', monospace; - padding: 9vmin; + padding: 7vmin; } a { @@ -21,6 +21,10 @@ a { text-decoration: underline; } +p { + line-height: 1.5; +} + /* Header */ header { @@ -29,6 +33,9 @@ header { gap: 2vmin; justify-content: center; margin-bottom: 5vmin; + max-width: 500px; + margin-left: auto; + margin-right: auto; } .avatar { @@ -37,29 +44,31 @@ header { } /* Projects Section */ - -/* Projects Accordion Section */ .projects { - /* border-radius: 5px; */ overflow: hidden; - max-width: 500px; margin-left: auto; margin-right: auto; } -.projects-header { +.title { background-color: #2D2E2F; padding: 15px 0 20px; color: white; text-align: center; border-radius: 5px; overflow: hidden; + max-width: 500px; + margin-left: auto; + margin-right: auto; } .project-wrapper { margin: 5vmin 0; border-radius: 5px; overflow: hidden; + max-width: 500px; + margin-left: auto; + margin-right: auto; } .project-header { @@ -73,7 +82,6 @@ header { width: 100%; border: none; outline: none; - /* border-radius: 5px; */ } .project-info { @@ -100,4 +108,160 @@ header { color: #DCD8DC; cursor: pointer; border-radius: 0; +} + +/* Chart Styling */ +.chart-container { + padding-bottom: 5vmin; + margin-left: auto; + margin-right: auto; + max-width: 500px; +} + +.chart { + padding-top: 5vmin; +} + + +/* Media Queries */ + +/* Media Query - Tablet */ +@media (min-width: 668px) and (max-width: 930px) { + body { + display: grid; + grid-template-columns: 2; + grid-template-rows: 3; + gap: 3vw; + padding: 5vw; + } + + header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + margin-bottom: 0; + } + + .chart-container { + + /* padding: 0; */ + margin-right: 0; + max-width: 44vw; + } + + .bootcamp-chart { + grid-column: 1 / span 1; + grid-row: 2 / span 1; + } + + .sprint-chart { + grid-column: 1 / span 1; + grid-row: 3 / span 1; + } + + .chart { + padding-top: 2vmin; + } + + .project-wrapper { + margin: 2vmin 0; + } + + .projects { + grid-column: 2 / span 1; + grid-row: 2 / span 1; + margin-left: 0; + max-width: 44vw; + } +} + +/* Media Query - Desktop */ +@media (min-width: 930px) { + +body { + display: grid; + grid-template-columns: 2; + grid-template-rows: 2; + gap: 2vmin; +} + +header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + margin-bottom: 0; +} + +.chart-container { + padding: 0; + margin-right: 0; + width: 400px; +} + +.bootcamp-chart { + grid-column: 1 / span 1; + grid-row: 2 / span 1; +} + +.sprint-chart { + grid-column: 1 / span 1; + grid-row: 3 / span 1; +} + +.chart { + padding-top: 2vmin; +} + +.project-wrapper { + margin: 2vmin 0; +} + +.projects { + grid-column: 2 / span 1; + grid-row: 2 / span 1; + margin-left: 0; + width: 400px; +} + +} + +/* Media Query - Mobile Landscape */ +@media (min-width: 640px) and (max-height: 420px) and (orientation: landscape) { + body { + display: grid; + grid-template-columns: 2; + grid-template-rows: 2; + gap: 5vw; + padding: 3vw; + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); + } + + header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + margin-bottom: -3vw; + } + + .chart-container { + grid-column: 1 / span 1; + grid-row: 2 / span 1; + padding: 0; + margin-right: 0; + max-width: 44vw; + } + + .chart { + padding-top: 2vh; + } + + .project-wrapper { + margin: 2vh 0; + } + + .projects { + grid-column: 2 / span 1; + grid-row: 2 / span 1; + margin-left: 0; + max-width: 44vw; + } + } \ No newline at end of file From 7836bb108396597da84ad20f1a634235f6e51147 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 15:09:50 +0100 Subject: [PATCH 16/31] added favicon --- code/android-chrome-192x192.png | Bin 0 -> 38936 bytes code/android-chrome-512x512.png | Bin 0 -> 209515 bytes code/apple-touch-icon.png | Bin 0 -> 34874 bytes code/favicon-16x16.png | Bin 0 -> 852 bytes code/favicon-32x32.png | Bin 0 -> 2286 bytes code/favicon.ico | Bin 0 -> 15406 bytes code/index.html | 10 +++++++-- code/site.webmanifest | 1 + code/style.css | 35 ++++++++++++++++---------------- 9 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 code/android-chrome-192x192.png create mode 100644 code/android-chrome-512x512.png create mode 100644 code/apple-touch-icon.png create mode 100644 code/favicon-16x16.png create mode 100644 code/favicon-32x32.png create mode 100644 code/favicon.ico create mode 100644 code/site.webmanifest diff --git a/code/android-chrome-192x192.png b/code/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..5ec76f5e51ee61d10d544f220a3cb02878a5243b GIT binary patch literal 38936 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

nVgbmng$AUnk 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

Ka+Gqw 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

uw9Gq3Uz%3QpzSJh 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 literal 0 HcmV?d00001 diff --git a/code/favicon-16x16.png b/code/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..d1517b341de0a70e310a99467d508b460f5e2f3f GIT binary patch literal 852 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 IM6N<$f;+QVk^lez literal 0 HcmV?d00001 diff --git a/code/favicon.ico b/code/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9a6ce77cd6b2fb973141f43d1eb208a00a8fe928 GIT binary patch literal 15406 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 Technigo Projects GitHub Tracker | michaelchangdk - + + + + + @@ -22,7 +26,8 @@

Technigo Projects:

- + ' +

Bootcamp Progress:

@@ -34,6 +39,7 @@

Sprint Progress:

+
diff --git a/code/site.webmanifest b/code/site.webmanifest new file mode 100644 index 00000000..45dc8a20 --- /dev/null +++ b/code/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/code/style.css b/code/style.css index 619e5504..d2e9f381 100644 --- a/code/style.css +++ b/code/style.css @@ -10,6 +10,7 @@ text-decoration: none; } +/* Overall Styling */ body { background: white; font-family: 'Roboto Mono', monospace; @@ -25,8 +26,7 @@ p { line-height: 1.5; } -/* Header */ - +/* Header Section with Avatar & Username */ header { display: flex; align-items: center; @@ -41,15 +41,10 @@ header { .avatar { border-radius: 50%; height: 40px; + margin-top: 1vmin; } -/* Projects Section */ -.projects { - overflow: hidden; - margin-left: auto; - margin-right: auto; -} - +/* Elements Across Sections */ .title { background-color: #2D2E2F; padding: 15px 0 20px; @@ -62,6 +57,13 @@ header { margin-right: auto; } +/* Projects Section */ +.projects { + overflow: hidden; + margin-left: auto; + margin-right: auto; +} + .project-wrapper { margin: 5vmin 0; border-radius: 5px; @@ -126,7 +128,7 @@ header { /* Media Queries */ /* Media Query - Tablet */ -@media (min-width: 668px) and (max-width: 930px) { +/* @media (min-width: 668px) and (max-width: 930px) { body { display: grid; grid-template-columns: 2; @@ -142,8 +144,6 @@ header { } .chart-container { - - /* padding: 0; */ margin-right: 0; max-width: 44vw; } @@ -172,10 +172,10 @@ header { margin-left: 0; max-width: 44vw; } -} +} */ /* Media Query - Desktop */ -@media (min-width: 930px) { +/* @media (min-width: 930px) { body { display: grid; @@ -191,6 +191,7 @@ header { } .chart-container { + padding: 0; margin-right: 0; width: 400px; @@ -221,10 +222,10 @@ header { width: 400px; } -} +} */ /* Media Query - Mobile Landscape */ -@media (min-width: 640px) and (max-height: 420px) and (orientation: landscape) { +/* @media (min-width: 640px) and (max-height: 420px) and (orientation: landscape) { body { display: grid; grid-template-columns: 2; @@ -264,4 +265,4 @@ header { max-width: 44vw; } -} \ No newline at end of file +} */ \ No newline at end of file From 87ed03054ed4df1e5fd0cf7bc05552b5cb8b0718 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 16:06:33 +0100 Subject: [PATCH 17/31] refactored CSS --- code/chart2.js | 5 +- code/index.html | 39 +++++----- code/style.css | 188 ++++++++++++++++++++++-------------------------- 3 files changed, 110 insertions(+), 122 deletions(-) diff --git a/code/chart2.js b/code/chart2.js index 20be96a0..8cc93a8e 100644 --- a/code/chart2.js +++ b/code/chart2.js @@ -221,8 +221,11 @@ const sprintProgress = () => { scales: { xAxis: { ticks: { - precision: 0, + // precision: 0, // stepSize: 1, + min: 0, + stepSize: 1, + max: 4, } } } diff --git a/code/index.html b/code/index.html index 3084eea7..e57af188 100644 --- a/code/index.html +++ b/code/index.html @@ -16,31 +16,36 @@ + -
-
-
+ + +
+ + +

Technigo Projects:

-
+ - ' -
-
-

Bootcamp Progress:

- - -
+ +
+
+

Bootcamp Progress:

+ + +
-
-

Sprint Progress:

- - -
-
+
+

Sprint Progress:

+ + +
+ + diff --git a/code/style.css b/code/style.css index d2e9f381..33a944e1 100644 --- a/code/style.css +++ b/code/style.css @@ -26,6 +26,22 @@ p { line-height: 1.5; } +/* Elements Across Sections */ +.title { + background-color: #2D2E2F; + padding: 15px 0 20px; + color: white; + text-align: center; + border-radius: 5px; + overflow: hidden; +} + +.layout { + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + /* Header Section with Avatar & Username */ header { display: flex; @@ -33,9 +49,6 @@ header { gap: 2vmin; justify-content: center; margin-bottom: 5vmin; - max-width: 500px; - margin-left: auto; - margin-right: auto; } .avatar { @@ -44,33 +57,11 @@ header { margin-top: 1vmin; } -/* Elements Across Sections */ -.title { - background-color: #2D2E2F; - padding: 15px 0 20px; - color: white; - text-align: center; - border-radius: 5px; - overflow: hidden; - max-width: 500px; - margin-left: auto; - margin-right: auto; -} - /* Projects Section */ -.projects { - overflow: hidden; - margin-left: auto; - margin-right: auto; -} - .project-wrapper { margin: 5vmin 0; border-radius: 5px; overflow: hidden; - max-width: 500px; - margin-left: auto; - margin-right: auto; } .project-header { @@ -87,7 +78,6 @@ header { } .project-info { - display: none; color: #2d2e2f; background-color: #bfb8b9; font-family: 'Montserrat', sans-serif; @@ -95,14 +85,15 @@ header { font-weight: 500; line-height: 1.5; padding: 10px 25px; + display: none; } /* Adding background color to button when clicked on and when moused over */ .project-header:hover { - background-color: #726a6a; - color: #DCD8DC; - cursor: pointer; - } + background-color: #726a6a; + color: #DCD8DC; + cursor: pointer; +} /* Add .active in JS */ .project-header--active { @@ -113,156 +104,145 @@ header { } /* Chart Styling */ +.charts-wrapper { +} + .chart-container { - padding-bottom: 5vmin; - margin-left: auto; - margin-right: auto; - max-width: 500px; + margin-bottom: 5vmin; } .chart { - padding-top: 5vmin; + margin-top: 5vmin; } /* Media Queries */ /* Media Query - Tablet */ -/* @media (min-width: 668px) and (max-width: 930px) { +@media (min-width: 668px) and (max-width: 930px) { body { display: grid; grid-template-columns: 2; grid-template-rows: 3; - gap: 3vw; + gap: 2vmin; padding: 5vw; } header { grid-column: 1 / span 2; grid-row: 1 / span 1; - margin-bottom: 0; - } - - .chart-container { - margin-right: 0; - max-width: 44vw; + margin-bottom: 1vw; + margin-top: -1vw; } - .bootcamp-chart { - grid-column: 1 / span 1; - grid-row: 2 / span 1; + .layout { + max-width: 44vw; } - .sprint-chart { - grid-column: 1 / span 1; - grid-row: 3 / span 1; - } - - .chart { - padding-top: 2vmin; - } - .project-wrapper { margin: 2vmin 0; } .projects { grid-column: 2 / span 1; - grid-row: 2 / span 1; + grid-row: 2 / span 2; margin-left: 0; - max-width: 44vw; } -} */ -/* Media Query - Desktop */ -/* @media (min-width: 930px) { + .charts-wrapper { + grid-column: 1 / span 1; + grid-row: 2 / span 2; + margin-right: 0; + } + + .chart { + margin-top: 2vmin; + } +} +/* Media Query - Desktop */ +@media (min-width: 930px) { body { display: grid; grid-template-columns: 2; grid-template-rows: 2; gap: 2vmin; + padding: 3vw; +} + +.layout { + width: 44vw; + max-width: 500px; } header { grid-column: 1 / span 2; grid-row: 1 / span 1; - margin-bottom: 0; + margin-bottom: 1vw; + margin-top: -1vw; } -.chart-container { - - padding: 0; - margin-right: 0; - width: 400px; -} - -.bootcamp-chart { - grid-column: 1 / span 1; - grid-row: 2 / span 1; +.projects { + margin-left: 0; } -.sprint-chart { - grid-column: 1 / span 1; - grid-row: 3 / span 1; +.project-wrapper { + margin: 2vmin 0; } .chart { - padding-top: 2vmin; + margin-top: 2vh; } -.project-wrapper { - margin: 2vmin 0; +.charts-wrapper { + grid-column: 1 / span 1; + grid-row: 2 / span 2; + margin-right: 0; + max-width: 400px; } - -.projects { - grid-column: 2 / span 1; - grid-row: 2 / span 1; - margin-left: 0; - width: 400px; } -} */ - /* Media Query - Mobile Landscape */ -/* @media (min-width: 640px) and (max-height: 420px) and (orientation: landscape) { +@media (min-width: 640px) and (max-height: 420px) and (orientation: landscape) { body { display: grid; grid-template-columns: 2; grid-template-rows: 2; - gap: 5vw; + gap: 3vw; padding: 3vw; padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); } + .layout { + width: 44vw; + max-width: 500px; + } + header { grid-column: 1 / span 2; grid-row: 1 / span 1; - margin-bottom: -3vw; + margin-bottom: 2vmin; } - .chart-container { - grid-column: 1 / span 1; - grid-row: 2 / span 1; - padding: 0; - margin-right: 0; - max-width: 44vw; + .projects { + margin-left: 0; + max-width: 450px; } - .chart { - padding-top: 2vh; + .project-wrapper { + margin: 2vmin 0; } - .project-wrapper { - margin: 2vh 0; + .chart { + margin-top: 2vh; } - .projects { - grid-column: 2 / span 1; - grid-row: 2 / span 1; - margin-left: 0; - max-width: 44vw; + .charts-wrapper { + grid-column: 1 / span 1; + grid-row: 2 / span 2; + margin-right: 0; + max-width: 400px; } -} */ \ No newline at end of file +} \ No newline at end of file From b883873ddb58d21a3fe482297cf5c91281c886ac Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 16:39:40 +0100 Subject: [PATCH 18/31] managed to fix axis and font family on charts --- code/chart.js | 7 +++---- code/chart2.js | 20 +++++++------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/code/chart.js b/code/chart.js index 0592c4de..f6b7ba3e 100644 --- a/code/chart.js +++ b/code/chart.js @@ -1,3 +1,6 @@ +// Global Font Family for Chart +Chart.defaults.font.family = 'Roboto Mono, monospace'; + //DOM-selector const ctx = document.getElementById('myChart') @@ -28,15 +31,11 @@ const completedProjects = (complete) => { options: { plugins: { legend: { - // display: false, position: 'top', - // align: 'start', labels: { font: { - family: 'Roboto Mono, monospace', size: 16, }, - // padding: 20 } }, } diff --git a/code/chart2.js b/code/chart2.js index 8cc93a8e..f12acf81 100644 --- a/code/chart2.js +++ b/code/chart2.js @@ -211,24 +211,18 @@ const sprintProgress = () => { type: 'bar', data: data, options: { - indexAxis: 'y', - // xAxisID: 'Weeks', - plugins: { - legend: { - display: false, - position: 'top', - }, scales: { - xAxis: { + x: { ticks: { - // precision: 0, - // stepSize: 1, - min: 0, stepSize: 1, - max: 4, } } - } + }, + indexAxis: 'y', + plugins: { + legend: { + display: false, + }, } } }; From 0deb73b3a9b96f36f043e25c4676d55084c66976 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 17:25:56 +0100 Subject: [PATCH 19/31] removed a tiny error --- code/script.js | 1 - 1 file changed, 1 deletion(-) diff --git a/code/script.js b/code/script.js index b025183e..06eef24d 100644 --- a/code/script.js +++ b/code/script.js @@ -123,7 +123,6 @@ const commitFetch = (projects, projectsID) => { document.getElementById(`${projectsID}2`).innerHTML += `

Number of commits: ${numberOfCommits}

Last commit date: ${commitDate}

-
` // If statement to check if last commit includes netlify link From fb03b8ee01c30b40d62e7ce83ab9f52251c9ec8d Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 17:56:49 +0100 Subject: [PATCH 20/31] trying to fix a small landscape error on safari ios --- code/style.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/style.css b/code/style.css index 33a944e1..b5de8c71 100644 --- a/code/style.css +++ b/code/style.css @@ -217,6 +217,8 @@ header { .layout { width: 44vw; max-width: 500px; + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); } header { From 161b99ee823510663b8e26dae1d5e2413e1d30f4 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 17:58:52 +0100 Subject: [PATCH 21/31] trying one more thing for safari ios --- code/style.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/style.css b/code/style.css index b5de8c71..d3e1462b 100644 --- a/code/style.css +++ b/code/style.css @@ -210,8 +210,6 @@ header { grid-template-rows: 2; gap: 3vw; padding: 3vw; - padding-left: env(safe-area-inset-left); - padding-right: env(safe-area-inset-right); } .layout { From c230afcc1d8a25ccca8405da4db82137adc5d764 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 18:01:49 +0100 Subject: [PATCH 22/31] trying one more fix for ios landscape --- code/style.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/style.css b/code/style.css index d3e1462b..ef72970d 100644 --- a/code/style.css +++ b/code/style.css @@ -215,8 +215,8 @@ header { .layout { width: 44vw; max-width: 500px; - padding-left: env(safe-area-inset-left); - padding-right: env(safe-area-inset-right); + /* padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); */ } header { @@ -228,6 +228,7 @@ header { .projects { margin-left: 0; max-width: 450px; + padding-right: env(safe-area-inset-right); } .project-wrapper { @@ -243,6 +244,7 @@ header { grid-row: 2 / span 2; margin-right: 0; max-width: 400px; + padding-left: env(safe-area-inset-left); } } \ No newline at end of file From 3933a721b964d83ecc3382be3ea65a7cace502fe Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 21:03:27 +0100 Subject: [PATCH 23/31] final code cleanup --- code/chart2.js | 5 ++-- code/style.css | 70 ++++++++++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/code/chart2.js b/code/chart2.js index f12acf81..0546b850 100644 --- a/code/chart2.js +++ b/code/chart2.js @@ -184,7 +184,7 @@ const dataColors = () => { //Chart function const sprintProgress = () => { - // Run dataColors for determining sprint progress + // Run dataColors for determining sprint progress & bar colors dataColors(); const data = { @@ -203,7 +203,6 @@ const sprintProgress = () => { hoverBorderColor: [ '#FFFFFF' ], - // xAxisID = 'xAxis' }] }; @@ -233,4 +232,4 @@ const sprintProgress = () => { ); } -sprintProgress(); \ No newline at end of file +sprintProgress(); diff --git a/code/style.css b/code/style.css index ef72970d..ac80f9be 100644 --- a/code/style.css +++ b/code/style.css @@ -104,9 +104,6 @@ header { } /* Chart Styling */ -.charts-wrapper { -} - .chart-container { margin-bottom: 5vmin; } @@ -162,44 +159,44 @@ header { /* Media Query - Desktop */ @media (min-width: 930px) { -body { - display: grid; - grid-template-columns: 2; - grid-template-rows: 2; - gap: 2vmin; - padding: 3vw; -} + body { + display: grid; + grid-template-columns: 2; + grid-template-rows: 2; + gap: 2vmin; + padding: 3vw; + } -.layout { - width: 44vw; - max-width: 500px; -} + .layout { + width: 44vw; + max-width: 500px; + } -header { - grid-column: 1 / span 2; - grid-row: 1 / span 1; - margin-bottom: 1vw; - margin-top: -1vw; -} + header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + margin-bottom: 1vw; + margin-top: -1vw; + } -.projects { - margin-left: 0; -} + .projects { + margin-left: 0; + } -.project-wrapper { - margin: 2vmin 0; -} + .project-wrapper { + margin: 2vmin 0; + } -.chart { - margin-top: 2vh; -} + .chart { + margin-top: 2vh; + } -.charts-wrapper { - grid-column: 1 / span 1; - grid-row: 2 / span 2; - margin-right: 0; - max-width: 400px; -} + .charts-wrapper { + grid-column: 1 / span 1; + grid-row: 2 / span 2; + margin-right: 0; + max-width: 400px; + } } /* Media Query - Mobile Landscape */ @@ -246,5 +243,4 @@ header { max-width: 400px; padding-left: env(safe-area-inset-left); } - -} \ No newline at end of file +} From f1c9cb10786a7782b21958003841fdbba1fe36bb Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Fri, 25 Feb 2022 21:04:25 +0100 Subject: [PATCH 24/31] https://github-tracker-week7.netlify.app/ --- code/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/index.html b/code/index.html index e57af188..b97cc66e 100644 --- a/code/index.html +++ b/code/index.html @@ -51,4 +51,4 @@

Sprint Progress:

- \ No newline at end of file + From b0dc2e50b6bea5ba53ef18887a58c9b029c6ac37 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Sat, 26 Feb 2022 15:31:52 +0100 Subject: [PATCH 25/31] https://github-tracker-week7.netlify.app/ --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1613a3b0..b34156c6 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # GitHub Tracker -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +The brief was to create a tracker over all of our forked technigo projects from Github, as well as a chart showing our current progress. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +I first tried the API and played around to try to find the different data I would need, and where I would find the links to the rest of the fetches I needed. After feeling comfortable with where I could find the different information I needed, I began setting up my javascript. I didn't spend much time on design this week, and I struggled a bit with the chart.js documentation, but in the end I was very satisfied with the solutions I came up with. I also spent some time debugging a few small issues I had and refactoring my code to make it more readable. ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +https://app.netlify.com/sites/github-tracker-week7/overview \ No newline at end of file From 5447a01272dba569ba207c10684b7ad60d007a0a Mon Sep 17 00:00:00 2001 From: Michael Chang <93939154+michaelchangdk@users.noreply.github.com> Date: Sat, 5 Mar 2022 19:30:22 +0100 Subject: [PATCH 26/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b34156c6..fd8dfe0c 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ I first tried the API and played around to try to find the different data I woul ## View it live -https://app.netlify.com/sites/github-tracker-week7/overview \ No newline at end of file +https://github-tracker-week7.netlify.app From 22ae41c1b005d83b9fc6f9738a9a5a4278151124 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Sat, 5 Mar 2022 19:33:38 +0100 Subject: [PATCH 27/31] https://github-tracker-week7.netlify.app/ --- code/script.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index 06eef24d..f3336d6d 100644 --- a/code/script.js +++ b/code/script.js @@ -112,7 +112,12 @@ const commitFetch = (projects, projectsID) => { } // Formatting MONTHS const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - let lastCommitMonthNumber = lastCommitDateRaw.substring(5,7).replace("0","") + let lastCommitMonthNumber + if (lastCommitDateRaw.substring(5,7) !== 10) { + lastCommitMonthNumber = lastCommitDateRaw.substring(5,7).replace("0","") + } else { + lastCommitMonthNumber = lastCommitDateRaw.substring(5,7) + } let lastCommitMonth = months[lastCommitMonthNumber - 1] // Extracting YEAR let lastCommitYear = lastCommitDateRaw.substring(0,4) From aff40e8e42bcbfe1226c563d8d494e4c94404219 Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Mon, 7 Mar 2022 22:13:30 +0100 Subject: [PATCH 28/31] https://github-tracker-week7.netlify.app/ --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index f3336d6d..8b1da8b7 100644 --- a/code/script.js +++ b/code/script.js @@ -131,7 +131,7 @@ const commitFetch = (projects, projectsID) => { ` // If statement to check if last commit includes netlify link - if (lastCommitLink.includes('netlify') === true) { + if (lastCommitLink.includes('https://') === true) { document.getElementById(`${projectsID}2`).innerHTML += `

View it live here. ` From 787f4e36127e0abb941f41a88c7a2776faefae8d Mon Sep 17 00:00:00 2001 From: Michael Dohmann Chang Date: Mon, 7 Mar 2022 22:55:21 +0100 Subject: [PATCH 29/31] https://github-tracker-week7.netlify.app/ --- code/script.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index 8b1da8b7..9011281d 100644 --- a/code/script.js +++ b/code/script.js @@ -92,8 +92,17 @@ const commitFetch = (projects, projectsID) => { .then(res => res.json()) .then(data => { + const filteredCommits = [] + + for (let i = 0; i < data.length; i++) { + let author = data[i].commit.author.name + if (author === "Michael Dohmann Chang") { + filteredCommits.push('1') + } + }; + // Defining number of commits - let numberOfCommits = data.length; + let numberOfCommits = filteredCommits.length; // Last Commit Message - Committed Netlify Link to add to live link. let lastCommitLink = data[0].commit.message From 1771d717d97428669b65e76542bbbf357087ac8d Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 7 Mar 2022 23:20:14 +0100 Subject: [PATCH 30/31] https://github-tracker-week7.netlify.app/ --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index 9011281d..b2103501 100644 --- a/code/script.js +++ b/code/script.js @@ -96,7 +96,7 @@ const commitFetch = (projects, projectsID) => { for (let i = 0; i < data.length; i++) { let author = data[i].commit.author.name - if (author === "Michael Dohmann Chang") { + if (author.includes("Chang") === true) { filteredCommits.push('1') } }; From cfb2fba51fab51e1e5c408467979e89ac6bbde69 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Tue, 8 Mar 2022 14:58:38 +0100 Subject: [PATCH 31/31] https://github-tracker-week7.netlify.app/ --- code/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/script.js b/code/script.js index b2103501..ee478ec5 100644 --- a/code/script.js +++ b/code/script.js @@ -87,7 +87,7 @@ const getProjects = async () => { // Function for Fetching Commits and Live Link const commitFetch = (projects, projectsID) => { - const GIT_COMMIT_API = projects.commits_url.replace("{/sha}", "") + const GIT_COMMIT_API = projects.commits_url.replace("{/sha}", "") + "?per_page=100" fetch(GIT_COMMIT_API, options) .then(res => res.json()) .then(data => {

Repo can be found here.