From 7d7b7354df91d42d4468a635837ca5d715cd5b12 Mon Sep 17 00:00:00 2001 From: Max Amundsen Date: Tue, 14 Jul 2026 13:05:12 -0400 Subject: [PATCH] Update kjol website with C documentation --- .vscode/launch.json | 67 +- .vscode/tasks.json | 112 +- CLAUDE.md | 18 +- go/cmd/kjol-web/README.md | 47 +- go/cmd/kjol-web/app/chart.go | 105 +- go/cmd/kjol-web/app/clayer.go | 600 + go/cmd/kjol-web/app/components.go | 1383 ++ go/cmd/kjol-web/app/docs.go | 178 +- go/cmd/kjol-web/app/kit.go | 355 - go/cmd/kjol-web/app/layers.go | 181 +- go/cmd/kjol-web/app/overlays.go | 406 - go/cmd/kjol-web/app/pages.go | 248 +- go/cmd/kjol-web/app/routes.gen.go | 36 +- go/cmd/kjol-web/app/ssr_test.go | 18 +- go/cmd/kjol-web/app/table.go | 153 - .../{landing_test.go => tworuntimes_test.go} | 19 +- go/cmd/kjol-web/build/build.go | 177 + go/cmd/kjol-web/build/main.go | 42 - go/cmd/kjol-web/buildsteps/buildsteps.go | 133 - go/cmd/kjol-web/css/app.css | 48 +- go/cmd/kjol-web/frontend/css/style.css | 41 +- go/cmd/kjol-web/frontend/src/app.ts | 33 +- .../kjol-web/frontend/src/componentGroups.ts | 36 + go/cmd/kjol-web/frontend/src/layers.ts | 84 +- go/cmd/kjol-web/frontend/src/layout/Shell.tsx | 206 +- .../frontend/src/pages/Components.tsx | 1540 ++ go/cmd/kjol-web/frontend/src/pages/Forms.tsx | 220 - go/cmd/kjol-web/frontend/src/pages/Kit.tsx | 199 - .../kjol-web/frontend/src/pages/NotFound.tsx | 41 + .../kjol-web/frontend/src/pages/Overview.tsx | 64 +- go/cmd/kjol-web/frontend/src/pages/Table.tsx | 167 - .../kjol-web/frontend/src/pages/Theming.tsx | 168 - .../src/pages/public/PublicLayout.tsx | 4 +- .../frontend/src/pages/public/Ssr.tsx | 4 +- .../frontend/src/pages/public/pages.ts | 2 +- .../frontend/src/pages/public/routes.gen.ts | 2 +- .../vendor/@kurkle/color/dist/color.esm.js | 589 + .../vendor/@kurkle/color/package.json | 77 + .../frontend/vendor/chart.js/dist/chart.js | 11599 ++++++++++++++++ .../chart.js/dist/chunks/helpers.dataset.cjs | 2915 ++++ .../dist/chunks/helpers.dataset.cjs.map | 1 + .../chart.js/dist/chunks/helpers.dataset.js | 2788 ++++ .../dist/chunks/helpers.dataset.js.map | 1 + .../frontend/vendor/chart.js/package.json | 139 + go/cmd/kjol-web/frontend/vendor/vendor.json | 6 +- go/cmd/kjol-web/grep.exe.stackdump | 31 + go/cmd/kjol-web/internal/handlers/public.go | 2 +- go/cmd/kjol-web/server/main.go | 29 +- go/cmd/kjol-web/wasm/main.go | 12 + go/jsruntime/doc.go | 2 +- go/jsruntime/icons/regular/bell.svg | 1 + go/jsruntime/icons/regular/chart-column.svg | 1 + go/jsruntime/icons/solid/bell.svg | 1 + go/jsruntime/icons/solid/chart-column.svg | 1 + go/jsruntime/styles/theme.css | 28 +- go/jsruntime/uikit/Theme.tsx | 2 +- go/lexer/lexer.go | 124 + go/lexer/lexer_c.go | 433 + go/lexer/lexer_c_test.go | 198 + go/lexer/lexer_go.go | 99 + .../code_test.go => lexer/lexer_go_test.go} | 12 +- go/wasmruntime/mount.go | 25 +- go/webui/autotable.go | 2 +- go/webui/cards.go | 2 +- go/webui/code.go | 174 - go/webui/general.go | 4 +- 66 files changed, 23884 insertions(+), 2551 deletions(-) create mode 100644 go/cmd/kjol-web/app/clayer.go create mode 100644 go/cmd/kjol-web/app/components.go delete mode 100644 go/cmd/kjol-web/app/kit.go delete mode 100644 go/cmd/kjol-web/app/overlays.go rename go/cmd/kjol-web/app/{landing_test.go => tworuntimes_test.go} (84%) create mode 100644 go/cmd/kjol-web/build/build.go delete mode 100644 go/cmd/kjol-web/build/main.go delete mode 100644 go/cmd/kjol-web/buildsteps/buildsteps.go create mode 100644 go/cmd/kjol-web/frontend/src/componentGroups.ts create mode 100644 go/cmd/kjol-web/frontend/src/pages/Components.tsx delete mode 100644 go/cmd/kjol-web/frontend/src/pages/Forms.tsx delete mode 100644 go/cmd/kjol-web/frontend/src/pages/Kit.tsx create mode 100644 go/cmd/kjol-web/frontend/src/pages/NotFound.tsx delete mode 100644 go/cmd/kjol-web/frontend/src/pages/Table.tsx delete mode 100644 go/cmd/kjol-web/frontend/src/pages/Theming.tsx create mode 100644 go/cmd/kjol-web/frontend/vendor/@kurkle/color/dist/color.esm.js create mode 100644 go/cmd/kjol-web/frontend/vendor/@kurkle/color/package.json create mode 100644 go/cmd/kjol-web/frontend/vendor/chart.js/dist/chart.js create mode 100644 go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs create mode 100644 go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs.map create mode 100644 go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js create mode 100644 go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js.map create mode 100644 go/cmd/kjol-web/frontend/vendor/chart.js/package.json create mode 100644 go/cmd/kjol-web/grep.exe.stackdump create mode 100644 go/jsruntime/icons/regular/bell.svg create mode 100644 go/jsruntime/icons/regular/chart-column.svg create mode 100644 go/jsruntime/icons/solid/bell.svg create mode 100644 go/jsruntime/icons/solid/chart-column.svg create mode 100644 go/lexer/lexer.go create mode 100644 go/lexer/lexer_c.go create mode 100644 go/lexer/lexer_c_test.go create mode 100644 go/lexer/lexer_go.go rename go/{webui/code_test.go => lexer/lexer_go_test.go} (91%) delete mode 100644 go/webui/code.go diff --git a/.vscode/launch.json b/.vscode/launch.json index 946ca046..1b0b7470 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,45 +1,74 @@ { - // Debug configs for the gowasm example. cwd is the example dir because the - // dev server resolves ./wwwroot, ./app, ./wasm and the watched engine dirs - // relative to it. + // Debug configs for kjol-web (the website). cwd is the site dir because the dev server + // resolves ./wwwroot, ./app, ./wasm, ./frontend and the watched engine dirs relative + // to it. // - // The dev-server configs preLaunchTask the prebuild (codegen + Tailwind). Codegen must - // run before the server is compiled — it writes app/*.gen.go, which the server imports — - // and under the debugger the binary is built by Delve, not by the server's own Build - // hook, so nothing else would generate it. + // Every dev-server config preLaunchTasks "kjol-web: prebuild" (codegen -> Tailwind -> + // Solid bundle). Codegen must run before the server is COMPILED — it writes + // app/*.gen.go, which the server imports — and under the debugger the binary is built + // by Delve, not by the server's own Build hook, so nothing else would generate it. "version": "0.2.0", "configurations": [ { - "name": "gowasm: dev server", + "name": "kjol-web: dev server", "type": "go", "request": "launch", "mode": "auto", - "program": "${workspaceFolder}/go/cmd/examples/go-wasm-web/server", - "cwd": "${workspaceFolder}/go/cmd/examples/go-wasm-web", + "program": "${workspaceFolder}/go/cmd/kjol-web/server", + "cwd": "${workspaceFolder}/go/cmd/kjol-web", "args": ["-addr", ":8085"], - "preLaunchTask": "gowasm: prebuild" + "preLaunchTask": "kjol-web: prebuild" }, { - "name": "gowasm: dev server (no watch)", + "name": "kjol-web: dev server (no watch)", "type": "go", "request": "launch", "mode": "auto", - "program": "${workspaceFolder}/go/cmd/examples/go-wasm-web/server", - "cwd": "${workspaceFolder}/go/cmd/examples/go-wasm-web", + "program": "${workspaceFolder}/go/cmd/kjol-web/server", + "cwd": "${workspaceFolder}/go/cmd/kjol-web", // -watch=false skips the server's own Build entirely, so wwwroot is served exactly - // as it sits on disk. The prebuild is what puts current CSS and generated code - // there; without it you would be debugging against stale artefacts. + // as it sits on disk. The prebuild is what puts current generated code, CSS and JS + // bundles there; without it you would be debugging against stale artefacts — and + // with no bundle at all, every /js/* page would come up blank. "args": ["-watch=false"], - "preLaunchTask": "gowasm: prebuild" + "preLaunchTask": "kjol-web: prebuild" }, { - "name": "gowasm: codegen (wasmgen)", + "name": "kjol-web: cold build (-build)", + "type": "go", + "request": "launch", + "mode": "auto", + // The same binary, with the flag that runs every build step once and exits. There is + // no separate ./build command: the steps are a library, because the server imports + // them and Go will not let you import a main. + "program": "${workspaceFolder}/go/cmd/kjol-web/server", + "cwd": "${workspaceFolder}/go/cmd/kjol-web", + "args": ["-build"] + }, + { + "name": "kjol-web: codegen (wasmgen)", "type": "go", "request": "launch", "mode": "auto", "program": "${workspaceFolder}/go/cmd/wasmgen", - "cwd": "${workspaceFolder}/go/cmd/examples/go-wasm-web", + "cwd": "${workspaceFolder}/go/cmd/kjol-web", "args": ["./app"] + }, + { + "name": "kjol-web: bundle (Solid)", + "type": "go", + "request": "launch", + "mode": "auto", + // Debug the JS build itself — the Solid compiler, the Tailwind engine, the SSR bake. + // All of it is Go, so all of it takes a breakpoint. + "program": "${workspaceFolder}/go/cmd/bundle", + "cwd": "${workspaceFolder}/go/cmd/kjol-web", + "args": [ + "-app", "frontend", + "-web", "../../jsruntime", + "-out", "wwwroot", + "-gen-ts", "frontend/src/ui/generated" + ] } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 49eb81b1..9e61020f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,63 +1,95 @@ { - // Tasks for the kjol repo. The gowasm example is a nested module at - // go/cmd/examples/go-wasm-web, so its tasks set cwd there; the module-wide - // Go tasks run in go/. + // Tasks for the kjol repo. // - // The example's build pipeline is Go, not a shell script — see - // go/cmd/examples/go-wasm-web/buildsteps. The dev server calls the same steps on every - // save, and a bash script would not run for anyone on Windows. "gowasm: prebuild" is - // codegen + Tailwind, and everything that runs the app depends on it; the two steps are - // also exposed on their own so you can rerun just the one you need. + // kjol-web — the website, and the runnable example of both web layers — is a nested + // module at go/cmd/kjol-web, so its tasks set cwd there; the module-wide Go tasks run + // in go/. + // + // These used to be called "gowasm: ...", which stopped being true: the site is two + // front-ends now, and one build compiles Go to WebAssembly AND bundles a Solid app. + // They are "kjol-web: ..." to match the directory they act on. + // + // The build pipeline is Go, not a shell script — see go/cmd/kjol-web/build. The dev + // server calls the same functions on every save, and a bash script would not run for + // anyone on Windows. Note there is no `./build` COMMAND any more: the steps are a + // library (the server imports them, and Go will not let you import a main), so a cold + // build is `go run ./server -build`. + // + // "kjol-web: prebuild" is what every run/debug config depends on. Codegen has to happen + // before the server is COMPILED — it writes app/*.gen.go, which the server imports — and + // under the debugger the binary is built by Delve, not by the server's own Build hook, + // so nothing else would generate it. Tailwind and the Solid bundle are in there too, so + // that a -watch=false session serves current artefacts instead of stale ones. Each step + // is also exposed on its own, so you can rerun just the one you need. "version": "2.0.0", "tasks": [ { - "label": "gowasm: prebuild", - "detail": "Codegen (pages/layouts/server components) then Tailwind. Dependency of the run + debug configs.", + "label": "kjol-web: prebuild", + "detail": "Codegen -> Tailwind -> Solid bundle. Dependency of the run + debug configs.", "dependsOrder": "sequence", - "dependsOn": ["gowasm: codegen", "gowasm: tailwind"], + "dependsOn": ["kjol-web: codegen", "kjol-web: tailwind", "kjol-web: bundle (Solid)"], "problemMatcher": [], "group": "build" }, { - "label": "gowasm: codegen", + "label": "kjol-web: codegen", "detail": "Regenerate app/*.gen.go from the //gowasm: directives (pages, layouts, server components)", "type": "shell", "command": "go", "args": ["run", "kjol/cmd/wasmgen", "./app"], - "options": { "cwd": "${workspaceFolder}/go/cmd/examples/go-wasm-web" }, + "options": { "cwd": "${workspaceFolder}/go/cmd/kjol-web" }, "problemMatcher": ["$go"], "presentation": { "reveal": "silent", "panel": "shared" }, "group": "build" }, { - "label": "gowasm: tailwind", - "detail": "Compile css/app.css -> wwwroot/app.css, scanning the webui kit + the example's Go markup", + "label": "kjol-web: tailwind", + "detail": "Compile css/app.css -> wwwroot/app.css, scanning the webui kit + the site's Go markup", "type": "shell", "command": "go", "args": [ "run", "./cmd/twcss", - "-entry", "cmd/examples/go-wasm-web/css/app.css", - "-out", "cmd/examples/go-wasm-web/wwwroot/app.css", + "-entry", "cmd/kjol-web/css/app.css", + "-out", "cmd/kjol-web/wwwroot/app.css", "-base", ".", "webui/**/*.go", - "cmd/examples/go-wasm-web/app/**/*.go", - "cmd/examples/go-wasm-web/server/**/*.go" + "cmd/kjol-web/app/**/*.go", + "cmd/kjol-web/server/**/*.go" ], // Run from the kjol module root so the Tailwind engine's deps resolve in kjol's - // go.mod, not the example's. + // go.mod, not the site's. "options": { "cwd": "${workspaceFolder}/go" }, "problemMatcher": ["$go"], "presentation": { "reveal": "silent", "panel": "shared" }, "group": "build" }, { - "label": "gowasm: dev server (hot reload)", - "detail": "SSR + hot reload on :8085. Rebuilds Go on save; a .css save recompiles Tailwind only and swaps the stylesheet in place.", + "label": "kjol-web: bundle (Solid)", + "detail": "TSX -> Solid -> esbuild, plus the SSR bake: wwwroot/bundle.min.{js,css} + public.bundle.min.*", + "type": "shell", + "command": "go", + "args": [ + "run", "kjol/cmd/bundle", + "-app", "frontend", + // The shared JS tree: the Solid kit, the vendored runtime, the icon SVGs and the + // @theme scaffold all live there. + "-web", "../../jsruntime", + "-out", "wwwroot", + "-gen-ts", "frontend/src/ui/generated" + ], + "options": { "cwd": "${workspaceFolder}/go/cmd/kjol-web" }, + "problemMatcher": ["$go"], + "presentation": { "reveal": "silent", "panel": "shared" }, + "group": "build" + }, + { + "label": "kjol-web: dev server (hot reload)", + "detail": "SSR + /rsc + hot reload on :8085. Rebuilds on save; a .css save recompiles Tailwind only and swaps the stylesheet in place.", "type": "shell", "command": "go", "args": ["run", "./server"], - "options": { "cwd": "${workspaceFolder}/go/cmd/examples/go-wasm-web" }, - "dependsOn": ["gowasm: prebuild"], + "options": { "cwd": "${workspaceFolder}/go/cmd/kjol-web" }, + "dependsOn": ["kjol-web: prebuild"], "isBackground": true, "problemMatcher": { "owner": "go", @@ -75,18 +107,18 @@ "group": { "kind": "build", "isDefault": true } }, { - "label": "gowasm: build", - "detail": "One-off cold build of the example: codegen + Tailwind + wasm + stage wasm_exec.js", + "label": "kjol-web: build (cold)", + "detail": "Cold build, then exit: codegen + Tailwind + wasm + Solid bundle + wasm_exec.js.", "type": "shell", "command": "go", - "args": ["run", "./build"], - "options": { "cwd": "${workspaceFolder}/go/cmd/examples/go-wasm-web" }, + "args": ["run", "./server", "-build"], + "options": { "cwd": "${workspaceFolder}/go/cmd/kjol-web" }, "problemMatcher": ["$go"], "group": "build" }, { "label": "kjol: build ./...", - "detail": "Build the whole kjol module (excludes the nested example)", + "detail": "Build the whole kjol module (does not descend into the nested kjol-web module)", "type": "shell", "command": "go build ./...", "options": { "cwd": "${workspaceFolder}/go" }, @@ -108,6 +140,28 @@ "options": { "cwd": "${workspaceFolder}/go" }, "problemMatcher": ["$go"], "group": { "kind": "test", "isDefault": true } + }, + { + "label": "kjol: test the reconciler (wasm)", + "detail": "The reconciler needs a DOM, so `go test ./...` cannot reach it. This runs it under node, against a minimal DOM shim.", + "type": "shell", + "command": "go", + "args": ["test", "-exec=node testdata/domexec.js", "./wasmruntime"], + "options": { + "cwd": "${workspaceFolder}/go", + "env": { "GOOS": "js", "GOARCH": "wasm" } + }, + "problemMatcher": ["$go"], + "group": "test" + }, + { + "label": "kjol-web: test", + "detail": "The site's own tests (SSR, the icon registry, the AutoTable export bytes). Its own module, so `kjol: test ./...` does not reach it.", + "type": "shell", + "command": "go test ./...", + "options": { "cwd": "${workspaceFolder}/go/cmd/kjol-web" }, + "problemMatcher": ["$go"], + "group": "test" } ] } diff --git a/CLAUDE.md b/CLAUDE.md index 2aceac49..c6fd7ff5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,7 +46,7 @@ drag a JavaScript bundler along for a CSS file. ### go/ — module `kjol` Packages, imported as `kjol/`: `appenv basic chrono config csv dbutil finance httputil -l4g security snailmail validation jsbundler tw`, plus the **gowasm** web-UI engine (`vdom` +l4g lexer security snailmail validation jsbundler tw`, plus the **gowasm** web-UI engine (`vdom` `wasmruntime` `rsc` `wasmdevserver`, and `webui` — a Tailwind-styled component kit ported from `jsruntime/uikit`; author components in pure Go compiled to WebAssembly; all stdlib-only), and `cmd/{bundle,twcss,migrate,loc,passgen,typecheck,wasmgen}`. @@ -54,6 +54,14 @@ stdlib-only), and `cmd/{bundle,twcss,migrate,loc,passgen,typecheck,wasmgen}`. `jsbundler` is the **JS** build (TSX → Solid → esbuild + the goja SSR bake). It was called `webbundler`. +`lexer` is syntax highlighting: source in, HTML with coloured spans out, `Highlight(lang, src)` +plus `HighlightGo` / `HighlightC`. It is a lexer and not a parser on purpose — it degrades to +escaped plain text rather than failing, and an unknown language is not an error. It is outside +`webui` because it touches no DOM. **Its output is Tailwind class names**, so any stylesheet that +has to render a code block must scan `lexer/**/*.go` — a build that forgets to still compiles and +just renders the snippet unstyled (see `cmd/kjol-web/build.Tailwind`). The C tables deliberately +mirror `c/lexer/lexer_c.c`; add a type to one, add it to the other. + **`cmd/kjol-web` is the website**: the landing page and documentation for the whole codebase, and the runnable example of both web layers. It is its own nested module (so its go-chart / esbuild / goja deps stay out of kjol) and it is ONE server running TWO @@ -61,6 +69,12 @@ front-ends — `/wasm/*` is the Go→WebAssembly SPA, `/js/*` is the Solid SPA, static+wasm landing page whose **Layers menu** is the site's primary navigation. Its README is the map. Anything user-visible you add to kjol should show up there, running. +The kit is documented on ONE page per layer (`/wasm/components`, `/js/components`), not one page +per source file — a reader hunting for a date picker should not have to guess whether it was filed +under forms or overlays. `componentGroups()` / `COMPONENT_GROUPS` is the single list that drives +the sections, the sidebar that jumps to them, and the index. Build it with `go run ./server -build`; +the steps live in the `build` package (a library, because the server imports it). + Build / test (run from repo root): ``` go -C go build ./... @@ -93,7 +107,7 @@ signals, never inside a render closure. Floating panels share one positioning en `bg-surface` / `border-line` / `text-ink` / `text-accent`, and a `.dark` class on `` re-points what those mean. A theme is a dozen CSS variables rather than four hundred class strings, and `dark:` on every component is exactly the thing to avoid. The Go and Solid kits use -the **same token names on purpose** — change `surface` once and both halves of a site move. +the **same token names on purpose** — change `surface` once and both layers of a site move. Only two things still need a `dark:` variant, because no re-pointed token can fix them: a coloured tint (a `red-50` wash is invisible on a near-black surface) and a fill that inverts (the diff --git a/go/cmd/kjol-web/README.md b/go/cmd/kjol-web/README.md index d5efcefc..b9a8eba5 100644 --- a/go/cmd/kjol-web/README.md +++ b/go/cmd/kjol-web/README.md @@ -13,8 +13,8 @@ build the site out of both. ```sh cd go/cmd/kjol-web -go run ./build # cold build: both halves -go run ./server # SSR + /rsc + hot reload at http://localhost:8085 +go run ./server -build # cold build, then exit +go run ./server # build, then SSR + /rsc + hot reload at http://localhost:8085 ``` `go run ./server` performs that same build on every save and hot-swaps the result into @@ -28,8 +28,8 @@ terminal you were not looking at. | Path | Rendered by | What it is | |---|---|---| | `/`, `/about` | Go → WebAssembly, SSR'd | The landing page. The **Layers** menu is the site's primary navigation. | -| `/wasm/*` | Go → WebAssembly | **Kjol Wasm Web** — the gowasm engine: SSR + hydration, server components, the `webui` kit, overlays, AutoTable, charts, client fetching. | -| `/js/*` | Solid → esbuild, client-rendered | **Kjol JS Web** — the Solid kit: components, forms, AutoTable, theming. | +| `/wasm/*` | Go → WebAssembly | **Kjol Wasm Web** — the gowasm engine: SSR + hydration, server components, data fetching, and the whole `webui` kit on one page (`/wasm/components`). | +| `/js/*` | Solid → esbuild, client-rendered | **Kjol JS Web** — the Solid kit, all of it on one page (`/js/components`), with a sidebar that jumps to each group. | | `/js/ssr` | Solid → **goja, at request time** | A public page server-rendered with live data injected — the ISR path. | Crossing between `/wasm` and `/js` is a real page load. They are different binaries, and @@ -38,16 +38,19 @@ pretending otherwise would mean shipping both to every visitor. ## Layout ``` -app/ the Go/WASM half — pages as plain Go functions returning a *VNode - pages.go Deps + Shell + the public/app layouts + the landing page - layers.go the Layers, as data. MIRRORED in frontend/src/layers.ts. - docs.go the docs chrome; docsNav() is the sidebar AND the index - kit.go table.go overlays.go chart.go data.go server_counter.go - *.gen.go GENERATED by kjol/cmd/wasmgen (routes, layout dispatch, RSC stubs) +app/ Kjol Wasm Web — pages as plain Go functions returning a *VNode + pages.go Deps + Shell + the public/app layouts + the landing page + layers.go the Layers, as data. MIRRORED in frontend/src/layers.ts. + docs.go the docs chrome; docsNav() is the sidebar AND the index + components.go THE WHOLE KIT, on one page. componentGroups() is the single source + for both the sections and the sidebar that jumps to them. + table.go the AutoTable's data + controller (shared with its tests) + chart.go data.go server_counter.go + *.gen.go GENERATED by kjol/cmd/wasmgen (routes, layout dispatch, RSC stubs) wasm/ the js/wasm client entry (main_native.go is a host stub) css/app.css its Tailwind entry — kjol/tw scans the .go files for class names -frontend/ the Solid half — .tsx pages written against @ui/* +frontend/ Kjol JS Web — .tsx pages written against @ui/* css/style.css brand ONLY. kjol's theme.css is prepended by the bundler, and it is the one that does `@import "tailwindcss"`. vendor/ pdf-lib + pdfjs-dist. @ui/AutoTable imports them at the TOP LEVEL, @@ -57,18 +60,21 @@ frontend/ the Solid half — .tsx pages written against @ui/* src/layers.ts the Layers again. Keep in step with app/layers.go. server/ ONE Go server: serves wwwroot, SSRs the wasm routes, hosts /rsc, - mounts the Solid SPA at /js/*, serves the SSR'd public pages. + mounts the Solid SPA at /js/*, serves the SSR'd public pages, and + carries the -build flag. +build/ the build pipeline, in Go rather than a shell script, so the cold build + and the watch loop call the SAME functions and cannot drift. It is a + library, not a command: the server imports it, and Go will not let you + import a main — hence `go run ./server -build` rather than `./build`. internal/handlers/ the app side of the public-page inversion: kjol generates the registry; this owns the type and the document shell. -buildsteps/ the build, in Go rather than a shell script, so the one-shot build and - the dev server's watch loop call the SAME functions and cannot drift. -wwwroot/ both halves write here. They never collide: app.css / app.wasm for one, +wwwroot/ both layers write here. They never collide: app.css / app.wasm for one, bundle.min.* for the other. One static dir, one server. ``` ## Theming -One theme, both halves. The kits are themed by **semantic tokens** — components say +One theme, both layers. The kits are themed by **semantic tokens** — components say `bg-surface`, `text-ink`, `border-line` and never name a colour — so dark mode re-points about a dozen CSS variables and not one component knows it happened. @@ -84,11 +90,12 @@ to invert (the neutral button, whose label must go dark when the fill goes pale) ## Adding things -**A Go/WASM page:** write the function, mark it `//gowasm:page /wasm/thing layout=app`, +**A Kjol Wasm Web page:** write the function, mark it `//gowasm:page /wasm/thing layout=app`, build. `wasmgen` regenerates the routing. Add `static` to have it server-rendered. -**A Solid page:** write the `.tsx`, add it to `routes` in `frontend/src/app.ts` and to -`NAV` in `frontend/src/layout/Shell.tsx`. +**A component:** add a section to `app/components.go` (Go) or `frontend/src/pages/Components.tsx` +(Solid), and one entry to `componentGroups()` / `COMPONENT_GROUPS`. That single list drives the +sections, the sidebar that jumps to them, and the index — so none of the three can drift. **A layer:** one entry in `app/layers.go` *and* one in `frontend/src/layers.ts`. They are two files because nothing is upstream of both a WebAssembly binary and an esbuild bundle; @@ -103,7 +110,7 @@ keeping each to a flat list of plain data is what makes that duplication surviva | `kjol/rsc` | stateless server components over HTTP | `//gowasm:server` + its generated client stub | | `kjol/wasmdevserver` | reusable dev server: SSR, `/rsc`, hot reload, error overlay | `server/main.go` fills a `wasmdevserver.Config` | | `kjol/webui` | the Go component kit | every `/wasm/*` page | -| `kjol/jsbundler` | TSX → Solid → esbuild, the Tailwind driver, the SSR bake | `buildsteps.JS`, and the ISR render at request time | +| `kjol/jsbundler` | TSX → Solid → esbuild, the Tailwind driver, the SSR bake | `build.JS`, and the ISR render at request time | | `kjol/jsruntime` | the Solid kit, the vendored runtime, the icons, `theme.css` | everything under `/js/*` | | `kjol/tw` | the Tailwind v4 engine, in Go | both stylesheets — it scans `.go` for one and `.tsx` for the other | diff --git a/go/cmd/kjol-web/app/chart.go b/go/cmd/kjol-web/app/chart.go index 6d3d52ea..8b113e14 100644 --- a/go/cmd/kjol-web/app/chart.go +++ b/go/cmd/kjol-web/app/chart.go @@ -8,6 +8,7 @@ import ( chart "github.com/wcharczuk/go-chart/v2" . "kjol/vdom" + "kjol/wasmruntime" ui "kjol/webui" ) @@ -60,9 +61,56 @@ func pieSVG(values []int) string { }) } +// chartSkeleton is what the SERVER puts where a chart is going to be: a box of the right +// height, so nothing jumps when the real one arrives. +func chartSkeleton(height string) *VNode { + return Div(Attr("class", "flex animate-pulse items-center justify-center rounded-default bg-surface-muted "+height), + Span(Attr("class", "text-xs text-ink-faint"), Text("drawing…")), + ) +} + +// newChartDrawing returns a signal that is FALSE on the server and on the client's first +// render, and true from the moment the WebAssembly has committed that first render. +// +// It is what keeps the charts CLIENT-DRAWN. go-chart is ordinary Go and would run just as +// happily on the server — it used to, and this page's markup carried two finished SVGs. +// Two reasons not to: +// +// - It is work the server does on every single request for a picture that only matters +// once the page is alive. Drawing it in the browser costs the server nothing and the +// reader nothing they can see. +// - It is the more honest demonstration. A Go charting library, compiled to WebAssembly, +// drawing an SVG in the browser is the thing this layer claims it can do. Shipping a +// server-rendered picture of one proves the opposite point. +// +// The false-on-first-render part is not optional: hydration walks the server's DOM +// alongside the client's first tree, so that tree has to be the SAME tree. Draw the charts +// on the client's first pass and the two disagree, and the reconciler has to rebuild what +// it should have adopted. +func newChartDrawing() *Signal[bool] { + drawn := NewSignal(false) + wasmruntime.AfterRender(func() { + if !drawn.Get() { + drawn.Set(true) // a write re-renders; the second pass draws for real + } + }) + return drawn +} + +// chartBox renders one chart, or the placeholder standing in for it. draw is a closure so +// that on the server go-chart is never called at all — not called and discarded, but never +// entered. +func chartBox(class, height string, drawn bool, draw func() string) *VNode { + if !drawn { + return Div(Attr("class", class), chartSkeleton(height)) + } + return Div(Attr("class", class), Raw(draw())) +} + //gowasm:page /wasm/chart static layout=app func ChartPage(d Deps) func() *VNode { data := NewSignal(fixedChartData()) + drawn := newChartDrawing() return func() *VNode { values := data.Get() @@ -85,19 +133,27 @@ func ChartPage(d Deps) func() *VNode { ), docSection("charts", "A worked example: charts", - prose("These charts are SVG produced by go-chart — a plain Go library that knows nothing about "+ - "the browser. The server draws them and ships the markup inline; there is no chart "+ - "JavaScript, and no canvas that has to wait for the client to boot before it shows anything."), - prose("Shuffle re-runs the same drawing code in the browser. The first render came from the "+ - "server and the next one comes from WebAssembly, and the page cannot tell the difference."), + prose("These charts are SVG produced by go-chart — a plain Go library that knows nothing "+ + "about the browser. They are drawn by the WEBASSEMBLY, in your browser, and never by the "+ + "server: what the server sends is the two placeholders you may have seen for a moment, "+ + "and the WebAssembly replaces them on its first commit."), + prose("That is the demonstration. A Go charting library, compiled to wasm, drawing an SVG in "+ + "the browser is exactly what this layer claims it can do — and a server-rendered picture "+ + "of a chart would prove the opposite point while looking identical. Shuffle redraws them, "+ + "and no request is made."), + prose("The rest of the page IS server-rendered — the headings, the prose, the code you are "+ + "reading. Static and client-drawn are not opposites: a route can be pre-rendered and still "+ + "leave the expensive, browser-only parts of itself for the client."), Div(Attr("class", "mt-4"), ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Icon: "chart-column", Text: "Shuffle data", OnClick: func() { data.Set(randomValues()) }}), ), Div(Attr("class", "mt-4 grid gap-4 lg:grid-cols-12"), - Div(Attr("class", "lg:col-span-7 rounded-default border border-line bg-surface p-3 shadow-xs overflow-auto"), Raw(barSVG(values))), - Div(Attr("class", "lg:col-span-5 rounded-default border border-line bg-surface p-3 shadow-xs overflow-auto"), Raw(pieSVG(values))), + chartBox("lg:col-span-7 rounded-default border border-line bg-surface p-3 shadow-xs overflow-auto", + "h-[260px]", drawn.Get(), func() string { return barSVG(values) }), + chartBox("lg:col-span-5 rounded-default border border-line bg-surface p-3 shadow-xs overflow-auto", + "h-[320px]", drawn.Get(), func() string { return pieSVG(values) }), ), note("go-chart lives in the EXAMPLE, not in kjol", @@ -111,7 +167,7 @@ func ChartPage(d Deps) func() *VNode { apiRow{"//gowasm:page /path static", "Pre-render this route on the server, then hydrate it."}, apiRow{"vdom.RenderHTML", "Render a tree to an HTML string. This is what the server calls."}, apiRow{"wasmruntime.Hydrate", "Adopt server-rendered DOM instead of building it. The client's entry point for a static route."}, - apiRow{"vdom.Raw", "Insert markup verbatim — how the server-drawn SVG gets in. The reconciler clears it correctly when the element is reused."}, + apiRow{"vdom.Raw", "Insert markup verbatim — how the SVG the WebAssembly drew gets in. The reconciler clears it correctly when the element is reused."}, ), ), ) @@ -120,17 +176,42 @@ func ChartPage(d Deps) func() *VNode { const chartSnippet = `//gowasm:page /wasm/chart static layout=app func ChartPage(d Deps) func() *VNode { - data := NewSignal(fixedChartData()) + data := NewSignal(fixedChartData()) + drawn := NewSignal(false) // false on the server AND on the first client render + + // AfterRender is the post-commit hook. It fires once the WebAssembly has put its + // first tree on the page — the earliest moment at which drawing is a client act. + wasmruntime.AfterRender(func() { + if !drawn.Get() { + drawn.Set(true) // a write re-renders; the second pass draws + } + }) return func() *VNode { - // go-chart draws an SVG string — on the server for the first paint, - // and in the browser for every render after that. return Div( ui.Button(ui.ButtonProps{ Text: "Shuffle data", OnClick: func() { data.Set(randomValues()) }, }), - Div(Raw(barSVG(data.Get()))), + + // The server never enters barSVG: chartBox takes a CLOSURE, and calls it only + // once drawn is true. It renders the placeholder instead, and the WebAssembly + // swaps in the real chart on its first commit. + // + // drawn must be FALSE on the client's first render too. Hydration walks the + // server's DOM alongside the client's first tree, so the two have to BE the + // same tree; draw on that first pass and the reconciler rebuilds what it + // should have adopted. + chartBox("", "h-[260px]", drawn.Get(), + func() string { return barSVG(data.Get()) }), ) } +} + +// chartBox is the whole trick, and it is four lines. +func chartBox(class, height string, drawn bool, draw func() string) *VNode { + if !drawn { + return Div(Attr("class", class), chartSkeleton(height)) + } + return Div(Attr("class", class), Raw(draw())) }` diff --git a/go/cmd/kjol-web/app/clayer.go b/go/cmd/kjol-web/app/clayer.go new file mode 100644 index 00000000..e7807b6e --- /dev/null +++ b/go/cmd/kjol-web/app/clayer.go @@ -0,0 +1,600 @@ +package app + +import ( + . "kjol/vdom" +) + +// The C layer's documentation. +// +// One page, like the component pages: a reader looking for the arena API should not have +// to guess which of six pages it was filed under. The sidebar calls the sections +// SUBSYSTEMS, which is what they are — each is a directory of C, and each has a different +// idea of what it depends on. +// +// Everything here is read off the source. Where a subsystem needs something the reader has +// to supply — a unity translation unit, a vendored ImGui, a resource script pointed at his +// own product — the page says what it needs and what to write, rather than filing it as a +// defect. This is a base layer: it is COPIED INTO a project and compiled with it, so "what +// you have to provide" is not a caveat, it is the interface. + +type subsystem struct { + ID string + Label string + Icon string + Blurb string +} + +// cSubsystems is the single source for the page's sections AND the sidebar that jumps to +// them — so the sidebar cannot offer a jump to a section that does not exist. +func cSubsystems() []subsystem { + return []subsystem{ + {ID: "base", Label: "base", Icon: "cube", + Blurb: "The vocabulary: fixed-width types, a bump allocator, counted strings, and 2D rectangle math."}, + {ID: "build", Label: "build", Icon: "bolt", + Blurb: "A single-header build system. Your build script is a C program that recompiles itself."}, + {ID: "platform", Label: "platform", Icon: "server", + Blurb: "A window, its input, the clipboard. Win32, and it hands every message to Dear ImGui first."}, + {ID: "lexer", Label: "lexer", Icon: "code", + Blurb: "Syntax highlighting. Five languages, thirteen themes, and a paint array rather than a token list."}, + {ID: "config", Label: "config", Icon: "file", + Blurb: "An INI file beside the executable. Program-managed state, not user-authored settings."}, + {ID: "installer", Label: "installer", Icon: "download", + Blurb: "A Win32 wizard that carries the product inside itself as a resource."}, + } +} + +// cNav is the sidebar while you are reading /c. The group is called Subsystems. +func cNav() []docsGroup { + items := make([]docsItem, 0, len(cSubsystems())) + for _, s := range cSubsystems() { + items = append(items, docsItem{ + Path: "/c#" + s.ID, + Label: s.Label, + Icon: s.Icon, + Blurb: s.Blurb, + }) + } + + return []docsGroup{ + { + Title: "Introduction", + Items: []docsItem{ + {Path: "/c", Label: "Overview", Icon: "book-open", + Blurb: "What this layer is, and where it came from."}, + {Path: "/c#compiling", Label: "Compiling it", Icon: "bolt", + Blurb: "The unity translation unit: what you write, and what each subsystem needs from you."}, + }, + }, + {Title: "Subsystems", Items: items}, + } +} + +//gowasm:page /c static layout=app +func CPage(d Deps) func() *VNode { + return func() *VNode { + return docPage("Layers", "Kjøl C", + "A base layer in C — a bump allocator, counted strings, a syntax highlighter, a Win32 window, "+ + "and a build system that is itself a C program. It was lifted out of codeMAX, a code editor, "+ + "which is why the pieces are the pieces an editor needs.", + + docSection("what-this-is", "What this is", + prose("Not a library you link against. A set of headers and translation units you COPY INTO "+ + "a project and compile with it — the base-layer style you will recognise if you have read "+ + "Ryan Fleury's RAD Debugger or watched Handmade Hero. The headers say as much: base_core, "+ + "base_arena and base_strings all credit raddebugger, and the naming (U32, Str8, Rng2F32) "+ + "comes straight from it."), + prose("There is no package manager, no version, and no ABI to keep stable — because there is "+ + "nothing to keep stable BETWEEN. The code and its consumer are compiled together. Which is "+ + "also why the next section is about the file YOU write: the layer does not build on its "+ + "own, by design. It builds as part of your program."), + + note("It arrived from codeMAX, and still carries its names", + "The whole directory came into Kjøl in one commit, out of codeMAX, a code editor. Its "+ + "fingerprints are still on it and they are worth knowing before you go looking: the "+ + "Win32 window class is codemax_wc, the installer installs codeMAX, and installer.rc "+ + "names codeMAX's icon, manifest and payload. Those are the strings to change when you "+ + "adopt it — they are the product's name, not the layer's."), + ), + + cCompiling(), + cBase(), + cBuild(), + cPlatform(), + cLexer(), + cConfig(), + cInstaller(), + ) + } +} + +// ---- compiling ----------------------------------------------------------- + +// How you actually compile the thing. It is second on the page, before any API, because a +// base layer's first question is "how does this get into my program" and the answer here is +// not the one a reader arriving from a package-manager language expects. +func cCompiling() *VNode { + return docSection("compiling", "Compiling it", + prose("One translation unit. You write a single .c that #includes the layer's .c files in order, "+ + "and hand THAT to the compiler — the whole layer is one TU, compiled with your program. "+ + "base/base_inc.c is the pattern in miniature: three lines, pulling in the arena and the "+ + "strings. Your own unity file is the same idea, one level up."), + codeLang("app_inc.c — the file you write", "c", unityTUSnippet), + prose("This is not a workaround for the absence of a build system; it IS the build system. There is "+ + "no per-file compilation, so there are no object files, no link order and no header guard "+ + "archaeology — and the compiler sees the whole layer at once, which is what makes every helper "+ + "in it `internal` and every call to one a candidate for inlining."), + + docSubheading("What each subsystem needs from you"), + prose("They are not equally self-contained, and the differences are worth knowing before you pick "+ + "the ones you want:"), + apiTable( + apiRow{"base", "Nothing. It stands on its own — base_inc.c is a complete TU, and everything above depends on it."}, + apiRow{"lexer", "Include lexer.c AND the five backends in the same TU. Their helpers are `internal`, so separate compilation would leave lexer.c's dispatch with nothing to call."}, + apiRow{"config", "Include config.h above config.c. config.c uses Config without including its own header — which is exactly what a unity build lets it do."}, + apiRow{"platform", "Dear ImGui, vendored, and a C++ compiler for it. See the subsystem below — the window procedure hands it every message before it looks at any of them."}, + apiRow{"installer", "Its own program and its own .rc, pointed at your icon, your manifest and your payload."}, + apiRow{"build", "Nothing — it is the thing that runs the compiler. Write a build.c, compile it once, and never compile it again."}, + ), + + note("There is no build.c in Kjøl yet, and that is the next job", + "build.h is here, complete and self-contained, and nothing in the repository includes it — so "+ + "the layer currently has a build system and no build. Writing that build.c is what turns "+ + "this directory into something you can type one command at, and it is the single most "+ + "useful thing anyone could add to the C layer. The build subsystem below documents the "+ + "header it would be written against, and the snippet there is a working sketch of it."), + ) +} + +// ---- base ---------------------------------------------------------------- + +func cBase() *VNode { + return docSection("base", "base", + prose("The vocabulary. Fixed-width integers with short names, a bump allocator, a counted string, "+ + "and the rectangle math a user interface actually spends its time on. Everything else in the "+ + "layer is written in these — which is why there is no malloc below this line, and no char* "+ + "pretending to be a string."), + + docSubheading("The types (base_core.h)"), + prose("U8 through U64, S8 through S64, B32 for a boolean, F32 and F64 — upper case, not the u8/i32 "+ + "of the Rust-adjacent style. Sizes are written KB(4) and MB(64) rather than as a number of "+ + "zeroes you have to count. And the three meanings of `static` in C get three different names: "+ + "`internal` for a file-local function, `global` for a translation-unit variable, `local_persist` "+ + "for a local that survives the call. They all expand to `static`; they do not all mean the same "+ + "thing, and the code says which one it meant."), + codeLang("c/base/base_core.h", "c", baseCoreSnippet), + prose("There is more in the header than the types: Min/Max/Clamp, the nil-aware doubly and singly "+ + "linked-list macros from raddebugger (DLLPushBack, SLLQueuePush and friends), DeferLoop — which "+ + "is `defer` written as a for-loop, because C has no defer — and an Assert that traps on MSVC "+ + "with __debugbreak."), + + docSubheading("The arena (base_arena.h)"), + prose("One allocator, and it is a bump pointer: arena_push moves a cursor and hands you the memory. "+ + "There is no free. You release the whole arena, or you roll it back to a saved position — which "+ + "is what Temp is. A function that wants scratch space opens a Temp, allocates as freely as it "+ + "likes, and closes it. Nothing has to be individually released, so nothing can be individually "+ + "forgotten."), + codeLang("c/base/base_arena.h", "c", baseArenaSnippet), + + note("It is malloc-backed and fixed-capacity — and that has a sharp edge", + "The header says so: this is raddebugger's arena with the virtual-memory reserve/commit taken "+ + "out. arena_alloc is one malloc of the capacity you asked for, and that capacity is final — "+ + "the arena does not grow and does not chain. Overflow hits Assert(!\"Arena overflow\") and "+ + "returns NULL. But Assert compiles to nothing unless _DEBUG is defined, so in a release build "+ + "an over-full arena hands you a silent NULL. Alignment is a hard-coded 8 bytes, so there is "+ + "no SIMD guarantee. And there is no thread-local scratch pool — if you came here expecting "+ + "raddebugger's scratch_begin, it is not in this snapshot."), + + docSubheading("Strings (base_strings.h)"), + prose("Str8 is a pointer and a length. Not NUL-terminated, not owned, does not allocate — so a "+ + "substring is free, and a Str8 can point into the middle of a file you mapped. The two "+ + "operations that MUST allocate take the arena, and so they say so in their signature. That is "+ + "the whole API: eight functions. It is early, and there is no split, join, trim or "+ + "case-insensitive compare yet."), + codeLang("c/base/base_strings.h", "c", baseStringsSnippet), + + docSubheading("Math (base_math.h)"), + prose("Look at the shape of this file and it tells you what it is for. Vec3F32 has a constructor "+ + "and no operations. Rng2F32 — a rectangle — has nine: width, height, dim, center, contains, "+ + "pad, shift, intersect. There are no matrices, no quaternions, no Mat4. This is 2D interface "+ + "math, and a layout written in it is a sequence of rectangle operations rather than eight lines "+ + "of x + w arithmetic with an off-by-one hiding in them."), + prose("The Axis2 / Side / Corner enums plus v2f32_axis are the raddebugger trick for axis-generic "+ + "code: one code path handles both X and Y by indexing into the vector instead of naming .x "+ + "and .y, so a horizontal and a vertical layout are the same function with a different argument."), + + apiTable( + apiRow{"arena_alloc / arena_release", "One malloc, fixed capacity. It does not grow."}, + apiRow{"arena_push / arena_push_no_zero", "Bump the cursor. Zeroed by default; the fast one has to be asked for by name."}, + apiRow{"temp_begin / temp_end", "A scratch scope: save the position, allocate freely, roll it all back."}, + apiRow{"push_array(arena, T, n)", "Typed sugar over arena_push. The macro you will actually use."}, + apiRow{"str8 / str8_cstr / str8_lit", "Make a counted string. None of them allocate."}, + apiRow{"str8_pushf / str8_push_copy", "The two that DO allocate — and so they take the arena."}, + apiRow{"Rng2F32 + rng2f32_intersect / _pad / _shift", "Rectangles, and the three things a layout does to them."}, + ), + ) +} + +// ---- build --------------------------------------------------------------- + +func cBuild() *VNode { + return docSection("build", "build", + prose("The build system is a C program, and the build system's build system is a C compiler. "+ + "build.h is a single header in the stb style: define BUILD_IMPLEMENTATION in one file, include "+ + "it, and write your build as a main() that shells out to a compiler. The lineage is nob.h — the "+ + "self-rebuild macro here is called GO_REBUILD_URSELF, which is a straight nod to it."), + + docSubheading("Bootstrapping"), + prose("You compile the build script once, by hand. After that you never compile it again, because "+ + "the first thing it does when it runs is compare its own source against its own executable — "+ + "and if the source is newer, it rebuilds itself, swaps the binary, and re-executes. Edit the "+ + "build script and just run it. It will notice."), + codeLang("terminal", "sh", buildBootstrapSnippet), + + note("On Windows you cannot overwrite a running .exe — so it doesn't", + "go_rebuild_urself RENAMES the current binary to build.exe.old, compiles the new one in its "+ + "place, and re-execs. If the compile FAILS, it renames the old one back. That is the whole "+ + "reason the dance exists, and it is the difference between a build script you can edit and "+ + "one that bricks itself the first time you make a typo."), + + docSubheading("Writing one"), + prose("GO_REBUILD_URSELF is the first line of main. Cmd is a growable argv you append to and run. "+ + "needs_rebuild compares timestamps, so a target whose inputs have not changed is skipped. That "+ + "is the whole of it — no rule syntax, no DSL, no dependency graph, because a C program already "+ + "has if-statements and for-loops and you already know how to write them."), + codeLang("build.c — the file you write", "c", buildUsageSnippet), + + note("What it deliberately does NOT do", + "There is no compiler detection: the compiler is cl.exe on Windows and cc everywhere else, "+ + "hard-coded, and only for the self-rebuild — for your own targets you assemble the command "+ + "yourself. There is no parallelism: cmd_run is synchronous, start to finish. There is no "+ + "globbing and no header-dependency scanning: needs_rebuild compares mtimes against the input "+ + "list YOU pass it, so if you edit a header that is not in that list, nothing rebuilds. Know "+ + "that going in and it is a fine tool; expect make and you will be bitten."), + + docSubheading("What else is in the header"), + prose("More than a build system strictly needs, and all of it is there because a real build wanted "+ + "it. A temp allocator — an 8 MB ring buffer that silently wraps, so temp_sprintf can hand you a "+ + "formatted path that you never free. A string builder. File I/O that reports its own errors. "+ + "And two functions that are squarely about shipping a graphical program: embed_file, which "+ + "turns any binary into a C array in a header, and compile_shader / embed_spirv, which run glslc "+ + "over a GLSL source and embed the SPIR-V the same way. That last pair is a fossil of a Vulkan "+ + "renderer, and it is the clearest evidence in the layer of what codeMAX was becoming."), + + apiTable( + apiRow{"GO_REBUILD_URSELF(argc, argv)", "First line of main. Rebuilds and re-executes the script if its source changed."}, + apiRow{"Cmd + cmd_append + cmd_run", "A growable argv, appended variadically, run synchronously. cmd_run resets it for reuse."}, + apiRow{"needs_rebuild(out, inputs, n)", "1 if any input is newer than the output, 0 if up to date, -1 on error. Mtimes only."}, + apiRow{"temp_sprintf / temp_reset", "Formatted strings from a ring buffer. You never free them; you reset the ring."}, + apiRow{"sb_read_file / write_entire_file", "Whole-file I/O into a String_Builder, and back out."}, + apiRow{"embed_file(in, out, var)", "Turn a binary into a C header: an array, and its size."}, + apiRow{"compile_shader / embed_spirv", "glslc a .glsl to .spv, then embed the .spv as a C array."}, + ), + ) +} + +// ---- platform ------------------------------------------------------------ + +func cPlatform() *VNode { + return docSection("platform", "platform", + prose("The operating system, behind one header — and it is a narrower header than the name "+ + "suggests. There is no file I/O here, no clock, no threads, and no virtual memory (which is why "+ + "the arena is malloc-backed). What there is: a window, the input that arrives at it, the "+ + "clipboard, and a way to spawn a terminal."), + prose("Input comes in two shapes on purpose. PlatformRawInput is what the OS actually said — UTF-16 "+ + "characters, virtual key codes, mouse coordinates. PlatformInput is what the application wants "+ + "to hear. platform_adapt_input converts one into the other, and that function is the seam a "+ + "second backend would be written behind."), + + note("It expects Dear ImGui in the build, and Windows underneath", + "platform_win32.c is the only backend, and its window procedure forward-declares "+ + "ImGui_ImplWin32_WndProcHandler and calls it on every message BEFORE it looks at any of "+ + "them — so ImGui gets first refusal on the input, which is what makes an ImGui text field "+ + "inside the window behave like a text field rather than a hole the editor's keybindings "+ + "fall through. To use this subsystem you vendor ImGui and compile it (it is C++) alongside; "+ + "to use it WITHOUT ImGui you cut that one call, and everything else in the file is C and "+ + "stands. A second backend would replace the PKEY_ enum — whose values ARE Windows virtual "+ + "key codes, passed straight through — and find a home for platform_spawn_terminal, which "+ + "hard-codes cmd.exe /k."), + + docSubheading("Two decisions worth stealing"), + prose("WM_SIZE calls the frame callback synchronously, from inside the window procedure. That looks "+ + "wrong and is the standard Win32 fix for a real problem: while you drag a window's edge, "+ + "Windows runs a modal resize loop that never returns to your main loop, so the window goes "+ + "blank or smears. Rendering from inside the message handler is the only way to keep drawing. "+ + "platform_set_frame_callback exists for exactly this."), + prose("And platform_get_input drains its accumulator on read — it memsets the buffered keys and "+ + "characters to zero on the way out, and returns was_mouse_down beside mouse_down so the caller "+ + "can see an edge without keeping its own copy of last frame."), + + apiTable( + apiRow{"platform_create_window / _destroy_window", "A window from a PlatformWindowDesc; and its teardown."}, + apiRow{"platform_poll_events", "Pump the message queue. False means the user wants to close."}, + apiRow{"platform_get_input / platform_adapt_input", "The raw OS input for this frame; and the conversion that is the portability seam."}, + apiRow{"platform_set_frame_callback", "Called per frame — including from inside a live resize, which is the point."}, + apiRow{"platform_get_dpi_scale", "GetDpiForWindow / 96. The layer is per-monitor DPI aware throughout."}, + apiRow{"platform_clipboard_get / _set", "Get returns a pointer into a static 64 KB buffer. Do not free it, do not keep it."}, + apiRow{"platform_get_native_handle", "The HWND, for the one place that genuinely needs it."}, + ), + ) +} + +// ---- lexer --------------------------------------------------------------- + +func cLexer() *VNode { + return docSection("lexer", "lexer", + prose("Syntax highlighting, and the most complete thing in this layer — about 2,800 lines of it. "+ + "The interesting decision is the output shape, and the header credits it to the Focus editor: a "+ + "tokenizer here does not return a list of tokens. It takes the buffer and an out_tokens array "+ + "of the SAME LENGTH, and paints one token-type byte per source byte."), + codeLang("c/lexer/lexer.h", "c", lexerSnippet), + prose("That sounds wasteful and is the opposite. An editor never wants to know what the tokens are; "+ + "it wants to know what colour to draw the character at offset N — and with a parallel array "+ + "that is one indexed read, not a search through a token list. The enum value IS the index into "+ + "the theme's colour table, which is why TOK_DEFAULT has to be zero. Painting is a memset. The "+ + "cost is one byte per byte of source, for a file you already have in memory."), + + note("It lets you rewrite the past, which is why it can find function names", + "All five backends share one trick. When the tokenizer emits a `(` and the PREVIOUS token was "+ + "an identifier, it goes back and re-paints that identifier as TOK_FUNCTION. With a token "+ + "list you would have to look ahead, or fix up afterwards. With a paint array the past is "+ + "just an array range, and repainting it costs a memset."), + + docSubheading("Languages"), + prose("Five backends — C, Go, JavaScript, Lua, SQL — plus plain text, each a single .c file behind "+ + "the same LexerTokenizeFn signature, selected by a hard-coded switch. There is no registry and "+ + "no plugin: adding a language means editing four things in lexer.c, deliberately."), + prose("They are not toys. The JavaScript one carries an explicit depth stack for nested tagged "+ + "template literals, so an html`…${css`…${x}`}…` lexes correctly at arbitrary nesting, and it "+ + "has a regex-versus-division heuristic — after a keyword or an operator a slash starts a regex, "+ + "after an identifier it is a divide. The Lua one counts the equals signs in a long bracket so "+ + "[==[ is closed by ]==] and not by ]]. The SQL one is case-insensitive and treats \"quoted\" as "+ + "an identifier rather than a string."), + + docSubheading("Themes"), + prose("A theme is a colour per token type, and there are thirteen: Default, Default Light, Focus, "+ + "Handmade Hero, Witness Classic, Witness, VS Classic, RAD Debugger, 4coder, Ryan Fleury, Gruber "+ + "Darker, VS Dark, Freshcut Contrast. The file is pure data — not one function in five hundred "+ + "lines."), + note("The Theme struct is a fossil, and you can date the rock", + "Every theme carries a 24-bit colour table AND a 256-colour ANSI fallback, with a use_truecolor "+ + "flag — because this started life in a TERMINAL editor, where you cannot assume truecolor. "+ + "But the platform layer beside it is a DPI-aware Win32 GUI window driven by ImGui, and the "+ + "struct has since grown status_bg, minibuffer and file-browser colours that mean nothing to "+ + "a terminal. It is a terminal-era struct with GUI-era fields bolted on. That is a seam, not "+ + "a design."), + + apiTable( + apiRow{"LexerTokenizeFn(data, len, out_tokens)", "The one signature every backend implements. One byte of token type per byte of source."}, + apiRow{"lexer_get_tokenize_fn(lang)", "The backend for a language. A switch, not a registry."}, + apiRow{"lexer_detect_lang(filename)", "Language from the file extension."}, + apiRow{"Tokenizer + tokenizer_init / _eat_whitespace", "The shared cursor the backends are written against."}, + apiRow{"g_themes / theme_active()", "Thirteen built-in themes, and the active one."}, + ), + ) +} + +// ---- config -------------------------------------------------------------- + +func cConfig() *VNode { + return docSection("config", "config", + prose("An INI file — [sections], key = value — that lives NEXT TO THE EXECUTABLE rather than in a "+ + "home directory, which makes the program portable: copy the folder, keep your settings. It "+ + "parses into one global Config struct and saves back out of it."), + codeLang("c/config/config.h", "c", configSnippet), + + prose("The struct is the schema, and it is a fixed-size one: ten recent directories, every path a "+ + "flat 1024 bytes. Nothing in this subsystem allocates — which is what lets it be loaded before "+ + "an arena exists. If the file is absent, config_load writes a default one."), + + note("It stores what the PROGRAM knows, not what the user wants", + "The header is explicit about the split, and it is a good one: this file is managed by the "+ + "program — your window size, your recent folders, your active project — and per-project "+ + "settings belong in .editorconfig, where other tools can read them. A config file that tries "+ + "to be both ends up being neither."), + + note("It needs a writable directory beside the executable", + "That is the one requirement the design carries, and it is easy to miss: the file is written "+ + "NEXT TO the .exe, and config_save does not check whether the write succeeded. Run out of a "+ + "folder you can write to — which is what \"copy the folder, keep your settings\" means — and "+ + "it does exactly what it says. Install the same binary into C:\\Program Files and a "+ + "non-elevated process cannot write there, so the save quietly does nothing. If you ship it "+ + "through the installer, either keep the config beside the exe and expect an elevated write, "+ + "or point config_get_path at %APPDATA% and give up the portability."), + + apiTable( + apiRow{"config_load / config_save", "Read the INI into g_config; write g_config back out. Writes defaults if the file is absent."}, + apiRow{"config_get_path", "Beside the executable — GetModuleFileNameA, then strip the filename."}, + apiRow{"config_push_recent_dir", "Dedupes case-insensitively, moves the entry to the front, caps at ten, and saves itself."}, + apiRow{"g_config", "The one global. There is no second one."}, + ), + ) +} + +// ---- installer ----------------------------------------------------------- + +func cInstaller() *VNode { + return docSection("installer", "installer", + prose("A Windows installer AND uninstaller, in one executable, with the thing it installs embedded "+ + "inside it as a resource. No NSIS, no WiX, no MSI: it is a Win32 property-sheet wizard — "+ + "welcome, directory, progress, finish — that extracts the payload, copies itself to "+ + "uninstall.exe, optionally appends itself to the system PATH, writes a Start Menu shortcut via "+ + "COM, and registers under HKLM so it shows up in Add/Remove Programs."), + prose("installer.manifest is what makes Windows ask for elevation UP FRONT rather than failing on "+ + "the first write to Program Files. installer.h exists solely so the resource script and the C "+ + "agree on the resource ids."), + + note("The wizard has no dialog resources — it writes the DLGTEMPLATEs by hand, at runtime", + "There is a small serializer in installer.c that assembles the Win32 DLGTEMPLATE and "+ + "DLGITEMTEMPLATE binary layout byte by byte, alignment padding and all, and hands the result "+ + "to the property sheet with PSP_DLGINDIRECT. Which means the installer's entire user "+ + "interface needs no resource compiler: only the icon, the manifest and the payload go "+ + "through the .rc. That is either magnificent or deranged, and it is certainly deliberate."), + + note("A running .exe cannot delete itself, so the uninstaller doesn't", + "It spawns a detached cmd.exe that waits two seconds, deletes uninstall.exe, and removes the "+ + "directory. The process outlives the program that started it, which is the only way this can "+ + "be done on Windows."), + + docSubheading("Pointing it at your own product"), + prose("The .rc is the part you edit. It names codeMAX's icon, codeMAX's manifest and codeMAX's "+ + "payload, at the paths the original repository had them at — so adopting the installer means "+ + "pointing those three lines at your icon, your manifest and your executable, and changing the "+ + "product name and the HKLM key the uninstall entry is written under. Nothing else in installer.c "+ + "knows what it is installing."), + prose("There is also visible residue of a two-binary product that was collapsed into one, and it is "+ + "worth recognising rather than copying: IDR_EXE_TERMINAL is still declared though nothing embeds "+ + "or extracts it, do_install's numbered steps skip step 3, and the uninstaller still removes a "+ + "second executable and a shortcut that the installer no longer creates. Those are the lines to "+ + "delete on the way in."), + ) +} + +// ---- helpers ------------------------------------------------------------- + +// docSubheading is a heading INSIDE a section. The subsystems each have several parts, and +// six sections with no internal structure is a wall. +func docSubheading(text string) *VNode { + return H3(Attr("class", "mt-8 text-base font-semibold text-text-heading"), Text(text)) +} + +// ---- snippets ------------------------------------------------------------ +// +// Every one of these is copied from the source, except two — unityTUSnippet and +// buildUsageSnippet, which are the two files the layer expects its CONSUMER to write and +// so cannot be copied from a layer that has no consumer yet. Both are written against the +// real API and both are captioned as the file you write, not as a file that is here. +// +// If you change the source, change these. A snippet that has drifted from the code it +// claims to show is the most expensive documentation there is, because it is believed. + +const unityTUSnippet = `// The whole layer, as one translation unit — the only .c you hand to the +// compiler. Order matters: it is textual inclusion, not linking. +#include "base/base_inc.c" // core, arena, strings. Everything below needs it. + +// The five backends declare their helpers `+ "`internal`" + ` (file-static), so they +// belong in the SAME TU as the dispatch that calls them. +#include "lexer/lexer.c" +#include "lexer/lexer_c.c" +#include "lexer/lexer_go.c" +#include "lexer/lexer_js.c" +#include "lexer/lexer_lua.c" +#include "lexer/lexer_sql.c" + +// config.c uses Config without including its own header — which is exactly the +// thing a unity build is for. Put the header above it. +#include "config/config.h" +#include "config/config.c" + +// ...and then your own program, compiled with all of it: +#include "app/app.c"` + +const baseCoreSnippet = `// The three meanings of `+ "`static`" + ` in C, given three names. +#define internal static // a function private to this file +#define global static // a variable owned by this translation unit +#define local_persist static // a local that survives the call + +typedef uint8_t U8; typedef int8_t S8; +typedef uint32_t U32; typedef int32_t S32; +typedef uint64_t U64; typedef int64_t S64; +typedef S32 B32; // a boolean, sized so it packs predictably +typedef float F32; typedef double F64; + +#define KB(n) (((U64)(n)) << 10) +#define MB(n) (((U64)(n)) << 20)` + +const baseArenaSnippet = `typedef struct Arena { U8 *base; U64 pos; U64 cap; } Arena; +typedef struct Temp { Arena *arena; U64 pos; } Temp; + +// One malloc, of exactly cap. It does not grow and it does not chain. +Arena *arena_alloc(U64 cap); +void *arena_push(Arena *arena, U64 size); // zeroed +void *arena_push_no_zero(Arena *arena, U64 size); // not +void arena_pop_to(Arena *arena, U64 pos); + +#define push_array(arena, T, count) ((T *)arena_push((arena), sizeof(T) * (count))) + +// A scratch scope. Allocate as freely as you like inside it; none of it needs +// releasing, because temp_end rolls the cursor back over all of it at once. +Temp scratch = temp_begin(arena); +Node *nodes = push_array(arena, Node, 1024); +temp_end(scratch);` + +const baseStringsSnippet = `// A pointer and a length. Not NUL-terminated, not owned, does not allocate — +// so a substring is free, and a Str8 can point into a file you mapped. +typedef struct Str8 { const char *str; U64 size; } Str8; + +static inline Str8 str8_lit(const char *s); +static inline B32 str8_match(Str8 a, Str8 b); +static inline B32 str8_is_empty(Str8 s); + +// The two that must allocate take the arena, and so they say so: +Str8 str8_pushf(Arena *arena, const char *fmt, ...); +Str8 str8_push_copy(Arena *arena, Str8 s);` + +const buildBootstrapSnippet = `# Once, by hand. (On Windows, from a Visual Studio developer prompt — +# it shells out to cl.exe.) +cl /nologo build.c # Windows +cc build.c -o build # macOS / Linux + +# Ever after, just run it. If build.c is newer than the binary, the binary +# rebuilds itself, swaps in the new one, and re-executes: +./build` + +const buildUsageSnippet = `#define BUILD_IMPLEMENTATION +#include "build.h" + +int main(int argc, char **argv) { + // Renames the running binary to .old, recompiles, and re-execs. If the + // compile fails it puts the old one back — so a typo in your build + // script cannot brick your build script. + GO_REBUILD_URSELF(argc, argv); + + mkdir_if_not_exists("out"); + + // needs_rebuild compares MTIMES against the list you pass it. There is + // no header scanning: if a .h you depend on is not in this list, editing + // it will not trigger a rebuild. + const char *srcs[] = {"base/base_inc.c", "lexer/lexer.c"}; + if (needs_rebuild("out/app.exe", srcs, ARRAY_LEN(srcs)) > 0) { + Cmd cmd = {0}; + cmd_append(&cmd, "cl", "/nologo", "/I.", "/Fe:out/app.exe"); + cmd_append(&cmd, srcs[0], srcs[1]); + if (!cmd_run(&cmd)) return 1; // synchronous; resets cmd for reuse + cmd_free(&cmd); + } + + build_log(LOG_INFO, "done"); + return 0; +}` + +const lexerSnippet = `// A tokenizer does not RETURN tokens. It paints one token-type byte per +// source byte, into an array the same length as the buffer. +// +// The editor never asks "what are the tokens". It asks "what colour is the +// character at offset N" — and this answers that with one indexed read. +// The enum value IS the index into the theme's colour table, which is why +// TOK_DEFAULT has to be 0. +typedef void (*LexerTokenizeFn)(const char *data, S32 length, U8 *out_tokens); + +typedef enum Lang { LANG_PLAIN_TEXT, LANG_C, LANG_GO, LANG_JS, LANG_LUA, LANG_SQL } Lang; + +LexerTokenizeFn lexer_get_tokenize_fn(Lang lang); // a switch, not a registry +Lang lexer_detect_lang(const char *filename);` + +const configSnippet = `// The struct IS the schema, and it is fixed-size throughout: this subsystem +// allocates nothing, so it can be loaded before an arena exists. +typedef struct Config { + char theme[64]; + char recent_dirs[CONFIG_MAX_RECENT_DIRS][CONFIG_PATH_MAX]; // most recent first + S32 recent_dir_count; + B32 show_line_numbers; + B32 syntax_enabled; + F32 ui_scale; + char editor_font[64]; + char active_project[CONFIG_PATH_MAX]; +} Config; + +extern Config g_config; // the one global + +static void config_load(void); // /config.ini — not $HOME +static void config_save(void);` diff --git a/go/cmd/kjol-web/app/components.go b/go/cmd/kjol-web/app/components.go new file mode 100644 index 00000000..80d13313 --- /dev/null +++ b/go/cmd/kjol-web/app/components.go @@ -0,0 +1,1383 @@ +package app + +import ( + "strings" + "time" + + . "kjol/vdom" + "kjol/wasmruntime" + ui "kjol/webui" +) + +// The whole kit, on one page. +// +// It used to be three pages — "UI kit", "Overlays", "AutoTable" — which is a split +// along the lines of the SOURCE FILES rather than along anything a reader wants. A +// person looking for a date picker does not know, and should not have to guess, +// whether it was filed under forms or under overlays. So: one page, one scroll, and a +// sidebar of groups that jumps you to the one you are after. +// +// The cost is a long page and a lot of live components on it at once. That is the +// right trade for documentation you search with ctrl-F. + +// componentGroup is one section of the page AND one line in the sidebar. Declaring +// them once, as data, is what keeps those two in step — the sidebar cannot list a +// section that does not exist, and a section cannot go missing from the sidebar. +type componentGroup struct { + ID string // the anchor, and the docSection's element id + Label string + Icon string + Blurb string // shown on the /wasm index; too long for the sidebar +} + +func componentGroups() []componentGroup { + return []componentGroup{ + {ID: "buttons", Label: "Buttons", Icon: "check", + Blurb: "Colour, outline, size, icon. Segmented controls and link-buttons."}, + {ID: "badges", Label: "Badges & alerts", Icon: "circle-info", + Blurb: "Solid badges, tinted alerts, and the environment badge that shows nothing in production."}, + {ID: "cards", Label: "Cards & layout", Icon: "squares", + Blurb: "Cards, headers, dividers, crumb trails, a spinner, a code box."}, + {ID: "icons", Label: "Icons", Icon: "cube", + Blurb: "The kit draws its own: stroke paths in Go, no FontAwesome anywhere near it."}, + {ID: "forms", Label: "Forms & inputs", Icon: "pencil", + Blurb: "Controlled inputs, and masks that are idempotent because they are fed their own output."}, + {ID: "selects", Label: "Selects & comboboxes", Icon: "filter", + Blurb: "Combobox, multi-select with collapsing pills, and an async one that discards stale responses."}, + {ID: "toggles", Label: "Toggles & signature", Icon: "check", + Blurb: "Switches, and a signature pad whose value IS the SVG you drew."}, + {ID: "dates", Label: "Dates", Icon: "calendar", + Blurb: "A picker you can type into — 7/4/26, Jul 4 2026 and 2026-07-04 all work."}, + {ID: "tables", Label: "Tables", Icon: "table", + Blurb: "PrettyTable prints, CellGrid edits, AutoTable filters, sorts, calculates and exports."}, + {ID: "overlays", Label: "Overlays", Icon: "layers", + Blurb: "Tooltips, popovers, menus, modals — measured, portaled, flipped to fit."}, + {ID: "feedback", Label: "Toasts & tours", Icon: "bolt", + Blurb: "Self-dismissing toasts, a spotlight tour, and a flash for data somebody else changed."}, + {ID: "navigation", Label: "Tabs & navigation", Icon: "bars", + Blurb: "Tabs, CRM tabs, accordions, and a sidebar of jump links."}, + {ID: "search", Label: "Fuzzy search", Icon: "magnifying-glass", + Blurb: "Subsequence matching with typo tolerance, scored and highlighted."}, + {ID: "charts", Label: "Charts", Icon: "chart-column", + Blurb: "SVG drawn in Go, on the server — in the HTML before any WebAssembly loads."}, + {ID: "theming", Label: "Theming", Icon: "sun", + Blurb: "The token contract, and the switch. No component names a colour."}, + } +} + +//gowasm:page /wasm/components layout=app +func ComponentsPage(d Deps) func() *VNode { + // Shared across sections, so both the overlay menus and the toast buttons raise the + // same toasts — and there is one queue rather than two arguing about the corner. + toaster := ui.NewToaster(ui.ToasterOptions{Position: ui.ToastBottomRight}) + push := func(kind ui.ToastType, msg string) { toaster.Push(ui.Toast{Message: msg, Type: kind}) } + + // The tour targets sections BY CSS SELECTOR — the same ids the sidebar jumps to. + tour := ui.NewTutorial(ui.TutorialOptions{ + Steps: []ui.TutorialStep{ + {Title: "Buttons", Target: "#buttons", + Content: func() *VNode { return Text("The presentational end: a props struct in, a VNode out.") }}, + {Title: "Overlays", Target: "#overlays", + Content: func() *VNode { return Text("Measured against the real viewport, portaled out of the tree.") }}, + {Title: "Tables", Target: "#tables", + Content: func() *VNode { return Text("Filter, sort, calculate, export — from a column list.") }}, + {Title: "That's the tour", + Content: func() *VNode { return Text("Escape ends it. Arrow keys and Enter move between steps.") }}, + }, + }) + + // Every section is a CLOSURE built once, here. The controllers inside them (menus, + // modals, pickers, the table) own refs, timers and open state — build one inside the + // render and it is thrown away and rebuilt every frame, so it can never stay open. + sections := []func() *VNode{ + buttonsSection(), + badgesSection(), + cardsSection(), + iconsSection(), + formsSection(), + selectsSection(), + togglesSection(), + datesSection(), + tablesSection(), + overlaysSection(push), + feedbackSection(toaster, tour), + navigationSection(), + searchSection(), + chartsSection(), + themingSection(), + } + + return func() *VNode { + body := []*VNode{ + docSection("using", "Using a component", + prose("A component is a function taking a props struct. There is no class hierarchy and "+ + "nothing to register: a component is a value, so you can build one, store it, pass it "+ + "around, and the compiler will tell you when you got it wrong."), + prose("The floating ones — menus, modals, tooltips, pickers, the table — are CONTROLLERS "+ + "instead. They own refs, timers and open state, so you build them once alongside your "+ + "signals. Build one inside a render closure and it is discarded and rebuilt on every "+ + "frame, which means it can never stay open long enough for you to see it."), + code("app/components.go", kitSnippet), + note("Styling is Tailwind, compiled from your Go", + "The Tailwind engine scans .go files for class names, because that is where the markup "+ + "is. There is no JavaScript build anywhere in Kjøl Wasm Web — the CSS is "+ + "compiled by a Go program, from Go source."), + row("mt-4 flex gap-2", tour.StartButton(0, "", Text("Take the tour"))), + ), + } + for _, s := range sections { + body = append(body, s()) + } + + // Fixed-position hosts. Where they sit in the tree makes no difference to where + // they appear, so they go last, once. + body = append(body, toaster.Render(), tour.Render()) + + return docPage("Kjøl Wasm Web", "Components", + "Every component in kjol/webui, running. Not a screenshot of one anywhere: each block below "+ + "is the real component, rendered by the same Go that renders it in an application. Use the "+ + "sidebar to jump to a group.", + body...) + } +} + +// ---- buttons ------------------------------------------------------------- + +func buttonsSection() func() *VNode { + clicks := NewSignal(0) + span := NewSignal("week") + + return func() *VNode { + return docSection("buttons", "Buttons", + prose("Colour, outline, size and an optional icon, from one props struct. A button with an "+ + "icon and no text gets square padding rather than the wide pill a text button gets — so "+ + "an icon button is a square, not a lozenge with a picture rattling around in it."), + + demo("Colours", + row("flex flex-wrap items-center gap-2", + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Primary"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonGreen, Text: "Green"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Text: "Red"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Text: "Blue"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Text: "Neutral"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonWhite, Text: "White"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Light"}), + ), + ), + + demo("Outline, size, icon, disabled", + row("flex flex-wrap items-center gap-2", + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Outline: true, Text: "Outline"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Outline: true, Text: "Danger"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonGreen, Small: true, Icon: "check", Text: "Small + icon"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Icon: "plus"}), + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Disabled", Disabled: true}), + ), + ), + + demo("Click it — clicked "+itoa(clicks.Get())+" times", + row("flex flex-wrap items-center gap-3", + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Count", + OnClick: func() { clicks.Set(clicks.Get() + 1) }}), + ui.ButtonLink(func() { clicks.Set(0) }, Text("Reset (a link that is a button)")), + ui.BackLink("#components", "Back link", false), + ), + ), + + demo("Segmented control — selected: "+span.Get(), + ui.SegmentedButtons([]ui.SegmentedButtonOption{ + {Value: "day", Label: "Day"}, + {Value: "week", Label: "Week"}, + {Value: "month", Label: "Month"}, + }, span.Get(), func(v string) { span.Set(v) }, false, "max-w-xs"), + ), + ) + } +} + +// ---- badges & alerts ----------------------------------------------------- + +func badgesSection() func() *VNode { + return func() *VNode { + return docSection("badges", "Badges & alerts", + prose("A badge is a solid fill with white text, so it stays legible in both themes without "+ + "a variant on it. An alert is the opposite: a wash of colour behind dark text, which is "+ + "exactly the case a token cannot carry into dark mode — a red-50 tint is invisible on a "+ + "near-black surface — so alerts are one of only two places in the kit with a dark: rule."), + + demo("Badges", + row("flex flex-wrap items-center gap-2", + ui.Badge(ui.BadgeProps{Color: ui.BadgeGreen}, Text("active")), + ui.Badge(ui.BadgeProps{Color: ui.BadgeRed}, Text("failed")), + ui.Badge(ui.BadgeProps{Color: ui.BadgeBlue}, Text("info")), + ui.Badge(ui.BadgeProps{Color: ui.BadgeAmber, Pill: true}, Text("pending")), + ui.Badge(ui.BadgeProps{Color: ui.BadgeNeutral}, Text("default")), + ui.Badge(ui.BadgeProps{Color: ui.BadgeMuted}, Text("muted")), + ), + ), + + demo("Alerts", + row("flex flex-col gap-3", + ui.Alert(ui.AlertBlue, "Heads up", Text("An informational message with a header.")), + ui.Alert(ui.AlertGreen, "", Text("A success alert, with no header.")), + ui.Alert(ui.AlertYellow, "Warning", Text("Something needs your attention.")), + ui.Alert(ui.AlertRed, "Error", Text("Something went wrong.")), + ui.Alert(ui.AlertGray, "", Text("And a neutral one.")), + ), + ), + + demo("Environment badge", + row("flex flex-wrap items-center gap-3", + ui.EnvBadge("development"), + ui.EnvBadge("staging"), + // Production deliberately renders NOTHING — the badge exists to tell you that + // you are NOT in production, and a badge that is always there says nothing. + Span(Attr("class", "text-xs text-ink-muted"), + Text("(production renders nothing — that is the point of it)")), + ), + ), + ) + } +} + +// ---- cards & layout ------------------------------------------------------ + +func cardsSection() func() *VNode { + return func() *VNode { + return docSection("cards", "Cards & layout", + prose("The structural furniture: cards, headings, dividers, crumb trails, a spinner, and a "+ + "code box. Nothing here holds state — they are containers, and they take their children."), + + demo("Cards", + row("grid gap-4 sm:grid-cols-3", + ui.Card("", ui.CardHeader("", Text("Card")), + P(Attr("class", "text-sm text-ink-soft"), Text("Padded, and grows to fill its row."))), + ui.BorderCard("", ui.CardSubheader("", Text("BorderCard")), + P(Attr("class", "text-sm text-ink-soft"), Text("A border instead of a shadow."))), + ui.BorderCutCornerCard("", ui.CardSubheader("", Text("Cut corner")), + P(Attr("class", "text-sm text-ink-soft"), Text("The same, with a clipped corner."))), + ), + ), + + demo("Headers, dividers, crumbs", + row("flex flex-col gap-2", + ui.Breadcrumbs([]ui.BreadcrumbItem{ + {URL: "/", DisplayText: "kjol"}, + {URL: "/wasm", DisplayText: "Kjøl Wasm Web"}, + {URL: "/wasm/components", DisplayText: "Components"}, + }), + ui.PageHeader("A page header", ""), + ui.Divider(), + ui.ManagerPageHeader("With a description and an action", "The three-part page heading.", + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Small: true, Text: "New"})), + ), + ), + + demo("Loader, code box, links", + row("flex flex-wrap items-center gap-6", + ui.Loader(), + ui.PageLink("https://go.dev", true, "text-accent underline underline-offset-4", + Text("An external link (new tab)")), + ), + ui.CodeBox("go run ./server -build\ngo run ./server", "mt-3"), + ), + ) + } +} + +// ---- icons --------------------------------------------------------------- + +func iconsSection() func() *VNode { + return func() *VNode { + names := []string{ + "check", "xmark", "plus", "minus", "pencil", "trash", "filter", "download", "print", + "calendar", "envelope", "globe", "user", "book-open", "chart-column", "server", "table", + "code", "bolt", "cube", "shield-check", "sun", "moon", "magnifying-glass", "circle-info", + "circle-check", "circle-xmark", "triangle-exclamation", "chevron-down", "bars", "layers", + } + cells := []*VNode{} + for _, n := range names { + cells = append(cells, Div(Attr("class", "flex flex-col items-center gap-1.5 rounded-default border border-line px-2 py-3"), + ui.Icon(n, 18, "text-ink-soft"), + Span(Attr("class", "font-mono text-[10px] text-ink-faint"), Text(n)), + )) + } + + return docSection("icons", "Icons", + prose("The Go kit draws its own icons: a small registry of stroke paths, in Go, with no "+ + "FontAwesome anywhere near it. That is a deliberate difference from the Solid kit, which "+ + "generates a tree-shaken FontAwesome subset at build time — this side has no build step "+ + "to hang that off, so it carries the few dozen glyphs it actually uses."), + prose("An icon the registry has never heard of renders an empty box, and a test fails the "+ + "build over it rather than letting it ship."), + + demo("The registry", + row("grid grid-cols-4 gap-2 sm:grid-cols-8", cells...), + ), + + demo("Variants", + row("flex flex-wrap items-center gap-5", + ui.IconContainer(ui.Icon("check", 16, ""), Span(Attr("class", "text-sm text-ink-soft"), Text("IconContainer"))), + ui.IconSuccess("circle-check", 18, ""), + ui.IconError("circle-xmark", 18, ""), + Span(Attr("class", "inline-flex items-center gap-1.5 text-sm text-ink-soft"), + ui.IconInline("bolt", 14, ""), Text("IconInline sits on the text baseline")), + ), + ), + apiTable( + apiRow{"ui.Icon(name, size, class)", "The icon. Size is height in px; the width follows the viewBox."}, + apiRow{"ui.RegisterIcon", "Add your own. The kit ships a small set; an app brings the rest."}, + ), + ) + } +} + +// ---- forms --------------------------------------------------------------- + +func formsSection() func() *VNode { + name := NewSignal("") + email := NewSignal("") + notes := NewSignal("") + plan := NewSignal("pro") + taxID := NewSignal("") + rate := NewSignal("") + money := NewSignal("") + pct := NewSignal("") + phone := NewSignal("") + zip := NewSignal("") + + return func() *VNode { + field := func(label string, input *VNode) *VNode { + return row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text(label)), input) + } + + return docSection("forms", "Forms & inputs", + prose("Inputs are controlled: the value goes in as a prop, the change comes out as a "+ + "callback, and the caller owns the state. There is no two-way binding and nothing hidden "+ + "inside the component — which is why the same input works on the server, where there is "+ + "no one to type into it."), + + demo("Text, email, textarea, select", + row("grid gap-4 sm:grid-cols-2", + field("Name", ui.FormInput(ui.FormInputProps{Value: name.Get(), Placeholder: "Ada Lovelace", + OnInput: func(v string) { name.Set(v) }})), + field("Email", ui.FormEmailInput(ui.FormInputProps{Value: email.Get(), Placeholder: "ada@example.com", + OnInput: func(v string) { email.Set(v) }}, true)), + field("Plan", ui.FormSelect(ui.FormSelectProps{Value: plan.Get(), OnChange: func(v string) { plan.Set(v) }}, + ui.FormOption("free", "Free", false), + ui.FormOption("pro", "Pro", false), + ui.FormOption("enterprise", "Enterprise", false))), + field("Notes", ui.FormTextarea(ui.FormTextareaProps{Value: notes.Get(), Rows: "3", + Placeholder: "Anything worth remembering…", OnInput: func(v string) { notes.Set(v) }})), + ), + ), + + demo("Masked inputs — type letters into them", + row("grid gap-4 sm:grid-cols-3", + // A mask is a pure function of the string, applied on every keystroke. It must + // be IDEMPOTENT — it is fed its own output — or the field corrupts itself as + // you type, one character at a time, and only for fast typists. + field("Tax ID", ui.FormInput(ui.FormInputProps{Value: taxID.Get(), Placeholder: "12-3456789", + OnInput: func(v string) { taxID.Set(ui.MaskTaxID(v)) }})), + field("Rate", ui.FormInput(ui.FormInputProps{Value: rate.Get(), Placeholder: "5.25", + OnInput: func(v string) { rate.Set(ui.MaskRate(v)) }})), + field("Currency", ui.FormCurrencyInput(ui.FormInputProps{Value: money.Get(), + OnInput: func(v string) { money.Set(v) }}, false)), + field("Percent", ui.FormPercentInput(ui.FormInputProps{Value: pct.Get(), + OnInput: func(v string) { pct.Set(v) }})), + field("Phone", ui.FormPhoneInput(ui.FormInputProps{Value: phone.Get(), + OnInput: func(v string) { phone.Set(v) }})), + field("Zip", ui.FormZipCodeInput(ui.FormInputProps{Value: zip.Get(), + OnInput: func(v string) { zip.Set(v) }})), + ), + ), + + demo("Fieldset, file input, and a validation error", + ui.FormFieldset("Account", "", + row("grid gap-4 sm:grid-cols-2", + field("Attachment", ui.FormFileInput(ui.FormInputProps{})), + field("Email (validated)", ui.FormEmailInput(ui.FormInputProps{ + Value: email.Get(), + Error: emailError(email.Get()), + OnInput: func(v string) { email.Set(v) }, + }, true)), + ), + ), + ), + ) + } +} + +// emailError is derived, not stored — so it cannot fall out of step with the field it +// describes. Blank is not "invalid", it is unfilled. +func emailError(v string) string { + if v != "" && !ui.IsEmailValid(v) { + return "That is not an email address." + } + return "" +} + +// ---- selects & comboboxes ------------------------------------------------ + +func selectsSection() func() *VNode { + langs := NewSignal([]string{"go"}) + tags := NewSignal([]string{"go"}) + one := NewSignal("") + state := NewSignal("") + tz := NewSignal("") + picked := NewSignal("") + + combo := ui.NewCombobox(ui.DropdownOptions{}) + skills := ui.NewMultiSelect(ui.DropdownOptions{}) + tagPicker := ui.NewMultiSelectTrigger(ui.DropdownOptions{}) + + // The search is the CALLER's. The component knows how to debounce, order and render; + // it knows nothing at all about where options come from. Here it is a local slice; in + // an app it would be a fetch. + people := ui.NewAsyncCombobox(ui.AsyncComboboxOptions{ + MinChars: 2, + Search: func(q string, done func([]ui.FormSelectOption)) { + var out []ui.FormSelectOption + for _, r := range employees() { + p := emp(r) + if strings.Contains(strings.ToLower(p.Name), strings.ToLower(q)) { + out = append(out, ui.FormSelectOption{Value: p.Email, Label: p.Name}) + } + } + done(out) + }, + }) + + return func() *VNode { + field := func(label string, input *VNode) *VNode { + return row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text(label)), input) + } + + return docSection("selects", "Selects & comboboxes", + prose("A combobox picks one; a multi-select picks several, with checkboxes on the rows and "+ + "removable pills in the field. Past three selections — or once the pills stop fitting — "+ + "the field collapses to \"N items selected\" rather than growing until it wraps."), + + demo("Combobox, multi-select, and the built-in selectors", + row("grid gap-4 sm:grid-cols-2", + field("Language (searchable, one)", combo.Render(ui.FormComboboxProps{ + Options: languageOptions(), Value: one.Get(), Placeholder: "Pick one", + Searchable: true, OnChange: func(v string) { one.Set(v) }, + })), + field("Languages (several)", skills.Render(ui.FormMultiSelectProps{ + Options: languageOptions(), Value: langs.Get(), Placeholder: "Pick a few", + Searchable: true, ShowSelectAll: true, OnChange: func(v []string) { langs.Set(v) }, + })), + field("State", ui.FormStateSelector(ui.FormSelectProps{ + Value: state.Get(), OnChange: func(v string) { state.Set(v) }})), + field("Timezone", ui.FormTimezoneSelector(ui.FormSelectProps{ + Value: tz.Get(), OnChange: func(v string) { tz.Set(v) }})), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("one="+orElse(one.Get(), "—")+" several="+orElse(strings.Join(langs.Get(), ","), "—"))), + ), + + demo("Async combobox — picked: "+orElse(picked.Get(), "nothing yet"), + row("max-w-sm", + people.Render(ui.FormAsyncComboboxProps{ + Placeholder: "Search people…", + OnSelect: func(o ui.FormSelectOption) { picked.Set(o.Label + " <" + o.Value + ">") }, + }), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("Two characters before it asks; 200 ms after you stop typing. A response for a query "+ + "you have already typed past is DISCARDED rather than shown — which is the whole bug "+ + "with hand-rolled autocompletes.")), + ), + + demo("Multi-select behind your own trigger", + tagPicker.Render(ui.FormMultiSelectTriggerProps{ + Trigger: ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, + Icon: "filter", Text: "Tags (" + itoa(len(tags.Get())) + ")"}), + Options: languageOptions(), + Value: tags.Get(), + Searchable: true, + ShowSelectAll: true, + OnChange: func(v []string) { tags.Set(v) }, + }), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("Same selection model as the field above; only the thing you click on differs.")), + ), + ) + } +} + +// ---- toggles & signature ------------------------------------------------- + +func togglesSection() func() *VNode { + notify := NewSignal(true) + locked := NewSignal(false) + signed := NewSignal("") + + pad := ui.NewSignaturePad(ui.SignaturePadOptions{ + OnChange: func(svg string) { signed.Set(svg) }, + }) + + return func() *VNode { + return docSection("toggles", "Toggles & signature", + prose("A toggle is a checkbox that admits what it is. The signature pad is an SVG rather "+ + "than a canvas, which is not a detail: the markup you draw IS the value the caller gets, "+ + "so a stored signature renders on the server like any other markup."), + + demo("Toggles", + row("flex flex-col gap-4", + ui.ToggleSwitch(notify.Get(), func(v bool) { notify.Set(v) }, + "Email notifications", "At most one message a day.", false, ""), + ui.ToggleSwitch(locked.Get(), func(v bool) { locked.Set(v) }, + "Locked", "This one is disabled.", true, ""), + ), + ), + + demo("Signature pad — "+itoa(len(signed.Get()))+" bytes of SVG", + pad.Render(ui.SignaturePadProps{}), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("Draw in it. Long strokes are smoothed; a single stray point is not a stroke and "+ + "is dropped, so a click does not leave a dot behind.")), + ), + ) + } +} + +// ---- dates --------------------------------------------------------------- + +func datesSection() func() *VNode { + picked := NewSignal("") + day := NewSignal("") + month := NewSignal(time.Time{}) + + dp := ui.NewDatePicker(ui.DatePickerProps{ + Placeholder: "Pick a date", + Clearable: true, + OnChange: func(v string) { picked.Set(v) }, + }) + dob := ui.NewDateOfBirthPicker(ui.DatePickerProps{Placeholder: "Date of birth"}) + + return func() *VNode { + return docSection("dates", "Dates", + prose("The field is TYPEABLE, not merely clickable. It parses loosely — 7/4/26, Jul 4 2026 "+ + "and 2026-07-04 all work — and commits what it understood on blur, so the calendar is an "+ + "affordance rather than the only way in. A picker you can only click is a picker that is "+ + "slower than the keyboard for everyone who knows the date already."), + + demo("Pickers — picked: "+orElse(picked.Get(), "nothing"), + row("grid gap-4 sm:grid-cols-2", + row("flex flex-col gap-1", + ui.FormLabel(ui.FormLabelProps{}, Text("Date (portaled; flips near the bottom)")), + dp.Render()), + row("flex flex-col gap-1", + ui.FormLabel(ui.FormLabelProps{}, Text("Date of birth (inline, three selects)")), + dob.Render()), + ), + ), + + demo("The calendar on its own — selected: "+orElse(day.Get(), "none"), + row("max-w-xs", + ui.Calendar(ui.CalendarProps{ + Selected: day.Get(), + ViewMonth: month.Get(), + OnSelect: func(k string) { day.Set(k) }, + OnNavigate: func(m time.Time) { month.Set(m) }, + }), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("The same grid the picker drops down, usable directly when you want it inline.")), + ), + ) + } +} + +// ---- tables -------------------------------------------------------------- + +func tablesSection() func() *VNode { + highlight := NewSignal("") + + table := newEmployeeTable(highlight) + table.SetRows(employees()) + + exportMenu := ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd}) + pdfSub := ui.NewSubmenu(exportMenu) + + // CellGrid is the editable one: a spreadsheet, not a report. + gridRows := NewSignal([]map[string]any{ + {"id": "1", "sku": "KJ-100", "qty": 12, "price": "$4.50"}, + {"id": "2", "sku": "KJ-220", "qty": 3, "price": "$18.00"}, + {"id": "3", "sku": "KJ-330", "qty": 47, "price": "$1.25"}, + }) + gridSort := NewSignal("sku") + gridDesc := NewSignal(false) + + pdfHeader := func(landscape bool) ui.AutoTablePDFHeader { + orientation := ui.PDF_ORIENTATION_PORTRAIT + if landscape { + orientation = ui.PDF_ORIENTATION_LANDSCAPE + } + return ui.AutoTablePDFHeader{ + Title: "Employees", + Subtitle: "Exported from the kjol-web components page", + ShowDate: true, + Orientation: orientation, + } + } + + return func() *VNode { + return docSection("tables", "Tables", + prose("Three of them, and the difference is what the user is allowed to do. PrettyTable "+ + "prints rows. CellGrid lets them be edited in place. AutoTable filters, sorts, pages, "+ + "reorders, resizes, computes and exports — configured with a column list and a slice of "+ + "rows, and everything the user changes about it is theirs and persists."), + + // --- PrettyTable + demo("PrettyTable — it prints rows, and that is all", + ui.PrettyTable( + []ui.PrettyTableColumn{ + {DisplayName: "Name"}, + {DisplayName: "Plan"}, + {DisplayName: "Status", DisplayPosition: ui.PrettyTableColRight}, + }, + ui.PrettyTableOptions{Hover: true, Alternate: true, SurroundingBorder: true, HeaderBorderY: true}, + ptRow("Ada Lovelace", "Pro", ui.Badge(ui.BadgeProps{Color: ui.BadgeGreen}, Text("active"))), + ptRow("Alan Turing", "Free", ui.Badge(ui.BadgeProps{Color: ui.BadgeNeutral}, Text("trial"))), + ptRow("Grace Hopper", "Enterprise", ui.Badge(ui.BadgeProps{Color: ui.BadgeBlue}, Text("invited"))), + ), + ), + + // --- CellGrid + demo("CellGrid — editable cells", + ui.CellGrid(ui.CellGridProps{ + Columns: []ui.CellGridColumn{ + {Key: "sku", Label: "SKU", SortKey: "sku"}, + {Key: "qty", Label: "Qty", SortKey: "qty", SortType: ui.SortTypeNumeric, Editable: true}, + {Key: "price", Label: "Price", SortKey: "price", SortType: ui.SortTypeMoney, Editable: true}, + }, + Rows: gridRows.Get(), + IDField: "id", + OnCellChange: func(id any, field string, v any) { + rows := gridRows.Get() + next := make([]map[string]any, len(rows)) + for i, r := range rows { + cp := map[string]any{} + for k, val := range r { + cp[k] = val + } + if cp["id"] == id { + cp[field] = v + } + next[i] = cp + } + gridRows.Set(next) + }, + SortKey: gridSort.Get(), + SetSortKey: func(k string) { gridSort.Set(k) }, + SortDesc: gridDesc.Get(), + SetSortDesc: func(b bool) { gridDesc.Set(b) }, + }), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("Click a Qty or Price cell and type. The grid does not own the rows — it tells you "+ + "which cell changed and hands the value back; what you do with it is yours.")), + ), + + // --- AutoTable + prose("AutoTable is the big one. Search matches name or email. Sort by Salary and it parses "+ + "the currency, so $980 sorts below $1,200.50. Unhide Rank and sort that: \"Item 2\" comes "+ + "before \"Item 10\", because numbers inside text are compared as numbers. Drag a header to "+ + "reorder it, drag its right edge to resize — reload the page and both are still where you "+ + "left them."), + prose("Then filter it and export. You get every matching row across every page, in the column "+ + "order you dragged them into, with the calculated columns computed per row — not the five "+ + "rows that happened to be on screen."), + + table.Render( + ui.AutoTableWithHover(), + ui.AutoTableWithAlternate(), + ui.AutoTableWithSurroundingBorder(), + ui.AutoTableWithPaginationShowAll(), + ui.AutoTableWithSearchFields( + table.GlobalSearch("Search name or email…", "Name", "Email"), + table.SelectSearch("Status", []string{"active", "inactive"}, "Any status"), + table.MultiSelectSearch("Team", "Any team", []string{"Engineering", "Research", "Networking"}), + ), + ui.AutoTableWithToolbarActions( + table.ColumnPicker(), + table.CalculatedColumnEditor(), + exportMenu.TriggerFunc(ui.MenuTriggerProps{Tag: "div"}, func(open bool) *VNode { + return ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, + Icon: "download", Text: "Export"}) + }), + exportMenu.Content("", + exportMenu.Item(ui.MenuItemProps{Icon: "file-csv", + OnClick: func() { table.DownloadCSV("employees") }}, Text("Download CSV")), + pdfSub.Submenu(ui.SubmenuProps{Trigger: "Download PDF", Icon: "file-pdf"}, + pdfSub.Item(ui.MenuItemProps{ + OnClick: func() { table.DownloadPDF("employees", pdfHeader(false)) }}, Text("Portrait")), + pdfSub.Item(ui.MenuItemProps{ + OnClick: func() { table.DownloadPDF("employees", pdfHeader(true)) }}, Text("Landscape")), + ), + ui.MenuDivider(""), + exportMenu.Item(ui.MenuItemProps{Icon: "print", + OnClick: func() { table.PrintPDF(pdfHeader(true)) }}, Text("Print")), + ), + ), + ), + + row("mt-4 flex flex-wrap items-center gap-2", + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, Text: "Find Radia Perlman", + OnClick: func() { highlight.Set("radia@example.com") }}), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, Text: "Clear highlight", + OnClick: func() { highlight.Set("") }}), + ), + + codeLang("formulas", "syntax", formulaSnippet), + note("The server renders a skeleton, on purpose", + "The layout — column order, widths, what is hidden, the calculated columns — lives in the "+ + "browser's localStorage, which the server cannot read. So the server ships a skeleton "+ + "rather than the DEFAULT table: a user who had reordered their columns would otherwise "+ + "watch them rearrange themselves the moment the WebAssembly booted."), + + apiTable( + apiRow{"NewAutoTableState", "The controller: the columns, and where to persist the layout."}, + apiRow{".SetRows", "Hand it the data. It filters, sorts and pages from there."}, + apiRow{".RestoreLayout", "Read the saved layout and reveal the table over its skeleton. Once, on the client."}, + apiRow{".FilteredRows / .ExportColumns", "What the user selected, and what they can see — the inputs to any export."}, + apiRow{"ExportCSV / ExportPDF", "Write the bytes, in Go, stdlib only. The PDF writer builds its own xref table."}, + ), + ) + } +} + +// ---- overlays ------------------------------------------------------------ + +func overlaysSection(push func(ui.ToastType, string)) func() *VNode { + tipTop := ui.NewHoverTooltip(ui.PlacementTop, "") + tipRight := ui.NewHoverTooltip(ui.PlacementRight, "") + tipFocus := ui.NewFocusTooltip(ui.PlacementBottom, "") + tipFast := ui.NewTooltip(ui.TooltipProps{Placement: ui.PlacementTop, Delay: -1}) + + pop := ui.NewPopover(ui.PopoverProps{Placement: ui.PlacementBottomStart}) + popEnd := ui.NewPopover(ui.PopoverProps{Placement: ui.PlacementBottomEnd}) + hoverPop := ui.NewHoverPopover(ui.HoverPopoverProps{ + Placement: ui.PlacementTop, + // The bridge: 300ms of grace for the cursor to cross the gap from trigger onto + // panel. Without it the panel closes in the dead space between them — which is + // exactly what happens once a panel is portaled and CSS :hover no longer reaches it. + HoverCloseDelay: 300, + }) + + menu := ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomStart}) + sub := ui.NewSubmenu(menu) + hoverMenu := ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomStart, OpenOnHover: true}) + + modal := ui.NewModal(ui.ModalOptions{Size: ui.ModalMedium}) + nested := ui.NewModal(ui.ModalOptions{Size: ui.ModalSmall}) + confirm := ui.NewModal(ui.ModalOptions{}) + deleted := NewSignal(false) + + wizardName := NewSignal("") + wizard := ui.NewWizard(ui.ModalOptions{}) + + return func() *VNode { + return docSection("overlays", "Overlays", + prose("Tooltips, popovers, menus and modals — every one measured against the real viewport. "+ + "A floating panel is portaled to document.body, positioned from its trigger's bounding "+ + "box, and flipped or shifted when it would otherwise run off the screen."), + prose("The position is written to the element with SetStyle, NOT through a signal. A signal "+ + "write re-renders the whole tree, and this runs on every scroll and resize frame; going "+ + "through the vdom would rebuild the page sixty times a second to move one panel four "+ + "pixels."), + code("webui/floating.go", floatingSnippet), + + demo("Tooltips — hover, focus, placement, delay", + row("flex flex-wrap items-center gap-3", + tipTop.Render(Span(Text("Above — the default")), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Top"})), + tipRight.Render(Span(Text("To the right, unless it would run off the edge")), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Right"})), + tipFast.Render(Span(Text("No open delay")), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Instant"})), + tipFocus.Render(Span(Text("Shown on FOCUS, not hover — tab to the field")), + ui.FormInput(ui.FormInputProps{Placeholder: "Focus me"})), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("Narrow the window and hover \"Right\": it flips to the left, and its arrow follows "+ + "it. A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.")), + ), + + demo("Popovers — click, alignment, and a hover bridge", + row("flex flex-wrap items-center gap-3", + pop.Trigger(ui.PopoverTriggerProps{}, + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Click me"})), + pop.Content(ui.PopoverContentProps{Class: "w-64"}, + P(Attr("class", "text-sm text-ink-soft"), + Text("Click outside, or press Escape. Only the TOPMOST floating panel closes per press."))), + + popEnd.Trigger(ui.PopoverTriggerProps{}, + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Aligned to my right edge"})), + popEnd.Content(ui.PopoverContentProps{Class: "w-56"}, + P(Attr("class", "text-sm text-ink-soft"), Text("Placement bottom-end."))), + + hoverPop.Trigger(ui.PopoverTriggerProps{}, + ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Outline: true, Text: "Hover, then reach the panel"})), + hoverPop.Content(ui.PopoverContentProps{Class: "w-64"}, + P(Attr("class", "text-sm text-ink-soft"), + Text("Move the cursor across the gap and onto this panel — it stays open. Select "+ + "this text to prove it."))), + ), + ), + + demo("Menus & submenus", + row("flex flex-wrap items-center gap-3", + menu.TriggerFunc(ui.MenuTriggerProps{Tag: "div"}, func(open bool) *VNode { + caret := " ▾" + if open { + caret = " ▴" + } + return ui.Button(ui.ButtonProps{Color: ui.ButtonWhite, Text: "Actions" + caret}) + }), + menu.Content("", + menu.Item(ui.MenuItemProps{Icon: "check", OnClick: func() { push(ui.ToastSuccess, "Profile opened") }}, + Text("Profile")), + menu.Item(ui.MenuItemProps{OnClick: func() { push(ui.ToastInfo, "Settings opened") }}, + Text("Settings")), + // Portaled — it used to be clipped by the parent menu's own overflow-y-auto. + sub.Submenu(ui.SubmenuProps{Trigger: "More", Icon: "ellipsis"}, + sub.Item(ui.MenuItemProps{OnClick: func() { push(ui.ToastInfo, "Archived") }}, Text("Archive")), + sub.Item(ui.MenuItemProps{OnClick: func() { push(ui.ToastWarning, "Duplicated") }}, Text("Duplicate")), + ), + ui.MenuDivider(""), + menu.Item(ui.MenuItemProps{KeepOpen: true, OnClick: func() { push(ui.ToastGeneric, "Menu stayed open") }}, + Text("Stay open (KeepOpen)")), + menu.Item(ui.MenuItemProps{Icon: "arrow-right-from-bracket", + OnClick: func() { push(ui.ToastError, "Signed out") }}, Text("Sign out")), + ), + + hoverMenu.TriggerFunc(ui.MenuTriggerProps{Tag: "div"}, func(bool) *VNode { + return ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Opens on hover"}) + }), + hoverMenu.Content("", + hoverMenu.Item(ui.MenuItemProps{}, Text("One")), + hoverMenu.Item(ui.MenuItemProps{}, Text("Two")), + ), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("Opening one closes the other — a single-open manager. Submenus are exempt, or a "+ + "submenu would close the very menu it belongs to as it opened. The items raise toasts, "+ + "which is how you can see that an item really does close its own menu.")), + ), + + demo("Modals — deleted: "+boolStr(deleted.Get()), + row("flex flex-wrap items-center gap-3", + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Open modal", OnClick: modal.Open}), + ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Outline: true, Text: "Delete something…", OnClick: confirm.Open}), + ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Outline: true, Text: "Open wizard", OnClick: wizard.Open}), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Open imperatively", + OnClick: func() { + // No component in the tree owns this one: OpenModal hands content to the + // shared host rendered once in the layout. + ui.OpenModal(func() *VNode { + return ui.ModalContent(ui.ModalContentProps{ + Header: H3(Attr("class", "text-lg font-semibold text-text-heading"), Text("Opened from anywhere")), + }, + P(Attr("class", "text-ink-soft"), + Text("This content was rendered by no component — it was handed to ModalHost "+ + "(see AppLayout) by webui.OpenModal. That is what a confirmation raised "+ + "from inside a save handler needs.")), + ) + }, ui.ModalOptions{Size: ui.ModalSmall}) + }}), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("Open the modal, then the nested one inside it, and press Escape twice: modals unwind "+ + "ONE LAYER per press rather than all at once.")), + + modal.Render(ui.ModalProps{ + Header: H3(Attr("class", "text-lg font-semibold text-text-heading"), Text("A modal")), + Footer: ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Text: "Close", OnClick: modal.Close}), + }, + P(Attr("class", "text-ink-soft"), + Text("Portaled to document.body, so no ancestor's overflow:hidden or transform can clip "+ + "it. It fades and scales in over a double requestAnimationFrame — a single frame does "+ + "not give the browser time to commit the initial style, so the transition never runs.")), + row("mt-4", ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Outline: true, + Text: "Open a nested modal", OnClick: nested.Open})), + ), + nested.Render(ui.ModalProps{ + Header: H3(Attr("class", "text-lg font-semibold text-text-heading"), Text("Nested")), + }, + P(Attr("class", "text-ink-soft"), Text("Escape closes THIS one first, not the one behind it.")), + ), + confirm.Confirm(ui.ConfirmModalProps{ + Title: "Delete row", + Message: "This cannot be undone. (Nothing is actually deleted — this is a docs page.)", + OnConfirm: func() { deleted.Set(true); push(ui.ToastError, "Row deleted") }, + }), + wizard.Render(ui.WizardProps{ + Title: "Set up your account", + FinishText: "Finish", + OnComplete: func() { push(ui.ToastSuccess, "Wizard complete: "+wizardName.Get()) }, + Steps: []ui.WizardStep{ + { + Title: "Your name", + // Each step gets its own context: SetCanContinue gates THIS step's Next + // button, which one shared bool could not express. + Content: func(ctx ui.WizardStepContext) *VNode { + ctx.SetCanContinue(wizardName.Get() != "") + return row("flex flex-col gap-1", + ui.FormLabel(ui.FormLabelProps{}, Text("Name (required to continue)")), + ui.FormInput(ui.FormInputProps{Value: wizardName.Get(), Placeholder: "Ada Lovelace", + OnInput: func(v string) { wizardName.Set(v) }}), + ) + }, + }, + { + Title: "Confirm", + Content: func(ctx ui.WizardStepContext) *VNode { + ctx.SetCanContinue(true) + return P(Attr("class", "text-ink-soft"), + Text("All set for "+orElse(wizardName.Get(), "nobody")+". Finish to close.")) + }, + }, + }, + }), + ), + + apiTable( + apiRow{"NewFloating", "The engine behind every panel: placement, offset, flip, shift, arrow."}, + apiRow{"NewTooltip / NewPopover / NewMenu / NewModal", "Controllers. Build once, outside the render."}, + apiRow{"Standalone", "Exempts a panel from the single-open manager. A submenu needs it, or it closes its own parent."}, + apiRow{"vdom.Portal", "Mounts children at document.body — the escape hatch from an ancestor's overflow:hidden."}, + apiRow{"webui.OpenModal / ModalHost", "Open a modal from code that owns no component. Render the host once, in your layout."}, + ), + ) + } +} + +func boolStr(b bool) string { + if b { + return "true" + } + return "false" +} + +// ---- feedback ------------------------------------------------------------ + +func feedbackSection(toaster *ui.Toaster, tour *ui.Tutorial) func() *VNode { + flash := ui.NewRemoteFlash(2000) + + return func() *VNode { + return docSection("feedback", "Toasts & tours", + prose("Toasts dismiss themselves after five seconds. Watch the bar count down: it is one CSS "+ + "transition, written straight at the element — not a re-render per frame, which is what a "+ + "progress bar driven through a signal would cost you."), + + demo("Push, dismiss, and a sticky one", + row("flex flex-wrap items-center gap-2", + ui.Button(ui.ButtonProps{Color: ui.ButtonGreen, Small: true, Text: "Success", + OnClick: func() { toaster.Success("Saved.") }}), + ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Small: true, Text: "Error", + OnClick: func() { toaster.Error("Something went wrong.") }}), + ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Small: true, Text: "Info", + OnClick: func() { toaster.Info("Just so you know.") }}), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, Text: "Sticky (no timer)", + OnClick: func() { + toaster.Push(ui.Toast{Message: "This one waits for you to dismiss it.", + Type: ui.ToastWarning, Duration: ui.ToastSticky}) + }}), + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, Text: "Clear all", + OnClick: toaster.Clear}), + ), + ), + + demo("The guided tour", + row("flex flex-wrap items-center gap-3", + tour.StartButton(0, "", Text("Take the tour")), + Span(Attr("class", "text-xs text-ink-muted"), + Text("It dims the page, cuts a hole around each target, and animates the spotlight from "+ + "one to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to.")), + ), + ), + + demo("Remote update flash", + row("flex flex-wrap items-center gap-3", + ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, Text: "Something changed elsewhere", + OnClick: flash.Fire}), + ui.RemoteUpdateFlash(flash.Visible()), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("A two-second acknowledgement that data you are looking at was changed by somebody "+ + "else. It is not a toast: it belongs next to the thing that moved, not in the corner.")), + ), + ) + } +} + +// ---- navigation ---------------------------------------------------------- + +func navigationSection() func() *VNode { + tab := NewSignal(0) + crm := NewSignal(0) + crmSub := NewSignal(0) + acc := NewSignal(0) + multi := NewSignal([]bool{true, false, false}) + side := NewSignal("buttons") + + return func() *VNode { + panel := func(s string) *VNode { + return P(Attr("class", "pt-3 text-sm text-ink-soft"), Text(s)) + } + + return docSection("navigation", "Tabs & navigation", + prose("Tabs, an accordion, and a sidebar of jump links. All controlled: the active index goes "+ + "in, the change comes out, and the caller decides what that means — which is what lets a "+ + "tab be driven by the URL, or persisted, without the component knowing anything about it."), + + demo("Tabs", + ui.TabGroup(ui.TabGroupProps{ + Items: []ui.TabItem{ + {Title: "Overview", Content: panel("The overview panel.")}, + {Title: "Details", Content: panel("The details panel.")}, + {Title: "Activity", Badge: 3, Content: panel("The activity panel (3 new).")}, + }, + ActiveIndex: tab.Get(), + OnTabChange: func(i int) { tab.Set(i) }, + }), + ), + + demo("CRM tabs — the same thing, wearing a different suit", + row("flex flex-col gap-4", + ui.CrmTabGroup(ui.CrmTabGroupProps{ + Items: []ui.CrmTabItem{ + {Title: "Contacts", Badge: 12, Content: panel("A contacts list would go here.")}, + {Title: "Deals", Badge: 3, Content: panel("And the deals.")}, + {Title: "Notes", Content: panel("And the notes.")}, + }, + ActiveIndex: crm.Get(), + OnTabChange: func(i int) { crm.Set(i) }, + }), + ui.CrmSubTabGroup(ui.CrmTabGroupProps{ + Items: []ui.CrmTabItem{ + {Title: "All", Content: panel("The sub-tab strip: smaller, for nesting inside a tab.")}, + {Title: "Mine", Content: panel("Mine.")}, + }, + ActiveIndex: crmSub.Get(), + OnTabChange: func(i int) { crmSub.Set(i) }, + }), + ), + ), + + demo("Accordion — one open at a time, or several", + row("grid gap-6 lg:grid-cols-2", + row("flex flex-col gap-2", + P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-ink-faint"), + Text("SingleAccordion")), + ui.SingleAccordion([]ui.AccordionItemData{ + {Title: "What is Kjøl Wasm Web?", Content: panel("kjol's Go→WebAssembly UI engine.")}, + {Title: "Is it isomorphic?", Content: panel("Yes — the same Go server-renders and then hydrates.")}, + {Title: "How is it styled?", Content: panel("Tailwind, compiled by a Go program that reads your Go.")}, + }, acc.Get(), func(i int) { acc.Set(i) }), + ), + row("flex flex-col gap-2", + P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-ink-faint"), + Text("Accordion (several at once)")), + ui.Accordion([]ui.AccordionItemData{ + {Title: "First", Content: panel("Open me.")}, + {Title: "Second", Content: panel("And me, at the same time.")}, + {Title: "Third", Content: panel("And me.")}, + }, multi.Get(), func(i int) { + open := append([]bool{}, multi.Get()...) + open[i] = !open[i] + multi.Set(open) + }), + ), + ), + ), + + demo("Sidebar nav — clicked: "+side.Get(), + row("max-w-xs", + ui.SidebarNav([]ui.SidebarNavItem{ + {ID: "buttons", Label: "Buttons", Icon: ui.IconInline("check", 14, "")}, + {ID: "forms", Label: "Forms", Icon: ui.IconInline("pencil", 14, "")}, + {ID: "tables", Label: "Tables", Icon: ui.IconInline("table", 14, ""), + Children: []ui.SidebarNavItem{ + {ID: "pretty", Label: "PrettyTable"}, + {ID: "auto", Label: "AutoTable"}, + }}, + }, func(id string) { side.Set(id) }, ""), + ), + P(Attr("class", "mt-3 text-xs text-ink-muted"), + Text("It reports the id you clicked and nothing else. The sidebar on THIS page is the "+ + "same idea, wired to scroll the section into view.")), + ), + ) + } +} + +// ---- fuzzy search -------------------------------------------------------- + +func searchSection() func() *VNode { + q := NewSignal("") + hit := NewSignal("") + + return func() *VNode { + options := []string{ + "AutoTable", "Accordion", "Alert", "Badge", "Button", "Calendar", "CellGrid", "Combobox", + "DatePicker", "FormInput", "Menu", "Modal", "MultiSelect", "Popover", "PrettyTable", + "SegmentedButtons", "SignaturePad", "TabGroup", "ThemeToggle", "Toast", "ToggleSwitch", "Tooltip", + } + + return docSection("search", "Fuzzy search", + prose("Subsequence matching with a typo tolerance, scored so the best hit sorts first, and "+ + "the matched characters highlighted in the result. \"atbl\" finds AutoTable; so does "+ + "\"autotbale\", which is what you actually typed."), + + demo("Type into it — picked: "+orElse(hit.Get(), "nothing"), + row("max-w-md", + ui.FuzzyMatch(ui.FuzzyMatchProps{ + Options: options, + Query: q.Get(), + Placeholder: "Search the kit…", + MaxResults: 6, + ShowScores: true, + OnQueryChange: func(v string) { q.Set(v) }, + OnSelect: func(v string, _ ui.FuzzyRankedItem) { hit.Set(v) }, + }), + ), + ), + apiTable( + apiRow{"RankFuzzyMatches", "The scorer, headless. Use it and render the results yourself."}, + apiRow{"FuzzySegments", "Splits a result into matched / unmatched runs, for highlighting."}, + apiRow{"FuzzyMatchTypoTolerant", "One transposition or substitution forgiven."}, + ), + ) + } +} + +// ---- charts -------------------------------------------------------------- + +func chartsSection() func() *VNode { + values := NewSignal(fixedChartData()) + drawn := newChartDrawing() + + return func() *VNode { + v := values.Get() + + return docSection("charts", "Charts", + prose("These are SVG, produced by go-chart — a plain Go library that knows nothing about "+ + "browsers. They are drawn by the WEBASSEMBLY, in your browser. The server never enters "+ + "the drawing code at all; it renders a placeholder, and the wasm replaces it on its first "+ + "commit."), + prose("A Go charting library, compiled to wasm, drawing an SVG in the browser is the thing "+ + "this layer claims it can do. Server-rendering a picture of a chart would look identical "+ + "and prove the opposite point."), + + demo("Drawn in the browser, by Go", + row("grid gap-4 lg:grid-cols-12", + chartBox("lg:col-span-7 rounded-default border border-line bg-surface p-3 shadow-xs overflow-auto", + "h-[260px]", drawn.Get(), func() string { return barSVG(v) }), + chartBox("lg:col-span-5 rounded-default border border-line bg-surface p-3 shadow-xs overflow-auto", + "h-[320px]", drawn.Get(), func() string { return pieSVG(v) }), + ), + row("mt-4 flex items-center gap-2", + ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Small: true, Text: "New data", + OnClick: func() { values.Set(randomValues()) }}), + Span(Attr("class", "text-xs text-ink-muted"), + Text("Re-drawn in Go, in the browser. No request is made.")), + ), + ), + + note("webui.ReactiveChart is a stub, and says so", + "The Solid kit's Chart wraps chart.js: it creates a Chart against a 2D context "+ + "on mount and pushes new data through an effect. None of that — canvas drawing, a JS "+ + "charting library, mount lifecycles — exists in the neutral Go runtime, so the Go port "+ + "keeps the props shape for parity and renders an empty canvas. Draw with go-chart "+ + "instead, as above. Pretending otherwise would be the one thing this site refuses to do."), + ) + } +} + +// ---- theming ------------------------------------------------------------- + +func themingSection() func() *VNode { + tokens := []struct{ Swatch, Name, Role string }{ + {"bg-surface", "surface", "the page"}, + {"bg-surface-muted", "surface-muted", "a recessed strip"}, + {"bg-surface-raised", "surface-raised", "a panel, a hover"}, + {"bg-surface-strong", "surface-strong", "a track, a divider fill"}, + {"bg-line", "line", "an ordinary border"}, + {"bg-line-strong", "line-strong", "a border that has to be seen"}, + {"bg-ink", "ink", "body text, headings"}, + {"bg-ink-soft", "ink-soft", "secondary text"}, + {"bg-ink-muted", "ink-muted", "captions, labels"}, + {"bg-ink-faint", "ink-faint", "placeholders, disabled"}, + } + + return func() *VNode { + rows := []*VNode{} + for i, t := range tokens { + cls := "flex items-center gap-4 px-4 py-2.5" + if i > 0 { + cls += " border-t border-line" + } + rows = append(rows, Div(Attr("class", cls), + Span(Attr("class", "h-7 w-7 shrink-0 rounded border border-line-strong "+t.Swatch)), + El("code", Attr("class", "w-40 shrink-0 font-mono text-[13px] text-ink"), Text(t.Name)), + Span(Attr("class", "text-sm text-ink-muted"), Text(t.Role)), + )) + } + + return docSection("theming", "Theming", + prose("No component in this kit names a colour. They say bg-surface, text-ink, border-line — "+ + "and what those mean is decided in one place. Dark mode re-points a dozen CSS variables "+ + "and not one component knows it happened."), + + demo("The switch", + row("flex flex-wrap items-center gap-4", + Theme.ThemeToggle(ui.ThemeToggleProps{}), + Span(Attr("class", "text-sm text-ink-soft"), + Text("Press it. Every component on this page moves — none of them were told.")), + ), + ), + + demo("The contract — each swatch is drawn WITH the token it names", + swatchTable(rows), + ), + + note("One vocabulary, two kits", + "The Solid kit uses these exact token names, and both layers of this site read the same "+ + "kjol-theme key — so choose dark here, walk over to Kjøl JS Web, and it is still dark. "+ + "Two front-ends that share no code at all, and one preference."), + ) + } +} + +// ---- the sidebar's jump links -------------------------------------------- + +// navigateAnchor makes a sidebar link scroll to a section instead of loading a page. +// +// A plain would work if the reader were already on this page, and +// would do nothing useful from anywhere else. So: intercept, route first if we are on +// another page, and scroll once the section actually exists in the DOM — which is what +// AfterRender is for, and the only moment at which it is true. +// +// On the server Navigate is nil, so this does not intercept at all and the anchor stays +// a real, working link. A crawler and a reader with JavaScript off both get the jump. +func navigateAnchor(d Deps, base, frag string) Mod { + return OnEvent(EVENT_CLICK, func(e Event) { + if d.Navigate == nil { + return + } + e.PreventDefault() + + if d.Path() != base { + d.Navigate(base) + // The section does not exist yet — the page it lives on has not rendered. + wasmruntime.AfterRender(func() { scrollToSection(frag) }) + return + } + // Already here: the element exists NOW, and no render is coming to hang an + // AfterRender off. + scrollToSection(frag) + }) +} + +func scrollToSection(id string) { + // QuerySelector always returns a Ref; ScrollIntoView is a no-op on one with no node, + // so a stale anchor scrolls nowhere rather than panicking. + wasmruntime.ScrollIntoView(wasmruntime.QuerySelector("#"+id), true, wasmruntime.ScrollBlockStart) +} + +// ---- shared helpers ------------------------------------------------------ + +// orElse is a fallback for an empty string. +func orElse(s, fallback string) string { + if s == "" { + return fallback + } + return s +} + +// row is a flex/grid container helper (appends *VNode children as Mods). +func row(class string, children ...*VNode) *VNode { + mods := []Mod{Attr("class", class)} + for _, c := range children { + mods = append(mods, c) + } + return Div(mods...) +} + +// kitSection is one labelled block of the gallery — a live demo panel, so that what you +// are looking at is unmistakably the component running rather than a picture of it. +func kitSection(title string, body ...*VNode) *VNode { + return demo(title, row("flex flex-col gap-4", body...)) +} + +func ptRow(name, plan string, status *VNode) *VNode { + td := func(cls string, c *VNode) *VNode { return El("td", Attr("class", "px-3 py-2 text-sm "+cls), c) } + return El("tr", + td("text-ink", Text(name)), + td("text-ink-soft", Text(plan)), + El("td", Attr("class", "px-3 py-2 text-sm text-right"), status), + ) +} + +// languageOptions is deliberately longer than the pill limit, so the multi-select +// demonstrates both ways it collapses: past 3 selections it says "N items selected" +// outright, and below that it still collapses if the pills are too wide for the field. +func languageOptions() []ui.FormSelectOption { + return []ui.FormSelectOption{ + {Value: "go", Label: "Go"}, + {Value: "rust", Label: "Rust"}, + {Value: "ts", Label: "TypeScript"}, + {Value: "python", Label: "Python"}, + {Value: "kotlin", Label: "Kotlin"}, + {Value: "swift", Label: "Swift"}, + } +} + + +const kitSnippet = `// A component is a function taking a props struct. +ui.Button(ui.ButtonProps{ + Color: ui.ButtonPrimary, + Icon: "check", + Text: "Save", + OnClick: func() { toaster.Success("Saved.") }, +}) + +// Inputs are controlled: the caller owns the state. +name := NewSignal("") + +ui.FormInput(ui.FormInputProps{ + Value: name.Get(), + OnInput: func(v string) { name.Set(v) }, // a write re-renders +})` + +const floatingSnippet = `// Built ONCE — it owns refs, timers, and whether it is open. +pop := ui.NewPopover(ui.PopoverOptions{ + Placement: ui.PlacementBottomStart, + Offset: 8, +}) + +// ...and in the render: +pop.Trigger(ui.PopoverTriggerProps{}, + ui.Button(ui.ButtonProps{Text: "Click me"}), +) +pop.Content(ui.PopoverContentProps{Class: "w-64"}, + P(Text("Outside click and Escape close me.")), +) + +// The panel is portaled to document.body and positioned imperatively: +// render invisible -> AfterRender -> measure -> ComputePosition -> SetStyle -> reveal +// Never through a signal: this runs on every scroll frame.` + +// swatchTable wraps the token rows. Div takes ...Mod, and a []*VNode is not that — so +// the conversion happens here rather than being repeated at every call site. +func swatchTable(rows []*VNode) *VNode { + mods := []Mod{Attr("class", "overflow-hidden rounded-default border border-line")} + for _, r := range rows { + mods = append(mods, r) + } + return Div(mods...) +} diff --git a/go/cmd/kjol-web/app/docs.go b/go/cmd/kjol-web/app/docs.go index 3ffecbdc..886b1bf2 100644 --- a/go/cmd/kjol-web/app/docs.go +++ b/go/cmd/kjol-web/app/docs.go @@ -1,7 +1,14 @@ package app import ( + "strings" + + "kjol/lexer" // syntax highlighting for the code blocks — a string in, HTML out . "kjol/vdom" + // The host API is dual-build — real under js/wasm, no-ops natively — so neutral page + // code can measure the browser and still server-render. This page uses it for exactly + // one thing: reading the clock when hydration commits. + "kjol/wasmruntime" ui "kjol/webui" ) @@ -31,18 +38,38 @@ type docsItem struct { Icon string } +// The Components group is not a list of PAGES — it is a list of anchors into the one +// components page. There used to be three pages there ("UI kit", "Overlays", +// "AutoTable"), which split the kit along the lines of its source files rather than +// along anything a reader wants: a person hunting for a date picker does not know, and +// should not have to guess, whether it was filed under forms or under overlays. +// +// So the whole kit is one page, and the sidebar jumps you down it. The groups come from +// componentGroups(), which is also what BUILDS the sections — so the sidebar cannot +// offer a jump to a section that does not exist, and a section cannot go missing from +// the sidebar. func docsNav() []docsGroup { + items := make([]docsItem, 0, len(componentGroups())) + for _, g := range componentGroups() { + items = append(items, docsItem{ + Path: "/wasm/components#" + g.ID, + Label: g.Label, + Icon: g.Icon, + Blurb: g.Blurb, + }) + } + return []docsGroup{{ Title: "Introduction", Items: []docsItem{ {Path: "/wasm", Label: "Overview", Icon: "book-open", - Blurb: "What Kjol Web is, how a page becomes a WebAssembly binary, and what runs where."}, + Blurb: "What Kjøl Wasm Web is, how a page becomes a WebAssembly binary, and what runs where."}, }, }, { Title: "Rendering", Items: []docsItem{ {Path: "/wasm/chart", Label: "SSR & hydration", Icon: "chart-column", - Blurb: "The same Go renders HTML on the server and takes over in the browser. Charts, server-drawn as SVG."}, + Blurb: "The same Go renders HTML on the server and takes over in the browser. Charts, drawn by the WebAssembly."}, {Path: "/wasm/server", Label: "Server components", Icon: "server", Blurb: "Components whose state and code stay on the server. Calling one looks like calling any other."}, {Path: "/wasm/data", Label: "Data fetching", Icon: "cloud-arrow-down", @@ -50,14 +77,7 @@ func docsNav() []docsGroup { }, }, { Title: "Components", - Items: []docsItem{ - {Path: "/wasm/kit", Label: "UI kit", Icon: "squares", - Blurb: "Buttons, forms, tabs, alerts, cards — the kjol/webui components, written in Go."}, - {Path: "/wasm/overlays", Label: "Overlays", Icon: "layers", - Blurb: "Tooltips, popovers, menus, modals: measured against the real viewport, flipped and shifted to fit."}, - {Path: "/wasm/table", Label: "AutoTable", Icon: "table", - Blurb: "Filtering, sorting, column management, calculated columns, CSV and PDF export."}, - }, + Items: items, }} } @@ -71,7 +91,7 @@ func docPage(eyebrow, title, lede string, sections ...*VNode) *VNode { Div(Attr("class", "border-b border-line pb-6"), P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-accent"), Text(eyebrow)), H1(Attr("class", "mt-2 text-3xl font-semibold tracking-tight text-text-heading"), Text(title)), - P(Attr("class", "mt-3 max-w-3xl text-ink-muted leading-relaxed"), Text(lede)), + P(Attr("class", "mt-3 text-ink-muted leading-relaxed"), Text(lede)), ), ) for _, s := range sections { @@ -93,11 +113,15 @@ func docSection(id, title string, body ...*VNode) *VNode { return El("section", mods...) } -// prose is a paragraph of explanation. Constrained to a reading measure: a line of body -// text that runs the full width of a wide screen is genuinely harder to read, and the -// demos beside it are allowed to be as wide as they like. +// prose is a paragraph of explanation. +// +// It used to be pinned to a reading measure (max-w-3xl). It is not any more: on a +// documentation page the paragraphs sit directly above demos, tables and code blocks +// that are as wide as the column, and a narrow ribbon of text over a full-width panel +// reads as a mistake rather than as typographic care. The column itself (max-w-6xl, set +// by AppLayout) is the measure now. func prose(text string) *VNode { - return P(Attr("class", "mt-3 max-w-3xl text-ink-soft leading-relaxed"), Text(text)) + return P(Attr("class", "mt-3 text-ink-soft leading-relaxed"), Text(text)) } // ---- code --------------------------------------------------------------- @@ -109,23 +133,20 @@ func prose(text string) *VNode { // lets you go and check. func code(caption, src string) *VNode { return codeLang(caption, "Go", src) } -// codeLang is code() for a block that is not Go — a shell session, a formula. The label -// in the corner says what you are looking at, and a shell command labelled "Go" is worse -// than no label at all. +// codeLang is code() for a block that is not Go — a C header, a shell session, a formula. +// The label in the corner says what you are looking at, and a shell command labelled "Go" +// is worse than no label at all. // -// Go blocks are syntax-highlighted (webui.HighlightGo); the others are shown verbatim. -// A shell transcript put through a Go lexer comes out with `serving` painted as an -// identifier and quotes as string literals — highlighting the wrong language is more -// distracting than not highlighting at all. +// The label is ALSO what picks the lexer, so the two cannot disagree: a block cannot be +// labelled C and painted as Go. A language kjol/lexer does not know comes back escaped and +// unpainted, which is what should happen — a shell transcript put through a Go lexer comes +// out with `serving` painted as an identifier and quotes as string literals, and +// highlighting the WRONG language is more distracting than not highlighting at all. func codeLang(caption, lang, src string) *VNode { - var body *VNode - if lang == "Go" { - // Raw, not Text: HighlightGo returns HTML. It escapes every run of source on the - // way out, so the snippets that contain markup stay inert. - body = El("code", Raw(ui.HighlightGo(src))) - } else { - body = El("code", Text(src)) - } + // Raw, not Text: the lexer returns HTML. It escapes every run of source on the way out + // — including for a language it does not know — so the snippets that contain markup, + // and every C snippet, which is all pointers and shifts, stay inert. + body := El("code", Raw(lexer.Highlight(lang, src))) return Div(Attr("class", "mt-4 overflow-hidden rounded-default border border-neutral-800 bg-neutral-900"), Div(Attr("class", "flex items-center gap-2 border-b border-neutral-800 px-4 py-2"), @@ -156,7 +177,7 @@ func demo(title string, body ...*VNode) *VNode { // note is an aside — a caveat, a gotcha, the reason something is the way it is. func note(title, body string) *VNode { - return Div(Attr("class", "mt-4 max-w-3xl rounded-default border border-primary-border bg-primary-subtle px-4 py-3"), + return Div(Attr("class", "mt-4 rounded-default border border-primary-border bg-primary-subtle px-4 py-3"), P(Attr("class", "text-sm font-semibold text-text-heading"), Text(title)), P(Attr("class", "mt-1 text-sm text-ink-soft leading-relaxed"), Text(body)), ) @@ -180,7 +201,10 @@ func apiTable(rows ...apiRow) *VNode { for _, b := range body { rowMods = append(rowMods, b) } - return Div(Attr("class", "mt-4 max-w-5xl overflow-x-auto"), + // Full width, like everything else on the page. A reference table pinned to max-w-5xl + // inside a max-w-6xl column is not narrower for a reason — it is narrower by an inch, + // which reads as a misalignment rather than as a decision. + return Div(Attr("class", "mt-4 overflow-x-auto"), El("table", Attr("class", "w-full border-collapse text-left"), Tbody(rowMods...), ), @@ -191,7 +215,38 @@ func apiTable(rows ...apiRow) *VNode { //gowasm:page /wasm static layout=app func DocsPage(d Deps) func() *VNode { + clicks := NewSignal(0) + + // The one measurement on the page: performance.now() when the client's first render + // commits. Zero until then — which is what the SERVER renders, and what the client + // renders on its first pass, so the two agree and hydration stays clean. + hydratedAt := NewSignal(0.0) + wasmruntime.AfterRender(func() { + if hydratedAt.Get() == 0 { + hydratedAt.Set(wasmruntime.Now()) + } + }) + + // demoTree is called TWICE per render below — once for the DOM, once for the HTML. + // That is the point: the two panes cannot drift, because there is only one of them. + // + // This demo used to be on the front page. It does not belong there — it is the Wasm + // Web engine's single best argument, and the front page is kjøl's, not this engine's. + // Here it is the first thing the section shows, which is where an argument like this + // one earns its place. + demoTree := func() *VNode { + return Div(Attr("class", "flex items-center gap-3"), + ui.Button(ui.ButtonProps{ + Color: ui.ButtonPrimary, Text: "Click me", + OnClick: func() { clicks.Set(clicks.Get() + 1) }, + }), + Span(Attr("class", "text-ink-soft"), Text("clicked "+itoa(clicks.Get())+" times")), + ) + } + return func() *VNode { + markup := RenderHTML(demoTree()) + var groups []*VNode for _, g := range docsNav() { grid := []Mod{Attr("class", "mt-3 grid gap-3 sm:grid-cols-2")} @@ -213,10 +268,39 @@ func DocsPage(d Deps) func() *VNode { } return docPage("Introduction", "Overview", - "Kjol Web is kjol's Go→WebAssembly UI engine. You write components as ordinary Go functions "+ - "returning a virtual DOM; the server renders them to HTML and the same code hydrates them "+ - "in the browser. There is no JavaScript build step, and the engine depends on nothing "+ - "outside the standard library.", + "Kjøl Wasm Web is Kjøl's Go→WebAssembly UI engine. You write components as ordinary Go "+ + "functions returning a virtual DOM; the server renders them to HTML and the same code "+ + "hydrates them in the browser. There is no JavaScript build step, and the engine depends "+ + "on nothing outside the standard library.", + + // ---- the demonstration ---- + // + // The one thing on this site that cannot be faked: the same Go function, rendered + // twice at once, as live DOM and as the HTML string the server sent. + docSection("two-runtimes", "One function, two runtimes", + prose("Below is a single Go function, shown twice. On the left it has been reconciled into "+ + "the DOM and you can use it. On the right is the HTML the same function produces when the "+ + "server renders it — the markup that reached your browser before any WebAssembly had "+ + "loaded. Click the button; both move."), + + Div(Attr("class", "mt-5 border border-line sm:grid sm:grid-cols-2"), + Div(Attr("class", "border-b border-line sm:border-b-0 sm:border-r"), + paneLabel("in your browser"), + Div(Attr("class", "px-4 py-8"), demoTree()), + ), + Div( + paneLabel(itoa(len(markup))+" bytes of HTML"), + Pre(Attr("class", "whitespace-pre-wrap px-4 py-4 font-mono text-[12px] leading-relaxed text-ink-muted"), + El("code", Text(prettyHTML(markup))), + ), + ), + ), + P(Attr("class", "mt-3 text-sm leading-relaxed text-ink-muted"), + Text("The right pane is not a picture of the source. It is vdom.RenderHTML, called on the "+ + "very tree the left pane is showing, recomputed on every click.")), + P(Attr("class", "mt-2 text-sm text-ink-muted"), + Text(hydrationNote(hydratedAt.Get()))), + ), docSection("what-runs-where", "What runs where", prose("A page is Go, compiled twice. On the server it renders to an HTML string, so the first "+ @@ -230,15 +314,35 @@ func DocsPage(d Deps) func() *VNode { "position itself, without a branch in the component."), ), + docSection("what-is-in-it", "What is in it", + Ul(Attr("class", "mt-3 space-y-1.5 leading-relaxed text-ink-soft"), + item("Server-side rendering and client hydration, from one codebase."), + item("Server components: mark a function and its code and state stay on the server."), + item("A component kit — forms, tabs, modals, tooltips, toasts — with dark mode."), + item("A table with filtering, sorting, column management, formulas, and CSV and PDF export."), + item("Tailwind, compiled by a Go program that reads your Go."), + ), + prose("Two commands build it. The first produced the page you are reading; the second serves "+ + "it and rebuilds on save."), + codeLang("terminal", "sh", buildTranscript), + ), + appendNodes(Div(Attr("class", "mt-14 border-t border-line pt-2")), groups...), ) } } func docsCard(d Deps, it docsItem) *VNode { + // A component card is a jump into the components page, not a page of its own — so it + // routes there and scrolls, exactly as the sidebar does. + click := navigate(d, it.Path) + if base, frag, ok := strings.Cut(it.Path, "#"); ok { + click = navigateAnchor(d, base, frag) + } + return A( Attr("class", "group block rounded-default border border-line bg-surface p-4 no-underline shadow-xs transition hover:border-primary-border hover:shadow-sm"), - Attr("href", it.Path), navigate(d, it.Path), + Attr("href", it.Path), click, Div(Attr("class", "flex items-center gap-2"), Span(Attr("class", "inline-flex h-7 w-7 items-center justify-center rounded-default bg-primary-subtle text-accent"), ui.IconInline(it.Icon, 14, "")), diff --git a/go/cmd/kjol-web/app/kit.go b/go/cmd/kjol-web/app/kit.go deleted file mode 100644 index 7423d6f1..00000000 --- a/go/cmd/kjol-web/app/kit.go +++ /dev/null @@ -1,355 +0,0 @@ -package app - -import ( - "strings" - - . "kjol/vdom" - ui "kjol/webui" -) - -// orElse is a fallback for an empty string. -func orElse(s, fallback string) string { - if s == "" { - return fallback - } - return s -} - -// row is a flex/grid container helper (appends *VNode children as Mods). -func row(class string, children ...*VNode) *VNode { - mods := []Mod{Attr("class", class)} - for _, c := range children { - mods = append(mods, c) - } - return Div(mods...) -} - -// kitSection is one labelled block of the gallery — a live demo panel, so that what you -// are looking at is unmistakably the component running rather than a picture of it. -func kitSection(title string, body ...*VNode) *VNode { - return demo(title, row("flex flex-col gap-4", body...)) -} - -func ptRow(name, plan string, status *VNode) *VNode { - td := func(cls string, c *VNode) *VNode { return El("td", Attr("class", "px-3 py-2 text-sm "+cls), c) } - return El("tr", - td("text-ink", Text(name)), - td("text-ink-soft", Text(plan)), - El("td", Attr("class", "px-3 py-2 text-sm text-right"), status), - ) -} - -// languageOptions is deliberately longer than the pill limit, so the multi-select -// demonstrates both ways it collapses: past 3 selections it says "N items selected" -// outright, and below that it still collapses if the pills are too wide for the field. -func languageOptions() []ui.FormSelectOption { - return []ui.FormSelectOption{ - {Value: "go", Label: "Go"}, - {Value: "rust", Label: "Rust"}, - {Value: "ts", Label: "TypeScript"}, - {Value: "python", Label: "Python"}, - {Value: "kotlin", Label: "Kotlin"}, - {Value: "swift", Label: "Swift"}, - } -} - -//gowasm:page /wasm/kit layout=app -func KitPage(d Deps) func() *VNode { - // Interactive demos own their state via signals (a write re-renders). - tab := NewSignal(0) - acc := NewSignal(0) - notify := NewSignal(true) - span := NewSignal("week") - name := NewSignal("") - email := NewSignal("") - plan := NewSignal("pro") - langs := NewSignal([]string{"go"}) - - // Floating components are CONTROLLERS: they own refs, timers and open state, so - // they are built once here — never inside the render closure below, which would - // rebuild them (and lose their state) on every frame. - modal := ui.NewModal(ui.ModalOptions{Size: ui.ModalMedium}) - menu := ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomStart}) - tip := ui.NewHoverTooltip(ui.PlacementTop, "") - skills := ui.NewMultiSelect(ui.DropdownOptions{}) - - // The controls the first port left out, now that the host API can carry them. - taxID := NewSignal("") - rate := NewSignal("") - signed := NewSignal("") - picked := NewSignal("") - tags := NewSignal([]string{"go"}) - - pad := ui.NewSignaturePad(ui.SignaturePadOptions{ - OnChange: func(svg string) { signed.Set(svg) }, - }) - // The search is the caller's: the component knows how to debounce, order and render, - // and nothing at all about where options come from. Here it is a local slice; in an - // app it would be a fetch. - people := ui.NewAsyncCombobox(ui.AsyncComboboxOptions{ - MinChars: 2, - Search: func(q string, done func([]ui.FormSelectOption)) { - var out []ui.FormSelectOption - for _, row := range employees() { - p, ok := row.(Employee) - if ok && strings.Contains(strings.ToLower(p.Name), strings.ToLower(q)) { - out = append(out, ui.FormSelectOption{Value: p.Email, Label: p.Name}) - } - } - done(out) - }, - }) - tagPicker := ui.NewMultiSelectTrigger(ui.DropdownOptions{}) - - return func() *VNode { - return docPage("Components", "UI kit", - "kjol/webui is the component library: buttons, badges, forms, tabs, alerts, cards, tables. "+ - "It is a Go port of the Solid.js kit the applications used before, styled with the same "+ - "Tailwind utilities — so the two can be swapped for one another a screen at a time.", - - docSection("using", "Using a component", - prose("Components are functions taking a props struct. There is no class hierarchy and nothing "+ - "to register: a component is a value, so you can build one, store it, pass it around, and "+ - "the compiler will tell you when you get it wrong."), - code("app/kit.go", kitSnippet), - note("Styling is Tailwind, compiled from your Go", - "The Tailwind engine scans .go files for class names, because that is where the markup is. "+ - "There is no JavaScript build in this example at all — the CSS is compiled by a Go "+ - "program from Go source."), - ), - - docSection("gallery", "The gallery", - prose("Everything below is running. Click it."), - ), - - kitSection("Buttons", - row("flex flex-wrap items-center gap-2", - ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Primary"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonGreen, Text: "Green"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Text: "Red"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Text: "Blue"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Text: "Neutral"}), - ), - row("flex flex-wrap items-center gap-2", - ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Outline: true, Text: "Outline"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Outline: true, Text: "Danger"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonGreen, Small: true, Icon: "check", Text: "Small + icon"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Icon: "plus"}), - ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Disabled", Disabled: true}), - ), - ), - - kitSection("Badges", - row("flex flex-wrap items-center gap-2", - ui.Badge(ui.BadgeProps{Color: ui.BadgeGreen}, Text("active")), - ui.Badge(ui.BadgeProps{Color: ui.BadgeRed}, Text("failed")), - ui.Badge(ui.BadgeProps{Color: ui.BadgeBlue}, Text("info")), - ui.Badge(ui.BadgeProps{Color: ui.BadgeAmber, Pill: true}, Text("pending")), - ui.Badge(ui.BadgeProps{Color: ui.BadgeNeutral}, Text("default")), - ui.Badge(ui.BadgeProps{Color: ui.BadgeMuted}, Text("muted")), - ), - ), - - kitSection("Alerts", - ui.Alert(ui.AlertBlue, "Heads up", Text("An informational message with a header.")), - ui.Alert(ui.AlertGreen, "", Text("A success alert without a header.")), - ui.Alert(ui.AlertYellow, "Warning", Text("Something needs your attention.")), - ui.Alert(ui.AlertRed, "Error", Text("Something went wrong.")), - ), - - kitSection("Toggles & segmented control", - ui.ToggleSwitch(notify.Get(), func(v bool) { notify.Set(v) }, "Email notifications", "Send me product updates", false, ""), - ui.SegmentedButtons([]ui.SegmentedButtonOption{ - {Value: "day", Label: "Day"}, - {Value: "week", Label: "Week"}, - {Value: "month", Label: "Month"}, - }, span.Get(), func(v string) { span.Set(v) }, false, "max-w-xs"), - ), - - kitSection("Tabs", - ui.TabGroup(ui.TabGroupProps{ - Items: []ui.TabItem{ - {Title: "Overview", Content: P(Attr("class", "pt-3 text-sm text-ink-soft"), Text("The overview panel."))}, - {Title: "Details", Content: P(Attr("class", "pt-3 text-sm text-ink-soft"), Text("The details panel."))}, - {Title: "Activity", Badge: 3, Content: P(Attr("class", "pt-3 text-sm text-ink-soft"), Text("The activity panel (3 new)."))}, - }, - ActiveIndex: tab.Get(), - OnTabChange: func(i int) { tab.Set(i) }, - }), - ), - - kitSection("Accordion", - ui.SingleAccordion([]ui.AccordionItemData{ - {Title: "What is Kjol Web?", Content: P(Attr("class", "text-sm text-ink-soft"), Text("kjol's Go→WebAssembly UI engine."))}, - {Title: "Is it isomorphic?", Content: P(Attr("class", "text-sm text-ink-soft"), Text("Yes — the same Go renders on the server (SSR) and hydrates on the client."))}, - {Title: "How is it styled?", Content: P(Attr("class", "text-sm text-ink-soft"), Text("Tailwind utility classes, compiled by kjol's native Tailwind engine."))}, - }, acc.Get(), func(i int) { acc.Set(i) }), - ), - - kitSection("Forms", - row("grid gap-4 sm:grid-cols-3", - row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text("Name")), - ui.FormInput(ui.FormInputProps{Value: name.Get(), Placeholder: "Ada Lovelace", OnInput: func(v string) { name.Set(v) }})), - row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text("Email")), - ui.FormEmailInput(ui.FormInputProps{Value: email.Get(), Placeholder: "ada@example.com", OnInput: func(v string) { email.Set(v) }}, true)), - row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text("Plan")), - ui.FormSelect(ui.FormSelectProps{Value: plan.Get(), OnChange: func(v string) { plan.Set(v) }}, - ui.FormOption("free", "Free", false), - ui.FormOption("pro", "Pro", false), - ui.FormOption("enterprise", "Enterprise", false))), - - // A multi-select. Its rows carry checkboxes, and the field shows the - // selection as removable pills — until they stop fitting, at which point - // it collapses to "N items selected". Tick a few and watch it flip. - row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text("Languages")), - skills.Render(ui.FormMultiSelectProps{ - Options: languageOptions(), - Value: langs.Get(), - Placeholder: "Pick a few", - Searchable: true, - ShowSelectAll: true, - OnChange: func(v []string) { langs.Set(v) }, - })), - ), - P(Attr("class", "text-xs text-ink-muted"), - Text("Live: name=\""+name.Get()+"\" email=\""+email.Get()+"\" plan=\""+plan.Get()+ - "\" languages="+strings.Join(langs.Get(), ","))), - ), - - kitSection("Masked inputs", - row("grid gap-4 sm:grid-cols-2", - row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text("Tax ID")), - // The mask is a pure function of the string, applied on every keystroke. - // It must be idempotent — it is fed its own output — or the field - // corrupts itself as you type. - ui.FormInput(ui.FormInputProps{ - Value: taxID.Get(), - Placeholder: "12-3456789", - OnInput: func(v string) { taxID.Set(ui.MaskTaxID(v)) }, - })), - row("flex flex-col gap-1", ui.FormLabel(ui.FormLabelProps{}, Text("Rate")), - ui.FormInput(ui.FormInputProps{ - Value: rate.Get(), - Placeholder: "5.25", - OnInput: func(v string) { rate.Set(ui.MaskRate(v)) }, - })), - ), - P(Attr("class", "text-xs text-ink-muted"), - Text("Type letters, extra dots, leading zeros — the mask takes what it can use.")), - ), - - kitSection("Async combobox", - row("max-w-sm", - people.Render(ui.FormAsyncComboboxProps{ - Placeholder: "Search people…", - OnSelect: func(o ui.FormSelectOption) { picked.Set(o.Label + " <" + o.Value + ">") }, - }), - ), - P(Attr("class", "text-xs text-ink-muted"), - Text("Two characters before it asks; 200 ms after you stop typing. A response for a "+ - "query you have already typed past is discarded rather than shown. Picked: "+ - orElse(picked.Get(), "nothing yet"))), - ), - - kitSection("Multi-select behind your own trigger", - tagPicker.Render(ui.FormMultiSelectTriggerProps{ - Trigger: ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, - Icon: "filter", Text: "Tags (" + itoa(len(tags.Get())) + ")"}), - Options: languageOptions(), - Value: tags.Get(), - Searchable: true, - ShowSelectAll: true, - OnChange: func(v []string) { tags.Set(v) }, - }), - P(Attr("class", "text-xs text-ink-muted"), - Text("Same selection model as the field above; only the thing you click on differs.")), - ), - - kitSection("Signature pad", - pad.Render(ui.SignaturePadProps{}), - P(Attr("class", "text-xs text-ink-muted"), - Text("Draw in it. It is an SVG, not a canvas — so the markup you are looking at IS the "+ - "value the caller gets ("+itoa(len(signed.Get()))+" bytes), and a stored signature "+ - "renders on the server.")), - ), - - kitSection("Table", - ui.PrettyTable( - []ui.PrettyTableColumn{ - {DisplayName: "Name"}, - {DisplayName: "Plan"}, - {DisplayName: "Status", DisplayPosition: ui.PrettyTableColRight}, - }, - ui.PrettyTableOptions{Hover: true, Alternate: true, SurroundingBorder: true, HeaderBorderY: true}, - ptRow("Ada Lovelace", "Pro", ui.Badge(ui.BadgeProps{Color: ui.BadgeGreen}, Text("active"))), - ptRow("Alan Turing", "Free", ui.Badge(ui.BadgeProps{Color: ui.BadgeNeutral}, Text("trial"))), - ptRow("Grace Hopper", "Enterprise", ui.Badge(ui.BadgeProps{Color: ui.BadgeBlue}, Text("invited"))), - ), - ), - - kitSection("Overlays (measured, portaled)", - row("flex flex-wrap items-center gap-4", - ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Open modal", OnClick: modal.Open}), - - // The menu measures itself against the viewport: drag the window - // narrow, or scroll it to the bottom, and it flips/shifts to stay on - // screen. Items close the menu themselves — no callback plumbing. - menu.TriggerFunc(ui.MenuTriggerProps{Tag: "div"}, func(open bool) *VNode { - caret := " ▾" - if open { - caret = " ▴" - } - return ui.Button(ui.ButtonProps{Color: ui.ButtonWhite, Text: "Menu" + caret}) - }), - menu.Content("", - menu.Item(ui.MenuItemProps{Icon: "check"}, Text("Profile")), - menu.Item(ui.MenuItemProps{}, Text("Settings")), - ui.MenuDivider(""), - menu.Item(ui.MenuItemProps{}, Text("Sign out")), - ), - - // The tooltip's arrow tracks the trigger even when the panel gets - // shifted away from it near a viewport edge. - tip.Render(Span(Text("A measured tooltip — try it near the window edge")), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Hover me"})), - ), - modal.Render(ui.ModalProps{ - Header: H3(Attr("class", "text-lg font-semibold text-text-heading"), Text("Example modal")), - }, - P(Attr("class", "text-ink-soft"), - Text("Portaled to document.body, so it is not clipped by any ancestor. Escape closes "+ - "the topmost modal; the backdrop click closes too.")), - ), - ), - - docSection("more", "Where to go next", - prose("The floating components on this page — the menu, the tooltip, the modal, the "+ - "multi-select — are the shallow end. Overlays covers how they are positioned, and what "+ - "happens when one would open off the edge of the screen."), - apiTable( - apiRow{"ui.Button / ui.Badge / ui.Alert", "The presentational set. Props structs, no state."}, - apiRow{"ui.FormInput / FormSelect / FormCombobox", "Inputs. Value in, OnChange out — the caller owns the state."}, - apiRow{"ui.NewMultiSelect", "A controller: checkboxed rows, pills that collapse to \"N items selected\" when they stop fitting."}, - apiRow{"ui.Tabs / ui.Accordion / ui.Card", "Layout and disclosure."}, - apiRow{"ui.RegisterIcon", "Add your own icons. The kit ships a small set; the app brings the rest."}, - ), - ), - ) - } -} - -const kitSnippet = `// A component is a function taking a props struct. -ui.Button(ui.ButtonProps{ - Color: ui.ButtonPrimary, - Icon: "check", - Text: "Save", - OnClick: func() { toaster.Success("Saved.") }, -}) - -// Inputs are controlled: the caller owns the state. -name := NewSignal("") - -ui.FormInput(ui.FormInputProps{ - Value: name.Get(), - OnInput: func(v string) { name.Set(v) }, // a write re-renders -})` diff --git a/go/cmd/kjol-web/app/layers.go b/go/cmd/kjol-web/app/layers.go index ab66e4ec..8e0f18ff 100644 --- a/go/cmd/kjol-web/app/layers.go +++ b/go/cmd/kjol-web/app/layers.go @@ -7,22 +7,42 @@ import ( ui "kjol/webui" ) -// The layers of kjol, as data. +// What kjøl is made of, as data. // -// This is the Go mirror of frontend/src/layers.ts. The site has two front-ends built -// by two completely different pipelines, and the Layers menu has to be identical in -// both — so it is a LIST in each, not markup, and the two lists are the only thing -// that has to be kept in step. +// There are two kinds of thing here, and conflating them was the mistake this file used +// to make — one flat list called "the layers", holding both. // -// (A shared source would be better than a mirrored one. There isn't one: this half -// compiles to WebAssembly and the other is bundled by esbuild, and nothing is upstream -// of both. Keeping it to a flat slice of plain data is what makes the duplication -// survivable — you can diff the two by eye.) +// LAYERS are LANGUAGES. What kjøl is written in, and what it gives you in each: +// the Go base, the TypeScript kit, the C base, the Jai modules. A layer is +// a directory of code you can use on its own. +// +// COMPOSITIONS are FRAMEWORKS. What you get when the layers are assembled into +// something that does a job — the two web engines. A composition is not +// another language; it is a use of them. +// +// Kjøl Wasm Web is Go, all the way down. Kjøl JS Web is TypeScript compiled by a Go +// toolchain — two layers, one framework. Listing that beside "C" as though they were the +// same kind of noun told the reader nothing about either. +// +// This is the Go mirror of frontend/src/layers.ts. The site has two front-ends built by +// two completely different pipelines, and these menus have to be identical in both — so +// each is a LIST, not markup, and the two lists are the only thing that has to be kept in +// step. +// +// (A shared source would be better than a mirrored one. There isn't one: this side +// compiles to WebAssembly and the other is bundled by esbuild, and nothing is upstream of +// both. Keeping each to a flat slice of plain data is what makes the duplication +// survivable — you can diff them by eye.) type Layer struct { Name string Href string Tagline string + // Sub is the half-line beside the wordmark while you are inside this layer. It says + // what you are standing in — "Go + WebAssembly", "arenas, strings, a lexer" — and a + // wordmark that says the same thing everywhere is one more thing the reader has to + // keep track of himself. + Sub string // Live means you can click into worked examples. The others are documented but // have no demo — they still appear, because a menu that silently omits half the // library teaches the reader that the library is half the size it is. @@ -30,66 +50,107 @@ type Layer struct { Icon string } -func Layers() []Layer { +// Wordmark is what the CHROME calls this layer — the top bar, and the page's own title. +// The menu calls it Name. +// +// They differ, and only for the languages. In a menu headed "Layers" the row says "C", +// because the row is answering "which language"; up in the top bar, alone, "C" is the name +// of a language rather than the name of the thing you are reading, and it has to say whose +// C this is. The compositions are already named "Kjøl Wasm Web" — the product's name is +// part of what they ARE, not a prefix bolted on — so they are returned unchanged. +func (l Layer) Wordmark() string { + if strings.HasPrefix(l.Name, "Kjøl") { + return l.Name + } + return "Kjøl " + l.Name +} + +// Languages: what kjøl is written in. +func Languages() []Layer { return []Layer{ { - Name: "Kjol Go", + Name: "Go", Href: "/go", - Tagline: "The server base: config, database, logging, HTTP, mail, validation.", + Tagline: "The base: config, database, logging, HTTP, mail, validation — and both web engines.", Icon: "server", }, { - Name: "Kjol Wasm Web", - Href: "/wasm", - Tagline: "Web interfaces written in Go, compiled to WebAssembly. SSR + hydration, no JS build.", - Live: true, - Icon: "code", - }, - { - Name: "Kjol JS Web", - Href: "/js", - Tagline: "The Solid component kit, bundled by a Go toolchain: TSX → Solid → esbuild, Tailwind in Go.", - Live: true, + Name: "TypeScript", + Href: "/ts", + Tagline: "The Solid component kit, the vendored runtime, and the generic scaffolding apps import as @kjol/*.", Icon: "squares", }, { - Name: "Kjol C", + Name: "C", Href: "/c", - Tagline: "Arena allocator, strings, math, lexer, platform layer.", + Tagline: "Arena allocator, counted strings, math, a lexer, a platform layer — and a build system that is a C file.", + Sub: "a base layer in C", + Live: true, Icon: "bolt", }, { - Name: "Kjol Jai", + Name: "Jai", Href: "/jai", - Tagline: "Console rendering module. Early.", + Tagline: "Console rendering. Early.", Icon: "cube", }, } } -// CurrentLayer is the layer the given path belongs to, or nil on the front page. +// Compositions: what the languages are assembled into. +func Compositions() []Layer { + return []Layer{ + { + Name: "Kjøl Wasm Web", + Href: "/wasm", + Tagline: "Web interfaces written in Go, compiled to WebAssembly. SSR + hydration, and no JavaScript build at all.", + Sub: "Go + WebAssembly", + Live: true, + Icon: "code", + }, + { + Name: "Kjøl JS Web", + Href: "/js", + Tagline: "The Solid component kit, bundled by a Go toolchain: TSX → Solid → esbuild, Tailwind in Go.", + Sub: "Solid + Go toolchain", + Live: true, + Icon: "table", + }, + } +} + +// CurrentLayer is the layer or composition the given path belongs to, or nil on the front +// page. The wordmark uses it to name where you are standing. func CurrentLayer(path string) *Layer { - for i, l := range Layers() { + all := append(Compositions(), Languages()...) + for i, l := range all { if path == l.Href || strings.HasPrefix(path, l.Href+"/") { - return &Layers()[i] + return &all[i] } } return nil } -// LayersMenuCtl is the Layers menu's controller. +// The two menus' controllers. // -// It is created ONCE, here, at package level — not inside layersMenu, which is called -// from a layout on every single render. A floating component is a controller: it owns -// an open signal, a positioning engine and document listeners, and building a fresh one -// per render would leak all three and give you a menu that never opens. Same rule as -// Theme, a few lines up in pages.go. -var LayersMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd}) +// They are created ONCE, here, at package level — not inside the functions below, which a +// layout calls on every single render. A floating component is a controller: it owns an +// open signal, a positioning engine and document listeners, and building a fresh one per +// render would leak all three and give you a menu that never opens. Same rule as Theme, a +// few lines up in pages.go. +// +// TWO menus, not one with two headings inside it. They are different questions — "what is +// this written in" and "what can I read" — and a reader who wants the second should not +// have to scroll past the first to find it. The kit's single-open manager means opening +// one closes the other, so they behave like one control with two halves. +var ( + LayersMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd}) + CompositionsMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd}) +) -// layersMenu is the site's primary navigation: kjol is a stack of layers, and this is -// how you get from any one of them to any other. +// layersMenu lists the LANGUAGES. compositionsMenu, below, lists the frameworks. // -// A layer that is Live is a link. One that is not is inert and dimmed, with the word +// An entry that is Live is a link. One that is not is inert and dimmed, with the word // "reference" on it — it exists, it is documented in the repository, there is simply // nothing here to click. // @@ -98,35 +159,41 @@ var LayersMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd}) // interception — an intercepted click would ask this WebAssembly to render a page it // does not have. func layersMenu(d Deps) *VNode { - content := []*VNode{ - P(Attr("class", "px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-widest text-ink-faint"), - Text("The layers of kjol")), - } - for _, l := range Layers() { + return dropdown(d, LayersMenuCtl, "Layers", Languages()) +} + +// compositionsMenu lists the FRAMEWORKS — the two things assembled out of the layers, and +// the two a reader can actually click into. +func compositionsMenu(d Deps) *VNode { + return dropdown(d, CompositionsMenuCtl, "Compositions", Compositions()) +} + +func dropdown(d Deps, ctl *ui.Menu, label string, rows []Layer) *VNode { + content := make([]*VNode, 0, len(rows)) + for _, l := range rows { content = append(content, layerItem(d, l)) } return Div(Attr("class", "relative"), - LayersMenuCtl.Trigger(ui.MenuTriggerProps{ + ctl.Trigger(ui.MenuTriggerProps{ Class: "inline-flex items-center gap-1.5 rounded-default px-3 py-1.5 text-sm font-medium text-ink-soft hover:bg-surface-raised hover:text-ink", }, - Text("Layers"), + Text(label), ui.IconInline("chevron-down", 11, "text-ink-faint"), ), - LayersMenuCtl.Content("w-96", content...), + ctl.Content("w-96", content...), ) } -// layersGrid is the front page's list of layers — the same data as the menu, laid out -// to be read rather than navigated. A layer with no examples still gets a row: the -// point of the page is what kjol IS, and half of it having no demo yet does not make -// that half not exist. -func layersGrid(d Deps) *VNode { - rows := []Mod{Attr("class", "mt-5 divide-y divide-line rounded-default border border-line")} - for _, l := range Layers() { - rows = append(rows, layerRow(l)) +// layerGrid is the front page's list — the same data as the menu, laid out to be read +// rather than navigated. A layer with no examples still gets a row: the point of the page +// is what kjøl IS, and half of it having no demo yet does not make that half not exist. +func layerGrid(rows []Layer) *VNode { + mods := []Mod{Attr("class", "mt-5 divide-y divide-line rounded-default border border-line")} + for _, l := range rows { + mods = append(mods, layerRow(l)) } - return Div(rows...) + return Div(mods...) } func layerRow(l Layer) *VNode { diff --git a/go/cmd/kjol-web/app/overlays.go b/go/cmd/kjol-web/app/overlays.go deleted file mode 100644 index 658e82c6..00000000 --- a/go/cmd/kjol-web/app/overlays.go +++ /dev/null @@ -1,406 +0,0 @@ -package app - -import ( - "strconv" - - . "kjol/vdom" - ui "kjol/webui" -) - -// Every floating component in the kit, on one page: tooltips, popovers, menus and -// submenus, the date picker, modals (plain, confirm, wizard, imperative), toasts, -// and the tutorial's spotlight coachmarks. -// -// All of them are CONTROLLERS. They own refs, timers and open state, so they are -// created once here — never inside the render closure, which runs on every signal -// write and would rebuild them (and their refs) from scratch every frame. That is -// the single rule to remember about the floating layer. -// -// The page is NOT `static`: nothing is open during SSR anyway, so pre-rendering it -// buys nothing, and it keeps the example honest about which routes need it. -// -//gowasm:page /wasm/overlays layout=app -func OverlaysPage(d Deps) func() *VNode { - // --- tooltips ----------------------------------------------------------- - tipTop := ui.NewHoverTooltip(ui.PlacementTop, "") - tipRight := ui.NewHoverTooltip(ui.PlacementRight, "") - tipFocus := ui.NewFocusTooltip(ui.PlacementBottom, "") - tipFast := ui.NewTooltip(ui.TooltipProps{Placement: ui.PlacementTop, Delay: -1}) - - // --- popovers ----------------------------------------------------------- - pop := ui.NewPopover(ui.PopoverProps{Placement: ui.PlacementBottomStart}) - popEnd := ui.NewPopover(ui.PopoverProps{Placement: ui.PlacementBottomEnd}) - hoverPop := ui.NewHoverPopover(ui.HoverPopoverProps{ - Placement: ui.PlacementTop, - // The bridge: the cursor gets 300ms of grace to cross the gap from the - // trigger onto the panel. Without it, the panel closes in the dead space - // between them — which is exactly what happens once a panel is portaled and - // CSS :hover no longer reaches it. - HoverCloseDelay: 300, - }) - - // --- menus -------------------------------------------------------------- - menu := ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomStart}) - sub := ui.NewSubmenu(menu) - hoverMenu := ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomStart, OpenOnHover: true}) - - // --- date pickers ------------------------------------------------------- - picked := NewSignal("") - dp := ui.NewDatePicker(ui.DatePickerProps{ - Placeholder: "Pick a date", - Clearable: true, - OnChange: func(v string) { picked.Set(v) }, - }) - dob := ui.NewDateOfBirthPicker(ui.DatePickerProps{Placeholder: "Date of birth"}) - - // --- modals ------------------------------------------------------------- - modal := ui.NewModal(ui.ModalOptions{Size: ui.ModalMedium}) - nested := ui.NewModal(ui.ModalOptions{Size: ui.ModalSmall}) - deleted := NewSignal(false) - confirm := ui.NewModal(ui.ModalOptions{}) - - // --- wizard ------------------------------------------------------------- - wizardName := NewSignal("") - wizardDone := NewSignal(false) - wizard := ui.NewWizard(ui.ModalOptions{}) - - // --- toasts ------------------------------------------------------------- - // The Toaster owns the queue AND the clocks: it generates IDs, runs the - // auto-dismiss timer, and animates the countdown bar down to zero. (ToastProvider, - // the dumb half, renders a list you hand it and removes nothing — a toast pushed - // through it stays until you take it away yourself.) - toaster := ui.NewToaster(ui.ToasterOptions{Position: ui.ToastBottomRight}) - pushToast := func(kind ui.ToastType, msg string) { - toaster.Push(ui.Toast{Message: msg, Type: kind}) - } - - // --- tutorial ----------------------------------------------------------- - // Steps target elements by CSS SELECTOR. The tour resolves each one with - // document.querySelector, measures it, scrolls it into view, and cuts a hole in - // the dimmed overlay around it — the spotlight animates from target to target. - tour := ui.NewTutorial(ui.TutorialOptions{ - Steps: []ui.TutorialStep{ - { - Title: "Tooltips", - Target: "#demo-tooltips", - Content: func() *VNode { return Text("Measured, portaled, and they flip near a viewport edge.") }, - }, - { - Title: "Popovers", - Target: "#demo-popovers", - Placement: ui.PlacementBottom, - Content: func() *VNode { return Text("Click or hover. The hover bridge lets you reach the panel.") }, - }, - { - Title: "Menus", - Target: "#demo-menus", - Content: func() *VNode { return Text("Items close the menu themselves; submenus are portaled.") }, - }, - { - // No Target: the page dims flat and the card centres in the viewport. - Title: "That's the tour", - Content: func() *VNode { return Text("Escape ends it. Arrow keys and Enter move between steps.") }, - }, - }, - }) - - return func() *VNode { - return docPage("Components", "Overlays", - "Tooltips, popovers, menus, modals and toasts — every one of them measured against the real "+ - "viewport. A floating panel is portaled to document.body, positioned from its trigger's "+ - "bounding box, and flipped or shifted when it would otherwise run off the screen.", - - docSection("engine", "How a panel is placed", - prose("Positioning is a pure function: given the trigger's rectangle, the panel's size and the "+ - "viewport, it returns coordinates. It is unit-tested natively, with no browser in sight, "+ - "because none of it is about the browser — the browser only supplies the three rectangles."), - prose("The result is written to the element with SetStyle, NOT through a signal. A signal write "+ - "re-renders the whole tree, and this runs on every scroll and resize frame; going through "+ - "the vdom would rebuild the page sixty times a second to move one panel four pixels."), - code("webui/floating.go", floatingSnippet), - note("Controllers are built once", - "A floating component owns refs, timers and its open state. Build it alongside your signals, "+ - "never inside the render closure — one built per frame can never stay open, because the "+ - "thing holding \"open\" is thrown away and replaced before you can see it."), - row("mt-4 flex gap-2", tour.StartButton(0, "", Text("Take the tour"))), - ), - - // ---- tooltips ---- - docSection("demo-tooltips", "Tooltips", - prose("Hover, or focus — a tooltip that only answers to a mouse is a tooltip a keyboard user "+ - "cannot read. Narrow the window and hover the Right one: it flips to the left, and its "+ - "arrow follows it. Near an edge the panel shifts back on screen and the arrow slides to "+ - "keep pointing at the trigger; in the original kit the arrow detached and pointed at "+ - "nothing."), - demo("Placement, delay, and focus triggers", - row("flex flex-wrap items-center gap-3", - tipTop.Render(Span(Text("Above — the default")), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Top"})), - tipRight.Render(Span(Text("To the right, unless it would run off the edge")), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Right"})), - tipFast.Render(Span(Text("No open delay")), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Instant"})), - tipFocus.Render(Span(Text("Shown on focus, not hover — tab to the field")), - ui.FormInput(ui.FormInputProps{Placeholder: "Focus me"})), - ), - ), - ), - - // ---- popovers ---- - docSection("demo-popovers", "Popovers", - prose("A popover closes on an outside click or on Escape — and only the TOPMOST one closes per "+ - "press, so a dropdown inside a popover does not take the popover down with it. The hover "+ - "variant keeps a bridge across the gap between trigger and panel, so the cursor can "+ - "actually reach the thing it opened."), - demo("Click, alignment, and hover-with-a-bridge", - row("flex flex-wrap items-center gap-3", - pop.Trigger(ui.PopoverTriggerProps{}, - ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Click me"})), - pop.Content(ui.PopoverContentProps{Class: "w-64"}, - P(Attr("class", "text-sm text-ink-soft"), - Text("Click outside, or press Escape, to close. Only the topmost floating closes per press.")), - ), - - popEnd.Trigger(ui.PopoverTriggerProps{}, - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Aligned to my right edge"})), - popEnd.Content(ui.PopoverContentProps{Class: "w-56"}, - P(Attr("class", "text-sm text-ink-soft"), Text("Placement bottom-end.")), - ), - - hoverPop.Trigger(ui.PopoverTriggerProps{}, - ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Outline: true, Text: "Hover me, then reach the panel"})), - hoverPop.Content(ui.PopoverContentProps{Class: "w-64"}, - P(Attr("class", "text-sm text-ink-soft"), - Text("Move the cursor across the gap and onto this panel — it stays open. "+ - "Select this text to prove it.")), - ), - ), - ), - ), - - // ---- menus ---- - docSection("demo-menus", "Menus & submenus", - prose("Opening one menu closes the other: a single-open manager keeps the page from filling up "+ - "with panels nobody asked for. Submenus are exempt from it — they are Standalone — or a "+ - "submenu would close the very menu it belongs to as it opened."), - prose("A submenu is portaled too, which is not a detail: the parent menu scrolls its own "+ - "contents, and a submenu rendered inside it was clipped by that overflow the moment it "+ - "was taller than its parent."), - demo("Items, icons, a submenu, and KeepOpen", - row("flex flex-wrap items-center gap-3", - menu.TriggerFunc(ui.MenuTriggerProps{Tag: "div"}, func(open bool) *VNode { - caret := " ▾" - if open { - caret = " ▴" - } - return ui.Button(ui.ButtonProps{Color: ui.ButtonWhite, Text: "Actions" + caret}) - }), - menu.Content("", - menu.Item(ui.MenuItemProps{Icon: "check", OnClick: func() { pushToast(ui.ToastSuccess, "Profile opened") }}, - Text("Profile")), - menu.Item(ui.MenuItemProps{OnClick: func() { pushToast(ui.ToastInfo, "Settings opened") }}, - Text("Settings")), - - // The submenu is portaled — it used to be clipped by the parent - // menu's own overflow-y-auto. - sub.Submenu(ui.SubmenuProps{Trigger: "More", Icon: "ellipsis"}, - sub.Item(ui.MenuItemProps{OnClick: func() { pushToast(ui.ToastInfo, "Archived") }}, Text("Archive")), - sub.Item(ui.MenuItemProps{OnClick: func() { pushToast(ui.ToastWarning, "Duplicated") }}, Text("Duplicate")), - ), - - ui.MenuDivider(""), - // KeepOpen is the TSX's closeOnClick inverted: by default an item - // closes the menu, which the first Go port dropped entirely. - menu.Item(ui.MenuItemProps{KeepOpen: true, OnClick: func() { pushToast(ui.ToastGeneric, "Menu stayed open") }}, - Text("Stay open (KeepOpen)")), - menu.Item(ui.MenuItemProps{Icon: "arrow-right-from-bracket", - OnClick: func() { pushToast(ui.ToastError, "Signed out") }}, Text("Sign out")), - ), - - hoverMenu.TriggerFunc(ui.MenuTriggerProps{Tag: "div"}, func(bool) *VNode { - return ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Opens on hover"}) - }), - hoverMenu.Content("", - hoverMenu.Item(ui.MenuItemProps{}, Text("One")), - hoverMenu.Item(ui.MenuItemProps{}, Text("Two")), - ), - ), - ), - ), - - // ---- date pickers ---- - docSection("demo-dates", "Date picker", - prose("The field is typeable, not merely clickable. It parses loosely — 7/4/26, Jul 4 2026 and "+ - "2026-07-04 all work — and commits what it understood on blur, so the calendar is an "+ - "affordance rather than the only way in."), - demo("Picked: \""+picked.Get()+"\"", - row("grid gap-4 sm:grid-cols-2", - row("flex flex-col gap-1", - ui.FormLabel(ui.FormLabelProps{}, Text("Date (portaled, flips near the bottom)")), - dp.Render(), - ), - row("flex flex-col gap-1", - ui.FormLabel(ui.FormLabelProps{}, Text("Date of birth (inline, three selects)")), - dob.Render(), - ), - ), - ), - ), - - // ---- modals ---- - docSection("demo-modals", "Modals", - prose("Portaled to document.body, so no ancestor's overflow:hidden or transform can clip them. "+ - "Open the modal, then the nested one inside it, and press Escape twice: modals unwind one "+ - "layer per press rather than all at once."), - prose("The last button opens a modal that no component in the tree owns — webui.OpenModal hands "+ - "content to a shared host rendered once in the layout. That is what code far from the view "+ - "needs: a confirmation raised from inside a save handler, say."), - demo("Deleted: "+strconv.FormatBool(deleted.Get()), - row("flex flex-wrap items-center gap-3", - ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Text: "Open modal", OnClick: modal.Open}), - ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Outline: true, Text: "Delete something…", OnClick: confirm.Open}), - ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Outline: true, Text: "Open wizard", OnClick: wizard.Open}), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Text: "Open imperatively", - OnClick: func() { - // No component in the tree owns this one: OpenModal hands content - // to the shared host rendered in the layout. - ui.OpenModal(func() *VNode { - return ui.ModalContent(ui.ModalContentProps{ - Header: H3(Attr("class", "text-lg font-semibold text-text-heading"), Text("Opened from anywhere")), - }, - P(Attr("class", "text-ink-soft"), - Text("This content was not rendered by any component — it was handed to "+ - "ModalHost (see AppLayout) by webui.OpenModal.")), - ) - }, ui.ModalOptions{Size: ui.ModalSmall}) - }}), - ), - ), - - // The modals themselves. They portal to document.body, so where they sit in - // the tree makes no difference to where they appear. - modal.Render(ui.ModalProps{ - Header: H3(Attr("class", "text-lg font-semibold text-text-heading"), Text("A modal")), - Footer: ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Text: "Close", OnClick: modal.Close}), - }, - P(Attr("class", "text-ink-soft"), - Text("Portaled to document.body, so no ancestor's overflow:hidden can clip it. It fades "+ - "and scales in — a double requestAnimationFrame, because a single frame does not "+ - "give the browser time to commit the initial style.")), - row("mt-4", - ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Outline: true, Text: "Open a nested modal", OnClick: nested.Open}), - ), - ), - nested.Render(ui.ModalProps{ - Header: H3(Attr("class", "text-lg font-semibold text-text-heading"), Text("Nested")), - }, - P(Attr("class", "text-ink-soft"), Text("Escape closes THIS one first, not the one behind it.")), - ), - confirm.Confirm(ui.ConfirmModalProps{ - Title: "Delete row", - Message: "This cannot be undone.", - OnConfirm: func() { - deleted.Set(true) - pushToast(ui.ToastError, "Row deleted") - }, - }), - wizard.Render(ui.WizardProps{ - Title: "Set up your account", - FinishText: "Finish", - OnComplete: func() { - wizardDone.Set(true) - pushToast(ui.ToastSuccess, "Wizard complete: "+wizardName.Get()) - }, - Steps: []ui.WizardStep{ - { - Title: "Your name", - // Each step gets its own context: SetCanContinue gates THIS step's - // Next button, which a single shared bool could not express. - Content: func(ctx ui.WizardStepContext) *VNode { - ctx.SetCanContinue(wizardName.Get() != "") - return row("flex flex-col gap-1", - ui.FormLabel(ui.FormLabelProps{}, Text("Name (required to continue)")), - ui.FormInput(ui.FormInputProps{ - Value: wizardName.Get(), - Placeholder: "Ada Lovelace", - OnInput: func(v string) { wizardName.Set(v) }, - }), - ) - }, - }, - { - Title: "Confirm", - Content: func(ctx ui.WizardStepContext) *VNode { - ctx.SetCanContinue(true) - return P(Attr("class", "text-ink-soft"), - Text("All set for "+wizardName.Get()+". Finish to close.")) - }, - }, - }, - }), - ), - - // ---- toasts ---- - docSection("demo-toasts", "Toasts", - prose("They dismiss themselves after five seconds. Watch the bar count down: it is one CSS "+ - "transition, written straight at the element — not a re-render per frame, which is what a "+ - "progress bar driven through a signal would cost you."), - prose("A sticky toast (Duration: ToastSticky) waits for the user instead. The menu items above "+ - "raise toasts too, which is how you can see that an item really does close its own menu."), - demo("Push, dismiss, and a sticky one", - row("flex flex-wrap items-center gap-2", - ui.Button(ui.ButtonProps{Color: ui.ButtonGreen, Small: true, Text: "Success", - OnClick: func() { toaster.Success("Saved.") }}), - ui.Button(ui.ButtonProps{Color: ui.ButtonRed, Small: true, Text: "Error", - OnClick: func() { toaster.Error("Something went wrong.") }}), - ui.Button(ui.ButtonProps{Color: ui.ButtonBlue, Small: true, Text: "Info", - OnClick: func() { toaster.Info("Just so you know.") }}), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, Text: "Sticky (no timer)", - OnClick: func() { - toaster.Push(ui.Toast{ - Message: "This one waits for you to dismiss it.", - Type: ui.ToastWarning, - Duration: ui.ToastSticky, - }) - }}), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, Text: "Clear all", - OnClick: toaster.Clear}), - ), - ), - ), - - docSection("overlay-api", "Reference", - apiTable( - apiRow{"NewFloating", "The positioning engine behind every panel: placement, offset, flip, shift, arrow."}, - apiRow{"NewTooltip / NewPopover / NewMenu", "Controllers. Build once, outside the render."}, - apiRow{"Standalone", "Exempts a panel from the single-open manager. A submenu needs it, or it closes its own parent."}, - apiRow{"vdom.Portal", "Mounts children at document.body — the escape hatch from an ancestor's overflow:hidden."}, - apiRow{"webui.OpenModal / ModalHost", "Open a modal from code that owns no component. Render the host once, in your layout."}, - ), - ), - - // The toast container and the tutorial's overlay both render here; both are - // fixed-position, so where they sit in the tree does not matter. - toaster.Render(), - tour.Render(), - ) - } -} - -const floatingSnippet = `// Built ONCE — it owns refs, timers, and whether it is open. -pop := ui.NewPopover(ui.PopoverOptions{ - Placement: ui.PlacementBottomStart, - Offset: 8, -}) - -// ...and in the render: -pop.Trigger(ui.PopoverTriggerProps{}, - ui.Button(ui.ButtonProps{Text: "Click me"}), -) -pop.Content(ui.PopoverContentProps{Class: "w-64"}, - P(Text("Outside click and Escape close me.")), -) - -// The panel is portaled to document.body and positioned imperatively: -// render invisible -> AfterRender -> measure -> ComputePosition -> SetStyle -> reveal -// Never through a signal: this runs on every scroll frame.` diff --git a/go/cmd/kjol-web/app/pages.go b/go/cmd/kjol-web/app/pages.go index 43772af9..b12495ac 100644 --- a/go/cmd/kjol-web/app/pages.go +++ b/go/cmd/kjol-web/app/pages.go @@ -17,10 +17,6 @@ import ( . "kjol/vdom" - // The host API is dual-build — real under js/wasm, no-ops natively — so neutral page - // code can measure the browser and still server-render. The landing page uses it for - // exactly one thing: reading the clock when hydration commits. - "kjol/wasmruntime" ui "kjol/webui" ) @@ -67,16 +63,18 @@ func notFound(path string) *VNode { // wordmark is the brand lockup, shared by both layouts so they cannot drift. // -// The boat is the point of the name: kjol is Norwegian for KEEL — the spine of a hull, +// The boat is the point of the name: kjøl is Norwegian for KEEL — the spine of a hull, // the thing every other part is built onto. Which is what this library is meant to be // for the applications that share it. func wordmark(d Deps, href string) *VNode { // The lockup names the LAYER you are standing in, not the site. On the front page - // that is kjol itself; inside /wasm it is Kjol Wasm Web. A wordmark that says the - // same thing everywhere is one more thing the reader has to keep track of himself. - name, sub := "kjol", "a shared base layer" + // that is Kjøl itself; inside /wasm it is Kjøl Wasm Web; inside /c it is Kjøl C — + // Wordmark, not Name, because up here "C" alone names a language rather than the thing + // you are reading. A wordmark that says the same thing everywhere is one more thing the + // reader has to keep track of himself. + name, sub := "Kjøl", "a shared base layer" if l := CurrentLayer(d.Path()); l != nil { - name, sub = l.Name, "Go + WebAssembly" + name, sub = l.Wordmark(), l.Sub } return A(Attr("class", "flex items-center gap-2.5 no-underline"), Attr("href", href), navigate(d, href), @@ -103,22 +101,39 @@ func PublicLayout(d Deps, content *VNode) *VNode { // read aloud. Div(Attr("class", "pointer-events-none fixed inset-0 -z-10 bg-grid grid-fade"), Attr("aria-hidden", "true")), + // The nav, the content and the footer are ONE column, and the way to get that is for + // all three to be built the same way: gutter on the outside, measure on the inside. + // + //
… + // + // This used to be `mx-auto max-w-3xl px-4` on the nav's inner div — measure and gutter + // on the SAME element. On a wide screen the gutter has nothing to do (the centring has + // already pushed the box in much further), so all it did was inset the nav's contents + // by another 16px: the wordmark sat a finger's width to the right of the headline + // underneath it. Close enough to look like a mistake, far enough to see. + // + // The footer was worse — it was max-w-2xl, a different measure entirely. Nav(Attr("class", "site-nav border-b border-line"), - Div(Attr("class", "mx-auto flex max-w-3xl items-center gap-2 px-4 py-4"), - wordmark(d, "/"), - Div(Attr("class", "ml-auto flex items-center gap-1"), - layersMenu(d), - Ul(Attr("class", "flex items-center gap-1"), - navItem(d, "/about", "About", false), - Li(Attr("class", "ml-1"), Theme.ThemeToggle(ui.ThemeToggleProps{Small: true})), - ), - ))), + Div(Attr("class", "px-4"), + Div(Attr("class", "mx-auto flex max-w-3xl items-center gap-2 py-4"), + wordmark(d, "/"), + Div(Attr("class", "ml-auto flex items-center gap-1"), + layersMenu(d), + compositionsMenu(d), + Ul(Attr("class", "flex items-center gap-1"), + navItem(d, "/about", "About", false), + Li(Attr("class", "ml-1"), Theme.ThemeToggle(ui.ThemeToggleProps{Small: true})), + ), + )))), Main(Attr("class", "px-4 py-14"), content), - Footer(Attr("class", "mx-auto max-w-2xl px-4 pb-14"), - P(Attr("class", "text-sm text-ink-faint"), - Text("kjol is a shared base layer, factored out of several applications so they stay in sync. It is Norwegian for keel.")), + Footer(Attr("class", "px-4 pb-14"), + Div(Attr("class", "mx-auto max-w-3xl"), + P(Attr("class", "text-sm text-ink-faint"), + Text("Kjøl is a shared base layer, factored out of several applications so they stay in "+ + "sync. It is Norwegian for keel.")), + ), ), ui.ModalHost(), ) @@ -127,7 +142,7 @@ func PublicLayout(d Deps, content *VNode) *VNode { // wideRoutes get a roomier container. A table with a dozen columns, a drag handle // and three calculated columns has no business being squeezed into a reading-width // column; prose pages still are. -var wideRoutes = map[string]bool{"/wasm/table": true} +var wideRoutes = map[string]bool{"/wasm/components": true} // AppLayout is the DOCUMENTATION shell: a sidebar of sections on the left, the page on // the right. The app routes are the framework's docs — each one explains a capability, @@ -155,6 +170,7 @@ func AppLayout(d Deps, content *VNode) *VNode { Span(Attr("class", "rounded-full border border-line px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wider text-ink-faint"), Text("Docs")), Div(Attr("class", "ml-auto flex items-center gap-2"), layersMenu(d), + compositionsMenu(d), Ul(Attr("class", "flex items-center gap-2"), navItem(d, "/", "Home", false), Li(Theme.ThemeToggle(ui.ThemeToggleProps{Small: true})), @@ -179,9 +195,22 @@ func AppLayout(d Deps, content *VNode) *VNode { // docsSidebar is the section list. Sticky, so it stays put while a long page scrolls — // on a documentation site the nav is how you know where you are, and a nav that scrolls // away leaves you nowhere. +// sidebarNav is the sidebar's contents, which depend on WHICH LAYER you are reading. +// +// AppLayout is shared by every documentation page in this binary, and those pages are no +// longer all about the same thing: /wasm/* documents the Go→WebAssembly engine, /c +// documents the C base layer. A sidebar listing the engine's chapters while you are +// reading about arenas would be worse than no sidebar at all. +func sidebarNav(path string) []docsGroup { + if path == "/c" || strings.HasPrefix(path, "/c/") { + return cNav() + } + return docsNav() +} + func docsSidebar(d Deps) *VNode { mods := []Mod{Attr("class", "sticky top-[3.75rem] hidden h-[calc(100vh-3.75rem)] w-56 shrink-0 overflow-y-auto py-10 lg:block")} - for _, g := range docsNav() { + for _, g := range sidebarNav(d.Path()) { items := []Mod{Attr("class", "mt-2 space-y-0.5")} for _, it := range g.Items { items = append(items, Li(sidebarLink(d, it))) @@ -197,13 +226,30 @@ func docsSidebar(d Deps) *VNode { } func sidebarLink(d Deps, it docsItem) *VNode { + base, frag, isAnchor := strings.Cut(it.Path, "#") + cls := "flex items-center gap-2 rounded-default px-2 py-1.5 text-sm no-underline text-ink-soft hover:bg-surface-raised hover:text-ink" iconCls := "text-ink-faint" - if d.Path() == it.Path { + + // A section link is NEVER "active", and that is deliberate. It cannot be: it would + // have to know which section you had scrolled to, which means measuring all fifteen of + // them on every scroll frame, and the only way to act on the answer is a signal write + // — which re-renders this entire page. Sixty times a second, to move a highlight. + // + // (Marking them active by PAGE instead lights up all fifteen at once, which is worse + // than no highlight: it tells you nothing and looks broken.) + active := !isAnchor && d.Path() == it.Path + if active { cls = "active flex items-center gap-2 rounded-default px-2 py-1.5 text-sm no-underline bg-primary-subtle font-medium text-accent" iconCls = "text-accent" } - return A(Attr("class", cls), Attr("href", it.Path), navigate(d, it.Path), + + click := navigate(d, it.Path) + if isAnchor { + click = navigateAnchor(d, base, frag) + } + + return A(Attr("class", cls), Attr("href", it.Path), click, ui.IconInline(it.Icon, 14, iconCls), Text(it.Label), ) @@ -251,122 +297,56 @@ func Counter(label string, count *Signal[int]) *VNode { // ---- landing ------------------------------------------------------------ -// The landing page is one narrow column of plain text, a demo, and a list. +// The landing page is a column of plain text and two lists. // -// It used to be a framework marketing page: an oversized headline, a hero glow, feature -// cards in a grid, numbered chapters, a call to action repeated at both ends. All of it -// was arguing. None of it was showing. A library this small does not need to argue — it -// needs to say what it is, show that it works, and get out of the way, and a reader who -// wants to be convinced can click into the docs and find every page running the code it -// documents. +// It used to carry the Wasm Web engine's own highlights: the two-runtime demo, a list of +// SSR/hydration/server-component features, the build transcript. All of it was true, and +// none of it belonged HERE — the front page is kjøl's, and kjøl is not the Go/WebAssembly +// engine any more than it is the C arena allocator. A reader landing on it should learn +// what the thing IS, not be pitched one of its five parts. // -// What survives is the part that could not be faked: the same Go function rendered twice -// at once, as live DOM and as the HTML string the server sends. +// So the demo moved to /wasm, where it is the first thing that section shows, and the +// front page says what is actually true of the whole: here are the languages, here are the +// frameworks assembled out of them, go and read one. // //gowasm:page / static layout=public func HomePage(d Deps) func() *VNode { - clicks := NewSignal(0) - - // The one measurement on the page: performance.now() when the client's first render - // commits. Zero until then — which is what the SERVER renders, and what the client - // renders on its first pass, so the two agree and hydration stays clean. - hydratedAt := NewSignal(0.0) - wasmruntime.AfterRender(func() { - if hydratedAt.Get() == 0 { - hydratedAt.Set(wasmruntime.Now()) - } - }) - - // demoTree is called TWICE per render below — once for the DOM, once for the HTML. - // That is the point: the two panes cannot drift, because there is only one of them. - demoTree := func() *VNode { - return Div(Attr("class", "flex items-center gap-3"), - ui.Button(ui.ButtonProps{ - Color: ui.ButtonPrimary, Text: "Click me", - OnClick: func() { clicks.Set(clicks.Get() + 1) }, - }), - Span(Attr("class", "text-ink-soft"), Text("clicked "+itoa(clicks.Get())+" times")), - ) - } - return func() *VNode { - markup := RenderHTML(demoTree()) - return Div(Attr("class", "mx-auto max-w-3xl"), H1(Attr("class", "text-3xl font-semibold tracking-tight text-text-heading"), - Text("kjol")), + Text("Kjøl")), P(Attr("class", "mt-3 leading-relaxed text-ink-soft"), Text("A shared base layer, factored out of several applications so they stay in sync. "+ - "Kjol is Norwegian for KEEL: the spine of a hull, the thing every other part is built onto.")), + "Kjøl is Norwegian for KEEL: the spine of a hull, the thing every other part is built onto.")), P(Attr("class", "mt-3 leading-relaxed text-ink-soft"), - Text("It is not one library. It is a stack of them, in several languages, and each one is "+ - "documented here.")), + Text("It is not one library. It is a set of them, in several languages, and a couple of "+ + "frameworks assembled out of those. Each one is documented here, and every page of that "+ + "documentation runs the code it documents.")), - // ---- the layers ---- - // - // The layers are the site. Everything else on this page is evidence that they - // work; this is the part you are meant to click. - H2(Attr("class", "mt-12 text-lg font-semibold text-text-heading"), Text("The layers")), - layersGrid(d), - - // ---- the demonstration ---- - // - // This survives from the old landing page because it is the one thing on the site - // that cannot be faked: the same Go function, rendered twice at once, as live DOM - // and as the HTML string the server sent. - H2(Attr("class", "mt-12 text-lg font-semibold text-text-heading"), Text("One function, two runtimes")), + // ---- layers: the languages ---- + H2(Attr("class", "mt-12 text-lg font-semibold text-text-heading"), Text("Layers")), P(Attr("class", "mt-2 leading-relaxed text-ink-soft"), - Text("Below is a single Go function, shown twice. On the left it has been reconciled into "+ - "the DOM and you can use it. On the right is the HTML the same function produces when "+ - "the server renders it — the markup that reached your browser before any WebAssembly "+ - "had loaded. Click the button; both move.")), + Text("What Kjøl is written in, and what it gives you in each. A layer is a directory of "+ + "code you can use on its own — the Go base does not know the C one exists.")), + layerGrid(Languages()), - Div(Attr("class", "mt-5 border border-line sm:grid sm:grid-cols-2"), - Div(Attr("class", "border-b border-line sm:border-b-0 sm:border-r"), - paneLabel("in your browser"), - Div(Attr("class", "px-4 py-8"), demoTree()), - ), - Div( - paneLabel(itoa(len(markup))+" bytes of HTML"), - Pre(Attr("class", "whitespace-pre-wrap px-4 py-4 font-mono text-[12px] leading-relaxed text-ink-muted"), - El("code", Text(prettyHTML(markup))), - ), - ), - ), - P(Attr("class", "mt-3 text-sm leading-relaxed text-ink-muted"), - Text("The right pane is not a picture of the source. It is vdom.RenderHTML, called on the "+ - "very tree the left pane is showing, recomputed on every click.")), - - // ---- what is in it ---- - H2(Attr("class", "mt-12 text-lg font-semibold text-text-heading"), Text("What is in it")), - Ul(Attr("class", "mt-3 space-y-1.5 leading-relaxed text-ink-soft"), - item("Server-side rendering and client hydration, from one codebase."), - item("Server components: mark a function and its code and state stay on the server."), - item("A component kit — forms, tabs, modals, tooltips, toasts — with dark mode."), - item("A table with filtering, sorting, column management, formulas, and CSV and PDF export."), - item("Tailwind, compiled by a Go program that reads your Go."), - ), - - // ---- building ---- - H2(Attr("class", "mt-12 text-lg font-semibold text-text-heading"), Text("Building it")), + // ---- compositions: the frameworks ---- + H2(Attr("class", "mt-12 text-lg font-semibold text-text-heading"), Text("Compositions")), P(Attr("class", "mt-2 leading-relaxed text-ink-soft"), - Text("Two commands. The first produced the page you are reading; the second serves it and "+ - "rebuilds on save.")), - codeLang("terminal", "sh", buildTranscript), + Text("What the layers become when they are assembled into something that does a job. A "+ + "composition is not another language: Kjøl Wasm Web is Go all the way down, and Kjøl JS "+ + "Web is TypeScript compiled by a Go toolchain. These are the two you can click into.")), + layerGrid(Compositions()), // ---- close ---- P(Attr("class", "mt-12 border-t border-line pt-6 leading-relaxed text-ink-soft"), - Text("Every page of the documentation runs the code it documents — there are no screenshots "+ - "of components anywhere on this site. "), + Text("There is not a screenshot of a component anywhere on this site. Every example is the "+ + "real thing, running — which is the only way a documentation page can tell you when it "+ + "has gone stale. "), A(Attr("class", "text-accent underline underline-offset-4"), - Attr("href", "/wasm"), navigate(d, "/wasm"), Text("Read the docs")), - Text(", or "), - A(Attr("class", "text-accent underline underline-offset-4"), - Attr("href", "/wasm/kit"), navigate(d, "/wasm/kit"), Text("look at the components")), + Attr("href", "/about"), navigate(d, "/about"), Text("Why this exists")), Text("."), ), - P(Attr("class", "mt-4 text-sm text-ink-muted"), - Text(hydrationNote(hydratedAt.Get()))), ) } } @@ -404,10 +384,14 @@ func prettyHTML(s string) string { return strings.ReplaceAll(s, "><", ">\n<") } -const buildTranscript = `$ go run ./build +// The real transcript. It is on the front page, so it is the first thing anybody copies — +// which makes it the first thing to notice when it goes stale. +const buildTranscript = `$ go run ./server -build ==> generating directive glue (//gowasm:page, //gowasm:layout, //gowasm:server) ==> compiling Tailwind CSS -> wwwroot/app.css ==> compiling ./wasm -> wwwroot/app.wasm (GOOS=js GOARCH=wasm) +==> bundling the Solid app -> wwwroot/bundle.min.{js,css} (TSX -> Solid -> esbuild) +==> copying Go's wasm_exec.js shim into wwwroot/ $ go run ./server serving "./wwwroot" on http://localhost:8085` @@ -422,16 +406,22 @@ func AboutPage(d Deps) func() *VNode { H1(Attr("class", "mt-2 text-4xl font-semibold tracking-tight text-text-heading"), Text("Why this exists")), P(Attr("class", "mt-6 text-lg leading-relaxed text-ink-soft"), - Text("Kjol Web is one part of kjol — a shared base layer factored out of several applications "+ - "so they stay in sync. (Kjol is Norwegian for KEEL: the spine of a hull, the thing every "+ - "other part is built onto.) The applications had drifted: the same table, the same forms, "+ - "the same charts, each subtly different in each app, each fixed twice.")), + Text("Kjøl is a shared base layer, factored out of several applications so they stay in sync. "+ + "(Kjøl is Norwegian for KEEL: the spine of a hull, the thing every other part is built "+ + "onto.) The applications had drifted: the same table, the same forms, the same charts, "+ + "each subtly different in each app, each fixed twice.")), P(Attr("class", "mt-4 leading-relaxed text-ink-soft"), - Text("The UI kit began as Solid.js components. Kjol Web is the same kit, written in Go and "+ - "compiled to WebAssembly — the same components, the same Tailwind, no JavaScript build. "+ - "That means one language across the server and the browser, and a table you can share "+ - "between a web app and a native one because it is a Go function, not a JSX file.")), + Text("The UI kit began as Solid.js components, and it still is — that is Kjøl JS Web, and it "+ + "is what those applications run today. Kjøl Wasm Web is the same kit written a second time "+ + "in Go and compiled to WebAssembly: the same components, the same Tailwind, no JavaScript "+ + "build at all. One language across the server and the browser, and a table you could share "+ + "with a native app, because it is a Go function rather than a JSX file.")), + + P(Attr("class", "mt-4 leading-relaxed text-ink-soft"), + Text("Neither of them is Kjøl. They are two compositions of it — two uses of the layers "+ + "underneath, which are just directories of Go, TypeScript, C and Jai. The front page lists "+ + "both, and does not argue for either.")), H2(Attr("class", "mt-12 text-2xl font-semibold tracking-tight text-text-heading"), Text("The rules it keeps")), Div(Attr("class", "mt-6 space-y-4"), diff --git a/go/cmd/kjol-web/app/routes.gen.go b/go/cmd/kjol-web/app/routes.gen.go index 9340b329..7f9cf671 100644 --- a/go/cmd/kjol-web/app/routes.gen.go +++ b/go/cmd/kjol-web/app/routes.gen.go @@ -6,15 +6,14 @@ import "kjol/vdom" // Routes maps each //gowasm:page path to its instantiated render function. func Routes(d Deps) map[string]func() *vdom.VNode { return map[string]func() *vdom.VNode{ - "/": HomePage(d), - "/about": AboutPage(d), - "/wasm": DocsPage(d), - "/wasm/chart": ChartPage(d), - "/wasm/data": DataPage(d), - "/wasm/kit": KitPage(d), - "/wasm/overlays": OverlaysPage(d), - "/wasm/server": ServerPage(d), - "/wasm/table": TablePage(d), + "/": HomePage(d), + "/about": AboutPage(d), + "/c": CPage(d), + "/wasm": DocsPage(d), + "/wasm/chart": ChartPage(d), + "/wasm/components": ComponentsPage(d), + "/wasm/data": DataPage(d), + "/wasm/server": ServerPage(d), } } @@ -22,23 +21,22 @@ func Routes(d Deps) map[string]func() *vdom.VNode { var StaticPaths = map[string]bool{ "/": true, "/about": true, + "/c": true, "/wasm": true, "/wasm/chart": true, "/wasm/data": true, - "/wasm/table": true, } // RouteLayout maps each route to the name of the layout that wraps it. var RouteLayout = map[string]string{ - "/": "public", - "/about": "public", - "/wasm": "app", - "/wasm/chart": "app", - "/wasm/data": "app", - "/wasm/kit": "app", - "/wasm/overlays": "app", - "/wasm/server": "app", - "/wasm/table": "app", + "/": "public", + "/about": "public", + "/c": "app", + "/wasm": "app", + "/wasm/chart": "app", + "/wasm/components": "app", + "/wasm/data": "app", + "/wasm/server": "app", } // LayoutFor wraps a page's content in the layout declared for its route. diff --git a/go/cmd/kjol-web/app/ssr_test.go b/go/cmd/kjol-web/app/ssr_test.go index 4da6c1a5..2bc8f9df 100644 --- a/go/cmd/kjol-web/app/ssr_test.go +++ b/go/cmd/kjol-web/app/ssr_test.go @@ -11,7 +11,7 @@ import ( ) func TestSSRPages(t *testing.T) { - for _, path := range []string{"/", "/about", "/wasm/chart", "/wasm/data", "/wasm/table", "/wasm/overlays", "/wasm/kit"} { + for _, path := range []string{"/", "/about", "/wasm/chart", "/wasm/data", "/wasm/components"} { deps := Deps{Path: func() string { return path }} html := vdom.RenderHTML(Shell(deps, Routes(deps))) t.Logf("%-8s %6d bytes portals=%d", path, len(html), strings.Count(html, "data-portal")) @@ -22,7 +22,7 @@ func TestSSRPages(t *testing.T) { } func TestSSRTablePage(t *testing.T) { - deps := Deps{Path: func() string { return "/wasm/table" }} + deps := Deps{Path: func() string { return "/wasm/components" }} html := vdom.RenderHTML(Shell(deps, Routes(deps))) // The table persists a personal layout in localStorage, which the SERVER CANNOT @@ -33,13 +33,21 @@ func TestSSRTablePage(t *testing.T) { // This is a real cost — the page ships no table content — and it is the price of // never showing the wrong table. See webui.RestoreLayout. if !strings.Contains(html, `aria-busy="true"`) { - t.Error("SSR /table should render the loading skeleton, not a table") + t.Error("SSR should render the AutoTable's loading skeleton, not a table") } if !strings.Contains(html, "animate-pulse") { t.Error("the skeleton bars are missing") } - if strings.Contains(html, "Ada Lovelace") { - t.Error("SSR rendered table CONTENT — a user with a saved layout would watch it rearrange") + + // A salary, which ONLY the AutoTable renders. + // + // This used to look for "Ada Lovelace", which was a fine proxy back when the table + // had a page to itself. It is not one any more: the components page also demos + // PrettyTable, and PrettyTable's rows are Ada, Alan and Grace — so the old assertion + // failed on a page that was behaving perfectly. A test that names a value only the + // component under test can produce cannot be fooled by its neighbours. + if strings.Contains(html, "$1,610.25") { + t.Error("SSR rendered AutoTable CONTENT — a user with a saved layout would watch it rearrange") } } diff --git a/go/cmd/kjol-web/app/table.go b/go/cmd/kjol-web/app/table.go index 51d1b738..609a0dfd 100644 --- a/go/cmd/kjol-web/app/table.go +++ b/go/cmd/kjol-web/app/table.go @@ -174,159 +174,6 @@ func newEmployeeTable(highlight *Signal[string]) *ui.AutoTableState { }) } -//gowasm:page /wasm/table layout=app static -func TablePage(d Deps) func() *VNode { - // Which row to spotlight, if any. - highlight := NewSignal("") - - table := newEmployeeTable(highlight) - table.SetRows(employees()) - - // The export menu, with a submenu for the PDF's page orientation. Both are - // controllers, both built once. A submenu is Standalone — opening it must not - // close the menu it lives in. - exportMenu := ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd}) - pdfSub := ui.NewSubmenu(exportMenu) - - // What the PDF prints above the table. - // - // Note what is NOT here: the footer lines. The export takes the table's OWN - // summary rows — including any the user builds at runtime in the Calculated - // editor — and evaluates them against the same filtered rows it is printing. Only - // pass Summaries explicitly to print something that is not one of the table's own - // rows. - pdfHeader := func(landscape bool) ui.AutoTablePDFHeader { - orientation := ui.PDF_ORIENTATION_PORTRAIT - if landscape { - orientation = ui.PDF_ORIENTATION_LANDSCAPE - } - return ui.AutoTablePDFHeader{ - Title: "Employees", - Subtitle: "Exported from the Kjol Web example", - ShowDate: true, - Orientation: orientation, - } - } - - return func() *VNode { - return docPage("Components", "AutoTable", - "A table that filters, sorts, pages, reorders, resizes, computes and exports — configured with "+ - "a column list and a slice of rows. Everything a user changes about it is theirs and persists; "+ - "everything it exports is what they filtered, not what happened to be on screen.", - - docSection("defining", "Defining one", - prose("A column says how to read a field, how to sort it, and how to render it. The state object "+ - "is a CONTROLLER: build it once, alongside your signals — never inside the render, which "+ - "would hand it fresh refs and a fresh idea of which page it was on every frame."), - code("app/table.go", tableSnippet), - note("The server renders a skeleton, on purpose", - "The layout — column order, widths, what is hidden, the calculated columns — lives in the "+ - "browser's localStorage, which the server cannot read. So the server ships a skeleton "+ - "rather than the DEFAULT table: a user who had reordered their columns would otherwise "+ - "watch them rearrange themselves the moment the WebAssembly booted."), - ), - - docSection("try-it", "Try it", - prose("Search matches name or email. Sort by Salary and it parses the currency, so $980 sorts "+ - "below $1,200.50. Unhide Rank and sort that: \"Item 2\" comes before \"Item 10\", because "+ - "numbers inside text are compared as numbers. Drag a header to reorder it, drag its right "+ - "edge to resize — reload the page and both are still where you left them."), - prose("Filter it, then export. You get every matching row across every page, in the column order "+ - "you dragged them into, with the calculated columns computed per row."), - ), - - table.Render( - ui.AutoTableWithHover(), - ui.AutoTableWithAlternate(), - ui.AutoTableWithSurroundingBorder(), - ui.AutoTableWithPaginationShowAll(), - ui.AutoTableWithSearchFields( - // One box, several fields: a global search. - table.GlobalSearch("Search name or email…", "Name", "Email"), - // Exact-match dropdown. - table.SelectSearch("Status", []string{"active", "inactive"}, "Any status"), - // IN-set: matches any of the selected teams. - table.MultiSelectSearch("Team", "Any team", []string{"Engineering", "Research", "Networking"}), - ), - ui.AutoTableWithToolbarActions( - table.ColumnPicker(), - - // Build calculated columns and footer rows at runtime. Basic picks a - // function and the columns it combines across each row; Advanced writes - // a formula, with insert menus for columns, functions and constants. - // The formula is compiled and previewed against the real first row as - // you type, so a typo shows up immediately rather than as a column of - // dashes. What you build is persisted with the rest of the layout. - table.CalculatedColumnEditor(), - - // Export writes what the FILTER selected — every matching row across - // every page — not the five rows on screen. And it writes the columns - // you can actually see, in the order you dragged them into. - exportMenu.TriggerFunc(ui.MenuTriggerProps{Tag: "div"}, func(open bool) *VNode { - return ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, - Icon: "download", Text: "Export"}) - }), - exportMenu.Content("", - exportMenu.Item(ui.MenuItemProps{Icon: "file-csv", - OnClick: func() { table.DownloadCSV("employees") }}, Text("Download CSV")), - - // A submenu — portaled, so it is not clipped by the menu's own - // overflow-y-auto, which is what broke it before. - pdfSub.Submenu(ui.SubmenuProps{Trigger: "Download PDF", Icon: "file-pdf"}, - pdfSub.Item(ui.MenuItemProps{ - OnClick: func() { table.DownloadPDF("employees", pdfHeader(false)) }}, Text("Portrait")), - pdfSub.Item(ui.MenuItemProps{ - OnClick: func() { table.DownloadPDF("employees", pdfHeader(true)) }}, Text("Landscape")), - ), - - ui.MenuDivider(""), - exportMenu.Item(ui.MenuItemProps{Icon: "print", - OnClick: func() { table.PrintPDF(pdfHeader(true)) }}, Text("Print")), - ), - ), - ), - - // Highlight + auto-page-jump: Radia is on page 3 by default, and the table - // pages itself to wherever she actually is once filters and sorting move her. - row("mt-4 flex flex-wrap items-center gap-2", - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, - Text: "Find Radia Perlman", - OnClick: func() { highlight.Set("radia@example.com") }}), - ui.Button(ui.ButtonProps{Color: ui.ButtonLightNeutral, Small: true, - Text: "Clear highlight", - OnClick: func() { highlight.Set("") }}), - ), - - docSection("calculated", "Calculated columns", - prose("The toolbar's calculator builds new columns at runtime, in two modes. Basic picks a "+ - "function and the columns it combines ACROSS each row — sum of Salary and Bonus, per "+ - "person. Advanced writes a formula, with insert menus for columns, functions and constants: "+ - "([Salary] + [Bonus]) * 12."), - prose("A summary row is the other axis: it aggregates ONE column DOWN the filtered rows and "+ - "prints the result in the footer. Confusing the two is the classic bug here — a column that "+ - "aggregates down shows every row the same number, and it looks plausible enough to ship."), - codeLang("formulas", "syntax", formulaSnippet), - note("Compiled as you type", - "The formula is parsed and evaluated against the real first row while you write it, so a "+ - "typo shows up as an error under the box — not as a column of dashes discovered later."), - ), - - docSection("export", "Export", - prose("CSV and PDF are written in Go, standard library only — the PDF writer builds its own "+ - "xref table and embeds Helvetica metrics. Export takes the FILTERED rows, the VISIBLE "+ - "columns, in the user's order, including whatever they calculated."), - apiTable( - apiRow{"NewAutoTableState", "Build the controller: the columns, and where to persist the layout."}, - apiRow{".SetRows", "Hand it the data. It filters, sorts and pages from there."}, - apiRow{".RestoreLayout", "Read the saved layout and reveal the table over its skeleton. Call it once, on the client."}, - apiRow{".FilteredRows / .ExportColumns", "What the user selected, and what they can see — the inputs to any export."}, - apiRow{"ExportCSV / ExportPDF", "Write the bytes. DownloadCSV / DownloadPDF / PrintPDF do it and hand them to the browser."}, - ), - ), - ) - } -} - const tableSnippet = `// A controller: built ONCE, next to your signals — never inside the render. table := ui.NewAutoTableState([]ui.AutoTableColumn{ {DisplayName: "Name", SortIdentifier: "Name", Sortable: true, diff --git a/go/cmd/kjol-web/app/landing_test.go b/go/cmd/kjol-web/app/tworuntimes_test.go similarity index 84% rename from go/cmd/kjol-web/app/landing_test.go rename to go/cmd/kjol-web/app/tworuntimes_test.go index 7b15117c..85fcaea3 100644 --- a/go/cmd/kjol-web/app/landing_test.go +++ b/go/cmd/kjol-web/app/tworuntimes_test.go @@ -7,15 +7,18 @@ import ( "kjol/vdom" ) -// The landing page's whole claim is that its two panes are ONE function: the live -// component on the left, and the HTML string the server sends on the right. If they -// could drift, the page would be a lie told in the most embarrassing possible place. +// The two-runtime demo.s whole claim is that its two panes are ONE function: the live +// component on the left, and the HTML string the server sends on the right. If they could +// drift, the page would be a lie told in the most embarrassing possible place. +// +// It lives on /wasm now, not on the front page — it is the Wasm Web engine.s argument, +// and the front page is kjøl.s. The test followed it. // // So: render it, click the button the way the browser would, render again, and check // that BOTH panes moved. A pane rendered from a stale copy of the tree — or from a // second, hand-written one — fails here. -func TestLandingPanesShareOneTree(t *testing.T) { - page := HomePage(Deps{Path: func() string { return "/" }}) +func TestTwoRuntimePanesShareOneTree(t *testing.T) { + page := DocsPage(Deps{Path: func() string { return "/wasm" }}) html := vdom.RenderHTML(page()) if !strings.Contains(html, "clicked 0 times") { @@ -38,8 +41,8 @@ func TestLandingPanesShareOneTree(t *testing.T) { // The byte count under the right-hand pane is the length of the string actually shown, // not a number typed in by hand — so it has to move when the markup does. -func TestLandingByteCountIsReal(t *testing.T) { - page := HomePage(Deps{Path: func() string { return "/" }}) +func TestTwoRuntimeByteCountIsReal(t *testing.T) { + page := DocsPage(Deps{Path: func() string { return "/wasm" }}) before := byteCountLabel(t, vdom.RenderHTML(page())) clickButton(t, page(), "Click me") @@ -60,7 +63,7 @@ func byteCountLabel(t *testing.T, html string) string { t.Helper() i := strings.Index(html, " bytes of HTML") if i < 0 { - t.Fatal("no byte-count caption on the landing page") + t.Fatal("no byte-count caption on the /wasm overview") } start := strings.LastIndexByte(html[:i], '>') + 1 return html[start : i+len(" bytes of HTML")] diff --git a/go/cmd/kjol-web/build/build.go b/go/cmd/kjol-web/build/build.go new file mode 100644 index 00000000..090b1a07 --- /dev/null +++ b/go/cmd/kjol-web/build/build.go @@ -0,0 +1,177 @@ +// Package build is the build pipeline: directive codegen, Tailwind, the wasm binary, +// the Solid bundle, and Go's JS shim. +// +// It used to be two packages — a `buildsteps` library and a `build` command that did +// nothing but loop over it and print. There was nothing for that split to be: the +// library had exactly two importers, both in this directory tree, and a package with no +// outside consumers is an import statement pretending to be a boundary. It is one +// package now. +// +// It cannot be `package main`, because the dev server imports it and Go will not let you +// import a main. So the cold build is a flag on the server rather than a second binary: +// +// go run ./server -build # build once and exit +// go run ./server # build, then watch and serve +// +// It is Go rather than a shell script for three reasons. The dev server has to call +// these steps on every save and cannot shell out to bash on Windows. Editors need to run +// them as tasks, and a task that only works on one platform is a task half the team +// cannot use. And the cold build and the watch build must be the SAME steps — the moment +// they are two scripts they drift, and the bug only shows up in whichever one you use +// less. +package build + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "strings" + + "kjol/jsbundler" +) + +// kjolRoot is the kjol Go module root, relative to the app. The Tailwind and codegen +// commands are run FROM there so the engine's dependencies resolve in kjol's own go.mod, +// and this app's stays lean. +const kjolRoot = "../.." + +// Wwwroot is where every build artefact lands, and what the server serves. +const Wwwroot = "wwwroot" + +// Step is one named stage. Naming them lets the cold build narrate itself without the +// watch loop having to care what they are called. +type Step struct { + Name string + Run func() ([]byte, error) +} + +func Steps() []Step { + return []Step{ + {"generating directive glue (//gowasm:page, //gowasm:layout, //gowasm:server)", Codegen}, + {"compiling Tailwind CSS -> wwwroot/app.css", Tailwind}, + {"compiling ./wasm -> wwwroot/app.wasm (GOOS=js GOARCH=wasm)", Wasm}, + {"bundling the Solid app -> wwwroot/bundle.min.{js,css} (TSX -> Solid -> esbuild)", JS}, + {"copying Go's wasm_exec.js shim into wwwroot/", Shim}, + } +} + +// All is the full build, in order. It is what the dev server runs on a code change. +// +// The returned bytes are the failing command's combined stdout+stderr, which the dev +// server puts straight into the browser's error overlay — so a compile error lands in +// front of you rather than in a terminal you were not looking at. +func All() ([]byte, error) { + for _, s := range Steps() { + if out, err := s.Run(); err != nil { + return out, err + } + } + return nil, nil +} + +// Cold runs every step once, narrating as it goes, and exits non-zero on the first +// failure. This is `go run ./server -build`: CI, a cold start, or an editor's pre-launch +// task — anywhere there is nobody watching a browser overlay. +func Cold() { + log.SetFlags(0) + for _, s := range Steps() { + log.Println("==>", s.Name) + if out, err := s.Run(); err != nil { + os.Stderr.Write(out) + log.Fatalln("build failed:", err) + } + } + log.Println("==> Done. Serve it with: go run ./server") +} + +// Codegen regenerates app/*.gen.go from the //gowasm: directives — the routes, the +// layouts, and the client stubs for server components. It runs FIRST: everything after +// it compiles the code it writes. +func Codegen() ([]byte, error) { + return exec.Command("go", "run", "kjol/cmd/wasmgen", "./app").CombinedOutput() +} + +// Tailwind compiles css/app.css to wwwroot/app.css, scanning the webui kit, the lexer's +// palette and this app's Go markup for utility candidates. +// +// It scans .go files, which is the whole point of kjol's native engine: the markup is +// written in Go, so that is where the class names are. Nothing in this stage involves +// JavaScript. +// +// EVERY package whose class names have to exist has to be listed here. That is not a +// warning about carelessness — it is the failure mode: a package left off this list still +// compiles, still renders, and just comes out unstyled, because the class it asked for was +// never generated. kjol/lexer is here for exactly that reason; its whole output is class +// names, and nothing else in the tree mentions text-teal-300. +func Tailwind() ([]byte, error) { + cmd := exec.Command("go", "run", "./cmd/twcss", + "-entry", "cmd/kjol-web/css/app.css", + "-out", "cmd/kjol-web/wwwroot/app.css", + "-base", ".", + "webui/**/*.go", + "lexer/**/*.go", + "cmd/kjol-web/app/**/*.go", + "cmd/kjol-web/server/**/*.go", + ) + cmd.Dir = kjolRoot + return cmd.CombinedOutput() +} + +// Wasm compiles ./wasm to wwwroot/app.wasm. +func Wasm() ([]byte, error) { + cmd := exec.Command("go", "build", "-o", filepath.Join(Wwwroot, "app.wasm"), "./wasm") + cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm") + return cmd.CombinedOutput() +} + +// JS builds the Solid app: the SPA under /js, the server-rendered public pages, and +// their stylesheet. It is kjol/jsbundler — TSX compiled to Solid by a Go program, bundled +// by esbuild's Go API, styled by kjol/tw — run in-process rather than shelled out to, so +// a compile error comes back as a Go error and lands in the browser's error overlay like +// every other failure. +// +// It writes bundle.min.{js,css} and public.bundle.min.{js,css} into the SAME wwwroot as +// the wasm build. They never collide: different filenames, one static dir, one server. +// +// WebDir points at the shared tree — the kit, the vendored Solid runtime, the icon SVGs +// and the @theme scaffold all live there. +func JS() ([]byte, error) { + err := jsbundler.Build(jsbundler.Config{ + AppFrontend: "frontend", + WebDir: filepath.Join(kjolRoot, "jsruntime"), + Output: Wwwroot, + GenTSDir: filepath.Join("frontend", "src", "ui", "generated"), + }) + if err != nil { + return []byte(err.Error()), err + } + return nil, nil +} + +// Shim copies Go's wasm_exec.js into wwwroot. It is the loader the browser needs to start +// a Go wasm binary, it ships with the toolchain, and it must match the compiler that +// produced the binary — so it is copied from GOROOT rather than vendored. +func Shim() ([]byte, error) { + out, err := exec.Command("go", "env", "GOROOT").Output() + if err != nil { + return out, err + } + goroot := strings.TrimSpace(string(out)) + + for _, src := range []string{ + filepath.Join(goroot, "lib", "wasm", "wasm_exec.js"), // Go >= 1.24 + filepath.Join(goroot, "misc", "wasm", "wasm_exec.js"), // Go <= 1.23 + } { + b, err := os.ReadFile(src) + if err != nil { + continue + } + dst := filepath.Join(Wwwroot, "wasm_exec.js") + // The GOROOT copy is read-only, and so is the copy we made last time. Remove it + // first, or the write fails with a permission error that says nothing useful. + os.Remove(dst) + return nil, os.WriteFile(dst, b, 0o644) + } + return nil, os.ErrNotExist +} diff --git a/go/cmd/kjol-web/build/main.go b/go/cmd/kjol-web/build/main.go deleted file mode 100644 index c89e6dc5..00000000 --- a/go/cmd/kjol-web/build/main.go +++ /dev/null @@ -1,42 +0,0 @@ -// Command build runs the example's full pre-compile step once: directive codegen, -// Tailwind, the wasm binary, and Go's JS shim. -// -// go run ./build # from go/cmd/kjol-web -// -// For day-to-day work run the dev server instead (`go run ./server`) — it performs -// these same steps on every save and hot-swaps the result into the browser. This -// command is for a cold build, CI, or an editor's pre-launch task. -package main - -import ( - "log" - "os" - - "kjolweb/buildsteps" -) - -func main() { - log.SetFlags(0) - - steps := []struct { - name string - run func() ([]byte, error) - }{ - {"generating directive glue (//gowasm:page, //gowasm:layout, //gowasm:server)", buildsteps.Codegen}, - {"compiling Tailwind CSS -> wwwroot/app.css", buildsteps.Tailwind}, - {"compiling ./wasm -> wwwroot/app.wasm (GOOS=js GOARCH=wasm)", buildsteps.Wasm}, - {"bundling the Solid half -> wwwroot/bundle.min.{js,css} (TSX -> Solid -> esbuild)", buildsteps.JS}, - {"copying Go's wasm_exec.js shim into wwwroot/", buildsteps.Shim}, - } - - for _, s := range steps { - log.Println("==>", s.name) - if out, err := s.run(); err != nil { - os.Stderr.Write(out) - log.Fatalln("build failed:", err) - } - } - - log.Println("==> Done. Run the server with: go run ./server") - log.Println(" then open http://localhost:8085") -} diff --git a/go/cmd/kjol-web/buildsteps/buildsteps.go b/go/cmd/kjol-web/buildsteps/buildsteps.go deleted file mode 100644 index 5c263d7b..00000000 --- a/go/cmd/kjol-web/buildsteps/buildsteps.go +++ /dev/null @@ -1,133 +0,0 @@ -// Package buildsteps is the example's build pipeline: directive codegen, Tailwind, -// the wasm binary, and Go's JS shim. -// -// It is Go, not a shell script, for three reasons. The dev server needs to call these -// steps on every save and cannot shell out to bash on Windows. Editors need to run -// them as tasks, and a task that only works on one platform is a task half the team -// cannot use. And the one-off build and the watch build must be the SAME steps — the -// moment they are two scripts, they drift, and the bug only shows up in whichever one -// you use less. -// -// Run the whole thing with `go run ./build`. -package buildsteps - -import ( - "os" - "os/exec" - "path/filepath" - "strings" - - "kjol/jsbundler" -) - -// kjolRoot is the kjol Go module root, relative to the example directory. The Tailwind -// and codegen commands are run FROM there so the engine's dependencies resolve in -// kjol's own go.mod, and this example's stays lean. -const kjolRoot = "../.." - -// Wwwroot is where every build artefact lands, and what the dev server serves. -const Wwwroot = "wwwroot" - -// Codegen regenerates app/*.gen.go from the //gowasm: directives — the routes, the -// layouts, and the client stubs for server components. It runs FIRST: everything after -// it compiles the code it writes. -func Codegen() ([]byte, error) { - return run("go", "run", "kjol/cmd/wasmgen", "./app") -} - -// Tailwind compiles css/app.css to wwwroot/app.css, scanning the webui kit and this -// example's Go markup for utility candidates. -// -// It scans .go files, which is the whole point of kjol's native engine: the markup is -// written in Go, so that is where the class names are. There is no JS build here at -// all. -func Tailwind() ([]byte, error) { - cmd := exec.Command("go", "run", "./cmd/twcss", - "-entry", "cmd/kjol-web/css/app.css", - "-out", "cmd/kjol-web/wwwroot/app.css", - "-base", ".", - "webui/**/*.go", - "cmd/kjol-web/app/**/*.go", - "cmd/kjol-web/server/**/*.go", - ) - cmd.Dir = kjolRoot - return cmd.CombinedOutput() -} - -// Wasm compiles ./wasm to wwwroot/app.wasm. -func Wasm() ([]byte, error) { - cmd := exec.Command("go", "build", "-o", filepath.Join(Wwwroot, "app.wasm"), "./wasm") - cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm") - return cmd.CombinedOutput() -} - -// JS builds the OTHER half of the site: the Solid SPA under /js, the SSR'd public -// pages, and their stylesheet. It is kjol/jsbundler — TSX compiled to Solid by a Go -// program, bundled by esbuild's Go API, styled by kjol/tw — run in-process rather than -// shelled out to, so a compile error comes back as a Go error and lands in the dev -// server's browser overlay like every other failure. -// -// It writes bundle.min.{js,css} and public.bundle.min.{js,css} into the SAME wwwroot as -// the wasm build. The two halves never collide: different filenames, one static dir, one -// server. -// -// -web points at the shared tree, which is where the kit, the vendored Solid runtime, -// the icon SVGs and the @theme scaffold all live. -func JS() ([]byte, error) { - err := jsbundler.Build(jsbundler.Config{ - AppFrontend: "frontend", - WebDir: filepath.Join(kjolRoot, "jsruntime"), - Output: Wwwroot, - GenTSDir: filepath.Join("frontend", "src", "ui", "generated"), - }) - if err != nil { - return []byte(err.Error()), err - } - return nil, nil -} - -// Shim copies Go's wasm_exec.js into wwwroot. It is the loader the browser needs to -// start a Go wasm binary, it ships with the toolchain, and it must match the compiler -// that produced the binary — so it is copied from GOROOT rather than vendored. -func Shim() ([]byte, error) { - out, err := exec.Command("go", "env", "GOROOT").Output() - if err != nil { - return out, err - } - goroot := strings.TrimSpace(string(out)) - - for _, src := range []string{ - filepath.Join(goroot, "lib", "wasm", "wasm_exec.js"), // Go >= 1.24 - filepath.Join(goroot, "misc", "wasm", "wasm_exec.js"), // Go <= 1.23 - } { - b, err := os.ReadFile(src) - if err != nil { - continue - } - dst := filepath.Join(Wwwroot, "wasm_exec.js") - // The GOROOT copy is read-only, and so is the copy we made last time. Remove it - // first, or the write fails with a permission error that says nothing useful. - os.Remove(dst) - return nil, os.WriteFile(dst, b, 0o644) - } - return nil, os.ErrNotExist -} - -// All is the full build, in order. It is what the dev server runs on a code change and -// what `go run ./build` runs once. -// -// Returned output is the failing command's combined stdout+stderr, which the dev server -// puts straight into the browser's error overlay — so a compile error lands in front of -// you rather than in a terminal you were not looking at. -func All() ([]byte, error) { - for _, step := range []func() ([]byte, error){Codegen, Tailwind, Wasm, JS, Shim} { - if out, err := step(); err != nil { - return out, err - } - } - return nil, nil -} - -func run(name string, args ...string) ([]byte, error) { - return exec.Command(name, args...).CombinedOutput() -} diff --git a/go/cmd/kjol-web/css/app.css b/go/cmd/kjol-web/css/app.css index 03e343ef..a39d1dbd 100644 --- a/go/cmd/kjol-web/css/app.css +++ b/go/cmd/kjol-web/css/app.css @@ -77,19 +77,20 @@ @theme { --radius-default: 0.375rem; - /* Sky: a cool, neutral blue. The page is mostly prose, code and tables, and the - accent's job is to mark the few things you can act on — a saturated indigo or a - primary blue competes with the content for attention instead of directing it. */ - --color-primary: #0284c7; /* sky-600 — accent FILLS; they carry white text */ - --color-primary-hover: #0369a1; /* sky-700 */ - --color-primary-subtle: #f0f9ff; /* sky-50 — tinted panels, badges, callouts */ - --color-primary-border: #bae6fd; /* sky-200 */ + /* Navy and red — the flag, muted. kjøl is a Norwegian word and the palette says so. + Both are dark and low-key: the page is mostly prose, code and tables, and the brand's + job is to mark the few things you can act on, not to compete with them for attention. + (The previous sky blue did the same job, but said nothing.) */ + --color-primary: #1e3a63; /* muted navy — FILLS; they carry white text */ + --color-primary-hover: #16294a; + --color-primary-subtle: #eef2f8; /* a navy wash — tinted panels, badges, callouts */ + --color-primary-border: #c5d1e2; - /* accent is for TEXT and icons. It is a separate token from primary because the two - have opposite constraints: a fill must be dark enough for white text on top of it, - and accent text must be readable ON the surface. One value cannot be both, and in - dark mode they diverge completely. */ - --color-accent: #0369a1; /* sky-700 */ + /* accent is for TEXT and icons, and it is the RED — which is why it is a separate token + from primary rather than a lighter shade of it. The two have opposite constraints: a + fill must be dark enough for white text on top of it, and accent text must be readable + ON the surface. Here they are not even the same hue: navy fills, red links. */ + --color-accent: #9e1b32; /* dark red */ /* Surfaces, lines, ink — the kit's theme contract. */ --color-surface: #ffffff; @@ -129,7 +130,7 @@ as graph paper you have to read the page through. --------------------------------------------------------------------------- */ :root { - --grid-line: rgba(15, 23, 42, 0.055); + --grid-line: rgba(30, 58, 99, 0.06); /* the navy, at the edge of visible */ } /* --------------------------------------------------------------------------- @@ -156,12 +157,21 @@ --color-ink-muted: #9a9aa3; --color-ink-faint: #71717a; - /* The fill stays put — white text has to remain readable on it — but accent TEXT has - to climb to stay readable on a near-black surface. This is exactly why they are two - tokens. */ - --color-accent: #7dd3fc; /* sky-300 */ - --color-primary-subtle: #0b2c3f; - --color-primary-border: #0e4966; + /* Both brand tokens move in the dark, and for different reasons. + + The accent has to CLIMB: a dark red on a near-black surface is unreadable, so it goes + up to a light rose. It is still the flag's red, just the only version of it you can + read here. + + The fill has to climb too — which the sky blue it replaced did not. Navy is dark + enough that on a #101013 page a navy button loses its edges and reads as a hole in the + surface. So it lifts to a steel blue that still carries white text (~7:1) and still + looks like a button. */ + --color-accent: #f0a3ad; + --color-primary: #2b4f80; + --color-primary-hover: #37619b; + --color-primary-subtle: #182234; + --color-primary-border: #2c3e5c; --color-text-heading: #f5f5f5; diff --git a/go/cmd/kjol-web/frontend/css/style.css b/go/cmd/kjol-web/frontend/css/style.css index 7fd3f931..e8822f4b 100644 --- a/go/cmd/kjol-web/frontend/css/style.css +++ b/go/cmd/kjol-web/frontend/css/style.css @@ -10,20 +10,25 @@ The values below match the Go/WASM section's css/app.css on purpose — same Lora, same sky accent — so that crossing between /wasm and /js reads as two - halves of ONE site rather than two demos that happen to share a domain. The + parts of ONE site rather than two demos that happen to share a domain. The two sections are built by completely different pipelines; they should not look like it. --------------------------------------------------------------------------- */ @theme { - /* The accent. `primary` is the ONE semantic token the Solid kit actually - honours (bg-primary / text-primary / border-primary / bg-primary-hover); - everything else in the kit names a raw Tailwind neutral directly. That is a - real difference from the Go/WASM kit — which is themed end to end by tokens - and can therefore switch to dark by changing ten values — and the /js/theming - page says so out loud rather than pretending otherwise. */ - --color-primary: #0284c7; /* sky-600 — fills; they carry white text */ - --color-primary-hover: #0369a1; /* sky-700 */ + /* The brand: navy and red, the Norwegian flag muted down. These are the SAME values + the Go/WASM section's css/app.css sets, and under the same names — so the Layers + menu, the sidebar highlight and the callouts are the same navy on both sides of the + site rather than two navies that happen to be close. + + primary FILLS (white text sits on it) and is the NAVY; accent is TEXT and icons and + is the RED (it has to be readable on the surface); primary-subtle/-border are the + tinted panel. */ + --color-primary: #1e3a63; /* muted navy — fills; they carry white text */ + --color-primary-hover: #16294a; + --color-primary-subtle: #eef2f8; /* a navy wash — tinted panels, callouts */ + --color-primary-border: #c5d1e2; + --color-accent: #9e1b32; /* dark red — accent TEXT */ /* Lora, the same body face the Go/WASM section vendors. The woff2 files are served out of wwwroot/fonts by the same server, so this section pays no @@ -77,3 +82,21 @@ html { font-family: var(--font-sans); } + +/* The dark values for the brand. + --------------------------------------------------------------------------- + Both tokens move, for different reasons. The accent CLIMBS: a dark red on a near-black + surface is unreadable, so it goes up to a light rose — still the flag's red, just the + only version of it you can read here. The FILL climbs too, which the sky blue it + replaced did not have to: navy is dark enough that a navy button on a #101013 page loses + its edges and reads as a hole. And the tinted panel inverts outright, because a pale + wash on #101013 is not a tint, it is a white box. + + Same values, same names, as the Go/WASM section's css/app.css. */ +.dark { + --color-accent: #f0a3ad; + --color-primary: #2b4f80; + --color-primary-hover: #37619b; + --color-primary-subtle: #182234; + --color-primary-border: #2c3e5c; +} diff --git a/go/cmd/kjol-web/frontend/src/app.ts b/go/cmd/kjol-web/frontend/src/app.ts index bcda6cdc..f4ce0081 100644 --- a/go/cmd/kjol-web/frontend/src/app.ts +++ b/go/cmd/kjol-web/frontend/src/app.ts @@ -1,4 +1,4 @@ -// SPA entry for the Kjol JS Web section (/js/*). +// SPA entry for the Kjøl JS Web section (/js/*). // // This file is .ts and NOT .tsx on purpose — it is not a style choice. The bundler // resolves the SPA entry as src/app.ts (falling back to src/app.js) and nothing @@ -6,13 +6,13 @@ // the pages it points at. // // The section is mounted under a base path rather than at the root: the front page -// and the whole /wasm section are served by the Go/WASM half of this site, which +// and the whole /wasm section are served by Kjøl Wasm Web, a different binary, which // this bundle knows nothing about. `base: "/js"` keeps every route in here relative -// to that, so a link to "/kit" resolves to /js/kit and the two SPAs never fight -// over a URL. +// to that, so a link to "/components" resolves to /js/components and the two SPAs +// never fight over a URL. // // Crossing OUT of /js (to the front page, or into /wasm) is a plain and a -// real page load — the other half of the site is a different binary. That is the +// real page load — the rest of the site is a different binary. That is the // honest cost of running two front-ends behind one server, and it is one navigation. import { render, createComponent } from "solid-js/web"; @@ -21,19 +21,26 @@ import type { RouteDefinition } from "@solidjs/router"; import { Shell } from "./layout/Shell.tsx"; import { Overview } from "./pages/Overview.tsx"; -import { Kit } from "./pages/Kit.tsx"; -import { Forms } from "./pages/Forms.tsx"; -import { Table } from "./pages/Table.tsx"; -import { Theming } from "./pages/Theming.tsx"; +import { Components } from "./pages/Components.tsx"; +import { NotFound } from "./pages/NotFound.tsx"; // Routes as plain data: solid-router accepts RouteDefinition[] as `children`, which // is what lets a JSX-free entry declare a full route tree. +// +// There are only two. The kit used to be spread across /kit, /forms, /table and +// /theming — a split along the lines of the SOURCE FILES rather than along anything a +// reader wants: a person looking for a date picker does not know, and should not have +// to guess, whether it was filed under forms or under overlays. It is one page now, +// and the sidebar jumps you down it. const routes: RouteDefinition[] = [ { path: "/", component: Overview }, - { path: "/kit", component: Kit }, - { path: "/forms", component: Forms }, - { path: "/table", component: Table }, - { path: "/theming", component: Theming }, + { path: "/components", component: Components }, + + // The catch-all, and it is not optional. The SERVER answers every /js/* URL with this + // shell — it has no idea which paths the router knows about — so without a fallback an + // unknown one renders the chrome around an empty
: a blank page, with a 200, and + // nothing to tell you why. Kjøl Wasm Web has the same catch-all for the same reason. + { path: "*", component: NotFound }, ]; const root = document.getElementById("app"); diff --git a/go/cmd/kjol-web/frontend/src/componentGroups.ts b/go/cmd/kjol-web/frontend/src/componentGroups.ts new file mode 100644 index 00000000..6a3db4c2 --- /dev/null +++ b/go/cmd/kjol-web/frontend/src/componentGroups.ts @@ -0,0 +1,36 @@ +// The component groups: one entry per section of /js/components, AND one line in the +// sidebar that jumps to it. +// +// Declaring them once, as data, is what keeps those two in step — the sidebar cannot +// offer a jump to a section that does not exist, and a section cannot go missing from +// the sidebar. +// +// This is the JS mirror of app/components.go's componentGroups(). The ids match, so the +// two sections of the site have the same shape and a reader crossing between them lands +// in the same place. The ICONS differ, and have to: this side names FontAwesome, the Go +// side names webui's own hand-drawn registry, and where the two have no glyph in common +// the names diverge. + +export interface ComponentGroup { + id: string; + label: string; + icon: string; +} + +export const COMPONENT_GROUPS: ComponentGroup[] = [ + { id: "buttons", label: "Buttons", icon: "check" }, + { id: "badges", label: "Badges & alerts", icon: "circle-info" }, + { id: "cards", label: "Cards & layout", icon: "table-columns" }, + { id: "icons", label: "Icons", icon: "star" }, + { id: "forms", label: "Forms & inputs", icon: "pen-to-square" }, + { id: "selects", label: "Selects & comboboxes", icon: "sliders" }, + { id: "toggles", label: "Toggles & signature", icon: "check" }, + { id: "dates", label: "Dates", icon: "calendar" }, + { id: "tables", label: "Tables", icon: "table" }, + { id: "overlays", label: "Overlays", icon: "copy" }, + { id: "feedback", label: "Toasts & tours", icon: "bell" }, + { id: "navigation", label: "Tabs & navigation", icon: "bars" }, + { id: "search", label: "Fuzzy search", icon: "magnifying-glass" }, + { id: "charts", label: "Charts", icon: "chart-column" }, + { id: "theming", label: "Theming", icon: "palette" }, +]; diff --git a/go/cmd/kjol-web/frontend/src/layers.ts b/go/cmd/kjol-web/frontend/src/layers.ts index 045ba604..6cfc6903 100644 --- a/go/cmd/kjol-web/frontend/src/layers.ts +++ b/go/cmd/kjol-web/frontend/src/layers.ts @@ -1,14 +1,22 @@ -// The layers of kjol, as data. +// What kjøl is made of, as data. // -// This is the JS mirror of app/layers.go on the Go/WASM side. The site has two -// front-ends built by two completely different pipelines, and the Layers menu has -// to be identical in both — so it is a LIST in each, not markup, and the two lists -// are the only thing that has to be kept in step. +// There are two kinds of thing here, and conflating them was the mistake this file used +// to make — one flat list called "the layers", holding both. // -// (A shared source would be better than a mirrored one. There isn't one: the Go -// side compiles to WebAssembly and the JS side is bundled by esbuild, and nothing -// is upstream of both. Keeping it to a flat array of plain data is what makes the -// duplication survivable — you can diff the two by eye.) +// LAYERS are LANGUAGES. What kjøl is written in, and what it gives you in each: +// the Go base, the TypeScript kit, the C base, the Jai modules. A layer is +// a directory of code you can use on its own. +// +// COMPOSITIONS are FRAMEWORKS. What you get when the layers are assembled into +// something that does a job — the two web engines. A composition is not +// another language; it is a use of them. +// +// Kjøl Wasm Web is Go, all the way down. Kjøl JS Web is TypeScript compiled by a Go +// toolchain — two layers, one framework. Listing that beside "C" as though they were the +// same kind of noun told the reader nothing about either. +// +// This is the JS mirror of app/layers.go. The ids, the order and the taglines match; only +// the ICONS diverge, and they have to — see below. export interface Layer { name: string; @@ -20,7 +28,7 @@ export interface Layer { * The ONE field that does not match app/layers.go, and cannot: the two kits have * different icon sets. This side names FontAwesome; the Go side names webui's own * hand-drawn registry, which has no FontAwesome in it at all. Where the two have no - * glyph in common the names diverge (here `table-columns`, there `squares`). + * glyph in common the names diverge. * * Both sides fail loudly rather than quietly — a name neither registry knows renders * an empty box, and app/icons_test.go fails the build over it. @@ -28,45 +36,59 @@ export interface Layer { icon: string; } -export const LAYERS: Layer[] = [ +/** Languages: what kjøl is written in. */ +export const LANGUAGES: Layer[] = [ { - name: "Kjol Go", + name: "Go", href: "/go", - tagline: "The server base: config, database, logging, HTTP, mail, validation.", + tagline: "The base: config, database, logging, HTTP, mail, validation — and both web engines.", live: false, icon: "server", }, { - name: "Kjol Wasm Web", - href: "/wasm", - tagline: "Web interfaces written in Go, compiled to WebAssembly. SSR + hydration, no JS build.", - live: true, + name: "TypeScript", + href: "/ts", + tagline: "The Solid component kit, the vendored runtime, and the generic scaffolding apps import as @kjol/*.", + live: false, icon: "code", }, { - name: "Kjol JS Web", - href: "/js", - tagline: "The Solid component kit, bundled by a Go toolchain: TSX → Solid → esbuild, Tailwind in Go.", - live: true, - icon: "table-columns", - }, - { - name: "Kjol C", + name: "C", href: "/c", - tagline: "Arena allocator, strings, math, lexer, platform layer.", - live: false, + tagline: "Arena allocator, counted strings, math, a lexer, a platform layer — and a build system that is a C file.", + live: true, icon: "bolt", }, { - name: "Kjol Jai", + name: "Jai", href: "/jai", - tagline: "Console rendering module. Early.", + tagline: "Console rendering. Early.", live: false, icon: "cube", }, ]; -/** The layer the current path belongs to, or undefined on the front page. */ +/** Compositions: what the languages are assembled into. */ +export const COMPOSITIONS: Layer[] = [ + { + name: "Kjøl Wasm Web", + href: "/wasm", + tagline: "Web interfaces written in Go, compiled to WebAssembly. SSR + hydration, and no JavaScript build at all.", + live: true, + icon: "code", + }, + { + name: "Kjøl JS Web", + href: "/js", + tagline: "The Solid component kit, bundled by a Go toolchain: TSX → Solid → esbuild, Tailwind in Go.", + live: true, + icon: "table-columns", + }, +]; + +/** The layer or composition the current path belongs to, or undefined on the front page. */ export function currentLayer(path: string): Layer | undefined { - return LAYERS.find((l) => path === l.href || path.startsWith(l.href + "/")); + return [...COMPOSITIONS, ...LANGUAGES].find( + (l) => path === l.href || path.startsWith(l.href + "/"), + ); } diff --git a/go/cmd/kjol-web/frontend/src/layout/Shell.tsx b/go/cmd/kjol-web/frontend/src/layout/Shell.tsx index 9d1be6ac..0db262b8 100644 --- a/go/cmd/kjol-web/frontend/src/layout/Shell.tsx +++ b/go/cmd/kjol-web/frontend/src/layout/Shell.tsx @@ -1,4 +1,4 @@ -// The Kjol JS Web shell: top bar (wordmark + Layers menu), sidebar, content. +// The Kjøl JS Web shell: top bar (wordmark + Layers menu), sidebar, content. // // It is deliberately a near-copy of the Go/WASM section's AppLayout. Two front-ends, // one site: if the chrome drifted, crossing from /wasm to /js would feel like leaving @@ -7,11 +7,12 @@ // and the page should not betray that. import { For, Show } from "solid-js"; -import { A, useLocation } from "@solidjs/router"; +import { A, useLocation, useNavigate } from "@solidjs/router"; import { Icon } from "@ui/Icons"; -import { Menu, MenuTrigger, MenuContent, MenuLink, MenuSection } from "@ui/Menu"; +import { Menu, MenuTrigger, MenuContent } from "@ui/Menu"; import { ThemeToggle, initTheme } from "@ui/Theme"; -import { LAYERS } from "../layers.ts"; +import { LANGUAGES, COMPOSITIONS, currentLayer, Layer } from "../layers.ts"; +import { COMPONENT_GROUPS } from "../componentGroups.ts"; interface NavItem { path: string; @@ -22,68 +23,109 @@ interface NavItem { // The section's own pages. Paths are relative to the router base (/js). const NAV: NavItem[] = [ { path: "/", label: "Overview", icon: "circle-info" }, - { path: "/kit", label: "Components", icon: "table-columns" }, - { path: "/forms", label: "Forms", icon: "pen-to-square" }, - { path: "/table", label: "AutoTable", icon: "table" }, - { path: "/theming", label: "Theming", icon: "palette" }, + { path: "/components", label: "Components", icon: "table-columns" }, ]; -// The Layers menu — the site's primary navigation. kjol is a stack of layers, and -// this is how you get from any one of them to any other. It is rendered from the -// LAYERS array so adding a layer is one object, not a nav edit in two front-ends. +// jumpTo scrolls a section into view, routing there first if we are somewhere else. // -// Layers that are not `live` still appear. A menu that silently omits half the -// library teaches the reader that the library is half the size it is; showing them -// greyed, with the reason, is the more honest shape. -function LayersMenu() { +// A plain would work if the reader were already on the components +// page, and would do nothing useful from anywhere else. The router's is no good +// either — it would try to navigate to a route called "#forms". +// +// The queueMicrotask is not superstition: after navigate() the target section does not +// exist yet, because the page it lives on has not rendered. Scrolling on the next tick +// is the earliest moment the element is actually there to scroll to. +function jumpTo(navigate: (to: string) => void, onComponentsPage: boolean, id: string) { + const scroll = () => document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" }); + + if (onComponentsPage) { + scroll(); + return; + } + navigate("/components"); + queueMicrotask(scroll); +} + +// The site's primary navigation, as TWO dropdowns: Layers (the languages) and +// Compositions (the frameworks assembled out of them — see layers.ts). +// +// Two menus, not one with two headings inside it. They answer different questions — +// "what is this written in" and "what can I read" — and a reader who wants the second +// should not have to scroll past the first to find it. The kit's single-open manager +// means opening one closes the other, so they behave like one control with two halves. +// +// Anything not `live` still appears, greyed, with the reason. A menu that silently omits +// half the library teaches the reader that the library is half the size it is. +// +// Same shape as the Go side (app/layers.go: layersMenu / compositionsMenu). +function Dropdown(props: { label: string; rows: Layer[] }) { + const location = useLocation(); + const current = () => currentLayer(location.pathname); + return ( - + // bottom-end, because the triggers sit at the right-hand end of the bar and a 24rem + // panel hanging off the left edge of one would run past the window. + - Layers + {props.label} + - -

- The layers of kjol -

- - {(layer) => ( - - - - {layer.name} - - reference - - - {layer.tagline} -
- } - > - {/* MenuLink is a real (not a router link), which is what a - cross-layer jump has to be: the other layers are served by a - different binary. */} - - - {layer.name} - {layer.tagline} - - - - )} - - + {(layer) => } ); } +// LayerItem is one row of the menu. +// +// The markup is deliberately the same shape and the same classes as app/layers.go's +// layerItem. Two front-ends, one menu: if they drifted, this is where it would show, +// because it is the one component a reader sees on both sides within seconds of each +// other. +function LayerItem(props: { layer: Layer; current?: Layer }) { + return ( + + + + {props.layer.name} + + reference + + + {props.layer.tagline} +
+ } + > + {/* A plain , not MenuLink and not the router's . Both of the + alternatives are wrong here: MenuLink lays its icon out BESIDE the whole + two-line block (so the tagline never lines up under the name, which is what + the Go menu does), and the router would try to handle the jump itself — but + the other side is served by a different binary, so it has to be a real + navigation. */} + + + + {props.layer.name} + + {props.layer.tagline} + + + ); +} + function Wordmark() { // A plain , not a router : "/" is the front page, which belongs to the // Go/WASM binary. Routing to it inside this SPA would resolve to /js and land you @@ -94,7 +136,7 @@ function Wordmark() { - Kjol JS Web + Kjøl JS Web Solid + Go toolchain @@ -103,29 +145,32 @@ function Wordmark() { function Sidebar() { const location = useLocation(); - // The router's pathname is absolute (/js/kit); NAV paths are base-relative (/kit). + const navigate = useNavigate(); + + // The router's pathname is absolute (/js/components); NAV paths are base-relative. const active = (path: string) => location.pathname === "/js" + (path === "/" ? "" : path); + const onComponents = () => location.pathname === "/js/components"; + + // The same active treatment the Go sidebar uses (app/pages.go: sidebarLink) — a + // tinted panel and accent text, not a grey fill. Now that the Solid theme carries the + // primary-subtle / accent tokens, the two sidebars are the same sidebar. + const linkCls = (on: boolean) => + on + ? "flex items-center gap-2 rounded-default bg-primary-subtle px-2 py-1.5 text-sm font-medium text-accent no-underline" + : "flex items-center gap-2 rounded-default px-2 py-1.5 text-sm text-ink-soft no-underline hover:bg-surface-raised hover:text-ink"; return ( ); } @@ -155,7 +230,8 @@ export function Shell(props: { children?: any }) { Docs
- + + +

{props.title}

+ {props.children} + + ); +} + +function Prose(props: { children?: JSXElement }) { + return

{props.children}

; +} + +// Panel is a live demo with no source alongside it — for the components whose usage is +// obvious from looking at them. Demo (with code) is for the ones where it is not. +function Panel(props: { title: string; children?: JSXElement }) { + return ( +
+
+ {props.title} +
+
{props.children}
+
+ ); +} + +function Field(props: { label: string; children?: JSXElement }) { + return ( +
+ {props.label} + {props.children} +
+ ); +} + +const TOUR_STEPS: TutorialStep[] = [ + { title: "Buttons", target: "#buttons", content: "The presentational end: props in, an element out." }, + { title: "Overlays", target: "#overlays", content: "Measured against the real viewport, portaled out of the tree." }, + { title: "Tables", target: "#tables", content: "Filter, sort, calculate, export — from a column list." }, + { title: "That's the tour", content: "Escape ends it. Arrow keys and Enter move between steps." }, +]; + +export function Components() { + // ToastProvider and TutorialProvider are the only two components in this kit that are + // PROVIDERS: useToast() and useTutorial() throw outside them. Everything else — + // modals, popovers, menus, the date picker — self-portals to document.body and needs + // no host mounted anywhere. + return ( + + + + + + ); +} + +function Body() { + return ( +
+
+

Kjøl JS Web

+

Components

+

+ Every component in the Solid kit, running. Not a screenshot of one anywhere: each block + below is the real component, imported from @ui/* and rendered + on this page. Use the sidebar to jump to a group. +

+
+ +
+ + They are ordinary Solid components: named exports, props in, an element out. Two things + catch people out. Handlers keep their DOM names —{" "} + onclick, oninput,{" "} + onchange, never onClick. And + many props accept a signal getter as well as a value, so you can pass{" "} + value={"{"}name{"}"} rather than{" "} + value={"{"}name(){"}"} and let the component track it. + +
+ Take the tour +
+
+ + + + + + + + + + + + + + + + +
+ ); +} + +// ---- buttons ------------------------------------------------------------- + +function Buttons() { + const [clicks, setClicks] = createSignal(0); + const [span, setSpan] = createSignal("week"); + + return ( +
+ + Colour, outline, size and an optional icon. A button with an icon and no text gets square + padding rather than the wide pill a text button gets — so an icon button is a square, not a + lozenge with a picture rattling around in it. + + + +
+ Primary + Green + Red + Blue + Neutral + White + Light +
+
+ + +
+ Outline + Danger + + Small + icon + + + + + Disabled +
+
+ + setClicks(clicks() + 1)}> + Clicked {clicks()} times + + +// onclick, not onClick. These are DOM handler names.`} + > +
+ setClicks(clicks() + 1)}> + Clicked {clicks()} times + + setClicks(0)}>Reset (a link that is a button) + setClicks(0)}>And a destructive one + +
+
+ + + + +
+ ); +} + +// ---- badges & alerts ----------------------------------------------------- + +function Badges() { + return ( +
+ + A badge is a solid fill with white text, so it stays legible in both themes without a variant + on it. An alert is the opposite: a wash of colour behind dark text, which is exactly the case + a token cannot carry into dark mode — a red-50 tint is invisible on a near-black surface — so + alerts are one of only two places in the kit with a dark: rule. + + + +
+ active + failed + info + pending + default + muted +
+
+ + +
+ An informational message with a header. + A success alert, with no header. + Something needs your attention. + Something went wrong. + And a neutral one. + On the plain surface. +
+
+ + + {/* It is absolutely positioned (top-0 -right-2), so it needs a relative + ancestor to hang off — normally the logo. And it renders NOTHING in + production, which is the whole point of it: a badge that is always + there tells you nothing. */} +
+ kjol + +
+

+ Nothing beside the wordmark? Then this build is production — which is what it is telling + you. +

+
+
+ ); +} + +// ---- cards & layout ------------------------------------------------------ + +function CardsAndLayout() { + return ( +
+ + The structural furniture: cards, headings, dividers, crumb trails, a spinner, a code box. + Nothing here holds state — they are containers, and they take their children. + + + +
+ + Card +

Padded, and grows to fill its row.

+
+ + BorderCard +

A border instead of a shadow.

+
+ + Cut corner +

The same, with a clipped corner.

+
+
+
+ + + + + + New} + /> + + + +
+ + + An external link (new tab) + +
+ +
+
+ ); +} + +// ---- icons --------------------------------------------------------------- + +const ICON_NAMES = [ + "check", "xmark", "plus", "pen-to-square", "trash-can", "download", "print", "calendar", + "envelope", "globe", "user", "house", "table", "code", "star", "bell", "sun", "moon", + "magnifying-glass", "circle-info", "circle-check", "circle-exclamation", "triangle-exclamation", + "chevron-down", "bars", "copy", "sliders", "palette", "cog", "server", "bolt", "cube", + "chart-column", "table-columns", "list-ol", "grip-vertical", "phone", "arrow-right", +]; + +function Icons() { + return ( +
+ + FontAwesome, tree-shaken. The bundler scans the source for the names actually referenced, + pulls just those paths out of the SVG kit, and emits a registry containing only them — + which is why this site ships a few dozen glyphs rather than FontAwesome's 41.5 MB kit. + + + A name nothing in the source references is not bundled; a name the kit has never heard of + renders an empty box and is reported by the bundler rather than shipping silently. + + + +
+ + {(name) => ( +
+ + {name} +
+ )} +
+
+
+ + +
+ + + IconContainer + + + + + IconInline sits on the text baseline + + + +
+

+ The last two are the same name in the solid and regular styles. Which one you get by + default is a CSS variable the app sets, read at runtime. +

+
+
+ ); +} + +// ---- forms --------------------------------------------------------------- + +function Forms() { + const [name, setName] = createSignal(""); + const [email, setEmail] = createSignal(""); + const [notes, setNotes] = createSignal(""); + const [plan, setPlan] = createSignal("pro"); + + // Derived, not stored — so it cannot fall out of step with the field it describes. + // Blank is not "invalid", it is unfilled. + const emailError = () => (email() && !isEmailValid(email()) ? "That is not an email address." : ""); + + return ( +
+ + Inputs are controlled: the value goes in as a prop, the change comes out as a callback, and + the caller owns the state. The masked fields carry their behaviour inside the component — a + currency field will not let you type a letter into it — which is the only reason it is the + same in every app. + + + + email() && !isEmailValid(email()) ? "That is not an email address." : ""; + + setEmail(e.currentTarget.value)} + error={emailError()} + showIcon +/>`} + > +
+ + setName(e.currentTarget.value)} + /> + + + setEmail(e.currentTarget.value)} + error={emailError()} + showIcon + /> + + + setPlan(e.currentTarget.value)}> + + + + + + + setNotes(e.currentTarget.value)} + /> + +
+
+ + +
+ + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + setName(e.currentTarget.value)} /> + + + setEmail(e.currentTarget.value)} + error={emailError()} + showIcon + /> + +
+
+
+
+ ); +} + +// ---- selects & comboboxes ------------------------------------------------ + +const LANGUAGES = [ + { value: "go", label: "Go" }, + { value: "rust", label: "Rust" }, + { value: "ts", label: "TypeScript" }, + { value: "python", label: "Python" }, + { value: "kotlin", label: "Kotlin" }, + { value: "swift", label: "Swift" }, +]; + +const PEOPLE = [ + { value: "ada@example.com", label: "Ada Lovelace" }, + { value: "alan@example.com", label: "Alan Turing" }, + { value: "grace@example.com", label: "Grace Hopper" }, + { value: "katherine@example.com", label: "Katherine Johnson" }, + { value: "margaret@example.com", label: "Margaret Hamilton" }, + { value: "radia@example.com", label: "Radia Perlman" }, +]; + +function Selects() { + const [one, setOne] = createSignal(""); + const [many, setMany] = createSignal(["go"]); + const [tags, setTags] = createSignal(["go"]); + const [state, setState] = createSignal(""); + const [tz, setTz] = createSignal(""); + const [picked, setPicked] = createSignal(""); + + return ( +
+ + A combobox picks one; a multi-select picks several, with checkboxes on the rows and removable + pills in the field. Past three selections — or once the pills stop fitting — the field + collapses to "N items selected" rather than growing until it wraps. + + + +
+ + + + + + + + + + + setTz(e?.currentTarget?.value ?? e)} /> + +
+

+ one: {one() || "—"} · several:{" "} + {many().join(", ") || "—"} +

+
+ + +
+ {/* The search is the CALLER's. The component knows how to debounce, order + and render; it knows nothing about where options come from. Here it is a + local array; in an app it would be a fetch. */} + + PEOPLE.filter((p) => p.label.toLowerCase().includes(q.toLowerCase())) + } + onSelect={(value, option) => setPicked(option.label + " <" + value + ">")} + /> +
+

+ Two characters before it asks; 200 ms after you stop typing. A response for a query you + have already typed past is DISCARDED rather than shown — which is the whole bug with + hand-rolled autocompletes. +

+
+ + + + Tags ({tags().length}) + + } + options={LANGUAGES} + value={tags} + onchange={setTags} + searchable + showSelectAll + /> +

+ Same selection model as the field above; only the thing you click on differs. +

+
+
+ ); +} + +// ---- toggles & signature ------------------------------------------------- + +function Toggles() { + const [notify, setNotify] = createSignal(true); + const [locked, setLocked] = createSignal(false); + const [signed, setSigned] = createSignal(""); + + return ( +
+ + A toggle is a checkbox that admits what it is — there is no{" "} + FormCheckbox in this kit, and that is deliberate. The + signature pad hands you back the drawing as an SVG string. + + + +
+ + +
+
+ + + +

+ Draw in it. Clearing it emits an empty string, so "did they sign?" is just a length check. +

+
+
+ ); +} + +// ---- dates --------------------------------------------------------------- + +function Dates() { + const [date, setDate] = createSignal(""); + const [dob, setDob] = createSignal(""); + const [day, setDay] = createSignal(""); + + return ( +
+ + The field is typeable, not merely clickable, and it parses loosely. A picker you can only + click is a picker that is slower than the keyboard for everyone who already knows the date. + + + +
+ + + + + + +
+
+ + +
+ +
+

+ The same grid the picker drops down, usable directly when you want it inline. Pass{" "} + variant="month" for the big version. +

+
+
+ ); +} + +// ---- tables -------------------------------------------------------------- + +interface Institution { + name: string; + state: string; + term: string; + rate: number; + minimum: number; + status: "open" | "closed" | "waitlist"; +} + +const ROWS: Institution[] = [ + { name: "First Meridian Bank", state: "CA", term: "90 day", rate: 4.85, minimum: 1000, status: "open" }, + { name: "Harborline Credit Union", state: "WA", term: "180 day", rate: 5.1, minimum: 2500, status: "open" }, + { name: "Cascade Federal", state: "OR", term: "1 year", rate: 5.35, minimum: 500, status: "waitlist" }, + { name: "Ironwood Savings", state: "IL", term: "90 day", rate: 4.6, minimum: 10000, status: "closed" }, + { name: "Great Lakes Trust", state: "MI", term: "2 year", rate: 5.55, minimum: 1000, status: "open" }, + { name: "Sunbelt National", state: "TX", term: "180 day", rate: 4.95, minimum: 5000, status: "open" }, + { name: "Granite State Bank", state: "NH", term: "1 year", rate: 5.2, minimum: 2000, status: "waitlist" }, + { name: "Pacific Crest", state: "CA", term: "5 year", rate: 5.75, minimum: 25000, status: "open" }, + { name: "Copper Ridge Bank", state: "AZ", term: "90 day", rate: 4.4, minimum: 1000, status: "closed" }, + { name: "Bayou Community", state: "LA", term: "1 year", rate: 5.05, minimum: 1500, status: "open" }, + { name: "Northern Pine FCU", state: "MN", term: "2 year", rate: 5.45, minimum: 500, status: "open" }, + { name: "Chesapeake First", state: "MD", term: "180 day", rate: 4.75, minimum: 3000, status: "waitlist" }, +]; + +// The whole AutoTable is this list. Sorting, column ordering, hiding, resizing and CSV +// export are all driven from it — there is no per-column wiring anywhere else. +const COLUMNS: AutoTableColumn[] = [ + { displayName: "Institution", sortable: true, sortIdentifier: "name", displayPosition: COL_POS_LEFT }, + { displayName: "State", sortable: true, sortIdentifier: "state", displayPosition: COL_POS_CENTER, toggleable: true }, + { displayName: "Term", sortable: true, sortIdentifier: "term", displayPosition: COL_POS_LEFT }, + { + displayName: "Rate", sortable: true, sortIdentifier: "rate", sortType: "numeric", + displayPosition: COL_POS_RIGHT, csvValue: (i: Institution) => i.rate, + }, + { + displayName: "Minimum", sortable: true, sortIdentifier: "minimum", sortType: "money", + displayPosition: COL_POS_RIGHT, toggleable: true, csvValue: (i: Institution) => i.minimum, + }, + { displayName: "Status", sortable: true, sortIdentifier: "status", displayPosition: COL_POS_CENTER }, +]; + +const money = (n: number) => "$" + n.toLocaleString("en-US"); + +function StatusBadge(props: { status: Institution["status"] }) { + if (props.status === "open") return open; + if (props.status === "closed") return closed; + return waitlist; +} + +const GRID_SEED = [ + { id: "1", sku: "KJ-100", qty: 12, price: 4.5 }, + { id: "2", sku: "KJ-220", qty: 3, price: 18 }, + { id: "3", sku: "KJ-330", qty: 47, price: 1.25 }, +]; + +function Tables() { + const [gridRows, setGridRows] = createSignal(GRID_SEED.map((r) => ({ ...r }))); + const [sortKey, setSortKey] = createSignal("sku"); + const [sortDesc, setSortDesc] = createSignal(false); + + return ( +
+ + Three of them, and the difference is what the user is allowed to do. PrettyTable prints rows. + CellGrid lets them be edited in place. AutoTable filters, sorts, pages, reorders, resizes and + exports — configured with a column list and an array of rows. + + + + + + Ada Lovelace + Pro + active + + + Alan Turing + Free + trial + + + Grace Hopper + Enterprise + invited + + + + + + + setGridRows((rows) => rows.map((r) => (r.id === id ? { ...r, [field]: value } : r))) + } + sortKey={sortKey()} + setSortKey={setSortKey} + sortDesc={sortDesc()} + setSortDesc={setSortDesc} + /> +

+ Click a Qty or Price cell and type; Tab and the arrow keys move between editable cells. + The grid does not own the rows — it tells you which cell changed and hands the value back. +

+
+ + + AutoTable is the big one. Sort by clicking a header, drag a header to reorder it, use the + toolbar to hide a column or export what you are looking at. The column layout is keyed to + localStorage, so it survives a reload. + + +
+ ( + + ctx.setSearchValue("name", v)} + /> + ctx.setSearchValue("state", v)} + /> + + )} + rowRenderer={(item: Institution) => ( + <> + {item.name} + {item.state} + {item.term} + {item.rate.toFixed(2)}% + {money(item.minimum)} + + + )} + /> +
+ + + This table is passed data. Give it{" "} + url instead and the same column list drives a server-side + query — the sort identifier becomes the sort key, the search fields become query parameters, + and pagination is handled for you. Nothing else on the page changes. + +
+ ); +} + +// ---- overlays ------------------------------------------------------------ + +function Overlays() { + const toast = useToast(); + + const [modalOpen, setModalOpen] = createSignal(false); + const [nestedOpen, setNestedOpen] = createSignal(false); + const [confirmOpen, setConfirmOpen] = createSignal(false); + const [wizardOpen, setWizardOpen] = createSignal(false); + const [deleted, setDeleted] = createSignal(false); + const [wizardName, setWizardName] = createSignal(""); + + return ( +
+ + Tooltips, popovers, menus and modals — every one measured against the real viewport. A + floating panel portals itself to document.body, positions from + its trigger's bounding box, and flips or shifts when it would otherwise run off the screen. + + + None of them needs a host or a portal root mounted anywhere. That is the difference from a + modal library that makes you remember a <div id="portal">{" "} + at the bottom of your index.html. + + + +
+ + Top + + + Right + + + Instant + + + + +
+

+ A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read. +

+
+ + +
+ + + Click me + + +

+ Click outside, or press Escape. Only the TOPMOST floating panel closes per press. +

+
+
+ + + + Aligned to my right edge + + +

Placement bottom-end.

+
+
+ + + + Hover, then reach the panel + + +

+ Move the cursor across the gap and onto this panel — it stays open. Select this + text to prove it. Without that grace period the panel would close in the dead + space between the two, which is what happens once a panel is portaled and CSS + :hover no longer reaches it. +

+
+
+
+
+ + +
+ + + + Actions + + + + toast.success("Profile opened")}>Profile + toast.info("Settings opened")}>Settings + + toast.info("Archived")}>Archive + toast.warning("Duplicated")}>Duplicate + + + toast.generic("Menu stayed open")}> + Stay open (closeOnClick={"{false}"}) + + toast.error("Signed out")}>Sign out + + + + + + Opens on hover + + + One + Two + + +
+

+ The items raise toasts, which is how you can see that an item really does close its own + menu — and that the one marked closeOnClick={"{false}"} does not. +

+
+ + +
+ setModalOpen(true)}>Open modal + setConfirmOpen(true)}> + Delete something… + + setWizardOpen(true)}> + Open wizard + +
+

+ Open the modal, then the nested one inside it, and press Escape twice: modals unwind ONE + LAYER per press rather than all at once. +

+ + setModalOpen(false)} + header={

A modal

} + footer={ + setModalOpen(false)}>Close + } + > +

+ Portaled to document.body, so no ancestor's{" "} + overflow: hidden or transform can clip it — the two + things that silently clip a floating panel. +

+
+ setNestedOpen(true)}> + Open a nested modal + +
+
+ + setNestedOpen(false)} + size="small" + header={

Nested

} + > +

Escape closes THIS one first, not the one behind it.

+
+ + setConfirmOpen(false)} + onConfirm={() => { + setDeleted(true); + toast.error("Row deleted"); + }} + title="Delete row" + message="This cannot be undone. (Nothing is actually deleted — this is a docs page.)" + confirmText="Delete" + /> + + setWizardOpen(false)} + onComplete={() => toast.success("Wizard complete: " + (wizardName() || "nobody"))} + title="Set up your account" + finishText="Finish" + steps={[ + { + title: "Your name", + // Each step gets its own context: setCanContinue gates THIS step's + // Next button, which one shared boolean could not express. + content: (ctx) => { + ctx.setCanContinue(wizardName() !== ""); + return ( + + setWizardName(e.currentTarget.value)} + /> + + ); + }, + }, + { + title: "Confirm", + content: (ctx) => { + ctx.setCanContinue(true); + return ( +

+ All set for {wizardName() || "nobody"}. Finish to close. +

+ ); + }, + }, + ]} + /> +
+
+ ); +} + +// ---- feedback ------------------------------------------------------------ + +function Feedback() { + const toast = useToast(); + const flash = createRemoteFlash(2000); + + return ( +
+ + Toasts dismiss themselves after five seconds, with a bar counting down. A sticky one ( + duration: null) waits for the user instead. + + + +
+ toast.success("Saved.")}> + Success + + toast.error("Something went wrong.")}> + Error + + toast.info("Just so you know.")}> + Info + + + toast.addToast({ + message: "This one waits for you to dismiss it.", + type: "warning", + duration: null, + }) + } + > + Sticky (no timer) + +
+

+ useToast() throws outside a{" "} + <ToastProvider> — which is one of only two providers + this kit has. +

+
+ + +
+ Take the tour + + It dims the page, cuts a hole around each target, and animates the spotlight from one + to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to. + +
+
+ + +
+ + Something changed elsewhere + + +
+

+ A brief acknowledgement that data you are looking at was changed by somebody else. It is + not a toast: it belongs next to the thing that moved, not in the corner. +

+
+
+ ); +} + +// ---- navigation ---------------------------------------------------------- + +function Navigation() { + const [side, setSide] = createSignal("buttons"); + + const panel = (s: string) =>

{s}

; + + return ( + + ); +} + +// ---- fuzzy search -------------------------------------------------------- + +const KIT_NAMES = [ + "AutoTable", "Accordion", "Alert", "Badge", "ButtonUI", "Calendar", "CellGrid", "FormCombobox", + "DatePicker", "FormInput", "Menu", "Modal", "FormMultiSelect", "Popover", "PrettyTable", + "SegmentedButtons", "FormSignaturePad", "TabGroup", "ThemeToggle", "ToastProvider", "ToggleSwitch", + "Tooltip", "TutorialProvider", "SidebarNav", "ReactiveChart", "FuzzyMatch", +]; + +function Search() { + const [hit, setHit] = createSignal(""); + + return ( + + ); +} + +// ---- charts -------------------------------------------------------------- + +function Charts() { + const [seed, setSeed] = createSignal(0); + + const data = () => { + const base = [42, 17, 63, 28, 55, 9, 71]; + const values = base.map((v) => (seed() === 0 ? v : Math.max(4, (v * 7 + seed() * 13) % 80))); + return { + labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], + datasets: [ + { + label: "Requests", + data: values, + // A literal, not var(--color-primary): chart.js paints a canvas, and a + // canvas cannot read a CSS variable. So this is the one place in the + // site that names the navy instead of asking for it — and it names the + // DARK theme's lighter navy, which is the only one of the two that + // reads on both a white page and a near-black one. + backgroundColor: "#2b4f80", + borderColor: "#2b4f80", + }, + ], + }; + }; + + return ( +
+ + @ui/Chart wraps chart.js: it creates a chart against a canvas + on mount and pushes new data through an effect, so changing the data updates the chart rather + than rebuilding the page. + + + This is the one component in the kit that is genuinely different on the other side. The Go + port keeps the props shape for parity and renders an empty canvas — canvas drawing, a + JavaScript charting library and mount lifecycles do not exist in the neutral Go runtime. Kjol + Wasm Web draws SVG with go-chart instead, and draws it in the browser, in WebAssembly, exactly + as this one draws to a canvas here. Same place, different library. Neither side pretends + otherwise. + + + + {/* The wrapper is h-full with maintainAspectRatio:false, so the PARENT must + have an explicit height or the canvas collapses to nothing at all. */} +
+
+ +
+
+ +
+
+
+ setSeed(seed() + 1)}> + New data + + + The chart updates in place — it is not torn down and rebuilt. + +
+
+ + + Chart.tsx imports chart.js at the top level and registers + every controller on module load. An app that uses it must vendor chart.js and{" "} + @kurkle/color, or the bundle does not degrade — it fails to + evaluate, and you get an empty page and one line in the console. + +
+ ); +} + +// ---- theming ------------------------------------------------------------- + +const TOKENS: { swatch: string; name: string; role: string }[] = [ + { swatch: "bg-surface", name: "surface", role: "the page" }, + { swatch: "bg-surface-muted", name: "surface-muted", role: "a recessed strip" }, + { swatch: "bg-surface-raised", name: "surface-raised", role: "a panel, a hover" }, + { swatch: "bg-surface-strong", name: "surface-strong", role: "a track, a divider fill" }, + { swatch: "bg-line", name: "line", role: "an ordinary border" }, + { swatch: "bg-line-strong", name: "line-strong", role: "a border that has to be seen" }, + { swatch: "bg-ink", name: "ink", role: "body text, headings" }, + { swatch: "bg-ink-soft", name: "ink-soft", role: "secondary text" }, + { swatch: "bg-ink-muted", name: "ink-muted", role: "captions, labels" }, + { swatch: "bg-ink-faint", name: "ink-faint", role: "placeholders, disabled" }, +]; + +function Theming() { + const { isDark, mode } = useTheme(); + + return ( +
+ + No component in this kit names a colour. They say bg-surface,{" "} + text-ink, border-line — and what + those mean is decided in one place. Dark mode re-points a dozen CSS variables and not one + component knows it happened. + + + +
+ +
+ currently {isDark() ? "dark" : "light"}, because + you asked for {mode()} +
+
+

+ Press it. Every component on this page moves — none of them were told. Your choice is + remembered, and it is the same choice Kjøl Wasm Web reads: both layers of this site share + one kjol-theme key, so the theme survives crossing between + two front-ends that share no code at all. +

+
+ + +
+ + {(t, i) => ( +
0 ? "border-t border-line" : "")}> + + {t.name} + {t.role} +
+ )} +
+
+

+ The swatch class is written out in full in the source, not built as{" "} + "bg-" + name. Tailwind finds the classes it must compile by + scanning the source for literal strings — a concatenation is invisible to it, and every + swatch here would come out colourless. +

+
+ +
+ + Coloured tints +

+ A red-50 wash is invisible on a near-black surface. An + alert's tint has to become a deep, transparent one — a different colour, not a + different value of the same one. One of only two places a{" "} + dark: variant survives. +

+
+ + Fills that invert +

+ The neutral button is dark on a light page and light on a dark one, so its label must + invert with it — text-white would disappear the moment + the fill went pale. Hence three tokens, not one. The other place. +

+
+
+
+ ); +} diff --git a/go/cmd/kjol-web/frontend/src/pages/Forms.tsx b/go/cmd/kjol-web/frontend/src/pages/Forms.tsx deleted file mode 100644 index e6410c4f..00000000 --- a/go/cmd/kjol-web/frontend/src/pages/Forms.tsx +++ /dev/null @@ -1,220 +0,0 @@ -// /js/forms — the form fields, and the masks that make them worth having. - -import { createSignal } from "solid-js"; -import { - FormInput, - FormLabel, - FormSelect, - FormTextarea, - FormCurrencyInput, - FormPercentInput, - FormPhoneInput, - FormEmailInput, - FormNumberInput, - FormCombobox, - FormMultiSelect, - FormFieldset, - US_STATES, -} from "@ui/Forms"; -import { ToggleSwitch } from "@ui/ToggleSwitch"; -import { ButtonUI, BUTTON_COLOR_PRIMARY } from "@ui/Buttons"; -import { AlertBlue } from "@ui/Alerts"; -import { isEmailValid } from "@ui/Validation"; -import { Demo } from "../layout/Demo.tsx"; - -export function Forms() { - const [name, setName] = createSignal(""); - const [email, setEmail] = createSignal(""); - const [amount, setAmount] = createSignal(""); - const [rate, setRate] = createSignal(""); - const [phone, setPhone] = createSignal(""); - const [term, setTerm] = createSignal("90"); - const [state, setState] = createSignal(""); - const [tags, setTags] = createSignal(["cd"]); - const [notify, setNotify] = createSignal(true); - const [notes, setNotes] = createSignal(""); - - // The error is a derived value, not a second piece of state — so it cannot get - // out of step with the field it describes. Blank is not "invalid", it is unfilled. - const emailError = () => (email() && !isEmailValid(email()) ? "That is not an email address." : ""); - - return ( -
-

Kjol JS Web

-

Forms

-

- The fields carry their own input masks. A currency field will not let you type a letter into - it; a percent field keeps one trailing symbol; a phone field formats as you go. That behaviour - is in the component, not in the page — which is the only reason it is the same in every app. -

- - - These are Solid components, so DOM handlers keep their DOM names:{" "} - oninput, onchange,{" "} - onclick — not onInput. It is the - single most common thing to get wrong when writing against this kit. - - - - email() && !isEmailValid(email()) ? "That is not an email address." : ""; - - setEmail(e.currentTarget.value)} - error={emailError()} - showIcon -/>`} - > -
-
- Name - setName(e.currentTarget.value)} - /> -
-
- Email - setEmail(e.currentTarget.value)} - error={emailError()} - showIcon - /> -
-
-
- - - - -`} - > -
-
- Amount - setAmount(e.currentTarget.value)} - /> -
-
- Rate - setRate(e.currentTarget.value)} /> -
-
- Phone - setPhone(e.currentTarget.value)} /> -
-
- Whole number - -
-
-

- Try typing letters into any of them. -

-
- - - -`} - > -
-
- Term (plain select) - setTerm(e.currentTarget.value)}> - - - - -
-
- State (searchable) - -
-
- Products (multi) - -
-
-

- selected: {tags().join(", ") || "—"} -

-
- - `} - > - - -
- Notes - setNotes(e.currentTarget.value)} - /> -
-
- -
- - Save - - - {emailError() ? "Fix the email address first." : "The button disables itself off derived state."} - -
-
-
- ); -} diff --git a/go/cmd/kjol-web/frontend/src/pages/Kit.tsx b/go/cmd/kjol-web/frontend/src/pages/Kit.tsx deleted file mode 100644 index 0dd66771..00000000 --- a/go/cmd/kjol-web/frontend/src/pages/Kit.tsx +++ /dev/null @@ -1,199 +0,0 @@ -// /js/kit — the components, running. - -import { createSignal, For } from "solid-js"; -import { - ButtonUI, - SegmentedButtons, - BUTTON_COLOR_PRIMARY, - BUTTON_COLOR_NEUTRAL, - BUTTON_COLOR_GREEN, - BUTTON_COLOR_RED, - BUTTON_COLOR_BLUE, -} from "@ui/Buttons"; -import { Badge, BADGE_GREEN, BADGE_RED, BADGE_BLUE, BADGE_AMBER, BADGE_NEUTRAL } from "@ui/Badges"; -import { AlertBlue, AlertGreen, AlertRed, AlertYellow } from "@ui/Alerts"; -import { Card, CardHeader } from "@ui/Cards"; -import { TabGroup } from "@ui/Tabs"; -import { Modal, ConfirmModal } from "@ui/Modal"; -import { Tooltip } from "@ui/Tooltips"; -import { Icon } from "@ui/Icons"; -import { Demo } from "../layout/Demo.tsx"; - -export function Kit() { - const [count, setCount] = createSignal(0); - const [seg, setSeg] = createSignal("day"); - const [modalOpen, setModalOpen] = createSignal(false); - const [confirmOpen, setConfirmOpen] = createSignal(false); - const [confirmed, setConfirmed] = createSignal(0); - - return ( -
-

Kjol JS Web

-

Components

-

- Every component below is the real one from{" "} - @ui/*, imported - and rendered on this page. Nothing here is a picture of a component. -

- - setCount(count() + 1)}> - Clicked {count()} times -`} - > -
- setCount(count() + 1)}> - Clicked {count()} times - - Neutral - Green - Red - - Outline - - - Small - - - Disabled - -
-
- - `} - > -
- -

- selected: {seg()} -

-
-
- - Active`} - > -
- - Active - - - Overdue - - Info - Pending - Draft -
-
- - Your changes have been written.`} - > -
- Your changes have been written. - The rate table refreshes every fifteen minutes. - Two rows are missing a maturity date. - The upload was rejected by the server. -
-
- -

}, …]} />`} - > - Three accounts, two of them funded.

}, - { title: "Activity", badge: 3, content:

Three events since Tuesday.

}, - { title: "Settings", content:

Nothing configurable yet.

}, - ]} - /> -
- - setModalOpen(false)} header="A modal"> - … - - -// no provider needed — it portals itself to document.body`} - > -
- setModalOpen(true)}> - Open modal - - setConfirmOpen(true)}> - Delete something - - confirmed {confirmed()} times -
- - setModalOpen(false)} header={

A modal

}> -

- It portals itself to document.body, so it escapes any - ancestor with overflow: hidden or a transform — the two - things that silently clip a floating panel. -

-
- - setConfirmOpen(false)} - onConfirm={() => setConfirmed(confirmed() + 1)} - title="Delete this?" - message="This cannot be undone. (Nothing is actually deleted — this is a docs page.)" - confirmText="Delete" - /> -
- - `} - > -
- - {(name) => ( - - - - - - )} - -
-

- Only the icons actually referenced in the source are bundled. The registry for this whole - site is a few dozen paths, not FontAwesome's 41.5 MB kit. -

-
- - - Not shown here -

- The kit also carries a calendar, a date picker, popovers, an accordion, a signature pad, a - chart wrapper, a toast system, a guided-tour overlay and a fuzzy matcher. They are in{" "} - go/jsruntime/uikit. -

-
-
- ); -} diff --git a/go/cmd/kjol-web/frontend/src/pages/NotFound.tsx b/go/cmd/kjol-web/frontend/src/pages/NotFound.tsx new file mode 100644 index 00000000..b8d55cc6 --- /dev/null +++ b/go/cmd/kjol-web/frontend/src/pages/NotFound.tsx @@ -0,0 +1,41 @@ +// The fallback for any /js/* URL the router does not know. +// +// The server cannot 404 these: it answers every /js/* path with the same SPA shell, +// because it has no idea which routes the bundle contains. So the router has to be the +// one to say so — and if it does not, an unknown URL renders the chrome around an empty +//
, which is a blank page with a 200 and no explanation. +// +// It names the paths that MOVED, because that is what a stale bookmark most likely wants: +// /js/kit, /js/forms, /js/table and /js/theming were four pages, and are now four +// sections of one. + +import { useLocation, useNavigate } from "@solidjs/router"; +import { ButtonUI, BUTTON_COLOR_PRIMARY, BUTTON_COLOR_LIGHT_NEUTRAL } from "@ui/Buttons"; + +export function NotFound() { + const location = useLocation(); + const navigate = useNavigate(); + + return ( +
+

Page not found

+

+ No route matches {location.pathname}. +

+

+ The kit used to be spread across several pages — /js/kit,{" "} + /js/forms, /js/table,{" "} + /js/theming. It is one page now, and they are sections of it. +

+ +
+ navigate("/components")}> + Go to Components + + navigate("/")}> + Overview + +
+
+ ); +} diff --git a/go/cmd/kjol-web/frontend/src/pages/Overview.tsx b/go/cmd/kjol-web/frontend/src/pages/Overview.tsx index 8e282afb..3fcbfaa3 100644 --- a/go/cmd/kjol-web/frontend/src/pages/Overview.tsx +++ b/go/cmd/kjol-web/frontend/src/pages/Overview.tsx @@ -1,13 +1,19 @@ // /js — what the JS layer is, and how it is built. +import { For } from "solid-js"; +import { useNavigate } from "@solidjs/router"; import { Card, CardHeader } from "@ui/Cards"; import { AlertBlue } from "@ui/Alerts"; import { CodeBox } from "@ui/General"; +import { Icon } from "@ui/Icons"; +import { COMPONENT_GROUPS } from "../componentGroups.ts"; export function Overview() { + const navigate = useNavigate(); + return ( -
-

Kjol JS Web

+
+

Kjøl JS Web

A Solid kit, built by a Go toolchain

@@ -48,7 +54,7 @@ export function Overview() { />
- + The single hardest invariant in this build is that there is exactly one copy of solid-js. Two @@ -57,26 +63,38 @@ export function Overview() { precisely this reason. -

What is on the other pages

-
); diff --git a/go/cmd/kjol-web/frontend/src/pages/Table.tsx b/go/cmd/kjol-web/frontend/src/pages/Table.tsx deleted file mode 100644 index b7dfe313..00000000 --- a/go/cmd/kjol-web/frontend/src/pages/Table.tsx +++ /dev/null @@ -1,167 +0,0 @@ -// /js/table — AutoTable, driven by an array of column definitions. - -import AutoTable, { - AutoTableColumn, - AutoTableSearch, - AutoTableFilterFields, - TdLeft, - TdRight, - TdCenter, - COL_POS_LEFT, - COL_POS_RIGHT, - COL_POS_CENTER, - AUTOTABLE_SIZE_COMPACT, -} from "@ui/AutoTable"; -import { Badge, BADGE_GREEN, BADGE_RED, BADGE_NEUTRAL } from "@ui/Badges"; -import { AlertBlue } from "@ui/Alerts"; - -interface Institution { - name: string; - state: string; - term: string; - rate: number; - minimum: number; - status: "open" | "closed" | "waitlist"; -} - -// Static rows: the point of the page is the table, not where the rows came from. -// Swapping `data` for `url` is the only change needed to make it fetch, sort and -// paginate against a server instead. -const ROWS: Institution[] = [ - { name: "First Meridian Bank", state: "CA", term: "90 day", rate: 4.85, minimum: 1000, status: "open" }, - { name: "Harborline Credit Union", state: "WA", term: "180 day", rate: 5.1, minimum: 2500, status: "open" }, - { name: "Cascade Federal", state: "OR", term: "1 year", rate: 5.35, minimum: 500, status: "waitlist" }, - { name: "Ironwood Savings", state: "IL", term: "90 day", rate: 4.6, minimum: 10000, status: "closed" }, - { name: "Great Lakes Trust", state: "MI", term: "2 year", rate: 5.55, minimum: 1000, status: "open" }, - { name: "Sunbelt National", state: "TX", term: "180 day", rate: 4.95, minimum: 5000, status: "open" }, - { name: "Granite State Bank", state: "NH", term: "1 year", rate: 5.2, minimum: 2000, status: "waitlist" }, - { name: "Pacific Crest", state: "CA", term: "5 year", rate: 5.75, minimum: 25000, status: "open" }, - { name: "Copper Ridge Bank", state: "AZ", term: "90 day", rate: 4.4, minimum: 1000, status: "closed" }, - { name: "Bayou Community", state: "LA", term: "1 year", rate: 5.05, minimum: 1500, status: "open" }, - { name: "Northern Pine FCU", state: "MN", term: "2 year", rate: 5.45, minimum: 500, status: "open" }, - { name: "Chesapeake First", state: "MD", term: "180 day", rate: 4.75, minimum: 3000, status: "waitlist" }, -]; - -// The whole table is this list. Sorting, column ordering, hiding, resizing and CSV -// export are all driven from it — there is no per-column wiring anywhere else. -const COLUMNS: AutoTableColumn[] = [ - { displayName: "Institution", sortable: true, sortIdentifier: "name", displayPosition: COL_POS_LEFT }, - { displayName: "State", sortable: true, sortIdentifier: "state", displayPosition: COL_POS_CENTER, toggleable: true }, - { displayName: "Term", sortable: true, sortIdentifier: "term", displayPosition: COL_POS_LEFT }, - { - displayName: "Rate", - sortable: true, - sortIdentifier: "rate", - sortType: "numeric", - displayPosition: COL_POS_RIGHT, - csvValue: (i: Institution) => i.rate, - }, - { - displayName: "Minimum", - sortable: true, - sortIdentifier: "minimum", - sortType: "money", - displayPosition: COL_POS_RIGHT, - toggleable: true, - csvValue: (i: Institution) => i.minimum, - }, - { displayName: "Status", displayPosition: COL_POS_CENTER, sortable: true, sortIdentifier: "status" }, -]; - -const money = (n: number) => "$" + n.toLocaleString("en-US"); - -function StatusBadge(props: { status: Institution["status"] }) { - if (props.status === "open") return open; - if (props.status === "closed") return closed; - return waitlist; -} - -export function Table() { - return ( -
-

Kjol JS Web

-

AutoTable

-

- One array of column definitions produces sorting, per-column search, column reordering by - drag, column show/hide, column resizing, pagination and CSV export. The page below writes no - table markup — only a rowRenderer{" "} - to say what a cell looks like. -

- - - Sort by clicking a header. Drag a header to reorder. Use the toolbar to hide a column or - export what you are looking at. The column layout persists — it is keyed to localStorage, so - it survives a reload. - - -
- ( - - ctx.setSearchValue("name", v)} - /> - ctx.setSearchValue("state", v)} - /> - - )} - rowRenderer={(item: Institution) => ( - <> - {item.name} - {item.state} - {item.term} - {item.rate.toFixed(2)}% - {money(item.minimum)} - - - - - )} - /> -
- -
-

Local rows, or a server

-

- This table is passed data. - Give it url instead and - the same column list drives a server-side query — the sort identifier becomes the sort key, - the search fields become query parameters, and pagination is handled for you. Nothing else - on the page changes. -

-

- The Go/WASM layer has this same table, rewritten as Go returning a virtual DOM. Same - behaviour, no JavaScript — which is the whole argument the other half of this site is - making. -

-
-
- ); -} diff --git a/go/cmd/kjol-web/frontend/src/pages/Theming.tsx b/go/cmd/kjol-web/frontend/src/pages/Theming.tsx deleted file mode 100644 index 3eea34bf..00000000 --- a/go/cmd/kjol-web/frontend/src/pages/Theming.tsx +++ /dev/null @@ -1,168 +0,0 @@ -// /js/theming — how the kit is themed, and the switch that proves it. - -import { AlertBlue, AlertGreen } from "@ui/Alerts"; -import { Card, CardHeader } from "@ui/Cards"; -import { ButtonUI, BUTTON_COLOR_PRIMARY, BUTTON_COLOR_NEUTRAL, BUTTON_COLOR_WHITE } from "@ui/Buttons"; -import { Badge, BADGE_GREEN, BADGE_NEUTRAL } from "@ui/Badges"; -import { CodeBox } from "@ui/General"; -import { ThemeToggle, useTheme } from "@ui/Theme"; -import { Demo } from "../layout/Demo.tsx"; - -// The swatch class is written out in full, not built as "bg-" + name. Tailwind finds -// the classes it must compile by SCANNING THE SOURCE for literal strings — a -// concatenation is invisible to it, and every swatch here would come out colourless. -// It is the one thing about a utility CSS engine you cannot forget. -const TOKENS: { swatch: string; name: string; role: string }[] = [ - { swatch: "bg-surface", name: "surface", role: "the page" }, - { swatch: "bg-surface-muted", name: "surface-muted", role: "a recessed strip" }, - { swatch: "bg-surface-raised", name: "surface-raised", role: "a panel, a hover" }, - { swatch: "bg-surface-strong", name: "surface-strong", role: "a track, a divider fill" }, - { swatch: "bg-line", name: "line", role: "an ordinary border" }, - { swatch: "bg-line-strong", name: "line-strong", role: "a border that has to be seen" }, - { swatch: "bg-ink", name: "ink", role: "body text, headings" }, - { swatch: "bg-ink-soft", name: "ink-soft", role: "secondary text" }, - { swatch: "bg-ink-muted", name: "ink-muted", role: "captions, labels" }, - { swatch: "bg-ink-faint", name: "ink-faint", role: "placeholders, disabled" }, -]; - -export function Theming() { - const { isDark, mode } = useTheme(); - - return ( -
-

Kjol JS Web

-

Theming

- -

- No component in this kit names a colour. They say{" "} - bg-surface,{" "} - text-ink,{" "} - border-line — and - what those mean is decided in one place. That is the whole of the theme system, and it is why - dark mode is a rule that re-points ten variables rather than a{" "} - dark: variant on four hundred class strings. -

- - -
- -
- currently {isDark() ? "dark" : "light"}, because - you asked for {mode()} -
-
-

- Press it. Every component on every page of this section moves — none of them were told. - Your choice is remembered, and it is the same choice the Go/WASM section reads: - both halves of this site share one localStorage key, so the theme survives crossing between - two entirely different front-ends. -

-
- -

The contract

-

- These are the tokens a component is allowed to name. Each swatch below is drawn with the token - itself, so this table is not a picture of the theme — it is the theme, and it repaints - when you press the switch. -

- -
- {TOKENS.map((t, i) => ( -
0 ? "border-t border-line" : "") - } - > - - {t.name} - {t.role} -
- ))} -
- - - The Go/WASM kit uses these exact token names. A designer changes{" "} - surface once and both halves of the site move together — even - though one is Solid compiled by esbuild and the other is Go compiled to WebAssembly. - - -

Where a variant is still needed

-

- Two things a re-pointed token cannot fix, so they are the only places the kit still carries a{" "} - dark: variant. -

- -
- - Coloured tints -

- A red-50 wash is invisible on a near-black surface. An - alert's tint has to become a deep, transparent one — a different colour, not a - different value of the same one. -

-
- - Fills that invert -

- The neutral button is dark on a light page and light on a dark one — so its label must - invert with it. text-white would disappear the moment - the fill went pale. Hence three tokens, not one. -

-
-
- - -
- Neutral (inverts) - White (a surface) - Primary (a fill) - solid - fills stay put -
-
- - - The theme class is applied by a ten-line script in the document head, before the stylesheet and - before any markup. The server cannot read localStorage, so it cannot know which theme to send; - if the class waited for the bundle, every dark-mode reader would get a white page and then have - it snatched away. It is the only hand-written JavaScript on the Go/WASM side of this site. - - - - - -`} - /> -
- ); -} diff --git a/go/cmd/kjol-web/frontend/src/pages/public/PublicLayout.tsx b/go/cmd/kjol-web/frontend/src/pages/public/PublicLayout.tsx index 306e4957..a1ef4f84 100644 --- a/go/cmd/kjol-web/frontend/src/pages/public/PublicLayout.tsx +++ b/go/cmd/kjol-web/frontend/src/pages/public/PublicLayout.tsx @@ -31,7 +31,7 @@ export function PublicLayout(props: { currentPath: string; children?: JSXElement - Kjol JS Web + Kjøl JS Web Solid + Go toolchain @@ -65,7 +65,7 @@ export function PublicLayout(props: { currentPath: string; children?: JSXElement

- Kjol JS Web is one layer of kjol — a shared base layer. kjol is Norwegian for keel. + Kjøl JS Web is one layer of kjol — a shared base layer. kjol is Norwegian for keel.

diff --git a/go/cmd/kjol-web/frontend/src/pages/public/Ssr.tsx b/go/cmd/kjol-web/frontend/src/pages/public/Ssr.tsx index 22bdf2a0..7cc3c512 100644 --- a/go/cmd/kjol-web/frontend/src/pages/public/Ssr.tsx +++ b/go/cmd/kjol-web/frontend/src/pages/public/Ssr.tsx @@ -29,7 +29,7 @@ export function Ssr() { return (
-

Kjol JS Web

+

Kjøl JS Web

This page was rendered by Go

@@ -69,7 +69,7 @@ export function Ssr() {

- Back to Kjol JS Web + Back to Kjøl JS Web

diff --git a/go/cmd/kjol-web/frontend/src/pages/public/pages.ts b/go/cmd/kjol-web/frontend/src/pages/public/pages.ts index 927b0631..cab759d1 100644 --- a/go/cmd/kjol-web/frontend/src/pages/public/pages.ts +++ b/go/cmd/kjol-web/frontend/src/pages/public/pages.ts @@ -22,7 +22,7 @@ export const publicPages: PublicPageDef[] = [ path: "/js/ssr", module: "pages/public/Ssr.tsx", component: "Ssr", - title: "Server-rendered — Kjol JS Web", + title: "Server-rendered — Kjøl JS Web", // dynamic: the server may inject data for this route at request time, so bake // the render bundle too, not just the static skeleton. dynamic: true, diff --git a/go/cmd/kjol-web/frontend/src/pages/public/routes.gen.ts b/go/cmd/kjol-web/frontend/src/pages/public/routes.gen.ts index 23460ece..0a2bc954 100644 --- a/go/cmd/kjol-web/frontend/src/pages/public/routes.gen.ts +++ b/go/cmd/kjol-web/frontend/src/pages/public/routes.gen.ts @@ -13,5 +13,5 @@ export const publicRoutes: Record JSXElement> = { // for each public route, applied by the client router on navigation // (the first load gets its title from the server-rendered shell). export const publicTitles: Record<string, string> = { - "/js/ssr": "Server-rendered — Kjol JS Web", + "/js/ssr": "Server-rendered — Kjøl JS Web", }; diff --git a/go/cmd/kjol-web/frontend/vendor/@kurkle/color/dist/color.esm.js b/go/cmd/kjol-web/frontend/vendor/@kurkle/color/dist/color.esm.js new file mode 100644 index 00000000..aacc4c22 --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/@kurkle/color/dist/color.esm.js @@ -0,0 +1,589 @@ +/*! + * @kurkle/color v0.3.4 + * https://github.com/kurkle/color#readme + * (c) 2024 Jukka Kurkela + * Released under the MIT License + */ +function round(v) { + return v + 0.5 | 0; +} +const lim = (v, l, h) => Math.max(Math.min(v, h), l); +function p2b(v) { + return lim(round(v * 2.55), 0, 255); +} +function b2p(v) { + return lim(round(v / 2.55), 0, 100); +} +function n2b(v) { + return lim(round(v * 255), 0, 255); +} +function b2n(v) { + return lim(round(v / 2.55) / 100, 0, 1); +} +function n2p(v) { + return lim(round(v * 100), 0, 100); +} + +const map$1 = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, A: 10, B: 11, C: 12, D: 13, E: 14, F: 15, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15}; +const hex = [...'0123456789ABCDEF']; +const h1 = b => hex[b & 0xF]; +const h2 = b => hex[(b & 0xF0) >> 4] + hex[b & 0xF]; +const eq = b => ((b & 0xF0) >> 4) === (b & 0xF); +const isShort = v => eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a); +function hexParse(str) { + var len = str.length; + var ret; + if (str[0] === '#') { + if (len === 4 || len === 5) { + ret = { + r: 255 & map$1[str[1]] * 17, + g: 255 & map$1[str[2]] * 17, + b: 255 & map$1[str[3]] * 17, + a: len === 5 ? map$1[str[4]] * 17 : 255 + }; + } else if (len === 7 || len === 9) { + ret = { + r: map$1[str[1]] << 4 | map$1[str[2]], + g: map$1[str[3]] << 4 | map$1[str[4]], + b: map$1[str[5]] << 4 | map$1[str[6]], + a: len === 9 ? (map$1[str[7]] << 4 | map$1[str[8]]) : 255 + }; + } + } + return ret; +} +const alpha = (a, f) => a < 255 ? f(a) : ''; +function hexString(v) { + var f = isShort(v) ? h1 : h2; + return v + ? '#' + f(v.r) + f(v.g) + f(v.b) + alpha(v.a, f) + : undefined; +} + +const HUE_RE = /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/; +function hsl2rgbn(h, s, l) { + const a = s * Math.min(l, 1 - l); + const f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); + return [f(0), f(8), f(4)]; +} +function hsv2rgbn(h, s, v) { + const f = (n, k = (n + h / 60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0); + return [f(5), f(3), f(1)]; +} +function hwb2rgbn(h, w, b) { + const rgb = hsl2rgbn(h, 1, 0.5); + let i; + if (w + b > 1) { + i = 1 / (w + b); + w *= i; + b *= i; + } + for (i = 0; i < 3; i++) { + rgb[i] *= 1 - w - b; + rgb[i] += w; + } + return rgb; +} +function hueValue(r, g, b, d, max) { + if (r === max) { + return ((g - b) / d) + (g < b ? 6 : 0); + } + if (g === max) { + return (b - r) / d + 2; + } + return (r - g) / d + 4; +} +function rgb2hsl(v) { + const range = 255; + const r = v.r / range; + const g = v.g / range; + const b = v.b / range; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const l = (max + min) / 2; + let h, s, d; + if (max !== min) { + d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + h = hueValue(r, g, b, d, max); + h = h * 60 + 0.5; + } + return [h | 0, s || 0, l]; +} +function calln(f, a, b, c) { + return ( + Array.isArray(a) + ? f(a[0], a[1], a[2]) + : f(a, b, c) + ).map(n2b); +} +function hsl2rgb(h, s, l) { + return calln(hsl2rgbn, h, s, l); +} +function hwb2rgb(h, w, b) { + return calln(hwb2rgbn, h, w, b); +} +function hsv2rgb(h, s, v) { + return calln(hsv2rgbn, h, s, v); +} +function hue(h) { + return (h % 360 + 360) % 360; +} +function hueParse(str) { + const m = HUE_RE.exec(str); + let a = 255; + let v; + if (!m) { + return; + } + if (m[5] !== v) { + a = m[6] ? p2b(+m[5]) : n2b(+m[5]); + } + const h = hue(+m[2]); + const p1 = +m[3] / 100; + const p2 = +m[4] / 100; + if (m[1] === 'hwb') { + v = hwb2rgb(h, p1, p2); + } else if (m[1] === 'hsv') { + v = hsv2rgb(h, p1, p2); + } else { + v = hsl2rgb(h, p1, p2); + } + return { + r: v[0], + g: v[1], + b: v[2], + a: a + }; +} +function rotate(v, deg) { + var h = rgb2hsl(v); + h[0] = hue(h[0] + deg); + h = hsl2rgb(h); + v.r = h[0]; + v.g = h[1]; + v.b = h[2]; +} +function hslString(v) { + if (!v) { + return; + } + const a = rgb2hsl(v); + const h = a[0]; + const s = n2p(a[1]); + const l = n2p(a[2]); + return v.a < 255 + ? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})` + : `hsl(${h}, ${s}%, ${l}%)`; +} + +const map = { + x: 'dark', + Z: 'light', + Y: 're', + X: 'blu', + W: 'gr', + V: 'medium', + U: 'slate', + A: 'ee', + T: 'ol', + S: 'or', + B: 'ra', + C: 'lateg', + D: 'ights', + R: 'in', + Q: 'turquois', + E: 'hi', + P: 'ro', + O: 'al', + N: 'le', + M: 'de', + L: 'yello', + F: 'en', + K: 'ch', + G: 'arks', + H: 'ea', + I: 'ightg', + J: 'wh' +}; +const names$1 = { + OiceXe: 'f0f8ff', + antiquewEte: 'faebd7', + aqua: 'ffff', + aquamarRe: '7fffd4', + azuY: 'f0ffff', + beige: 'f5f5dc', + bisque: 'ffe4c4', + black: '0', + blanKedOmond: 'ffebcd', + Xe: 'ff', + XeviTet: '8a2be2', + bPwn: 'a52a2a', + burlywood: 'deb887', + caMtXe: '5f9ea0', + KartYuse: '7fff00', + KocTate: 'd2691e', + cSO: 'ff7f50', + cSnflowerXe: '6495ed', + cSnsilk: 'fff8dc', + crimson: 'dc143c', + cyan: 'ffff', + xXe: '8b', + xcyan: '8b8b', + xgTMnPd: 'b8860b', + xWay: 'a9a9a9', + xgYF: '6400', + xgYy: 'a9a9a9', + xkhaki: 'bdb76b', + xmagFta: '8b008b', + xTivegYF: '556b2f', + xSange: 'ff8c00', + xScEd: '9932cc', + xYd: '8b0000', + xsOmon: 'e9967a', + xsHgYF: '8fbc8f', + xUXe: '483d8b', + xUWay: '2f4f4f', + xUgYy: '2f4f4f', + xQe: 'ced1', + xviTet: '9400d3', + dAppRk: 'ff1493', + dApskyXe: 'bfff', + dimWay: '696969', + dimgYy: '696969', + dodgerXe: '1e90ff', + fiYbrick: 'b22222', + flSOwEte: 'fffaf0', + foYstWAn: '228b22', + fuKsia: 'ff00ff', + gaRsbSo: 'dcdcdc', + ghostwEte: 'f8f8ff', + gTd: 'ffd700', + gTMnPd: 'daa520', + Way: '808080', + gYF: '8000', + gYFLw: 'adff2f', + gYy: '808080', + honeyMw: 'f0fff0', + hotpRk: 'ff69b4', + RdianYd: 'cd5c5c', + Rdigo: '4b0082', + ivSy: 'fffff0', + khaki: 'f0e68c', + lavFMr: 'e6e6fa', + lavFMrXsh: 'fff0f5', + lawngYF: '7cfc00', + NmoncEffon: 'fffacd', + ZXe: 'add8e6', + ZcSO: 'f08080', + Zcyan: 'e0ffff', + ZgTMnPdLw: 'fafad2', + ZWay: 'd3d3d3', + ZgYF: '90ee90', + ZgYy: 'd3d3d3', + ZpRk: 'ffb6c1', + ZsOmon: 'ffa07a', + ZsHgYF: '20b2aa', + ZskyXe: '87cefa', + ZUWay: '778899', + ZUgYy: '778899', + ZstAlXe: 'b0c4de', + ZLw: 'ffffe0', + lime: 'ff00', + limegYF: '32cd32', + lRF: 'faf0e6', + magFta: 'ff00ff', + maPon: '800000', + VaquamarRe: '66cdaa', + VXe: 'cd', + VScEd: 'ba55d3', + VpurpN: '9370db', + VsHgYF: '3cb371', + VUXe: '7b68ee', + VsprRggYF: 'fa9a', + VQe: '48d1cc', + VviTetYd: 'c71585', + midnightXe: '191970', + mRtcYam: 'f5fffa', + mistyPse: 'ffe4e1', + moccasR: 'ffe4b5', + navajowEte: 'ffdead', + navy: '80', + Tdlace: 'fdf5e6', + Tive: '808000', + TivedBb: '6b8e23', + Sange: 'ffa500', + SangeYd: 'ff4500', + ScEd: 'da70d6', + pOegTMnPd: 'eee8aa', + pOegYF: '98fb98', + pOeQe: 'afeeee', + pOeviTetYd: 'db7093', + papayawEp: 'ffefd5', + pHKpuff: 'ffdab9', + peru: 'cd853f', + pRk: 'ffc0cb', + plum: 'dda0dd', + powMrXe: 'b0e0e6', + purpN: '800080', + YbeccapurpN: '663399', + Yd: 'ff0000', + Psybrown: 'bc8f8f', + PyOXe: '4169e1', + saddNbPwn: '8b4513', + sOmon: 'fa8072', + sandybPwn: 'f4a460', + sHgYF: '2e8b57', + sHshell: 'fff5ee', + siFna: 'a0522d', + silver: 'c0c0c0', + skyXe: '87ceeb', + UXe: '6a5acd', + UWay: '708090', + UgYy: '708090', + snow: 'fffafa', + sprRggYF: 'ff7f', + stAlXe: '4682b4', + tan: 'd2b48c', + teO: '8080', + tEstN: 'd8bfd8', + tomato: 'ff6347', + Qe: '40e0d0', + viTet: 'ee82ee', + JHt: 'f5deb3', + wEte: 'ffffff', + wEtesmoke: 'f5f5f5', + Lw: 'ffff00', + LwgYF: '9acd32' +}; +function unpack() { + const unpacked = {}; + const keys = Object.keys(names$1); + const tkeys = Object.keys(map); + let i, j, k, ok, nk; + for (i = 0; i < keys.length; i++) { + ok = nk = keys[i]; + for (j = 0; j < tkeys.length; j++) { + k = tkeys[j]; + nk = nk.replace(k, map[k]); + } + k = parseInt(names$1[ok], 16); + unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF]; + } + return unpacked; +} + +let names; +function nameParse(str) { + if (!names) { + names = unpack(); + names.transparent = [0, 0, 0, 0]; + } + const a = names[str.toLowerCase()]; + return a && { + r: a[0], + g: a[1], + b: a[2], + a: a.length === 4 ? a[3] : 255 + }; +} + +const RGB_RE = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/; +function rgbParse(str) { + const m = RGB_RE.exec(str); + let a = 255; + let r, g, b; + if (!m) { + return; + } + if (m[7] !== r) { + const v = +m[7]; + a = m[8] ? p2b(v) : lim(v * 255, 0, 255); + } + r = +m[1]; + g = +m[3]; + b = +m[5]; + r = 255 & (m[2] ? p2b(r) : lim(r, 0, 255)); + g = 255 & (m[4] ? p2b(g) : lim(g, 0, 255)); + b = 255 & (m[6] ? p2b(b) : lim(b, 0, 255)); + return { + r: 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 to = v => v <= 0.0031308 ? v * 12.92 : Math.pow(v, 1.0 / 2.4) * 1.055 - 0.055; +const from = v => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); +function interpolate(rgb1, rgb2, t) { + const r = from(b2n(rgb1.r)); + const g = from(b2n(rgb1.g)); + const b = from(b2n(rgb1.b)); + return { + r: n2b(to(r + t * (from(b2n(rgb2.r)) - r))), + g: n2b(to(g + t * (from(b2n(rgb2.g)) - g))), + b: n2b(to(b + t * (from(b2n(rgb2.b)) - b))), + a: rgb1.a + t * (rgb2.a - rgb1.a) + }; +} + +function modHSL(v, i, ratio) { + if (v) { + let tmp = rgb2hsl(v); + tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 360 : 1)); + tmp = hsl2rgb(tmp); + v.r = tmp[0]; + v.g = tmp[1]; + v.b = tmp[2]; + } +} +function clone(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) : undefined; + } + hexString() { + return this._valid ? hexString(this._rgb) : undefined; + } + hslString() { + return this._valid ? hslString(this._rgb) : undefined; + } + mix(color, weight) { + if (color) { + const c1 = this.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; + this.rgb = c1; + } + return this; + } + interpolate(color, t) { + if (color) { + this._rgb = interpolate(this._rgb, color._rgb, t); + } + return this; + } + clone() { + return new Color(this.rgb); + } + alpha(a) { + this._rgb.a = n2b(a); + return this; + } + clearer(ratio) { + const rgb = this._rgb; + rgb.a *= 1 - ratio; + return this; + } + greyscale() { + const rgb = this._rgb; + const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11); + rgb.r = rgb.g = rgb.b = val; + return this; + } + opaquer(ratio) { + const rgb = this._rgb; + rgb.a *= 1 + ratio; + return this; + } + negate() { + const v = this._rgb; + v.r = 255 - v.r; + v.g = 255 - v.g; + v.b = 255 - v.b; + return this; + } + lighten(ratio) { + modHSL(this._rgb, 2, ratio); + return this; + } + darken(ratio) { + modHSL(this._rgb, 2, -ratio); + return this; + } + saturate(ratio) { + modHSL(this._rgb, 1, ratio); + return this; + } + desaturate(ratio) { + modHSL(this._rgb, 1, -ratio); + return this; + } + rotate(deg) { + rotate(this._rgb, deg); + return this; + } +} + +function index_esm(input) { + return new Color(input); +} + +export { Color, b2n, b2p, index_esm as default, hexParse, hexString, hsl2rgb, hslString, hsv2rgb, hueParse, hwb2rgb, lim, n2b, n2p, nameParse, p2b, rgb2hsl, rgbParse, rgbString, rotate, round }; diff --git a/go/cmd/kjol-web/frontend/vendor/@kurkle/color/package.json b/go/cmd/kjol-web/frontend/vendor/@kurkle/color/package.json new file mode 100644 index 00000000..5a4b80e7 --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/@kurkle/color/package.json @@ -0,0 +1,77 @@ +{ + "name": "@kurkle/color", + "type": "module", + "version": "0.3.4", + "description": "css color parsing, manupulation and conversion", + "sideEffects": false, + "main": "dist/color.cjs", + "module": "dist/color.esm.js", + "types": "dist/color.d.ts", + "exports": { + "types": "./dist/color.d.ts", + "import": "./dist/color.esm.js", + "require": "./dist/color.cjs" + }, + "scripts": { + "build": "node util/copy_dist.js && rollup -c", + "lint": "eslint src/*.js test/*.js util/*.js", + "test": "node test/index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/kurkle/color.git" + }, + "files": [ + "dist/*", + "dist/color.d.ts" + ], + "keywords": [ + "color", + "colour", + "css", + "hsl", + "hex", + "rgb", + "rgba", + "hwb", + "hsv", + "cmyk" + ], + "author": "Jukka Kurkela", + "license": "MIT", + "bugs": { + "url": "https://github.com/kurkle/color/issues" + }, + "homepage": "https://github.com/kurkle/color#readme", + "devDependencies": { + "@rollup/plugin-terser": "^0.4.0", + "assert": "^2.0.0", + "benchmark": "^2.1.4", + "chartjs-color": "^2.4.1", + "chartjs-color-string": "^0.6.0", + "child_process": "^1.0.2", + "chroma-js": "^3.1.1", + "color-name": "^2.0.0", + "color-names": "^2.0.0", + "color-parse": "^2.0.2", + "color-parser": "^0.1.0", + "color-string": "^1.5.5", + "csscolorparser": "^1.0.3", + "eslint": "^9.15.0", + "eslint-config-chartjs": "^0.3.0", + "eslint-config-defaults": "^9.0.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-react": "^7.22.0", + "fs": "0.0.1-security", + "perf_hooks": "0.0.1", + "rollup": "^4.25.0", + "rollup-plugin-analyzer": "^4.0.0", + "rollup-plugin-cleanup": "^3.2.1", + "rollup-plugin-istanbul": "^5.0.0", + "rollup-plugin-visualizer": "^5.8.3", + "tinycolor2": "^1.4.2", + "typedoc": "^0.26.7", + "typescript": "^5.6.2", + "util": "^0.12.3" + } +} diff --git a/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chart.js b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chart.js new file mode 100644 index 00000000..6eb29750 --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chart.js @@ -0,0 +1,11599 @@ +/*! + * Chart.js v4.5.1 + * https://www.chartjs.org + * (c) 2025 Chart.js Contributors + * Released under the MIT License + */ +import { r as requestAnimFrame, a as resolve, e as effects, c as color, i as isObject, d as defaults, b as isArray, v as valueOrDefault, u as unlistenArrayEvents, l as listenArrayEvents, f as resolveObjectKey, g as isNumberFinite, h as defined, s as sign, j as createContext, 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 _getStartAndCountOfVisiblePoints, w as _scaleRangesChanged, x as isNumber, y as _parseObjectDataRadialScale, z as getRelativePosition, A as _rlookupByKey, B as _lookupByKey, C as _isPointInArea, D as getAngleFromPoint, E as toPadding, F as each, G as getMaximumSize, I as _getParentNode, J as readUsedSize, K as supportsEventListenerOptions, L as throttled, M as _isDomSupported, N as _factorize, O as finiteOrDefault, Q as callback, R as _addGrace, S as _limitValue, U as toDegrees, V as _measureText, W as _int16Range, X as _alignPixel, Y as clipArea, Z as renderText, $ as unclipArea, a0 as toFont, a1 as _toLeftRightCenter, a2 as _alignStartEnd, a3 as overrides, a4 as merge, a5 as _capitalize, a6 as descriptors, a7 as isFunction, a8 as _attachContext, a9 as _createResolver, aa as _descriptors, ab as mergeIf, ac as uid, ad as debounce, ae as retinaScale, af as clearCanvas, ag as setsEqual, ah as getDatasetClipArea, ai as _elementsEqual, aj as _isClickEvent, ak as _isBetween, al as _normalizeAngle, am as _readValueToProps, an as _updateBezierControlPoints, ao as _computeSegments, ap as _boundSegments, aq as _steppedInterpolation, ar as _bezierInterpolation, as as _pointInLine, at as _steppedLineTo, au as _bezierCurveTo, av as drawPoint, aw as addRoundedRectPath, ax as toTRBL, ay as toTRBLCorners, az as _boundSegment, aA as getRtlAdapter, aB as overrideTextDirection, aC as _textX, aD as restoreTextDirection, aE as drawPointLegend, aF as distanceBetweenPoints, aG as noop, aH as _setMinAndMaxByKey, aI as niceNum, aJ as almostWhole, aK as almostEquals, aL as _decimalPlaces, aM as Ticks, aN as log10, aO as _longestText, aP as _filterBetween, aQ as _lookup } from './chunks/helpers.dataset.js'; +import '@kurkle/color'; + +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 = /* #__PURE__ */ 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](); + } + } +} + +class Animations { + constructor(chart, config){ + this._chart = chart; + this._properties = new Map(); + this.configure(config); + } + configure(config) { + if (!isObject(config)) { + return; + } + const animationOptions = Object.keys(defaults.animation); + const animatedProps = this._properties; + Object.getOwnPropertyNames(config).forEach((key)=>{ + const cfg = config[key]; + if (!isObject(cfg)) { + return; + } + const resolved = {}; + for (const option of animationOptions){ + resolved[option] = cfg[option]; + } + (isArray(cfg.properties) && cfg.properties || [ + key + ]).forEach((prop)=>{ + if (prop === key || !animatedProps.has(prop)) { + animatedProps.set(prop, resolved); + } + }); + }); + } + _animateOptions(target, values) { + const newOptions = values.options; + const options = resolveTargetOptions(target, newOptions); + if (!options) { + return []; + } + const animations = this._createAnimations(options, newOptions); + if (newOptions.$shared) { + awaitAll(target.options.$animations, newOptions).then(()=>{ + target.options = newOptions; + }, ()=>{ + }); + } + return animations; + } + _createAnimations(target, values) { + const animatedProps = this._properties; + const animations = []; + const running = target.$animations || (target.$animations = {}); + const props = Object.keys(values); + const date = Date.now(); + let i; + for(i = props.length - 1; i >= 0; --i){ + const prop = props[i]; + if (prop.charAt(0) === '$') { + continue; + } + if (prop === 'options') { + animations.push(...this._animateOptions(target, values)); + continue; + } + const value = values[prop]; + let animation = running[prop]; + const cfg = animatedProps.get(prop); + if (animation) { + if (cfg && animation.active()) { + animation.update(cfg, value, date); + continue; + } else { + animation.cancel(); + } + } + if (!cfg || !cfg.duration) { + target[prop] = value; + continue; + } + running[prop] = animation = new Animation(cfg, target, prop, value); + animations.push(animation); + } + return animations; + } + update(target, values) { + if (this._properties.size === 0) { + Object.assign(target, values); + return; + } + const animations = this._createAnimations(target, values); + if (animations.length) { + animator.add(this._chart, animations); + return true; + } + } +} +function awaitAll(animations, properties) { + const running = []; + const keys = Object.keys(properties); + for(let i = 0; i < keys.length; i++){ + const anim = animations[keys[i]]; + if (anim && anim.active()) { + running.push(anim.wait()); + } + } + return Promise.all(running); +} +function resolveTargetOptions(target, newOptions) { + if (!newOptions) { + return; + } + let options = target.options; + if (!options) { + target.options = newOptions; + return; + } + if (options.$shared) { + target.options = options = Object.assign({}, options, { + $shared: false, + $animations: {} + }); + } + return options; +} + +function scaleClip(scale, allowedOverflow) { + const opts = scale && scale.options || {}; + const reverse = opts.reverse; + const min = opts.min === 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; + } + let found = false; + for(i = 0, ilen = keys.length; i < ilen; ++i){ + datasetIndex = +keys[i]; + if (datasetIndex === dsIndex) { + found = true; + if (options.all) { + continue; + } + break; + } + otherValue = stack.values[datasetIndex]; + if (isNumberFinite(otherValue) && (singleMode || value === 0 || sign(value) === sign(otherValue))) { + value += otherValue; + } + } + if (!found && !options.all) { + return 0; + } + return value; +} +function convertObjectDataToArray(data, meta) { + const { iScale , vScale } = meta; + const iAxisKey = iScale.axis === 'x' ? 'x' : 'y'; + const vAxisKey = vScale.axis === 'x' ? 'x' : 'y'; + const keys = Object.keys(data); + const adata = new Array(keys.length); + let i, ilen, key; + for(i = 0, ilen = keys.length; i < ilen; ++i){ + key = keys[i]; + adata[i] = { + [iAxisKey]: key, + [vAxisKey]: data[key] + }; + } + return adata; +} +function isStacked(scale, meta) { + const stacked = scale && scale.options.stacked; + return stacked || stacked === 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); + const visualValues = stack._visualValues || (stack._visualValues = {}); + visualValues[datasetIndex] = value; + } +} +function getFirstScaleId(chart, axis) { + const scales = chart.scales; + return Object.keys(scales).filter((key)=>scales[key].axis === axis).shift(); +} +function createDatasetContext(parent, 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]; + if (stacks[axis]._visualValues !== undefined && stacks[axis]._visualValues[datasetIndex] !== undefined) { + delete stacks[axis]._visualValues[datasetIndex]; + } + } +} +const isDirectUpdateMode = (mode)=>mode === 'reset' || mode === 'none'; +const cloneIfNotShared = (cached, shared)=>shared ? cached : Object.assign({}, cached); +const createStack = (canStack, meta, chart)=>canStack && !meta.hidden && meta._stacked && { + keys: getSortedDatasetIndices(chart, true), + values: null + }; +class DatasetController { + static defaults = {}; + static datasetElementType = null; + static dataElementType = null; + 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.supportsDecimation = false; + this.$context = undefined; + this._syncList = []; + this.datasetElementType = new.target.datasetElementType; + this.dataElementType = new.target.dataElementType; + this.initialize(); + } + initialize() { + const meta = this._cachedMeta; + this.configure(); + this.linkScales(); + meta._stacked = isStacked(meta.vScale, meta); + this.addElements(); + if (this.options.fill && !this.chart.isPluginEnabled('filler')) { + console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options"); + } + } + updateIndex(datasetIndex) { + if (this.index !== datasetIndex) { + clearStacks(this._cachedMeta); + } + this.index = datasetIndex; + } + linkScales() { + const chart = this.chart; + const meta = this._cachedMeta; + const dataset = this.getDataset(); + const chooseId = (axis, x, y, r)=>axis === 'x' ? x : axis === 'r' ? r : y; + const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x')); + const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y')); + const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, 'r')); + const indexAxis = meta.indexAxis; + const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid); + const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid); + meta.xScale = this.getScaleForId(xid); + meta.yScale = this.getScaleForId(yid); + meta.rScale = this.getScaleForId(rid); + meta.iScale = this.getScaleForId(iid); + meta.vScale = this.getScaleForId(vid); + } + getDataset() { + return this.chart.data.datasets[this.index]; + } + getMeta() { + return this.chart.getDatasetMeta(this.index); + } + getScaleForId(scaleID) { + return this.chart.scales[scaleID]; + } + _getOtherScale(scale) { + const meta = this._cachedMeta; + return scale === meta.iScale ? meta.vScale : meta.iScale; + } + reset() { + this._update('reset'); + } + _destroy() { + const meta = this._cachedMeta; + if (this._data) { + unlistenArrayEvents(this._data, this); + } + if (meta._stacked) { + clearStacks(meta); + } + } + _dataCheck() { + const dataset = this.getDataset(); + const data = dataset.data || (dataset.data = []); + const _data = this._data; + if (isObject(data)) { + const meta = this._cachedMeta; + this._data = convertObjectDataToArray(data, meta); + } else if (_data !== data) { + if (_data) { + unlistenArrayEvents(_data, this); + const meta = this._cachedMeta; + clearStacks(meta); + meta._parsed = []; + } + if (data && Object.isExtensible(data)) { + listenArrayEvents(data, this); + } + this._syncList = []; + this._data = data; + } + } + addElements() { + const meta = this._cachedMeta; + this._dataCheck(); + if (this.datasetElementType) { + meta.dataset = new this.datasetElementType(); + } + } + buildOrUpdateElements(resetNewElements) { + const meta = this._cachedMeta; + const dataset = this.getDataset(); + let stackChanged = false; + this._dataCheck(); + const oldStacked = meta._stacked; + meta._stacked = isStacked(meta.vScale, meta); + if (meta.stack !== dataset.stack) { + stackChanged = true; + clearStacks(meta); + meta.stack = dataset.stack; + } + this._resyncElements(resetNewElements); + if (stackChanged || oldStacked !== meta._stacked) { + updateStacks(this, meta._parsed); + meta._stacked = isStacked(meta.vScale, meta); + } + } + configure() { + const config = this.chart.config; + const scopeKeys = config.datasetScopeKeys(this._type); + const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true); + this.options = config.createResolver(scopes, this.getContext()); + this._parsing = this.options.parsing; + this._cachedDataOpts = {}; + } + parse(start, count) { + const { _cachedMeta: meta , _data: data } = this; + const { iScale , _stacked } = meta; + const iAxis = iScale.axis; + let sorted = start === 0 && count === data.length ? true : meta._sorted; + let prev = start > 0 && meta._parsed[start - 1]; + let i, cur, parsed; + if (this._parsing === false) { + meta._parsed = data; + meta._sorted = true; + parsed = data; + } else { + if (isArray(data[start])) { + parsed = this.parseArrayData(meta, data, start, count); + } else if (isObject(data[start])) { + parsed = this.parseObjectData(meta, data, start, count); + } else { + parsed = this.parsePrimitiveData(meta, data, start, count); + } + const isNotInOrderComparedToPrev = ()=>cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis]; + for(i = 0; i < count; ++i){ + meta._parsed[i + start] = cur = parsed[i]; + if (sorted) { + if (isNotInOrderComparedToPrev()) { + sorted = false; + } + prev = cur; + } + } + meta._sorted = sorted; + } + if (_stacked) { + updateStacks(this, parsed); + } + } + parsePrimitiveData(meta, data, start, count) { + const { iScale , vScale } = meta; + const iAxis = iScale.axis; + const vAxis = vScale.axis; + const labels = iScale.getLabels(); + const singleScale = iScale === vScale; + const parsed = new Array(count); + let i, ilen, 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]._visualValues + }; + return applyStack(stack, value, meta.index, { + mode + }); + } + updateRangeFromParsed(range, scale, parsed, stack) { + const parsedValue = parsed[scale.axis]; + let value = parsedValue === null ? NaN : parsedValue; + const values = stack && parsed._stacks[scale.axis]; + if (stack && values) { + stack.values = values; + value = applyStack(stack, parsedValue, this._cachedMeta.index); + } + range.min = Math.min(range.min, value); + range.max = Math.max(range.max, value); + } + getMinMax(scale, canStack) { + const meta = this._cachedMeta; + const _parsed = meta._parsed; + const sorted = meta._sorted && scale === meta.iScale; + const ilen = _parsed.length; + const otherScale = this._getOtherScale(scale); + const stack = createStack(canStack, meta, this.chart); + const range = { + min: Number.POSITIVE_INFINITY, + max: Number.NEGATIVE_INFINITY + }; + const { min: otherMin , max: otherMax } = getUserBounds(otherScale); + let i, parsed; + function _skip() { + parsed = _parsed[i]; + const otherValue = parsed[otherScale.axis]; + return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue; + } + for(i = 0; i < ilen; ++i){ + if (_skip()) { + continue; + } + this.updateRangeFromParsed(range, scale, parsed, stack); + if (sorted) { + break; + } + } + if (sorted) { + for(i = ilen - 1; i >= 0; --i){ + if (_skip()) { + continue; + } + this.updateRangeFromParsed(range, scale, parsed, stack); + break; + } + } + return range; + } + getAllParsedValues(scale) { + const parsed = this._cachedMeta._parsed; + const values = []; + let i, ilen, value; + for(i = 0, ilen = parsed.length; i < ilen; ++i){ + value = parsed[i][scale.axis]; + if (isNumberFinite(value)) { + values.push(value); + } + } + return values; + } + getMaxOverflow() { + return false; + } + getLabelAndValue(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, mode); + 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; + } + _getSharedOptions(start, mode) { + const firstOpts = this.resolveDataElementOptions(start, mode); + const previouslySharedOptions = this._sharedOptions; + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions; + this.updateSharedOptions(sharedOptions, mode, firstOpts); + return { + sharedOptions, + includeOptions + }; + } + updateElement(element, 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 + ]); + } +} + +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; + } + if (edge === true) { + properties.borderSkipped = { + top: true, + right: true, + bottom: true, + left: true + }; + 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 { + static id = 'bar'; + static defaults = { + datasetElementType: false, + dataElementType: 'bar', + categoryPercentage: 0.8, + barPercentage: 0.9, + grouped: true, + animations: { + numbers: { + type: 'number', + properties: [ + 'x', + 'y', + 'base', + 'width', + 'height' + ] + } + } + }; + static overrides = { + scales: { + _index_: { + type: 'category', + offset: true, + grid: { + offset: true + } + }, + _value_: { + type: 'linear', + beginAtZero: true + } + } + }; + 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 { sharedOptions , includeOptions } = this._getSharedOptions(start, mode); + 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 { iScale } = this._cachedMeta; + const metasets = iScale.getMatchingVisibleMetas(this._type).filter((meta)=>meta.controller.options.grouped); + const stacked = iScale.options.stacked; + const stacks = []; + const currentParsed = this._cachedMeta.controller.getParsed(dataIndex); + const iScaleValue = currentParsed && currentParsed[iScale.axis]; + const skipNull = (meta)=>{ + const parsed = meta._parsed.find((item)=>item[iScale.axis] === iScaleValue); + const val = parsed && parsed[meta.vScale.axis]; + if (isNullOrUndef(val) || isNaN(val)) { + return true; + } + }; + for (const meta of metasets){ + if (dataIndex !== undefined && skipNull(meta)) { + continue; + } + if (stacked === false || stacks.indexOf(meta.stack) === -1 || stacked === undefined && meta.stack === undefined) { + stacks.push(meta.stack); + } + if (meta.index === last) { + break; + } + } + if (!stacks.length) { + stacks.push(undefined); + } + return stacks; + } + _getStackCount(index) { + return this._getStacks(undefined, index).length; + } + _getAxisCount() { + return this._getAxis().length; + } + getFirstScaleIdForIndexAxis() { + const scales = this.chart.scales; + const indexScaleId = this.chart.options.indexAxis; + return Object.keys(scales).filter((key)=>scales[key].axis === indexScaleId).shift(); + } + _getAxis() { + const axis = {}; + const firstScaleAxisId = this.getFirstScaleIdForIndexAxis(); + for (const dataset of this.chart.data.datasets){ + axis[valueOrDefault(this.chart.options.indexAxis === 'x' ? dataset.xAxisID : dataset.yAxisID, firstScaleAxisId)] = true; + } + return Object.keys(axis); + } + _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 , index: datasetIndex } , 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; + } + const startPixel = vScale.getPixelForDecimal(0); + const endPixel = vScale.getPixelForDecimal(1); + const min = Math.min(startPixel, endPixel); + const max = Math.max(startPixel, endPixel); + base = Math.max(Math.min(base, max), min); + head = base + size; + if (_stacked && !floating) { + parsed._stacks[vScale.axis]._visualValues[datasetIndex] = vScale.getValueForPixel(head) - vScale.getValueForPixel(base); + } + } + 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; + const axisCount = this._getAxisCount(); + if (ruler.grouped) { + const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount; + const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount * axisCount) : computeFitCategoryTraits(index, ruler, options, stackCount * axisCount); + const axisID = this.chart.options.indexAxis === 'x' ? this.getDataset().xAxisID : this.getDataset().yAxisID; + const axisNumber = this._getAxis().indexOf(valueOrDefault(axisID, this.getFirstScaleIdForIndexAxis())); + const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined) + axisNumber; + 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].hidden) { + rects[i].draw(this._ctx); + } + } + } +} + +class BubbleController extends DatasetController { + static id = 'bubble'; + static defaults = { + datasetElementType: false, + dataElementType: 'point', + animations: { + numbers: { + type: 'number', + properties: [ + 'x', + 'y', + 'borderWidth', + 'radius' + ] + } + } + }; + static overrides = { + scales: { + x: { + type: 'linear' + }, + y: { + type: 'linear' + } + } + }; + 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 labels = this.chart.data.labels || []; + 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: labels[index] || '', + 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 { sharedOptions , includeOptions } = this._getSharedOptions(start, mode); + 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 = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode); + if (reset) { + properties.options.radius = 0; + } + } + this.updateElement(point, i, properties, mode); + } + } + 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; + } +} + +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 { + static id = 'doughnut'; + static 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' + }; + static descriptors = { + _scriptable: (name)=>name !== 'spacing', + _indexable: (name)=>name !== 'spacing' && !name.startsWith('borderDash') && !name.startsWith('hoverBorderDash') + }; + static overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels (chart) { + const data = chart.data; + const { labels: { pointStyle , textAlign , color , useBorderRadius , borderRadius } } = chart.legend.options; + if (data.labels.length && data.datasets.length) { + return data.labels.map((label, i)=>{ + const meta = chart.getDatasetMeta(0); + const style = meta.controller.getStyle(i); + return { + text: label, + fillStyle: style.backgroundColor, + fontColor: color, + hidden: !chart.getDataVisibility(i), + lineDash: style.borderDash, + lineDashOffset: style.borderDashOffset, + lineJoin: style.borderJoinStyle, + lineWidth: style.borderWidth, + strokeStyle: style.borderColor, + textAlign: textAlign, + pointStyle: pointStyle, + borderRadius: useBorderRadius && (borderRadius || style.borderRadius), + index: i + }; + }); + } + return []; + } + }, + onClick (e, legendItem, legend) { + legend.chart.toggleDataVisibility(legendItem.index); + legend.chart.update(); + } + } + } + }; + 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) && this.chart.getDatasetMeta(i).type === this._type) { + 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 { sharedOptions , includeOptions } = this._getSharedOptions(start, mode); + 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); + } + } + 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; + } +} + +class LineController extends DatasetController { + static id = 'line'; + static defaults = { + datasetElementType: 'line', + dataElementType: 'point', + showLine: true, + spanGaps: false + }; + static overrides = { + scales: { + _index_: { + type: 'category' + }, + _value_: { + type: 'linear' + } + } + }; + initialize() { + this.enableOptionSharing = true; + this.supportsDecimation = true; + super.initialize(); + } + update(mode) { + const meta = this._cachedMeta; + const { dataset: line , data: points = [] , _dataset } = meta; + const animationsDisabled = this.chart._animationsDisabled; + let { start , count } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled); + this._drawStart = start; + this._drawCount = count; + if (_scaleRangesChanged(meta)) { + start = 0; + count = points.length; + } + line._chart = this.chart; + line._datasetIndex = this.index; + line._decimated = !!_dataset._decimated; + line.points = points; + const options = this.resolveDatasetElementOptions(mode); + if (!this.options.showLine) { + options.borderWidth = 0; + } + options.segment = this.options.segment; + this.updateElement(line, 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 { sharedOptions , includeOptions } = this._getSharedOptions(start, mode); + const iAxis = iScale.axis; + const vAxis = vScale.axis; + const { spanGaps , segment } = this.options; + const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY; + const directUpdate = this.chart._animationsDisabled || reset || mode === 'none'; + const end = start + count; + const pointsCount = points.length; + let prevParsed = start > 0 && this.getParsed(start - 1); + for(let i = 0; i < pointsCount; ++i){ + const point = points[i]; + const properties = directUpdate ? point : {}; + if (i < start || i >= end) { + properties.skip = true; + continue; + } + const parsed = this.getParsed(i); + const nullData = isNullOrUndef(parsed[vAxis]); + const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i); + const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i); + properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData; + properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength; + if (segment) { + properties.parsed = parsed; + properties.raw = _dataset.data[i]; + } + if (includeOptions) { + properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode); + } + if (!directUpdate) { + this.updateElement(point, i, properties, mode); + } + prevParsed = parsed; + } + } + getMaxOverflow() { + const meta = this._cachedMeta; + const dataset = meta.dataset; + const border = dataset.options && dataset.options.borderWidth || 0; + const data = meta.data || []; + if (!data.length) { + return border; + } + const firstPoint = data[0].size(this.resolveDataElementOptions(0)); + const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1)); + return Math.max(border, firstPoint, lastPoint) / 2; + } + draw() { + const meta = this._cachedMeta; + meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis); + super.draw(); + } +} + +class PolarAreaController extends DatasetController { + static id = 'polarArea'; + static defaults = { + dataElementType: 'arc', + animation: { + animateRotate: true, + animateScale: true + }, + animations: { + numbers: { + type: 'number', + properties: [ + 'x', + 'y', + 'startAngle', + 'endAngle', + 'innerRadius', + 'outerRadius' + ] + } + }, + indexAxis: 'r', + startAngle: 0 + }; + static overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels (chart) { + const data = chart.data; + if (data.labels.length && data.datasets.length) { + const { labels: { pointStyle , color } } = 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, + fontColor: color, + lineWidth: style.borderWidth, + pointStyle: pointStyle, + hidden: !chart.getDataVisibility(i), + index: i + }; + }); + } + return []; + } + }, + onClick (e, legendItem, legend) { + legend.chart.toggleDataVisibility(legendItem.index); + legend.chart.update(); + } + } + }, + scales: { + r: { + type: 'radialLinear', + angleLines: { + display: false + }, + beginAtZero: true, + grid: { + circular: true + }, + pointLabels: { + display: false + }, + startAngle: 0 + } + } + }; + 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 + }; + } + parseObjectData(meta, data, start, count) { + return _parseObjectDataRadialScale.bind(this)(meta, data, start, count); + } + update(mode) { + const arcs = this._cachedMeta.data; + this._updateRadius(); + this.updateElements(arcs, 0, arcs.length, mode); + } + getMinMax() { + const meta = this._cachedMeta; + const range = { + min: Number.POSITIVE_INFINITY, + max: Number.NEGATIVE_INFINITY + }; + meta.data.forEach((element, index)=>{ + const parsed = this.getParsed(index).r; + if (!isNaN(parsed) && this.chart.getDataVisibility(index)) { + if (parsed < range.min) { + range.min = parsed; + } + if (parsed > range.max) { + range.max = parsed; + } + } + }); + return range; + } + _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 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(this.getParsed(i).r) : 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 meta = this._cachedMeta; + let count = 0; + meta.data.forEach((element, index)=>{ + if (!isNaN(this.getParsed(index).r) && this.chart.getDataVisibility(index)) { + count++; + } + }); + return count; + } + _computeAngle(index, mode, defaultAngle) { + return this.chart.getDataVisibility(index) ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) : 0; + } +} + +class PieController extends DoughnutController { + static id = 'pie'; + static defaults = { + cutout: 0, + rotation: 0, + circumference: 360, + radius: '100%' + }; +} + +class RadarController extends DatasetController { + static id = 'radar'; + static defaults = { + datasetElementType: 'line', + dataElementType: 'point', + indexAxis: 'r', + showLine: true, + elements: { + line: { + fill: 'start' + } + } + }; + static overrides = { + aspectRatio: 1, + scales: { + r: { + type: 'radialLinear' + } + } + }; + getLabelAndValue(index) { + const vScale = this._cachedMeta.vScale; + const parsed = this.getParsed(index); + return { + label: vScale.getLabels()[index], + value: '' + vScale.getLabelForValue(parsed[vScale.axis]) + }; + } + parseObjectData(meta, data, start, count) { + return _parseObjectDataRadialScale.bind(this)(meta, data, start, count); + } + update(mode) { + const meta = this._cachedMeta; + 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 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, this.getParsed(i).r); + 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); + } + } +} + +class ScatterController extends DatasetController { + static id = 'scatter'; + static defaults = { + datasetElementType: false, + dataElementType: 'point', + showLine: false, + fill: false + }; + static overrides = { + interaction: { + mode: 'point' + }, + scales: { + x: { + type: 'linear' + }, + y: { + type: 'linear' + } + } + }; + getLabelAndValue(index) { + const meta = this._cachedMeta; + const labels = this.chart.data.labels || []; + const { xScale , yScale } = meta; + const parsed = this.getParsed(index); + const x = xScale.getLabelForValue(parsed.x); + const y = yScale.getLabelForValue(parsed.y); + return { + label: labels[index] || '', + value: '(' + x + ', ' + y + ')' + }; + } + update(mode) { + const meta = this._cachedMeta; + const { data: points = [] } = 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; + } + if (this.options.showLine) { + if (!this.datasetElementType) { + this.addElements(); + } + const { dataset: line , _dataset } = meta; + line._chart = this.chart; + line._datasetIndex = this.index; + line._decimated = !!_dataset._decimated; + line.points = points; + const options = this.resolveDatasetElementOptions(mode); + options.segment = this.options.segment; + this.updateElement(line, undefined, { + animated: !animationsDisabled, + options + }, mode); + } else if (this.datasetElementType) { + delete meta.dataset; + this.datasetElementType = false; + } + this.updateElements(points, start, count, mode); + } + addElements() { + const { showLine } = this.options; + if (!this.datasetElementType && showLine) { + this.datasetElementType = this.chart.registry.getElement('line'); + } + super.addElements(); + } + 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 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength; + if (segment) { + properties.parsed = parsed; + properties.raw = _dataset.data[i]; + } + if (includeOptions) { + properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode); + } + if (!directUpdate) { + this.updateElement(point, i, properties, mode); + } + prevParsed = parsed; + } + this.updateSharedOptions(sharedOptions, mode, firstOpts); + } + getMaxOverflow() { + const meta = this._cachedMeta; + const data = meta.data || []; + if (!this.options.showLine) { + 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; + } + const dataset = meta.dataset; + const border = dataset.options && dataset.options.borderWidth || 0; + 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; + } +} + +var controllers = /*#__PURE__*/Object.freeze({ +__proto__: null, +BarController: BarController, +BubbleController: BubbleController, +DoughnutController: DoughnutController, +LineController: LineController, +PieController: PieController, +PolarAreaController: PolarAreaController, +RadarController: RadarController, +ScatterController: ScatterController +}); + +/** + * @namespace Chart._adapters + * @since 2.8.0 + * @private + */ function abstract() { + throw new Error('This method is not implemented: Check that a complete date adapter is provided.'); +} +/** + * Date adapter (current used by the time scale) + * @namespace Chart._adapters._date + * @memberof Chart._adapters + * @private + */ class DateAdapterBase { + /** + * Override default date adapter methods. + * Accepts type parameter to define options type. + * @example + * Chart._adapters._date.override<{myAdapterOption: string}>({ + * init() { + * console.log(this.options.myAdapterOption); + * } + * }) + */ static override(members) { + Object.assign(DateAdapterBase.prototype, members); + } + options; + constructor(options){ + this.options = options || {}; + } + // eslint-disable-next-line @typescript-eslint/no-empty-function + init() {} + formats() { + return abstract(); + } + parse() { + return abstract(); + } + format() { + return abstract(); + } + add() { + return abstract(); + } + diff() { + return abstract(); + } + startOf() { + return abstract(); + } + endOf() { + return abstract(); + } +} +var adapters = { + _date: DateAdapterBase +}; + +function binarySearch(metaset, axis, value, intersect) { + const { controller , data , _sorted } = metaset; + const iScale = controller._cachedMeta.iScale; + const spanGaps = metaset.dataset ? metaset.dataset.options ? metaset.dataset.options.spanGaps : null : null; + if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) { + const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey; + if (!intersect) { + const result = lookupMethod(data, axis, value); + if (spanGaps) { + const { vScale } = controller._cachedMeta; + const { _parsed } = metaset; + const distanceToDefinedLo = _parsed.slice(0, result.lo + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis])); + result.lo -= Math.max(0, distanceToDefinedLo); + const distanceToDefinedHi = _parsed.slice(result.hi).findIndex((point)=>!isNullOrUndef(point[vScale.axis])); + result.hi += Math.max(0, distanceToDefinedHi); + } + return result; + } else if (controller._sharedOptions) { + const el = data[0]; + const range = typeof el.getRange === 'function' && el.getRange(axis); + if (range) { + const start = lookupMethod(data, axis, value - range); + const end = lookupMethod(data, axis, value + range); + return { + lo: start.lo, + hi: end.hi + }; + } + } + } + return { + lo: 0, + hi: data.length - 1 + }; +} + function evaluateInteractionItems(chart, axis, position, handler, intersect) { + const metasets = chart.getSortedVisibleDatasetMetas(); + const value = position[axis]; + for(let i = 0, ilen = metasets.length; i < ilen; ++i){ + const { index , 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, includeInvisible) { + const items = []; + if (!includeInvisible && !chart.isPointInArea(position)) { + return items; + } + const evaluationFunc = function(element, datasetIndex, index) { + if (!includeInvisible && !_isPointInArea(element, chart.chartArea, 0)) { + return; + } + if (element.inRange(position.x, position.y, useFinalPosition)) { + items.push({ + element, + datasetIndex, + index + }); + } + }; + evaluateInteractionItems(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 + }); + } + } + evaluateInteractionItems(chart, axis, position, evaluationFunc); + return items; +} + function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) { + let items = []; + const distanceMetric = getDistanceMetricForAxis(axis); + let minDistance = Number.POSITIVE_INFINITY; + function evaluationFunc(element, datasetIndex, index) { + const inRange = element.inRange(position.x, position.y, useFinalPosition); + if (intersect && !inRange) { + return; + } + const center = element.getCenterPoint(useFinalPosition); + const pointInArea = !!includeInvisible || chart.isPointInArea(center); + if (!pointInArea && !inRange) { + return; + } + const distance = distanceMetric(position, center); + if (distance < minDistance) { + items = [ + { + element, + datasetIndex, + index + } + ]; + minDistance = distance; + } else if (distance === minDistance) { + items.push({ + element, + datasetIndex, + index + }); + } + } + evaluateInteractionItems(chart, axis, position, evaluationFunc); + return items; +} + function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) { + if (!includeInvisible && !chart.isPointInArea(position)) { + return []; + } + return axis === 'r' && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible); +} + function getAxisItems(chart, position, axis, intersect, useFinalPosition) { + const items = []; + const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange'; + let intersectsItem = false; + evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index)=>{ + if (element[rangeMethod] && element[rangeMethod](position[axis], useFinalPosition)) { + items.push({ + element, + datasetIndex, + index + }); + intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition); + } + }); + if (intersect && !intersectsItem) { + return []; + } + return items; +} + var Interaction = { + evaluateInteractionItems, + modes: { + index (chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'x'; + const includeInvisible = options.includeInvisible || false; + const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible); + const elements = []; + if (!items.length) { + return []; + } + chart.getSortedVisibleDatasetMetas().forEach((meta)=>{ + const 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'; + const includeInvisible = options.includeInvisible || false; + let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible); + if (items.length > 0) { + const datasetIndex = items[0].datasetIndex; + const data = chart.getDatasetMeta(datasetIndex).data; + items = []; + for(let i = 0; i < data.length; ++i){ + items.push({ + element: data[i], + datasetIndex, + index: i + }); + } + } + return items; + }, + point (chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'xy'; + const includeInvisible = options.includeInvisible || false; + return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible); + }, + nearest (chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + const axis = options.axis || 'xy'; + const includeInvisible = options.includeInvisible || false; + return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible); + }, + x (chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + return getAxisItems(chart, position, 'x', options.intersect, useFinalPosition); + }, + y (chart, e, options, useFinalPosition) { + const position = getRelativePosition(e, chart); + return getAxisItems(chart, position, 'y', options.intersect, useFinalPosition); + } + } +}; + +const STATIC_POSITIONS = [ + 'left', + 'top', + 'right', + 'bottom' +]; +function filterByPosition(array, position) { + return array.filter((v)=>v.pos === position); +} +function filterDynamicPositionByAxis(array, axis) { + return array.filter((v)=>STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis); +} +function sortByWeight(array, reverse) { + return array.sort((a, b)=>{ + const v0 = reverse ? b : a; + const v1 = reverse ? a : b; + return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight; + }); +} +function wrapBoxes(boxes) { + const layoutBoxes = []; + let i, ilen, box, pos, stack, stackWeight; + for(i = 0, ilen = (boxes || []).length; i < ilen; ++i){ + box = boxes[i]; + ({ position: pos , options: { stack , stackWeight =1 } } = box); + layoutBoxes.push({ + index: i, + box, + pos, + horizontal: box.isHorizontal(), + weight: box.weight, + stack: stack && pos + stack, + stackWeight + }); + } + return layoutBoxes; +} +function buildStacks(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; +} +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) { + if (node) { + node.addEventListener(type, listener, eventListenerOptions); + } +} +function removeListener(chart, type, listener) { + if (chart && chart.canvas) { + chart.canvas.removeEventListener(type, listener, eventListenerOptions); + } +} +function fromNativeEvent(event, chart) { + const type = EVENT_TYPES[event.type] || event.type; + const { x , y } = getRelativePosition(event, chart); + return { + type, + chart, + native: event, + x: x !== 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); + 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 = canvas && _getParentNode(canvas); + return !!(container && container.isConnected); + } +} + +function _detectPlatform(canvas) { + if (!_isDomSupported() || typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) { + return BasicPlatform; + } + return DomPlatform; +} + +class Element { + static defaults = {}; + static defaultRoutes = undefined; + x; + y; + active = false; + options; + $animations; + tooltipPosition(useFinalPosition) { + const { x , y } = this.getProps([ + 'x', + 'y' + ], useFinalPosition); + return { + x, + y + }; + } + hasValue() { + return isNumber(this.x) && isNumber(this.y); + } + getProps(props, final) { + const anims = this.$animations; + if (!final || !anims) { + // let's not create an object, if not needed + return this; + } + const ret = {}; + props.forEach((prop)=>{ + ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop]; + }); + return ret; + } +} + +function autoSkip(scale, ticks) { + const tickOpts = scale.options.ticks; + const determinedMaxTicks = determineMaxTicks(scale); + const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks); + const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : []; + const numMajorIndices = majorIndices.length; + const first = majorIndices[0]; + const last = majorIndices[numMajorIndices - 1]; + const newTicks = []; + if (numMajorIndices > ticksLimit) { + skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit); + return newTicks; + } + const spacing = calculateSpacing(majorIndices, ticks, ticksLimit); + if (numMajorIndices > 0) { + let i, ilen; + const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null; + skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first); + for(i = 0, ilen = numMajorIndices - 1; i < ilen; i++){ + skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]); + } + skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing); + return newTicks; + } + skip(ticks, newTicks, spacing); + return newTicks; +} +function determineMaxTicks(scale) { + const offset = scale.options.offset; + const tickLength = scale._tickSize(); + const maxScale = scale._length / tickLength + (offset ? 0 : 1); + const maxChart = scale._maxLength / tickLength; + return Math.floor(Math.min(maxScale, maxChart)); +} + function calculateSpacing(majorIndices, ticks, ticksLimit) { + const evenMajorSpacing = getEvenSpacing(majorIndices); + const spacing = ticks.length / ticksLimit; + if (!evenMajorSpacing) { + return Math.max(spacing, 1); + } + const factors = _factorize(evenMajorSpacing); + for(let i = 0, ilen = factors.length - 1; i < ilen; i++){ + const factor = factors[i]; + if (factor > spacing) { + return factor; + } + } + return Math.max(spacing, 1); +} + function getMajorIndices(ticks) { + const result = []; + let i, ilen; + for(i = 0, ilen = ticks.length; i < ilen; i++){ + if (ticks[i].major) { + result.push(i); + } + } + return result; +} + function skipMajors(ticks, newTicks, majorIndices, spacing) { + let count = 0; + let next = majorIndices[0]; + let i; + spacing = Math.ceil(spacing); + for(i = 0; i < ticks.length; i++){ + if (i === next) { + newTicks.push(ticks[i]); + count++; + next = majorIndices[count * spacing]; + } + } +} + function skip(ticks, newTicks, spacing, majorStart, majorEnd) { + const start = valueOrDefault(majorStart, 0); + const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length); + let count = 0; + let length, i, next; + spacing = Math.ceil(spacing); + if (majorEnd) { + length = majorEnd - majorStart; + spacing = length / Math.floor(length / spacing); + } + next = start; + while(next < 0){ + count++; + next = Math.round(start + count * spacing); + } + for(i = Math.max(start, 0); i < end; i++){ + if (i === next) { + newTicks.push(ticks[i]); + count++; + next = Math.round(start + count * spacing); + } + } +} + function getEvenSpacing(arr) { + const len = arr.length; + let i, diff; + if (len < 2) { + return false; + } + for(diff = arr[0], i = 1; i < len; ++i){ + if (arr[i] - arr[i - 1] !== diff) { + return false; + } + } + return diff; +} + +const reverseAlign = (align)=>align === 'left' ? 'right' : align === 'right' ? 'left' : align; +const offsetFromEdge = (scale, edge, offset)=>edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset; +const getTicksLimit = (ticksLength, maxTicksLimit)=>Math.min(maxTicksLimit || ticksLength, ticksLength); + function sample(arr, numItems) { + const result = []; + const increment = arr.length / numItems; + const len = arr.length; + let i = 0; + for(; i < len; i += increment){ + result.push(arr[Math.floor(i)]); + } + return result; +} + function getPixelForGridLine(scale, 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 || []; + } + getLabelItems(chartArea = this.chart.chartArea) { + const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea)); + return items; + } + beforeLayout() { + this._cache = {}; + this._dataLimitsCached = false; + } + beforeUpdate() { + callback(this.options.beforeUpdate, [ + this + ]); + } + update(maxWidth, maxHeight, margins) { + const { beginAtZero , grace , ticks: tickOpts } = this.options; + const sampleSize = tickOpts.sampleSize; + this.beforeUpdate(); + this.maxWidth = maxWidth; + this.maxHeight = maxHeight; + this._margins = margins = Object.assign({ + left: 0, + right: 0, + top: 0, + bottom: 0 + }, margins); + this.ticks = null; + this._labelSizes = null; + this._gridLineItems = null; + this._labelItems = null; + this.beforeSetDimensions(); + this.setDimensions(); + this.afterSetDimensions(); + this._maxLength = this.isHorizontal() ? this.width + margins.left + margins.right : this.height + margins.top + margins.bottom; + if (!this._dataLimitsCached) { + this.beforeDataLimits(); + this.determineDataLimits(); + this.afterDataLimits(); + this._range = _addGrace(this, grace, beginAtZero); + this._dataLimitsCached = true; + } + this.beforeBuildTicks(); + this.ticks = this.buildTicks() || []; + this.afterBuildTicks(); + const samplingEnabled = sampleSize < this.ticks.length; + this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks); + this.configure(); + this.beforeCalculateLabelRotation(); + this.calculateLabelRotation(); + this.afterCalculateLabelRotation(); + if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) { + this.ticks = autoSkip(this, this.ticks); + this._labelSizes = null; + this.afterAutoSkip(); + } + if (samplingEnabled) { + this._convertTicksToLabels(this.ticks); + } + this.beforeFit(); + this.fit(); + this.afterFit(); + this.afterUpdate(); + } + configure() { + let reversePixels = this.options.reverse; + let startPixel, endPixel; + if (this.isHorizontal()) { + startPixel = this.left; + endPixel = this.right; + } else { + startPixel = this.top; + endPixel = this.bottom; + reversePixels = !reversePixels; + } + this._startPixel = startPixel; + this._endPixel = endPixel; + this._reversePixels = reversePixels; + this._length = endPixel - startPixel; + this._alignToPixels = this.options.alignToPixels; + } + afterUpdate() { + callback(this.options.afterUpdate, [ + this + ]); + } + beforeSetDimensions() { + callback(this.options.beforeSetDimensions, [ + this + ]); + } + setDimensions() { + if (this.isHorizontal()) { + this.width = this.maxWidth; + this.left = 0; + this.right = this.width; + } else { + this.height = this.maxHeight; + this.top = 0; + this.bottom = this.height; + } + this.paddingLeft = 0; + this.paddingTop = 0; + this.paddingRight = 0; + this.paddingBottom = 0; + } + afterSetDimensions() { + callback(this.options.afterSetDimensions, [ + this + ]); + } + _callHooks(name) { + this.chart.notifyPlugins(name, this.getContext()); + callback(this.options[name], [ + this + ]); + } + beforeDataLimits() { + this._callHooks('beforeDataLimits'); + } + determineDataLimits() {} + afterDataLimits() { + this._callHooks('afterDataLimits'); + } + beforeBuildTicks() { + this._callHooks('beforeBuildTicks'); + } + buildTicks() { + return []; + } + afterBuildTicks() { + this._callHooks('afterBuildTicks'); + } + beforeTickToLabelConversion() { + callback(this.options.beforeTickToLabelConversion, [ + this + ]); + } + generateTickLabels(ticks) { + const tickOpts = this.options.ticks; + let i, ilen, tick; + for(i = 0, ilen = ticks.length; i < ilen; i++){ + tick = ticks[i]; + tick.label = callback(tickOpts.callback, [ + tick.value, + i, + ticks + ], this); + } + } + afterTickToLabelConversion() { + callback(this.options.afterTickToLabelConversion, [ + this + ]); + } + beforeCalculateLabelRotation() { + callback(this.options.beforeCalculateLabelRotation, [ + this + ]); + } + calculateLabelRotation() { + const options = this.options; + const tickOpts = options.ticks; + const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit); + const minRotation = tickOpts.minRotation || 0; + const maxRotation = tickOpts.maxRotation; + let labelRotation = minRotation; + let tickWidth, maxHeight, maxLabelDiagonal; + if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) { + this.labelRotation = minRotation; + return; + } + const labelSizes = this._getLabelSizes(); + const maxLabelWidth = labelSizes.widest.width; + const maxLabelHeight = labelSizes.highest.height; + const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth); + tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1); + if (maxLabelWidth + 6 > tickWidth) { + tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1)); + maxHeight = this.maxHeight - getTickMarkLength(options.grid) - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font); + maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight); + labelRotation = toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)), Math.asin(_limitValue(maxHeight / maxLabelDiagonal, -1, 1)) - Math.asin(_limitValue(maxLabelHeight / maxLabelDiagonal, -1, 1)))); + labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation)); + } + this.labelRotation = labelRotation; + } + afterCalculateLabelRotation() { + callback(this.options.afterCalculateLabelRotation, [ + this + ]); + } + afterAutoSkip() {} + beforeFit() { + callback(this.options.beforeFit, [ + this + ]); + } + fit() { + const minSize = { + width: 0, + height: 0 + }; + const { chart , options: { ticks: tickOpts , title: titleOpts , grid: gridOpts } } = this; + const display = this._isVisible(); + const isHorizontal = this.isHorizontal(); + if (display) { + const titleHeight = getTitleHeight(titleOpts, chart.options.font); + if (isHorizontal) { + minSize.width = this.maxWidth; + minSize.height = getTickMarkLength(gridOpts) + titleHeight; + } else { + minSize.height = this.maxHeight; + minSize.width = getTickMarkLength(gridOpts) + titleHeight; + } + if (tickOpts.display && this.ticks.length) { + const { first , last , widest , highest } = this._getLabelSizes(); + const tickPadding = tickOpts.padding * 2; + const angleRadians = toRadians(this.labelRotation); + const cos = Math.cos(angleRadians); + const sin = Math.sin(angleRadians); + if (isHorizontal) { + const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height; + minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding); + } else { + const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height; + minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding); + } + this._calculatePadding(first, last, sin, cos); + } + } + this._handleMargins(); + if (isHorizontal) { + this.width = this._length = chart.width - this._margins.left - this._margins.right; + this.height = minSize.height; + } else { + this.width = minSize.width; + this.height = this._length = chart.height - this._margins.top - this._margins.bottom; + } + } + _calculatePadding(first, last, sin, cos) { + const { ticks: { align , padding } , position } = this.options; + const isRotated = this.labelRotation !== 0; + const labelsBelowTicks = position !== 'top' && this.axis === 'x'; + if (this.isHorizontal()) { + const offsetLeft = this.getPixelForTick(0) - this.left; + const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1); + let paddingLeft = 0; + let paddingRight = 0; + if (isRotated) { + if (labelsBelowTicks) { + paddingLeft = cos * first.width; + paddingRight = sin * last.height; + } else { + paddingLeft = sin * first.height; + paddingRight = cos * last.width; + } + } else if (align === 'start') { + paddingRight = last.width; + } else if (align === 'end') { + paddingLeft = first.width; + } else if (align !== 'inner') { + paddingLeft = first.width / 2; + paddingRight = last.width / 2; + } + this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0); + this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0); + } else { + let paddingTop = last.height / 2; + let paddingBottom = first.height / 2; + if (align === 'start') { + paddingTop = 0; + paddingBottom = first.height; + } else if (align === 'end') { + paddingTop = last.height; + paddingBottom = 0; + } + this.paddingTop = paddingTop + padding; + this.paddingBottom = paddingBottom + padding; + } + } + _handleMargins() { + if (this._margins) { + this._margins.left = Math.max(this.paddingLeft, this._margins.left); + this._margins.top = Math.max(this.paddingTop, this._margins.top); + this._margins.right = Math.max(this.paddingRight, this._margins.right); + this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom); + } + } + afterFit() { + callback(this.options.afterFit, [ + this + ]); + } + isHorizontal() { + const { axis , position } = this.options; + return position === 'top' || position === 'bottom' || axis === 'x'; + } + isFullSize() { + return this.options.fullSize; + } + _convertTicksToLabels(ticks) { + this.beforeTickToLabelConversion(); + this.generateTickLabels(ticks); + let i, ilen; + for(i = 0, ilen = ticks.length; i < ilen; i++){ + if (isNullOrUndef(ticks[i].label)) { + ticks.splice(i, 1); + ilen--; + i--; + } + } + this.afterTickToLabelConversion(); + } + _getLabelSizes() { + let labelSizes = this._labelSizes; + if (!labelSizes) { + const sampleSize = this.options.ticks.sampleSize; + let ticks = this.ticks; + if (sampleSize < ticks.length) { + ticks = sample(ticks, sampleSize); + } + this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit); + } + return labelSizes; + } + _computeLabelSizes(ticks, length, maxTicksLimit) { + const { ctx , _longestTextCache: caches } = this; + const widths = []; + const heights = []; + const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit)); + let widestLabelSize = 0; + let highestLabelSize = 0; + let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel; + for(i = 0; i < length; i += increment){ + label = ticks[i].label; + tickFont = this._resolveTickFontOptions(i); + ctx.font = fontString = tickFont.string; + cache = caches[fontString] = caches[fontString] || { + data: {}, + gc: [] + }; + lineHeight = tickFont.lineHeight; + width = height = 0; + if (!isNullOrUndef(label) && !isArray(label)) { + width = _measureText(ctx, cache.data, cache.gc, width, label); + height = lineHeight; + } else if (isArray(label)) { + for(j = 0, jlen = label.length; j < jlen; ++j){ + nestedLabel = label[j]; + if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) { + width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel); + height += lineHeight; + } + } + } + widths.push(width); + heights.push(height); + widestLabelSize = Math.max(width, widestLabelSize); + highestLabelSize = Math.max(height, highestLabelSize); + } + garbageCollect(caches, length); + const widest = widths.indexOf(widestLabelSize); + const highest = heights.indexOf(highestLabelSize); + const valueAt = (idx)=>({ + width: widths[idx] || 0, + height: heights[idx] || 0 + }); + return { + first: valueAt(0), + last: valueAt(length - 1), + widest: valueAt(widest), + highest: valueAt(highest), + widths, + heights + }; + } + getLabelForValue(value) { + return value; + } + getPixelForValue(value, 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 , border } = options; + const offset = grid.offset; + const isHorizontal = this.isHorizontal(); + const ticks = this.ticks; + const ticksLength = ticks.length + (offset ? 1 : 0); + const tl = getTickMarkLength(grid); + const items = []; + const borderOpts = border.setContext(this.getContext()); + const axisWidth = borderOpts.display ? borderOpts.width : 0; + const axisHalfWidth = axisWidth / 2; + const alignBorderValue = function(pixel) { + return _alignPixel(chart, pixel, axisWidth); + }; + let borderValue, i, lineValue, alignedLineValue; + let tx1, ty1, tx2, ty2, x1, y1, x2, y2; + if (position === 'top') { + borderValue = alignBorderValue(this.bottom); + ty1 = this.bottom - tl; + ty2 = borderValue - axisHalfWidth; + y1 = alignBorderValue(chartArea.top) + axisHalfWidth; + y2 = chartArea.bottom; + } else if (position === 'bottom') { + borderValue = alignBorderValue(this.top); + y1 = chartArea.top; + y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth; + ty1 = borderValue + axisHalfWidth; + ty2 = this.top + tl; + } else if (position === 'left') { + borderValue = alignBorderValue(this.right); + tx1 = this.right - tl; + tx2 = borderValue - axisHalfWidth; + x1 = alignBorderValue(chartArea.left) + axisHalfWidth; + x2 = chartArea.right; + } else if (position === 'right') { + borderValue = alignBorderValue(this.left); + x1 = chartArea.left; + x2 = alignBorderValue(chartArea.right) - axisHalfWidth; + tx1 = borderValue + axisHalfWidth; + tx2 = this.left + tl; + } else if (axis === 'x') { + if (position === 'center') { + borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5); + } else if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const value = position[positionAxisID]; + borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value)); + } + y1 = chartArea.top; + y2 = chartArea.bottom; + ty1 = borderValue + axisHalfWidth; + ty2 = ty1 + tl; + } else if (axis === 'y') { + if (position === 'center') { + borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2); + } else if (isObject(position)) { + const positionAxisID = Object.keys(position)[0]; + const value = position[positionAxisID]; + borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value)); + } + tx1 = borderValue - axisHalfWidth; + tx2 = tx1 - tl; + x1 = chartArea.left; + x2 = chartArea.right; + } + const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength); + const step = Math.max(1, Math.ceil(ticksLength / limit)); + for(i = 0; i < ticksLength; i += step){ + const context = this.getContext(i); + const optsAtIndex = grid.setContext(context); + const optsAtIndexBorder = border.setContext(context); + const lineWidth = optsAtIndex.lineWidth; + const lineColor = optsAtIndex.color; + const borderDash = optsAtIndexBorder.dash || []; + const borderDashOffset = optsAtIndexBorder.dashOffset; + const tickWidth = optsAtIndex.tickWidth; + const tickColor = optsAtIndex.tickColor; + const tickBorderDash = optsAtIndex.tickBorderDash || []; + const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset; + lineValue = getPixelForGridLine(this, i, offset); + if (lineValue === 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; + let tickTextAlign = textAlign; + if (isHorizontal) { + x = pixel; + if (textAlign === 'inner') { + if (i === ilen - 1) { + tickTextAlign = !this.options.reverse ? 'right' : 'left'; + } else if (i === 0) { + tickTextAlign = !this.options.reverse ? 'left' : 'right'; + } else { + tickTextAlign = 'center'; + } + } + if (position === 'top') { + if (crossAlign === 'near' || rotation !== 0) { + textOffset = -lineCount * lineHeight + lineHeight / 2; + } else if (crossAlign === 'center') { + textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight; + } else { + textOffset = -labelSizes.highest.height + lineHeight / 2; + } + } else { + if (crossAlign === 'near' || rotation !== 0) { + textOffset = lineHeight / 2; + } else if (crossAlign === 'center') { + textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight; + } else { + textOffset = labelSizes.highest.height - lineCount * lineHeight; + } + } + if (mirror) { + textOffset *= -1; + } + if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) { + x += lineHeight / 2 * Math.sin(rotation); + } + } else { + y = pixel; + textOffset = (1 - lineCount) * lineHeight / 2; + } + let backdrop; + if (optsAtIndex.showLabelBackdrop) { + const labelPadding = toPadding(optsAtIndex.backdropPadding); + const height = labelSizes.heights[i]; + const width = labelSizes.widths[i]; + let top = textOffset - labelPadding.top; + let left = 0 - labelPadding.left; + switch(textBaseline){ + case 'middle': + top -= height / 2; + break; + case 'bottom': + top -= height; + break; + } + switch(textAlign){ + case 'center': + left -= width / 2; + break; + case 'right': + left -= width; + break; + case 'inner': + if (i === ilen - 1) { + left -= width; + } else if (i > 0) { + left -= width / 2; + } + break; + } + backdrop = { + left, + top, + width: width + labelPadding.width, + height: height + labelPadding.height, + color: optsAtIndex.backdropColor + }; + } + items.push({ + label, + font, + textOffset, + options: { + rotation, + color, + strokeColor, + strokeWidth, + textAlign: tickTextAlign, + textBaseline, + translation: [ + x, + y + ], + backdrop + } + }); + } + return items; + } + _getXAxisLabelAlignment() { + const { position , ticks } = this.options; + const rotation = -toRadians(this.labelRotation); + if (rotation) { + return position === 'top' ? 'left' : 'right'; + } + let align = 'center'; + if (ticks.align === 'start') { + align = 'left'; + } else if (ticks.align === 'end') { + align = 'right'; + } else if (ticks.align === 'inner') { + align = 'inner'; + } + return align; + } + _getYAxisLabelAlignment(tl) { + const { position , ticks: { crossAlign , mirror , padding } } = this.options; + const labelSizes = this._getLabelSizes(); + const tickAndPadding = tl + padding; + const widest = labelSizes.widest.width; + let textAlign; + let x; + if (position === 'left') { + if (mirror) { + x = this.right + padding; + if (crossAlign === 'near') { + textAlign = 'left'; + } else if (crossAlign === 'center') { + textAlign = 'center'; + x += widest / 2; + } else { + textAlign = 'right'; + x += widest; + } + } else { + x = this.right - tickAndPadding; + if (crossAlign === 'near') { + textAlign = 'right'; + } else if (crossAlign === 'center') { + textAlign = 'center'; + x -= widest / 2; + } else { + textAlign = 'left'; + x = this.left; + } + } + } else if (position === 'right') { + if (mirror) { + x = this.left + padding; + if (crossAlign === 'near') { + textAlign = 'right'; + } else if (crossAlign === 'center') { + textAlign = 'center'; + x -= widest / 2; + } else { + textAlign = 'left'; + x -= widest; + } + } else { + x = this.left + tickAndPadding; + if (crossAlign === 'near') { + textAlign = 'left'; + } else if (crossAlign === 'center') { + textAlign = 'center'; + x += widest / 2; + } else { + textAlign = 'right'; + x = this.right; + } + } + } else { + textAlign = 'right'; + } + return { + textAlign, + x + }; + } + _computeLabelArea() { + if (this.options.ticks.mirror) { + return; + } + const chart = this.chart; + const position = this.options.position; + if (position === 'left' || position === 'right') { + return { + top: 0, + left: this.left, + bottom: chart.height, + right: this.right + }; + } + if (position === 'top' || position === 'bottom') { + return { + top: this.top, + left: 0, + bottom: this.bottom, + right: chart.width + }; + } + } + drawBackground() { + const { ctx , options: { backgroundColor } , left , top , width , height } = this; + if (backgroundColor) { + ctx.save(); + ctx.fillStyle = backgroundColor; + ctx.fillRect(left, top, width, height); + ctx.restore(); + } + } + getLineWidthForValue(value) { + const grid = this.options.grid; + if (!this._isVisible() || !grid.display) { + return 0; + } + const ticks = this.ticks; + const 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: { border , grid } } = this; + const borderOpts = border.setContext(this.getContext()); + const axisWidth = border.display ? borderOpts.width : 0; + if (!axisWidth) { + return; + } + const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth; + const borderValue = this._borderValue; + let x1, x2, y1, y2; + if (this.isHorizontal()) { + x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2; + x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2; + y1 = y2 = borderValue; + } else { + y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2; + y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2; + x1 = x2 = borderValue; + } + ctx.save(); + ctx.lineWidth = borderOpts.width; + ctx.strokeStyle = borderOpts.color; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + ctx.restore(); + } + drawLabels(chartArea) { + const optionTicks = this.options.ticks; + if (!optionTicks.display) { + return; + } + const ctx = this.ctx; + const area = this._computeLabelArea(); + if (area) { + clipArea(ctx, area); + } + const items = this.getLabelItems(chartArea); + for (const item of items){ + const renderTextOptions = item.options; + const tickFont = item.font; + const label = item.label; + const y = item.textOffset; + renderText(ctx, label, 0, y, tickFont, renderTextOptions); + } + if (area) { + unclipArea(ctx); + } + } + drawTitle() { + const { ctx , options: { position , title , reverse } } = this; + if (!title.display) { + return; + } + const font = toFont(title.font); + const padding = toPadding(title.padding); + const align = title.align; + let offset = font.lineHeight / 2; + if (position === 'bottom' || position === 'center' || isObject(position)) { + offset += padding.bottom; + if (isArray(title.text)) { + offset += font.lineHeight * (title.text.length - 1); + } + } else { + offset += padding.top; + } + const { titleX , titleY , maxWidth , rotation } = titleArgs(this, offset, position, align); + renderText(ctx, title.text, 0, 0, font, { + color: title.color, + maxWidth, + rotation, + textAlign: titleAlign(align, position, reverse), + textBaseline: 'middle', + translation: [ + titleX, + titleY + ] + }); + } + draw(chartArea) { + if (!this._isVisible()) { + return; + } + this.drawBackground(); + this.drawGrid(chartArea); + this.drawBorder(); + this.drawTitle(); + this.drawLabels(chartArea); + } + _layers() { + const opts = this.options; + const tz = opts.ticks && opts.ticks.z || 0; + const gz = valueOrDefault(opts.grid && opts.grid.z, -1); + const bz = valueOrDefault(opts.border && opts.border.z, 0); + if (!this._isVisible() || this.draw !== Scale.prototype.draw) { + return [ + { + z: tz, + draw: (chartArea)=>{ + this.draw(chartArea); + } + } + ]; + } + return [ + { + z: gz, + draw: (chartArea)=>{ + this.drawBackground(); + this.drawGrid(chartArea); + this.drawTitle(); + } + }, + { + z: bz, + draw: ()=>{ + this.drawBorder(); + } + }, + { + z: tz, + draw: (chartArea)=>{ + this.drawLabels(chartArea); + } + } + ]; + } + getMatchingVisibleMetas(type) { + const metas = this.chart.getSortedVisibleDatasetMetas(); + const axisID = this.axis + 'AxisID'; + const result = []; + let i, ilen; + for(i = 0, ilen = metas.length; i < ilen; ++i){ + const meta = metas[i]; + if (meta[axisID] === this.id && (!type || meta.type === type)) { + result.push(meta); + } + } + return result; + } + _resolveTickFontOptions(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 = /* #__PURE__ */ new Registry(); + +class PluginService { + constructor(){ + this._init = undefined; + } + notify(chart, hook, args, filter) { + if (hook === 'beforeInit') { + this._init = this._createDescriptors(chart, true); + this._notify(this._init, chart, 'install'); + } + if (this._init === undefined) { + return; + } + 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'); + this._init = undefined; + } + 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 localIds = {}; + const plugins = []; + const keys = Object.keys(registry.plugins.items); + for(let i = 0; i < keys.length; i++){ + plugins.push(registry.getPlugin(keys[i])); + } + const local = config.plugins || []; + for(let i = 0; i < local.length; i++){ + const plugin = local[i]; + if (plugins.indexOf(plugin) === -1) { + plugins.push(plugin); + localIds[plugin.id] = true; + } + } + return { + plugins, + localIds + }; +} +function getOpts(options, all) { + if (!all && options === false) { + return null; + } + if (options === true) { + return {}; + } + return options; +} +function createDescriptors(chart, { plugins , localIds }, options, all) { + const result = []; + const context = chart.getContext(); + for (const plugin of plugins){ + const id = plugin.id; + const opts = getOpts(options[id], all); + if (opts === null) { + continue; + } + result.push({ + plugin, + options: pluginOpts(chart.config, { + plugin, + local: localIds[id] + }, opts, context) + }); + } + return result; +} +function pluginOpts(config, { plugin , local }, opts, context) { + const keys = config.pluginScopeKeys(plugin); + const scopes = config.getOptionScopes(opts, keys); + if (local && plugin.defaults) { + scopes.push(plugin.defaults); + } + return config.createResolver(scopes, context, [ + '' + ], { + scriptable: false, + indexable: false, + allKeys: true + }); +} + +function getIndexAxis(type, options) { + const datasetDefaults = defaults.datasets[type] || {}; + const datasetOptions = (options.datasets || {})[type] || {}; + return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x'; +} +function getAxisFromDefaultScaleID(id, indexAxis) { + let axis = id; + if (id === '_index_') { + axis = indexAxis; + } else if (id === '_value_') { + axis = indexAxis === 'x' ? 'y' : 'x'; + } + return axis; +} +function getDefaultScaleIDFromAxis(axis, indexAxis) { + return axis === indexAxis ? '_index_' : '_value_'; +} +function idMatchesAxis(id) { + if (id === 'x' || id === 'y' || id === 'r') { + return id; + } +} +function axisFromPosition(position) { + if (position === 'top' || position === 'bottom') { + return 'x'; + } + if (position === 'left' || position === 'right') { + return 'y'; + } +} +function determineAxis(id, ...scaleOptions) { + if (idMatchesAxis(id)) { + return id; + } + for (const opts of scaleOptions){ + const axis = opts.axis || axisFromPosition(opts.position) || id.length > 1 && idMatchesAxis(id[0].toLowerCase()); + if (axis) { + return axis; + } + } + throw new Error(`Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.`); +} +function getAxisFromDataset(id, axis, dataset) { + if (dataset[axis + 'AxisID'] === id) { + return { + axis + }; + } +} +function retrieveAxisFromDatasets(id, config) { + if (config.data && config.data.datasets) { + const boundDs = config.data.datasets.filter((d)=>d.xAxisID === id || d.yAxisID === id); + if (boundDs.length) { + return getAxisFromDataset(id, 'x', boundDs[0]) || getAxisFromDataset(id, 'y', boundDs[0]); + } + } + return {}; +} +function mergeScaleConfig(config, options) { + const chartDefaults = overrides[config.type] || { + scales: {} + }; + const configScales = options.scales || {}; + const chartIndexAxis = getIndexAxis(config.type, options); + const scales = Object.create(null); + Object.keys(configScales).forEach((id)=>{ + const scaleConf = configScales[id]; + if (!isObject(scaleConf)) { + return console.error(`Invalid scale configuration for scale: ${id}`); + } + if (scaleConf._proxy) { + return console.warn(`Ignoring resolver passed as options for scale: ${id}`); + } + const axis = determineAxis(id, scaleConf, retrieveAxisFromDatasets(id, config), defaults.scales[scaleConf.type]); + const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis); + const defaultScaleOptions = chartDefaults.scales || {}; + scales[id] = mergeIf(Object.create(null), [ + { + axis + }, + scaleConf, + defaultScaleOptions[axis], + defaultScaleOptions[defaultId] + ]); + }); + config.data.datasets.forEach((dataset)=>{ + const type = dataset.type || config.type; + const indexAxis = dataset.indexAxis || getIndexAxis(type, options); + const datasetDefaults = overrides[type] || {}; + const defaultScaleOptions = datasetDefaults.scales || {}; + Object.keys(defaultScaleOptions).forEach((defaultID)=>{ + const axis = getAxisFromDefaultScaleID(defaultID, indexAxis); + const id = dataset[axis + 'AxisID'] || axis; + scales[id] = scales[id] || 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).some((key)=>isFunction(value[key])); +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 = "4.5.1"; + +const KNOWN_POSITIONS = [ + 'top', + 'bottom', + 'left', + 'right', + 'chartArea' +]; +function positionIsHorizontal(position, axis) { + return position === 'top' || position === 'bottom' || KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x'; +} +function compare2Level(l1, l2) { + return function(a, b) { + return a[l1] === b[l1] ? a[l2] - b[l2] : a[l1] - b[l1]; + }; +} +function onAnimationsComplete(context) { + const chart = context.chart; + const animationOptions = chart.options.animation; + chart.notifyPlugins('afterRender'); + callback(animationOptions && animationOptions.onComplete, [ + context + ], chart); +} +function onAnimationProgress(context) { + const chart = context.chart; + const animationOptions = chart.options.animation; + callback(animationOptions && animationOptions.onProgress, [ + context + ], chart); +} + function getCanvas(item) { + if (_isDomSupported() && typeof item === 'string') { + item = document.getElementById(item); + } else if (item && item.length) { + item = item[0]; + } + if (item && item.canvas) { + item = item.canvas; + } + return item; +} +const instances = {}; +const getChart = (key)=>{ + const canvas = getCanvas(key); + return Object.values(instances).filter((c)=>c.canvas === canvas).pop(); +}; +function moveNumericKeys(obj, start, move) { + const keys = Object.keys(obj); + for (const key of keys){ + const intKey = +key; + if (intKey >= start) { + const value = obj[key]; + delete obj[key]; + if (move > 0 || intKey > start) { + obj[intKey + move] = value; + } + } + } +} + function determineLastEvent(e, lastEvent, inChartArea, isClick) { + if (!inChartArea || e.type === 'mouseout') { + return null; + } + if (isClick) { + return lastEvent; + } + return e; +} +class Chart { + static defaults = defaults; + static instances = instances; + static overrides = overrides; + static registry = registry; + static version = version; + static getChart = getChart; + static register(...items) { + registry.add(...items); + invalidatePlugins(); + } + static unregister(...items) { + registry.remove(...items); + invalidatePlugins(); + } + constructor(item, userConfig){ + const config = this.config = new Config(userConfig); + const initialCanvas = getCanvas(item); + const existingChart = getChart(initialCanvas); + if (existingChart) { + throw new Error('Canvas is already in use. Chart with ID \'' + existingChart.id + '\'' + ' must be destroyed before the canvas with ID \'' + existingChart.canvas.id + '\' can be reused.'); + } + const options = config.createResolver(config.chartOptionScopes(), this.getContext()); + this.platform = new (config.platform || _detectPlatform(initialCanvas))(); + this.platform.updateConfig(config); + const context = this.platform.acquireContext(initialCanvas, options.aspectRatio); + const canvas = context && context.canvas; + const height = canvas && canvas.height; + const width = canvas && canvas.width; + this.id = uid(); + this.ctx = context; + this.canvas = canvas; + this.width = width; + this.height = height; + this._options = options; + this._aspectRatio = this.aspectRatio; + this._layers = []; + this._metasets = []; + this._stacks = 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; + } + get registry() { + return registry; + } + _initialize() { + this.notifyPlugins('beforeInit'); + if (this.options.responsive) { + this.resize(); + } else { + retinaScale(this, this.options.devicePixelRatio); + } + this.bindEvents(); + this.notifyPlugins('afterInit'); + return this; + } + clear() { + clearCanvas(this.canvas, this.ctx); + return this; + } + stop() { + animator.stop(this); + return this; + } + resize(width, height) { + if (!animator.running(this)) { + this._resize(width, height); + } else { + this._resizeBeforeDraw = { + width, + height + }; + } + } + _resize(width, height) { + const options = this.options; + const canvas = this.canvas; + const aspectRatio = options.maintainAspectRatio && this.aspectRatio; + const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio); + const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio(); + const mode = this.width ? 'resize' : 'attach'; + this.width = newSize.width; + this.height = newSize.height; + this._aspectRatio = this.aspectRatio; + if (!retinaScale(this, newRatio, true)) { + return; + } + this.notifyPlugins('resize', { + size: newSize + }); + callback(options.onResize, [ + this, + newSize + ], this); + if (this.attached) { + if (this._doResize(mode)) { + this.render(); + } + } + } + ensureScalesHaveIDs() { + const options = this.options; + const scalesOptions = options.scales || {}; + each(scalesOptions, (axisOptions, axisID)=>{ + axisOptions.id = axisID; + }); + } + buildOrUpdateScales() { + const options = this.options; + const scaleOpts = options.scales; + const scales = this.scales; + const updated = Object.keys(scales).reduce((obj, id)=>{ + obj[id] = false; + return obj; + }, {}); + let items = []; + if (scaleOpts) { + items = items.concat(Object.keys(scaleOpts).map((id)=>{ + const scaleOptions = scaleOpts[id]; + const axis = determineAxis(id, scaleOptions); + const isRadial = axis === 'r'; + const isHorizontal = axis === 'x'; + return { + options: scaleOptions, + dposition: isRadial ? 'chartArea' : isHorizontal ? 'bottom' : 'left', + dtype: isRadial ? 'radialLinear' : isHorizontal ? 'category' : 'linear' + }; + })); + } + each(items, (item)=>{ + const scaleOptions = item.options; + const id = scaleOptions.id; + const axis = determineAxis(id, scaleOptions); + const scaleType = valueOrDefault(scaleOptions.type, item.dtype); + if (scaleOptions.position === 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, { + 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._resizeBeforeDraw = null; + this._resize(width, height); + } + this.clear(); + if (this.width <= 0 || this.height <= 0) { + return; + } + if (this.notifyPlugins('beforeDraw', { + cancelable: true + }) === false) { + return; + } + const layers = this._layers; + for(i = 0; i < layers.length && layers[i].z <= 0; ++i){ + layers[i].draw(this.chartArea); + } + this._drawDatasets(); + for(; i < layers.length; ++i){ + layers[i].draw(this.chartArea); + } + this.notifyPlugins('afterDraw'); + } + _getSortedDatasetMetas(filterVisible) { + const metasets = this._sortedMetasets; + const result = []; + let i, ilen; + for(i = 0, ilen = metasets.length; i < ilen; ++i){ + const meta = metasets[i]; + if (!filterVisible || meta.visible) { + result.push(meta); + } + } + return result; + } + getSortedVisibleDatasetMetas() { + return this._getSortedDatasetMetas(true); + } + _drawDatasets() { + if (this.notifyPlugins('beforeDatasetsDraw', { + cancelable: true + }) === false) { + return; + } + const metasets = this.getSortedVisibleDatasetMetas(); + for(let i = metasets.length - 1; i >= 0; --i){ + this._drawDataset(metasets[i]); + } + this.notifyPlugins('afterDatasetsDraw'); + } + _drawDataset(meta) { + const ctx = this.ctx; + const args = { + meta, + index: meta.index, + cancelable: true + }; + const clip = getDatasetClipArea(this, meta); + if (this.notifyPlugins('beforeDatasetDraw', args) === false) { + return; + } + if (clip) { + clipArea(ctx, clip); + } + meta.controller.draw(); + if (clip) { + unclipArea(ctx); + } + args.cancelable = false; + this.notifyPlugins('afterDatasetDraw', args); + } + isPointInArea(point) { + return _isPointInArea(point, this.chartArea, this._minPadding); + } + getElementsAtEventForMode(e, mode, options, useFinalPosition) { + const method = Interaction.modes[mode]; + if (typeof method === 'function') { + return method(this, e, options, useFinalPosition); + } + return []; + } + getDatasetMeta(datasetIndex) { + const dataset = this.data.datasets[datasetIndex]; + const metasets = this._metasets; + let meta = metasets.filter((x)=>x && x._dataset === dataset).pop(); + if (!meta) { + meta = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, + xAxisID: null, + yAxisID: null, + order: dataset && dataset.order || 0, + index: datasetIndex, + _dataset: dataset, + _parsed: [], + _sorted: false + }; + metasets.push(meta); + } + return meta; + } + getContext() { + return this.$context || (this.$context = createContext(null, { + chart: this, + type: 'chart' + })); + } + getVisibleDatasetCount() { + return this.getSortedVisibleDatasetMetas().length; + } + isDatasetVisible(datasetIndex) { + const dataset = this.data.datasets[datasetIndex]; + if (!dataset) { + return false; + } + const meta = this.getDatasetMeta(datasetIndex); + return typeof meta.hidden === 'boolean' ? !meta.hidden : !dataset.hidden; + } + setDatasetVisibility(datasetIndex, visible) { + const meta = this.getDatasetMeta(datasetIndex); + meta.hidden = !visible; + } + toggleDataVisibility(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; + } + 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); + } + isPluginEnabled(pluginId) { + return this._plugins._cache.filter((p)=>p.plugin.id === pluginId).length === 1; + } + _updateHoverStyles(active, lastActive, replay) { + const hoverOptions = this.options.hover; + const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.datasetIndex === y.datasetIndex && x.index === y.index)); + const deactivated = diff(lastActive, active); + const activated = replay ? active : diff(active, lastActive); + if (deactivated.length) { + this.updateHoverStyle(deactivated, hoverOptions.mode, false); + } + if (activated.length && hoverOptions.mode) { + this.updateHoverStyle(activated, hoverOptions.mode, true); + } + } + _eventHandler(e, replay) { + const args = { + event: e, + replay, + cancelable: true, + inChartArea: this.isPointInArea(e) + }; + const eventFilter = (plugin)=>(plugin.options.events || this.options.events).includes(e.native.type); + if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) { + return; + } + const changed = this._handleEvent(e, replay, args.inChartArea); + args.cancelable = false; + this.notifyPlugins('afterEvent', args, eventFilter); + if (changed || args.changed) { + this.render(); + } + return this; + } + _handleEvent(e, replay, inChartArea) { + const { _active: lastActive = [] , options } = this; + const useFinalPosition = replay; + const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition); + const isClick = _isClickEvent(e); + const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick); + if (inChartArea) { + this._lastEvent = null; + callback(options.onHover, [ + e, + active, + this + ], this); + if (isClick) { + callback(options.onClick, [ + e, + active, + this + ], this); + } + } + const changed = !_elementsEqual(active, lastActive); + if (changed || replay) { + this._active = active; + this._updateHoverStyles(active, lastActive, replay); + } + this._lastEvent = lastEvent; + return changed; + } + _getActiveElements(e, lastActive, inChartArea, useFinalPosition) { + if (e.type === 'mouseout') { + return []; + } + if (!inChartArea) { + return lastActive; + } + const hoverOptions = this.options.hover; + return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition); + } +} +function invalidatePlugins() { + return each(Chart.instances, (chart)=>chart._plugins.invalidate()); +} + +function clipSelf(ctx, element, endAngle) { + const { startAngle , x , y , outerRadius , innerRadius , options } = element; + const { borderWidth , borderJoinStyle } = options; + const outerAngleClip = Math.min(borderWidth / outerRadius, _normalizeAngle(startAngle - endAngle)); + ctx.beginPath(); + ctx.arc(x, y, outerRadius - borderWidth / 2, startAngle + outerAngleClip / 2, endAngle - outerAngleClip / 2); + if (innerRadius > 0) { + const innerAngleClip = Math.min(borderWidth / innerRadius, _normalizeAngle(startAngle - endAngle)); + ctx.arc(x, y, innerRadius + borderWidth / 2, endAngle - innerAngleClip / 2, startAngle + innerAngleClip / 2, true); + } else { + const clipWidth = Math.min(borderWidth / 2, outerRadius * _normalizeAngle(startAngle - endAngle)); + if (borderJoinStyle === 'round') { + ctx.arc(x, y, clipWidth, endAngle - PI / 2, startAngle + PI / 2, true); + } else if (borderJoinStyle === 'bevel') { + const r = 2 * clipWidth * clipWidth; + const endX = -r * Math.cos(endAngle + PI / 2) + x; + const endY = -r * Math.sin(endAngle + PI / 2) + y; + const startX = r * Math.cos(startAngle + PI / 2) + x; + const startY = r * Math.sin(startAngle + PI / 2) + y; + ctx.lineTo(endX, endY); + ctx.lineTo(startX, startY); + } + } + ctx.closePath(); + ctx.moveTo(0, 0); + ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.clip('evenodd'); +} +function clipArc(ctx, element, endAngle) { + const { startAngle , pixelMargin , x , y , outerRadius , innerRadius } = element; + let angleMargin = pixelMargin / outerRadius; + // Draw an inner border by clipping the arc and drawing a double-width border + // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders + 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' + ]); +} +/** + * Parse border radius from the provided options + */ 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); + // Outer limits are complicated. We want to compute the available angular distance at + // a radius of outerRadius - borderRadius because for small angular distances, this term limits. + // We compute at r = outerRadius - borderRadius because this circle defines the center of the border corners. + // + // If the borderRadius is large, that value can become negative. + // This causes the outer borders to lose their radius entirely, which is rather unexpected. To solve that, if borderRadius > outerRadius + // we know that the thickness term will dominate and compute the limits at that point + 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) + }; +} +/** + * Convert (r, 𝜃) to (x, y) + */ function rThetaToXY(r, theta, x, y) { + return { + x: x + r * Math.cos(theta), + y: y + r * Math.sin(theta) + }; +} +/** + * Path the arc, respecting border radius by separating into left and right halves. + * + * Start End + * + * 1--->a--->2 Outer + * / \ + * 8 3 + * | | + * | | + * 7 4 + * \ / + * 6<---b<---5 Inner + */ function pathArc(ctx, element, offset, spacing, end, circular) { + 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) { + // When spacing is present, it is the same for all items + // So we adjust the start and end angle of the arc such that + // the distance is the same as it would be without the 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(); + if (circular) { + // The first arc segments from point 1 to point a to point 2 + const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2; + ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle); + ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle); + // The corner segment from point 2 to point 3 + if (outerEnd > 0) { + const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI); + } + // The line from point 3 to point 4 + const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y); + ctx.lineTo(p4.x, p4.y); + // The corner segment from point 4 to point 5 + if (innerEnd > 0) { + const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI); + } + // The inner arc from point 5 to point b to point 6 + const innerMidAdjustedAngle = (endAngle - innerEnd / innerRadius + (startAngle + innerStart / innerRadius)) / 2; + ctx.arc(x, y, innerRadius, endAngle - innerEnd / innerRadius, innerMidAdjustedAngle, true); + ctx.arc(x, y, innerRadius, innerMidAdjustedAngle, startAngle + innerStart / innerRadius, true); + // The corner segment from point 6 to point 7 + if (innerStart > 0) { + const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI); + } + // The line from point 7 to point 8 + const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y); + ctx.lineTo(p8.x, p8.y); + // The corner segment from point 8 to point 1 + if (outerStart > 0) { + const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y); + ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle); + } + } else { + ctx.moveTo(x, y); + const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x; + const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y; + ctx.lineTo(outerStartX, outerStartY); + const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x; + const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y; + ctx.lineTo(outerEndX, outerEndY); + } + ctx.closePath(); +} +function drawArc(ctx, element, offset, spacing, circular) { + const { fullCircles , startAngle , circumference } = element; + let endAngle = element.endAngle; + if (fullCircles) { + pathArc(ctx, element, offset, spacing, endAngle, circular); + for(let i = 0; i < fullCircles; ++i){ + ctx.fill(); + } + if (!isNaN(circumference)) { + endAngle = startAngle + (circumference % TAU || TAU); + } + } + pathArc(ctx, element, offset, spacing, endAngle, circular); + ctx.fill(); + return endAngle; +} +function drawBorder(ctx, element, offset, spacing, circular) { + const { fullCircles , startAngle , circumference , options } = element; + const { borderWidth , borderJoinStyle , borderDash , borderDashOffset , borderRadius } = options; + const inner = options.borderAlign === 'inner'; + if (!borderWidth) { + return; + } + ctx.setLineDash(borderDash || []); + ctx.lineDashOffset = borderDashOffset; + if (inner) { + ctx.lineWidth = borderWidth * 2; + ctx.lineJoin = borderJoinStyle || 'round'; + } else { + ctx.lineWidth = borderWidth; + ctx.lineJoin = borderJoinStyle || 'bevel'; + } + let endAngle = element.endAngle; + if (fullCircles) { + pathArc(ctx, element, offset, spacing, endAngle, circular); + for(let i = 0; i < fullCircles; ++i){ + ctx.stroke(); + } + if (!isNaN(circumference)) { + endAngle = startAngle + (circumference % TAU || TAU); + } + } + if (inner) { + clipArc(ctx, element, endAngle); + } + if (options.selfJoin && endAngle - startAngle >= PI && borderRadius === 0 && borderJoinStyle !== 'miter') { + clipSelf(ctx, element, endAngle); + } + if (!fullCircles) { + pathArc(ctx, element, offset, spacing, endAngle, circular); + ctx.stroke(); + } +} +class ArcElement extends Element { + static id = 'arc'; + static defaults = { + borderAlign: 'center', + borderColor: '#fff', + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: undefined, + borderRadius: 0, + borderWidth: 2, + offset: 0, + spacing: 0, + angle: undefined, + circular: true, + selfJoin: false + }; + static defaultRoutes = { + backgroundColor: 'backgroundColor' + }; + static descriptors = { + _scriptable: true, + _indexable: (name)=>name !== 'borderDash' + }; + circumference; + endAngle; + fullCircles; + innerRadius; + outerRadius; + pixelMargin; + startAngle; + 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 + this.options.borderWidth) / 2; + const _circumference = valueOrDefault(circumference, endAngle - startAngle); + const nonZeroBetween = _angleBetween(angle, startAngle, endAngle) && startAngle !== endAngle; + const betweenAngles = _circumference >= TAU || nonZeroBetween; + 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' + ], 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) / 4; + const spacing = (options.spacing || 0) / 2; + const circular = options.circular; + 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(); + const halfAngle = (this.startAngle + this.endAngle) / 2; + ctx.translate(Math.cos(halfAngle) * offset, Math.sin(halfAngle) * offset); + const fix = 1 - Math.sin(Math.min(PI, circumference || 0)); + const radiusOffset = offset * fix; + ctx.fillStyle = options.backgroundColor; + ctx.strokeStyle = options.borderColor; + drawArc(ctx, this, radiusOffset, spacing, circular); + drawBorder(ctx, this, radiusOffset, spacing, circular); + ctx.restore(); + } +} + +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 { + static id = 'line'; + static defaults = { + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: 'miter', + borderWidth: 3, + capBezierPoints: true, + cubicInterpolationMode: 'default', + fill: false, + spanGaps: false, + stepped: false, + tension: 0 + }; + static defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' + }; + static descriptors = { + _scriptable: true, + _indexable: (name)=>name !== 'borderDash' && name !== 'fill' + }; + 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; + } + } +} + +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 { + static id = 'point'; + parsed; + skip; + stop; + /** + * @type {any} + */ static defaults = { + borderWidth: 1, + hitRadius: 1, + hoverBorderWidth: 1, + hoverRadius: 4, + pointStyle: 'circle', + radius: 3, + rotation: 0 + }; + /** + * @type {any} + */ static defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' + }; + 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 || {}; + // @ts-expect-error Fallbacks should never be hit in practice + return options.radius + options.hitRadius; + } +} + +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 { + static id = 'bar'; + static defaults = { + borderSkipped: 'start', + borderWidth: 0, + borderRadius: 0, + inflateAmount: 'auto', + pointStyle: undefined + }; + static defaultRoutes = { + backgroundColor: 'backgroundColor', + borderColor: 'borderColor' + }; + 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; + } +} + +var elements = /*#__PURE__*/Object.freeze({ +__proto__: null, +ArcElement: ArcElement, +BarElement: BarElement, +LineElement: LineElement, +PointElement: PointElement +}); + +const BORDER_COLORS = [ + 'rgb(54, 162, 235)', + 'rgb(255, 99, 132)', + 'rgb(255, 159, 64)', + 'rgb(255, 205, 86)', + 'rgb(75, 192, 192)', + 'rgb(153, 102, 255)', + 'rgb(201, 203, 207)' // grey +]; +// Border colors with 50% transparency +const BACKGROUND_COLORS = /* #__PURE__ */ BORDER_COLORS.map((color)=>color.replace('rgb(', 'rgba(').replace(')', ', 0.5)')); +function getBorderColor(i) { + return BORDER_COLORS[i % BORDER_COLORS.length]; +} +function getBackgroundColor(i) { + return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length]; +} +function colorizeDefaultDataset(dataset, i) { + dataset.borderColor = getBorderColor(i); + dataset.backgroundColor = getBackgroundColor(i); + return ++i; +} +function colorizeDoughnutDataset(dataset, i) { + dataset.backgroundColor = dataset.data.map(()=>getBorderColor(i++)); + return i; +} +function colorizePolarAreaDataset(dataset, i) { + dataset.backgroundColor = dataset.data.map(()=>getBackgroundColor(i++)); + return i; +} +function getColorizer(chart) { + let i = 0; + return (dataset, datasetIndex)=>{ + const controller = chart.getDatasetMeta(datasetIndex).controller; + if (controller instanceof DoughnutController) { + i = colorizeDoughnutDataset(dataset, i); + } else if (controller instanceof PolarAreaController) { + i = colorizePolarAreaDataset(dataset, i); + } else if (controller) { + i = colorizeDefaultDataset(dataset, i); + } + }; +} +function containsColorsDefinitions(descriptors) { + let k; + for(k in descriptors){ + if (descriptors[k].borderColor || descriptors[k].backgroundColor) { + return true; + } + } + return false; +} +function containsColorsDefinition(descriptor) { + return descriptor && (descriptor.borderColor || descriptor.backgroundColor); +} +function containsDefaultColorsDefenitions() { + return defaults.borderColor !== 'rgba(0,0,0,0.1)' || defaults.backgroundColor !== 'rgba(0,0,0,0.1)'; +} +var plugin_colors = { + id: 'colors', + defaults: { + enabled: true, + forceOverride: false + }, + beforeLayout (chart, _args, options) { + if (!options.enabled) { + return; + } + const { data: { datasets } , options: chartOptions } = chart.config; + const { elements } = chartOptions; + const containsColorDefenition = containsColorsDefinitions(datasets) || containsColorsDefinition(chartOptions) || elements && containsColorsDefinitions(elements) || containsDefaultColorsDefenitions(); + if (!options.forceOverride && containsColorDefenition) { + return; + } + const colorizer = getColorizer(chart); + datasets.forEach(colorizer); + } +}; + +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', { + configurable: true, + enumerable: true, + writable: true, + 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.controller.supportsDecimation) { + 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 _segments(line, target, property) { + const segments = line.segments; + const points = line.points; + const tpoints = target.points; + const parts = []; + for (const segment of segments){ + let { start , end } = segment; + end = _findSegmentEnd(start, end, points); + const bounds = _getBounds(property, points[start], points[end], segment.loop); + if (!target.segments) { + parts.push({ + source: segment, + target: bounds, + start: points[start], + end: points[end] + }); + continue; + } + const targetSegments = _boundSegments(target, bounds); + for (const tgt of targetSegments){ + const subBounds = _getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop); + const fillSources = _boundSegment(segment, points, subBounds); + for (const fillSource of fillSources){ + parts.push({ + source: fillSource, + target: tgt, + start: { + [property]: _getEdge(bounds, subBounds, 'start', Math.max) + }, + end: { + [property]: _getEdge(bounds, subBounds, 'end', Math.min) + } + }); + } + } + } + return parts; +} +function _getBounds(property, first, last, loop) { + if (loop) { + return; + } + let start = first[property]; + let end = last[property]; + if (property === 'angle') { + start = _normalizeAngle(start); + end = _normalizeAngle(end); + } + return { + property, + start, + end + }; +} +function _pointsFromSegments(boundary, line) { + const { x =null , y =null } = boundary || {}; + const linePoints = line.points; + const points = []; + line.segments.forEach(({ start , end })=>{ + end = _findSegmentEnd(start, end, linePoints); + const first = linePoints[start]; + const last = linePoints[end]; + if (y !== null) { + points.push({ + x: first.x, + y + }); + points.push({ + x: last.x, + y + }); + } else if (x !== null) { + points.push({ + x, + y: first.y + }); + points.push({ + x, + y: last.y + }); + } + }); + return points; +} +function _findSegmentEnd(start, end, points) { + for(; end > start; end--){ + const point = points[end]; + if (!isNaN(point.x) && !isNaN(point.y)) { + break; + } + } + return end; +} +function _getEdge(a, b, prop, fn) { + if (a && b) { + return fn(a[prop], b[prop]); + } + return a ? a[prop] : b ? b[prop] : 0; +} + +function _createBoundaryLine(boundary, line) { + let points = []; + let _loop = false; + if (isArray(boundary)) { + _loop = true; + points = boundary; + } else { + points = _pointsFromSegments(boundary, line); + } + return points.length ? new LineElement({ + points, + options: { + tension: 0 + }, + _loop, + _fullLoop: _loop + }) : null; +} +function _shouldApplyFill(source) { + return source && source.fill !== false; +} + +function _resolveTarget(sources, 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 _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) { + return decodeTargetIndex(fill[0], index, target, count); + } + return [ + 'origin', + 'start', + 'end', + 'stack', + 'shape' + ].indexOf(fill) >= 0 && fill; +} +function decodeTargetIndex(firstCh, index, target, count) { + if (firstCh === '-' || firstCh === '+') { + target = index + target; + } + if (target === index || target < 0 || target >= count) { + return false; + } + return target; +} + function _getTargetPixel(fill, scale) { + let pixel = null; + if (fill === 'start') { + pixel = scale.bottom; + } else if (fill === 'end') { + pixel = scale.top; + } else if (isObject(fill)) { + pixel = scale.getPixelForValue(fill.value); + } else if (scale.getBasePixel) { + pixel = scale.getBasePixel(); + } + return pixel; +} + function _getTargetValue(fill, scale, startValue) { + let value; + if (fill === 'start') { + value = startValue; + } else if (fill === 'end') { + value = scale.options.reverse ? scale.min : scale.max; + } else if (isObject(fill)) { + value = fill.value; + } else { + value = scale.getBaseValue(); + } + return value; +} + 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 _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 + }; +} + +class simpleArc { + constructor(opts){ + this.x = opts.x; + this.y = opts.y; + this.radius = opts.radius; + } + pathSegment(ctx, bounds, opts) { + const { x , y , radius } = this; + bounds = bounds || { + start: 0, + end: TAU + }; + ctx.arc(x, y, radius, bounds.end, bounds.start, true); + return !opts.bounds; + } + interpolate(point) { + const { x , y , radius } = this; + const angle = point.angle; + return { + x: x + Math.cos(angle) * radius, + y: y + Math.sin(angle) * radius, + angle + }; + } +} + +function _getTarget(source) { + const { chart , fill , 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 getLineByIndex(chart, index) { + const meta = chart.getDatasetMeta(index); + const visible = meta && chart.isDatasetVisible(index); + return visible ? meta.dataset : null; +} +function computeBoundary(source) { + const scale = source.scale || {}; + if (scale.getPointPositionForValue) { + return computeCircularBoundary(source); + } + return computeLinearBoundary(source); +} +function computeLinearBoundary(source) { + const { scale ={} , fill } = source; + const pixel = _getTargetPixel(fill, scale); + if (isNumberFinite(pixel)) { + const horizontal = scale.isHorizontal(); + return { + x: horizontal ? pixel : null, + y: horizontal ? null : pixel + }; + } + return null; +} +function computeCircularBoundary(source) { + const { scale , fill } = source; + const options = scale.options; + const length = scale.getLabels().length; + const start = options.reverse ? scale.max : scale.min; + const value = _getTargetValue(fill, scale, start); + const target = []; + if (options.grid.circular) { + const center = scale.getPointPositionForValue(0, start); + return new simpleArc({ + x: center.x, + y: center.y, + radius: scale.getDistanceFromCenterForValue(value) + }); + } + for(let i = 0; i < length; ++i){ + target.push(scale.getPointPositionForValue(i, value)); + } + return target; +} + +function _drawfill(ctx, source, area) { + const target = _getTarget(source); + const { chart , index , line , scale , axis } = source; + const lineOpts = line.options; + const fillOption = lineOpts.fill; + const color = lineOpts.backgroundColor; + const { above =color , below =color } = fillOption || {}; + const meta = chart.getDatasetMeta(index); + const clip = getDatasetClipArea(chart, meta); + if (target && line.points.length) { + clipArea(ctx, area); + doFill(ctx, { + line, + target, + above, + below, + area, + scale, + axis, + clip + }); + unclipArea(ctx); + } +} +function doFill(ctx, cfg) { + const { line , target , above , below , area , scale , clip } = cfg; + const property = line._loop ? 'angle' : cfg.axis; + ctx.save(); + let fillColor = below; + if (below !== above) { + if (property === 'x') { + clipVertical(ctx, target, area.top); + fill(ctx, { + line, + target, + color: above, + scale, + property, + clip + }); + ctx.restore(); + ctx.save(); + clipVertical(ctx, target, area.bottom); + } else if (property === 'y') { + clipHorizontal(ctx, target, area.left); + fill(ctx, { + line, + target, + color: below, + scale, + property, + clip + }); + ctx.restore(); + ctx.save(); + clipHorizontal(ctx, target, area.right); + fillColor = above; + } + } + fill(ctx, { + line, + target, + color: fillColor, + scale, + property, + clip + }); + ctx.restore(); +} +function clipVertical(ctx, target, clipY) { + const { segments , points } = target; + let first = true; + let lineLoop = false; + ctx.beginPath(); + for (const segment of segments){ + const { start , end } = segment; + const firstPoint = points[start]; + const lastPoint = points[_findSegmentEnd(start, end, points)]; + if (first) { + ctx.moveTo(firstPoint.x, firstPoint.y); + first = false; + } else { + ctx.lineTo(firstPoint.x, clipY); + ctx.lineTo(firstPoint.x, firstPoint.y); + } + lineLoop = !!target.pathSegment(ctx, segment, { + move: lineLoop + }); + if (lineLoop) { + ctx.closePath(); + } else { + ctx.lineTo(lastPoint.x, clipY); + } + } + ctx.lineTo(target.first().x, clipY); + ctx.closePath(); + ctx.clip(); +} +function clipHorizontal(ctx, target, clipX) { + const { segments , points } = target; + let first = true; + let lineLoop = false; + ctx.beginPath(); + for (const segment of segments){ + const { start , end } = segment; + const firstPoint = points[start]; + const lastPoint = points[_findSegmentEnd(start, end, points)]; + if (first) { + ctx.moveTo(firstPoint.x, firstPoint.y); + first = false; + } else { + ctx.lineTo(clipX, firstPoint.y); + ctx.lineTo(firstPoint.x, firstPoint.y); + } + lineLoop = !!target.pathSegment(ctx, segment, { + move: lineLoop + }); + if (lineLoop) { + ctx.closePath(); + } else { + ctx.lineTo(clipX, lastPoint.y); + } + } + ctx.lineTo(clipX, target.first().y); + ctx.closePath(); + ctx.clip(); +} +function fill(ctx, cfg) { + const { line , target , property , color , scale , clip } = 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, clip, notShape && _getBounds(property, start, end)); + ctx.beginPath(); + const lineLoop = !!line.pathSegment(ctx, src); + let loop; + if (notShape) { + if (lineLoop) { + ctx.closePath(); + } else { + interpolatedLineTo(ctx, target, end, property); + } + const targetLoop = !!target.pathSegment(ctx, tgt, { + move: lineLoop, + reverse: true + }); + loop = lineLoop && targetLoop; + if (!loop) { + interpolatedLineTo(ctx, target, start, property); + } + } + ctx.closePath(); + ctx.fill(loop ? 'evenodd' : 'nonzero'); + ctx.restore(); + } +} +function clipBounds(ctx, scale, clip, bounds) { + const chartArea = scale.chart.chartArea; + const { property , start , end } = bounds || {}; + if (property === 'x' || property === 'y') { + let left, top, right, bottom; + if (property === 'x') { + left = start; + top = chartArea.top; + right = end; + bottom = chartArea.bottom; + } else { + left = chartArea.left; + top = start; + right = chartArea.right; + bottom = end; + } + ctx.beginPath(); + if (clip) { + left = Math.max(left, clip.left); + right = Math.min(right, clip.right); + top = Math.max(top, clip.top); + bottom = Math.min(bottom, clip.bottom); + } + ctx.rect(left, top, right - left, bottom - top); + ctx.clip(); + } +} +function interpolatedLineTo(ctx, target, point, property) { + const interpolatedPoint = target.interpolate(point, property); + if (interpolatedPoint) { + ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y); + } +} + +var index = { + id: 'filler', + afterDatasetsUpdate (chart, _args, options) { + const count = (chart.data.datasets || []).length; + const sources = []; + let meta, i, line, source; + for(i = 0; i < count; ++i){ + meta = chart.getDatasetMeta(i); + line = meta.dataset; + source = null; + if (line && line.options && line instanceof LineElement) { + source = { + visible: chart.isDatasetVisible(i), + index: i, + fill: _decodeFill(line, i, count), + chart, + axis: meta.controller.options.indexAxis, + scale: meta.vScale, + line + }; + } + meta.$filler = source; + sources.push(source); + } + for(i = 0; i < count; ++i){ + source = sources[i]; + if (!source || source.fill === false) { + continue; + } + source.fill = _resolveTarget(sources, i, options.propagate); + } + }, + beforeDraw (chart, _args, options) { + const 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 && source.fill) { + _drawfill(chart.ctx, source, area); + } + } + }, + beforeDatasetsDraw (chart, _args, options) { + if (options.drawTime !== 'beforeDatasetsDraw') { + return; + } + const metasets = chart.getSortedVisibleDatasetMetas(); + for(let i = metasets.length - 1; i >= 0; --i){ + const source = metasets[i].$filler; + if (_shouldApplyFill(source)) { + _drawfill(chart.ctx, source, chart.chartArea); + } + } + }, + beforeDatasetDraw (chart, args, options) { + const source = args.meta.$filler; + if (!_shouldApplyFill(source) || options.drawTime !== 'beforeDatasetDraw') { + return; + } + _drawfill(chart.ctx, source, chart.chartArea); + }, + defaults: { + propagate: true, + drawTime: 'beforeDatasetDraw' + } +}; + +const getBoxSize = (labelOpts, fontSize)=>{ + let { boxHeight =fontSize , boxWidth =fontSize } = labelOpts; + if (labelOpts.usePointStyle) { + boxHeight = Math.min(boxHeight, fontSize); + boxWidth = labelOpts.pointStyleWidth || Math.min(boxWidth, fontSize); + } + return { + boxWidth, + boxHeight, + itemHeight: Math.max(fontSize, boxHeight) + }; +}; +const itemsEqual = (a, b)=>a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index; +class Legend extends Element { + constructor(config){ + super(); + this._added = false; + this.legendHitBoxes = []; + this._hoveredItem = null; + this.doughnutMode = false; + this.chart = config.chart; + this.options = config.options; + this.ctx = config.ctx; + this.legendItems = 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, labelFont, boxWidth, itemHeight) + 10; + } + this.width = Math.min(width, options.maxWidth || this.maxWidth); + this.height = Math.min(height, options.maxHeight || this.maxHeight); + } + _fitRows(titleHeight, fontSize, boxWidth, itemHeight) { + const { ctx , maxWidth , options: { labels: { padding } } } = this; + const hitboxes = this.legendHitBoxes = []; + const lineWidths = this.lineWidths = [ + 0 + ]; + const lineHeight = itemHeight + padding; + let totalHeight = titleHeight; + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + let row = -1; + let top = -lineHeight; + this.legendItems.forEach((legendItem, i)=>{ + const itemWidth = boxWidth + fontSize / 2 + ctx.measureText(legendItem.text).width; + if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) { + totalHeight += lineHeight; + lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0; + top += lineHeight; + row++; + } + hitboxes[i] = { + left: 0, + top, + row, + width: itemWidth, + height: itemHeight + }; + lineWidths[lineWidths.length - 1] += itemWidth + padding; + }); + return totalHeight; + } + _fitCols(titleHeight, labelFont, boxWidth, _itemHeight) { + const { ctx , maxHeight , options: { labels: { padding } } } = this; + const hitboxes = this.legendHitBoxes = []; + const columnSizes = this.columnSizes = []; + const heightLimit = maxHeight - titleHeight; + let totalWidth = padding; + let currentColWidth = 0; + let currentColHeight = 0; + let left = 0; + let col = 0; + this.legendItems.forEach((legendItem, i)=>{ + const { itemWidth , itemHeight } = calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight); + if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) { + totalWidth += currentColWidth + padding; + columnSizes.push({ + width: currentColWidth, + height: currentColHeight + }); + left += currentColWidth + padding; + col++; + currentColWidth = currentColHeight = 0; + } + hitboxes[i] = { + left, + top: currentColHeight, + col, + width: itemWidth, + height: itemHeight + }; + currentColWidth = Math.max(currentColWidth, itemWidth); + currentColHeight += itemHeight + padding; + }); + totalWidth += currentColWidth; + columnSizes.push({ + width: currentColWidth, + height: currentColHeight + }); + return totalWidth; + } + adjustHitBoxes() { + if (!this.options.display) { + return; + } + const titleHeight = this._computeTitleHeight(); + const { legendHitBoxes: hitboxes , options: { align , labels: { padding } , rtl } } = this; + const rtlHelper = getRtlAdapter(rtl, this.left, this.width); + if (this.isHorizontal()) { + let row = 0; + let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]); + for (const hitbox of hitboxes){ + if (row !== hitbox.row) { + row = hitbox.row; + left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]); + } + hitbox.top += this.top + titleHeight + padding; + hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width); + left += hitbox.width + padding; + } + } else { + let col = 0; + let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height); + for (const hitbox of hitboxes){ + if (hitbox.col !== col) { + col = hitbox.col; + top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height); + } + hitbox.top = top; + hitbox.left += this.left + padding; + hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox.left), hitbox.width); + top += hitbox.height + padding; + } + } + } + isHorizontal() { + return this.options.position === 'top' || this.options.position === 'bottom'; + } + draw() { + if (this.options.display) { + const ctx = this.ctx; + clipArea(ctx, this); + this._draw(); + unclipArea(ctx); + } + } + _draw() { + const { options: opts , columnSizes , lineWidths , ctx } = this; + const { align , labels: labelOpts } = opts; + const defaultColor = defaults.color; + const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width); + const labelFont = toFont(labelOpts.font); + const { padding } = labelOpts; + const fontSize = labelFont.size; + const halfFontSize = fontSize / 2; + let cursor; + this.drawTitle(); + ctx.textAlign = rtlHelper.textAlign('left'); + ctx.textBaseline = 'middle'; + ctx.lineWidth = 0.5; + ctx.font = labelFont.string; + const { boxWidth , boxHeight , itemHeight } = getBoxSize(labelOpts, fontSize); + const drawLegendBox = function(x, y, legendItem) { + if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) { + return; + } + ctx.save(); + const lineWidth = valueOrDefault(legendItem.lineWidth, 1); + ctx.fillStyle = valueOrDefault(legendItem.fillStyle, defaultColor); + ctx.lineCap = valueOrDefault(legendItem.lineCap, 'butt'); + ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, 0); + ctx.lineJoin = valueOrDefault(legendItem.lineJoin, 'miter'); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, defaultColor); + ctx.setLineDash(valueOrDefault(legendItem.lineDash, [])); + if (labelOpts.usePointStyle) { + const drawOptions = { + radius: boxHeight * Math.SQRT2 / 2, + pointStyle: legendItem.pointStyle, + rotation: legendItem.rotation, + borderWidth: lineWidth + }; + const centerX = rtlHelper.xPlus(x, boxWidth / 2); + const centerY = y + halfFontSize; + drawPointLegend(ctx, drawOptions, centerX, centerY, labelOpts.pointStyleWidth && boxWidth); + } else { + const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0); + const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth); + const borderRadius = toTRBLCorners(legendItem.borderRadius); + ctx.beginPath(); + if (Object.values(borderRadius).some((v)=>v !== 0)) { + addRoundedRectPath(ctx, { + x: xBoxLeft, + y: yBoxTop, + w: boxWidth, + h: boxHeight, + radius: borderRadius + }); + } else { + ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight); + } + ctx.fill(); + if (lineWidth !== 0) { + ctx.stroke(); + } + } + ctx.restore(); + }; + const fillText = function(x, y, legendItem) { + renderText(ctx, legendItem.text, x, y + itemHeight / 2, labelFont, { + strikethrough: legendItem.hidden, + textAlign: rtlHelper.textAlign(legendItem.textAlign) + }); + }; + const isHorizontal = this.isHorizontal(); + const titleHeight = this._computeTitleHeight(); + if (isHorizontal) { + cursor = { + x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]), + y: this.top + padding + titleHeight, + line: 0 + }; + } else { + cursor = { + x: this.left + padding, + y: _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height), + line: 0 + }; + } + overrideTextDirection(this.ctx, opts.textDirection); + const lineHeight = itemHeight + padding; + this.legendItems.forEach((legendItem, i)=>{ + ctx.strokeStyle = legendItem.fontColor; + ctx.fillStyle = legendItem.fontColor; + const textWidth = ctx.measureText(legendItem.text).width; + const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign)); + const width = boxWidth + halfFontSize + textWidth; + let x = cursor.x; + let y = cursor.y; + rtlHelper.setWidth(this.width); + if (isHorizontal) { + if (i > 0 && x + width + padding > this.right) { + y = cursor.y += lineHeight; + cursor.line++; + x = cursor.x = _alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]); + } + } else if (i > 0 && y + lineHeight > this.bottom) { + x = cursor.x = x + columnSizes[cursor.line].width + padding; + cursor.line++; + y = cursor.y = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height); + } + const realX = rtlHelper.x(x); + drawLegendBox(realX, y, legendItem); + x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl); + fillText(rtlHelper.x(x), y, legendItem); + if (isHorizontal) { + cursor.x += width + padding; + } else if (typeof legendItem.text !== 'string') { + const fontLineHeight = labelFont.lineHeight; + cursor.y += calculateLegendItemHeight(legendItem, fontLineHeight) + padding; + } else { + cursor.y += lineHeight; + } + }); + restoreTextDirection(this.ctx, opts.textDirection); + } + drawTitle() { + const opts = this.options; + const titleOpts = opts.title; + const titleFont = toFont(titleOpts.font); + const titlePadding = toPadding(titleOpts.padding); + if (!titleOpts.display) { + return; + } + const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width); + const ctx = this.ctx; + const position = titleOpts.position; + const halfFontSize = titleFont.size / 2; + const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize; + let y; + let left = this.left; + let maxWidth = this.width; + if (this.isHorizontal()) { + maxWidth = Math.max(...this.lineWidths); + y = this.top + topPaddingPlusHalfFontSize; + left = _alignStartEnd(opts.align, left, this.right - maxWidth); + } else { + const maxHeight = this.columnSizes.reduce((acc, size)=>Math.max(acc, size.height), 0); + y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight()); + } + const x = _alignStartEnd(position, left, left + maxWidth); + ctx.textAlign = rtlHelper.textAlign(_toLeftRightCenter(position)); + ctx.textBaseline = 'middle'; + ctx.strokeStyle = titleOpts.color; + ctx.fillStyle = titleOpts.color; + ctx.font = titleFont.string; + renderText(ctx, titleOpts.text, x, y, titleFont); + } + _computeTitleHeight() { + const titleOpts = this.options.title; + const titleFont = toFont(titleOpts.font); + const titlePadding = toPadding(titleOpts.padding); + return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0; + } + _getLegendItemAt(x, y) { + let i, hitBox, lh; + if (_isBetween(x, this.left, this.right) && _isBetween(y, this.top, this.bottom)) { + lh = this.legendHitBoxes; + for(i = 0; i < lh.length; ++i){ + hitBox = lh[i]; + if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width) && _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) { + return this.legendItems[i]; + } + } + } + return null; + } + handleEvent(e) { + const opts = this.options; + if (!isListened(e.type, opts)) { + return; + } + const hoveredItem = this._getLegendItemAt(e.x, e.y); + if (e.type === 'mousemove' || e.type === 'mouseout') { + const previous = this._hoveredItem; + const sameItem = itemsEqual(previous, hoveredItem); + if (previous && !sameItem) { + callback(opts.onLeave, [ + e, + previous, + this + ], this); + } + this._hoveredItem = hoveredItem; + if (hoveredItem && !sameItem) { + callback(opts.onHover, [ + e, + hoveredItem, + this + ], this); + } + } else if (hoveredItem) { + callback(opts.onClick, [ + e, + hoveredItem, + this + ], this); + } + } +} +function calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight) { + const itemWidth = calculateItemWidth(legendItem, boxWidth, labelFont, ctx); + const itemHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight); + return { + itemWidth, + itemHeight + }; +} +function calculateItemWidth(legendItem, boxWidth, labelFont, ctx) { + let legendItemText = legendItem.text; + if (legendItemText && typeof legendItemText !== 'string') { + legendItemText = legendItemText.reduce((a, b)=>a.length > b.length ? a : b); + } + return boxWidth + labelFont.size / 2 + ctx.measureText(legendItemText).width; +} +function calculateItemHeight(_itemHeight, legendItem, fontLineHeight) { + let itemHeight = _itemHeight; + if (typeof legendItem.text !== 'string') { + itemHeight = calculateLegendItemHeight(legendItem, fontLineHeight); + } + return itemHeight; +} +function calculateLegendItemHeight(legendItem, fontLineHeight) { + const labelHeight = legendItem.text ? legendItem.text.length : 0; + return fontLineHeight * labelHeight; +} +function isListened(type, opts) { + if ((type === 'mousemove' || type === 'mouseout') && (opts.onHover || opts.onLeave)) { + return true; + } + if (opts.onClick && (type === 'click' || type === 'mouseup')) { + return true; + } + return false; +} +var plugin_legend = { + id: 'legend', + _element: Legend, + start (chart, _args, options) { + const legend = chart.legend = new Legend({ + ctx: chart.ctx, + options, + chart + }); + layouts.configure(chart, legend, options); + layouts.addBox(chart, legend); + }, + stop (chart) { + layouts.removeBox(chart, chart.legend); + delete chart.legend; + }, + beforeUpdate (chart, _args, options) { + const legend = chart.legend; + layouts.configure(chart, legend, options); + legend.options = options; + }, + afterUpdate (chart) { + const legend = chart.legend; + legend.buildLabels(); + legend.adjustHitBoxes(); + }, + afterEvent (chart, args) { + if (!args.replay) { + chart.legend.handleEvent(args.event); + } + }, + defaults: { + display: true, + position: 'top', + align: 'center', + fullSize: true, + reverse: false, + weight: 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 , useBorderRadius , borderRadius } } = 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: useBorderRadius && (borderRadius || style.borderRadius), + datasetIndex: meta.index + }; + }, this); + } + }, + title: { + color: (ctx)=>ctx.chart.options.color, + display: false, + position: 'center', + text: '' + } + }, + descriptors: { + _scriptable: (name)=>!name.startsWith('on'), + labels: { + _scriptable: (name)=>![ + 'generateLabels', + 'filter', + 'sort' + ].includes(name) + } + } +}; + +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 xSet = new Set(); + let y = 0; + let count = 0; + for(i = 0, len = items.length; i < len; ++i){ + const el = items[i].element; + if (el && el.hasValue()) { + const pos = el.tooltipPosition(); + xSet.add(pos.x); + y += pos.y; + ++count; + } + } + if (count === 0 || xSet.size === 0) { + return false; + } + const xAverage = [ + ...xSet + ].reduce((a, b)=>a + b) / xSet.size; + return { + x: xAverage, + y: y / count + }; + }, + nearest (items, eventPosition) { + if (!items.length) { + return false; + } + let x = eventPosition.x; + let y = eventPosition.y; + let minDistance = Number.POSITIVE_INFINITY; + let i, len, nearestElement; + for(i = 0, len = items.length; i < len; ++i){ + const el = items[i].element; + if (el && el.hasValue()) { + const center = el.getCenterPoint(); + const d = distanceBetweenPoints(eventPosition, center); + if (d < minDistance) { + minDistance = d; + nearestElement = el; + } + } + } + if (nearestElement) { + const tp = nearestElement.tooltipPosition(); + x = tp.x; + y = tp.y; + } + return { + x, + y + }; + } +}; +function pushOrConcat(base, toPush) { + if (toPush) { + if (isArray(toPush)) { + Array.prototype.push.apply(base, toPush); + } else { + base.push(toPush); + } + } + return base; +} + function splitNewlines(str) { + if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { + return str.split('\n'); + } + return str; +} + function createTooltipItem(chart, item) { + const { element , datasetIndex , index } = 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; +} +const defaultCallbacks = { + beforeTitle: noop, + title (tooltipItems) { + if (tooltipItems.length > 0) { + const item = tooltipItems[0]; + const labels = item.chart.data.labels; + const labelCount = labels ? labels.length : 0; + if (this && this.options && this.options.mode === 'dataset') { + return item.dataset.label || ''; + } else if (item.label) { + return item.label; + } else if (labelCount > 0 && item.dataIndex < labelCount) { + return labels[item.dataIndex]; + } + } + return ''; + }, + afterTitle: noop, + beforeBody: noop, + beforeLabel: noop, + label (tooltipItem) { + if (this && this.options && this.options.mode === 'dataset') { + return tooltipItem.label + ': ' + tooltipItem.formattedValue || tooltipItem.formattedValue; + } + let label = tooltipItem.dataset.label || ''; + if (label) { + label += ': '; + } + const value = tooltipItem.formattedValue; + if (!isNullOrUndef(value)) { + label += value; + } + return label; + }, + labelColor (tooltipItem) { + const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex); + const options = meta.controller.getStyle(tooltipItem.dataIndex); + return { + borderColor: options.borderColor, + backgroundColor: options.backgroundColor, + borderWidth: options.borderWidth, + borderDash: options.borderDash, + borderDashOffset: options.borderDashOffset, + borderRadius: 0 + }; + }, + labelTextColor () { + return this.options.bodyColor; + }, + labelPointStyle (tooltipItem) { + const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex); + const options = meta.controller.getStyle(tooltipItem.dataIndex); + return { + pointStyle: options.pointStyle, + rotation: options.rotation + }; + }, + afterLabel: noop, + afterBody: noop, + beforeFooter: noop, + footer: noop, + afterFooter: noop +}; + function invokeCallbackWithFallback(callbacks, name, ctx, arg) { + const result = callbacks[name].call(ctx, arg); + if (typeof result === 'undefined') { + return defaultCallbacks[name].call(ctx, arg); + } + return result; +} +class Tooltip extends Element { + static positioners = positioners; + 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; + 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 = invokeCallbackWithFallback(callbacks, 'beforeTitle', this, context); + const title = invokeCallbackWithFallback(callbacks, 'title', this, context); + const afterTitle = invokeCallbackWithFallback(callbacks, 'afterTitle', this, context); + let lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeTitle)); + lines = pushOrConcat(lines, splitNewlines(title)); + lines = pushOrConcat(lines, splitNewlines(afterTitle)); + return lines; + } + getBeforeBody(tooltipItems, options) { + return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'beforeBody', this, tooltipItems)); + } + getBody(tooltipItems, options) { + const { callbacks } = options; + const bodyItems = []; + each(tooltipItems, (context)=>{ + const bodyItem = { + before: [], + lines: [], + after: [] + }; + const scoped = overrideCallbacks(callbacks, context); + pushOrConcat(bodyItem.before, splitNewlines(invokeCallbackWithFallback(scoped, 'beforeLabel', this, context))); + pushOrConcat(bodyItem.lines, invokeCallbackWithFallback(scoped, 'label', this, context)); + pushOrConcat(bodyItem.after, splitNewlines(invokeCallbackWithFallback(scoped, 'afterLabel', this, context))); + bodyItems.push(bodyItem); + }); + return bodyItems; + } + getAfterBody(tooltipItems, options) { + return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'afterBody', this, tooltipItems)); + } + getFooter(tooltipItems, options) { + const { callbacks } = options; + const beforeFooter = invokeCallbackWithFallback(callbacks, 'beforeFooter', this, tooltipItems); + const footer = invokeCallbackWithFallback(callbacks, 'footer', this, tooltipItems); + const afterFooter = invokeCallbackWithFallback(callbacks, 'afterFooter', this, tooltipItems); + let lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeFooter)); + lines = pushOrConcat(lines, splitNewlines(footer)); + lines = pushOrConcat(lines, splitNewlines(afterFooter)); + return lines; + } + _createItems(options) { + const active = this._active; + const data = this.chart.data; + const labelColors = []; + const labelPointStyles = []; + const labelTextColors = []; + let tooltipItems = []; + let i, len; + for(i = 0, len = active.length; i < len; ++i){ + tooltipItems.push(createTooltipItem(this.chart, active[i])); + } + if (options.filter) { + tooltipItems = tooltipItems.filter((element, 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(invokeCallbackWithFallback(scoped, 'labelColor', this, context)); + labelPointStyles.push(invokeCallbackWithFallback(scoped, 'labelPointStyle', this, context)); + labelTextColors.push(invokeCallbackWithFallback(scoped, 'labelTextColor', this, context)); + }); + this.labelColors = labelColors; + this.labelPointStyles = labelPointStyles; + this.labelTextColors = labelTextColors; + this.dataPoints = tooltipItems; + return tooltipItems; + } + update(changed, replay) { + const options = this.options.setContext(this.getContext()); + const active = this._active; + let properties; + let tooltipItems = []; + if (!active.length) { + if (this.opacity !== 0) { + properties = { + opacity: 0 + }; + } + } else { + const position = positioners[options.position].call(this, active, this._eventPosition); + tooltipItems = this._createItems(options); + this.title = this.getTitle(tooltipItems, options); + this.beforeBody = this.getBeforeBody(tooltipItems, options); + this.body = this.getBody(tooltipItems, options); + this.afterBody = this.getAfterBody(tooltipItems, options); + this.footer = this.getFooter(tooltipItems, options); + const size = this._size = getTooltipSize(this, options); + const positionAndSize = Object.assign({}, position, size); + const alignment = determineAlignment(this.chart, options, positionAndSize); + const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this.chart); + this.xAlign = alignment.xAlign; + this.yAlign = alignment.yAlign; + properties = { + opacity: 1, + x: backgroundPoint.x, + y: backgroundPoint.y, + width: size.width, + height: size.height, + caretX: position.x, + caretY: position.y + }; + } + this._tooltipItems = tooltipItems; + this.$context = 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 labelColor = this.labelColors[i]; + const labelPointStyle = this.labelPointStyles[i]; + const { boxHeight , boxWidth } = options; + const bodyFont = toFont(options.bodyFont); + const colorX = getAlignedX(this, 'left', options); + const rtlColorX = rtlHelper.x(colorX); + const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0; + const colorY = pt.y + yOffSet; + if (options.usePointStyle) { + const drawOptions = { + radius: Math.min(boxWidth, boxHeight) / 2, + pointStyle: labelPointStyle.pointStyle, + rotation: labelPointStyle.rotation, + borderWidth: 1 + }; + const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2; + const centerY = colorY + boxHeight / 2; + ctx.strokeStyle = options.multiKeyBackground; + ctx.fillStyle = options.multiKeyBackground; + drawPoint(ctx, drawOptions, centerX, centerY); + ctx.strokeStyle = labelColor.borderColor; + ctx.fillStyle = labelColor.backgroundColor; + drawPoint(ctx, drawOptions, centerX, centerY); + } else { + ctx.lineWidth = isObject(labelColor.borderWidth) ? Math.max(...Object.values(labelColor.borderWidth)) : labelColor.borderWidth || 1; + ctx.strokeStyle = labelColor.borderColor; + ctx.setLineDash(labelColor.borderDash || []); + ctx.lineDashOffset = labelColor.borderDashOffset || 0; + const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth); + const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2); + const borderRadius = toTRBLCorners(labelColor.borderRadius); + if (Object.values(borderRadius).some((v)=>v !== 0)) { + ctx.beginPath(); + ctx.fillStyle = options.multiKeyBackground; + addRoundedRectPath(ctx, { + x: outerX, + y: colorY, + w: boxWidth, + h: boxHeight, + radius: borderRadius + }); + ctx.fill(); + ctx.stroke(); + ctx.fillStyle = labelColor.backgroundColor; + ctx.beginPath(); + addRoundedRectPath(ctx, { + x: innerX, + y: colorY + 1, + w: boxWidth - 2, + h: boxHeight - 2, + radius: borderRadius + }); + ctx.fill(); + } else { + ctx.fillStyle = options.multiKeyBackground; + ctx.fillRect(outerX, colorY, boxWidth, boxHeight); + ctx.strokeRect(outerX, colorY, boxWidth, boxHeight); + ctx.fillStyle = labelColor.backgroundColor; + ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2); + } + } + ctx.fillStyle = this.labelTextColors[i]; + } + drawBody(pt, ctx, options) { + const { body } = this; + const { bodySpacing , bodyAlign , displayColors , boxHeight , boxWidth , boxPadding } = options; + const bodyFont = toFont(options.bodyFont); + let bodyLineHeight = bodyFont.lineHeight; + let xLinePadding = 0; + const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width); + const fillLineOfText = function(line) { + ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2); + pt.y += bodyLineHeight + bodySpacing; + }; + const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign); + let bodyItem, textColor, lines, i, j, ilen, jlen; + ctx.textAlign = bodyAlign; + ctx.textBaseline = 'middle'; + ctx.font = bodyFont.string; + pt.x = getAlignedX(this, bodyAlignForCalculation, options); + ctx.fillStyle = options.bodyColor; + each(this.beforeBody, fillLineOfText); + xLinePadding = displayColors && bodyAlignForCalculation !== 'right' ? bodyAlign === 'center' ? boxWidth / 2 + boxPadding : boxWidth + 2 + boxPadding : 0; + for(i = 0, ilen = body.length; i < ilen; ++i){ + bodyItem = body[i]; + textColor = this.labelTextColors[i]; + ctx.fillStyle = textColor; + each(bodyItem.before, fillLineOfText); + lines = bodyItem.lines; + if (displayColors && lines.length) { + this._drawColorBox(ctx, pt, i, rtlHelper, options); + bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight); + } + for(j = 0, jlen = lines.length; j < jlen; ++j){ + fillLineOfText(lines[j]); + bodyLineHeight = bodyFont.lineHeight; + } + each(bodyItem.after, fillLineOfText); + } + xLinePadding = 0; + bodyLineHeight = bodyFont.lineHeight; + each(this.afterBody, fillLineOfText); + pt.y -= bodySpacing; + } + drawFooter(pt, ctx, options) { + const footer = this.footer; + const length = footer.length; + let footerFont, i; + if (length) { + const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width); + pt.x = getAlignedX(this, options.footerAlign, options); + pt.y += options.footerMarginTop; + ctx.textAlign = rtlHelper.textAlign(options.footerAlign); + ctx.textBaseline = 'middle'; + footerFont = toFont(options.footerFont); + ctx.fillStyle = options.footerColor; + ctx.font = footerFont.string; + for(i = 0; i < length; ++i){ + ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2); + pt.y += footerFont.lineHeight + options.footerSpacing; + } + } + } + drawBackground(pt, ctx, tooltipSize, options) { + const { xAlign , yAlign } = this; + const { x , y } = pt; + const { width , height } = tooltipSize; + const { topLeft , topRight , bottomLeft , bottomRight } = toTRBLCorners(options.cornerRadius); + ctx.fillStyle = options.backgroundColor; + ctx.strokeStyle = options.borderColor; + ctx.lineWidth = options.borderWidth; + ctx.beginPath(); + ctx.moveTo(x + topLeft, y); + if (yAlign === 'top') { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x + width - topRight, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + topRight); + if (yAlign === 'center' && xAlign === 'right') { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x + width, y + height - bottomRight); + ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height); + if (yAlign === 'bottom') { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x + bottomLeft, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft); + if (yAlign === 'center' && xAlign === 'left') { + this.drawCaret(pt, ctx, tooltipSize, options); + } + ctx.lineTo(x, y + topLeft); + ctx.quadraticCurveTo(x, y, x + topLeft, y); + ctx.closePath(); + ctx.fill(); + if (options.borderWidth > 0) { + ctx.stroke(); + } + } + _updateAnimationTarget(options) { + const chart = this.chart; + const anims = this.$animations; + const animX = anims && anims.x; + const animY = anims && anims.y; + if (animX || animY) { + const position = positioners[options.position].call(this, this._active, this._eventPosition); + if (!position) { + return; + } + const size = this._size = getTooltipSize(this, options); + const positionAndSize = Object.assign({}, position, this._size); + const alignment = determineAlignment(chart, options, positionAndSize); + const point = getBackgroundPoint(options, positionAndSize, alignment, chart); + if (animX._to !== point.x || animY._to !== point.y) { + this.xAlign = alignment.xAlign; + this.yAlign = alignment.yAlign; + this.width = size.width; + this.height = size.height; + this.caretX = position.x; + this.caretY = position.y; + this._resolveAnimations().update(this, point); + } + } + } + _willRender() { + return !!this.opacity; + } + draw(ctx) { + const options = this.options.setContext(this.getContext()); + let opacity = this.opacity; + if (!opacity) { + return; + } + this._updateAnimationTarget(options); + const tooltipSize = { + width: this.width, + height: this.height + }; + const pt = { + x: this.x, + y: this.y + }; + opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity; + const padding = toPadding(options.padding); + const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length; + if (options.enabled && hasTooltipContent) { + ctx.save(); + ctx.globalAlpha = opacity; + this.drawBackground(pt, ctx, tooltipSize, options); + overrideTextDirection(ctx, options.textDirection); + pt.y += padding.top; + this.drawTitle(pt, ctx, options); + this.drawBody(pt, ctx, options); + this.drawFooter(pt, ctx, options); + restoreTextDirection(ctx, options.textDirection); + ctx.restore(); + } + } + getActiveElements() { + return this._active || []; + } + setActiveElements(activeElements, eventPosition) { + const lastActive = this._active; + const active = activeElements.map(({ datasetIndex , index })=>{ + 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.filter((i)=>this.chart.data.datasets[i.datasetIndex] && this.chart.getDatasetMeta(i.datasetIndex).controller.getParsed(i.index) !== undefined); + } + 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); + } +} +var plugin_tooltip = { + id: 'tooltip', + _element: Tooltip, + positioners, + afterInit (chart, _args, options) { + if (options) { + chart.tooltip = new Tooltip({ + chart, + options + }); + } + }, + beforeUpdate (chart, _args, options) { + if (chart.tooltip) { + chart.tooltip.initialize(options); + } + }, + reset (chart, _args, options) { + if (chart.tooltip) { + chart.tooltip.initialize(options); + } + }, + afterDraw (chart) { + const tooltip = chart.tooltip; + if (tooltip && tooltip._willRender()) { + const args = { + tooltip + }; + if (chart.notifyPlugins('beforeTooltipDraw', { + ...args, + cancelable: true + }) === false) { + return; + } + tooltip.draw(chart.ctx); + chart.notifyPlugins('afterTooltipDraw', args); + } + }, + afterEvent (chart, args) { + if (chart.tooltip) { + const useFinalPosition = args.replay; + if (chart.tooltip.handleEvent(args.event, useFinalPosition, args.inChartArea)) { + args.changed = true; + } + } + }, + defaults: { + enabled: true, + external: null, + position: 'average', + backgroundColor: 'rgba(0,0,0,0.8)', + titleColor: '#fff', + titleFont: { + weight: 'bold' + }, + titleSpacing: 2, + titleMarginBottom: 6, + titleAlign: 'left', + bodyColor: '#fff', + bodySpacing: 2, + bodyFont: {}, + bodyAlign: 'left', + footerColor: '#fff', + footerSpacing: 2, + footerMarginTop: 6, + footerFont: { + weight: 'bold' + }, + footerAlign: 'left', + padding: 6, + caretPadding: 2, + caretSize: 5, + cornerRadius: 6, + boxHeight: (ctx, opts)=>opts.bodyFont.size, + boxWidth: (ctx, opts)=>opts.bodyFont.size, + multiKeyBackground: '#fff', + displayColors: true, + boxPadding: 0, + borderColor: 'rgba(0,0,0,0)', + borderWidth: 0, + animation: { + duration: 400, + easing: 'easeOutQuart' + }, + animations: { + numbers: { + type: 'number', + properties: [ + 'x', + 'y', + 'width', + 'height', + 'caretX', + 'caretY' + ] + }, + opacity: { + easing: 'linear', + duration: 200 + } + }, + callbacks: defaultCallbacks + }, + defaultRoutes: { + bodyFont: 'font', + footerFont: 'font', + titleFont: 'font' + }, + descriptors: { + _scriptable: (name)=>name !== 'filter' && name !== 'itemSort' && name !== 'external', + _indexable: false, + callbacks: { + _scriptable: false, + _indexable: false + }, + animation: { + _fallback: false + }, + animations: { + _fallback: 'animation' + } + }, + additionalOptionScopes: [ + 'interaction' + ] +}; + +var plugins = /*#__PURE__*/Object.freeze({ +__proto__: null, +Colors: plugin_colors, +Decimation: plugin_decimation, +Filler: index, +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); +function _getLabelForValue(value) { + const labels = this.getLabels(); + if (value >= 0 && value < labels.length) { + return labels[value]; + } + return value; +} +class CategoryScale extends Scale { + static id = 'category'; + static defaults = { + ticks: { + callback: _getLabelForValue + } + }; + 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) { + return _getLabelForValue.call(this, value); + } + configure() { + super.configure(); + if (!this.isHorizontal()) { + this._reversePixels = !this._reversePixels; + } + } + getPixelForValue(value) { + if (typeof value !== 'number') { + value = this.parse(value); + } + return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange); + } + getPixelForTick(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; + } +} + +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){ + const tickValue = Math.round((niceMin + j * spacing) * factor) / factor; + if (maxDefined && tickValue > max) { + break; + } + ticks.push({ + value: tickValue + }); + } + if (maxDefined && includeBounds && niceMax !== max) { + if (ticks.length && almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) { + ticks[ticks.length - 1].value = max; + } else { + ticks.push({ + value: max + }); + } + } else if (!maxDefined || niceMax === max) { + ticks.push({ + value: niceMax + }); + } + return ticks; +} +function relativeLabelSize(value, minSpacing, { horizontal , minRotation }) { + const rad = toRadians(minRotation); + const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 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 = max === 0 ? 1 : Math.abs(max * 0.05); + setMax(max + offset); + if (!beginAtZero) { + setMin(min - offset); + } + } + this.min = min; + this.max = max; + } + getTickLimit() { + const tickOpts = this.options.ticks; + let { maxTicksLimit , stepSize } = tickOpts; + let maxTicks; + if (stepSize) { + maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1; + if (maxTicks > 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 { + static id = 'linear'; + static defaults = { + ticks: { + callback: Ticks.formatters.numeric + } + }; + 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; + } +} + +const log10Floor = (v)=>Math.floor(log10(v)); +const changeExponent = (v, m)=>Math.pow(10, log10Floor(v) + m); +function isMajor(tickVal) { + const remain = tickVal / Math.pow(10, log10Floor(tickVal)); + return remain === 1; +} +function steps(min, max, rangeExp) { + const rangeStep = Math.pow(10, rangeExp); + const start = Math.floor(min / rangeStep); + const end = Math.ceil(max / rangeStep); + return end - start; +} +function startExp(min, max) { + const range = max - min; + let rangeExp = log10Floor(range); + while(steps(min, max, rangeExp) > 10){ + rangeExp++; + } + while(steps(min, max, rangeExp) < 10){ + rangeExp--; + } + return Math.min(rangeExp, log10Floor(min)); +} + function generateTicks(generationOptions, { min , max }) { + min = finiteOrDefault(generationOptions.min, min); + const ticks = []; + const minExp = log10Floor(min); + let exp = startExp(min, max); + let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; + const stepSize = Math.pow(10, exp); + const base = minExp > exp ? Math.pow(10, minExp) : 0; + const start = Math.round((min - base) * precision) / precision; + const offset = Math.floor((min - base) / stepSize / 10) * stepSize * 10; + let significand = Math.floor((start - offset) / Math.pow(10, exp)); + let value = finiteOrDefault(generationOptions.min, Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision); + while(value < max){ + ticks.push({ + value, + major: isMajor(value), + significand + }); + if (significand >= 10) { + significand = significand < 15 ? 15 : 20; + } else { + significand++; + } + if (significand >= 20) { + exp++; + significand = 2; + precision = exp >= 0 ? 1 : precision; + } + value = Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision; + } + const lastTick = finiteOrDefault(generationOptions.max, value); + ticks.push({ + value: lastTick, + major: isMajor(lastTick), + significand + }); + return ticks; +} +class LogarithmicScale extends Scale { + static id = 'logarithmic'; + static defaults = { + ticks: { + callback: Ticks.formatters.logarithmic, + major: { + enabled: true + } + } + }; + 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; + } + if (this._zero && this.min !== this._suggestedMin && !isNumberFinite(this._userMin)) { + this.min = min === changeExponent(this.min, 0) ? changeExponent(this.min, -1) : changeExponent(this.min, 0); + } + 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; + if (min === max) { + if (min <= 0) { + setMin(1); + setMax(10); + } else { + setMin(changeExponent(min, -1)); + setMax(changeExponent(max, +1)); + } + } + if (min <= 0) { + setMin(changeExponent(max, -1)); + } + if (max <= 0) { + setMax(changeExponent(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); + } +} + +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 createPointLabelItem(scale, index, itemOpts) { + const outerDistance = scale.drawingArea; + const { extra , additionalAngle , padding , size } = itemOpts; + const pointLabelPosition = scale.getPointPosition(index, outerDistance + extra + padding, additionalAngle); + const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI))); + const y = yForAngle(pointLabelPosition.y, size.h, angle); + const textAlign = getTextAlignForAngle(angle); + const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign); + return { + visible: true, + x: pointLabelPosition.x, + y, + textAlign, + left, + top: y, + right: left + size.w, + bottom: y + size.h + }; +} +function isNotOverlapped(item, area) { + if (!area) { + return true; + } + const { left , top , right , bottom } = item; + const apexesInArea = _isPointInArea({ + x: left, + y: top + }, area) || _isPointInArea({ + x: left, + y: bottom + }, area) || _isPointInArea({ + x: right, + y: top + }, area) || _isPointInArea({ + x: right, + y: bottom + }, area); + return !apexesInArea; +} +function buildPointLabelItems(scale, labelSizes, padding) { + const items = []; + const valueCount = scale._pointLabels.length; + const opts = scale.options; + const { centerPointLabels , display } = opts.pointLabels; + const itemOpts = { + extra: getTickBackdropHeight(opts) / 2, + additionalAngle: centerPointLabels ? PI / valueCount : 0 + }; + let area; + for(let i = 0; i < valueCount; i++){ + itemOpts.padding = padding[i]; + itemOpts.size = labelSizes[i]; + const item = createPointLabelItem(scale, i, itemOpts); + items.push(item); + if (display === 'auto') { + item.visible = isNotOverlapped(item, area); + if (item.visible) { + area = item; + } + } + } + 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 drawPointLabelBox(ctx, opts, item) { + const { left , top , right , bottom } = item; + const { backdropColor } = opts; + if (!isNullOrUndef(backdropColor)) { + const borderRadius = toTRBLCorners(opts.borderRadius); + const padding = toPadding(opts.backdropPadding); + ctx.fillStyle = backdropColor; + const backdropLeft = left - padding.left; + const backdropTop = top - padding.top; + const backdropWidth = right - left + padding.width; + const backdropHeight = bottom - top + padding.height; + if (Object.values(borderRadius).some((v)=>v !== 0)) { + ctx.beginPath(); + addRoundedRectPath(ctx, { + x: backdropLeft, + y: backdropTop, + w: backdropWidth, + h: backdropHeight, + radius: borderRadius + }); + ctx.fill(); + } else { + ctx.fillRect(backdropLeft, backdropTop, backdropWidth, backdropHeight); + } + } +} +function drawPointLabels(scale, labelCount) { + const { ctx , options: { pointLabels } } = scale; + for(let i = labelCount - 1; i >= 0; i--){ + const item = scale._pointLabelItems[i]; + if (!item.visible) { + continue; + } + const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i)); + drawPointLabelBox(ctx, optsAtIndex, item); + const plFont = toFont(optsAtIndex.font); + const { x , y , textAlign } = item; + 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, borderOpts) { + 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(borderOpts.dash || []); + ctx.lineDashOffset = borderOpts.dashOffset; + 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 { + static id = 'radialLinear'; + static 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 + } + }; + static defaultRoutes = { + 'angleLines.color': 'borderColor', + 'pointLabels.color': 'color', + 'ticks.color': 'color' + }; + static descriptors = { + angleLines: { + _fallback: 'grid' + } + }; + 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 , border } = 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 || index === 0 && this.min < 0) { + offset = this.getDistanceFromCenterForValue(tick.value); + const context = this.getContext(index); + const optsAtIndex = grid.setContext(context); + const optsAtIndexBorder = border.setContext(context); + drawRadiusLine(this, optsAtIndex, offset, labelCount, optsAtIndexBorder); + } + }); + } + 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.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 && this.min >= 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, + strokeColor: optsAtIndex.textStrokeColor, + strokeWidth: optsAtIndex.textStrokeWidth + }); + }); + ctx.restore(); + } + drawTitle() {} +} + +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 = /* #__PURE__ */ Object.keys(INTERVALS); + function sorter(a, b) { + return a - b; +} + function parse(scale, input) { + if (isNullOrUndef(input)) { + return null; + } + const adapter = scale._adapter; + const { parser , round , 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 { + static id = 'time'; + static defaults = { + bounds: 'data', + adapters: {}, + time: { + parser: false, + unit: false, + round: false, + isoWeekday: false, + minUnit: 'millisecond', + displayFormats: {} + }, + ticks: { + source: 'auto', + callback: false, + major: { + enabled: false + } + } + }; + 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); + adapter.init(opts); + mergeIf(time.displayFormats, adapter.formats()); + this._parseOpts = { + parser: time.parser, + round: time.round, + isoWeekday: time.isoWeekday + }; + super.init(scaleOpts); + this._normalized = opts.normalized; + } + parse(raw, 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); + } + afterAutoSkip() { + if (this.options.offsetAfterAutoskip) { + this.initOffsets(this.ticks.map((tick)=>+tick.value)); + } + } + initOffsets(timestamps = []) { + let start = 0; + let end = 0; + let first, last; + if (this.options.offset && timestamps.length) { + first = this.getDecimalForValue(timestamps[0]); + if (timestamps.length === 1) { + start = 1 - first; + } else { + start = (this.getDecimalForValue(timestamps[1]) - first) / 2; + } + last = this.getDecimalForValue(timestamps[timestamps.length - 1]); + if (timestamps.length === 1) { + end = last; + } else { + end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2; + } + } + const limit = timestamps.length < 3 ? 0.5 : 0.25; + start = _limitValue(start, 0, limit); + end = _limitValue(end, 0, limit); + this._offsets = { + start, + end, + factor: 1 / (start + 1 + end) + }; + } + _generate() { + const adapter = this._adapter; + const min = this.min; + const max = this.max; + const options = this.options; + const timeOpts = options.time; + const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min)); + const stepSize = valueOrDefault(options.ticks.stepSize, 1); + const weekday = minor === 'week' ? timeOpts.isoWeekday : false; + const hasWeekday = isNumber(weekday) || weekday === true; + const ticks = {}; + let first = min; + let time, count; + if (hasWeekday) { + first = +adapter.startOf(first, 'isoWeek', weekday); + } + first = +adapter.startOf(first, hasWeekday ? 'day' : minor); + if (adapter.diff(max, min, minor) > 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(sorter).map((x)=>+x); + } + getLabelForValue(value) { + const adapter = this._adapter; + const timeOpts = this.options.time; + if (timeOpts.tooltipFormat) { + return adapter.format(value, timeOpts.tooltipFormat); + } + return adapter.format(value, timeOpts.displayFormats.datetime); + } + format(value, format) { + const options = this.options; + const formats = options.time.displayFormats; + const unit = this._unit; + const fmt = format || formats[unit]; + return this._adapter.format(value, fmt); + } + _tickFormatFunction(time, index, ticks, format) { + const options = this.options; + const formatter = options.ticks.callback; + if (formatter) { + return callback(formatter, [ + time, + index, + ticks + ], this); + } + const formats = options.time.displayFormats; + const unit = this._unit; + const majorUnit = this._majorUnit; + const minorFormat = unit && formats[unit]; + const majorFormat = majorUnit && formats[majorUnit]; + const tick = ticks[index]; + const major = majorUnit && majorFormat && tick && tick.major; + return this._adapter.format(time, format || (major ? majorFormat : minorFormat)); + } + generateTickLabels(ticks) { + let i, ilen, tick; + for(i = 0, ilen = ticks.length; i < ilen; ++i){ + tick = ticks[i]; + tick.label = this._tickFormatFunction(tick.value, i, ticks); + } + } + getDecimalForValue(value) { + return value === null ? NaN : (value - this.min) / (this.max - this.min); + } + getPixelForValue(value) { + const offsets = this._offsets; + const pos = this.getDecimalForValue(value); + return this.getPixelForDecimal((offsets.start + pos) * offsets.factor); + } + getValueForPixel(pixel) { + const offsets = this._offsets; + const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + return this.min + pos * (this.max - this.min); + } + _getLabelSize(label) { + const ticksOpts = this.options.ticks; + const tickLabelWidth = this.ctx.measureText(label).width; + const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); + const cosRotation = Math.cos(angle); + const sinRotation = Math.sin(angle); + const tickFontSize = this._resolveTickFontOptions(0).size; + return { + w: tickLabelWidth * cosRotation + tickFontSize * sinRotation, + h: tickLabelWidth * sinRotation + tickFontSize * cosRotation + }; + } + _getLabelCapacity(exampleTime) { + const timeOpts = this.options.time; + const displayFormats = timeOpts.displayFormats; + const format = displayFormats[timeOpts.unit] || displayFormats.millisecond; + const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [ + exampleTime + ], this._majorUnit), format); + const size = this._getLabelSize(exampleLabel); + const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1; + return capacity > 0 ? capacity : 1; + } + getDataTimestamps() { + let timestamps = this._cache.data || []; + let i, ilen; + if (timestamps.length) { + return timestamps; + } + const metas = this.getMatchingVisibleMetas(); + if (this._normalized && metas.length) { + return this._cache.data = metas[0].controller.getAllParsedValues(this); + } + for(i = 0, ilen = metas.length; i < ilen; ++i){ + timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this)); + } + return this._cache.data = this.normalize(timestamps); + } + getLabelTimestamps() { + const timestamps = this._cache.labels || []; + let i, ilen; + if (timestamps.length) { + return timestamps; + } + const labels = this.getLabels(); + for(i = 0, ilen = labels.length; i < ilen; ++i){ + timestamps.push(parse(this, labels[i])); + } + return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps); + } + normalize(values) { + return _arrayUnique(values.sort(sorter)); + } +} + +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 { + static id = 'timeseries'; + static defaults = TimeScale.defaults; + 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; + } + _generate() { + const min = this.min; + const max = this.max; + let timestamps = super.getDataTimestamps(); + if (!timestamps.includes(min) || !timestamps.length) { + timestamps.splice(0, 0, min); + } + if (!timestamps.includes(max) || timestamps.length === 1) { + timestamps.push(max); + } + return timestamps.sort((a, b)=>a - b); + } + _getTimestampsForTable() { + let timestamps = this._cache.all || []; + if (timestamps.length) { + return timestamps; + } + const data = this.getDataTimestamps(); + const label = this.getLabelTimestamps(); + if (data.length && label.length) { + timestamps = this.normalize(data.concat(label)); + } else { + timestamps = data.length ? data : label; + } + timestamps = this._cache.all = timestamps; + return timestamps; + } + getDecimalForValue(value) { + return (interpolate(this._table, value) - this._minPos) / this._tableRange; + } + getValueForPixel(pixel) { + const offsets = this._offsets; + const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + return interpolate(this._table, decimal * this._tableRange + this._minPos, true); + } +} + +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, plugin_colors as Colors, DatasetController, plugin_decimation as Decimation, DomPlatform, DoughnutController, Element, index 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, defaults, elements, layouts, plugins, registerables, registry, scales }; +//# sourceMappingURL=chart.js.map diff --git a/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs new file mode 100644 index 00000000..3502b47f --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs @@ -0,0 +1,2915 @@ +/*! + * Chart.js v4.5.1 + * https://www.chartjs.org + * (c) 2025 Chart.js Contributors + * Released under the MIT License + */ +'use strict'; + +var color$1 = require('@kurkle/color'); + +/** + * @namespace Chart.helpers + */ /** + * An empty function that can be used, for example, for optional callback. + */ function noop() { +/* noop */ } +/** + * Returns a unique id, sequentially generated from a global variable. + */ const uid = (()=>{ + let id = 0; + return ()=>id++; +})(); +/** + * Returns true if `value` is neither null nor undefined, else returns false. + * @param value - The value to test. + * @since 2.7.0 + */ function isNullOrUndef(value) { + return value === null || value === undefined; +} +/** + * Returns true if `value` is an array (including typed arrays), else returns false. + * @param value - The value to test. + * @function + */ function isArray(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + const type = Object.prototype.toString.call(value); + if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') { + return true; + } + return false; +} +/** + * Returns true if `value` is an object (excluding null), else returns false. + * @param value - The value to test. + * @since 2.7.0 + */ function isObject(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; +} +/** + * Returns true if `value` is a finite number, else returns false + * @param value - The value to test. + */ function isNumberFinite(value) { + return (typeof value === 'number' || value instanceof Number) && isFinite(+value); +} +/** + * 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. + */ function finiteOrDefault(value, defaultValue) { + return isNumberFinite(value) ? value : defaultValue; +} +/** + * 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. + */ 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; +/** + * 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`. + */ 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]); + } + } +} +/** + * Returns true if the `a0` and `a1` arrays have the same content, else returns false. + * @param a0 - The array to compare + * @param a1 - The array to compare + * @private + */ 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; +} +/** + * Returns a deep copy of `source` without keeping references on objects and arrays. + * @param source - The value to clone. + */ 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; +} +/** + * The default merger when Chart.helpers.merge is called without merger option. + * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. + * @private + */ function _merger(key, target, source, options) { + if (!isValidKey(key)) { + return; + } + const tval = target[key]; + const sval = source[key]; + if (isObject(tval) && isObject(sval)) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + merge(tval, sval, options); + } else { + target[key] = clone(sval); + } +} +function merge(target, source, options) { + const sources = isArray(source) ? source : [ + source + ]; + const ilen = sources.length; + if (!isObject(target)) { + return target; + } + options = options || {}; + const merger = options.merger || _merger; + let current; + for(let i = 0; i < ilen; ++i){ + current = sources[i]; + if (!isObject(current)) { + continue; + } + const keys = Object.keys(current); + for(let k = 0, klen = keys.length; k < klen; ++k){ + merger(keys[k], target, current, options); + } + } + return target; +} +function mergeIf(target, source) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + return merge(target, source, { + merger: _mergerIf + }); +} +/** + * Merges source[key] in target[key] only if target[key] is undefined. + * @private + */ 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); + } +} +/** + * @private + */ function _deprecated(scope, value, previous, current) { + if (value !== undefined) { + console.warn(scope + ': "' + previous + '" is deprecated. Please use "' + current + '" instead'); + } +} +// resolveObjectKey resolver cache +const keyResolvers = { + // Chart.helpers.core resolveObjectKey should resolve empty key to root object + '': (v)=>v, + // default resolvers + x: (o)=>o.x, + y: (o)=>o.y +}; +/** + * @private + */ function _splitKey(key) { + const parts = key.split('.'); + const keys = []; + let tmp = ''; + for (const part of parts){ + tmp += part; + if (tmp.endsWith('\\')) { + tmp = tmp.slice(0, -1) + '.'; + } else { + keys.push(tmp); + tmp = ''; + } + } + return keys; +} +function _getKeyResolver(key) { + const keys = _splitKey(key); + return (obj)=>{ + for (const k of keys){ + if (k === '') { + break; + } + obj = obj && obj[k]; + } + return obj; + }; +} +function resolveObjectKey(obj, key) { + const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key)); + return resolver(obj); +} +/** + * @private + */ function _capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} +const defined = (value)=>typeof value !== 'undefined'; +const isFunction = (value)=>typeof value === 'function'; +// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384 +const setsEqual = (a, b)=>{ + if (a.size !== b.size) { + return false; + } + for (const item of a){ + if (!b.has(item)) { + return false; + } + } + return true; +}; +/** + * @param e - The event + * @private + */ function _isClickEvent(e) { + return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu'; +} + +/** + * @alias Chart.helpers.math + * @namespace + */ const PI = Math.PI; +const TAU = 2 * PI; +const PITAU = TAU + PI; +const INFINITY = Number.POSITIVE_INFINITY; +const RAD_PER_DEG = PI / 180; +const HALF_PI = PI / 2; +const QUARTER_PI = PI / 4; +const TWO_THIRDS_PI = PI * 2 / 3; +const log10 = Math.log10; +const sign = Math.sign; +function almostEquals(x, y, epsilon) { + return Math.abs(x - y) < epsilon; +} +/** + * Implementation of the nice number algorithm used in determining where axis labels will go + */ 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; +} +/** + * Returns an array of factors sorted from 1 to sqrt(value) + * @private + */ 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; +} +/** + * Verifies that attempting to coerce n to string or number won't throw a TypeError. + */ function isNonPrimitive(n) { + return typeof n === 'symbol' || typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n); +} +function isNumber(n) { + return !isNonPrimitive(n) && !isNaN(parseFloat(n)) && isFinite(n); +} +function almostWhole(x, epsilon) { + const rounded = Math.round(x); + return rounded - epsilon <= x && rounded + epsilon >= x; +} +/** + * @private + */ 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); +} +/** + * Returns the number of decimal places + * i.e. the number of digits after the decimal point, of the value of this Number. + * @param x - A number. + * @returns The number of decimal places. + * @private + */ 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; +} +// Gets the angle from vertical upright to the point about a centre. +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; // make sure the returned angle is in the range of (-PI/2, 3PI/2] + } + 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)); +} +/** + * Shortest distance between angles, in either direction. + * @private + */ function _angleDiff(a, b) { + return (a - b + PITAU) % TAU - PI; +} +/** + * Normalize angle to be between 0 and 2*PI + * @private + */ function _normalizeAngle(a) { + return (a % TAU + TAU) % TAU; +} +/** + * @private + */ 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; +} +/** + * Limit `value` between `min` and `max` + * @param value + * @param min + * @param max + * @private + */ function _limitValue(value, min, max) { + return Math.max(min, Math.min(max, value)); +} +/** + * @param {number} value + * @private + */ function _int16Range(value) { + return _limitValue(value, -32768, 32767); +} +/** + * @param value + * @param start + * @param end + * @param [epsilon] + * @private + */ function _isBetween(value, start, end, epsilon = 1e-6) { + return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon; +} + +function _lookup(table, value, cmp) { + cmp = cmp || ((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 + }; +} +/** + * Binary search + * @param table - the table search. must be sorted! + * @param key - property name for the value in each entry + * @param value - value to find + * @param last - lookup last index + * @private + */ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{ + const ti = table[index][key]; + return ti < value || ti === value && table[index + 1][key] === value; + } : (index)=>table[index][key] < value); +/** + * Reverse binary search + * @param table - the table search. must be sorted! + * @param key - property name for the value in each entry + * @param value - value to find + * @private + */ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value); +/** + * Return subset of `values` between `min` and `max` inclusive. + * Values are assumed to be in sorted order. + * @param values - sorted array of values + * @param min - min value + * @param max - max 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; +} +/** + * @param items + */ function _arrayUnique(items) { + const set = new Set(items); + if (set.size === items.length) { + return items; + } + return Array.from(set); +} + +function fontString(pixelSize, fontStyle, fontFamily) { + return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; +} +/** +* Request animation polyfill +*/ const requestAnimFrame = function() { + if (typeof window === 'undefined') { + return function(callback) { + return callback(); + }; + } + return window.requestAnimationFrame; +}(); +/** + * Throttles calling `fn` once per animation frame + * Latest arguments are used on the actual call + */ function throttled(fn, thisArg) { + let argsToUse = []; + let ticking = false; + return function(...args) { + // Save the args for use later + argsToUse = args; + if (!ticking) { + ticking = true; + requestAnimFrame.call(window, ()=>{ + ticking = false; + fn.apply(thisArg, argsToUse); + }); + } + }; +} +/** + * Debounces calling `fn` for `delay` ms + */ 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; + }; +} +/** + * Converts 'start' to 'left', 'end' to 'right' and others to 'center' + * @private + */ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center'; +/** + * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center` + * @private + */ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2; +/** + * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left` + * @private + */ const _textX = (align, left, right, rtl)=>{ + const check = rtl ? 'left' : 'right'; + return align === check ? right : align === 'center' ? (left + right) / 2 : left; +}; +/** + * Return start and count of visible points. + * @private + */ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) { + const pointCount = points.length; + let start = 0; + let count = pointCount; + if (meta._sorted) { + const { iScale , vScale , _parsed } = meta; + const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null; + const axis = iScale.axis; + const { min , max , minDefined , maxDefined } = iScale.getUserBounds(); + if (minDefined) { + start = Math.min(// @ts-expect-error Need to type _parsed + _lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey + animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo); + if (spanGaps) { + const distanceToDefinedLo = _parsed.slice(0, start + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis])); + start -= Math.max(0, distanceToDefinedLo); + } + start = _limitValue(start, 0, pointCount - 1); + } + if (maxDefined) { + let end = Math.max(// @ts-expect-error Need to type _parsed + _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey + animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1); + if (spanGaps) { + const distanceToDefinedHi = _parsed.slice(end - 1).findIndex((point)=>!isNullOrUndef(point[vScale.axis])); + end += Math.max(0, distanceToDefinedHi); + } + count = _limitValue(end, start, pointCount) - start; + } else { + count = pointCount - start; + } + } + return { + start, + count + }; +} +/** + * Checks if the scale ranges have changed. + * @param {object} meta - dataset meta. + * @returns {boolean} + * @private + */ function _scaleRangesChanged(meta) { + const { xScale , yScale , _scaleRanges } = meta; + const newRanges = { + xmin: xScale.min, + xmax: xScale.max, + ymin: yScale.min, + ymax: yScale.max + }; + if (!_scaleRanges) { + meta._scaleRanges = newRanges; + return true; + } + const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max; + Object.assign(_scaleRanges, newRanges); + return changed; +} + +const atEdge = (t)=>t === 0 || t === 1; +const elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p)); +const elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1; +/** + * Easing functions adapted from Robert Penner's easing equations. + * @namespace Chart.helpers.easing.effects + * @see http://www.robertpenner.com/easing/ + */ const effects = { + linear: (t)=>t, + easeInQuad: (t)=>t * t, + easeOutQuad: (t)=>-t * (t - 2), + easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1), + easeInCubic: (t)=>t * t * t, + easeOutCubic: (t)=>(t -= 1) * t * t + 1, + easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2), + easeInQuart: (t)=>t * t * t * t, + easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1), + easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2), + easeInQuint: (t)=>t * t * t * t * t, + easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1, + easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2), + easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1, + easeOutSine: (t)=>Math.sin(t * HALF_PI), + easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1), + easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)), + easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1, + easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2), + easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1), + easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t), + easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1), + easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3), + easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3), + easeInOutElastic (t) { + const s = 0.1125; + const p = 0.45; + return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p); + }, + easeInBack (t) { + const s = 1.70158; + return t * t * ((s + 1) * t - s); + }, + easeOutBack (t) { + const s = 1.70158; + return (t -= 1) * t * ((s + 1) * t + s) + 1; + }, + easeInOutBack (t) { + let s = 1.70158; + if ((t /= 0.5) < 1) { + return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2); + }, + easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t), + easeOutBounce (t) { + const m = 7.5625; + const d = 2.75; + if (t < 1 / d) { + return m * t * t; + } + if (t < 2 / d) { + return m * (t -= 1.5 / d) * t + 0.75; + } + if (t < 2.5 / d) { + return m * (t -= 2.25 / d) * t + 0.9375; + } + return m * (t -= 2.625 / d) * t + 0.984375; + }, + easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5 +}; + +function isPatternOrGradient(value) { + if (value && typeof value === 'object') { + const type = value.toString(); + return type === '[object CanvasPattern]' || type === '[object CanvasGradient]'; + } + return false; +} +function color(value) { + return isPatternOrGradient(value) ? value : new color$1.Color(value); +} +function getHoverColor(value) { + return isPatternOrGradient(value) ? value : new color$1.Color(value).saturate(0.5).darken(0.1).hexString(); +} + +const numbers = [ + 'x', + 'y', + 'borderWidth', + 'radius', + 'tension' +]; +const colors = [ + 'color', + 'borderColor', + 'backgroundColor' +]; +function applyAnimationsDefaults(defaults) { + defaults.set('animation', { + delay: undefined, + duration: 1000, + easing: 'easeOutQuart', + fn: undefined, + from: undefined, + loop: undefined, + to: undefined, + type: undefined + }); + 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 + } + } + } + }); +} + +function applyLayoutsDefaults(defaults) { + defaults.set('layout', { + autoPadding: true, + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }); +} + +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 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 = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0); + const options = { + notation, + minimumFractionDigits: numDecimal, + maximumFractionDigits: numDecimal + }; + Object.assign(options, this.options.ticks.format); + return formatNumber(tickValue, locale, options); + }, + logarithmic (tickValue, index, ticks) { + if (tickValue === 0) { + return '0'; + } + const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue))); + if ([ + 1, + 2, + 3, + 5, + 10, + 15 + ].includes(remain) || index > 0.8 * ticks.length) { + 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 +}; + +function applyScaleDefaults(defaults) { + defaults.set('scale', { + display: true, + offset: false, + reverse: false, + beginAtZero: false, + bounds: 'ticks', + clip: true, + grace: 0, + grid: { + display: true, + lineWidth: 1, + drawOnChartArea: true, + drawTicks: true, + tickLength: 8, + tickWidth: (_ctx, options)=>options.lineWidth, + tickColor: (_ctx, options)=>options.color, + offset: false + }, + border: { + display: true, + dash: [], + dashOffset: 0.0, + width: 1 + }, + title: { + display: false, + text: '', + padding: { + top: 4, + bottom: 4 + } + }, + ticks: { + minRotation: 0, + maxRotation: 50, + mirror: false, + textStrokeWidth: 0, + textStrokeColor: '', + padding: 3, + display: true, + autoSkip: true, + autoSkipPadding: 3, + labelOffset: 0, + callback: Ticks.formatters.values, + minor: {}, + major: {}, + align: 'center', + crossAlign: 'near', + showLabelBackdrop: false, + backdropColor: 'rgba(255, 255, 255, 0.75)', + backdropPadding: 2 + } + }); + defaults.route('scale.ticks', 'color', '', 'color'); + defaults.route('scale.grid', 'color', '', 'borderColor'); + defaults.route('scale.border', 'color', '', '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' && name !== 'dash' + }); + defaults.describe('scales', { + _fallback: 'scale' + }); + defaults.describe('scale.ticks', { + _scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback', + _indexable: (name)=>name !== 'backdropPadding' + }); +} + +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, _appliers){ + 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, + includeInvisible: false + }; + 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); + this.apply(_appliers); + } + set(scope, values) { + return set(this, scope, values); + } + get(scope) { + return getScope$1(this, scope); + } + describe(scope, values) { + return set(descriptors, scope, values); + } + override(scope, values) { + return set(overrides, scope, values); + } + route(scope, name, targetScope, targetName) { + const scopeObject = getScope$1(this, scope); + const targetScopeObject = getScope$1(this, targetScope); + const privateName = '_' + name; + Object.defineProperties(scopeObject, { + [privateName]: { + value: scopeObject[name], + writable: true + }, + [name]: { + enumerable: true, + get () { + const local = this[privateName]; + const target = targetScopeObject[targetName]; + if (isObject(local)) { + return Object.assign({}, target, local); + } + return valueOrDefault(local, target); + }, + set (value) { + this[privateName] = value; + } + } + }); + } + apply(appliers) { + appliers.forEach((apply)=>apply(this)); + } +} +var defaults = /* #__PURE__ */ new Defaults({ + _scriptable: (name)=>!name.startsWith('on'), + _indexable: (name)=>name !== 'events', + hover: { + _fallback: 'interaction' + }, + interaction: { + _scriptable: false, + _indexable: false + } +}, [ + applyAnimationsDefaults, + applyLayoutsDefaults, + applyScaleDefaults +]); + +/** + * 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 + * @private + */ 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; +} +/** + * @private + */ 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; +} +/** + * @private + */ // eslint-disable-next-line complexity +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]; + // Undefined strings and arrays should not be measured + if (thing !== undefined && thing !== null && !isArray(thing)) { + longest = _measureText(ctx, data, gc, longest, thing); + } else if (isArray(thing)) { + // if it is an array lets measure each element + // to do maybe simplify this function a bit so we can do this more recursively? + for(j = 0, jlen = thing.length; j < jlen; j++){ + nestedThing = thing[j]; + // Undefined strings and arrays should not be measured + 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; +} +/** + * Returns the aligned pixel value to avoid anti-aliasing blur + * @param chart - The chart instance. + * @param pixel - A pixel value. + * @param width - The width of the element. + * @returns The aligned pixel value. + * @private + */ 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; +} +/** + * Clears the entire canvas. + */ function clearCanvas(canvas, ctx) { + if (!ctx && !canvas) { + return; + } + ctx = ctx || canvas.getContext('2d'); + ctx.save(); + // canvas.width and canvas.height do not consider the canvas transform, + // while clearRect does + ctx.resetTransform(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.restore(); +} +function drawPoint(ctx, options, x, y) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + drawPointLegend(ctx, options, x, y, null); +} +// eslint-disable-next-line complexity +function drawPointLegend(ctx, options, x, y, w) { + let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW; + const style = options.pointStyle; + const rotation = options.rotation; + const radius = options.radius; + let rad = (rotation || 0) * RAD_PER_DEG; + if (style && typeof style === 'object') { + type = style.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.save(); + ctx.translate(x, y); + ctx.rotate(rad); + ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); + ctx.restore(); + return; + } + } + if (isNaN(radius) || radius <= 0) { + return; + } + ctx.beginPath(); + switch(style){ + // Default includes circle + default: + if (w) { + ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU); + } else { + ctx.arc(x, y, radius, 0, TAU); + } + ctx.closePath(); + break; + case 'triangle': + width = w ? w / 2 : radius; + ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case 'rectRounded': + // NOTE: the rounded rect implementation changed to use `arc` instead of + // `quadraticCurveTo` since it generates better results when rect is + // almost a circle. 0.516 (instead of 0.5) produces results with visually + // closer proportion to the previous impl and it is inscribed in the + // circle with `radius`. For more details, see the following PRs: + // https://github.com/chartjs/Chart.js/issues/5597 + // https://github.com/chartjs/Chart.js/issues/5858 + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size); + yOffset = Math.sin(rad + QUARTER_PI) * size; + yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size); + ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case 'rect': + if (!rotation) { + size = Math.SQRT1_2 * radius; + width = w ? w / 2 : size; + ctx.rect(x - width, y - size, 2 * width, 2 * size); + break; + } + rad += QUARTER_PI; + /* falls through */ case 'rectRot': + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + ctx.closePath(); + break; + case 'crossRot': + rad += QUARTER_PI; + /* falls through */ case 'cross': + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + break; + case 'star': + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + rad += QUARTER_PI; + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + break; + case 'line': + xOffset = w ? w / 2 : Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + break; + case 'dash': + ctx.moveTo(x, y); + ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius); + break; + case false: + ctx.closePath(); + break; + } + ctx.fill(); + if (options.borderWidth > 0) { + ctx.stroke(); + } +} +/** + * Returns true if the point is inside the rectangle + * @param point - The point to test + * @param area - The rectangle + * @param margin - allowed margin + * @private + */ function _isPointInArea(point, area, margin) { + margin = margin || 0.5; // margin - default is to match rounded decimals + 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(); +} +/** + * @private + */ 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); +} +/** + * @private + */ function _bezierCurveTo(ctx, previous, target, flip) { + if (!previous) { + return ctx.lineTo(target.x, target.y); + } + ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y); +} +function setRenderOpts(ctx, opts) { + if (opts.translation) { + ctx.translate(opts.translation[0], opts.translation[1]); + } + if (!isNullOrUndef(opts.rotation)) { + ctx.rotate(opts.rotation); + } + if (opts.color) { + ctx.fillStyle = opts.color; + } + if (opts.textAlign) { + ctx.textAlign = opts.textAlign; + } + if (opts.textBaseline) { + ctx.textBaseline = opts.textBaseline; + } +} +function decorateText(ctx, x, y, line, opts) { + if (opts.strikethrough || opts.underline) { + /** + * Now that IE11 support has been dropped, we can use more + * of the TextMetrics object. The actual bounding boxes + * are unflagged in Chrome, Firefox, Edge, and Safari so they + * can be safely used. + * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility + */ const metrics = ctx.measureText(line); + const left = x - metrics.actualBoundingBoxLeft; + const right = x + metrics.actualBoundingBoxRight; + const top = y - metrics.actualBoundingBoxAscent; + const bottom = y + metrics.actualBoundingBoxDescent; + const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom; + ctx.strokeStyle = ctx.fillStyle; + ctx.beginPath(); + ctx.lineWidth = opts.decorationWidth || 2; + ctx.moveTo(left, yDecoration); + ctx.lineTo(right, yDecoration); + ctx.stroke(); + } +} +function drawBackdrop(ctx, opts) { + const oldColor = ctx.fillStyle; + ctx.fillStyle = opts.color; + ctx.fillRect(opts.left, opts.top, opts.width, opts.height); + ctx.fillStyle = oldColor; +} +/** + * Render text onto the canvas + */ function renderText(ctx, text, x, y, font, opts = {}) { + const lines = isArray(text) ? text : [ + text + ]; + const stroke = opts.strokeWidth > 0 && opts.strokeColor !== ''; + let i, line; + ctx.save(); + ctx.font = font.string; + setRenderOpts(ctx, opts); + for(i = 0; i < lines.length; ++i){ + line = lines[i]; + if (opts.backdrop) { + drawBackdrop(ctx, opts.backdrop); + } + if (stroke) { + if (opts.strokeColor) { + ctx.strokeStyle = opts.strokeColor; + } + if (!isNullOrUndef(opts.strokeWidth)) { + ctx.lineWidth = opts.strokeWidth; + } + ctx.strokeText(line, x, y, opts.maxWidth); + } + ctx.fillText(line, x, y, opts.maxWidth); + decorateText(ctx, x, y, line, opts); + y += Number(font.lineHeight); + } + ctx.restore(); +} +/** + * Add a path of a rectangle with rounded corners to the current sub-path + * @param ctx - Context + * @param rect - Bounding rect + */ function addRoundedRectPath(ctx, rect) { + const { x , y , w , h , radius } = rect; + // top left arc + ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true); + // line from top left to bottom left + ctx.lineTo(x, y + h - radius.bottomLeft); + // bottom left arc + ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true); + // line from bottom left to bottom right + ctx.lineTo(x + w - radius.bottomRight, y + h); + // bottom right arc + ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true); + // line from bottom right to top right + ctx.lineTo(x + w, y + radius.topRight); + // top right arc + ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true); + // line from top right to top left + ctx.lineTo(x + radius.topLeft, y); +} + +const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/; +const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/; +/** + * @alias Chart.helpers.options + * @namespace + */ /** + * Converts the given line height `value` in pixels for a specific font `size`. + * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). + * @param size - The font size (in pixels) used to resolve relative `value`. + * @returns 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 + */ 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; +} +/** + * Converts the given value into a TRBL object. + * @param value - If a number, set the value to all TRBL component, + * else, if an object, use defined properties and sets undefined ones to 0. + * x / y are shorthands for same value for left/right and top/bottom. + * @returns The padding values (top, right, bottom, left) + * @since 3.0.0 + */ function toTRBL(value) { + return _readValueToProps(value, { + top: 'y', + right: 'x', + bottom: 'y', + left: 'x' + }); +} +/** + * Converts the given value into a TRBL corners object (similar with css border-radius). + * @param value - If a number, set the value to all TRBL corner components, + * else, if an object, use defined properties and sets undefined ones to 0. + * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight) + * @since 3.0.0 + */ function toTRBLCorners(value) { + return _readValueToProps(value, [ + 'topLeft', + 'topRight', + 'bottomLeft', + 'bottomRight' + ]); +} +/** + * Converts the given value into a padding object with pre-computed width/height. + * @param value - If a number, set the value to all TRBL component, + * else, if an object, use defined properties and sets undefined ones to 0. + * x / y are shorthands for same value for left/right and top/bottom. + * @returns The padding values (top, right, bottom, left, width, height) + * @since 2.7.0 + */ function toPadding(value) { + const obj = toTRBL(value); + obj.width = obj.left + obj.right; + obj.height = obj.top + obj.bottom; + return obj; +} +/** + * Parses font options and returns the font object. + * @param options - A object that contains font options to be parsed. + * @param fallback - A object that contains fallback font options. + * @return The font object. + * @private + */ 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 = undefined; + } + 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; +} +/** + * 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 + */ 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; + } + } +} +/** + * @param minmax + * @param grace + * @param beginAtZero + * @private + */ 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); +} + +/** + * Creates a Proxy for resolving raw values for options. + * @param scopes - The option scopes to look for values, in resolution order + * @param prefixes - The prefixes for values, in resolution order. + * @param rootScopes - The root option scopes + * @param fallback - Parent scopes fallback + * @param getTarget - callback for getting the target for changed values + * @returns Proxy + * @private + */ function _createResolver(scopes, prefixes = [ + '' +], rootScopes, fallback, getTarget = ()=>scopes[0]) { + const finalRootScopes = rootScopes || scopes; + if (typeof fallback === 'undefined') { + fallback = _resolve('_fallback', scopes); + } + const cache = { + [Symbol.toStringTag]: 'Object', + _cacheable: true, + _scopes: scopes, + _rootScopes: finalRootScopes, + _fallback: fallback, + _getTarget: getTarget, + override: (scope)=>_createResolver([ + scope, + ...scopes + ], prefixes, finalRootScopes, fallback) + }; + return new Proxy(cache, { + /** + * A trap for the delete operator. + */ deleteProperty (target, prop) { + delete target[prop]; // remove from cache + delete target._keys; // remove cached keys + delete scopes[0][prop]; // remove from top level scope + return true; + }, + /** + * A trap for getting property values. + */ get (target, prop) { + return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target)); + }, + /** + * A trap for Object.getOwnPropertyDescriptor. + * Also used by Object.hasOwnProperty. + */ getOwnPropertyDescriptor (target, prop) { + return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop); + }, + /** + * A trap for Object.getPrototypeOf. + */ getPrototypeOf () { + return Reflect.getPrototypeOf(scopes[0]); + }, + /** + * A trap for the in operator. + */ has (target, prop) { + return getKeysFromAllScopes(target).includes(prop); + }, + /** + * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols. + */ ownKeys (target) { + return getKeysFromAllScopes(target); + }, + /** + * A trap for setting property values. + */ set (target, prop, value) { + const storage = target._storage || (target._storage = getTarget()); + target[prop] = storage[prop] = value; // set to top level scope + cache + delete target._keys; // remove cached keys + return true; + } + }); +} +/** + * Returns an Proxy for resolving option values with context. + * @param proxy - The Proxy returned by `_createResolver` + * @param context - Context object for scriptable/indexable options + * @param subProxy - The proxy provided for scriptable options + * @param descriptorDefaults - Defaults for descriptors + * @private + */ 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, { + /** + * A trap for the delete operator. + */ deleteProperty (target, prop) { + delete target[prop]; // remove from cache + delete proxy[prop]; // remove from proxy + return true; + }, + /** + * A trap for getting property values. + */ get (target, prop, receiver) { + return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver)); + }, + /** + * A trap for Object.getOwnPropertyDescriptor. + * Also used by Object.hasOwnProperty. + */ getOwnPropertyDescriptor (target, prop) { + return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? { + enumerable: true, + configurable: true + } : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop); + }, + /** + * A trap for Object.getPrototypeOf. + */ getPrototypeOf () { + return Reflect.getPrototypeOf(proxy); + }, + /** + * A trap for the in operator. + */ has (target, prop) { + return Reflect.has(proxy, prop); + }, + /** + * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols. + */ ownKeys () { + return Reflect.ownKeys(proxy); + }, + /** + * A trap for setting property values. + */ set (target, prop, value) { + proxy[prop] = value; // set to proxy + delete target[prop]; // remove from cache + return true; + } + }); +} +/** + * @private + */ 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) || prop === 'constructor') { + return target[prop]; + } + const value = resolve(); + // cache the resolved value + target[prop] = value; + return value; +} +function _resolveWithContext(target, prop, receiver) { + const { _proxy , _context , _subProxy , _descriptors: descriptors } = target; + let value = _proxy[prop]; // resolve from proxy + // resolve with context + 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)) { + // if the resolved value is an object, create a sub resolver for it + value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors); + } + return value; +} +function _resolveScriptable(prop, getValue, target, receiver) { + const { _proxy , _context , _subProxy , _stack } = target; + if (_stack.has(prop)) { + throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop); + } + _stack.add(prop); + let value = getValue(_context, _subProxy || receiver); + _stack.delete(prop); + if (needsSubResolver(prop, value)) { + // When scriptable option returns an object, create a resolver on that. + value = createSubResolver(_proxy._scopes, _proxy, prop, value); + } + return value; +} +function _resolveArray(prop, value, target, isIndexable) { + const { _proxy , _context , _subProxy , _descriptors: descriptors } = target; + if (typeof _context.index !== 'undefined' && isIndexable(prop)) { + return value[_context.index % value.length]; + } else if (isObject(value[0])) { + // Array of objects, return array or resolvers + 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 (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) { + // When we reach the descriptor that defines a new _fallback, return that. + // The fallback will resume to that new scope. + return fallback; + } + } else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) { + // Fallback to `false` results to `false`, when falling back to different key. + // For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations` + 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 (typeof fallback !== 'undefined' && 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)) { + // For array of objects, the object is used to store updated values + return value; + } + return target || {}; +} +function _resolveWithPrefixes(prop, prefixes, scopes, proxy) { + let value; + for (const prefix of prefixes){ + value = _resolve(readKey(prefix, prop), scopes); + if (typeof value !== 'undefined') { + return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value; + } + } +} +function _resolve(key, scopes) { + for (const scope of scopes){ + if (!scope) { + continue; + } + const value = scope[key]; + if (typeof value !== 'undefined') { + return value; + } + } +} +function getKeysFromAllScopes(target) { + let keys = target._keys; + if (!keys) { + keys = target._keys = resolveKeysFromAllScopes(target._scopes); + } + return keys; +} +function resolveKeysFromAllScopes(scopes) { + const 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); +} +function _parseObjectDataRadialScale(meta, data, start, count) { + const { iScale } = meta; + const { key ='r' } = 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] = { + r: iScale.parse(resolveObjectKey(item, key), index) + }; + } + return parsed; +} + +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) { + // Props to Rob Spencer at scaled innovation for his post on splining between points + // http://scaledinnovation.com/analytics/splines/aboutSplines.html + // This function must also respect "skipped" points + 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); + // If all points are the same, s01 & s02 will be inf + s01 = isNaN(s01) ? 0 : s01; + s12 = isNaN(s12) ? 0 : s12; + const fa = t * s01; // scaling factor for triangle Ta + 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) + } + }; +} +/** + * Adjust tangents to ensure monotonic properties + */ 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]; + } + } +} +/** + * 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 + */ function splineCurveMonotone(points, indexAxis = 'x') { + const valueAxis = getValueAxis(indexAxis); + const pointsLen = points.length; + const deltaK = Array(pointsLen).fill(0); + const mK = Array(pointsLen); + // Calculate slopes (deltaK) and initialize tangents (mK) + 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]; + // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 + 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); + } + } +} +/** + * @private + */ function _updateBezierControlPoints(points, options, area, loop, indexAxis) { + let i, ilen, point, controlPoints; + // Only consider points that are drawn in case the spanGaps option is used + 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); + } +} + +/** + * @private + */ function _isDomSupported() { + return typeof window !== 'undefined' && typeof document !== 'undefined'; +} +/** + * @private + */ function _getParentNode(domNode) { + let parent = domNode.parentNode; + if (parent && parent.toString() === '[object ShadowRoot]') { + parent = parent.host; + } + return parent; +} +/** + * convert max-width/max-height values that may be percentages into a number + * @private + */ function parseMaxStyle(styleValue, node, parentProperty) { + let valueInPixels; + if (typeof styleValue === 'string') { + valueInPixels = parseInt(styleValue, 10); + if (styleValue.indexOf('%') !== -1) { + // percentage * size in dimension + valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; + } + } else { + valueInPixels = styleValue; + } + return valueInPixels; +} +const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null); +function getStyle(el, property) { + return getComputedStyle(el).getPropertyValue(property); +} +const positions = [ + 'top', + 'right', + 'bottom', + 'left' +]; +function getPositionedStyle(styles, style, suffix) { + const result = {}; + suffix = suffix ? '-' + suffix : ''; + for(let i = 0; i < 4; i++){ + const pos = positions[i]; + result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0; + } + result.width = result.left + result.right; + result.height = result.top + result.bottom; + return result; +} +const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot); +/** + * @param e + * @param canvas + * @returns Canvas position + */ function getCanvasPosition(e, canvas) { + const touches = e.touches; + const source = touches && touches.length ? touches[0] : e; + const { offsetX , offsetY } = source; + let box = false; + let x, y; + if (useOffsetPos(offsetX, offsetY, e.target)) { + x = offsetX; + y = offsetY; + } else { + const rect = canvas.getBoundingClientRect(); + x = source.clientX - rect.left; + y = source.clientY - rect.top; + box = true; + } + return { + x, + y, + box + }; +} +/** + * Gets an event's x, y coordinates, relative to the chart area + * @param event + * @param chart + * @returns x and y coordinates of the event + */ function getRelativePosition(event, chart) { + if ('native' in event) { + return event; + } + const { canvas , currentDevicePixelRatio } = chart; + const style = getComputedStyle(canvas); + const borderBox = style.boxSizing === 'border-box'; + const paddings = getPositionedStyle(style, 'padding'); + const borders = getPositionedStyle(style, 'border', 'width'); + const { x , y , box } = getCanvasPosition(event, canvas); + const xOffset = paddings.left + (box && borders.left); + const yOffset = paddings.top + (box && borders.top); + let { width , height } = chart; + if (borderBox) { + width -= paddings.width + borders.width; + height -= paddings.height + borders.height; + } + return { + x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio), + y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio) + }; +} +function getContainerSize(canvas, width, height) { + let maxWidth, maxHeight; + if (width === undefined || height === undefined) { + const container = canvas && _getParentNode(canvas); + if (!container) { + width = canvas.clientWidth; + height = canvas.clientHeight; + } else { + const rect = container.getBoundingClientRect(); // this is the border box of the container + 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; +// eslint-disable-next-line complexity +function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) { + const style = getComputedStyle(canvas); + const margins = getPositionedStyle(style, 'margin'); + const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY; + const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY; + const containerSize = getContainerSize(canvas, bbWidth, bbHeight); + let { width , height } = containerSize; + if (style.boxSizing === 'content-box') { + const borders = getPositionedStyle(style, 'border', 'width'); + const paddings = getPositionedStyle(style, 'padding'); + width -= paddings.width + borders.width; + height -= paddings.height + borders.height; + } + width = Math.max(0, width - margins.width); + height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height); + width = round1(Math.min(width, maxWidth, containerSize.maxWidth)); + height = round1(Math.min(height, maxHeight, containerSize.maxHeight)); + if (width && !height) { + // https://github.com/chartjs/Chart.js/issues/4659 + // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default) + height = round1(width / 2); + } + const maintainHeight = bbWidth !== undefined || bbHeight !== undefined; + if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) { + height = containerSize.height; + width = round1(Math.floor(height * aspectRatio)); + } + return { + width, + height + }; +} +/** + * @param chart + * @param forceRatio + * @param forceStyle + * @returns True if the canvas context size or transformation has changed. + */ function retinaScale(chart, forceRatio, forceStyle) { + const pixelRatio = forceRatio || 1; + const deviceHeight = round1(chart.height * pixelRatio); + const deviceWidth = round1(chart.width * pixelRatio); + chart.height = round1(chart.height); + chart.width = round1(chart.width); + const canvas = chart.canvas; + // If no style has been set on the canvas, the render size is used as display size, + // making the chart visually bigger, so let's enforce it to the "correct" values. + // See https://github.com/chartjs/Chart.js/issues/3575 + 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; +} +/** + * Detects support for options object argument in addEventListener. + * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support + * @private + */ const supportsEventListenerOptions = function() { + let passiveSupported = false; + try { + const options = { + get passive () { + passiveSupported = true; + return false; + } + }; + if (_isDomSupported()) { + window.addEventListener('test', null, options); + window.removeEventListener('test', null, options); + } + } catch (e) { + // continue regardless of error + } + return passiveSupported; +}(); +/** + * The "used" size is the final value of a dimension property after all calculations have + * been performed. This method uses the computed style of `element` but returns undefined + * if the computed style is not expressed in pixels. That can happen in some cases where + * `element` has a size relative to its parent and this last one is not yet displayed, + * for example because of `display: none` on a parent node. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * @returns Size in pixels or undefined if unknown. + */ function readUsedSize(element, property) { + const value = getStyle(element, property); + const matches = value && value.match(/^(\d+)(\.\d+)?px$/); + return matches ? +matches[1] : undefined; +} + +/** + * @private + */ function _pointInLine(p1, p2, t, mode) { + return { + x: p1.x + t * (p2.x - p1.x), + y: p1.y + t * (p2.y - p1.y) + }; +} +/** + * @private + */ 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 + }; +} +/** + * @private + */ function _bezierInterpolation(p1, p2, t, mode) { + const cp1 = { + x: p1.cp2x, + y: p1.cp2y + }; + const cp2 = { + x: p2.cp1x, + y: p2.cp1y + }; + const a = _pointInLine(p1, cp1, t); + const b = _pointInLine(cp1, cp2, t); + const c = _pointInLine(cp2, p2, t); + const d = _pointInLine(a, b, t); + const e = _pointInLine(b, c, t); + return _pointInLine(d, e, t); +} + +const getRightToLeftAdapter = function(rectX, width) { + return { + x (x) { + return rectX + rectX + width - x; + }, + setWidth (w) { + width = w; + }, + textAlign (align) { + if (align === 'center') { + return align; + } + return align === 'right' ? 'left' : 'right'; + }, + xPlus (x, value) { + return x - value; + }, + leftForLtr (x, itemWidth) { + return x - itemWidth; + } + }; +}; +const getLeftToRightAdapter = function() { + return { + x (x) { + return x; + }, + setWidth (w) {}, + textAlign (align) { + return align; + }, + xPlus (x, value) { + return x + value; + }, + leftForLtr (x, _itemWidth) { + return x; + } + }; +}; +function getRtlAdapter(rtl, rectX, width) { + return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter(); +} +function overrideTextDirection(ctx, direction) { + let style, original; + if (direction === 'ltr' || direction === 'rtl') { + style = ctx.canvas.style; + original = [ + style.getPropertyValue('direction'), + style.getPropertyPriority('direction') + ]; + style.setProperty('direction', direction, 'important'); + ctx.prevTextDirection = original; + } +} +function restoreTextDirection(ctx, original) { + if (original !== 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) { + if (!prevStyle) { + return false; + } + const cache = []; + const replacer = function(key, value) { + if (!isPatternOrGradient(value)) { + return value; + } + if (!cache.includes(value)) { + cache.push(value); + } + return cache.indexOf(value); + }; + return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer); +} + +function getSizeForArea(scale, chartArea, field) { + return scale.options.clip ? scale[field] : chartArea[field]; +} +function getDatasetArea(meta, chartArea) { + const { xScale , yScale } = meta; + if (xScale && yScale) { + return { + left: getSizeForArea(xScale, chartArea, 'left'), + right: getSizeForArea(xScale, chartArea, 'right'), + top: getSizeForArea(yScale, chartArea, 'top'), + bottom: getSizeForArea(yScale, chartArea, 'bottom') + }; + } + return chartArea; +} +function getDatasetClipArea(chart, meta) { + const clip = meta._clip; + if (clip.disabled) { + return false; + } + const area = getDatasetArea(meta, chart.chartArea); + return { + left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left), + right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right), + top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top), + bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom) + }; +} + +exports.HALF_PI = HALF_PI; +exports.INFINITY = INFINITY; +exports.PI = PI; +exports.PITAU = PITAU; +exports.QUARTER_PI = QUARTER_PI; +exports.RAD_PER_DEG = RAD_PER_DEG; +exports.TAU = TAU; +exports.TWO_THIRDS_PI = TWO_THIRDS_PI; +exports.Ticks = Ticks; +exports._addGrace = _addGrace; +exports._alignPixel = _alignPixel; +exports._alignStartEnd = _alignStartEnd; +exports._angleBetween = _angleBetween; +exports._angleDiff = _angleDiff; +exports._arrayUnique = _arrayUnique; +exports._attachContext = _attachContext; +exports._bezierCurveTo = _bezierCurveTo; +exports._bezierInterpolation = _bezierInterpolation; +exports._boundSegment = _boundSegment; +exports._boundSegments = _boundSegments; +exports._capitalize = _capitalize; +exports._computeSegments = _computeSegments; +exports._createResolver = _createResolver; +exports._decimalPlaces = _decimalPlaces; +exports._deprecated = _deprecated; +exports._descriptors = _descriptors; +exports._elementsEqual = _elementsEqual; +exports._factorize = _factorize; +exports._filterBetween = _filterBetween; +exports._getParentNode = _getParentNode; +exports._getStartAndCountOfVisiblePoints = _getStartAndCountOfVisiblePoints; +exports._int16Range = _int16Range; +exports._isBetween = _isBetween; +exports._isClickEvent = _isClickEvent; +exports._isDomSupported = _isDomSupported; +exports._isPointInArea = _isPointInArea; +exports._limitValue = _limitValue; +exports._longestText = _longestText; +exports._lookup = _lookup; +exports._lookupByKey = _lookupByKey; +exports._measureText = _measureText; +exports._merger = _merger; +exports._mergerIf = _mergerIf; +exports._normalizeAngle = _normalizeAngle; +exports._parseObjectDataRadialScale = _parseObjectDataRadialScale; +exports._pointInLine = _pointInLine; +exports._readValueToProps = _readValueToProps; +exports._rlookupByKey = _rlookupByKey; +exports._scaleRangesChanged = _scaleRangesChanged; +exports._setMinAndMaxByKey = _setMinAndMaxByKey; +exports._splitKey = _splitKey; +exports._steppedInterpolation = _steppedInterpolation; +exports._steppedLineTo = _steppedLineTo; +exports._textX = _textX; +exports._toLeftRightCenter = _toLeftRightCenter; +exports._updateBezierControlPoints = _updateBezierControlPoints; +exports.addRoundedRectPath = addRoundedRectPath; +exports.almostEquals = almostEquals; +exports.almostWhole = almostWhole; +exports.callback = callback; +exports.clearCanvas = clearCanvas; +exports.clipArea = clipArea; +exports.clone = clone; +exports.color = color; +exports.createContext = createContext; +exports.debounce = debounce; +exports.defaults = defaults; +exports.defined = defined; +exports.descriptors = descriptors; +exports.distanceBetweenPoints = distanceBetweenPoints; +exports.drawPoint = drawPoint; +exports.drawPointLegend = drawPointLegend; +exports.each = each; +exports.effects = effects; +exports.finiteOrDefault = finiteOrDefault; +exports.fontString = fontString; +exports.formatNumber = formatNumber; +exports.getAngleFromPoint = getAngleFromPoint; +exports.getDatasetClipArea = getDatasetClipArea; +exports.getHoverColor = getHoverColor; +exports.getMaximumSize = getMaximumSize; +exports.getRelativePosition = getRelativePosition; +exports.getRtlAdapter = getRtlAdapter; +exports.getStyle = getStyle; +exports.isArray = isArray; +exports.isFunction = isFunction; +exports.isNullOrUndef = isNullOrUndef; +exports.isNumber = isNumber; +exports.isNumberFinite = isNumberFinite; +exports.isObject = isObject; +exports.isPatternOrGradient = isPatternOrGradient; +exports.listenArrayEvents = listenArrayEvents; +exports.log10 = log10; +exports.merge = merge; +exports.mergeIf = mergeIf; +exports.niceNum = niceNum; +exports.noop = noop; +exports.overrideTextDirection = overrideTextDirection; +exports.overrides = overrides; +exports.readUsedSize = readUsedSize; +exports.renderText = renderText; +exports.requestAnimFrame = requestAnimFrame; +exports.resolve = resolve; +exports.resolveObjectKey = resolveObjectKey; +exports.restoreTextDirection = restoreTextDirection; +exports.retinaScale = retinaScale; +exports.setsEqual = setsEqual; +exports.sign = sign; +exports.splineCurve = splineCurve; +exports.splineCurveMonotone = splineCurveMonotone; +exports.supportsEventListenerOptions = supportsEventListenerOptions; +exports.throttled = throttled; +exports.toDegrees = toDegrees; +exports.toDimension = toDimension; +exports.toFont = toFont; +exports.toFontString = toFontString; +exports.toLineHeight = toLineHeight; +exports.toPadding = toPadding; +exports.toPercentage = toPercentage; +exports.toRadians = toRadians; +exports.toTRBL = toTRBL; +exports.toTRBLCorners = toTRBLCorners; +exports.uid = uid; +exports.unclipArea = unclipArea; +exports.unlistenArrayEvents = unlistenArrayEvents; +exports.valueOrDefault = valueOrDefault; +//# sourceMappingURL=helpers.dataset.cjs.map diff --git a/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs.map b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs.map new file mode 100644 index 00000000..ca7367c3 --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs.map @@ -0,0 +1 @@ +{"version":3,"file":"helpers.dataset.cjs","sources":["../../src/helpers/helpers.core.ts","../../src/helpers/helpers.math.ts","../../src/helpers/helpers.collection.ts","../../src/helpers/helpers.extras.ts","../../src/helpers/helpers.easing.ts","../../src/helpers/helpers.color.ts","../../src/core/core.animations.defaults.js","../../src/core/core.layouts.defaults.js","../../src/helpers/helpers.intl.ts","../../src/core/core.ticks.js","../../src/core/core.scale.defaults.js","../../src/core/core.defaults.js","../../src/helpers/helpers.canvas.ts","../../src/helpers/helpers.options.ts","../../src/helpers/helpers.config.ts","../../src/helpers/helpers.curve.ts","../../src/helpers/helpers.dom.ts","../../src/helpers/helpers.interpolation.ts","../../src/helpers/helpers.rtl.ts","../../src/helpers/helpers.segment.js","../../src/helpers/helpers.dataset.ts"],"sourcesContent":["/**\n * @namespace Chart.helpers\n */\n\nimport type {AnyObject} from '../types/basic.js';\nimport type {ActiveDataPoint, ChartEvent} from '../types/index.js';\n\n/**\n * An empty function that can be used, for example, for optional callback.\n */\nexport function noop() {\n /* noop */\n}\n\n/**\n * Returns a unique id, sequentially generated from a global variable.\n */\nexport const uid = (() => {\n let id = 0;\n return () => id++;\n})();\n\n/**\n * Returns true if `value` is neither null nor undefined, else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport function isNullOrUndef(value: unknown): value is null | undefined {\n return value === null || value === undefined;\n}\n\n/**\n * Returns true if `value` is an array (including typed arrays), else returns false.\n * @param value - The value to test.\n * @function\n */\nexport function isArray<T = unknown>(value: unknown): value is T[] {\n if (Array.isArray && Array.isArray(value)) {\n return true;\n }\n const type = Object.prototype.toString.call(value);\n if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {\n return true;\n }\n return false;\n}\n\n/**\n * Returns true if `value` is an object (excluding null), else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport function isObject(value: unknown): value is AnyObject {\n return value !== null && Object.prototype.toString.call(value) === '[object Object]';\n}\n\n/**\n * Returns true if `value` is a finite number, else returns false\n * @param value - The value to test.\n */\nfunction isNumberFinite(value: unknown): value is number {\n return (typeof value === 'number' || value instanceof Number) && isFinite(+value);\n}\nexport {\n isNumberFinite as isFinite,\n};\n\n/**\n * Returns `value` if finite, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is not finite.\n */\nexport function finiteOrDefault(value: unknown, defaultValue: number) {\n return isNumberFinite(value) ? value : defaultValue;\n}\n\n/**\n * Returns `value` if defined, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is undefined.\n */\nexport function valueOrDefault<T>(value: T | undefined, defaultValue: T) {\n return typeof value === 'undefined' ? defaultValue : value;\n}\n\nexport const toPercentage = (value: number | string, dimension: number) =>\n typeof value === 'string' && value.endsWith('%') ?\n parseFloat(value) / 100\n : +value / dimension;\n\nexport const toDimension = (value: number | string, dimension: number) =>\n typeof value === 'string' && value.endsWith('%') ?\n parseFloat(value) / 100 * dimension\n : +value;\n\n/**\n * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n * @param fn - The function to call.\n * @param args - The arguments with which `fn` should be called.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n */\nexport function callback<T extends (this: TA, ...restArgs: unknown[]) => R, TA, R>(\n fn: T | undefined,\n args: unknown[],\n thisArg?: TA\n): R | undefined {\n if (fn && typeof fn.call === 'function') {\n return fn.apply(thisArg, args);\n }\n}\n\n/**\n * Note(SB) for performance sake, this method should only be used when loopable type\n * is unknown or in none intensive code (not called often and small loopable). Else\n * it's preferable to use a regular for() loop and save extra function calls.\n * @param loopable - The object or array to be iterated.\n * @param fn - The function to call for each item.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n * @param [reverse] - If true, iterates backward on the loopable.\n */\nexport function each<T, TA>(\n loopable: Record<string, T>,\n fn: (this: TA, v: T, i: string) => void,\n thisArg?: TA,\n reverse?: boolean\n): void;\nexport function each<T, TA>(\n loopable: T[],\n fn: (this: TA, v: T, i: number) => void,\n thisArg?: TA,\n reverse?: boolean\n): void;\nexport function each<T, TA>(\n loopable: T[] | Record<string, T>,\n fn: (this: TA, v: T, i: any) => void,\n thisArg?: TA,\n reverse?: boolean\n) {\n let i: number, len: number, keys: string[];\n if (isArray(loopable)) {\n len = loopable.length;\n if (reverse) {\n for (i = len - 1; i >= 0; i--) {\n fn.call(thisArg, loopable[i], i);\n }\n } else {\n for (i = 0; i < len; i++) {\n fn.call(thisArg, loopable[i], i);\n }\n }\n } else if (isObject(loopable)) {\n keys = Object.keys(loopable);\n len = keys.length;\n for (i = 0; i < len; i++) {\n fn.call(thisArg, loopable[keys[i]], keys[i]);\n }\n }\n}\n\n/**\n * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n * @param a0 - The array to compare\n * @param a1 - The array to compare\n * @private\n */\nexport function _elementsEqual(a0: ActiveDataPoint[], a1: ActiveDataPoint[]) {\n let i: number, ilen: number, v0: ActiveDataPoint, v1: ActiveDataPoint;\n\n if (!a0 || !a1 || a0.length !== a1.length) {\n return false;\n }\n\n for (i = 0, ilen = a0.length; i < ilen; ++i) {\n v0 = a0[i];\n v1 = a1[i];\n\n if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Returns a deep copy of `source` without keeping references on objects and arrays.\n * @param source - The value to clone.\n */\nexport function clone<T>(source: T): T {\n if (isArray(source)) {\n return source.map(clone) as unknown as T;\n }\n\n if (isObject(source)) {\n const target = Object.create(null);\n const keys = Object.keys(source);\n const klen = keys.length;\n let k = 0;\n\n for (; k < klen; ++k) {\n target[keys[k]] = clone(source[keys[k]]);\n }\n\n return target;\n }\n\n return source;\n}\n\nfunction isValidKey(key: string) {\n return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;\n}\n\n/**\n * The default merger when Chart.helpers.merge is called without merger option.\n * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.\n * @private\n */\nexport function _merger(key: string, target: AnyObject, source: AnyObject, options: AnyObject) {\n if (!isValidKey(key)) {\n return;\n }\n\n const tval = target[key];\n const sval = source[key];\n\n if (isObject(tval) && isObject(sval)) {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n merge(tval, sval, options);\n } else {\n target[key] = clone(sval);\n }\n}\n\nexport interface MergeOptions {\n merger?: (key: string, target: AnyObject, source: AnyObject, options?: AnyObject) => void;\n}\n\n/**\n * Recursively deep copies `source` properties into `target` with the given `options`.\n * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n * @param target - The target object in which all sources are merged into.\n * @param source - Object(s) to merge into `target`.\n * @param [options] - Merging options:\n * @param [options.merger] - The merge method (key, target, source, options)\n * @returns The `target` object.\n */\nexport function merge<T>(target: T, source: [], options?: MergeOptions): T;\nexport function merge<T, S1>(target: T, source: S1, options?: MergeOptions): T & S1;\nexport function merge<T, S1>(target: T, source: [S1], options?: MergeOptions): T & S1;\nexport function merge<T, S1, S2>(target: T, source: [S1, S2], options?: MergeOptions): T & S1 & S2;\nexport function merge<T, S1, S2, S3>(target: T, source: [S1, S2, S3], options?: MergeOptions): T & S1 & S2 & S3;\nexport function merge<T, S1, S2, S3, S4>(\n target: T,\n source: [S1, S2, S3, S4],\n options?: MergeOptions\n): T & S1 & S2 & S3 & S4;\nexport function merge<T>(target: T, source: AnyObject[], options?: MergeOptions): AnyObject;\nexport function merge<T>(target: T, source: AnyObject[], options?: MergeOptions): AnyObject {\n const sources = isArray(source) ? source : [source];\n const ilen = sources.length;\n\n if (!isObject(target)) {\n return target as AnyObject;\n }\n\n options = options || {};\n const merger = options.merger || _merger;\n let current: AnyObject;\n\n for (let i = 0; i < ilen; ++i) {\n current = sources[i];\n if (!isObject(current)) {\n continue;\n }\n\n const keys = Object.keys(current);\n for (let k = 0, klen = keys.length; k < klen; ++k) {\n merger(keys[k], target, current, options as AnyObject);\n }\n }\n\n return target;\n}\n\n/**\n * Recursively deep copies `source` properties into `target` *only* if not defined in target.\n * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n * @param target - The target object in which all sources are merged into.\n * @param source - Object(s) to merge into `target`.\n * @returns The `target` object.\n */\nexport function mergeIf<T>(target: T, source: []): T;\nexport function mergeIf<T, S1>(target: T, source: S1): T & S1;\nexport function mergeIf<T, S1>(target: T, source: [S1]): T & S1;\nexport function mergeIf<T, S1, S2>(target: T, source: [S1, S2]): T & S1 & S2;\nexport function mergeIf<T, S1, S2, S3>(target: T, source: [S1, S2, S3]): T & S1 & S2 & S3;\nexport function mergeIf<T, S1, S2, S3, S4>(target: T, source: [S1, S2, S3, S4]): T & S1 & S2 & S3 & S4;\nexport function mergeIf<T>(target: T, source: AnyObject[]): AnyObject;\nexport function mergeIf<T>(target: T, source: AnyObject[]): AnyObject {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n return merge<T>(target, source, {merger: _mergerIf});\n}\n\n/**\n * Merges source[key] in target[key] only if target[key] is undefined.\n * @private\n */\nexport function _mergerIf(key: string, target: AnyObject, source: AnyObject) {\n if (!isValidKey(key)) {\n return;\n }\n\n const tval = target[key];\n const sval = source[key];\n\n if (isObject(tval) && isObject(sval)) {\n mergeIf(tval, sval);\n } else if (!Object.prototype.hasOwnProperty.call(target, key)) {\n target[key] = clone(sval);\n }\n}\n\n/**\n * @private\n */\nexport function _deprecated(scope: string, value: unknown, previous: string, current: string) {\n if (value !== undefined) {\n console.warn(scope + ': \"' + previous +\n '\" is deprecated. Please use \"' + current + '\" instead');\n }\n}\n\n// resolveObjectKey resolver cache\nconst keyResolvers = {\n // Chart.helpers.core resolveObjectKey should resolve empty key to root object\n '': v => v,\n // default resolvers\n x: o => o.x,\n y: o => o.y\n};\n\n/**\n * @private\n */\nexport function _splitKey(key: string) {\n const parts = key.split('.');\n const keys: string[] = [];\n let tmp = '';\n for (const part of parts) {\n tmp += part;\n if (tmp.endsWith('\\\\')) {\n tmp = tmp.slice(0, -1) + '.';\n } else {\n keys.push(tmp);\n tmp = '';\n }\n }\n return keys;\n}\n\nfunction _getKeyResolver(key: string) {\n const keys = _splitKey(key);\n return obj => {\n for (const k of keys) {\n if (k === '') {\n // For backward compatibility:\n // Chart.helpers.core resolveObjectKey should break at empty key\n break;\n }\n obj = obj && obj[k];\n }\n return obj;\n };\n}\n\nexport function resolveObjectKey(obj: AnyObject, key: string): any {\n const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));\n return resolver(obj);\n}\n\n/**\n * @private\n */\nexport function _capitalize(str: string) {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n\nexport const defined = (value: unknown) => typeof value !== 'undefined';\n\nexport const isFunction = (value: unknown): value is (...args: any[]) => any => typeof value === 'function';\n\n// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384\nexport const setsEqual = <T>(a: Set<T>, b: Set<T>) => {\n if (a.size !== b.size) {\n return false;\n }\n\n for (const item of a) {\n if (!b.has(item)) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * @param e - The event\n * @private\n */\nexport function _isClickEvent(e: ChartEvent) {\n return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';\n}\n","import type {Point} from '../types/geometric.js';\nimport {isFinite as isFiniteNumber} from './helpers.core.js';\n\n/**\n * @alias Chart.helpers.math\n * @namespace\n */\n\nexport const PI = Math.PI;\nexport const TAU = 2 * PI;\nexport const PITAU = TAU + PI;\nexport const INFINITY = Number.POSITIVE_INFINITY;\nexport const RAD_PER_DEG = PI / 180;\nexport const HALF_PI = PI / 2;\nexport const QUARTER_PI = PI / 4;\nexport const TWO_THIRDS_PI = PI * 2 / 3;\n\nexport const log10 = Math.log10;\nexport const sign = Math.sign;\n\nexport function almostEquals(x: number, y: number, epsilon: number) {\n return Math.abs(x - y) < epsilon;\n}\n\n/**\n * Implementation of the nice number algorithm used in determining where axis labels will go\n */\nexport function niceNum(range: number) {\n const roundedRange = Math.round(range);\n range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;\n const niceRange = Math.pow(10, Math.floor(log10(range)));\n const fraction = range / niceRange;\n const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;\n return niceFraction * niceRange;\n}\n\n/**\n * Returns an array of factors sorted from 1 to sqrt(value)\n * @private\n */\nexport function _factorize(value: number) {\n const result: number[] = [];\n const sqrt = Math.sqrt(value);\n let i: number;\n\n for (i = 1; i < sqrt; i++) {\n if (value % i === 0) {\n result.push(i);\n result.push(value / i);\n }\n }\n if (sqrt === (sqrt | 0)) { // if value is a square number\n result.push(sqrt);\n }\n\n result.sort((a, b) => a - b).pop();\n return result;\n}\n\n/**\n * Verifies that attempting to coerce n to string or number won't throw a TypeError.\n */\nfunction isNonPrimitive(n: unknown) {\n return typeof n === 'symbol' || (typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n));\n}\n\nexport function isNumber(n: unknown): n is number {\n return !isNonPrimitive(n) && !isNaN(parseFloat(n as string)) && isFinite(n as number);\n}\n\nexport function almostWhole(x: number, epsilon: number) {\n const rounded = Math.round(x);\n return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x);\n}\n\n/**\n * @private\n */\nexport function _setMinAndMaxByKey(\n array: Record<string, number>[],\n target: { min: number, max: number },\n property: string\n) {\n let i: number, ilen: number, value: number;\n\n for (i = 0, ilen = array.length; i < ilen; i++) {\n value = array[i][property];\n if (!isNaN(value)) {\n target.min = Math.min(target.min, value);\n target.max = Math.max(target.max, value);\n }\n }\n}\n\nexport function toRadians(degrees: number) {\n return degrees * (PI / 180);\n}\n\nexport function toDegrees(radians: number) {\n return radians * (180 / PI);\n}\n\n/**\n * Returns the number of decimal places\n * i.e. the number of digits after the decimal point, of the value of this Number.\n * @param x - A number.\n * @returns The number of decimal places.\n * @private\n */\nexport function _decimalPlaces(x: number) {\n if (!isFiniteNumber(x)) {\n return;\n }\n let e = 1;\n let p = 0;\n while (Math.round(x * e) / e !== x) {\n e *= 10;\n p++;\n }\n return p;\n}\n\n// Gets the angle from vertical upright to the point about a centre.\nexport function getAngleFromPoint(\n centrePoint: Point,\n anglePoint: Point\n) {\n const distanceFromXCenter = anglePoint.x - centrePoint.x;\n const distanceFromYCenter = anglePoint.y - centrePoint.y;\n const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);\n\n let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);\n\n if (angle < (-0.5 * PI)) {\n angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]\n }\n\n return {\n angle,\n distance: radialDistanceFromCenter\n };\n}\n\nexport function distanceBetweenPoints(pt1: Point, pt2: Point) {\n return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));\n}\n\n/**\n * Shortest distance between angles, in either direction.\n * @private\n */\nexport function _angleDiff(a: number, b: number) {\n return (a - b + PITAU) % TAU - PI;\n}\n\n/**\n * Normalize angle to be between 0 and 2*PI\n * @private\n */\nexport function _normalizeAngle(a: number) {\n return (a % TAU + TAU) % TAU;\n}\n\n/**\n * @private\n */\nexport function _angleBetween(angle: number, start: number, end: number, sameAngleIsFullCircle?: boolean) {\n const a = _normalizeAngle(angle);\n const s = _normalizeAngle(start);\n const e = _normalizeAngle(end);\n const angleToStart = _normalizeAngle(s - a);\n const angleToEnd = _normalizeAngle(e - a);\n const startToAngle = _normalizeAngle(a - s);\n const endToAngle = _normalizeAngle(a - e);\n return a === s || a === e || (sameAngleIsFullCircle && s === e)\n || (angleToStart > angleToEnd && startToAngle < endToAngle);\n}\n\n/**\n * Limit `value` between `min` and `max`\n * @param value\n * @param min\n * @param max\n * @private\n */\nexport function _limitValue(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value));\n}\n\n/**\n * @param {number} value\n * @private\n */\nexport function _int16Range(value: number) {\n return _limitValue(value, -32768, 32767);\n}\n\n/**\n * @param value\n * @param start\n * @param end\n * @param [epsilon]\n * @private\n */\nexport function _isBetween(value: number, start: number, end: number, epsilon = 1e-6) {\n return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;\n}\n","import {_capitalize} from './helpers.core.js';\n\n/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param value - value to find\n * @param cmp\n * @private\n */\nexport function _lookup(\n table: number[],\n value: number,\n cmp?: (value: number) => boolean\n): {lo: number, hi: number};\nexport function _lookup<T>(\n table: T[],\n value: number,\n cmp: (value: number) => boolean\n): {lo: number, hi: number};\nexport function _lookup(\n table: unknown[],\n value: number,\n cmp?: (value: number) => boolean\n) {\n cmp = cmp || ((index) => table[index] < value);\n let hi = table.length - 1;\n let lo = 0;\n let mid: number;\n\n while (hi - lo > 1) {\n mid = (lo + hi) >> 1;\n if (cmp(mid)) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n return {lo, hi};\n}\n\n/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @param last - lookup last index\n * @private\n */\nexport const _lookupByKey = (\n table: Record<string, number>[],\n key: string,\n value: number,\n last?: boolean\n) =>\n _lookup(table, value, last\n ? index => {\n const ti = table[index][key];\n return ti < value || ti === value && table[index + 1][key] === value;\n }\n : index => table[index][key] < value);\n\n/**\n * Reverse binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @private\n */\nexport const _rlookupByKey = (\n table: Record<string, number>[],\n key: string,\n value: number\n) =>\n _lookup(table, value, index => table[index][key] >= value);\n\n/**\n * Return subset of `values` between `min` and `max` inclusive.\n * Values are assumed to be in sorted order.\n * @param values - sorted array of values\n * @param min - min value\n * @param max - max value\n */\nexport function _filterBetween(values: number[], min: number, max: number) {\n let start = 0;\n let end = values.length;\n\n while (start < end && values[start] < min) {\n start++;\n }\n while (end > start && values[end - 1] > max) {\n end--;\n }\n\n return start > 0 || end < values.length\n ? values.slice(start, end)\n : values;\n}\n\nconst arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'] as const;\n\nexport interface ArrayListener<T> {\n _onDataPush?(...item: T[]): void;\n _onDataPop?(): void;\n _onDataShift?(): void;\n _onDataSplice?(index: number, deleteCount: number, ...items: T[]): void;\n _onDataUnshift?(...item: T[]): void;\n}\n\n/**\n * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',\n * 'unshift') and notify the listener AFTER the array has been altered. Listeners are\n * called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments.\n */\nexport function listenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;\nexport function listenArrayEvents(array, listener) {\n if (array._chartjs) {\n array._chartjs.listeners.push(listener);\n return;\n }\n\n Object.defineProperty(array, '_chartjs', {\n configurable: true,\n enumerable: false,\n value: {\n listeners: [listener]\n }\n });\n\n arrayEvents.forEach((key) => {\n const method = '_onData' + _capitalize(key);\n const base = array[key];\n\n Object.defineProperty(array, key, {\n configurable: true,\n enumerable: false,\n value(...args) {\n const res = base.apply(this, args);\n\n array._chartjs.listeners.forEach((object) => {\n if (typeof object[method] === 'function') {\n object[method](...args);\n }\n });\n\n return res;\n }\n });\n });\n}\n\n\n/**\n * Removes the given array event listener and cleanup extra attached properties (such as\n * the _chartjs stub and overridden methods) if array doesn't have any more listeners.\n */\nexport function unlistenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;\nexport function unlistenArrayEvents(array, listener) {\n const stub = array._chartjs;\n if (!stub) {\n return;\n }\n\n const listeners = stub.listeners;\n const index = listeners.indexOf(listener);\n if (index !== -1) {\n listeners.splice(index, 1);\n }\n\n if (listeners.length > 0) {\n return;\n }\n\n arrayEvents.forEach((key) => {\n delete array[key];\n });\n\n delete array._chartjs;\n}\n\n/**\n * @param items\n */\nexport function _arrayUnique<T>(items: T[]) {\n const set = new Set<T>(items);\n\n if (set.size === items.length) {\n return items;\n }\n\n return Array.from(set);\n}\n","import type {ChartMeta, PointElement} from '../types/index.js';\n\nimport {_limitValue} from './helpers.math.js';\nimport {_lookupByKey} from './helpers.collection.js';\nimport {isNullOrUndef} from './helpers.core.js';\n\nexport function fontString(pixelSize: number, fontStyle: string, fontFamily: string) {\n return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;\n}\n\n/**\n* Request animation polyfill\n*/\nexport const requestAnimFrame = (function() {\n if (typeof window === 'undefined') {\n return function(callback) {\n return callback();\n };\n }\n return window.requestAnimationFrame;\n}());\n\n/**\n * Throttles calling `fn` once per animation frame\n * Latest arguments are used on the actual call\n */\nexport function throttled<TArgs extends Array<any>>(\n fn: (...args: TArgs) => void,\n thisArg: any,\n) {\n let argsToUse = [] as TArgs;\n let ticking = false;\n\n return function(...args: TArgs) {\n // Save the args for use later\n argsToUse = args;\n if (!ticking) {\n ticking = true;\n requestAnimFrame.call(window, () => {\n ticking = false;\n fn.apply(thisArg, argsToUse);\n });\n }\n };\n}\n\n/**\n * Debounces calling `fn` for `delay` ms\n */\nexport function debounce<TArgs extends Array<any>>(fn: (...args: TArgs) => void, delay: number) {\n let timeout;\n return function(...args: TArgs) {\n if (delay) {\n clearTimeout(timeout);\n timeout = setTimeout(fn, delay, args);\n } else {\n fn.apply(this, args);\n }\n return delay;\n };\n}\n\n/**\n * Converts 'start' to 'left', 'end' to 'right' and others to 'center'\n * @private\n */\nexport const _toLeftRightCenter = (align: 'start' | 'end' | 'center') => align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';\n\n/**\n * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`\n * @private\n */\nexport const _alignStartEnd = (align: 'start' | 'end' | 'center', start: number, end: number) => align === 'start' ? start : align === 'end' ? end : (start + end) / 2;\n\n/**\n * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`\n * @private\n */\nexport const _textX = (align: 'left' | 'right' | 'center', left: number, right: number, rtl: boolean) => {\n const check = rtl ? 'left' : 'right';\n return align === check ? right : align === 'center' ? (left + right) / 2 : left;\n};\n\n/**\n * Return start and count of visible points.\n * @private\n */\nexport function _getStartAndCountOfVisiblePoints(meta: ChartMeta<'line' | 'scatter'>, points: PointElement[], animationsDisabled: boolean) {\n const pointCount = points.length;\n\n let start = 0;\n let count = pointCount;\n\n if (meta._sorted) {\n const {iScale, vScale, _parsed} = meta;\n const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null;\n const axis = iScale.axis;\n const {min, max, minDefined, maxDefined} = iScale.getUserBounds();\n\n if (minDefined) {\n start = Math.min(\n // @ts-expect-error Need to type _parsed\n _lookupByKey(_parsed, axis, min).lo,\n // @ts-expect-error Need to fix types on _lookupByKey\n animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo);\n if (spanGaps) {\n const distanceToDefinedLo = (_parsed\n .slice(0, start + 1)\n .reverse()\n .findIndex(\n point => !isNullOrUndef(point[vScale.axis])));\n start -= Math.max(0, distanceToDefinedLo);\n }\n start = _limitValue(start, 0, pointCount - 1);\n }\n if (maxDefined) {\n let end = Math.max(\n // @ts-expect-error Need to type _parsed\n _lookupByKey(_parsed, iScale.axis, max, true).hi + 1,\n // @ts-expect-error Need to fix types on _lookupByKey\n animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1);\n if (spanGaps) {\n const distanceToDefinedHi = (_parsed\n .slice(end - 1)\n .findIndex(\n point => !isNullOrUndef(point[vScale.axis])));\n end += Math.max(0, distanceToDefinedHi);\n }\n count = _limitValue(end, start, pointCount) - start;\n } else {\n count = pointCount - start;\n }\n }\n\n return {start, count};\n}\n\n/**\n * Checks if the scale ranges have changed.\n * @param {object} meta - dataset meta.\n * @returns {boolean}\n * @private\n */\nexport function _scaleRangesChanged(meta) {\n const {xScale, yScale, _scaleRanges} = meta;\n const newRanges = {\n xmin: xScale.min,\n xmax: xScale.max,\n ymin: yScale.min,\n ymax: yScale.max\n };\n if (!_scaleRanges) {\n meta._scaleRanges = newRanges;\n return true;\n }\n const changed = _scaleRanges.xmin !== xScale.min\n\t\t|| _scaleRanges.xmax !== xScale.max\n\t\t|| _scaleRanges.ymin !== yScale.min\n\t\t|| _scaleRanges.ymax !== yScale.max;\n\n Object.assign(_scaleRanges, newRanges);\n return changed;\n}\n","import {PI, TAU, HALF_PI} from './helpers.math.js';\n\nconst atEdge = (t: number) => t === 0 || t === 1;\nconst elasticIn = (t: number, s: number, p: number) => -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));\nconst elasticOut = (t: number, s: number, p: number) => Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;\n\n/**\n * Easing functions adapted from Robert Penner's easing equations.\n * @namespace Chart.helpers.easing.effects\n * @see http://www.robertpenner.com/easing/\n */\nconst effects = {\n linear: (t: number) => t,\n\n easeInQuad: (t: number) => t * t,\n\n easeOutQuad: (t: number) => -t * (t - 2),\n\n easeInOutQuad: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t\n : -0.5 * ((--t) * (t - 2) - 1),\n\n easeInCubic: (t: number) => t * t * t,\n\n easeOutCubic: (t: number) => (t -= 1) * t * t + 1,\n\n easeInOutCubic: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t * t\n : 0.5 * ((t -= 2) * t * t + 2),\n\n easeInQuart: (t: number) => t * t * t * t,\n\n easeOutQuart: (t: number) => -((t -= 1) * t * t * t - 1),\n\n easeInOutQuart: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t * t * t\n : -0.5 * ((t -= 2) * t * t * t - 2),\n\n easeInQuint: (t: number) => t * t * t * t * t,\n\n easeOutQuint: (t: number) => (t -= 1) * t * t * t * t + 1,\n\n easeInOutQuint: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t * t * t * t\n : 0.5 * ((t -= 2) * t * t * t * t + 2),\n\n easeInSine: (t: number) => -Math.cos(t * HALF_PI) + 1,\n\n easeOutSine: (t: number) => Math.sin(t * HALF_PI),\n\n easeInOutSine: (t: number) => -0.5 * (Math.cos(PI * t) - 1),\n\n easeInExpo: (t: number) => (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)),\n\n easeOutExpo: (t: number) => (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1,\n\n easeInOutExpo: (t: number) => atEdge(t) ? t : t < 0.5\n ? 0.5 * Math.pow(2, 10 * (t * 2 - 1))\n : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),\n\n easeInCirc: (t: number) => (t >= 1) ? t : -(Math.sqrt(1 - t * t) - 1),\n\n easeOutCirc: (t: number) => Math.sqrt(1 - (t -= 1) * t),\n\n easeInOutCirc: (t: number) => ((t /= 0.5) < 1)\n ? -0.5 * (Math.sqrt(1 - t * t) - 1)\n : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),\n\n easeInElastic: (t: number) => atEdge(t) ? t : elasticIn(t, 0.075, 0.3),\n\n easeOutElastic: (t: number) => atEdge(t) ? t : elasticOut(t, 0.075, 0.3),\n\n easeInOutElastic(t: number) {\n const s = 0.1125;\n const p = 0.45;\n return atEdge(t) ? t :\n t < 0.5\n ? 0.5 * elasticIn(t * 2, s, p)\n : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);\n },\n\n easeInBack(t: number) {\n const s = 1.70158;\n return t * t * ((s + 1) * t - s);\n },\n\n easeOutBack(t: number) {\n const s = 1.70158;\n return (t -= 1) * t * ((s + 1) * t + s) + 1;\n },\n\n easeInOutBack(t: number) {\n let s = 1.70158;\n if ((t /= 0.5) < 1) {\n return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));\n }\n return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);\n },\n\n easeInBounce: (t: number) => 1 - effects.easeOutBounce(1 - t),\n\n easeOutBounce(t: number) {\n const m = 7.5625;\n const d = 2.75;\n if (t < (1 / d)) {\n return m * t * t;\n }\n if (t < (2 / d)) {\n return m * (t -= (1.5 / d)) * t + 0.75;\n }\n if (t < (2.5 / d)) {\n return m * (t -= (2.25 / d)) * t + 0.9375;\n }\n return m * (t -= (2.625 / d)) * t + 0.984375;\n },\n\n easeInOutBounce: (t: number) => (t < 0.5)\n ? effects.easeInBounce(t * 2) * 0.5\n : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5,\n} as const;\n\nexport type EasingFunction = keyof typeof effects\n\nexport default effects;\n","import {Color} from '@kurkle/color';\n\nexport function isPatternOrGradient(value: unknown): value is CanvasPattern | CanvasGradient {\n if (value && typeof value === 'object') {\n const type = value.toString();\n return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';\n }\n\n return false;\n}\n\nexport function color(value: CanvasGradient): CanvasGradient;\nexport function color(value: CanvasPattern): CanvasPattern;\nexport function color(\n value:\n | string\n | { r: number; g: number; b: number; a: number }\n | [number, number, number]\n | [number, number, number, number]\n): Color;\nexport function color(value) {\n return isPatternOrGradient(value) ? value : new Color(value);\n}\n\nexport function getHoverColor(value: CanvasGradient): CanvasGradient;\nexport function getHoverColor(value: CanvasPattern): CanvasPattern;\nexport function getHoverColor(value: string): string;\nexport function getHoverColor(value) {\n return isPatternOrGradient(value)\n ? value\n : new Color(value).saturate(0.5).darken(0.1).hexString();\n}\n","const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension'];\nconst colors = ['color', 'borderColor', 'backgroundColor'];\n\nexport function applyAnimationsDefaults(defaults) {\n defaults.set('animation', {\n delay: undefined,\n duration: 1000,\n easing: 'easeOutQuart',\n fn: undefined,\n from: undefined,\n loop: undefined,\n to: undefined,\n type: undefined,\n });\n\n defaults.describe('animation', {\n _fallback: false,\n _indexable: false,\n _scriptable: (name) => name !== 'onProgress' && name !== 'onComplete' && name !== 'fn',\n });\n\n defaults.set('animations', {\n colors: {\n type: 'color',\n properties: colors\n },\n numbers: {\n type: 'number',\n properties: numbers\n },\n });\n\n defaults.describe('animations', {\n _fallback: 'animation',\n });\n\n defaults.set('transitions', {\n active: {\n animation: {\n duration: 400\n }\n },\n resize: {\n animation: {\n duration: 0\n }\n },\n show: {\n animations: {\n colors: {\n from: 'transparent'\n },\n visible: {\n type: 'boolean',\n duration: 0 // show immediately\n },\n }\n },\n hide: {\n animations: {\n colors: {\n to: 'transparent'\n },\n visible: {\n type: 'boolean',\n easing: 'linear',\n fn: v => v | 0 // for keeping the dataset visible all the way through the animation\n },\n }\n }\n });\n}\n","export function applyLayoutsDefaults(defaults) {\n defaults.set('layout', {\n autoPadding: true,\n padding: {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n }\n });\n}\n","\nconst intlCache = new Map<string, Intl.NumberFormat>();\n\nfunction getNumberFormat(locale: string, options?: Intl.NumberFormatOptions) {\n options = options || {};\n const cacheKey = locale + JSON.stringify(options);\n let formatter = intlCache.get(cacheKey);\n if (!formatter) {\n formatter = new Intl.NumberFormat(locale, options);\n intlCache.set(cacheKey, formatter);\n }\n return formatter;\n}\n\nexport function formatNumber(num: number, locale: string, options?: Intl.NumberFormatOptions) {\n return getNumberFormat(locale, options).format(num);\n}\n","import {isArray} from '../helpers/helpers.core.js';\nimport {formatNumber} from '../helpers/helpers.intl.js';\nimport {log10} from '../helpers/helpers.math.js';\n\n/**\n * Namespace to hold formatters for different types of ticks\n * @namespace Chart.Ticks.formatters\n */\nconst formatters = {\n /**\n * Formatter for value labels\n * @method Chart.Ticks.formatters.values\n * @param value the value to display\n * @return {string|string[]} the label to display\n */\n values(value) {\n return isArray(value) ? /** @type {string[]} */ (value) : '' + value;\n },\n\n /**\n * Formatter for numeric ticks\n * @method Chart.Ticks.formatters.numeric\n * @param tickValue {number} the value to be formatted\n * @param index {number} the position of the tickValue parameter in the ticks array\n * @param ticks {object[]} the list of ticks being converted\n * @return {string} string representation of the tickValue parameter\n */\n numeric(tickValue, index, ticks) {\n if (tickValue === 0) {\n return '0'; // never show decimal places for 0\n }\n\n const locale = this.chart.options.locale;\n let notation;\n let delta = tickValue; // This is used when there are less than 2 ticks as the tick interval.\n\n if (ticks.length > 1) {\n // all ticks are small or there huge numbers; use scientific notation\n const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));\n if (maxTick < 1e-4 || maxTick > 1e+15) {\n notation = 'scientific';\n }\n\n delta = calculateDelta(tickValue, ticks);\n }\n\n const logDelta = log10(Math.abs(delta));\n\n // When datasets have values approaching Number.MAX_VALUE, the tick calculations might result in\n // infinity and eventually NaN. Passing NaN for minimumFractionDigits or maximumFractionDigits\n // will make the number formatter throw. So instead we check for isNaN and use a fallback value.\n //\n // toFixed has a max of 20 decimal places\n const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);\n\n const options = {notation, minimumFractionDigits: numDecimal, maximumFractionDigits: numDecimal};\n Object.assign(options, this.options.ticks.format);\n\n return formatNumber(tickValue, locale, options);\n },\n\n\n /**\n * Formatter for logarithmic ticks\n * @method Chart.Ticks.formatters.logarithmic\n * @param tickValue {number} the value to be formatted\n * @param index {number} the position of the tickValue parameter in the ticks array\n * @param ticks {object[]} the list of ticks being converted\n * @return {string} string representation of the tickValue parameter\n */\n logarithmic(tickValue, index, ticks) {\n if (tickValue === 0) {\n return '0';\n }\n const remain = ticks[index].significand || (tickValue / (Math.pow(10, Math.floor(log10(tickValue)))));\n if ([1, 2, 3, 5, 10, 15].includes(remain) || index > 0.8 * ticks.length) {\n return formatters.numeric.call(this, tickValue, index, ticks);\n }\n return '';\n }\n\n};\n\n\nfunction calculateDelta(tickValue, ticks) {\n // Figure out how many digits to show\n // The space between the first two ticks might be smaller than normal spacing\n let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;\n\n // If we have a number like 2.5 as the delta, figure out how many decimal places we need\n if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {\n // not an integer\n delta = tickValue - Math.floor(tickValue);\n }\n return delta;\n}\n\n/**\n * Namespace to hold static tick generation functions\n * @namespace Chart.Ticks\n */\nexport default {formatters};\n","import Ticks from './core.ticks.js';\n\nexport function applyScaleDefaults(defaults) {\n defaults.set('scale', {\n display: true,\n offset: false,\n reverse: false,\n beginAtZero: false,\n\n /**\n * Scale boundary strategy (bypassed by min/max time options)\n * - `data`: make sure data are fully visible, ticks outside are removed\n * - `ticks`: make sure ticks are fully visible, data outside are truncated\n * @see https://github.com/chartjs/Chart.js/pull/4556\n * @since 3.0.0\n */\n bounds: 'ticks',\n\n clip: true,\n\n /**\n * Addition grace added to max and reduced from min data value.\n * @since 3.0.0\n */\n grace: 0,\n\n // grid line settings\n grid: {\n display: true,\n lineWidth: 1,\n drawOnChartArea: true,\n drawTicks: true,\n tickLength: 8,\n tickWidth: (_ctx, options) => options.lineWidth,\n tickColor: (_ctx, options) => options.color,\n offset: false,\n },\n\n border: {\n display: true,\n dash: [],\n dashOffset: 0.0,\n width: 1\n },\n\n // scale title\n title: {\n // display property\n display: false,\n\n // actual label\n text: '',\n\n // top/bottom padding\n padding: {\n top: 4,\n bottom: 4\n }\n },\n\n // label settings\n ticks: {\n minRotation: 0,\n maxRotation: 50,\n mirror: false,\n textStrokeWidth: 0,\n textStrokeColor: '',\n padding: 3,\n display: true,\n autoSkip: true,\n autoSkipPadding: 3,\n labelOffset: 0,\n // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.\n callback: Ticks.formatters.values,\n minor: {},\n major: {},\n align: 'center',\n crossAlign: 'near',\n\n showLabelBackdrop: false,\n backdropColor: 'rgba(255, 255, 255, 0.75)',\n backdropPadding: 2,\n }\n });\n\n defaults.route('scale.ticks', 'color', '', 'color');\n defaults.route('scale.grid', 'color', '', 'borderColor');\n defaults.route('scale.border', 'color', '', 'borderColor');\n defaults.route('scale.title', 'color', '', 'color');\n\n defaults.describe('scale', {\n _fallback: false,\n _scriptable: (name) => !name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',\n _indexable: (name) => name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash',\n });\n\n defaults.describe('scales', {\n _fallback: 'scale',\n });\n\n defaults.describe('scale.ticks', {\n _scriptable: (name) => name !== 'backdropPadding' && name !== 'callback',\n _indexable: (name) => name !== 'backdropPadding',\n });\n}\n","import {getHoverColor} from '../helpers/helpers.color.js';\nimport {isObject, merge, valueOrDefault} from '../helpers/helpers.core.js';\nimport {applyAnimationsDefaults} from './core.animations.defaults.js';\nimport {applyLayoutsDefaults} from './core.layouts.defaults.js';\nimport {applyScaleDefaults} from './core.scale.defaults.js';\n\nexport const overrides = Object.create(null);\nexport const descriptors = Object.create(null);\n\n/**\n * @param {object} node\n * @param {string} key\n * @return {object}\n */\nfunction getScope(node, key) {\n if (!key) {\n return node;\n }\n const keys = key.split('.');\n for (let i = 0, n = keys.length; i < n; ++i) {\n const k = keys[i];\n node = node[k] || (node[k] = Object.create(null));\n }\n return node;\n}\n\nfunction set(root, scope, values) {\n if (typeof scope === 'string') {\n return merge(getScope(root, scope), values);\n }\n return merge(getScope(root, ''), scope);\n}\n\n/**\n * Please use the module's default export which provides a singleton instance\n * Note: class is exported for typedoc\n */\nexport class Defaults {\n constructor(_descriptors, _appliers) {\n this.animation = undefined;\n this.backgroundColor = 'rgba(0,0,0,0.1)';\n this.borderColor = 'rgba(0,0,0,0.1)';\n this.color = '#666';\n this.datasets = {};\n this.devicePixelRatio = (context) => context.chart.platform.getDevicePixelRatio();\n this.elements = {};\n this.events = [\n 'mousemove',\n 'mouseout',\n 'click',\n 'touchstart',\n 'touchmove'\n ];\n this.font = {\n family: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n size: 12,\n style: 'normal',\n lineHeight: 1.2,\n weight: null\n };\n this.hover = {};\n this.hoverBackgroundColor = (ctx, options) => getHoverColor(options.backgroundColor);\n this.hoverBorderColor = (ctx, options) => getHoverColor(options.borderColor);\n this.hoverColor = (ctx, options) => getHoverColor(options.color);\n this.indexAxis = 'x';\n this.interaction = {\n mode: 'nearest',\n intersect: true,\n includeInvisible: false\n };\n this.maintainAspectRatio = true;\n this.onHover = null;\n this.onClick = null;\n this.parsing = true;\n this.plugins = {};\n this.responsive = true;\n this.scale = undefined;\n this.scales = {};\n this.showLine = true;\n this.drawActiveElementsOnTop = true;\n\n this.describe(_descriptors);\n this.apply(_appliers);\n }\n\n /**\n\t * @param {string|object} scope\n\t * @param {object} [values]\n\t */\n set(scope, values) {\n return set(this, scope, values);\n }\n\n /**\n\t * @param {string} scope\n\t */\n get(scope) {\n return getScope(this, scope);\n }\n\n /**\n\t * @param {string|object} scope\n\t * @param {object} [values]\n\t */\n describe(scope, values) {\n return set(descriptors, scope, values);\n }\n\n override(scope, values) {\n return set(overrides, scope, values);\n }\n\n /**\n\t * Routes the named defaults to fallback to another scope/name.\n\t * This routing is useful when those target values, like defaults.color, are changed runtime.\n\t * If the values would be copied, the runtime change would not take effect. By routing, the\n\t * fallback is evaluated at each access, so its always up to date.\n\t *\n\t * Example:\n\t *\n\t * \tdefaults.route('elements.arc', 'backgroundColor', '', 'color')\n\t * - reads the backgroundColor from defaults.color when undefined locally\n\t *\n\t * @param {string} scope Scope this route applies to.\n\t * @param {string} name Property name that should be routed to different namespace when not defined here.\n\t * @param {string} targetScope The namespace where those properties should be routed to.\n\t * Empty string ('') is the root of defaults.\n\t * @param {string} targetName The target name in the target scope the property should be routed to.\n\t */\n route(scope, name, targetScope, targetName) {\n const scopeObject = getScope(this, scope);\n const targetScopeObject = getScope(this, targetScope);\n const privateName = '_' + name;\n\n Object.defineProperties(scopeObject, {\n // A private property is defined to hold the actual value, when this property is set in its scope (set in the setter)\n [privateName]: {\n value: scopeObject[name],\n writable: true\n },\n // The actual property is defined as getter/setter so we can do the routing when value is not locally set.\n [name]: {\n enumerable: true,\n get() {\n const local = this[privateName];\n const target = targetScopeObject[targetName];\n if (isObject(local)) {\n return Object.assign({}, target, local);\n }\n return valueOrDefault(local, target);\n },\n set(value) {\n this[privateName] = value;\n }\n }\n });\n }\n\n apply(appliers) {\n appliers.forEach((apply) => apply(this));\n }\n}\n\n// singleton instance\nexport default /* #__PURE__ */ new Defaults({\n _scriptable: (name) => !name.startsWith('on'),\n _indexable: (name) => name !== 'events',\n hover: {\n _fallback: 'interaction'\n },\n interaction: {\n _scriptable: false,\n _indexable: false,\n }\n}, [applyAnimationsDefaults, applyLayoutsDefaults, applyScaleDefaults]);\n","import type {\n Chart,\n Point,\n FontSpec,\n CanvasFontSpec,\n PointStyle,\n RenderTextOpts,\n BackdropOptions\n} from '../types/index.js';\nimport type {\n TRBL,\n SplinePoint,\n RoundedRect,\n TRBLCorners\n} from '../types/geometric.js';\nimport {isArray, isNullOrUndef} from './helpers.core.js';\nimport {PI, TAU, HALF_PI, QUARTER_PI, TWO_THIRDS_PI, RAD_PER_DEG} from './helpers.math.js';\n\n/**\n * Converts the given font object into a CSS font string.\n * @param font - A font object.\n * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font\n * @private\n */\nexport function toFontString(font: FontSpec) {\n if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {\n return null;\n }\n\n return (font.style ? font.style + ' ' : '')\n\t\t+ (font.weight ? font.weight + ' ' : '')\n\t\t+ font.size + 'px '\n\t\t+ font.family;\n}\n\n/**\n * @private\n */\nexport function _measureText(\n ctx: CanvasRenderingContext2D,\n data: Record<string, number>,\n gc: string[],\n longest: number,\n string: string\n) {\n let textWidth = data[string];\n if (!textWidth) {\n textWidth = data[string] = ctx.measureText(string).width;\n gc.push(string);\n }\n if (textWidth > longest) {\n longest = textWidth;\n }\n return longest;\n}\n\ntype Thing = string | undefined | null\ntype Things = (Thing | Thing[])[]\n\n/**\n * @private\n */\n// eslint-disable-next-line complexity\nexport function _longestText(\n ctx: CanvasRenderingContext2D,\n font: string,\n arrayOfThings: Things,\n cache?: {data?: Record<string, number>, garbageCollect?: string[], font?: string}\n) {\n cache = cache || {};\n let data = cache.data = cache.data || {};\n let gc = cache.garbageCollect = cache.garbageCollect || [];\n\n if (cache.font !== font) {\n data = cache.data = {};\n gc = cache.garbageCollect = [];\n cache.font = font;\n }\n\n ctx.save();\n\n ctx.font = font;\n let longest = 0;\n const ilen = arrayOfThings.length;\n let i: number, j: number, jlen: number, thing: Thing | Thing[], nestedThing: Thing | Thing[];\n for (i = 0; i < ilen; i++) {\n thing = arrayOfThings[i];\n\n // Undefined strings and arrays should not be measured\n if (thing !== undefined && thing !== null && !isArray(thing)) {\n longest = _measureText(ctx, data, gc, longest, thing);\n } else if (isArray(thing)) {\n // if it is an array lets measure each element\n // to do maybe simplify this function a bit so we can do this more recursively?\n for (j = 0, jlen = thing.length; j < jlen; j++) {\n nestedThing = thing[j];\n // Undefined strings and arrays should not be measured\n if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {\n longest = _measureText(ctx, data, gc, longest, nestedThing);\n }\n }\n }\n }\n\n ctx.restore();\n\n const gcLen = gc.length / 2;\n if (gcLen > arrayOfThings.length) {\n for (i = 0; i < gcLen; i++) {\n delete data[gc[i]];\n }\n gc.splice(0, gcLen);\n }\n return longest;\n}\n\n/**\n * Returns the aligned pixel value to avoid anti-aliasing blur\n * @param chart - The chart instance.\n * @param pixel - A pixel value.\n * @param width - The width of the element.\n * @returns The aligned pixel value.\n * @private\n */\nexport function _alignPixel(chart: Chart, pixel: number, width: number) {\n const devicePixelRatio = chart.currentDevicePixelRatio;\n const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;\n return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;\n}\n\n/**\n * Clears the entire canvas.\n */\nexport function clearCanvas(canvas?: HTMLCanvasElement, ctx?: CanvasRenderingContext2D) {\n if (!ctx && !canvas) {\n return;\n }\n\n ctx = ctx || canvas.getContext('2d');\n\n ctx.save();\n // canvas.width and canvas.height do not consider the canvas transform,\n // while clearRect does\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.restore();\n}\n\nexport interface DrawPointOptions {\n pointStyle: PointStyle;\n rotation?: number;\n radius: number;\n borderWidth: number;\n}\n\nexport function drawPoint(\n ctx: CanvasRenderingContext2D,\n options: DrawPointOptions,\n x: number,\n y: number\n) {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n drawPointLegend(ctx, options, x, y, null);\n}\n\n// eslint-disable-next-line complexity\nexport function drawPointLegend(\n ctx: CanvasRenderingContext2D,\n options: DrawPointOptions,\n x: number,\n y: number,\n w: number\n) {\n let type: string, xOffset: number, yOffset: number, size: number, cornerRadius: number, width: number, xOffsetW: number, yOffsetW: number;\n const style = options.pointStyle;\n const rotation = options.rotation;\n const radius = options.radius;\n let rad = (rotation || 0) * RAD_PER_DEG;\n\n if (style && typeof style === 'object') {\n type = style.toString();\n if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {\n ctx.save();\n ctx.translate(x, y);\n ctx.rotate(rad);\n ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);\n ctx.restore();\n return;\n }\n }\n\n if (isNaN(radius) || radius <= 0) {\n return;\n }\n\n ctx.beginPath();\n\n switch (style) {\n // Default includes circle\n default:\n if (w) {\n ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);\n } else {\n ctx.arc(x, y, radius, 0, TAU);\n }\n ctx.closePath();\n break;\n case 'triangle':\n width = w ? w / 2 : radius;\n ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n rad += TWO_THIRDS_PI;\n ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n rad += TWO_THIRDS_PI;\n ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n ctx.closePath();\n break;\n case 'rectRounded':\n // NOTE: the rounded rect implementation changed to use `arc` instead of\n // `quadraticCurveTo` since it generates better results when rect is\n // almost a circle. 0.516 (instead of 0.5) produces results with visually\n // closer proportion to the previous impl and it is inscribed in the\n // circle with `radius`. For more details, see the following PRs:\n // https://github.com/chartjs/Chart.js/issues/5597\n // https://github.com/chartjs/Chart.js/issues/5858\n cornerRadius = radius * 0.516;\n size = radius - cornerRadius;\n xOffset = Math.cos(rad + QUARTER_PI) * size;\n xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n yOffset = Math.sin(rad + QUARTER_PI) * size;\n yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);\n ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);\n ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);\n ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);\n ctx.closePath();\n break;\n case 'rect':\n if (!rotation) {\n size = Math.SQRT1_2 * radius;\n width = w ? w / 2 : size;\n ctx.rect(x - width, y - size, 2 * width, 2 * size);\n break;\n }\n rad += QUARTER_PI;\n /* falls through */\n case 'rectRot':\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n ctx.closePath();\n break;\n case 'crossRot':\n rad += QUARTER_PI;\n /* falls through */\n case 'cross':\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.moveTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n break;\n case 'star':\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.moveTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n rad += QUARTER_PI;\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.moveTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n break;\n case 'line':\n xOffset = w ? w / 2 : Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n ctx.moveTo(x - xOffset, y - yOffset);\n ctx.lineTo(x + xOffset, y + yOffset);\n break;\n case 'dash':\n ctx.moveTo(x, y);\n ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);\n break;\n case false:\n ctx.closePath();\n break;\n }\n\n ctx.fill();\n if (options.borderWidth > 0) {\n ctx.stroke();\n }\n}\n\n/**\n * Returns true if the point is inside the rectangle\n * @param point - The point to test\n * @param area - The rectangle\n * @param margin - allowed margin\n * @private\n */\nexport function _isPointInArea(\n point: Point,\n area: TRBL,\n margin?: number\n) {\n margin = margin || 0.5; // margin - default is to match rounded decimals\n\n return !area || (point && point.x > area.left - margin && point.x < area.right + margin &&\n\t\tpoint.y > area.top - margin && point.y < area.bottom + margin);\n}\n\nexport function clipArea(ctx: CanvasRenderingContext2D, area: TRBL) {\n ctx.save();\n ctx.beginPath();\n ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);\n ctx.clip();\n}\n\nexport function unclipArea(ctx: CanvasRenderingContext2D) {\n ctx.restore();\n}\n\n/**\n * @private\n */\nexport function _steppedLineTo(\n ctx: CanvasRenderingContext2D,\n previous: Point,\n target: Point,\n flip?: boolean,\n mode?: string\n) {\n if (!previous) {\n return ctx.lineTo(target.x, target.y);\n }\n if (mode === 'middle') {\n const midpoint = (previous.x + target.x) / 2.0;\n ctx.lineTo(midpoint, previous.y);\n ctx.lineTo(midpoint, target.y);\n } else if (mode === 'after' !== !!flip) {\n ctx.lineTo(previous.x, target.y);\n } else {\n ctx.lineTo(target.x, previous.y);\n }\n ctx.lineTo(target.x, target.y);\n}\n\n/**\n * @private\n */\nexport function _bezierCurveTo(\n ctx: CanvasRenderingContext2D,\n previous: SplinePoint,\n target: SplinePoint,\n flip?: boolean\n) {\n if (!previous) {\n return ctx.lineTo(target.x, target.y);\n }\n ctx.bezierCurveTo(\n flip ? previous.cp1x : previous.cp2x,\n flip ? previous.cp1y : previous.cp2y,\n flip ? target.cp2x : target.cp1x,\n flip ? target.cp2y : target.cp1y,\n target.x,\n target.y);\n}\n\nfunction setRenderOpts(ctx: CanvasRenderingContext2D, opts: RenderTextOpts) {\n if (opts.translation) {\n ctx.translate(opts.translation[0], opts.translation[1]);\n }\n\n if (!isNullOrUndef(opts.rotation)) {\n ctx.rotate(opts.rotation);\n }\n\n if (opts.color) {\n ctx.fillStyle = opts.color;\n }\n\n if (opts.textAlign) {\n ctx.textAlign = opts.textAlign;\n }\n\n if (opts.textBaseline) {\n ctx.textBaseline = opts.textBaseline;\n }\n}\n\nfunction decorateText(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n line: string,\n opts: RenderTextOpts\n) {\n if (opts.strikethrough || opts.underline) {\n /**\n * Now that IE11 support has been dropped, we can use more\n * of the TextMetrics object. The actual bounding boxes\n * are unflagged in Chrome, Firefox, Edge, and Safari so they\n * can be safely used.\n * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility\n */\n const metrics = ctx.measureText(line);\n const left = x - metrics.actualBoundingBoxLeft;\n const right = x + metrics.actualBoundingBoxRight;\n const top = y - metrics.actualBoundingBoxAscent;\n const bottom = y + metrics.actualBoundingBoxDescent;\n const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;\n\n ctx.strokeStyle = ctx.fillStyle;\n ctx.beginPath();\n ctx.lineWidth = opts.decorationWidth || 2;\n ctx.moveTo(left, yDecoration);\n ctx.lineTo(right, yDecoration);\n ctx.stroke();\n }\n}\n\nfunction drawBackdrop(ctx: CanvasRenderingContext2D, opts: BackdropOptions) {\n const oldColor = ctx.fillStyle;\n\n ctx.fillStyle = opts.color as string;\n ctx.fillRect(opts.left, opts.top, opts.width, opts.height);\n ctx.fillStyle = oldColor;\n}\n\n/**\n * Render text onto the canvas\n */\nexport function renderText(\n ctx: CanvasRenderingContext2D,\n text: string | string[],\n x: number,\n y: number,\n font: CanvasFontSpec,\n opts: RenderTextOpts = {}\n) {\n const lines = isArray(text) ? text : [text];\n const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';\n let i: number, line: string;\n\n ctx.save();\n ctx.font = font.string;\n setRenderOpts(ctx, opts);\n\n for (i = 0; i < lines.length; ++i) {\n line = lines[i];\n\n if (opts.backdrop) {\n drawBackdrop(ctx, opts.backdrop);\n }\n\n if (stroke) {\n if (opts.strokeColor) {\n ctx.strokeStyle = opts.strokeColor;\n }\n\n if (!isNullOrUndef(opts.strokeWidth)) {\n ctx.lineWidth = opts.strokeWidth;\n }\n\n ctx.strokeText(line, x, y, opts.maxWidth);\n }\n\n ctx.fillText(line, x, y, opts.maxWidth);\n decorateText(ctx, x, y, line, opts);\n\n y += Number(font.lineHeight);\n }\n\n ctx.restore();\n}\n\n/**\n * Add a path of a rectangle with rounded corners to the current sub-path\n * @param ctx - Context\n * @param rect - Bounding rect\n */\nexport function addRoundedRectPath(\n ctx: CanvasRenderingContext2D,\n rect: RoundedRect & { radius: TRBLCorners }\n) {\n const {x, y, w, h, radius} = rect;\n\n // top left arc\n ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true);\n\n // line from top left to bottom left\n ctx.lineTo(x, y + h - radius.bottomLeft);\n\n // bottom left arc\n ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);\n\n // line from bottom left to bottom right\n ctx.lineTo(x + w - radius.bottomRight, y + h);\n\n // bottom right arc\n ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);\n\n // line from bottom right to top right\n ctx.lineTo(x + w, y + radius.topRight);\n\n // top right arc\n ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);\n\n // line from top right to top left\n ctx.lineTo(x + radius.topLeft, y);\n}\n","import defaults from '../core/core.defaults.js';\nimport {isArray, isObject, toDimension, valueOrDefault} from './helpers.core.js';\nimport {toFontString} from './helpers.canvas.js';\nimport type {ChartArea, FontSpec, Point} from '../types/index.js';\nimport type {TRBL, TRBLCorners} from '../types/geometric.js';\n\nconst LINE_HEIGHT = /^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/;\nconst FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;\n\n/**\n * @alias Chart.helpers.options\n * @namespace\n */\n/**\n * Converts the given line height `value` in pixels for a specific font `size`.\n * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n * @param size - The font size (in pixels) used to resolve relative `value`.\n * @returns The effective line height in pixels (size * 1.2 if value is invalid).\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n * @since 2.7.0\n */\nexport function toLineHeight(value: number | string, size: number): number {\n const matches = ('' + value).match(LINE_HEIGHT);\n if (!matches || matches[1] === 'normal') {\n return size * 1.2;\n }\n\n value = +matches[2];\n\n switch (matches[3]) {\n case 'px':\n return value;\n case '%':\n value /= 100;\n break;\n default:\n break;\n }\n\n return size * value;\n}\n\nconst numberOrZero = (v: unknown) => +v || 0;\n\n/**\n * @param value\n * @param props\n */\nexport function _readValueToProps<K extends string>(value: number | Record<K, number>, props: K[]): Record<K, number>;\nexport function _readValueToProps<K extends string, T extends string>(value: number | Record<K & T, number>, props: Record<T, K>): Record<T, number>;\nexport function _readValueToProps(value: number | Record<string, number>, props: string[] | Record<string, string>) {\n const ret = {};\n const objProps = isObject(props);\n const keys = objProps ? Object.keys(props) : props;\n const read = isObject(value)\n ? objProps\n ? prop => valueOrDefault(value[prop], value[props[prop]])\n : prop => value[prop]\n : () => value;\n\n for (const prop of keys) {\n ret[prop] = numberOrZero(read(prop));\n }\n return ret;\n}\n\n/**\n * Converts the given value into a TRBL object.\n * @param value - If a number, set the value to all TRBL component,\n * else, if an object, use defined properties and sets undefined ones to 0.\n * x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left)\n * @since 3.0.0\n */\nexport function toTRBL(value: number | TRBL | Point) {\n return _readValueToProps(value, {top: 'y', right: 'x', bottom: 'y', left: 'x'});\n}\n\n/**\n * Converts the given value into a TRBL corners object (similar with css border-radius).\n * @param value - If a number, set the value to all TRBL corner components,\n * else, if an object, use defined properties and sets undefined ones to 0.\n * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)\n * @since 3.0.0\n */\nexport function toTRBLCorners(value: number | TRBLCorners) {\n return _readValueToProps(value, ['topLeft', 'topRight', 'bottomLeft', 'bottomRight']);\n}\n\n/**\n * Converts the given value into a padding object with pre-computed width/height.\n * @param value - If a number, set the value to all TRBL component,\n * else, if an object, use defined properties and sets undefined ones to 0.\n * x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left, width, height)\n * @since 2.7.0\n */\nexport function toPadding(value?: number | TRBL): ChartArea {\n const obj = toTRBL(value) as ChartArea;\n\n obj.width = obj.left + obj.right;\n obj.height = obj.top + obj.bottom;\n\n return obj;\n}\n\n/**\n * Parses font options and returns the font object.\n * @param options - A object that contains font options to be parsed.\n * @param fallback - A object that contains fallback font options.\n * @return The font object.\n * @private\n */\n\nexport function toFont(options: Partial<FontSpec>, fallback?: Partial<FontSpec>) {\n options = options || {};\n fallback = fallback || defaults.font as FontSpec;\n\n let size = valueOrDefault(options.size, fallback.size);\n\n if (typeof size === 'string') {\n size = parseInt(size, 10);\n }\n let style = valueOrDefault(options.style, fallback.style);\n if (style && !('' + style).match(FONT_STYLE)) {\n console.warn('Invalid font style specified: \"' + style + '\"');\n style = undefined;\n }\n\n const font = {\n family: valueOrDefault(options.family, fallback.family),\n lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),\n size,\n style,\n weight: valueOrDefault(options.weight, fallback.weight),\n string: ''\n };\n\n font.string = toFontString(font);\n return font;\n}\n\n/**\n * Evaluates the given `inputs` sequentially and returns the first defined value.\n * @param inputs - An array of values, falling back to the last value.\n * @param context - If defined and the current value is a function, the value\n * is called with `context` as first argument and the result becomes the new input.\n * @param index - If defined and the current value is an array, the value\n * at `index` become the new input.\n * @param info - object to return information about resolution in\n * @param info.cacheable - Will be set to `false` if option is not cacheable.\n * @since 2.7.0\n */\nexport function resolve(inputs: Array<unknown>, context?: object, index?: number, info?: { cacheable: boolean }) {\n let cacheable = true;\n let i: number, ilen: number, value: unknown;\n\n for (i = 0, ilen = inputs.length; i < ilen; ++i) {\n value = inputs[i];\n if (value === undefined) {\n continue;\n }\n if (context !== undefined && typeof value === 'function') {\n value = value(context);\n cacheable = false;\n }\n if (index !== undefined && isArray(value)) {\n value = value[index % value.length];\n cacheable = false;\n }\n if (value !== undefined) {\n if (info && !cacheable) {\n info.cacheable = false;\n }\n return value;\n }\n }\n}\n\n/**\n * @param minmax\n * @param grace\n * @param beginAtZero\n * @private\n */\nexport function _addGrace(minmax: { min: number; max: number; }, grace: number | string, beginAtZero: boolean) {\n const {min, max} = minmax;\n const change = toDimension(grace, (max - min) / 2);\n const keepZero = (value: number, add: number) => beginAtZero && value === 0 ? 0 : value + add;\n return {\n min: keepZero(min, -Math.abs(change)),\n max: keepZero(max, change)\n };\n}\n\n/**\n * Create a context inheriting parentContext\n * @param parentContext\n * @param context\n * @returns\n */\nexport function createContext<T extends object>(parentContext: null, context: T): T;\nexport function createContext<T extends object, P extends T>(parentContext: P, context: T): P & T;\nexport function createContext(parentContext: object, context: object) {\n return Object.assign(Object.create(parentContext), context);\n}\n","/* eslint-disable @typescript-eslint/no-use-before-define */\nimport type {AnyObject} from '../types/basic.js';\nimport type {ChartMeta} from '../types/index.js';\nimport type {\n ResolverObjectKey,\n ResolverCache,\n ResolverProxy,\n DescriptorDefaults,\n Descriptor,\n ContextCache,\n ContextProxy\n} from './helpers.config.types.js';\nimport {isArray, isFunction, isObject, resolveObjectKey, _capitalize} from './helpers.core.js';\n\nexport * from './helpers.config.types.js';\n\n/**\n * Creates a Proxy for resolving raw values for options.\n * @param scopes - The option scopes to look for values, in resolution order\n * @param prefixes - The prefixes for values, in resolution order.\n * @param rootScopes - The root option scopes\n * @param fallback - Parent scopes fallback\n * @param getTarget - callback for getting the target for changed values\n * @returns Proxy\n * @private\n */\nexport function _createResolver<\n T extends AnyObject[] = AnyObject[],\n R extends AnyObject[] = T\n>(\n scopes: T,\n prefixes = [''],\n rootScopes?: R,\n fallback?: ResolverObjectKey,\n getTarget = () => scopes[0]\n) {\n const finalRootScopes = rootScopes || scopes;\n if (typeof fallback === 'undefined') {\n fallback = _resolve('_fallback', scopes);\n }\n const cache: ResolverCache<T, R> = {\n [Symbol.toStringTag]: 'Object',\n _cacheable: true,\n _scopes: scopes,\n _rootScopes: finalRootScopes,\n _fallback: fallback,\n _getTarget: getTarget,\n override: (scope: AnyObject) => _createResolver([scope, ...scopes], prefixes, finalRootScopes, fallback),\n };\n return new Proxy(cache, {\n /**\n * A trap for the delete operator.\n */\n deleteProperty(target, prop: string) {\n delete target[prop]; // remove from cache\n delete target._keys; // remove cached keys\n delete scopes[0][prop]; // remove from top level scope\n return true;\n },\n\n /**\n * A trap for getting property values.\n */\n get(target, prop: string) {\n return _cached(target, prop,\n () => _resolveWithPrefixes(prop, prefixes, scopes, target));\n },\n\n /**\n * A trap for Object.getOwnPropertyDescriptor.\n * Also used by Object.hasOwnProperty.\n */\n getOwnPropertyDescriptor(target, prop) {\n return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);\n },\n\n /**\n * A trap for Object.getPrototypeOf.\n */\n getPrototypeOf() {\n return Reflect.getPrototypeOf(scopes[0]);\n },\n\n /**\n * A trap for the in operator.\n */\n has(target, prop: string) {\n return getKeysFromAllScopes(target).includes(prop);\n },\n\n /**\n * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.\n */\n ownKeys(target) {\n return getKeysFromAllScopes(target);\n },\n\n /**\n * A trap for setting property values.\n */\n set(target, prop: string, value) {\n const storage = target._storage || (target._storage = getTarget());\n target[prop] = storage[prop] = value; // set to top level scope + cache\n delete target._keys; // remove cached keys\n return true;\n }\n }) as ResolverProxy<T, R>;\n}\n\n/**\n * Returns an Proxy for resolving option values with context.\n * @param proxy - The Proxy returned by `_createResolver`\n * @param context - Context object for scriptable/indexable options\n * @param subProxy - The proxy provided for scriptable options\n * @param descriptorDefaults - Defaults for descriptors\n * @private\n */\nexport function _attachContext<\n T extends AnyObject[] = AnyObject[],\n R extends AnyObject[] = T\n>(\n proxy: ResolverProxy<T, R>,\n context: AnyObject,\n subProxy?: ResolverProxy<T, R>,\n descriptorDefaults?: DescriptorDefaults\n) {\n const cache: ContextCache<T, R> = {\n _cacheable: false,\n _proxy: proxy,\n _context: context,\n _subProxy: subProxy,\n _stack: new Set(),\n _descriptors: _descriptors(proxy, descriptorDefaults),\n setContext: (ctx: AnyObject) => _attachContext(proxy, ctx, subProxy, descriptorDefaults),\n override: (scope: AnyObject) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)\n };\n return new Proxy(cache, {\n /**\n * A trap for the delete operator.\n */\n deleteProperty(target, prop) {\n delete target[prop]; // remove from cache\n delete proxy[prop]; // remove from proxy\n return true;\n },\n\n /**\n * A trap for getting property values.\n */\n get(target, prop: string, receiver) {\n return _cached(target, prop,\n () => _resolveWithContext(target, prop, receiver));\n },\n\n /**\n * A trap for Object.getOwnPropertyDescriptor.\n * Also used by Object.hasOwnProperty.\n */\n getOwnPropertyDescriptor(target, prop) {\n return target._descriptors.allKeys\n ? Reflect.has(proxy, prop) ? {enumerable: true, configurable: true} : undefined\n : Reflect.getOwnPropertyDescriptor(proxy, prop);\n },\n\n /**\n * A trap for Object.getPrototypeOf.\n */\n getPrototypeOf() {\n return Reflect.getPrototypeOf(proxy);\n },\n\n /**\n * A trap for the in operator.\n */\n has(target, prop) {\n return Reflect.has(proxy, prop);\n },\n\n /**\n * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.\n */\n ownKeys() {\n return Reflect.ownKeys(proxy);\n },\n\n /**\n * A trap for setting property values.\n */\n set(target, prop, value) {\n proxy[prop] = value; // set to proxy\n delete target[prop]; // remove from cache\n return true;\n }\n }) as ContextProxy<T, R>;\n}\n\n/**\n * @private\n */\nexport function _descriptors(\n proxy: ResolverCache,\n defaults: DescriptorDefaults = {scriptable: true, indexable: true}\n): Descriptor {\n const {_scriptable = defaults.scriptable, _indexable = defaults.indexable, _allKeys = defaults.allKeys} = proxy;\n return {\n allKeys: _allKeys,\n scriptable: _scriptable,\n indexable: _indexable,\n isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable,\n isIndexable: isFunction(_indexable) ? _indexable : () => _indexable\n };\n}\n\nconst readKey = (prefix: string, name: string) => prefix ? prefix + _capitalize(name) : name;\nconst needsSubResolver = (prop: string, value: unknown) => isObject(value) && prop !== 'adapters' &&\n (Object.getPrototypeOf(value) === null || value.constructor === Object);\n\nfunction _cached(\n target: AnyObject,\n prop: string,\n resolve: () => unknown\n) {\n if (Object.prototype.hasOwnProperty.call(target, prop) || prop === 'constructor') {\n return target[prop];\n }\n\n const value = resolve();\n // cache the resolved value\n target[prop] = value;\n return value;\n}\n\nfunction _resolveWithContext(\n target: ContextCache,\n prop: string,\n receiver: AnyObject\n) {\n const {_proxy, _context, _subProxy, _descriptors: descriptors} = target;\n let value = _proxy[prop]; // resolve from proxy\n\n // resolve with context\n if (isFunction(value) && descriptors.isScriptable(prop)) {\n value = _resolveScriptable(prop, value, target, receiver);\n }\n if (isArray(value) && value.length) {\n value = _resolveArray(prop, value, target, descriptors.isIndexable);\n }\n if (needsSubResolver(prop, value)) {\n // if the resolved value is an object, create a sub resolver for it\n value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);\n }\n return value;\n}\n\nfunction _resolveScriptable(\n prop: string,\n getValue: (ctx: AnyObject, sub: AnyObject) => unknown,\n target: ContextCache,\n receiver: AnyObject\n) {\n const {_proxy, _context, _subProxy, _stack} = target;\n if (_stack.has(prop)) {\n throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);\n }\n _stack.add(prop);\n let value = getValue(_context, _subProxy || receiver);\n _stack.delete(prop);\n if (needsSubResolver(prop, value)) {\n // When scriptable option returns an object, create a resolver on that.\n value = createSubResolver(_proxy._scopes, _proxy, prop, value);\n }\n return value;\n}\n\nfunction _resolveArray(\n prop: string,\n value: unknown[],\n target: ContextCache,\n isIndexable: (key: string) => boolean\n) {\n const {_proxy, _context, _subProxy, _descriptors: descriptors} = target;\n\n if (typeof _context.index !== 'undefined' && isIndexable(prop)) {\n return value[_context.index % value.length];\n } else if (isObject(value[0])) {\n // Array of objects, return array or resolvers\n const arr = value;\n const scopes = _proxy._scopes.filter(s => s !== arr);\n value = [];\n for (const item of arr) {\n const resolver = createSubResolver(scopes, _proxy, prop, item);\n value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));\n }\n }\n return value;\n}\n\nfunction resolveFallback(\n fallback: ResolverObjectKey | ((prop: ResolverObjectKey, value: unknown) => ResolverObjectKey),\n prop: ResolverObjectKey,\n value: unknown\n) {\n return isFunction(fallback) ? fallback(prop, value) : fallback;\n}\n\nconst getScope = (key: ResolverObjectKey, parent: AnyObject) => key === true ? parent\n : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;\n\nfunction addScopes(\n set: Set<AnyObject>,\n parentScopes: AnyObject[],\n key: ResolverObjectKey,\n parentFallback: ResolverObjectKey,\n value: unknown\n) {\n for (const parent of parentScopes) {\n const scope = getScope(key, parent);\n if (scope) {\n set.add(scope);\n const fallback = resolveFallback(scope._fallback, key, value);\n if (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) {\n // When we reach the descriptor that defines a new _fallback, return that.\n // The fallback will resume to that new scope.\n return fallback;\n }\n } else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) {\n // Fallback to `false` results to `false`, when falling back to different key.\n // For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations`\n return null;\n }\n }\n return false;\n}\n\nfunction createSubResolver(\n parentScopes: AnyObject[],\n resolver: ResolverCache,\n prop: ResolverObjectKey,\n value: unknown\n) {\n const rootScopes = resolver._rootScopes;\n const fallback = resolveFallback(resolver._fallback, prop, value);\n const allScopes = [...parentScopes, ...rootScopes];\n const set = new Set<AnyObject>();\n set.add(value);\n let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);\n if (key === null) {\n return false;\n }\n if (typeof fallback !== 'undefined' && fallback !== prop) {\n key = addScopesFromKey(set, allScopes, fallback, key, value);\n if (key === null) {\n return false;\n }\n }\n return _createResolver(Array.from(set), [''], rootScopes, fallback,\n () => subGetTarget(resolver, prop as string, value));\n}\n\nfunction addScopesFromKey(\n set: Set<AnyObject>,\n allScopes: AnyObject[],\n key: ResolverObjectKey,\n fallback: ResolverObjectKey,\n item: unknown\n) {\n while (key) {\n key = addScopes(set, allScopes, key, fallback, item);\n }\n return key;\n}\n\nfunction subGetTarget(\n resolver: ResolverCache,\n prop: string,\n value: unknown\n) {\n const parent = resolver._getTarget();\n if (!(prop in parent)) {\n parent[prop] = {};\n }\n const target = parent[prop];\n if (isArray(target) && isObject(value)) {\n // For array of objects, the object is used to store updated values\n return value;\n }\n return target || {};\n}\n\nfunction _resolveWithPrefixes(\n prop: string,\n prefixes: string[],\n scopes: AnyObject[],\n proxy: ResolverProxy\n) {\n let value: unknown;\n for (const prefix of prefixes) {\n value = _resolve(readKey(prefix, prop), scopes);\n if (typeof value !== 'undefined') {\n return needsSubResolver(prop, value)\n ? createSubResolver(scopes, proxy, prop, value)\n : value;\n }\n }\n}\n\nfunction _resolve(key: string, scopes: AnyObject[]) {\n for (const scope of scopes) {\n if (!scope) {\n continue;\n }\n const value = scope[key];\n if (typeof value !== 'undefined') {\n return value;\n }\n }\n}\n\nfunction getKeysFromAllScopes(target: ResolverCache) {\n let keys = target._keys;\n if (!keys) {\n keys = target._keys = resolveKeysFromAllScopes(target._scopes);\n }\n return keys;\n}\n\nfunction resolveKeysFromAllScopes(scopes: AnyObject[]) {\n const set = new Set<string>();\n for (const scope of scopes) {\n for (const key of Object.keys(scope).filter(k => !k.startsWith('_'))) {\n set.add(key);\n }\n }\n return Array.from(set);\n}\n\nexport function _parseObjectDataRadialScale(\n meta: ChartMeta<'line' | 'scatter'>,\n data: AnyObject[],\n start: number,\n count: number\n) {\n const {iScale} = meta;\n const {key = 'r'} = this._parsing;\n const parsed = new Array<{r: unknown}>(count);\n let i: number, ilen: number, index: number, item: AnyObject;\n\n for (i = 0, ilen = count; i < ilen; ++i) {\n index = i + start;\n item = data[index];\n parsed[i] = {\n r: iScale.parse(resolveObjectKey(item, key), index)\n };\n }\n return parsed;\n}\n","import {almostEquals, distanceBetweenPoints, sign} from './helpers.math.js';\nimport {_isPointInArea} from './helpers.canvas.js';\nimport type {ChartArea} from '../types/index.js';\nimport type {SplinePoint} from '../types/geometric.js';\n\nconst EPSILON = Number.EPSILON || 1e-14;\n\ntype OptionalSplinePoint = SplinePoint | false\nconst getPoint = (points: SplinePoint[], i: number): OptionalSplinePoint => i < points.length && !points[i].skip && points[i];\nconst getValueAxis = (indexAxis: 'x' | 'y') => indexAxis === 'x' ? 'y' : 'x';\n\nexport function splineCurve(\n firstPoint: SplinePoint,\n middlePoint: SplinePoint,\n afterPoint: SplinePoint,\n t: number\n): {\n previous: SplinePoint\n next: SplinePoint\n } {\n // Props to Rob Spencer at scaled innovation for his post on splining between points\n // http://scaledinnovation.com/analytics/splines/aboutSplines.html\n\n // This function must also respect \"skipped\" points\n\n const previous = firstPoint.skip ? middlePoint : firstPoint;\n const current = middlePoint;\n const next = afterPoint.skip ? middlePoint : afterPoint;\n const d01 = distanceBetweenPoints(current, previous);\n const d12 = distanceBetweenPoints(next, current);\n\n let s01 = d01 / (d01 + d12);\n let s12 = d12 / (d01 + d12);\n\n // If all points are the same, s01 & s02 will be inf\n s01 = isNaN(s01) ? 0 : s01;\n s12 = isNaN(s12) ? 0 : s12;\n\n const fa = t * s01; // scaling factor for triangle Ta\n const fb = t * s12;\n\n return {\n previous: {\n x: current.x - fa * (next.x - previous.x),\n y: current.y - fa * (next.y - previous.y)\n },\n next: {\n x: current.x + fb * (next.x - previous.x),\n y: current.y + fb * (next.y - previous.y)\n }\n };\n}\n\n/**\n * Adjust tangents to ensure monotonic properties\n */\nfunction monotoneAdjust(points: SplinePoint[], deltaK: number[], mK: number[]) {\n const pointsLen = points.length;\n\n let alphaK: number, betaK: number, tauK: number, squaredMagnitude: number, pointCurrent: OptionalSplinePoint;\n let pointAfter = getPoint(points, 0);\n for (let i = 0; i < pointsLen - 1; ++i) {\n pointCurrent = pointAfter;\n pointAfter = getPoint(points, i + 1);\n if (!pointCurrent || !pointAfter) {\n continue;\n }\n\n if (almostEquals(deltaK[i], 0, EPSILON)) {\n mK[i] = mK[i + 1] = 0;\n continue;\n }\n\n alphaK = mK[i] / deltaK[i];\n betaK = mK[i + 1] / deltaK[i];\n squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);\n if (squaredMagnitude <= 9) {\n continue;\n }\n\n tauK = 3 / Math.sqrt(squaredMagnitude);\n mK[i] = alphaK * tauK * deltaK[i];\n mK[i + 1] = betaK * tauK * deltaK[i];\n }\n}\n\nfunction monotoneCompute(points: SplinePoint[], mK: number[], indexAxis: 'x' | 'y' = 'x') {\n const valueAxis = getValueAxis(indexAxis);\n const pointsLen = points.length;\n let delta: number, pointBefore: OptionalSplinePoint, pointCurrent: OptionalSplinePoint;\n let pointAfter = getPoint(points, 0);\n\n for (let i = 0; i < pointsLen; ++i) {\n pointBefore = pointCurrent;\n pointCurrent = pointAfter;\n pointAfter = getPoint(points, i + 1);\n if (!pointCurrent) {\n continue;\n }\n\n const iPixel = pointCurrent[indexAxis];\n const vPixel = pointCurrent[valueAxis];\n if (pointBefore) {\n delta = (iPixel - pointBefore[indexAxis]) / 3;\n pointCurrent[`cp1${indexAxis}`] = iPixel - delta;\n pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];\n }\n if (pointAfter) {\n delta = (pointAfter[indexAxis] - iPixel) / 3;\n pointCurrent[`cp2${indexAxis}`] = iPixel + delta;\n pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];\n }\n }\n}\n\n/**\n * This function calculates Bézier control points in a similar way than |splineCurve|,\n * but preserves monotonicity of the provided data and ensures no local extremums are added\n * between the dataset discrete points due to the interpolation.\n * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation\n */\nexport function splineCurveMonotone(points: SplinePoint[], indexAxis: 'x' | 'y' = 'x') {\n const valueAxis = getValueAxis(indexAxis);\n const pointsLen = points.length;\n const deltaK: number[] = Array(pointsLen).fill(0);\n const mK: number[] = Array(pointsLen);\n\n // Calculate slopes (deltaK) and initialize tangents (mK)\n let i, pointBefore: OptionalSplinePoint, pointCurrent: OptionalSplinePoint;\n let pointAfter = getPoint(points, 0);\n\n for (i = 0; i < pointsLen; ++i) {\n pointBefore = pointCurrent;\n pointCurrent = pointAfter;\n pointAfter = getPoint(points, i + 1);\n if (!pointCurrent) {\n continue;\n }\n\n if (pointAfter) {\n const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];\n\n // In the case of two points that appear at the same x pixel, slopeDeltaX is 0\n deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;\n }\n mK[i] = !pointBefore ? deltaK[i]\n : !pointAfter ? deltaK[i - 1]\n : (sign(deltaK[i - 1]) !== sign(deltaK[i])) ? 0\n : (deltaK[i - 1] + deltaK[i]) / 2;\n }\n\n monotoneAdjust(points, deltaK, mK);\n\n monotoneCompute(points, mK, indexAxis);\n}\n\nfunction capControlPoint(pt: number, min: number, max: number) {\n return Math.max(Math.min(pt, max), min);\n}\n\nfunction capBezierPoints(points: SplinePoint[], area: ChartArea) {\n let i, ilen, point, inArea, inAreaPrev;\n let inAreaNext = _isPointInArea(points[0], area);\n for (i = 0, ilen = points.length; i < ilen; ++i) {\n inAreaPrev = inArea;\n inArea = inAreaNext;\n inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);\n if (!inArea) {\n continue;\n }\n point = points[i];\n if (inAreaPrev) {\n point.cp1x = capControlPoint(point.cp1x, area.left, area.right);\n point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);\n }\n if (inAreaNext) {\n point.cp2x = capControlPoint(point.cp2x, area.left, area.right);\n point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);\n }\n }\n}\n\n/**\n * @private\n */\nexport function _updateBezierControlPoints(\n points: SplinePoint[],\n options,\n area: ChartArea,\n loop: boolean,\n indexAxis: 'x' | 'y'\n) {\n let i: number, ilen: number, point: SplinePoint, controlPoints: ReturnType<typeof splineCurve>;\n\n // Only consider points that are drawn in case the spanGaps option is used\n if (options.spanGaps) {\n points = points.filter((pt) => !pt.skip);\n }\n\n if (options.cubicInterpolationMode === 'monotone') {\n splineCurveMonotone(points, indexAxis);\n } else {\n let prev = loop ? points[points.length - 1] : points[0];\n for (i = 0, ilen = points.length; i < ilen; ++i) {\n point = points[i];\n controlPoints = splineCurve(\n prev,\n point,\n points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen],\n options.tension\n );\n point.cp1x = controlPoints.previous.x;\n point.cp1y = controlPoints.previous.y;\n point.cp2x = controlPoints.next.x;\n point.cp2y = controlPoints.next.y;\n prev = point;\n }\n }\n\n if (options.capBezierPoints) {\n capBezierPoints(points, area);\n }\n}\n","import type {ChartArea, Scale} from '../types/index.js';\nimport type PrivateChart from '../core/core.controller.js';\nimport type {Chart, ChartEvent} from '../types.js';\nimport {INFINITY} from './helpers.math.js';\n\n/**\n * @private\n */\nexport function _isDomSupported(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * @private\n */\nexport function _getParentNode(domNode: HTMLCanvasElement): HTMLCanvasElement {\n let parent = domNode.parentNode;\n if (parent && parent.toString() === '[object ShadowRoot]') {\n parent = (parent as ShadowRoot).host;\n }\n return parent as HTMLCanvasElement;\n}\n\n/**\n * convert max-width/max-height values that may be percentages into a number\n * @private\n */\n\nfunction parseMaxStyle(styleValue: string | number, node: HTMLElement, parentProperty: string) {\n let valueInPixels: number;\n if (typeof styleValue === 'string') {\n valueInPixels = parseInt(styleValue, 10);\n\n if (styleValue.indexOf('%') !== -1) {\n // percentage * size in dimension\n valueInPixels = (valueInPixels / 100) * node.parentNode[parentProperty];\n }\n } else {\n valueInPixels = styleValue;\n }\n\n return valueInPixels;\n}\n\nconst getComputedStyle = (element: HTMLElement): CSSStyleDeclaration =>\n element.ownerDocument.defaultView.getComputedStyle(element, null);\n\nexport function getStyle(el: HTMLElement, property: string): string {\n return getComputedStyle(el).getPropertyValue(property);\n}\n\nconst positions = ['top', 'right', 'bottom', 'left'];\nfunction getPositionedStyle(styles: CSSStyleDeclaration, style: string, suffix?: string): ChartArea {\n const result = {} as ChartArea;\n suffix = suffix ? '-' + suffix : '';\n for (let i = 0; i < 4; i++) {\n const pos = positions[i];\n result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;\n }\n result.width = result.left + result.right;\n result.height = result.top + result.bottom;\n return result;\n}\n\nconst useOffsetPos = (x: number, y: number, target: HTMLElement | EventTarget) =>\n (x > 0 || y > 0) && (!target || !(target as HTMLElement).shadowRoot);\n\n/**\n * @param e\n * @param canvas\n * @returns Canvas position\n */\nfunction getCanvasPosition(\n e: Event | TouchEvent | MouseEvent,\n canvas: HTMLCanvasElement\n): {\n x: number;\n y: number;\n box: boolean;\n } {\n const touches = (e as TouchEvent).touches;\n const source = (touches && touches.length ? touches[0] : e) as MouseEvent;\n const {offsetX, offsetY} = source as MouseEvent;\n let box = false;\n let x, y;\n if (useOffsetPos(offsetX, offsetY, e.target)) {\n x = offsetX;\n y = offsetY;\n } else {\n const rect = canvas.getBoundingClientRect();\n x = source.clientX - rect.left;\n y = source.clientY - rect.top;\n box = true;\n }\n return {x, y, box};\n}\n\n/**\n * Gets an event's x, y coordinates, relative to the chart area\n * @param event\n * @param chart\n * @returns x and y coordinates of the event\n */\n\nexport function getRelativePosition(\n event: Event | ChartEvent | TouchEvent | MouseEvent,\n chart: Chart | PrivateChart\n): { x: number; y: number } {\n if ('native' in event) {\n return event;\n }\n\n const {canvas, currentDevicePixelRatio} = chart;\n const style = getComputedStyle(canvas);\n const borderBox = style.boxSizing === 'border-box';\n const paddings = getPositionedStyle(style, 'padding');\n const borders = getPositionedStyle(style, 'border', 'width');\n const {x, y, box} = getCanvasPosition(event, canvas);\n const xOffset = paddings.left + (box && borders.left);\n const yOffset = paddings.top + (box && borders.top);\n\n let {width, height} = chart;\n if (borderBox) {\n width -= paddings.width + borders.width;\n height -= paddings.height + borders.height;\n }\n return {\n x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),\n y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)\n };\n}\n\nfunction getContainerSize(canvas: HTMLCanvasElement, width: number, height: number): Partial<Scale> {\n let maxWidth: number, maxHeight: number;\n\n if (width === undefined || height === undefined) {\n const container = canvas && _getParentNode(canvas);\n if (!container) {\n width = canvas.clientWidth;\n height = canvas.clientHeight;\n } else {\n const rect = container.getBoundingClientRect(); // this is the border box of the container\n const containerStyle = getComputedStyle(container);\n const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');\n const containerPadding = getPositionedStyle(containerStyle, 'padding');\n width = rect.width - containerPadding.width - containerBorder.width;\n height = rect.height - containerPadding.height - containerBorder.height;\n maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');\n maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');\n }\n }\n return {\n width,\n height,\n maxWidth: maxWidth || INFINITY,\n maxHeight: maxHeight || INFINITY\n };\n}\n\nconst round1 = (v: number) => Math.round(v * 10) / 10;\n\n// eslint-disable-next-line complexity\nexport function getMaximumSize(\n canvas: HTMLCanvasElement,\n bbWidth?: number,\n bbHeight?: number,\n aspectRatio?: number\n): { width: number; height: number } {\n const style = getComputedStyle(canvas);\n const margins = getPositionedStyle(style, 'margin');\n const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;\n const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;\n const containerSize = getContainerSize(canvas, bbWidth, bbHeight);\n let {width, height} = containerSize;\n\n if (style.boxSizing === 'content-box') {\n const borders = getPositionedStyle(style, 'border', 'width');\n const paddings = getPositionedStyle(style, 'padding');\n width -= paddings.width + borders.width;\n height -= paddings.height + borders.height;\n }\n width = Math.max(0, width - margins.width);\n height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);\n width = round1(Math.min(width, maxWidth, containerSize.maxWidth));\n height = round1(Math.min(height, maxHeight, containerSize.maxHeight));\n if (width && !height) {\n // https://github.com/chartjs/Chart.js/issues/4659\n // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)\n height = round1(width / 2);\n }\n\n const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;\n\n if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {\n height = containerSize.height;\n width = round1(Math.floor(height * aspectRatio));\n }\n\n return {width, height};\n}\n\n/**\n * @param chart\n * @param forceRatio\n * @param forceStyle\n * @returns True if the canvas context size or transformation has changed.\n */\nexport function retinaScale(\n chart: Chart | PrivateChart,\n forceRatio: number,\n forceStyle?: boolean\n): boolean | void {\n const pixelRatio = forceRatio || 1;\n const deviceHeight = round1(chart.height * pixelRatio);\n const deviceWidth = round1(chart.width * pixelRatio);\n\n (chart as PrivateChart).height = round1(chart.height);\n (chart as PrivateChart).width = round1(chart.width);\n\n const canvas = chart.canvas;\n\n // If no style has been set on the canvas, the render size is used as display size,\n // making the chart visually bigger, so let's enforce it to the \"correct\" values.\n // See https://github.com/chartjs/Chart.js/issues/3575\n if (canvas.style && (forceStyle || (!canvas.style.height && !canvas.style.width))) {\n canvas.style.height = `${chart.height}px`;\n canvas.style.width = `${chart.width}px`;\n }\n\n if (chart.currentDevicePixelRatio !== pixelRatio\n || canvas.height !== deviceHeight\n || canvas.width !== deviceWidth) {\n (chart as PrivateChart).currentDevicePixelRatio = pixelRatio;\n canvas.height = deviceHeight;\n canvas.width = deviceWidth;\n chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);\n return true;\n }\n return false;\n}\n\n/**\n * Detects support for options object argument in addEventListener.\n * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n * @private\n */\nexport const supportsEventListenerOptions = (function() {\n let passiveSupported = false;\n try {\n const options = {\n get passive() { // This function will be called when the browser attempts to access the passive property.\n passiveSupported = true;\n return false;\n }\n } as EventListenerOptions;\n\n if (_isDomSupported()) {\n window.addEventListener('test', null, options);\n window.removeEventListener('test', null, options);\n }\n } catch (e) {\n // continue regardless of error\n }\n return passiveSupported;\n}());\n\n/**\n * The \"used\" size is the final value of a dimension property after all calculations have\n * been performed. This method uses the computed style of `element` but returns undefined\n * if the computed style is not expressed in pixels. That can happen in some cases where\n * `element` has a size relative to its parent and this last one is not yet displayed,\n * for example because of `display: none` on a parent node.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n * @returns Size in pixels or undefined if unknown.\n */\n\nexport function readUsedSize(\n element: HTMLElement,\n property: 'width' | 'height'\n): number | undefined {\n const value = getStyle(element, property);\n const matches = value && value.match(/^(\\d+)(\\.\\d+)?px$/);\n return matches ? +matches[1] : undefined;\n}\n","import type {Point, SplinePoint} from '../types/geometric.js';\n\n/**\n * @private\n */\nexport function _pointInLine(p1: Point, p2: Point, t: number, mode?) { // eslint-disable-line @typescript-eslint/no-unused-vars\n return {\n x: p1.x + t * (p2.x - p1.x),\n y: p1.y + t * (p2.y - p1.y)\n };\n}\n\n/**\n * @private\n */\nexport function _steppedInterpolation(\n p1: Point,\n p2: Point,\n t: number, mode: 'middle' | 'after' | unknown\n) {\n return {\n x: p1.x + t * (p2.x - p1.x),\n y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y\n : mode === 'after' ? t < 1 ? p1.y : p2.y\n : t > 0 ? p2.y : p1.y\n };\n}\n\n/**\n * @private\n */\nexport function _bezierInterpolation(p1: SplinePoint, p2: SplinePoint, t: number, mode?) { // eslint-disable-line @typescript-eslint/no-unused-vars\n const cp1 = {x: p1.cp2x, y: p1.cp2y};\n const cp2 = {x: p2.cp1x, y: p2.cp1y};\n const a = _pointInLine(p1, cp1, t);\n const b = _pointInLine(cp1, cp2, t);\n const c = _pointInLine(cp2, p2, t);\n const d = _pointInLine(a, b, t);\n const e = _pointInLine(b, c, t);\n return _pointInLine(d, e, t);\n}\n","export interface RTLAdapter {\n x(x: number): number;\n setWidth(w: number): void;\n textAlign(align: 'center' | 'left' | 'right'): 'center' | 'left' | 'right';\n xPlus(x: number, value: number): number;\n leftForLtr(x: number, itemWidth: number): number;\n}\n\nconst getRightToLeftAdapter = function(rectX: number, width: number): RTLAdapter {\n return {\n x(x) {\n return rectX + rectX + width - x;\n },\n setWidth(w) {\n width = w;\n },\n textAlign(align) {\n if (align === 'center') {\n return align;\n }\n return align === 'right' ? 'left' : 'right';\n },\n xPlus(x, value) {\n return x - value;\n },\n leftForLtr(x, itemWidth) {\n return x - itemWidth;\n },\n };\n};\n\nconst getLeftToRightAdapter = function(): RTLAdapter {\n return {\n x(x) {\n return x;\n },\n setWidth(w) { // eslint-disable-line no-unused-vars\n },\n textAlign(align) {\n return align;\n },\n xPlus(x, value) {\n return x + value;\n },\n leftForLtr(x, _itemWidth) { // eslint-disable-line @typescript-eslint/no-unused-vars\n return x;\n },\n };\n};\n\nexport function getRtlAdapter(rtl: boolean, rectX: number, width: number) {\n return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();\n}\n\nexport function overrideTextDirection(ctx: CanvasRenderingContext2D, direction: 'ltr' | 'rtl') {\n let style: CSSStyleDeclaration, original: [string, string];\n if (direction === 'ltr' || direction === 'rtl') {\n style = ctx.canvas.style;\n original = [\n style.getPropertyValue('direction'),\n style.getPropertyPriority('direction'),\n ];\n\n style.setProperty('direction', direction, 'important');\n (ctx as { prevTextDirection?: [string, string] }).prevTextDirection = original;\n }\n}\n\nexport function restoreTextDirection(ctx: CanvasRenderingContext2D, original?: [string, string]) {\n if (original !== undefined) {\n delete (ctx as { prevTextDirection?: [string, string] }).prevTextDirection;\n ctx.canvas.style.setProperty('direction', original[0], original[1]);\n }\n}\n","import {_angleBetween, _angleDiff, _isBetween, _normalizeAngle} from './helpers.math.js';\nimport {createContext} from './helpers.options.js';\nimport {isPatternOrGradient} from './helpers.color.js';\n\n/**\n * @typedef { import('../elements/element.line.js').default } LineElement\n * @typedef { import('../elements/element.point.js').default } PointElement\n * @typedef {{start: number, end: number, loop: boolean, style?: any}} Segment\n */\n\nfunction propertyFn(property) {\n if (property === 'angle') {\n return {\n between: _angleBetween,\n compare: _angleDiff,\n normalize: _normalizeAngle,\n };\n }\n return {\n between: _isBetween,\n compare: (a, b) => a - b,\n normalize: x => x\n };\n}\n\nfunction normalizeSegment({start, end, count, loop, style}) {\n return {\n start: start % count,\n end: end % count,\n loop: loop && (end - start + 1) % count === 0,\n style\n };\n}\n\nfunction getSegment(segment, points, bounds) {\n const {property, start: startBound, end: endBound} = bounds;\n const {between, normalize} = propertyFn(property);\n const count = points.length;\n // eslint-disable-next-line prefer-const\n let {start, end, loop} = segment;\n let i, ilen;\n\n if (loop) {\n start += count;\n end += count;\n for (i = 0, ilen = count; i < ilen; ++i) {\n if (!between(normalize(points[start % count][property]), startBound, endBound)) {\n break;\n }\n start--;\n end--;\n }\n start %= count;\n end %= count;\n }\n\n if (end < start) {\n end += count;\n }\n return {start, end, loop, style: segment.style};\n}\n\n/**\n * Returns the sub-segment(s) of a line segment that fall in the given bounds\n * @param {object} segment\n * @param {number} segment.start - start index of the segment, referring the points array\n * @param {number} segment.end - end index of the segment, referring the points array\n * @param {boolean} segment.loop - indicates that the segment is a loop\n * @param {object} [segment.style] - segment style\n * @param {PointElement[]} points - the points that this segment refers to\n * @param {object} [bounds]\n * @param {string} bounds.property - the property of a `PointElement` we are bounding. `x`, `y` or `angle`.\n * @param {number} bounds.start - start value of the property\n * @param {number} bounds.end - end value of the property\n * @private\n **/\nexport function _boundSegment(segment, points, bounds) {\n if (!bounds) {\n return [segment];\n }\n\n const {property, start: startBound, end: endBound} = bounds;\n const count = points.length;\n const {compare, between, normalize} = propertyFn(property);\n const {start, end, loop, style} = getSegment(segment, points, bounds);\n\n const result = [];\n let inside = false;\n let subStart = null;\n let value, point, prevValue;\n\n const startIsBefore = () => between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;\n const endIsBefore = () => compare(endBound, value) === 0 || between(endBound, prevValue, value);\n const shouldStart = () => inside || startIsBefore();\n const shouldStop = () => !inside || endIsBefore();\n\n for (let i = start, prev = start; i <= end; ++i) {\n point = points[i % count];\n\n if (point.skip) {\n continue;\n }\n\n value = normalize(point[property]);\n\n if (value === prevValue) {\n continue;\n }\n\n inside = between(value, startBound, endBound);\n\n if (subStart === null && shouldStart()) {\n subStart = compare(value, startBound) === 0 ? i : prev;\n }\n\n if (subStart !== null && shouldStop()) {\n result.push(normalizeSegment({start: subStart, end: i, loop, count, style}));\n subStart = null;\n }\n prev = i;\n prevValue = value;\n }\n\n if (subStart !== null) {\n result.push(normalizeSegment({start: subStart, end, loop, count, style}));\n }\n\n return result;\n}\n\n\n/**\n * Returns the segments of the line that are inside given bounds\n * @param {LineElement} line\n * @param {object} [bounds]\n * @param {string} bounds.property - the property we are bounding with. `x`, `y` or `angle`.\n * @param {number} bounds.start - start value of the `property`\n * @param {number} bounds.end - end value of the `property`\n * @private\n */\nexport function _boundSegments(line, bounds) {\n const result = [];\n const segments = line.segments;\n\n for (let i = 0; i < segments.length; i++) {\n const sub = _boundSegment(segments[i], line.points, bounds);\n if (sub.length) {\n result.push(...sub);\n }\n }\n return result;\n}\n\n/**\n * Find start and end index of a line.\n */\nfunction findStartAndEnd(points, count, loop, spanGaps) {\n let start = 0;\n let end = count - 1;\n\n if (loop && !spanGaps) {\n // loop and not spanning gaps, first find a gap to start from\n while (start < count && !points[start].skip) {\n start++;\n }\n }\n\n // find first non skipped point (after the first gap possibly)\n while (start < count && points[start].skip) {\n start++;\n }\n\n // if we looped to count, start needs to be 0\n start %= count;\n\n if (loop) {\n // loop will go past count, if start > 0\n end += start;\n }\n\n while (end > start && points[end % count].skip) {\n end--;\n }\n\n // end could be more than count, normalize\n end %= count;\n\n return {start, end};\n}\n\n/**\n * Compute solid segments from Points, when spanGaps === false\n * @param {PointElement[]} points - the points\n * @param {number} start - start index\n * @param {number} max - max index (can go past count on a loop)\n * @param {boolean} loop - boolean indicating that this would be a loop if no gaps are found\n */\nfunction solidSegments(points, start, max, loop) {\n const count = points.length;\n const result = [];\n let last = start;\n let prev = points[start];\n let end;\n\n for (end = start + 1; end <= max; ++end) {\n const cur = points[end % count];\n if (cur.skip || cur.stop) {\n if (!prev.skip) {\n loop = false;\n result.push({start: start % count, end: (end - 1) % count, loop});\n // @ts-ignore\n start = last = cur.stop ? end : null;\n }\n } else {\n last = end;\n if (prev.skip) {\n start = end;\n }\n }\n prev = cur;\n }\n\n if (last !== null) {\n result.push({start: start % count, end: last % count, loop});\n }\n\n return result;\n}\n\n/**\n * Compute the continuous segments that define the whole line\n * There can be skipped points within a segment, if spanGaps is true.\n * @param {LineElement} line\n * @param {object} [segmentOptions]\n * @return {Segment[]}\n * @private\n */\nexport function _computeSegments(line, segmentOptions) {\n const points = line.points;\n const spanGaps = line.options.spanGaps;\n const count = points.length;\n\n if (!count) {\n return [];\n }\n\n const loop = !!line._loop;\n const {start, end} = findStartAndEnd(points, count, loop, spanGaps);\n\n if (spanGaps === true) {\n return splitByStyles(line, [{start, end, loop}], points, segmentOptions);\n }\n\n const max = end < start ? end + count : end;\n const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;\n return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);\n}\n\n/**\n * @param {Segment[]} segments\n * @param {PointElement[]} points\n * @param {object} [segmentOptions]\n * @return {Segment[]}\n */\nfunction splitByStyles(line, segments, points, segmentOptions) {\n if (!segmentOptions || !segmentOptions.setContext || !points) {\n return segments;\n }\n return doSplitByStyles(line, segments, points, segmentOptions);\n}\n\n/**\n * @param {LineElement} line\n * @param {Segment[]} segments\n * @param {PointElement[]} points\n * @param {object} [segmentOptions]\n * @return {Segment[]}\n */\nfunction doSplitByStyles(line, segments, points, segmentOptions) {\n const chartContext = line._chart.getContext();\n const baseStyle = readStyle(line.options);\n const {_datasetIndex: datasetIndex, options: {spanGaps}} = line;\n const count = points.length;\n const result = [];\n let prevStyle = baseStyle;\n let start = segments[0].start;\n let i = start;\n\n function addStyle(s, e, l, st) {\n const dir = spanGaps ? -1 : 1;\n if (s === e) {\n return;\n }\n // Style can not start/end on a skipped point, adjust indices accordingly\n s += count;\n while (points[s % count].skip) {\n s -= dir;\n }\n while (points[e % count].skip) {\n e += dir;\n }\n if (s % count !== e % count) {\n result.push({start: s % count, end: e % count, loop: l, style: st});\n prevStyle = st;\n start = e % count;\n }\n }\n\n for (const segment of segments) {\n start = spanGaps ? start : segment.start;\n let prev = points[start % count];\n let style;\n for (i = start + 1; i <= segment.end; i++) {\n const pt = points[i % count];\n style = readStyle(segmentOptions.setContext(createContext(chartContext, {\n type: 'segment',\n p0: prev,\n p1: pt,\n p0DataIndex: (i - 1) % count,\n p1DataIndex: i % count,\n datasetIndex\n })));\n if (styleChanged(style, prevStyle)) {\n addStyle(start, i - 1, segment.loop, prevStyle);\n }\n prev = pt;\n prevStyle = style;\n }\n if (start < i - 1) {\n addStyle(start, i - 1, segment.loop, prevStyle);\n }\n }\n\n return result;\n}\n\nfunction readStyle(options) {\n return {\n backgroundColor: options.backgroundColor,\n borderCapStyle: options.borderCapStyle,\n borderDash: options.borderDash,\n borderDashOffset: options.borderDashOffset,\n borderJoinStyle: options.borderJoinStyle,\n borderWidth: options.borderWidth,\n borderColor: options.borderColor\n };\n}\n\nfunction styleChanged(style, prevStyle) {\n if (!prevStyle) {\n return false;\n }\n const cache = [];\n const replacer = function(key, value) {\n if (!isPatternOrGradient(value)) {\n return value;\n }\n if (!cache.includes(value)) {\n cache.push(value);\n }\n return cache.indexOf(value);\n };\n return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);\n}\n","import type {Chart, ChartArea, ChartMeta, Scale, TRBL} from '../types/index.js';\n\nfunction getSizeForArea(scale: Scale, chartArea: ChartArea, field: keyof ChartArea) {\n return scale.options.clip ? scale[field] : chartArea[field];\n}\n\nfunction getDatasetArea(meta: ChartMeta, chartArea: ChartArea): TRBL {\n const {xScale, yScale} = meta;\n if (xScale && yScale) {\n return {\n left: getSizeForArea(xScale, chartArea, 'left'),\n right: getSizeForArea(xScale, chartArea, 'right'),\n top: getSizeForArea(yScale, chartArea, 'top'),\n bottom: getSizeForArea(yScale, chartArea, 'bottom')\n };\n }\n return chartArea;\n}\n\nexport function getDatasetClipArea(chart: Chart, meta: ChartMeta): TRBL | false {\n const clip = meta._clip;\n if (clip.disabled) {\n return false;\n }\n const area = getDatasetArea(meta, chart.chartArea);\n\n return {\n left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left),\n right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right),\n top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top),\n bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom)\n };\n}\n"],"names":["noop","uid","id","isNullOrUndef","value","undefined","isArray","Array","type","Object","prototype","toString","call","slice","isObject","isNumberFinite","Number","isFinite","finiteOrDefault","defaultValue","valueOrDefault","toPercentage","dimension","endsWith","parseFloat","toDimension","callback","fn","args","thisArg","apply","each","loopable","reverse","i","len","keys","length","_elementsEqual","a0","a1","ilen","v0","v1","datasetIndex","index","clone","source","map","target","create","klen","k","isValidKey","key","indexOf","_merger","options","tval","sval","merge","sources","merger","current","mergeIf","_mergerIf","hasOwnProperty","_deprecated","scope","previous","console","warn","keyResolvers","v","x","o","y","_splitKey","parts","split","tmp","part","push","_getKeyResolver","obj","resolveObjectKey","resolver","_capitalize","str","charAt","toUpperCase","defined","isFunction","setsEqual","a","b","size","item","has","_isClickEvent","e","PI","Math","TAU","PITAU","INFINITY","POSITIVE_INFINITY","RAD_PER_DEG","HALF_PI","QUARTER_PI","TWO_THIRDS_PI","log10","sign","almostEquals","epsilon","abs","niceNum","range","roundedRange","round","niceRange","pow","floor","fraction","niceFraction","_factorize","result","sqrt","sort","pop","isNonPrimitive","n","Symbol","toPrimitive","isNumber","isNaN","almostWhole","rounded","_setMinAndMaxByKey","array","property","min","max","toRadians","degrees","toDegrees","radians","_decimalPlaces","isFiniteNumber","p","getAngleFromPoint","centrePoint","anglePoint","distanceFromXCenter","distanceFromYCenter","radialDistanceFromCenter","angle","atan2","distance","distanceBetweenPoints","pt1","pt2","_angleDiff","_normalizeAngle","_angleBetween","start","end","sameAngleIsFullCircle","s","angleToStart","angleToEnd","startToAngle","endToAngle","_limitValue","_int16Range","_isBetween","_lookup","table","cmp","hi","lo","mid","_lookupByKey","last","ti","_rlookupByKey","_filterBetween","values","arrayEvents","listenArrayEvents","listener","_chartjs","listeners","defineProperty","configurable","enumerable","forEach","method","base","res","object","unlistenArrayEvents","stub","splice","_arrayUnique","items","set","Set","from","fontString","pixelSize","fontStyle","fontFamily","requestAnimFrame","window","requestAnimationFrame","throttled","argsToUse","ticking","debounce","delay","timeout","clearTimeout","setTimeout","_toLeftRightCenter","align","_alignStartEnd","_textX","left","right","rtl","check","_getStartAndCountOfVisiblePoints","meta","points","animationsDisabled","pointCount","count","_sorted","iScale","vScale","_parsed","spanGaps","dataset","axis","minDefined","maxDefined","getUserBounds","getPixelForValue","distanceToDefinedLo","findIndex","point","distanceToDefinedHi","_scaleRangesChanged","xScale","yScale","_scaleRanges","newRanges","xmin","xmax","ymin","ymax","changed","assign","atEdge","t","elasticIn","sin","elasticOut","effects","linear","easeInQuad","easeOutQuad","easeInOutQuad","easeInCubic","easeOutCubic","easeInOutCubic","easeInQuart","easeOutQuart","easeInOutQuart","easeInQuint","easeOutQuint","easeInOutQuint","easeInSine","cos","easeOutSine","easeInOutSine","easeInExpo","easeOutExpo","easeInOutExpo","easeInCirc","easeOutCirc","easeInOutCirc","easeInElastic","easeOutElastic","easeInOutElastic","easeInBack","easeOutBack","easeInOutBack","easeInBounce","easeOutBounce","m","d","easeInOutBounce","isPatternOrGradient","color","Color","getHoverColor","saturate","darken","hexString","numbers","colors","applyAnimationsDefaults","defaults","duration","easing","loop","to","describe","_fallback","_indexable","_scriptable","name","properties","active","animation","resize","show","animations","visible","hide","applyLayoutsDefaults","autoPadding","padding","top","bottom","intlCache","Map","getNumberFormat","locale","cacheKey","JSON","stringify","formatter","get","Intl","NumberFormat","formatNumber","num","format","formatters","numeric","tickValue","ticks","chart","notation","delta","maxTick","calculateDelta","logDelta","numDecimal","minimumFractionDigits","maximumFractionDigits","logarithmic","remain","significand","includes","applyScaleDefaults","display","offset","beginAtZero","bounds","clip","grace","grid","lineWidth","drawOnChartArea","drawTicks","tickLength","tickWidth","_ctx","tickColor","border","dash","dashOffset","width","title","text","minRotation","maxRotation","mirror","textStrokeWidth","textStrokeColor","autoSkip","autoSkipPadding","labelOffset","Ticks","minor","major","crossAlign","showLabelBackdrop","backdropColor","backdropPadding","route","startsWith","overrides","descriptors","getScope","node","root","Defaults","constructor","_descriptors","_appliers","backgroundColor","borderColor","datasets","devicePixelRatio","context","platform","getDevicePixelRatio","elements","events","font","family","style","lineHeight","weight","hover","hoverBackgroundColor","ctx","hoverBorderColor","hoverColor","indexAxis","interaction","mode","intersect","includeInvisible","maintainAspectRatio","onHover","onClick","parsing","plugins","responsive","scale","scales","showLine","drawActiveElementsOnTop","override","targetScope","targetName","scopeObject","targetScopeObject","privateName","defineProperties","writable","local","appliers","toFontString","_measureText","data","gc","longest","string","textWidth","measureText","_longestText","arrayOfThings","cache","garbageCollect","save","j","jlen","thing","nestedThing","restore","gcLen","_alignPixel","pixel","currentDevicePixelRatio","halfWidth","clearCanvas","canvas","getContext","resetTransform","clearRect","height","drawPoint","drawPointLegend","w","xOffset","yOffset","cornerRadius","xOffsetW","yOffsetW","pointStyle","rotation","radius","rad","translate","rotate","drawImage","beginPath","ellipse","arc","closePath","moveTo","lineTo","SQRT1_2","rect","fill","borderWidth","stroke","_isPointInArea","area","margin","clipArea","unclipArea","_steppedLineTo","flip","midpoint","_bezierCurveTo","bezierCurveTo","cp1x","cp2x","cp1y","cp2y","setRenderOpts","opts","translation","fillStyle","textAlign","textBaseline","decorateText","line","strikethrough","underline","metrics","actualBoundingBoxLeft","actualBoundingBoxRight","actualBoundingBoxAscent","actualBoundingBoxDescent","yDecoration","strokeStyle","decorationWidth","drawBackdrop","oldColor","fillRect","renderText","lines","strokeWidth","strokeColor","backdrop","strokeText","maxWidth","fillText","addRoundedRectPath","h","topLeft","bottomLeft","bottomRight","topRight","LINE_HEIGHT","FONT_STYLE","toLineHeight","matches","match","numberOrZero","_readValueToProps","props","ret","objProps","read","prop","toTRBL","toTRBLCorners","toPadding","toFont","fallback","parseInt","resolve","inputs","info","cacheable","_addGrace","minmax","change","keepZero","add","createContext","parentContext","_createResolver","scopes","prefixes","rootScopes","getTarget","finalRootScopes","_resolve","toStringTag","_cacheable","_scopes","_rootScopes","_getTarget","Proxy","deleteProperty","_keys","_cached","_resolveWithPrefixes","getOwnPropertyDescriptor","Reflect","getPrototypeOf","getKeysFromAllScopes","ownKeys","storage","_storage","_attachContext","proxy","subProxy","descriptorDefaults","_proxy","_context","_subProxy","_stack","setContext","receiver","_resolveWithContext","allKeys","scriptable","indexable","_allKeys","isScriptable","isIndexable","readKey","prefix","needsSubResolver","_resolveScriptable","_resolveArray","getValue","Error","join","delete","createSubResolver","arr","filter","resolveFallback","parent","addScopes","parentScopes","parentFallback","allScopes","addScopesFromKey","subGetTarget","resolveKeysFromAllScopes","_parseObjectDataRadialScale","_parsing","parsed","r","parse","EPSILON","getPoint","skip","getValueAxis","splineCurve","firstPoint","middlePoint","afterPoint","next","d01","d12","s01","s12","fa","fb","monotoneAdjust","deltaK","mK","pointsLen","alphaK","betaK","tauK","squaredMagnitude","pointCurrent","pointAfter","monotoneCompute","valueAxis","pointBefore","iPixel","vPixel","splineCurveMonotone","slopeDelta","capControlPoint","pt","capBezierPoints","inArea","inAreaPrev","inAreaNext","_updateBezierControlPoints","controlPoints","cubicInterpolationMode","prev","tension","_isDomSupported","document","_getParentNode","domNode","parentNode","host","parseMaxStyle","styleValue","parentProperty","valueInPixels","getComputedStyle","element","ownerDocument","defaultView","getStyle","el","getPropertyValue","positions","getPositionedStyle","styles","suffix","pos","useOffsetPos","shadowRoot","getCanvasPosition","touches","offsetX","offsetY","box","getBoundingClientRect","clientX","clientY","getRelativePosition","event","borderBox","boxSizing","paddings","borders","getContainerSize","maxHeight","container","clientWidth","clientHeight","containerStyle","containerBorder","containerPadding","round1","getMaximumSize","bbWidth","bbHeight","aspectRatio","margins","containerSize","maintainHeight","retinaScale","forceRatio","forceStyle","pixelRatio","deviceHeight","deviceWidth","setTransform","supportsEventListenerOptions","passiveSupported","passive","addEventListener","removeEventListener","readUsedSize","_pointInLine","p1","p2","_steppedInterpolation","_bezierInterpolation","cp1","cp2","c","getRightToLeftAdapter","rectX","setWidth","xPlus","leftForLtr","itemWidth","getLeftToRightAdapter","_itemWidth","getRtlAdapter","overrideTextDirection","direction","original","getPropertyPriority","setProperty","prevTextDirection","restoreTextDirection","propertyFn","between","compare","normalize","normalizeSegment","getSegment","segment","startBound","endBound","_boundSegment","inside","subStart","prevValue","startIsBefore","endIsBefore","shouldStart","shouldStop","_boundSegments","segments","sub","findStartAndEnd","solidSegments","cur","stop","_computeSegments","segmentOptions","_loop","splitByStyles","completeLoop","_fullLoop","doSplitByStyles","chartContext","_chart","baseStyle","readStyle","_datasetIndex","prevStyle","addStyle","l","st","dir","p0","p0DataIndex","p1DataIndex","styleChanged","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","replacer","getSizeForArea","chartArea","field","getDatasetArea","getDatasetClipArea","_clip","disabled"],"mappings":"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"} \ No newline at end of file diff --git a/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js new file mode 100644 index 00000000..20ff2864 --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js @@ -0,0 +1,2788 @@ +/*! + * Chart.js v4.5.1 + * https://www.chartjs.org + * (c) 2025 Chart.js Contributors + * Released under the MIT License + */ +import { Color } from '@kurkle/color'; + +/** + * @namespace Chart.helpers + */ /** + * An empty function that can be used, for example, for optional callback. + */ function noop() { +/* noop */ } +/** + * Returns a unique id, sequentially generated from a global variable. + */ const uid = (()=>{ + let id = 0; + return ()=>id++; +})(); +/** + * Returns true if `value` is neither null nor undefined, else returns false. + * @param value - The value to test. + * @since 2.7.0 + */ function isNullOrUndef(value) { + return value === null || value === undefined; +} +/** + * Returns true if `value` is an array (including typed arrays), else returns false. + * @param value - The value to test. + * @function + */ function isArray(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + const type = Object.prototype.toString.call(value); + if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') { + return true; + } + return false; +} +/** + * Returns true if `value` is an object (excluding null), else returns false. + * @param value - The value to test. + * @since 2.7.0 + */ function isObject(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; +} +/** + * Returns true if `value` is a finite number, else returns false + * @param value - The value to test. + */ function isNumberFinite(value) { + return (typeof value === 'number' || value instanceof Number) && isFinite(+value); +} +/** + * 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. + */ function finiteOrDefault(value, defaultValue) { + return isNumberFinite(value) ? value : defaultValue; +} +/** + * 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. + */ 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; +/** + * 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`. + */ 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]); + } + } +} +/** + * Returns true if the `a0` and `a1` arrays have the same content, else returns false. + * @param a0 - The array to compare + * @param a1 - The array to compare + * @private + */ 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; +} +/** + * Returns a deep copy of `source` without keeping references on objects and arrays. + * @param source - The value to clone. + */ 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; +} +/** + * The default merger when Chart.helpers.merge is called without merger option. + * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. + * @private + */ function _merger(key, target, source, options) { + if (!isValidKey(key)) { + return; + } + const tval = target[key]; + const sval = source[key]; + if (isObject(tval) && isObject(sval)) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + merge(tval, sval, options); + } else { + target[key] = clone(sval); + } +} +function merge(target, source, options) { + const sources = isArray(source) ? source : [ + source + ]; + const ilen = sources.length; + if (!isObject(target)) { + return target; + } + options = options || {}; + const merger = options.merger || _merger; + let current; + for(let i = 0; i < ilen; ++i){ + current = sources[i]; + if (!isObject(current)) { + continue; + } + const keys = Object.keys(current); + for(let k = 0, klen = keys.length; k < klen; ++k){ + merger(keys[k], target, current, options); + } + } + return target; +} +function mergeIf(target, source) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + return merge(target, source, { + merger: _mergerIf + }); +} +/** + * Merges source[key] in target[key] only if target[key] is undefined. + * @private + */ 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); + } +} +/** + * @private + */ function _deprecated(scope, value, previous, current) { + if (value !== undefined) { + console.warn(scope + ': "' + previous + '" is deprecated. Please use "' + current + '" instead'); + } +} +// resolveObjectKey resolver cache +const keyResolvers = { + // Chart.helpers.core resolveObjectKey should resolve empty key to root object + '': (v)=>v, + // default resolvers + x: (o)=>o.x, + y: (o)=>o.y +}; +/** + * @private + */ function _splitKey(key) { + const parts = key.split('.'); + const keys = []; + let tmp = ''; + for (const part of parts){ + tmp += part; + if (tmp.endsWith('\\')) { + tmp = tmp.slice(0, -1) + '.'; + } else { + keys.push(tmp); + tmp = ''; + } + } + return keys; +} +function _getKeyResolver(key) { + const keys = _splitKey(key); + return (obj)=>{ + for (const k of keys){ + if (k === '') { + break; + } + obj = obj && obj[k]; + } + return obj; + }; +} +function resolveObjectKey(obj, key) { + const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key)); + return resolver(obj); +} +/** + * @private + */ function _capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} +const defined = (value)=>typeof value !== 'undefined'; +const isFunction = (value)=>typeof value === 'function'; +// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384 +const setsEqual = (a, b)=>{ + if (a.size !== b.size) { + return false; + } + for (const item of a){ + if (!b.has(item)) { + return false; + } + } + return true; +}; +/** + * @param e - The event + * @private + */ function _isClickEvent(e) { + return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu'; +} + +/** + * @alias Chart.helpers.math + * @namespace + */ const PI = Math.PI; +const TAU = 2 * PI; +const PITAU = TAU + PI; +const INFINITY = Number.POSITIVE_INFINITY; +const RAD_PER_DEG = PI / 180; +const HALF_PI = PI / 2; +const QUARTER_PI = PI / 4; +const TWO_THIRDS_PI = PI * 2 / 3; +const log10 = Math.log10; +const sign = Math.sign; +function almostEquals(x, y, epsilon) { + return Math.abs(x - y) < epsilon; +} +/** + * Implementation of the nice number algorithm used in determining where axis labels will go + */ 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; +} +/** + * Returns an array of factors sorted from 1 to sqrt(value) + * @private + */ 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; +} +/** + * Verifies that attempting to coerce n to string or number won't throw a TypeError. + */ function isNonPrimitive(n) { + return typeof n === 'symbol' || typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n); +} +function isNumber(n) { + return !isNonPrimitive(n) && !isNaN(parseFloat(n)) && isFinite(n); +} +function almostWhole(x, epsilon) { + const rounded = Math.round(x); + return rounded - epsilon <= x && rounded + epsilon >= x; +} +/** + * @private + */ 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); +} +/** + * Returns the number of decimal places + * i.e. the number of digits after the decimal point, of the value of this Number. + * @param x - A number. + * @returns The number of decimal places. + * @private + */ 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; +} +// Gets the angle from vertical upright to the point about a centre. +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; // make sure the returned angle is in the range of (-PI/2, 3PI/2] + } + 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)); +} +/** + * Shortest distance between angles, in either direction. + * @private + */ function _angleDiff(a, b) { + return (a - b + PITAU) % TAU - PI; +} +/** + * Normalize angle to be between 0 and 2*PI + * @private + */ function _normalizeAngle(a) { + return (a % TAU + TAU) % TAU; +} +/** + * @private + */ 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; +} +/** + * Limit `value` between `min` and `max` + * @param value + * @param min + * @param max + * @private + */ function _limitValue(value, min, max) { + return Math.max(min, Math.min(max, value)); +} +/** + * @param {number} value + * @private + */ function _int16Range(value) { + return _limitValue(value, -32768, 32767); +} +/** + * @param value + * @param start + * @param end + * @param [epsilon] + * @private + */ function _isBetween(value, start, end, epsilon = 1e-6) { + return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon; +} + +function _lookup(table, value, cmp) { + cmp = cmp || ((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 + }; +} +/** + * Binary search + * @param table - the table search. must be sorted! + * @param key - property name for the value in each entry + * @param value - value to find + * @param last - lookup last index + * @private + */ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{ + const ti = table[index][key]; + return ti < value || ti === value && table[index + 1][key] === value; + } : (index)=>table[index][key] < value); +/** + * Reverse binary search + * @param table - the table search. must be sorted! + * @param key - property name for the value in each entry + * @param value - value to find + * @private + */ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value); +/** + * Return subset of `values` between `min` and `max` inclusive. + * Values are assumed to be in sorted order. + * @param values - sorted array of values + * @param min - min value + * @param max - max 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; +} +/** + * @param items + */ function _arrayUnique(items) { + const set = new Set(items); + if (set.size === items.length) { + return items; + } + return Array.from(set); +} + +function fontString(pixelSize, fontStyle, fontFamily) { + return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; +} +/** +* Request animation polyfill +*/ const requestAnimFrame = function() { + if (typeof window === 'undefined') { + return function(callback) { + return callback(); + }; + } + return window.requestAnimationFrame; +}(); +/** + * Throttles calling `fn` once per animation frame + * Latest arguments are used on the actual call + */ function throttled(fn, thisArg) { + let argsToUse = []; + let ticking = false; + return function(...args) { + // Save the args for use later + argsToUse = args; + if (!ticking) { + ticking = true; + requestAnimFrame.call(window, ()=>{ + ticking = false; + fn.apply(thisArg, argsToUse); + }); + } + }; +} +/** + * Debounces calling `fn` for `delay` ms + */ 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; + }; +} +/** + * Converts 'start' to 'left', 'end' to 'right' and others to 'center' + * @private + */ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center'; +/** + * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center` + * @private + */ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2; +/** + * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left` + * @private + */ const _textX = (align, left, right, rtl)=>{ + const check = rtl ? 'left' : 'right'; + return align === check ? right : align === 'center' ? (left + right) / 2 : left; +}; +/** + * Return start and count of visible points. + * @private + */ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) { + const pointCount = points.length; + let start = 0; + let count = pointCount; + if (meta._sorted) { + const { iScale , vScale , _parsed } = meta; + const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null; + const axis = iScale.axis; + const { min , max , minDefined , maxDefined } = iScale.getUserBounds(); + if (minDefined) { + start = Math.min(// @ts-expect-error Need to type _parsed + _lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey + animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo); + if (spanGaps) { + const distanceToDefinedLo = _parsed.slice(0, start + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis])); + start -= Math.max(0, distanceToDefinedLo); + } + start = _limitValue(start, 0, pointCount - 1); + } + if (maxDefined) { + let end = Math.max(// @ts-expect-error Need to type _parsed + _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey + animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1); + if (spanGaps) { + const distanceToDefinedHi = _parsed.slice(end - 1).findIndex((point)=>!isNullOrUndef(point[vScale.axis])); + end += Math.max(0, distanceToDefinedHi); + } + count = _limitValue(end, start, pointCount) - start; + } else { + count = pointCount - start; + } + } + return { + start, + count + }; +} +/** + * Checks if the scale ranges have changed. + * @param {object} meta - dataset meta. + * @returns {boolean} + * @private + */ function _scaleRangesChanged(meta) { + const { xScale , yScale , _scaleRanges } = meta; + const newRanges = { + xmin: xScale.min, + xmax: xScale.max, + ymin: yScale.min, + ymax: yScale.max + }; + if (!_scaleRanges) { + meta._scaleRanges = newRanges; + return true; + } + const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max; + Object.assign(_scaleRanges, newRanges); + return changed; +} + +const atEdge = (t)=>t === 0 || t === 1; +const elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p)); +const elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1; +/** + * Easing functions adapted from Robert Penner's easing equations. + * @namespace Chart.helpers.easing.effects + * @see http://www.robertpenner.com/easing/ + */ const effects = { + linear: (t)=>t, + easeInQuad: (t)=>t * t, + easeOutQuad: (t)=>-t * (t - 2), + easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1), + easeInCubic: (t)=>t * t * t, + easeOutCubic: (t)=>(t -= 1) * t * t + 1, + easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2), + easeInQuart: (t)=>t * t * t * t, + easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1), + easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2), + easeInQuint: (t)=>t * t * t * t * t, + easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1, + easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2), + easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1, + easeOutSine: (t)=>Math.sin(t * HALF_PI), + easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1), + easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)), + easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1, + easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2), + easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1), + easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t), + easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1), + easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3), + easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3), + easeInOutElastic (t) { + const s = 0.1125; + const p = 0.45; + return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p); + }, + easeInBack (t) { + const s = 1.70158; + return t * t * ((s + 1) * t - s); + }, + easeOutBack (t) { + const s = 1.70158; + return (t -= 1) * t * ((s + 1) * t + s) + 1; + }, + easeInOutBack (t) { + let s = 1.70158; + if ((t /= 0.5) < 1) { + return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2); + }, + easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t), + easeOutBounce (t) { + const m = 7.5625; + const d = 2.75; + if (t < 1 / d) { + return m * t * t; + } + if (t < 2 / d) { + return m * (t -= 1.5 / d) * t + 0.75; + } + if (t < 2.5 / d) { + return m * (t -= 2.25 / d) * t + 0.9375; + } + return m * (t -= 2.625 / d) * t + 0.984375; + }, + easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5 +}; + +function isPatternOrGradient(value) { + if (value && typeof value === 'object') { + const type = value.toString(); + return type === '[object CanvasPattern]' || type === '[object CanvasGradient]'; + } + return false; +} +function color(value) { + return isPatternOrGradient(value) ? value : new Color(value); +} +function getHoverColor(value) { + return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString(); +} + +const numbers = [ + 'x', + 'y', + 'borderWidth', + 'radius', + 'tension' +]; +const colors = [ + 'color', + 'borderColor', + 'backgroundColor' +]; +function applyAnimationsDefaults(defaults) { + defaults.set('animation', { + delay: undefined, + duration: 1000, + easing: 'easeOutQuart', + fn: undefined, + from: undefined, + loop: undefined, + to: undefined, + type: undefined + }); + 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 + } + } + } + }); +} + +function applyLayoutsDefaults(defaults) { + defaults.set('layout', { + autoPadding: true, + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }); +} + +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 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 = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0); + const options = { + notation, + minimumFractionDigits: numDecimal, + maximumFractionDigits: numDecimal + }; + Object.assign(options, this.options.ticks.format); + return formatNumber(tickValue, locale, options); + }, + logarithmic (tickValue, index, ticks) { + if (tickValue === 0) { + return '0'; + } + const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue))); + if ([ + 1, + 2, + 3, + 5, + 10, + 15 + ].includes(remain) || index > 0.8 * ticks.length) { + 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 +}; + +function applyScaleDefaults(defaults) { + defaults.set('scale', { + display: true, + offset: false, + reverse: false, + beginAtZero: false, + bounds: 'ticks', + clip: true, + grace: 0, + grid: { + display: true, + lineWidth: 1, + drawOnChartArea: true, + drawTicks: true, + tickLength: 8, + tickWidth: (_ctx, options)=>options.lineWidth, + tickColor: (_ctx, options)=>options.color, + offset: false + }, + border: { + display: true, + dash: [], + dashOffset: 0.0, + width: 1 + }, + title: { + display: false, + text: '', + padding: { + top: 4, + bottom: 4 + } + }, + ticks: { + minRotation: 0, + maxRotation: 50, + mirror: false, + textStrokeWidth: 0, + textStrokeColor: '', + padding: 3, + display: true, + autoSkip: true, + autoSkipPadding: 3, + labelOffset: 0, + callback: Ticks.formatters.values, + minor: {}, + major: {}, + align: 'center', + crossAlign: 'near', + showLabelBackdrop: false, + backdropColor: 'rgba(255, 255, 255, 0.75)', + backdropPadding: 2 + } + }); + defaults.route('scale.ticks', 'color', '', 'color'); + defaults.route('scale.grid', 'color', '', 'borderColor'); + defaults.route('scale.border', 'color', '', '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' && name !== 'dash' + }); + defaults.describe('scales', { + _fallback: 'scale' + }); + defaults.describe('scale.ticks', { + _scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback', + _indexable: (name)=>name !== 'backdropPadding' + }); +} + +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, _appliers){ + 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, + includeInvisible: false + }; + 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); + this.apply(_appliers); + } + set(scope, values) { + return set(this, scope, values); + } + get(scope) { + return getScope$1(this, scope); + } + describe(scope, values) { + return set(descriptors, scope, values); + } + override(scope, values) { + return set(overrides, scope, values); + } + route(scope, name, targetScope, targetName) { + const scopeObject = getScope$1(this, scope); + const targetScopeObject = getScope$1(this, targetScope); + const privateName = '_' + name; + Object.defineProperties(scopeObject, { + [privateName]: { + value: scopeObject[name], + writable: true + }, + [name]: { + enumerable: true, + get () { + const local = this[privateName]; + const target = targetScopeObject[targetName]; + if (isObject(local)) { + return Object.assign({}, target, local); + } + return valueOrDefault(local, target); + }, + set (value) { + this[privateName] = value; + } + } + }); + } + apply(appliers) { + appliers.forEach((apply)=>apply(this)); + } +} +var defaults = /* #__PURE__ */ new Defaults({ + _scriptable: (name)=>!name.startsWith('on'), + _indexable: (name)=>name !== 'events', + hover: { + _fallback: 'interaction' + }, + interaction: { + _scriptable: false, + _indexable: false + } +}, [ + applyAnimationsDefaults, + applyLayoutsDefaults, + applyScaleDefaults +]); + +/** + * 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 + * @private + */ 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; +} +/** + * @private + */ 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; +} +/** + * @private + */ // eslint-disable-next-line complexity +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]; + // Undefined strings and arrays should not be measured + if (thing !== undefined && thing !== null && !isArray(thing)) { + longest = _measureText(ctx, data, gc, longest, thing); + } else if (isArray(thing)) { + // if it is an array lets measure each element + // to do maybe simplify this function a bit so we can do this more recursively? + for(j = 0, jlen = thing.length; j < jlen; j++){ + nestedThing = thing[j]; + // Undefined strings and arrays should not be measured + 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; +} +/** + * Returns the aligned pixel value to avoid anti-aliasing blur + * @param chart - The chart instance. + * @param pixel - A pixel value. + * @param width - The width of the element. + * @returns The aligned pixel value. + * @private + */ 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; +} +/** + * Clears the entire canvas. + */ function clearCanvas(canvas, ctx) { + if (!ctx && !canvas) { + return; + } + ctx = ctx || canvas.getContext('2d'); + ctx.save(); + // canvas.width and canvas.height do not consider the canvas transform, + // while clearRect does + ctx.resetTransform(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.restore(); +} +function drawPoint(ctx, options, x, y) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + drawPointLegend(ctx, options, x, y, null); +} +// eslint-disable-next-line complexity +function drawPointLegend(ctx, options, x, y, w) { + let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW; + const style = options.pointStyle; + const rotation = options.rotation; + const radius = options.radius; + let rad = (rotation || 0) * RAD_PER_DEG; + if (style && typeof style === 'object') { + type = style.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.save(); + ctx.translate(x, y); + ctx.rotate(rad); + ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); + ctx.restore(); + return; + } + } + if (isNaN(radius) || radius <= 0) { + return; + } + ctx.beginPath(); + switch(style){ + // Default includes circle + default: + if (w) { + ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU); + } else { + ctx.arc(x, y, radius, 0, TAU); + } + ctx.closePath(); + break; + case 'triangle': + width = w ? w / 2 : radius; + ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case 'rectRounded': + // NOTE: the rounded rect implementation changed to use `arc` instead of + // `quadraticCurveTo` since it generates better results when rect is + // almost a circle. 0.516 (instead of 0.5) produces results with visually + // closer proportion to the previous impl and it is inscribed in the + // circle with `radius`. For more details, see the following PRs: + // https://github.com/chartjs/Chart.js/issues/5597 + // https://github.com/chartjs/Chart.js/issues/5858 + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size); + yOffset = Math.sin(rad + QUARTER_PI) * size; + yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size); + ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case 'rect': + if (!rotation) { + size = Math.SQRT1_2 * radius; + width = w ? w / 2 : size; + ctx.rect(x - width, y - size, 2 * width, 2 * size); + break; + } + rad += QUARTER_PI; + /* falls through */ case 'rectRot': + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + ctx.closePath(); + break; + case 'crossRot': + rad += QUARTER_PI; + /* falls through */ case 'cross': + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + break; + case 'star': + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + rad += QUARTER_PI; + xOffsetW = Math.cos(rad) * (w ? w / 2 : radius); + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + yOffsetW = Math.sin(rad) * (w ? w / 2 : radius); + ctx.moveTo(x - xOffsetW, y - yOffset); + ctx.lineTo(x + xOffsetW, y + yOffset); + ctx.moveTo(x + yOffsetW, y - xOffset); + ctx.lineTo(x - yOffsetW, y + xOffset); + break; + case 'line': + xOffset = w ? w / 2 : Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + break; + case 'dash': + ctx.moveTo(x, y); + ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius); + break; + case false: + ctx.closePath(); + break; + } + ctx.fill(); + if (options.borderWidth > 0) { + ctx.stroke(); + } +} +/** + * Returns true if the point is inside the rectangle + * @param point - The point to test + * @param area - The rectangle + * @param margin - allowed margin + * @private + */ function _isPointInArea(point, area, margin) { + margin = margin || 0.5; // margin - default is to match rounded decimals + 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(); +} +/** + * @private + */ 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); +} +/** + * @private + */ function _bezierCurveTo(ctx, previous, target, flip) { + if (!previous) { + return ctx.lineTo(target.x, target.y); + } + ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y); +} +function setRenderOpts(ctx, opts) { + if (opts.translation) { + ctx.translate(opts.translation[0], opts.translation[1]); + } + if (!isNullOrUndef(opts.rotation)) { + ctx.rotate(opts.rotation); + } + if (opts.color) { + ctx.fillStyle = opts.color; + } + if (opts.textAlign) { + ctx.textAlign = opts.textAlign; + } + if (opts.textBaseline) { + ctx.textBaseline = opts.textBaseline; + } +} +function decorateText(ctx, x, y, line, opts) { + if (opts.strikethrough || opts.underline) { + /** + * Now that IE11 support has been dropped, we can use more + * of the TextMetrics object. The actual bounding boxes + * are unflagged in Chrome, Firefox, Edge, and Safari so they + * can be safely used. + * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility + */ const metrics = ctx.measureText(line); + const left = x - metrics.actualBoundingBoxLeft; + const right = x + metrics.actualBoundingBoxRight; + const top = y - metrics.actualBoundingBoxAscent; + const bottom = y + metrics.actualBoundingBoxDescent; + const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom; + ctx.strokeStyle = ctx.fillStyle; + ctx.beginPath(); + ctx.lineWidth = opts.decorationWidth || 2; + ctx.moveTo(left, yDecoration); + ctx.lineTo(right, yDecoration); + ctx.stroke(); + } +} +function drawBackdrop(ctx, opts) { + const oldColor = ctx.fillStyle; + ctx.fillStyle = opts.color; + ctx.fillRect(opts.left, opts.top, opts.width, opts.height); + ctx.fillStyle = oldColor; +} +/** + * Render text onto the canvas + */ function renderText(ctx, text, x, y, font, opts = {}) { + const lines = isArray(text) ? text : [ + text + ]; + const stroke = opts.strokeWidth > 0 && opts.strokeColor !== ''; + let i, line; + ctx.save(); + ctx.font = font.string; + setRenderOpts(ctx, opts); + for(i = 0; i < lines.length; ++i){ + line = lines[i]; + if (opts.backdrop) { + drawBackdrop(ctx, opts.backdrop); + } + if (stroke) { + if (opts.strokeColor) { + ctx.strokeStyle = opts.strokeColor; + } + if (!isNullOrUndef(opts.strokeWidth)) { + ctx.lineWidth = opts.strokeWidth; + } + ctx.strokeText(line, x, y, opts.maxWidth); + } + ctx.fillText(line, x, y, opts.maxWidth); + decorateText(ctx, x, y, line, opts); + y += Number(font.lineHeight); + } + ctx.restore(); +} +/** + * Add a path of a rectangle with rounded corners to the current sub-path + * @param ctx - Context + * @param rect - Bounding rect + */ function addRoundedRectPath(ctx, rect) { + const { x , y , w , h , radius } = rect; + // top left arc + ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true); + // line from top left to bottom left + ctx.lineTo(x, y + h - radius.bottomLeft); + // bottom left arc + ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true); + // line from bottom left to bottom right + ctx.lineTo(x + w - radius.bottomRight, y + h); + // bottom right arc + ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true); + // line from bottom right to top right + ctx.lineTo(x + w, y + radius.topRight); + // top right arc + ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true); + // line from top right to top left + ctx.lineTo(x + radius.topLeft, y); +} + +const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/; +const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/; +/** + * @alias Chart.helpers.options + * @namespace + */ /** + * Converts the given line height `value` in pixels for a specific font `size`. + * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). + * @param size - The font size (in pixels) used to resolve relative `value`. + * @returns 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 + */ 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; +} +/** + * Converts the given value into a TRBL object. + * @param value - If a number, set the value to all TRBL component, + * else, if an object, use defined properties and sets undefined ones to 0. + * x / y are shorthands for same value for left/right and top/bottom. + * @returns The padding values (top, right, bottom, left) + * @since 3.0.0 + */ function toTRBL(value) { + return _readValueToProps(value, { + top: 'y', + right: 'x', + bottom: 'y', + left: 'x' + }); +} +/** + * Converts the given value into a TRBL corners object (similar with css border-radius). + * @param value - If a number, set the value to all TRBL corner components, + * else, if an object, use defined properties and sets undefined ones to 0. + * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight) + * @since 3.0.0 + */ function toTRBLCorners(value) { + return _readValueToProps(value, [ + 'topLeft', + 'topRight', + 'bottomLeft', + 'bottomRight' + ]); +} +/** + * Converts the given value into a padding object with pre-computed width/height. + * @param value - If a number, set the value to all TRBL component, + * else, if an object, use defined properties and sets undefined ones to 0. + * x / y are shorthands for same value for left/right and top/bottom. + * @returns The padding values (top, right, bottom, left, width, height) + * @since 2.7.0 + */ function toPadding(value) { + const obj = toTRBL(value); + obj.width = obj.left + obj.right; + obj.height = obj.top + obj.bottom; + return obj; +} +/** + * Parses font options and returns the font object. + * @param options - A object that contains font options to be parsed. + * @param fallback - A object that contains fallback font options. + * @return The font object. + * @private + */ 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 = undefined; + } + 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; +} +/** + * 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 + */ 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; + } + } +} +/** + * @param minmax + * @param grace + * @param beginAtZero + * @private + */ 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); +} + +/** + * Creates a Proxy for resolving raw values for options. + * @param scopes - The option scopes to look for values, in resolution order + * @param prefixes - The prefixes for values, in resolution order. + * @param rootScopes - The root option scopes + * @param fallback - Parent scopes fallback + * @param getTarget - callback for getting the target for changed values + * @returns Proxy + * @private + */ function _createResolver(scopes, prefixes = [ + '' +], rootScopes, fallback, getTarget = ()=>scopes[0]) { + const finalRootScopes = rootScopes || scopes; + if (typeof fallback === 'undefined') { + fallback = _resolve('_fallback', scopes); + } + const cache = { + [Symbol.toStringTag]: 'Object', + _cacheable: true, + _scopes: scopes, + _rootScopes: finalRootScopes, + _fallback: fallback, + _getTarget: getTarget, + override: (scope)=>_createResolver([ + scope, + ...scopes + ], prefixes, finalRootScopes, fallback) + }; + return new Proxy(cache, { + /** + * A trap for the delete operator. + */ deleteProperty (target, prop) { + delete target[prop]; // remove from cache + delete target._keys; // remove cached keys + delete scopes[0][prop]; // remove from top level scope + return true; + }, + /** + * A trap for getting property values. + */ get (target, prop) { + return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target)); + }, + /** + * A trap for Object.getOwnPropertyDescriptor. + * Also used by Object.hasOwnProperty. + */ getOwnPropertyDescriptor (target, prop) { + return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop); + }, + /** + * A trap for Object.getPrototypeOf. + */ getPrototypeOf () { + return Reflect.getPrototypeOf(scopes[0]); + }, + /** + * A trap for the in operator. + */ has (target, prop) { + return getKeysFromAllScopes(target).includes(prop); + }, + /** + * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols. + */ ownKeys (target) { + return getKeysFromAllScopes(target); + }, + /** + * A trap for setting property values. + */ set (target, prop, value) { + const storage = target._storage || (target._storage = getTarget()); + target[prop] = storage[prop] = value; // set to top level scope + cache + delete target._keys; // remove cached keys + return true; + } + }); +} +/** + * Returns an Proxy for resolving option values with context. + * @param proxy - The Proxy returned by `_createResolver` + * @param context - Context object for scriptable/indexable options + * @param subProxy - The proxy provided for scriptable options + * @param descriptorDefaults - Defaults for descriptors + * @private + */ 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, { + /** + * A trap for the delete operator. + */ deleteProperty (target, prop) { + delete target[prop]; // remove from cache + delete proxy[prop]; // remove from proxy + return true; + }, + /** + * A trap for getting property values. + */ get (target, prop, receiver) { + return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver)); + }, + /** + * A trap for Object.getOwnPropertyDescriptor. + * Also used by Object.hasOwnProperty. + */ getOwnPropertyDescriptor (target, prop) { + return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? { + enumerable: true, + configurable: true + } : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop); + }, + /** + * A trap for Object.getPrototypeOf. + */ getPrototypeOf () { + return Reflect.getPrototypeOf(proxy); + }, + /** + * A trap for the in operator. + */ has (target, prop) { + return Reflect.has(proxy, prop); + }, + /** + * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols. + */ ownKeys () { + return Reflect.ownKeys(proxy); + }, + /** + * A trap for setting property values. + */ set (target, prop, value) { + proxy[prop] = value; // set to proxy + delete target[prop]; // remove from cache + return true; + } + }); +} +/** + * @private + */ 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) || prop === 'constructor') { + return target[prop]; + } + const value = resolve(); + // cache the resolved value + target[prop] = value; + return value; +} +function _resolveWithContext(target, prop, receiver) { + const { _proxy , _context , _subProxy , _descriptors: descriptors } = target; + let value = _proxy[prop]; // resolve from proxy + // resolve with context + 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)) { + // if the resolved value is an object, create a sub resolver for it + value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors); + } + return value; +} +function _resolveScriptable(prop, getValue, target, receiver) { + const { _proxy , _context , _subProxy , _stack } = target; + if (_stack.has(prop)) { + throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop); + } + _stack.add(prop); + let value = getValue(_context, _subProxy || receiver); + _stack.delete(prop); + if (needsSubResolver(prop, value)) { + // When scriptable option returns an object, create a resolver on that. + value = createSubResolver(_proxy._scopes, _proxy, prop, value); + } + return value; +} +function _resolveArray(prop, value, target, isIndexable) { + const { _proxy , _context , _subProxy , _descriptors: descriptors } = target; + if (typeof _context.index !== 'undefined' && isIndexable(prop)) { + return value[_context.index % value.length]; + } else if (isObject(value[0])) { + // Array of objects, return array or resolvers + 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 (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) { + // When we reach the descriptor that defines a new _fallback, return that. + // The fallback will resume to that new scope. + return fallback; + } + } else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) { + // Fallback to `false` results to `false`, when falling back to different key. + // For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations` + 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 (typeof fallback !== 'undefined' && 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)) { + // For array of objects, the object is used to store updated values + return value; + } + return target || {}; +} +function _resolveWithPrefixes(prop, prefixes, scopes, proxy) { + let value; + for (const prefix of prefixes){ + value = _resolve(readKey(prefix, prop), scopes); + if (typeof value !== 'undefined') { + return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value; + } + } +} +function _resolve(key, scopes) { + for (const scope of scopes){ + if (!scope) { + continue; + } + const value = scope[key]; + if (typeof value !== 'undefined') { + return value; + } + } +} +function getKeysFromAllScopes(target) { + let keys = target._keys; + if (!keys) { + keys = target._keys = resolveKeysFromAllScopes(target._scopes); + } + return keys; +} +function resolveKeysFromAllScopes(scopes) { + const 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); +} +function _parseObjectDataRadialScale(meta, data, start, count) { + const { iScale } = meta; + const { key ='r' } = 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] = { + r: iScale.parse(resolveObjectKey(item, key), index) + }; + } + return parsed; +} + +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) { + // Props to Rob Spencer at scaled innovation for his post on splining between points + // http://scaledinnovation.com/analytics/splines/aboutSplines.html + // This function must also respect "skipped" points + 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); + // If all points are the same, s01 & s02 will be inf + s01 = isNaN(s01) ? 0 : s01; + s12 = isNaN(s12) ? 0 : s12; + const fa = t * s01; // scaling factor for triangle Ta + 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) + } + }; +} +/** + * Adjust tangents to ensure monotonic properties + */ 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]; + } + } +} +/** + * 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 + */ function splineCurveMonotone(points, indexAxis = 'x') { + const valueAxis = getValueAxis(indexAxis); + const pointsLen = points.length; + const deltaK = Array(pointsLen).fill(0); + const mK = Array(pointsLen); + // Calculate slopes (deltaK) and initialize tangents (mK) + 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]; + // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 + 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); + } + } +} +/** + * @private + */ function _updateBezierControlPoints(points, options, area, loop, indexAxis) { + let i, ilen, point, controlPoints; + // Only consider points that are drawn in case the spanGaps option is used + 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); + } +} + +/** + * @private + */ function _isDomSupported() { + return typeof window !== 'undefined' && typeof document !== 'undefined'; +} +/** + * @private + */ function _getParentNode(domNode) { + let parent = domNode.parentNode; + if (parent && parent.toString() === '[object ShadowRoot]') { + parent = parent.host; + } + return parent; +} +/** + * convert max-width/max-height values that may be percentages into a number + * @private + */ function parseMaxStyle(styleValue, node, parentProperty) { + let valueInPixels; + if (typeof styleValue === 'string') { + valueInPixels = parseInt(styleValue, 10); + if (styleValue.indexOf('%') !== -1) { + // percentage * size in dimension + valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; + } + } else { + valueInPixels = styleValue; + } + return valueInPixels; +} +const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null); +function getStyle(el, property) { + return getComputedStyle(el).getPropertyValue(property); +} +const positions = [ + 'top', + 'right', + 'bottom', + 'left' +]; +function getPositionedStyle(styles, style, suffix) { + const result = {}; + suffix = suffix ? '-' + suffix : ''; + for(let i = 0; i < 4; i++){ + const pos = positions[i]; + result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0; + } + result.width = result.left + result.right; + result.height = result.top + result.bottom; + return result; +} +const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot); +/** + * @param e + * @param canvas + * @returns Canvas position + */ function getCanvasPosition(e, canvas) { + const touches = e.touches; + const source = touches && touches.length ? touches[0] : e; + const { offsetX , offsetY } = source; + let box = false; + let x, y; + if (useOffsetPos(offsetX, offsetY, e.target)) { + x = offsetX; + y = offsetY; + } else { + const rect = canvas.getBoundingClientRect(); + x = source.clientX - rect.left; + y = source.clientY - rect.top; + box = true; + } + return { + x, + y, + box + }; +} +/** + * Gets an event's x, y coordinates, relative to the chart area + * @param event + * @param chart + * @returns x and y coordinates of the event + */ function getRelativePosition(event, chart) { + if ('native' in event) { + return event; + } + const { canvas , currentDevicePixelRatio } = chart; + const style = getComputedStyle(canvas); + const borderBox = style.boxSizing === 'border-box'; + const paddings = getPositionedStyle(style, 'padding'); + const borders = getPositionedStyle(style, 'border', 'width'); + const { x , y , box } = getCanvasPosition(event, canvas); + const xOffset = paddings.left + (box && borders.left); + const yOffset = paddings.top + (box && borders.top); + let { width , height } = chart; + if (borderBox) { + width -= paddings.width + borders.width; + height -= paddings.height + borders.height; + } + return { + x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio), + y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio) + }; +} +function getContainerSize(canvas, width, height) { + let maxWidth, maxHeight; + if (width === undefined || height === undefined) { + const container = canvas && _getParentNode(canvas); + if (!container) { + width = canvas.clientWidth; + height = canvas.clientHeight; + } else { + const rect = container.getBoundingClientRect(); // this is the border box of the container + 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; +// eslint-disable-next-line complexity +function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) { + const style = getComputedStyle(canvas); + const margins = getPositionedStyle(style, 'margin'); + const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY; + const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY; + const containerSize = getContainerSize(canvas, bbWidth, bbHeight); + let { width , height } = containerSize; + if (style.boxSizing === 'content-box') { + const borders = getPositionedStyle(style, 'border', 'width'); + const paddings = getPositionedStyle(style, 'padding'); + width -= paddings.width + borders.width; + height -= paddings.height + borders.height; + } + width = Math.max(0, width - margins.width); + height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height); + width = round1(Math.min(width, maxWidth, containerSize.maxWidth)); + height = round1(Math.min(height, maxHeight, containerSize.maxHeight)); + if (width && !height) { + // https://github.com/chartjs/Chart.js/issues/4659 + // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default) + height = round1(width / 2); + } + const maintainHeight = bbWidth !== undefined || bbHeight !== undefined; + if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) { + height = containerSize.height; + width = round1(Math.floor(height * aspectRatio)); + } + return { + width, + height + }; +} +/** + * @param chart + * @param forceRatio + * @param forceStyle + * @returns True if the canvas context size or transformation has changed. + */ function retinaScale(chart, forceRatio, forceStyle) { + const pixelRatio = forceRatio || 1; + const deviceHeight = round1(chart.height * pixelRatio); + const deviceWidth = round1(chart.width * pixelRatio); + chart.height = round1(chart.height); + chart.width = round1(chart.width); + const canvas = chart.canvas; + // If no style has been set on the canvas, the render size is used as display size, + // making the chart visually bigger, so let's enforce it to the "correct" values. + // See https://github.com/chartjs/Chart.js/issues/3575 + 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; +} +/** + * Detects support for options object argument in addEventListener. + * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support + * @private + */ const supportsEventListenerOptions = function() { + let passiveSupported = false; + try { + const options = { + get passive () { + passiveSupported = true; + return false; + } + }; + if (_isDomSupported()) { + window.addEventListener('test', null, options); + window.removeEventListener('test', null, options); + } + } catch (e) { + // continue regardless of error + } + return passiveSupported; +}(); +/** + * The "used" size is the final value of a dimension property after all calculations have + * been performed. This method uses the computed style of `element` but returns undefined + * if the computed style is not expressed in pixels. That can happen in some cases where + * `element` has a size relative to its parent and this last one is not yet displayed, + * for example because of `display: none` on a parent node. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * @returns Size in pixels or undefined if unknown. + */ function readUsedSize(element, property) { + const value = getStyle(element, property); + const matches = value && value.match(/^(\d+)(\.\d+)?px$/); + return matches ? +matches[1] : undefined; +} + +/** + * @private + */ function _pointInLine(p1, p2, t, mode) { + return { + x: p1.x + t * (p2.x - p1.x), + y: p1.y + t * (p2.y - p1.y) + }; +} +/** + * @private + */ 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 + }; +} +/** + * @private + */ function _bezierInterpolation(p1, p2, t, mode) { + const cp1 = { + x: p1.cp2x, + y: p1.cp2y + }; + const cp2 = { + x: p2.cp1x, + y: p2.cp1y + }; + const a = _pointInLine(p1, cp1, t); + const b = _pointInLine(cp1, cp2, t); + const c = _pointInLine(cp2, p2, t); + const d = _pointInLine(a, b, t); + const e = _pointInLine(b, c, t); + return _pointInLine(d, e, t); +} + +const getRightToLeftAdapter = function(rectX, width) { + return { + x (x) { + return rectX + rectX + width - x; + }, + setWidth (w) { + width = w; + }, + textAlign (align) { + if (align === 'center') { + return align; + } + return align === 'right' ? 'left' : 'right'; + }, + xPlus (x, value) { + return x - value; + }, + leftForLtr (x, itemWidth) { + return x - itemWidth; + } + }; +}; +const getLeftToRightAdapter = function() { + return { + x (x) { + return x; + }, + setWidth (w) {}, + textAlign (align) { + return align; + }, + xPlus (x, value) { + return x + value; + }, + leftForLtr (x, _itemWidth) { + return x; + } + }; +}; +function getRtlAdapter(rtl, rectX, width) { + return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter(); +} +function overrideTextDirection(ctx, direction) { + let style, original; + if (direction === 'ltr' || direction === 'rtl') { + style = ctx.canvas.style; + original = [ + style.getPropertyValue('direction'), + style.getPropertyPriority('direction') + ]; + style.setProperty('direction', direction, 'important'); + ctx.prevTextDirection = original; + } +} +function restoreTextDirection(ctx, original) { + if (original !== 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) { + if (!prevStyle) { + return false; + } + const cache = []; + const replacer = function(key, value) { + if (!isPatternOrGradient(value)) { + return value; + } + if (!cache.includes(value)) { + cache.push(value); + } + return cache.indexOf(value); + }; + return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer); +} + +function getSizeForArea(scale, chartArea, field) { + return scale.options.clip ? scale[field] : chartArea[field]; +} +function getDatasetArea(meta, chartArea) { + const { xScale , yScale } = meta; + if (xScale && yScale) { + return { + left: getSizeForArea(xScale, chartArea, 'left'), + right: getSizeForArea(xScale, chartArea, 'right'), + top: getSizeForArea(yScale, chartArea, 'top'), + bottom: getSizeForArea(yScale, chartArea, 'bottom') + }; + } + return chartArea; +} +function getDatasetClipArea(chart, meta) { + const clip = meta._clip; + if (clip.disabled) { + return false; + } + const area = getDatasetArea(meta, chart.chartArea); + return { + left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left), + right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right), + top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top), + bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom) + }; +} + +export { unclipArea as $, _rlookupByKey as A, _lookupByKey as B, _isPointInArea as C, getAngleFromPoint as D, toPadding as E, each as F, getMaximumSize as G, HALF_PI as H, _getParentNode as I, readUsedSize as J, supportsEventListenerOptions as K, throttled as L, _isDomSupported as M, _factorize as N, finiteOrDefault as O, PI as P, callback as Q, _addGrace as R, _limitValue as S, TAU as T, toDegrees as U, _measureText as V, _int16Range as W, _alignPixel as X, clipArea as Y, renderText as Z, _arrayUnique as _, resolve as a, getStyle as a$, toFont as a0, _toLeftRightCenter as a1, _alignStartEnd as a2, overrides as a3, merge as a4, _capitalize as a5, descriptors as a6, isFunction as a7, _attachContext as a8, _createResolver as a9, getRtlAdapter as aA, overrideTextDirection as aB, _textX as aC, restoreTextDirection as aD, drawPointLegend as aE, distanceBetweenPoints as aF, noop as aG, _setMinAndMaxByKey as aH, niceNum as aI, almostWhole as aJ, almostEquals as aK, _decimalPlaces as aL, Ticks as aM, log10 as aN, _longestText as aO, _filterBetween as aP, _lookup as aQ, isPatternOrGradient as aR, getHoverColor as aS, clone as aT, _merger as aU, _mergerIf as aV, _deprecated as aW, _splitKey as aX, toFontString as aY, splineCurve as aZ, splineCurveMonotone as a_, _descriptors as aa, mergeIf as ab, uid as ac, debounce as ad, retinaScale as ae, clearCanvas as af, setsEqual as ag, getDatasetClipArea as ah, _elementsEqual as ai, _isClickEvent as aj, _isBetween as ak, _normalizeAngle as al, _readValueToProps as am, _updateBezierControlPoints as an, _computeSegments as ao, _boundSegments as ap, _steppedInterpolation as aq, _bezierInterpolation as ar, _pointInLine as as, _steppedLineTo as at, _bezierCurveTo as au, drawPoint as av, addRoundedRectPath as aw, toTRBL as ax, toTRBLCorners as ay, _boundSegment as az, isArray as b, fontString as b0, toLineHeight as b1, PITAU as b2, INFINITY as b3, RAD_PER_DEG as b4, QUARTER_PI as b5, TWO_THIRDS_PI as b6, _angleDiff as b7, color as c, defaults as d, effects as e, resolveObjectKey as f, isNumberFinite as g, defined as h, isObject as i, createContext as j, isNullOrUndef as k, listenArrayEvents as l, toPercentage as m, toDimension as n, formatNumber as o, _angleBetween as p, _getStartAndCountOfVisiblePoints as q, requestAnimFrame as r, sign as s, toRadians as t, unlistenArrayEvents as u, valueOrDefault as v, _scaleRangesChanged as w, isNumber as x, _parseObjectDataRadialScale as y, getRelativePosition as z }; +//# sourceMappingURL=helpers.dataset.js.map diff --git a/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js.map b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js.map new file mode 100644 index 00000000..1f1b0f19 --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js.map @@ -0,0 +1 @@ +{"version":3,"file":"helpers.dataset.js","sources":["../../src/helpers/helpers.core.ts","../../src/helpers/helpers.math.ts","../../src/helpers/helpers.collection.ts","../../src/helpers/helpers.extras.ts","../../src/helpers/helpers.easing.ts","../../src/helpers/helpers.color.ts","../../src/core/core.animations.defaults.js","../../src/core/core.layouts.defaults.js","../../src/helpers/helpers.intl.ts","../../src/core/core.ticks.js","../../src/core/core.scale.defaults.js","../../src/core/core.defaults.js","../../src/helpers/helpers.canvas.ts","../../src/helpers/helpers.options.ts","../../src/helpers/helpers.config.ts","../../src/helpers/helpers.curve.ts","../../src/helpers/helpers.dom.ts","../../src/helpers/helpers.interpolation.ts","../../src/helpers/helpers.rtl.ts","../../src/helpers/helpers.segment.js","../../src/helpers/helpers.dataset.ts"],"sourcesContent":["/**\n * @namespace Chart.helpers\n */\n\nimport type {AnyObject} from '../types/basic.js';\nimport type {ActiveDataPoint, ChartEvent} from '../types/index.js';\n\n/**\n * An empty function that can be used, for example, for optional callback.\n */\nexport function noop() {\n /* noop */\n}\n\n/**\n * Returns a unique id, sequentially generated from a global variable.\n */\nexport const uid = (() => {\n let id = 0;\n return () => id++;\n})();\n\n/**\n * Returns true if `value` is neither null nor undefined, else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport function isNullOrUndef(value: unknown): value is null | undefined {\n return value === null || value === undefined;\n}\n\n/**\n * Returns true if `value` is an array (including typed arrays), else returns false.\n * @param value - The value to test.\n * @function\n */\nexport function isArray<T = unknown>(value: unknown): value is T[] {\n if (Array.isArray && Array.isArray(value)) {\n return true;\n }\n const type = Object.prototype.toString.call(value);\n if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {\n return true;\n }\n return false;\n}\n\n/**\n * Returns true if `value` is an object (excluding null), else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport function isObject(value: unknown): value is AnyObject {\n return value !== null && Object.prototype.toString.call(value) === '[object Object]';\n}\n\n/**\n * Returns true if `value` is a finite number, else returns false\n * @param value - The value to test.\n */\nfunction isNumberFinite(value: unknown): value is number {\n return (typeof value === 'number' || value instanceof Number) && isFinite(+value);\n}\nexport {\n isNumberFinite as isFinite,\n};\n\n/**\n * Returns `value` if finite, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is not finite.\n */\nexport function finiteOrDefault(value: unknown, defaultValue: number) {\n return isNumberFinite(value) ? value : defaultValue;\n}\n\n/**\n * Returns `value` if defined, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is undefined.\n */\nexport function valueOrDefault<T>(value: T | undefined, defaultValue: T) {\n return typeof value === 'undefined' ? defaultValue : value;\n}\n\nexport const toPercentage = (value: number | string, dimension: number) =>\n typeof value === 'string' && value.endsWith('%') ?\n parseFloat(value) / 100\n : +value / dimension;\n\nexport const toDimension = (value: number | string, dimension: number) =>\n typeof value === 'string' && value.endsWith('%') ?\n parseFloat(value) / 100 * dimension\n : +value;\n\n/**\n * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n * @param fn - The function to call.\n * @param args - The arguments with which `fn` should be called.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n */\nexport function callback<T extends (this: TA, ...restArgs: unknown[]) => R, TA, R>(\n fn: T | undefined,\n args: unknown[],\n thisArg?: TA\n): R | undefined {\n if (fn && typeof fn.call === 'function') {\n return fn.apply(thisArg, args);\n }\n}\n\n/**\n * Note(SB) for performance sake, this method should only be used when loopable type\n * is unknown or in none intensive code (not called often and small loopable). Else\n * it's preferable to use a regular for() loop and save extra function calls.\n * @param loopable - The object or array to be iterated.\n * @param fn - The function to call for each item.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n * @param [reverse] - If true, iterates backward on the loopable.\n */\nexport function each<T, TA>(\n loopable: Record<string, T>,\n fn: (this: TA, v: T, i: string) => void,\n thisArg?: TA,\n reverse?: boolean\n): void;\nexport function each<T, TA>(\n loopable: T[],\n fn: (this: TA, v: T, i: number) => void,\n thisArg?: TA,\n reverse?: boolean\n): void;\nexport function each<T, TA>(\n loopable: T[] | Record<string, T>,\n fn: (this: TA, v: T, i: any) => void,\n thisArg?: TA,\n reverse?: boolean\n) {\n let i: number, len: number, keys: string[];\n if (isArray(loopable)) {\n len = loopable.length;\n if (reverse) {\n for (i = len - 1; i >= 0; i--) {\n fn.call(thisArg, loopable[i], i);\n }\n } else {\n for (i = 0; i < len; i++) {\n fn.call(thisArg, loopable[i], i);\n }\n }\n } else if (isObject(loopable)) {\n keys = Object.keys(loopable);\n len = keys.length;\n for (i = 0; i < len; i++) {\n fn.call(thisArg, loopable[keys[i]], keys[i]);\n }\n }\n}\n\n/**\n * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n * @param a0 - The array to compare\n * @param a1 - The array to compare\n * @private\n */\nexport function _elementsEqual(a0: ActiveDataPoint[], a1: ActiveDataPoint[]) {\n let i: number, ilen: number, v0: ActiveDataPoint, v1: ActiveDataPoint;\n\n if (!a0 || !a1 || a0.length !== a1.length) {\n return false;\n }\n\n for (i = 0, ilen = a0.length; i < ilen; ++i) {\n v0 = a0[i];\n v1 = a1[i];\n\n if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Returns a deep copy of `source` without keeping references on objects and arrays.\n * @param source - The value to clone.\n */\nexport function clone<T>(source: T): T {\n if (isArray(source)) {\n return source.map(clone) as unknown as T;\n }\n\n if (isObject(source)) {\n const target = Object.create(null);\n const keys = Object.keys(source);\n const klen = keys.length;\n let k = 0;\n\n for (; k < klen; ++k) {\n target[keys[k]] = clone(source[keys[k]]);\n }\n\n return target;\n }\n\n return source;\n}\n\nfunction isValidKey(key: string) {\n return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;\n}\n\n/**\n * The default merger when Chart.helpers.merge is called without merger option.\n * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.\n * @private\n */\nexport function _merger(key: string, target: AnyObject, source: AnyObject, options: AnyObject) {\n if (!isValidKey(key)) {\n return;\n }\n\n const tval = target[key];\n const sval = source[key];\n\n if (isObject(tval) && isObject(sval)) {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n merge(tval, sval, options);\n } else {\n target[key] = clone(sval);\n }\n}\n\nexport interface MergeOptions {\n merger?: (key: string, target: AnyObject, source: AnyObject, options?: AnyObject) => void;\n}\n\n/**\n * Recursively deep copies `source` properties into `target` with the given `options`.\n * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n * @param target - The target object in which all sources are merged into.\n * @param source - Object(s) to merge into `target`.\n * @param [options] - Merging options:\n * @param [options.merger] - The merge method (key, target, source, options)\n * @returns The `target` object.\n */\nexport function merge<T>(target: T, source: [], options?: MergeOptions): T;\nexport function merge<T, S1>(target: T, source: S1, options?: MergeOptions): T & S1;\nexport function merge<T, S1>(target: T, source: [S1], options?: MergeOptions): T & S1;\nexport function merge<T, S1, S2>(target: T, source: [S1, S2], options?: MergeOptions): T & S1 & S2;\nexport function merge<T, S1, S2, S3>(target: T, source: [S1, S2, S3], options?: MergeOptions): T & S1 & S2 & S3;\nexport function merge<T, S1, S2, S3, S4>(\n target: T,\n source: [S1, S2, S3, S4],\n options?: MergeOptions\n): T & S1 & S2 & S3 & S4;\nexport function merge<T>(target: T, source: AnyObject[], options?: MergeOptions): AnyObject;\nexport function merge<T>(target: T, source: AnyObject[], options?: MergeOptions): AnyObject {\n const sources = isArray(source) ? source : [source];\n const ilen = sources.length;\n\n if (!isObject(target)) {\n return target as AnyObject;\n }\n\n options = options || {};\n const merger = options.merger || _merger;\n let current: AnyObject;\n\n for (let i = 0; i < ilen; ++i) {\n current = sources[i];\n if (!isObject(current)) {\n continue;\n }\n\n const keys = Object.keys(current);\n for (let k = 0, klen = keys.length; k < klen; ++k) {\n merger(keys[k], target, current, options as AnyObject);\n }\n }\n\n return target;\n}\n\n/**\n * Recursively deep copies `source` properties into `target` *only* if not defined in target.\n * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n * @param target - The target object in which all sources are merged into.\n * @param source - Object(s) to merge into `target`.\n * @returns The `target` object.\n */\nexport function mergeIf<T>(target: T, source: []): T;\nexport function mergeIf<T, S1>(target: T, source: S1): T & S1;\nexport function mergeIf<T, S1>(target: T, source: [S1]): T & S1;\nexport function mergeIf<T, S1, S2>(target: T, source: [S1, S2]): T & S1 & S2;\nexport function mergeIf<T, S1, S2, S3>(target: T, source: [S1, S2, S3]): T & S1 & S2 & S3;\nexport function mergeIf<T, S1, S2, S3, S4>(target: T, source: [S1, S2, S3, S4]): T & S1 & S2 & S3 & S4;\nexport function mergeIf<T>(target: T, source: AnyObject[]): AnyObject;\nexport function mergeIf<T>(target: T, source: AnyObject[]): AnyObject {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n return merge<T>(target, source, {merger: _mergerIf});\n}\n\n/**\n * Merges source[key] in target[key] only if target[key] is undefined.\n * @private\n */\nexport function _mergerIf(key: string, target: AnyObject, source: AnyObject) {\n if (!isValidKey(key)) {\n return;\n }\n\n const tval = target[key];\n const sval = source[key];\n\n if (isObject(tval) && isObject(sval)) {\n mergeIf(tval, sval);\n } else if (!Object.prototype.hasOwnProperty.call(target, key)) {\n target[key] = clone(sval);\n }\n}\n\n/**\n * @private\n */\nexport function _deprecated(scope: string, value: unknown, previous: string, current: string) {\n if (value !== undefined) {\n console.warn(scope + ': \"' + previous +\n '\" is deprecated. Please use \"' + current + '\" instead');\n }\n}\n\n// resolveObjectKey resolver cache\nconst keyResolvers = {\n // Chart.helpers.core resolveObjectKey should resolve empty key to root object\n '': v => v,\n // default resolvers\n x: o => o.x,\n y: o => o.y\n};\n\n/**\n * @private\n */\nexport function _splitKey(key: string) {\n const parts = key.split('.');\n const keys: string[] = [];\n let tmp = '';\n for (const part of parts) {\n tmp += part;\n if (tmp.endsWith('\\\\')) {\n tmp = tmp.slice(0, -1) + '.';\n } else {\n keys.push(tmp);\n tmp = '';\n }\n }\n return keys;\n}\n\nfunction _getKeyResolver(key: string) {\n const keys = _splitKey(key);\n return obj => {\n for (const k of keys) {\n if (k === '') {\n // For backward compatibility:\n // Chart.helpers.core resolveObjectKey should break at empty key\n break;\n }\n obj = obj && obj[k];\n }\n return obj;\n };\n}\n\nexport function resolveObjectKey(obj: AnyObject, key: string): any {\n const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));\n return resolver(obj);\n}\n\n/**\n * @private\n */\nexport function _capitalize(str: string) {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n\nexport const defined = (value: unknown) => typeof value !== 'undefined';\n\nexport const isFunction = (value: unknown): value is (...args: any[]) => any => typeof value === 'function';\n\n// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384\nexport const setsEqual = <T>(a: Set<T>, b: Set<T>) => {\n if (a.size !== b.size) {\n return false;\n }\n\n for (const item of a) {\n if (!b.has(item)) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * @param e - The event\n * @private\n */\nexport function _isClickEvent(e: ChartEvent) {\n return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';\n}\n","import type {Point} from '../types/geometric.js';\nimport {isFinite as isFiniteNumber} from './helpers.core.js';\n\n/**\n * @alias Chart.helpers.math\n * @namespace\n */\n\nexport const PI = Math.PI;\nexport const TAU = 2 * PI;\nexport const PITAU = TAU + PI;\nexport const INFINITY = Number.POSITIVE_INFINITY;\nexport const RAD_PER_DEG = PI / 180;\nexport const HALF_PI = PI / 2;\nexport const QUARTER_PI = PI / 4;\nexport const TWO_THIRDS_PI = PI * 2 / 3;\n\nexport const log10 = Math.log10;\nexport const sign = Math.sign;\n\nexport function almostEquals(x: number, y: number, epsilon: number) {\n return Math.abs(x - y) < epsilon;\n}\n\n/**\n * Implementation of the nice number algorithm used in determining where axis labels will go\n */\nexport function niceNum(range: number) {\n const roundedRange = Math.round(range);\n range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;\n const niceRange = Math.pow(10, Math.floor(log10(range)));\n const fraction = range / niceRange;\n const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;\n return niceFraction * niceRange;\n}\n\n/**\n * Returns an array of factors sorted from 1 to sqrt(value)\n * @private\n */\nexport function _factorize(value: number) {\n const result: number[] = [];\n const sqrt = Math.sqrt(value);\n let i: number;\n\n for (i = 1; i < sqrt; i++) {\n if (value % i === 0) {\n result.push(i);\n result.push(value / i);\n }\n }\n if (sqrt === (sqrt | 0)) { // if value is a square number\n result.push(sqrt);\n }\n\n result.sort((a, b) => a - b).pop();\n return result;\n}\n\n/**\n * Verifies that attempting to coerce n to string or number won't throw a TypeError.\n */\nfunction isNonPrimitive(n: unknown) {\n return typeof n === 'symbol' || (typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n));\n}\n\nexport function isNumber(n: unknown): n is number {\n return !isNonPrimitive(n) && !isNaN(parseFloat(n as string)) && isFinite(n as number);\n}\n\nexport function almostWhole(x: number, epsilon: number) {\n const rounded = Math.round(x);\n return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x);\n}\n\n/**\n * @private\n */\nexport function _setMinAndMaxByKey(\n array: Record<string, number>[],\n target: { min: number, max: number },\n property: string\n) {\n let i: number, ilen: number, value: number;\n\n for (i = 0, ilen = array.length; i < ilen; i++) {\n value = array[i][property];\n if (!isNaN(value)) {\n target.min = Math.min(target.min, value);\n target.max = Math.max(target.max, value);\n }\n }\n}\n\nexport function toRadians(degrees: number) {\n return degrees * (PI / 180);\n}\n\nexport function toDegrees(radians: number) {\n return radians * (180 / PI);\n}\n\n/**\n * Returns the number of decimal places\n * i.e. the number of digits after the decimal point, of the value of this Number.\n * @param x - A number.\n * @returns The number of decimal places.\n * @private\n */\nexport function _decimalPlaces(x: number) {\n if (!isFiniteNumber(x)) {\n return;\n }\n let e = 1;\n let p = 0;\n while (Math.round(x * e) / e !== x) {\n e *= 10;\n p++;\n }\n return p;\n}\n\n// Gets the angle from vertical upright to the point about a centre.\nexport function getAngleFromPoint(\n centrePoint: Point,\n anglePoint: Point\n) {\n const distanceFromXCenter = anglePoint.x - centrePoint.x;\n const distanceFromYCenter = anglePoint.y - centrePoint.y;\n const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);\n\n let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);\n\n if (angle < (-0.5 * PI)) {\n angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]\n }\n\n return {\n angle,\n distance: radialDistanceFromCenter\n };\n}\n\nexport function distanceBetweenPoints(pt1: Point, pt2: Point) {\n return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));\n}\n\n/**\n * Shortest distance between angles, in either direction.\n * @private\n */\nexport function _angleDiff(a: number, b: number) {\n return (a - b + PITAU) % TAU - PI;\n}\n\n/**\n * Normalize angle to be between 0 and 2*PI\n * @private\n */\nexport function _normalizeAngle(a: number) {\n return (a % TAU + TAU) % TAU;\n}\n\n/**\n * @private\n */\nexport function _angleBetween(angle: number, start: number, end: number, sameAngleIsFullCircle?: boolean) {\n const a = _normalizeAngle(angle);\n const s = _normalizeAngle(start);\n const e = _normalizeAngle(end);\n const angleToStart = _normalizeAngle(s - a);\n const angleToEnd = _normalizeAngle(e - a);\n const startToAngle = _normalizeAngle(a - s);\n const endToAngle = _normalizeAngle(a - e);\n return a === s || a === e || (sameAngleIsFullCircle && s === e)\n || (angleToStart > angleToEnd && startToAngle < endToAngle);\n}\n\n/**\n * Limit `value` between `min` and `max`\n * @param value\n * @param min\n * @param max\n * @private\n */\nexport function _limitValue(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value));\n}\n\n/**\n * @param {number} value\n * @private\n */\nexport function _int16Range(value: number) {\n return _limitValue(value, -32768, 32767);\n}\n\n/**\n * @param value\n * @param start\n * @param end\n * @param [epsilon]\n * @private\n */\nexport function _isBetween(value: number, start: number, end: number, epsilon = 1e-6) {\n return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;\n}\n","import {_capitalize} from './helpers.core.js';\n\n/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param value - value to find\n * @param cmp\n * @private\n */\nexport function _lookup(\n table: number[],\n value: number,\n cmp?: (value: number) => boolean\n): {lo: number, hi: number};\nexport function _lookup<T>(\n table: T[],\n value: number,\n cmp: (value: number) => boolean\n): {lo: number, hi: number};\nexport function _lookup(\n table: unknown[],\n value: number,\n cmp?: (value: number) => boolean\n) {\n cmp = cmp || ((index) => table[index] < value);\n let hi = table.length - 1;\n let lo = 0;\n let mid: number;\n\n while (hi - lo > 1) {\n mid = (lo + hi) >> 1;\n if (cmp(mid)) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n return {lo, hi};\n}\n\n/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @param last - lookup last index\n * @private\n */\nexport const _lookupByKey = (\n table: Record<string, number>[],\n key: string,\n value: number,\n last?: boolean\n) =>\n _lookup(table, value, last\n ? index => {\n const ti = table[index][key];\n return ti < value || ti === value && table[index + 1][key] === value;\n }\n : index => table[index][key] < value);\n\n/**\n * Reverse binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @private\n */\nexport const _rlookupByKey = (\n table: Record<string, number>[],\n key: string,\n value: number\n) =>\n _lookup(table, value, index => table[index][key] >= value);\n\n/**\n * Return subset of `values` between `min` and `max` inclusive.\n * Values are assumed to be in sorted order.\n * @param values - sorted array of values\n * @param min - min value\n * @param max - max value\n */\nexport function _filterBetween(values: number[], min: number, max: number) {\n let start = 0;\n let end = values.length;\n\n while (start < end && values[start] < min) {\n start++;\n }\n while (end > start && values[end - 1] > max) {\n end--;\n }\n\n return start > 0 || end < values.length\n ? values.slice(start, end)\n : values;\n}\n\nconst arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'] as const;\n\nexport interface ArrayListener<T> {\n _onDataPush?(...item: T[]): void;\n _onDataPop?(): void;\n _onDataShift?(): void;\n _onDataSplice?(index: number, deleteCount: number, ...items: T[]): void;\n _onDataUnshift?(...item: T[]): void;\n}\n\n/**\n * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',\n * 'unshift') and notify the listener AFTER the array has been altered. Listeners are\n * called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments.\n */\nexport function listenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;\nexport function listenArrayEvents(array, listener) {\n if (array._chartjs) {\n array._chartjs.listeners.push(listener);\n return;\n }\n\n Object.defineProperty(array, '_chartjs', {\n configurable: true,\n enumerable: false,\n value: {\n listeners: [listener]\n }\n });\n\n arrayEvents.forEach((key) => {\n const method = '_onData' + _capitalize(key);\n const base = array[key];\n\n Object.defineProperty(array, key, {\n configurable: true,\n enumerable: false,\n value(...args) {\n const res = base.apply(this, args);\n\n array._chartjs.listeners.forEach((object) => {\n if (typeof object[method] === 'function') {\n object[method](...args);\n }\n });\n\n return res;\n }\n });\n });\n}\n\n\n/**\n * Removes the given array event listener and cleanup extra attached properties (such as\n * the _chartjs stub and overridden methods) if array doesn't have any more listeners.\n */\nexport function unlistenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;\nexport function unlistenArrayEvents(array, listener) {\n const stub = array._chartjs;\n if (!stub) {\n return;\n }\n\n const listeners = stub.listeners;\n const index = listeners.indexOf(listener);\n if (index !== -1) {\n listeners.splice(index, 1);\n }\n\n if (listeners.length > 0) {\n return;\n }\n\n arrayEvents.forEach((key) => {\n delete array[key];\n });\n\n delete array._chartjs;\n}\n\n/**\n * @param items\n */\nexport function _arrayUnique<T>(items: T[]) {\n const set = new Set<T>(items);\n\n if (set.size === items.length) {\n return items;\n }\n\n return Array.from(set);\n}\n","import type {ChartMeta, PointElement} from '../types/index.js';\n\nimport {_limitValue} from './helpers.math.js';\nimport {_lookupByKey} from './helpers.collection.js';\nimport {isNullOrUndef} from './helpers.core.js';\n\nexport function fontString(pixelSize: number, fontStyle: string, fontFamily: string) {\n return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;\n}\n\n/**\n* Request animation polyfill\n*/\nexport const requestAnimFrame = (function() {\n if (typeof window === 'undefined') {\n return function(callback) {\n return callback();\n };\n }\n return window.requestAnimationFrame;\n}());\n\n/**\n * Throttles calling `fn` once per animation frame\n * Latest arguments are used on the actual call\n */\nexport function throttled<TArgs extends Array<any>>(\n fn: (...args: TArgs) => void,\n thisArg: any,\n) {\n let argsToUse = [] as TArgs;\n let ticking = false;\n\n return function(...args: TArgs) {\n // Save the args for use later\n argsToUse = args;\n if (!ticking) {\n ticking = true;\n requestAnimFrame.call(window, () => {\n ticking = false;\n fn.apply(thisArg, argsToUse);\n });\n }\n };\n}\n\n/**\n * Debounces calling `fn` for `delay` ms\n */\nexport function debounce<TArgs extends Array<any>>(fn: (...args: TArgs) => void, delay: number) {\n let timeout;\n return function(...args: TArgs) {\n if (delay) {\n clearTimeout(timeout);\n timeout = setTimeout(fn, delay, args);\n } else {\n fn.apply(this, args);\n }\n return delay;\n };\n}\n\n/**\n * Converts 'start' to 'left', 'end' to 'right' and others to 'center'\n * @private\n */\nexport const _toLeftRightCenter = (align: 'start' | 'end' | 'center') => align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';\n\n/**\n * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`\n * @private\n */\nexport const _alignStartEnd = (align: 'start' | 'end' | 'center', start: number, end: number) => align === 'start' ? start : align === 'end' ? end : (start + end) / 2;\n\n/**\n * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`\n * @private\n */\nexport const _textX = (align: 'left' | 'right' | 'center', left: number, right: number, rtl: boolean) => {\n const check = rtl ? 'left' : 'right';\n return align === check ? right : align === 'center' ? (left + right) / 2 : left;\n};\n\n/**\n * Return start and count of visible points.\n * @private\n */\nexport function _getStartAndCountOfVisiblePoints(meta: ChartMeta<'line' | 'scatter'>, points: PointElement[], animationsDisabled: boolean) {\n const pointCount = points.length;\n\n let start = 0;\n let count = pointCount;\n\n if (meta._sorted) {\n const {iScale, vScale, _parsed} = meta;\n const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null;\n const axis = iScale.axis;\n const {min, max, minDefined, maxDefined} = iScale.getUserBounds();\n\n if (minDefined) {\n start = Math.min(\n // @ts-expect-error Need to type _parsed\n _lookupByKey(_parsed, axis, min).lo,\n // @ts-expect-error Need to fix types on _lookupByKey\n animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo);\n if (spanGaps) {\n const distanceToDefinedLo = (_parsed\n .slice(0, start + 1)\n .reverse()\n .findIndex(\n point => !isNullOrUndef(point[vScale.axis])));\n start -= Math.max(0, distanceToDefinedLo);\n }\n start = _limitValue(start, 0, pointCount - 1);\n }\n if (maxDefined) {\n let end = Math.max(\n // @ts-expect-error Need to type _parsed\n _lookupByKey(_parsed, iScale.axis, max, true).hi + 1,\n // @ts-expect-error Need to fix types on _lookupByKey\n animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1);\n if (spanGaps) {\n const distanceToDefinedHi = (_parsed\n .slice(end - 1)\n .findIndex(\n point => !isNullOrUndef(point[vScale.axis])));\n end += Math.max(0, distanceToDefinedHi);\n }\n count = _limitValue(end, start, pointCount) - start;\n } else {\n count = pointCount - start;\n }\n }\n\n return {start, count};\n}\n\n/**\n * Checks if the scale ranges have changed.\n * @param {object} meta - dataset meta.\n * @returns {boolean}\n * @private\n */\nexport function _scaleRangesChanged(meta) {\n const {xScale, yScale, _scaleRanges} = meta;\n const newRanges = {\n xmin: xScale.min,\n xmax: xScale.max,\n ymin: yScale.min,\n ymax: yScale.max\n };\n if (!_scaleRanges) {\n meta._scaleRanges = newRanges;\n return true;\n }\n const changed = _scaleRanges.xmin !== xScale.min\n\t\t|| _scaleRanges.xmax !== xScale.max\n\t\t|| _scaleRanges.ymin !== yScale.min\n\t\t|| _scaleRanges.ymax !== yScale.max;\n\n Object.assign(_scaleRanges, newRanges);\n return changed;\n}\n","import {PI, TAU, HALF_PI} from './helpers.math.js';\n\nconst atEdge = (t: number) => t === 0 || t === 1;\nconst elasticIn = (t: number, s: number, p: number) => -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));\nconst elasticOut = (t: number, s: number, p: number) => Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;\n\n/**\n * Easing functions adapted from Robert Penner's easing equations.\n * @namespace Chart.helpers.easing.effects\n * @see http://www.robertpenner.com/easing/\n */\nconst effects = {\n linear: (t: number) => t,\n\n easeInQuad: (t: number) => t * t,\n\n easeOutQuad: (t: number) => -t * (t - 2),\n\n easeInOutQuad: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t\n : -0.5 * ((--t) * (t - 2) - 1),\n\n easeInCubic: (t: number) => t * t * t,\n\n easeOutCubic: (t: number) => (t -= 1) * t * t + 1,\n\n easeInOutCubic: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t * t\n : 0.5 * ((t -= 2) * t * t + 2),\n\n easeInQuart: (t: number) => t * t * t * t,\n\n easeOutQuart: (t: number) => -((t -= 1) * t * t * t - 1),\n\n easeInOutQuart: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t * t * t\n : -0.5 * ((t -= 2) * t * t * t - 2),\n\n easeInQuint: (t: number) => t * t * t * t * t,\n\n easeOutQuint: (t: number) => (t -= 1) * t * t * t * t + 1,\n\n easeInOutQuint: (t: number) => ((t /= 0.5) < 1)\n ? 0.5 * t * t * t * t * t\n : 0.5 * ((t -= 2) * t * t * t * t + 2),\n\n easeInSine: (t: number) => -Math.cos(t * HALF_PI) + 1,\n\n easeOutSine: (t: number) => Math.sin(t * HALF_PI),\n\n easeInOutSine: (t: number) => -0.5 * (Math.cos(PI * t) - 1),\n\n easeInExpo: (t: number) => (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)),\n\n easeOutExpo: (t: number) => (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1,\n\n easeInOutExpo: (t: number) => atEdge(t) ? t : t < 0.5\n ? 0.5 * Math.pow(2, 10 * (t * 2 - 1))\n : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),\n\n easeInCirc: (t: number) => (t >= 1) ? t : -(Math.sqrt(1 - t * t) - 1),\n\n easeOutCirc: (t: number) => Math.sqrt(1 - (t -= 1) * t),\n\n easeInOutCirc: (t: number) => ((t /= 0.5) < 1)\n ? -0.5 * (Math.sqrt(1 - t * t) - 1)\n : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),\n\n easeInElastic: (t: number) => atEdge(t) ? t : elasticIn(t, 0.075, 0.3),\n\n easeOutElastic: (t: number) => atEdge(t) ? t : elasticOut(t, 0.075, 0.3),\n\n easeInOutElastic(t: number) {\n const s = 0.1125;\n const p = 0.45;\n return atEdge(t) ? t :\n t < 0.5\n ? 0.5 * elasticIn(t * 2, s, p)\n : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);\n },\n\n easeInBack(t: number) {\n const s = 1.70158;\n return t * t * ((s + 1) * t - s);\n },\n\n easeOutBack(t: number) {\n const s = 1.70158;\n return (t -= 1) * t * ((s + 1) * t + s) + 1;\n },\n\n easeInOutBack(t: number) {\n let s = 1.70158;\n if ((t /= 0.5) < 1) {\n return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));\n }\n return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);\n },\n\n easeInBounce: (t: number) => 1 - effects.easeOutBounce(1 - t),\n\n easeOutBounce(t: number) {\n const m = 7.5625;\n const d = 2.75;\n if (t < (1 / d)) {\n return m * t * t;\n }\n if (t < (2 / d)) {\n return m * (t -= (1.5 / d)) * t + 0.75;\n }\n if (t < (2.5 / d)) {\n return m * (t -= (2.25 / d)) * t + 0.9375;\n }\n return m * (t -= (2.625 / d)) * t + 0.984375;\n },\n\n easeInOutBounce: (t: number) => (t < 0.5)\n ? effects.easeInBounce(t * 2) * 0.5\n : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5,\n} as const;\n\nexport type EasingFunction = keyof typeof effects\n\nexport default effects;\n","import {Color} from '@kurkle/color';\n\nexport function isPatternOrGradient(value: unknown): value is CanvasPattern | CanvasGradient {\n if (value && typeof value === 'object') {\n const type = value.toString();\n return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';\n }\n\n return false;\n}\n\nexport function color(value: CanvasGradient): CanvasGradient;\nexport function color(value: CanvasPattern): CanvasPattern;\nexport function color(\n value:\n | string\n | { r: number; g: number; b: number; a: number }\n | [number, number, number]\n | [number, number, number, number]\n): Color;\nexport function color(value) {\n return isPatternOrGradient(value) ? value : new Color(value);\n}\n\nexport function getHoverColor(value: CanvasGradient): CanvasGradient;\nexport function getHoverColor(value: CanvasPattern): CanvasPattern;\nexport function getHoverColor(value: string): string;\nexport function getHoverColor(value) {\n return isPatternOrGradient(value)\n ? value\n : new Color(value).saturate(0.5).darken(0.1).hexString();\n}\n","const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension'];\nconst colors = ['color', 'borderColor', 'backgroundColor'];\n\nexport function applyAnimationsDefaults(defaults) {\n defaults.set('animation', {\n delay: undefined,\n duration: 1000,\n easing: 'easeOutQuart',\n fn: undefined,\n from: undefined,\n loop: undefined,\n to: undefined,\n type: undefined,\n });\n\n defaults.describe('animation', {\n _fallback: false,\n _indexable: false,\n _scriptable: (name) => name !== 'onProgress' && name !== 'onComplete' && name !== 'fn',\n });\n\n defaults.set('animations', {\n colors: {\n type: 'color',\n properties: colors\n },\n numbers: {\n type: 'number',\n properties: numbers\n },\n });\n\n defaults.describe('animations', {\n _fallback: 'animation',\n });\n\n defaults.set('transitions', {\n active: {\n animation: {\n duration: 400\n }\n },\n resize: {\n animation: {\n duration: 0\n }\n },\n show: {\n animations: {\n colors: {\n from: 'transparent'\n },\n visible: {\n type: 'boolean',\n duration: 0 // show immediately\n },\n }\n },\n hide: {\n animations: {\n colors: {\n to: 'transparent'\n },\n visible: {\n type: 'boolean',\n easing: 'linear',\n fn: v => v | 0 // for keeping the dataset visible all the way through the animation\n },\n }\n }\n });\n}\n","export function applyLayoutsDefaults(defaults) {\n defaults.set('layout', {\n autoPadding: true,\n padding: {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n }\n });\n}\n","\nconst intlCache = new Map<string, Intl.NumberFormat>();\n\nfunction getNumberFormat(locale: string, options?: Intl.NumberFormatOptions) {\n options = options || {};\n const cacheKey = locale + JSON.stringify(options);\n let formatter = intlCache.get(cacheKey);\n if (!formatter) {\n formatter = new Intl.NumberFormat(locale, options);\n intlCache.set(cacheKey, formatter);\n }\n return formatter;\n}\n\nexport function formatNumber(num: number, locale: string, options?: Intl.NumberFormatOptions) {\n return getNumberFormat(locale, options).format(num);\n}\n","import {isArray} from '../helpers/helpers.core.js';\nimport {formatNumber} from '../helpers/helpers.intl.js';\nimport {log10} from '../helpers/helpers.math.js';\n\n/**\n * Namespace to hold formatters for different types of ticks\n * @namespace Chart.Ticks.formatters\n */\nconst formatters = {\n /**\n * Formatter for value labels\n * @method Chart.Ticks.formatters.values\n * @param value the value to display\n * @return {string|string[]} the label to display\n */\n values(value) {\n return isArray(value) ? /** @type {string[]} */ (value) : '' + value;\n },\n\n /**\n * Formatter for numeric ticks\n * @method Chart.Ticks.formatters.numeric\n * @param tickValue {number} the value to be formatted\n * @param index {number} the position of the tickValue parameter in the ticks array\n * @param ticks {object[]} the list of ticks being converted\n * @return {string} string representation of the tickValue parameter\n */\n numeric(tickValue, index, ticks) {\n if (tickValue === 0) {\n return '0'; // never show decimal places for 0\n }\n\n const locale = this.chart.options.locale;\n let notation;\n let delta = tickValue; // This is used when there are less than 2 ticks as the tick interval.\n\n if (ticks.length > 1) {\n // all ticks are small or there huge numbers; use scientific notation\n const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));\n if (maxTick < 1e-4 || maxTick > 1e+15) {\n notation = 'scientific';\n }\n\n delta = calculateDelta(tickValue, ticks);\n }\n\n const logDelta = log10(Math.abs(delta));\n\n // When datasets have values approaching Number.MAX_VALUE, the tick calculations might result in\n // infinity and eventually NaN. Passing NaN for minimumFractionDigits or maximumFractionDigits\n // will make the number formatter throw. So instead we check for isNaN and use a fallback value.\n //\n // toFixed has a max of 20 decimal places\n const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);\n\n const options = {notation, minimumFractionDigits: numDecimal, maximumFractionDigits: numDecimal};\n Object.assign(options, this.options.ticks.format);\n\n return formatNumber(tickValue, locale, options);\n },\n\n\n /**\n * Formatter for logarithmic ticks\n * @method Chart.Ticks.formatters.logarithmic\n * @param tickValue {number} the value to be formatted\n * @param index {number} the position of the tickValue parameter in the ticks array\n * @param ticks {object[]} the list of ticks being converted\n * @return {string} string representation of the tickValue parameter\n */\n logarithmic(tickValue, index, ticks) {\n if (tickValue === 0) {\n return '0';\n }\n const remain = ticks[index].significand || (tickValue / (Math.pow(10, Math.floor(log10(tickValue)))));\n if ([1, 2, 3, 5, 10, 15].includes(remain) || index > 0.8 * ticks.length) {\n return formatters.numeric.call(this, tickValue, index, ticks);\n }\n return '';\n }\n\n};\n\n\nfunction calculateDelta(tickValue, ticks) {\n // Figure out how many digits to show\n // The space between the first two ticks might be smaller than normal spacing\n let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;\n\n // If we have a number like 2.5 as the delta, figure out how many decimal places we need\n if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {\n // not an integer\n delta = tickValue - Math.floor(tickValue);\n }\n return delta;\n}\n\n/**\n * Namespace to hold static tick generation functions\n * @namespace Chart.Ticks\n */\nexport default {formatters};\n","import Ticks from './core.ticks.js';\n\nexport function applyScaleDefaults(defaults) {\n defaults.set('scale', {\n display: true,\n offset: false,\n reverse: false,\n beginAtZero: false,\n\n /**\n * Scale boundary strategy (bypassed by min/max time options)\n * - `data`: make sure data are fully visible, ticks outside are removed\n * - `ticks`: make sure ticks are fully visible, data outside are truncated\n * @see https://github.com/chartjs/Chart.js/pull/4556\n * @since 3.0.0\n */\n bounds: 'ticks',\n\n clip: true,\n\n /**\n * Addition grace added to max and reduced from min data value.\n * @since 3.0.0\n */\n grace: 0,\n\n // grid line settings\n grid: {\n display: true,\n lineWidth: 1,\n drawOnChartArea: true,\n drawTicks: true,\n tickLength: 8,\n tickWidth: (_ctx, options) => options.lineWidth,\n tickColor: (_ctx, options) => options.color,\n offset: false,\n },\n\n border: {\n display: true,\n dash: [],\n dashOffset: 0.0,\n width: 1\n },\n\n // scale title\n title: {\n // display property\n display: false,\n\n // actual label\n text: '',\n\n // top/bottom padding\n padding: {\n top: 4,\n bottom: 4\n }\n },\n\n // label settings\n ticks: {\n minRotation: 0,\n maxRotation: 50,\n mirror: false,\n textStrokeWidth: 0,\n textStrokeColor: '',\n padding: 3,\n display: true,\n autoSkip: true,\n autoSkipPadding: 3,\n labelOffset: 0,\n // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.\n callback: Ticks.formatters.values,\n minor: {},\n major: {},\n align: 'center',\n crossAlign: 'near',\n\n showLabelBackdrop: false,\n backdropColor: 'rgba(255, 255, 255, 0.75)',\n backdropPadding: 2,\n }\n });\n\n defaults.route('scale.ticks', 'color', '', 'color');\n defaults.route('scale.grid', 'color', '', 'borderColor');\n defaults.route('scale.border', 'color', '', 'borderColor');\n defaults.route('scale.title', 'color', '', 'color');\n\n defaults.describe('scale', {\n _fallback: false,\n _scriptable: (name) => !name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',\n _indexable: (name) => name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash',\n });\n\n defaults.describe('scales', {\n _fallback: 'scale',\n });\n\n defaults.describe('scale.ticks', {\n _scriptable: (name) => name !== 'backdropPadding' && name !== 'callback',\n _indexable: (name) => name !== 'backdropPadding',\n });\n}\n","import {getHoverColor} from '../helpers/helpers.color.js';\nimport {isObject, merge, valueOrDefault} from '../helpers/helpers.core.js';\nimport {applyAnimationsDefaults} from './core.animations.defaults.js';\nimport {applyLayoutsDefaults} from './core.layouts.defaults.js';\nimport {applyScaleDefaults} from './core.scale.defaults.js';\n\nexport const overrides = Object.create(null);\nexport const descriptors = Object.create(null);\n\n/**\n * @param {object} node\n * @param {string} key\n * @return {object}\n */\nfunction getScope(node, key) {\n if (!key) {\n return node;\n }\n const keys = key.split('.');\n for (let i = 0, n = keys.length; i < n; ++i) {\n const k = keys[i];\n node = node[k] || (node[k] = Object.create(null));\n }\n return node;\n}\n\nfunction set(root, scope, values) {\n if (typeof scope === 'string') {\n return merge(getScope(root, scope), values);\n }\n return merge(getScope(root, ''), scope);\n}\n\n/**\n * Please use the module's default export which provides a singleton instance\n * Note: class is exported for typedoc\n */\nexport class Defaults {\n constructor(_descriptors, _appliers) {\n this.animation = undefined;\n this.backgroundColor = 'rgba(0,0,0,0.1)';\n this.borderColor = 'rgba(0,0,0,0.1)';\n this.color = '#666';\n this.datasets = {};\n this.devicePixelRatio = (context) => context.chart.platform.getDevicePixelRatio();\n this.elements = {};\n this.events = [\n 'mousemove',\n 'mouseout',\n 'click',\n 'touchstart',\n 'touchmove'\n ];\n this.font = {\n family: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n size: 12,\n style: 'normal',\n lineHeight: 1.2,\n weight: null\n };\n this.hover = {};\n this.hoverBackgroundColor = (ctx, options) => getHoverColor(options.backgroundColor);\n this.hoverBorderColor = (ctx, options) => getHoverColor(options.borderColor);\n this.hoverColor = (ctx, options) => getHoverColor(options.color);\n this.indexAxis = 'x';\n this.interaction = {\n mode: 'nearest',\n intersect: true,\n includeInvisible: false\n };\n this.maintainAspectRatio = true;\n this.onHover = null;\n this.onClick = null;\n this.parsing = true;\n this.plugins = {};\n this.responsive = true;\n this.scale = undefined;\n this.scales = {};\n this.showLine = true;\n this.drawActiveElementsOnTop = true;\n\n this.describe(_descriptors);\n this.apply(_appliers);\n }\n\n /**\n\t * @param {string|object} scope\n\t * @param {object} [values]\n\t */\n set(scope, values) {\n return set(this, scope, values);\n }\n\n /**\n\t * @param {string} scope\n\t */\n get(scope) {\n return getScope(this, scope);\n }\n\n /**\n\t * @param {string|object} scope\n\t * @param {object} [values]\n\t */\n describe(scope, values) {\n return set(descriptors, scope, values);\n }\n\n override(scope, values) {\n return set(overrides, scope, values);\n }\n\n /**\n\t * Routes the named defaults to fallback to another scope/name.\n\t * This routing is useful when those target values, like defaults.color, are changed runtime.\n\t * If the values would be copied, the runtime change would not take effect. By routing, the\n\t * fallback is evaluated at each access, so its always up to date.\n\t *\n\t * Example:\n\t *\n\t * \tdefaults.route('elements.arc', 'backgroundColor', '', 'color')\n\t * - reads the backgroundColor from defaults.color when undefined locally\n\t *\n\t * @param {string} scope Scope this route applies to.\n\t * @param {string} name Property name that should be routed to different namespace when not defined here.\n\t * @param {string} targetScope The namespace where those properties should be routed to.\n\t * Empty string ('') is the root of defaults.\n\t * @param {string} targetName The target name in the target scope the property should be routed to.\n\t */\n route(scope, name, targetScope, targetName) {\n const scopeObject = getScope(this, scope);\n const targetScopeObject = getScope(this, targetScope);\n const privateName = '_' + name;\n\n Object.defineProperties(scopeObject, {\n // A private property is defined to hold the actual value, when this property is set in its scope (set in the setter)\n [privateName]: {\n value: scopeObject[name],\n writable: true\n },\n // The actual property is defined as getter/setter so we can do the routing when value is not locally set.\n [name]: {\n enumerable: true,\n get() {\n const local = this[privateName];\n const target = targetScopeObject[targetName];\n if (isObject(local)) {\n return Object.assign({}, target, local);\n }\n return valueOrDefault(local, target);\n },\n set(value) {\n this[privateName] = value;\n }\n }\n });\n }\n\n apply(appliers) {\n appliers.forEach((apply) => apply(this));\n }\n}\n\n// singleton instance\nexport default /* #__PURE__ */ new Defaults({\n _scriptable: (name) => !name.startsWith('on'),\n _indexable: (name) => name !== 'events',\n hover: {\n _fallback: 'interaction'\n },\n interaction: {\n _scriptable: false,\n _indexable: false,\n }\n}, [applyAnimationsDefaults, applyLayoutsDefaults, applyScaleDefaults]);\n","import type {\n Chart,\n Point,\n FontSpec,\n CanvasFontSpec,\n PointStyle,\n RenderTextOpts,\n BackdropOptions\n} from '../types/index.js';\nimport type {\n TRBL,\n SplinePoint,\n RoundedRect,\n TRBLCorners\n} from '../types/geometric.js';\nimport {isArray, isNullOrUndef} from './helpers.core.js';\nimport {PI, TAU, HALF_PI, QUARTER_PI, TWO_THIRDS_PI, RAD_PER_DEG} from './helpers.math.js';\n\n/**\n * Converts the given font object into a CSS font string.\n * @param font - A font object.\n * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font\n * @private\n */\nexport function toFontString(font: FontSpec) {\n if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {\n return null;\n }\n\n return (font.style ? font.style + ' ' : '')\n\t\t+ (font.weight ? font.weight + ' ' : '')\n\t\t+ font.size + 'px '\n\t\t+ font.family;\n}\n\n/**\n * @private\n */\nexport function _measureText(\n ctx: CanvasRenderingContext2D,\n data: Record<string, number>,\n gc: string[],\n longest: number,\n string: string\n) {\n let textWidth = data[string];\n if (!textWidth) {\n textWidth = data[string] = ctx.measureText(string).width;\n gc.push(string);\n }\n if (textWidth > longest) {\n longest = textWidth;\n }\n return longest;\n}\n\ntype Thing = string | undefined | null\ntype Things = (Thing | Thing[])[]\n\n/**\n * @private\n */\n// eslint-disable-next-line complexity\nexport function _longestText(\n ctx: CanvasRenderingContext2D,\n font: string,\n arrayOfThings: Things,\n cache?: {data?: Record<string, number>, garbageCollect?: string[], font?: string}\n) {\n cache = cache || {};\n let data = cache.data = cache.data || {};\n let gc = cache.garbageCollect = cache.garbageCollect || [];\n\n if (cache.font !== font) {\n data = cache.data = {};\n gc = cache.garbageCollect = [];\n cache.font = font;\n }\n\n ctx.save();\n\n ctx.font = font;\n let longest = 0;\n const ilen = arrayOfThings.length;\n let i: number, j: number, jlen: number, thing: Thing | Thing[], nestedThing: Thing | Thing[];\n for (i = 0; i < ilen; i++) {\n thing = arrayOfThings[i];\n\n // Undefined strings and arrays should not be measured\n if (thing !== undefined && thing !== null && !isArray(thing)) {\n longest = _measureText(ctx, data, gc, longest, thing);\n } else if (isArray(thing)) {\n // if it is an array lets measure each element\n // to do maybe simplify this function a bit so we can do this more recursively?\n for (j = 0, jlen = thing.length; j < jlen; j++) {\n nestedThing = thing[j];\n // Undefined strings and arrays should not be measured\n if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {\n longest = _measureText(ctx, data, gc, longest, nestedThing);\n }\n }\n }\n }\n\n ctx.restore();\n\n const gcLen = gc.length / 2;\n if (gcLen > arrayOfThings.length) {\n for (i = 0; i < gcLen; i++) {\n delete data[gc[i]];\n }\n gc.splice(0, gcLen);\n }\n return longest;\n}\n\n/**\n * Returns the aligned pixel value to avoid anti-aliasing blur\n * @param chart - The chart instance.\n * @param pixel - A pixel value.\n * @param width - The width of the element.\n * @returns The aligned pixel value.\n * @private\n */\nexport function _alignPixel(chart: Chart, pixel: number, width: number) {\n const devicePixelRatio = chart.currentDevicePixelRatio;\n const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;\n return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;\n}\n\n/**\n * Clears the entire canvas.\n */\nexport function clearCanvas(canvas?: HTMLCanvasElement, ctx?: CanvasRenderingContext2D) {\n if (!ctx && !canvas) {\n return;\n }\n\n ctx = ctx || canvas.getContext('2d');\n\n ctx.save();\n // canvas.width and canvas.height do not consider the canvas transform,\n // while clearRect does\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.restore();\n}\n\nexport interface DrawPointOptions {\n pointStyle: PointStyle;\n rotation?: number;\n radius: number;\n borderWidth: number;\n}\n\nexport function drawPoint(\n ctx: CanvasRenderingContext2D,\n options: DrawPointOptions,\n x: number,\n y: number\n) {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n drawPointLegend(ctx, options, x, y, null);\n}\n\n// eslint-disable-next-line complexity\nexport function drawPointLegend(\n ctx: CanvasRenderingContext2D,\n options: DrawPointOptions,\n x: number,\n y: number,\n w: number\n) {\n let type: string, xOffset: number, yOffset: number, size: number, cornerRadius: number, width: number, xOffsetW: number, yOffsetW: number;\n const style = options.pointStyle;\n const rotation = options.rotation;\n const radius = options.radius;\n let rad = (rotation || 0) * RAD_PER_DEG;\n\n if (style && typeof style === 'object') {\n type = style.toString();\n if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {\n ctx.save();\n ctx.translate(x, y);\n ctx.rotate(rad);\n ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);\n ctx.restore();\n return;\n }\n }\n\n if (isNaN(radius) || radius <= 0) {\n return;\n }\n\n ctx.beginPath();\n\n switch (style) {\n // Default includes circle\n default:\n if (w) {\n ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);\n } else {\n ctx.arc(x, y, radius, 0, TAU);\n }\n ctx.closePath();\n break;\n case 'triangle':\n width = w ? w / 2 : radius;\n ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n rad += TWO_THIRDS_PI;\n ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n rad += TWO_THIRDS_PI;\n ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n ctx.closePath();\n break;\n case 'rectRounded':\n // NOTE: the rounded rect implementation changed to use `arc` instead of\n // `quadraticCurveTo` since it generates better results when rect is\n // almost a circle. 0.516 (instead of 0.5) produces results with visually\n // closer proportion to the previous impl and it is inscribed in the\n // circle with `radius`. For more details, see the following PRs:\n // https://github.com/chartjs/Chart.js/issues/5597\n // https://github.com/chartjs/Chart.js/issues/5858\n cornerRadius = radius * 0.516;\n size = radius - cornerRadius;\n xOffset = Math.cos(rad + QUARTER_PI) * size;\n xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n yOffset = Math.sin(rad + QUARTER_PI) * size;\n yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);\n ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);\n ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);\n ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);\n ctx.closePath();\n break;\n case 'rect':\n if (!rotation) {\n size = Math.SQRT1_2 * radius;\n width = w ? w / 2 : size;\n ctx.rect(x - width, y - size, 2 * width, 2 * size);\n break;\n }\n rad += QUARTER_PI;\n /* falls through */\n case 'rectRot':\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n ctx.closePath();\n break;\n case 'crossRot':\n rad += QUARTER_PI;\n /* falls through */\n case 'cross':\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.moveTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n break;\n case 'star':\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.moveTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n rad += QUARTER_PI;\n xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n xOffset = Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n ctx.moveTo(x - xOffsetW, y - yOffset);\n ctx.lineTo(x + xOffsetW, y + yOffset);\n ctx.moveTo(x + yOffsetW, y - xOffset);\n ctx.lineTo(x - yOffsetW, y + xOffset);\n break;\n case 'line':\n xOffset = w ? w / 2 : Math.cos(rad) * radius;\n yOffset = Math.sin(rad) * radius;\n ctx.moveTo(x - xOffset, y - yOffset);\n ctx.lineTo(x + xOffset, y + yOffset);\n break;\n case 'dash':\n ctx.moveTo(x, y);\n ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);\n break;\n case false:\n ctx.closePath();\n break;\n }\n\n ctx.fill();\n if (options.borderWidth > 0) {\n ctx.stroke();\n }\n}\n\n/**\n * Returns true if the point is inside the rectangle\n * @param point - The point to test\n * @param area - The rectangle\n * @param margin - allowed margin\n * @private\n */\nexport function _isPointInArea(\n point: Point,\n area: TRBL,\n margin?: number\n) {\n margin = margin || 0.5; // margin - default is to match rounded decimals\n\n return !area || (point && point.x > area.left - margin && point.x < area.right + margin &&\n\t\tpoint.y > area.top - margin && point.y < area.bottom + margin);\n}\n\nexport function clipArea(ctx: CanvasRenderingContext2D, area: TRBL) {\n ctx.save();\n ctx.beginPath();\n ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);\n ctx.clip();\n}\n\nexport function unclipArea(ctx: CanvasRenderingContext2D) {\n ctx.restore();\n}\n\n/**\n * @private\n */\nexport function _steppedLineTo(\n ctx: CanvasRenderingContext2D,\n previous: Point,\n target: Point,\n flip?: boolean,\n mode?: string\n) {\n if (!previous) {\n return ctx.lineTo(target.x, target.y);\n }\n if (mode === 'middle') {\n const midpoint = (previous.x + target.x) / 2.0;\n ctx.lineTo(midpoint, previous.y);\n ctx.lineTo(midpoint, target.y);\n } else if (mode === 'after' !== !!flip) {\n ctx.lineTo(previous.x, target.y);\n } else {\n ctx.lineTo(target.x, previous.y);\n }\n ctx.lineTo(target.x, target.y);\n}\n\n/**\n * @private\n */\nexport function _bezierCurveTo(\n ctx: CanvasRenderingContext2D,\n previous: SplinePoint,\n target: SplinePoint,\n flip?: boolean\n) {\n if (!previous) {\n return ctx.lineTo(target.x, target.y);\n }\n ctx.bezierCurveTo(\n flip ? previous.cp1x : previous.cp2x,\n flip ? previous.cp1y : previous.cp2y,\n flip ? target.cp2x : target.cp1x,\n flip ? target.cp2y : target.cp1y,\n target.x,\n target.y);\n}\n\nfunction setRenderOpts(ctx: CanvasRenderingContext2D, opts: RenderTextOpts) {\n if (opts.translation) {\n ctx.translate(opts.translation[0], opts.translation[1]);\n }\n\n if (!isNullOrUndef(opts.rotation)) {\n ctx.rotate(opts.rotation);\n }\n\n if (opts.color) {\n ctx.fillStyle = opts.color;\n }\n\n if (opts.textAlign) {\n ctx.textAlign = opts.textAlign;\n }\n\n if (opts.textBaseline) {\n ctx.textBaseline = opts.textBaseline;\n }\n}\n\nfunction decorateText(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n line: string,\n opts: RenderTextOpts\n) {\n if (opts.strikethrough || opts.underline) {\n /**\n * Now that IE11 support has been dropped, we can use more\n * of the TextMetrics object. The actual bounding boxes\n * are unflagged in Chrome, Firefox, Edge, and Safari so they\n * can be safely used.\n * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility\n */\n const metrics = ctx.measureText(line);\n const left = x - metrics.actualBoundingBoxLeft;\n const right = x + metrics.actualBoundingBoxRight;\n const top = y - metrics.actualBoundingBoxAscent;\n const bottom = y + metrics.actualBoundingBoxDescent;\n const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;\n\n ctx.strokeStyle = ctx.fillStyle;\n ctx.beginPath();\n ctx.lineWidth = opts.decorationWidth || 2;\n ctx.moveTo(left, yDecoration);\n ctx.lineTo(right, yDecoration);\n ctx.stroke();\n }\n}\n\nfunction drawBackdrop(ctx: CanvasRenderingContext2D, opts: BackdropOptions) {\n const oldColor = ctx.fillStyle;\n\n ctx.fillStyle = opts.color as string;\n ctx.fillRect(opts.left, opts.top, opts.width, opts.height);\n ctx.fillStyle = oldColor;\n}\n\n/**\n * Render text onto the canvas\n */\nexport function renderText(\n ctx: CanvasRenderingContext2D,\n text: string | string[],\n x: number,\n y: number,\n font: CanvasFontSpec,\n opts: RenderTextOpts = {}\n) {\n const lines = isArray(text) ? text : [text];\n const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';\n let i: number, line: string;\n\n ctx.save();\n ctx.font = font.string;\n setRenderOpts(ctx, opts);\n\n for (i = 0; i < lines.length; ++i) {\n line = lines[i];\n\n if (opts.backdrop) {\n drawBackdrop(ctx, opts.backdrop);\n }\n\n if (stroke) {\n if (opts.strokeColor) {\n ctx.strokeStyle = opts.strokeColor;\n }\n\n if (!isNullOrUndef(opts.strokeWidth)) {\n ctx.lineWidth = opts.strokeWidth;\n }\n\n ctx.strokeText(line, x, y, opts.maxWidth);\n }\n\n ctx.fillText(line, x, y, opts.maxWidth);\n decorateText(ctx, x, y, line, opts);\n\n y += Number(font.lineHeight);\n }\n\n ctx.restore();\n}\n\n/**\n * Add a path of a rectangle with rounded corners to the current sub-path\n * @param ctx - Context\n * @param rect - Bounding rect\n */\nexport function addRoundedRectPath(\n ctx: CanvasRenderingContext2D,\n rect: RoundedRect & { radius: TRBLCorners }\n) {\n const {x, y, w, h, radius} = rect;\n\n // top left arc\n ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true);\n\n // line from top left to bottom left\n ctx.lineTo(x, y + h - radius.bottomLeft);\n\n // bottom left arc\n ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);\n\n // line from bottom left to bottom right\n ctx.lineTo(x + w - radius.bottomRight, y + h);\n\n // bottom right arc\n ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);\n\n // line from bottom right to top right\n ctx.lineTo(x + w, y + radius.topRight);\n\n // top right arc\n ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);\n\n // line from top right to top left\n ctx.lineTo(x + radius.topLeft, y);\n}\n","import defaults from '../core/core.defaults.js';\nimport {isArray, isObject, toDimension, valueOrDefault} from './helpers.core.js';\nimport {toFontString} from './helpers.canvas.js';\nimport type {ChartArea, FontSpec, Point} from '../types/index.js';\nimport type {TRBL, TRBLCorners} from '../types/geometric.js';\n\nconst LINE_HEIGHT = /^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/;\nconst FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;\n\n/**\n * @alias Chart.helpers.options\n * @namespace\n */\n/**\n * Converts the given line height `value` in pixels for a specific font `size`.\n * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n * @param size - The font size (in pixels) used to resolve relative `value`.\n * @returns The effective line height in pixels (size * 1.2 if value is invalid).\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n * @since 2.7.0\n */\nexport function toLineHeight(value: number | string, size: number): number {\n const matches = ('' + value).match(LINE_HEIGHT);\n if (!matches || matches[1] === 'normal') {\n return size * 1.2;\n }\n\n value = +matches[2];\n\n switch (matches[3]) {\n case 'px':\n return value;\n case '%':\n value /= 100;\n break;\n default:\n break;\n }\n\n return size * value;\n}\n\nconst numberOrZero = (v: unknown) => +v || 0;\n\n/**\n * @param value\n * @param props\n */\nexport function _readValueToProps<K extends string>(value: number | Record<K, number>, props: K[]): Record<K, number>;\nexport function _readValueToProps<K extends string, T extends string>(value: number | Record<K & T, number>, props: Record<T, K>): Record<T, number>;\nexport function _readValueToProps(value: number | Record<string, number>, props: string[] | Record<string, string>) {\n const ret = {};\n const objProps = isObject(props);\n const keys = objProps ? Object.keys(props) : props;\n const read = isObject(value)\n ? objProps\n ? prop => valueOrDefault(value[prop], value[props[prop]])\n : prop => value[prop]\n : () => value;\n\n for (const prop of keys) {\n ret[prop] = numberOrZero(read(prop));\n }\n return ret;\n}\n\n/**\n * Converts the given value into a TRBL object.\n * @param value - If a number, set the value to all TRBL component,\n * else, if an object, use defined properties and sets undefined ones to 0.\n * x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left)\n * @since 3.0.0\n */\nexport function toTRBL(value: number | TRBL | Point) {\n return _readValueToProps(value, {top: 'y', right: 'x', bottom: 'y', left: 'x'});\n}\n\n/**\n * Converts the given value into a TRBL corners object (similar with css border-radius).\n * @param value - If a number, set the value to all TRBL corner components,\n * else, if an object, use defined properties and sets undefined ones to 0.\n * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)\n * @since 3.0.0\n */\nexport function toTRBLCorners(value: number | TRBLCorners) {\n return _readValueToProps(value, ['topLeft', 'topRight', 'bottomLeft', 'bottomRight']);\n}\n\n/**\n * Converts the given value into a padding object with pre-computed width/height.\n * @param value - If a number, set the value to all TRBL component,\n * else, if an object, use defined properties and sets undefined ones to 0.\n * x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left, width, height)\n * @since 2.7.0\n */\nexport function toPadding(value?: number | TRBL): ChartArea {\n const obj = toTRBL(value) as ChartArea;\n\n obj.width = obj.left + obj.right;\n obj.height = obj.top + obj.bottom;\n\n return obj;\n}\n\n/**\n * Parses font options and returns the font object.\n * @param options - A object that contains font options to be parsed.\n * @param fallback - A object that contains fallback font options.\n * @return The font object.\n * @private\n */\n\nexport function toFont(options: Partial<FontSpec>, fallback?: Partial<FontSpec>) {\n options = options || {};\n fallback = fallback || defaults.font as FontSpec;\n\n let size = valueOrDefault(options.size, fallback.size);\n\n if (typeof size === 'string') {\n size = parseInt(size, 10);\n }\n let style = valueOrDefault(options.style, fallback.style);\n if (style && !('' + style).match(FONT_STYLE)) {\n console.warn('Invalid font style specified: \"' + style + '\"');\n style = undefined;\n }\n\n const font = {\n family: valueOrDefault(options.family, fallback.family),\n lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),\n size,\n style,\n weight: valueOrDefault(options.weight, fallback.weight),\n string: ''\n };\n\n font.string = toFontString(font);\n return font;\n}\n\n/**\n * Evaluates the given `inputs` sequentially and returns the first defined value.\n * @param inputs - An array of values, falling back to the last value.\n * @param context - If defined and the current value is a function, the value\n * is called with `context` as first argument and the result becomes the new input.\n * @param index - If defined and the current value is an array, the value\n * at `index` become the new input.\n * @param info - object to return information about resolution in\n * @param info.cacheable - Will be set to `false` if option is not cacheable.\n * @since 2.7.0\n */\nexport function resolve(inputs: Array<unknown>, context?: object, index?: number, info?: { cacheable: boolean }) {\n let cacheable = true;\n let i: number, ilen: number, value: unknown;\n\n for (i = 0, ilen = inputs.length; i < ilen; ++i) {\n value = inputs[i];\n if (value === undefined) {\n continue;\n }\n if (context !== undefined && typeof value === 'function') {\n value = value(context);\n cacheable = false;\n }\n if (index !== undefined && isArray(value)) {\n value = value[index % value.length];\n cacheable = false;\n }\n if (value !== undefined) {\n if (info && !cacheable) {\n info.cacheable = false;\n }\n return value;\n }\n }\n}\n\n/**\n * @param minmax\n * @param grace\n * @param beginAtZero\n * @private\n */\nexport function _addGrace(minmax: { min: number; max: number; }, grace: number | string, beginAtZero: boolean) {\n const {min, max} = minmax;\n const change = toDimension(grace, (max - min) / 2);\n const keepZero = (value: number, add: number) => beginAtZero && value === 0 ? 0 : value + add;\n return {\n min: keepZero(min, -Math.abs(change)),\n max: keepZero(max, change)\n };\n}\n\n/**\n * Create a context inheriting parentContext\n * @param parentContext\n * @param context\n * @returns\n */\nexport function createContext<T extends object>(parentContext: null, context: T): T;\nexport function createContext<T extends object, P extends T>(parentContext: P, context: T): P & T;\nexport function createContext(parentContext: object, context: object) {\n return Object.assign(Object.create(parentContext), context);\n}\n","/* eslint-disable @typescript-eslint/no-use-before-define */\nimport type {AnyObject} from '../types/basic.js';\nimport type {ChartMeta} from '../types/index.js';\nimport type {\n ResolverObjectKey,\n ResolverCache,\n ResolverProxy,\n DescriptorDefaults,\n Descriptor,\n ContextCache,\n ContextProxy\n} from './helpers.config.types.js';\nimport {isArray, isFunction, isObject, resolveObjectKey, _capitalize} from './helpers.core.js';\n\nexport * from './helpers.config.types.js';\n\n/**\n * Creates a Proxy for resolving raw values for options.\n * @param scopes - The option scopes to look for values, in resolution order\n * @param prefixes - The prefixes for values, in resolution order.\n * @param rootScopes - The root option scopes\n * @param fallback - Parent scopes fallback\n * @param getTarget - callback for getting the target for changed values\n * @returns Proxy\n * @private\n */\nexport function _createResolver<\n T extends AnyObject[] = AnyObject[],\n R extends AnyObject[] = T\n>(\n scopes: T,\n prefixes = [''],\n rootScopes?: R,\n fallback?: ResolverObjectKey,\n getTarget = () => scopes[0]\n) {\n const finalRootScopes = rootScopes || scopes;\n if (typeof fallback === 'undefined') {\n fallback = _resolve('_fallback', scopes);\n }\n const cache: ResolverCache<T, R> = {\n [Symbol.toStringTag]: 'Object',\n _cacheable: true,\n _scopes: scopes,\n _rootScopes: finalRootScopes,\n _fallback: fallback,\n _getTarget: getTarget,\n override: (scope: AnyObject) => _createResolver([scope, ...scopes], prefixes, finalRootScopes, fallback),\n };\n return new Proxy(cache, {\n /**\n * A trap for the delete operator.\n */\n deleteProperty(target, prop: string) {\n delete target[prop]; // remove from cache\n delete target._keys; // remove cached keys\n delete scopes[0][prop]; // remove from top level scope\n return true;\n },\n\n /**\n * A trap for getting property values.\n */\n get(target, prop: string) {\n return _cached(target, prop,\n () => _resolveWithPrefixes(prop, prefixes, scopes, target));\n },\n\n /**\n * A trap for Object.getOwnPropertyDescriptor.\n * Also used by Object.hasOwnProperty.\n */\n getOwnPropertyDescriptor(target, prop) {\n return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);\n },\n\n /**\n * A trap for Object.getPrototypeOf.\n */\n getPrototypeOf() {\n return Reflect.getPrototypeOf(scopes[0]);\n },\n\n /**\n * A trap for the in operator.\n */\n has(target, prop: string) {\n return getKeysFromAllScopes(target).includes(prop);\n },\n\n /**\n * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.\n */\n ownKeys(target) {\n return getKeysFromAllScopes(target);\n },\n\n /**\n * A trap for setting property values.\n */\n set(target, prop: string, value) {\n const storage = target._storage || (target._storage = getTarget());\n target[prop] = storage[prop] = value; // set to top level scope + cache\n delete target._keys; // remove cached keys\n return true;\n }\n }) as ResolverProxy<T, R>;\n}\n\n/**\n * Returns an Proxy for resolving option values with context.\n * @param proxy - The Proxy returned by `_createResolver`\n * @param context - Context object for scriptable/indexable options\n * @param subProxy - The proxy provided for scriptable options\n * @param descriptorDefaults - Defaults for descriptors\n * @private\n */\nexport function _attachContext<\n T extends AnyObject[] = AnyObject[],\n R extends AnyObject[] = T\n>(\n proxy: ResolverProxy<T, R>,\n context: AnyObject,\n subProxy?: ResolverProxy<T, R>,\n descriptorDefaults?: DescriptorDefaults\n) {\n const cache: ContextCache<T, R> = {\n _cacheable: false,\n _proxy: proxy,\n _context: context,\n _subProxy: subProxy,\n _stack: new Set(),\n _descriptors: _descriptors(proxy, descriptorDefaults),\n setContext: (ctx: AnyObject) => _attachContext(proxy, ctx, subProxy, descriptorDefaults),\n override: (scope: AnyObject) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)\n };\n return new Proxy(cache, {\n /**\n * A trap for the delete operator.\n */\n deleteProperty(target, prop) {\n delete target[prop]; // remove from cache\n delete proxy[prop]; // remove from proxy\n return true;\n },\n\n /**\n * A trap for getting property values.\n */\n get(target, prop: string, receiver) {\n return _cached(target, prop,\n () => _resolveWithContext(target, prop, receiver));\n },\n\n /**\n * A trap for Object.getOwnPropertyDescriptor.\n * Also used by Object.hasOwnProperty.\n */\n getOwnPropertyDescriptor(target, prop) {\n return target._descriptors.allKeys\n ? Reflect.has(proxy, prop) ? {enumerable: true, configurable: true} : undefined\n : Reflect.getOwnPropertyDescriptor(proxy, prop);\n },\n\n /**\n * A trap for Object.getPrototypeOf.\n */\n getPrototypeOf() {\n return Reflect.getPrototypeOf(proxy);\n },\n\n /**\n * A trap for the in operator.\n */\n has(target, prop) {\n return Reflect.has(proxy, prop);\n },\n\n /**\n * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.\n */\n ownKeys() {\n return Reflect.ownKeys(proxy);\n },\n\n /**\n * A trap for setting property values.\n */\n set(target, prop, value) {\n proxy[prop] = value; // set to proxy\n delete target[prop]; // remove from cache\n return true;\n }\n }) as ContextProxy<T, R>;\n}\n\n/**\n * @private\n */\nexport function _descriptors(\n proxy: ResolverCache,\n defaults: DescriptorDefaults = {scriptable: true, indexable: true}\n): Descriptor {\n const {_scriptable = defaults.scriptable, _indexable = defaults.indexable, _allKeys = defaults.allKeys} = proxy;\n return {\n allKeys: _allKeys,\n scriptable: _scriptable,\n indexable: _indexable,\n isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable,\n isIndexable: isFunction(_indexable) ? _indexable : () => _indexable\n };\n}\n\nconst readKey = (prefix: string, name: string) => prefix ? prefix + _capitalize(name) : name;\nconst needsSubResolver = (prop: string, value: unknown) => isObject(value) && prop !== 'adapters' &&\n (Object.getPrototypeOf(value) === null || value.constructor === Object);\n\nfunction _cached(\n target: AnyObject,\n prop: string,\n resolve: () => unknown\n) {\n if (Object.prototype.hasOwnProperty.call(target, prop) || prop === 'constructor') {\n return target[prop];\n }\n\n const value = resolve();\n // cache the resolved value\n target[prop] = value;\n return value;\n}\n\nfunction _resolveWithContext(\n target: ContextCache,\n prop: string,\n receiver: AnyObject\n) {\n const {_proxy, _context, _subProxy, _descriptors: descriptors} = target;\n let value = _proxy[prop]; // resolve from proxy\n\n // resolve with context\n if (isFunction(value) && descriptors.isScriptable(prop)) {\n value = _resolveScriptable(prop, value, target, receiver);\n }\n if (isArray(value) && value.length) {\n value = _resolveArray(prop, value, target, descriptors.isIndexable);\n }\n if (needsSubResolver(prop, value)) {\n // if the resolved value is an object, create a sub resolver for it\n value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);\n }\n return value;\n}\n\nfunction _resolveScriptable(\n prop: string,\n getValue: (ctx: AnyObject, sub: AnyObject) => unknown,\n target: ContextCache,\n receiver: AnyObject\n) {\n const {_proxy, _context, _subProxy, _stack} = target;\n if (_stack.has(prop)) {\n throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);\n }\n _stack.add(prop);\n let value = getValue(_context, _subProxy || receiver);\n _stack.delete(prop);\n if (needsSubResolver(prop, value)) {\n // When scriptable option returns an object, create a resolver on that.\n value = createSubResolver(_proxy._scopes, _proxy, prop, value);\n }\n return value;\n}\n\nfunction _resolveArray(\n prop: string,\n value: unknown[],\n target: ContextCache,\n isIndexable: (key: string) => boolean\n) {\n const {_proxy, _context, _subProxy, _descriptors: descriptors} = target;\n\n if (typeof _context.index !== 'undefined' && isIndexable(prop)) {\n return value[_context.index % value.length];\n } else if (isObject(value[0])) {\n // Array of objects, return array or resolvers\n const arr = value;\n const scopes = _proxy._scopes.filter(s => s !== arr);\n value = [];\n for (const item of arr) {\n const resolver = createSubResolver(scopes, _proxy, prop, item);\n value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));\n }\n }\n return value;\n}\n\nfunction resolveFallback(\n fallback: ResolverObjectKey | ((prop: ResolverObjectKey, value: unknown) => ResolverObjectKey),\n prop: ResolverObjectKey,\n value: unknown\n) {\n return isFunction(fallback) ? fallback(prop, value) : fallback;\n}\n\nconst getScope = (key: ResolverObjectKey, parent: AnyObject) => key === true ? parent\n : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;\n\nfunction addScopes(\n set: Set<AnyObject>,\n parentScopes: AnyObject[],\n key: ResolverObjectKey,\n parentFallback: ResolverObjectKey,\n value: unknown\n) {\n for (const parent of parentScopes) {\n const scope = getScope(key, parent);\n if (scope) {\n set.add(scope);\n const fallback = resolveFallback(scope._fallback, key, value);\n if (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) {\n // When we reach the descriptor that defines a new _fallback, return that.\n // The fallback will resume to that new scope.\n return fallback;\n }\n } else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) {\n // Fallback to `false` results to `false`, when falling back to different key.\n // For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations`\n return null;\n }\n }\n return false;\n}\n\nfunction createSubResolver(\n parentScopes: AnyObject[],\n resolver: ResolverCache,\n prop: ResolverObjectKey,\n value: unknown\n) {\n const rootScopes = resolver._rootScopes;\n const fallback = resolveFallback(resolver._fallback, prop, value);\n const allScopes = [...parentScopes, ...rootScopes];\n const set = new Set<AnyObject>();\n set.add(value);\n let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);\n if (key === null) {\n return false;\n }\n if (typeof fallback !== 'undefined' && fallback !== prop) {\n key = addScopesFromKey(set, allScopes, fallback, key, value);\n if (key === null) {\n return false;\n }\n }\n return _createResolver(Array.from(set), [''], rootScopes, fallback,\n () => subGetTarget(resolver, prop as string, value));\n}\n\nfunction addScopesFromKey(\n set: Set<AnyObject>,\n allScopes: AnyObject[],\n key: ResolverObjectKey,\n fallback: ResolverObjectKey,\n item: unknown\n) {\n while (key) {\n key = addScopes(set, allScopes, key, fallback, item);\n }\n return key;\n}\n\nfunction subGetTarget(\n resolver: ResolverCache,\n prop: string,\n value: unknown\n) {\n const parent = resolver._getTarget();\n if (!(prop in parent)) {\n parent[prop] = {};\n }\n const target = parent[prop];\n if (isArray(target) && isObject(value)) {\n // For array of objects, the object is used to store updated values\n return value;\n }\n return target || {};\n}\n\nfunction _resolveWithPrefixes(\n prop: string,\n prefixes: string[],\n scopes: AnyObject[],\n proxy: ResolverProxy\n) {\n let value: unknown;\n for (const prefix of prefixes) {\n value = _resolve(readKey(prefix, prop), scopes);\n if (typeof value !== 'undefined') {\n return needsSubResolver(prop, value)\n ? createSubResolver(scopes, proxy, prop, value)\n : value;\n }\n }\n}\n\nfunction _resolve(key: string, scopes: AnyObject[]) {\n for (const scope of scopes) {\n if (!scope) {\n continue;\n }\n const value = scope[key];\n if (typeof value !== 'undefined') {\n return value;\n }\n }\n}\n\nfunction getKeysFromAllScopes(target: ResolverCache) {\n let keys = target._keys;\n if (!keys) {\n keys = target._keys = resolveKeysFromAllScopes(target._scopes);\n }\n return keys;\n}\n\nfunction resolveKeysFromAllScopes(scopes: AnyObject[]) {\n const set = new Set<string>();\n for (const scope of scopes) {\n for (const key of Object.keys(scope).filter(k => !k.startsWith('_'))) {\n set.add(key);\n }\n }\n return Array.from(set);\n}\n\nexport function _parseObjectDataRadialScale(\n meta: ChartMeta<'line' | 'scatter'>,\n data: AnyObject[],\n start: number,\n count: number\n) {\n const {iScale} = meta;\n const {key = 'r'} = this._parsing;\n const parsed = new Array<{r: unknown}>(count);\n let i: number, ilen: number, index: number, item: AnyObject;\n\n for (i = 0, ilen = count; i < ilen; ++i) {\n index = i + start;\n item = data[index];\n parsed[i] = {\n r: iScale.parse(resolveObjectKey(item, key), index)\n };\n }\n return parsed;\n}\n","import {almostEquals, distanceBetweenPoints, sign} from './helpers.math.js';\nimport {_isPointInArea} from './helpers.canvas.js';\nimport type {ChartArea} from '../types/index.js';\nimport type {SplinePoint} from '../types/geometric.js';\n\nconst EPSILON = Number.EPSILON || 1e-14;\n\ntype OptionalSplinePoint = SplinePoint | false\nconst getPoint = (points: SplinePoint[], i: number): OptionalSplinePoint => i < points.length && !points[i].skip && points[i];\nconst getValueAxis = (indexAxis: 'x' | 'y') => indexAxis === 'x' ? 'y' : 'x';\n\nexport function splineCurve(\n firstPoint: SplinePoint,\n middlePoint: SplinePoint,\n afterPoint: SplinePoint,\n t: number\n): {\n previous: SplinePoint\n next: SplinePoint\n } {\n // Props to Rob Spencer at scaled innovation for his post on splining between points\n // http://scaledinnovation.com/analytics/splines/aboutSplines.html\n\n // This function must also respect \"skipped\" points\n\n const previous = firstPoint.skip ? middlePoint : firstPoint;\n const current = middlePoint;\n const next = afterPoint.skip ? middlePoint : afterPoint;\n const d01 = distanceBetweenPoints(current, previous);\n const d12 = distanceBetweenPoints(next, current);\n\n let s01 = d01 / (d01 + d12);\n let s12 = d12 / (d01 + d12);\n\n // If all points are the same, s01 & s02 will be inf\n s01 = isNaN(s01) ? 0 : s01;\n s12 = isNaN(s12) ? 0 : s12;\n\n const fa = t * s01; // scaling factor for triangle Ta\n const fb = t * s12;\n\n return {\n previous: {\n x: current.x - fa * (next.x - previous.x),\n y: current.y - fa * (next.y - previous.y)\n },\n next: {\n x: current.x + fb * (next.x - previous.x),\n y: current.y + fb * (next.y - previous.y)\n }\n };\n}\n\n/**\n * Adjust tangents to ensure monotonic properties\n */\nfunction monotoneAdjust(points: SplinePoint[], deltaK: number[], mK: number[]) {\n const pointsLen = points.length;\n\n let alphaK: number, betaK: number, tauK: number, squaredMagnitude: number, pointCurrent: OptionalSplinePoint;\n let pointAfter = getPoint(points, 0);\n for (let i = 0; i < pointsLen - 1; ++i) {\n pointCurrent = pointAfter;\n pointAfter = getPoint(points, i + 1);\n if (!pointCurrent || !pointAfter) {\n continue;\n }\n\n if (almostEquals(deltaK[i], 0, EPSILON)) {\n mK[i] = mK[i + 1] = 0;\n continue;\n }\n\n alphaK = mK[i] / deltaK[i];\n betaK = mK[i + 1] / deltaK[i];\n squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);\n if (squaredMagnitude <= 9) {\n continue;\n }\n\n tauK = 3 / Math.sqrt(squaredMagnitude);\n mK[i] = alphaK * tauK * deltaK[i];\n mK[i + 1] = betaK * tauK * deltaK[i];\n }\n}\n\nfunction monotoneCompute(points: SplinePoint[], mK: number[], indexAxis: 'x' | 'y' = 'x') {\n const valueAxis = getValueAxis(indexAxis);\n const pointsLen = points.length;\n let delta: number, pointBefore: OptionalSplinePoint, pointCurrent: OptionalSplinePoint;\n let pointAfter = getPoint(points, 0);\n\n for (let i = 0; i < pointsLen; ++i) {\n pointBefore = pointCurrent;\n pointCurrent = pointAfter;\n pointAfter = getPoint(points, i + 1);\n if (!pointCurrent) {\n continue;\n }\n\n const iPixel = pointCurrent[indexAxis];\n const vPixel = pointCurrent[valueAxis];\n if (pointBefore) {\n delta = (iPixel - pointBefore[indexAxis]) / 3;\n pointCurrent[`cp1${indexAxis}`] = iPixel - delta;\n pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];\n }\n if (pointAfter) {\n delta = (pointAfter[indexAxis] - iPixel) / 3;\n pointCurrent[`cp2${indexAxis}`] = iPixel + delta;\n pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];\n }\n }\n}\n\n/**\n * This function calculates Bézier control points in a similar way than |splineCurve|,\n * but preserves monotonicity of the provided data and ensures no local extremums are added\n * between the dataset discrete points due to the interpolation.\n * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation\n */\nexport function splineCurveMonotone(points: SplinePoint[], indexAxis: 'x' | 'y' = 'x') {\n const valueAxis = getValueAxis(indexAxis);\n const pointsLen = points.length;\n const deltaK: number[] = Array(pointsLen).fill(0);\n const mK: number[] = Array(pointsLen);\n\n // Calculate slopes (deltaK) and initialize tangents (mK)\n let i, pointBefore: OptionalSplinePoint, pointCurrent: OptionalSplinePoint;\n let pointAfter = getPoint(points, 0);\n\n for (i = 0; i < pointsLen; ++i) {\n pointBefore = pointCurrent;\n pointCurrent = pointAfter;\n pointAfter = getPoint(points, i + 1);\n if (!pointCurrent) {\n continue;\n }\n\n if (pointAfter) {\n const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];\n\n // In the case of two points that appear at the same x pixel, slopeDeltaX is 0\n deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;\n }\n mK[i] = !pointBefore ? deltaK[i]\n : !pointAfter ? deltaK[i - 1]\n : (sign(deltaK[i - 1]) !== sign(deltaK[i])) ? 0\n : (deltaK[i - 1] + deltaK[i]) / 2;\n }\n\n monotoneAdjust(points, deltaK, mK);\n\n monotoneCompute(points, mK, indexAxis);\n}\n\nfunction capControlPoint(pt: number, min: number, max: number) {\n return Math.max(Math.min(pt, max), min);\n}\n\nfunction capBezierPoints(points: SplinePoint[], area: ChartArea) {\n let i, ilen, point, inArea, inAreaPrev;\n let inAreaNext = _isPointInArea(points[0], area);\n for (i = 0, ilen = points.length; i < ilen; ++i) {\n inAreaPrev = inArea;\n inArea = inAreaNext;\n inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);\n if (!inArea) {\n continue;\n }\n point = points[i];\n if (inAreaPrev) {\n point.cp1x = capControlPoint(point.cp1x, area.left, area.right);\n point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);\n }\n if (inAreaNext) {\n point.cp2x = capControlPoint(point.cp2x, area.left, area.right);\n point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);\n }\n }\n}\n\n/**\n * @private\n */\nexport function _updateBezierControlPoints(\n points: SplinePoint[],\n options,\n area: ChartArea,\n loop: boolean,\n indexAxis: 'x' | 'y'\n) {\n let i: number, ilen: number, point: SplinePoint, controlPoints: ReturnType<typeof splineCurve>;\n\n // Only consider points that are drawn in case the spanGaps option is used\n if (options.spanGaps) {\n points = points.filter((pt) => !pt.skip);\n }\n\n if (options.cubicInterpolationMode === 'monotone') {\n splineCurveMonotone(points, indexAxis);\n } else {\n let prev = loop ? points[points.length - 1] : points[0];\n for (i = 0, ilen = points.length; i < ilen; ++i) {\n point = points[i];\n controlPoints = splineCurve(\n prev,\n point,\n points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen],\n options.tension\n );\n point.cp1x = controlPoints.previous.x;\n point.cp1y = controlPoints.previous.y;\n point.cp2x = controlPoints.next.x;\n point.cp2y = controlPoints.next.y;\n prev = point;\n }\n }\n\n if (options.capBezierPoints) {\n capBezierPoints(points, area);\n }\n}\n","import type {ChartArea, Scale} from '../types/index.js';\nimport type PrivateChart from '../core/core.controller.js';\nimport type {Chart, ChartEvent} from '../types.js';\nimport {INFINITY} from './helpers.math.js';\n\n/**\n * @private\n */\nexport function _isDomSupported(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * @private\n */\nexport function _getParentNode(domNode: HTMLCanvasElement): HTMLCanvasElement {\n let parent = domNode.parentNode;\n if (parent && parent.toString() === '[object ShadowRoot]') {\n parent = (parent as ShadowRoot).host;\n }\n return parent as HTMLCanvasElement;\n}\n\n/**\n * convert max-width/max-height values that may be percentages into a number\n * @private\n */\n\nfunction parseMaxStyle(styleValue: string | number, node: HTMLElement, parentProperty: string) {\n let valueInPixels: number;\n if (typeof styleValue === 'string') {\n valueInPixels = parseInt(styleValue, 10);\n\n if (styleValue.indexOf('%') !== -1) {\n // percentage * size in dimension\n valueInPixels = (valueInPixels / 100) * node.parentNode[parentProperty];\n }\n } else {\n valueInPixels = styleValue;\n }\n\n return valueInPixels;\n}\n\nconst getComputedStyle = (element: HTMLElement): CSSStyleDeclaration =>\n element.ownerDocument.defaultView.getComputedStyle(element, null);\n\nexport function getStyle(el: HTMLElement, property: string): string {\n return getComputedStyle(el).getPropertyValue(property);\n}\n\nconst positions = ['top', 'right', 'bottom', 'left'];\nfunction getPositionedStyle(styles: CSSStyleDeclaration, style: string, suffix?: string): ChartArea {\n const result = {} as ChartArea;\n suffix = suffix ? '-' + suffix : '';\n for (let i = 0; i < 4; i++) {\n const pos = positions[i];\n result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;\n }\n result.width = result.left + result.right;\n result.height = result.top + result.bottom;\n return result;\n}\n\nconst useOffsetPos = (x: number, y: number, target: HTMLElement | EventTarget) =>\n (x > 0 || y > 0) && (!target || !(target as HTMLElement).shadowRoot);\n\n/**\n * @param e\n * @param canvas\n * @returns Canvas position\n */\nfunction getCanvasPosition(\n e: Event | TouchEvent | MouseEvent,\n canvas: HTMLCanvasElement\n): {\n x: number;\n y: number;\n box: boolean;\n } {\n const touches = (e as TouchEvent).touches;\n const source = (touches && touches.length ? touches[0] : e) as MouseEvent;\n const {offsetX, offsetY} = source as MouseEvent;\n let box = false;\n let x, y;\n if (useOffsetPos(offsetX, offsetY, e.target)) {\n x = offsetX;\n y = offsetY;\n } else {\n const rect = canvas.getBoundingClientRect();\n x = source.clientX - rect.left;\n y = source.clientY - rect.top;\n box = true;\n }\n return {x, y, box};\n}\n\n/**\n * Gets an event's x, y coordinates, relative to the chart area\n * @param event\n * @param chart\n * @returns x and y coordinates of the event\n */\n\nexport function getRelativePosition(\n event: Event | ChartEvent | TouchEvent | MouseEvent,\n chart: Chart | PrivateChart\n): { x: number; y: number } {\n if ('native' in event) {\n return event;\n }\n\n const {canvas, currentDevicePixelRatio} = chart;\n const style = getComputedStyle(canvas);\n const borderBox = style.boxSizing === 'border-box';\n const paddings = getPositionedStyle(style, 'padding');\n const borders = getPositionedStyle(style, 'border', 'width');\n const {x, y, box} = getCanvasPosition(event, canvas);\n const xOffset = paddings.left + (box && borders.left);\n const yOffset = paddings.top + (box && borders.top);\n\n let {width, height} = chart;\n if (borderBox) {\n width -= paddings.width + borders.width;\n height -= paddings.height + borders.height;\n }\n return {\n x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),\n y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)\n };\n}\n\nfunction getContainerSize(canvas: HTMLCanvasElement, width: number, height: number): Partial<Scale> {\n let maxWidth: number, maxHeight: number;\n\n if (width === undefined || height === undefined) {\n const container = canvas && _getParentNode(canvas);\n if (!container) {\n width = canvas.clientWidth;\n height = canvas.clientHeight;\n } else {\n const rect = container.getBoundingClientRect(); // this is the border box of the container\n const containerStyle = getComputedStyle(container);\n const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');\n const containerPadding = getPositionedStyle(containerStyle, 'padding');\n width = rect.width - containerPadding.width - containerBorder.width;\n height = rect.height - containerPadding.height - containerBorder.height;\n maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');\n maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');\n }\n }\n return {\n width,\n height,\n maxWidth: maxWidth || INFINITY,\n maxHeight: maxHeight || INFINITY\n };\n}\n\nconst round1 = (v: number) => Math.round(v * 10) / 10;\n\n// eslint-disable-next-line complexity\nexport function getMaximumSize(\n canvas: HTMLCanvasElement,\n bbWidth?: number,\n bbHeight?: number,\n aspectRatio?: number\n): { width: number; height: number } {\n const style = getComputedStyle(canvas);\n const margins = getPositionedStyle(style, 'margin');\n const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;\n const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;\n const containerSize = getContainerSize(canvas, bbWidth, bbHeight);\n let {width, height} = containerSize;\n\n if (style.boxSizing === 'content-box') {\n const borders = getPositionedStyle(style, 'border', 'width');\n const paddings = getPositionedStyle(style, 'padding');\n width -= paddings.width + borders.width;\n height -= paddings.height + borders.height;\n }\n width = Math.max(0, width - margins.width);\n height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);\n width = round1(Math.min(width, maxWidth, containerSize.maxWidth));\n height = round1(Math.min(height, maxHeight, containerSize.maxHeight));\n if (width && !height) {\n // https://github.com/chartjs/Chart.js/issues/4659\n // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)\n height = round1(width / 2);\n }\n\n const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;\n\n if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {\n height = containerSize.height;\n width = round1(Math.floor(height * aspectRatio));\n }\n\n return {width, height};\n}\n\n/**\n * @param chart\n * @param forceRatio\n * @param forceStyle\n * @returns True if the canvas context size or transformation has changed.\n */\nexport function retinaScale(\n chart: Chart | PrivateChart,\n forceRatio: number,\n forceStyle?: boolean\n): boolean | void {\n const pixelRatio = forceRatio || 1;\n const deviceHeight = round1(chart.height * pixelRatio);\n const deviceWidth = round1(chart.width * pixelRatio);\n\n (chart as PrivateChart).height = round1(chart.height);\n (chart as PrivateChart).width = round1(chart.width);\n\n const canvas = chart.canvas;\n\n // If no style has been set on the canvas, the render size is used as display size,\n // making the chart visually bigger, so let's enforce it to the \"correct\" values.\n // See https://github.com/chartjs/Chart.js/issues/3575\n if (canvas.style && (forceStyle || (!canvas.style.height && !canvas.style.width))) {\n canvas.style.height = `${chart.height}px`;\n canvas.style.width = `${chart.width}px`;\n }\n\n if (chart.currentDevicePixelRatio !== pixelRatio\n || canvas.height !== deviceHeight\n || canvas.width !== deviceWidth) {\n (chart as PrivateChart).currentDevicePixelRatio = pixelRatio;\n canvas.height = deviceHeight;\n canvas.width = deviceWidth;\n chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);\n return true;\n }\n return false;\n}\n\n/**\n * Detects support for options object argument in addEventListener.\n * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n * @private\n */\nexport const supportsEventListenerOptions = (function() {\n let passiveSupported = false;\n try {\n const options = {\n get passive() { // This function will be called when the browser attempts to access the passive property.\n passiveSupported = true;\n return false;\n }\n } as EventListenerOptions;\n\n if (_isDomSupported()) {\n window.addEventListener('test', null, options);\n window.removeEventListener('test', null, options);\n }\n } catch (e) {\n // continue regardless of error\n }\n return passiveSupported;\n}());\n\n/**\n * The \"used\" size is the final value of a dimension property after all calculations have\n * been performed. This method uses the computed style of `element` but returns undefined\n * if the computed style is not expressed in pixels. That can happen in some cases where\n * `element` has a size relative to its parent and this last one is not yet displayed,\n * for example because of `display: none` on a parent node.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n * @returns Size in pixels or undefined if unknown.\n */\n\nexport function readUsedSize(\n element: HTMLElement,\n property: 'width' | 'height'\n): number | undefined {\n const value = getStyle(element, property);\n const matches = value && value.match(/^(\\d+)(\\.\\d+)?px$/);\n return matches ? +matches[1] : undefined;\n}\n","import type {Point, SplinePoint} from '../types/geometric.js';\n\n/**\n * @private\n */\nexport function _pointInLine(p1: Point, p2: Point, t: number, mode?) { // eslint-disable-line @typescript-eslint/no-unused-vars\n return {\n x: p1.x + t * (p2.x - p1.x),\n y: p1.y + t * (p2.y - p1.y)\n };\n}\n\n/**\n * @private\n */\nexport function _steppedInterpolation(\n p1: Point,\n p2: Point,\n t: number, mode: 'middle' | 'after' | unknown\n) {\n return {\n x: p1.x + t * (p2.x - p1.x),\n y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y\n : mode === 'after' ? t < 1 ? p1.y : p2.y\n : t > 0 ? p2.y : p1.y\n };\n}\n\n/**\n * @private\n */\nexport function _bezierInterpolation(p1: SplinePoint, p2: SplinePoint, t: number, mode?) { // eslint-disable-line @typescript-eslint/no-unused-vars\n const cp1 = {x: p1.cp2x, y: p1.cp2y};\n const cp2 = {x: p2.cp1x, y: p2.cp1y};\n const a = _pointInLine(p1, cp1, t);\n const b = _pointInLine(cp1, cp2, t);\n const c = _pointInLine(cp2, p2, t);\n const d = _pointInLine(a, b, t);\n const e = _pointInLine(b, c, t);\n return _pointInLine(d, e, t);\n}\n","export interface RTLAdapter {\n x(x: number): number;\n setWidth(w: number): void;\n textAlign(align: 'center' | 'left' | 'right'): 'center' | 'left' | 'right';\n xPlus(x: number, value: number): number;\n leftForLtr(x: number, itemWidth: number): number;\n}\n\nconst getRightToLeftAdapter = function(rectX: number, width: number): RTLAdapter {\n return {\n x(x) {\n return rectX + rectX + width - x;\n },\n setWidth(w) {\n width = w;\n },\n textAlign(align) {\n if (align === 'center') {\n return align;\n }\n return align === 'right' ? 'left' : 'right';\n },\n xPlus(x, value) {\n return x - value;\n },\n leftForLtr(x, itemWidth) {\n return x - itemWidth;\n },\n };\n};\n\nconst getLeftToRightAdapter = function(): RTLAdapter {\n return {\n x(x) {\n return x;\n },\n setWidth(w) { // eslint-disable-line no-unused-vars\n },\n textAlign(align) {\n return align;\n },\n xPlus(x, value) {\n return x + value;\n },\n leftForLtr(x, _itemWidth) { // eslint-disable-line @typescript-eslint/no-unused-vars\n return x;\n },\n };\n};\n\nexport function getRtlAdapter(rtl: boolean, rectX: number, width: number) {\n return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();\n}\n\nexport function overrideTextDirection(ctx: CanvasRenderingContext2D, direction: 'ltr' | 'rtl') {\n let style: CSSStyleDeclaration, original: [string, string];\n if (direction === 'ltr' || direction === 'rtl') {\n style = ctx.canvas.style;\n original = [\n style.getPropertyValue('direction'),\n style.getPropertyPriority('direction'),\n ];\n\n style.setProperty('direction', direction, 'important');\n (ctx as { prevTextDirection?: [string, string] }).prevTextDirection = original;\n }\n}\n\nexport function restoreTextDirection(ctx: CanvasRenderingContext2D, original?: [string, string]) {\n if (original !== undefined) {\n delete (ctx as { prevTextDirection?: [string, string] }).prevTextDirection;\n ctx.canvas.style.setProperty('direction', original[0], original[1]);\n }\n}\n","import {_angleBetween, _angleDiff, _isBetween, _normalizeAngle} from './helpers.math.js';\nimport {createContext} from './helpers.options.js';\nimport {isPatternOrGradient} from './helpers.color.js';\n\n/**\n * @typedef { import('../elements/element.line.js').default } LineElement\n * @typedef { import('../elements/element.point.js').default } PointElement\n * @typedef {{start: number, end: number, loop: boolean, style?: any}} Segment\n */\n\nfunction propertyFn(property) {\n if (property === 'angle') {\n return {\n between: _angleBetween,\n compare: _angleDiff,\n normalize: _normalizeAngle,\n };\n }\n return {\n between: _isBetween,\n compare: (a, b) => a - b,\n normalize: x => x\n };\n}\n\nfunction normalizeSegment({start, end, count, loop, style}) {\n return {\n start: start % count,\n end: end % count,\n loop: loop && (end - start + 1) % count === 0,\n style\n };\n}\n\nfunction getSegment(segment, points, bounds) {\n const {property, start: startBound, end: endBound} = bounds;\n const {between, normalize} = propertyFn(property);\n const count = points.length;\n // eslint-disable-next-line prefer-const\n let {start, end, loop} = segment;\n let i, ilen;\n\n if (loop) {\n start += count;\n end += count;\n for (i = 0, ilen = count; i < ilen; ++i) {\n if (!between(normalize(points[start % count][property]), startBound, endBound)) {\n break;\n }\n start--;\n end--;\n }\n start %= count;\n end %= count;\n }\n\n if (end < start) {\n end += count;\n }\n return {start, end, loop, style: segment.style};\n}\n\n/**\n * Returns the sub-segment(s) of a line segment that fall in the given bounds\n * @param {object} segment\n * @param {number} segment.start - start index of the segment, referring the points array\n * @param {number} segment.end - end index of the segment, referring the points array\n * @param {boolean} segment.loop - indicates that the segment is a loop\n * @param {object} [segment.style] - segment style\n * @param {PointElement[]} points - the points that this segment refers to\n * @param {object} [bounds]\n * @param {string} bounds.property - the property of a `PointElement` we are bounding. `x`, `y` or `angle`.\n * @param {number} bounds.start - start value of the property\n * @param {number} bounds.end - end value of the property\n * @private\n **/\nexport function _boundSegment(segment, points, bounds) {\n if (!bounds) {\n return [segment];\n }\n\n const {property, start: startBound, end: endBound} = bounds;\n const count = points.length;\n const {compare, between, normalize} = propertyFn(property);\n const {start, end, loop, style} = getSegment(segment, points, bounds);\n\n const result = [];\n let inside = false;\n let subStart = null;\n let value, point, prevValue;\n\n const startIsBefore = () => between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;\n const endIsBefore = () => compare(endBound, value) === 0 || between(endBound, prevValue, value);\n const shouldStart = () => inside || startIsBefore();\n const shouldStop = () => !inside || endIsBefore();\n\n for (let i = start, prev = start; i <= end; ++i) {\n point = points[i % count];\n\n if (point.skip) {\n continue;\n }\n\n value = normalize(point[property]);\n\n if (value === prevValue) {\n continue;\n }\n\n inside = between(value, startBound, endBound);\n\n if (subStart === null && shouldStart()) {\n subStart = compare(value, startBound) === 0 ? i : prev;\n }\n\n if (subStart !== null && shouldStop()) {\n result.push(normalizeSegment({start: subStart, end: i, loop, count, style}));\n subStart = null;\n }\n prev = i;\n prevValue = value;\n }\n\n if (subStart !== null) {\n result.push(normalizeSegment({start: subStart, end, loop, count, style}));\n }\n\n return result;\n}\n\n\n/**\n * Returns the segments of the line that are inside given bounds\n * @param {LineElement} line\n * @param {object} [bounds]\n * @param {string} bounds.property - the property we are bounding with. `x`, `y` or `angle`.\n * @param {number} bounds.start - start value of the `property`\n * @param {number} bounds.end - end value of the `property`\n * @private\n */\nexport function _boundSegments(line, bounds) {\n const result = [];\n const segments = line.segments;\n\n for (let i = 0; i < segments.length; i++) {\n const sub = _boundSegment(segments[i], line.points, bounds);\n if (sub.length) {\n result.push(...sub);\n }\n }\n return result;\n}\n\n/**\n * Find start and end index of a line.\n */\nfunction findStartAndEnd(points, count, loop, spanGaps) {\n let start = 0;\n let end = count - 1;\n\n if (loop && !spanGaps) {\n // loop and not spanning gaps, first find a gap to start from\n while (start < count && !points[start].skip) {\n start++;\n }\n }\n\n // find first non skipped point (after the first gap possibly)\n while (start < count && points[start].skip) {\n start++;\n }\n\n // if we looped to count, start needs to be 0\n start %= count;\n\n if (loop) {\n // loop will go past count, if start > 0\n end += start;\n }\n\n while (end > start && points[end % count].skip) {\n end--;\n }\n\n // end could be more than count, normalize\n end %= count;\n\n return {start, end};\n}\n\n/**\n * Compute solid segments from Points, when spanGaps === false\n * @param {PointElement[]} points - the points\n * @param {number} start - start index\n * @param {number} max - max index (can go past count on a loop)\n * @param {boolean} loop - boolean indicating that this would be a loop if no gaps are found\n */\nfunction solidSegments(points, start, max, loop) {\n const count = points.length;\n const result = [];\n let last = start;\n let prev = points[start];\n let end;\n\n for (end = start + 1; end <= max; ++end) {\n const cur = points[end % count];\n if (cur.skip || cur.stop) {\n if (!prev.skip) {\n loop = false;\n result.push({start: start % count, end: (end - 1) % count, loop});\n // @ts-ignore\n start = last = cur.stop ? end : null;\n }\n } else {\n last = end;\n if (prev.skip) {\n start = end;\n }\n }\n prev = cur;\n }\n\n if (last !== null) {\n result.push({start: start % count, end: last % count, loop});\n }\n\n return result;\n}\n\n/**\n * Compute the continuous segments that define the whole line\n * There can be skipped points within a segment, if spanGaps is true.\n * @param {LineElement} line\n * @param {object} [segmentOptions]\n * @return {Segment[]}\n * @private\n */\nexport function _computeSegments(line, segmentOptions) {\n const points = line.points;\n const spanGaps = line.options.spanGaps;\n const count = points.length;\n\n if (!count) {\n return [];\n }\n\n const loop = !!line._loop;\n const {start, end} = findStartAndEnd(points, count, loop, spanGaps);\n\n if (spanGaps === true) {\n return splitByStyles(line, [{start, end, loop}], points, segmentOptions);\n }\n\n const max = end < start ? end + count : end;\n const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;\n return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);\n}\n\n/**\n * @param {Segment[]} segments\n * @param {PointElement[]} points\n * @param {object} [segmentOptions]\n * @return {Segment[]}\n */\nfunction splitByStyles(line, segments, points, segmentOptions) {\n if (!segmentOptions || !segmentOptions.setContext || !points) {\n return segments;\n }\n return doSplitByStyles(line, segments, points, segmentOptions);\n}\n\n/**\n * @param {LineElement} line\n * @param {Segment[]} segments\n * @param {PointElement[]} points\n * @param {object} [segmentOptions]\n * @return {Segment[]}\n */\nfunction doSplitByStyles(line, segments, points, segmentOptions) {\n const chartContext = line._chart.getContext();\n const baseStyle = readStyle(line.options);\n const {_datasetIndex: datasetIndex, options: {spanGaps}} = line;\n const count = points.length;\n const result = [];\n let prevStyle = baseStyle;\n let start = segments[0].start;\n let i = start;\n\n function addStyle(s, e, l, st) {\n const dir = spanGaps ? -1 : 1;\n if (s === e) {\n return;\n }\n // Style can not start/end on a skipped point, adjust indices accordingly\n s += count;\n while (points[s % count].skip) {\n s -= dir;\n }\n while (points[e % count].skip) {\n e += dir;\n }\n if (s % count !== e % count) {\n result.push({start: s % count, end: e % count, loop: l, style: st});\n prevStyle = st;\n start = e % count;\n }\n }\n\n for (const segment of segments) {\n start = spanGaps ? start : segment.start;\n let prev = points[start % count];\n let style;\n for (i = start + 1; i <= segment.end; i++) {\n const pt = points[i % count];\n style = readStyle(segmentOptions.setContext(createContext(chartContext, {\n type: 'segment',\n p0: prev,\n p1: pt,\n p0DataIndex: (i - 1) % count,\n p1DataIndex: i % count,\n datasetIndex\n })));\n if (styleChanged(style, prevStyle)) {\n addStyle(start, i - 1, segment.loop, prevStyle);\n }\n prev = pt;\n prevStyle = style;\n }\n if (start < i - 1) {\n addStyle(start, i - 1, segment.loop, prevStyle);\n }\n }\n\n return result;\n}\n\nfunction readStyle(options) {\n return {\n backgroundColor: options.backgroundColor,\n borderCapStyle: options.borderCapStyle,\n borderDash: options.borderDash,\n borderDashOffset: options.borderDashOffset,\n borderJoinStyle: options.borderJoinStyle,\n borderWidth: options.borderWidth,\n borderColor: options.borderColor\n };\n}\n\nfunction styleChanged(style, prevStyle) {\n if (!prevStyle) {\n return false;\n }\n const cache = [];\n const replacer = function(key, value) {\n if (!isPatternOrGradient(value)) {\n return value;\n }\n if (!cache.includes(value)) {\n cache.push(value);\n }\n return cache.indexOf(value);\n };\n return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);\n}\n","import type {Chart, ChartArea, ChartMeta, Scale, TRBL} from '../types/index.js';\n\nfunction getSizeForArea(scale: Scale, chartArea: ChartArea, field: keyof ChartArea) {\n return scale.options.clip ? scale[field] : chartArea[field];\n}\n\nfunction getDatasetArea(meta: ChartMeta, chartArea: ChartArea): TRBL {\n const {xScale, yScale} = meta;\n if (xScale && yScale) {\n return {\n left: getSizeForArea(xScale, chartArea, 'left'),\n right: getSizeForArea(xScale, chartArea, 'right'),\n top: getSizeForArea(yScale, chartArea, 'top'),\n bottom: getSizeForArea(yScale, chartArea, 'bottom')\n };\n }\n return chartArea;\n}\n\nexport function getDatasetClipArea(chart: Chart, meta: ChartMeta): TRBL | false {\n const clip = meta._clip;\n if (clip.disabled) {\n return false;\n }\n const area = getDatasetArea(meta, chart.chartArea);\n\n return {\n left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left),\n right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right),\n top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top),\n bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom)\n };\n}\n"],"names":["noop","uid","id","isNullOrUndef","value","undefined","isArray","Array","type","Object","prototype","toString","call","slice","isObject","isNumberFinite","Number","isFinite","finiteOrDefault","defaultValue","valueOrDefault","toPercentage","dimension","endsWith","parseFloat","toDimension","callback","fn","args","thisArg","apply","each","loopable","reverse","i","len","keys","length","_elementsEqual","a0","a1","ilen","v0","v1","datasetIndex","index","clone","source","map","target","create","klen","k","isValidKey","key","indexOf","_merger","options","tval","sval","merge","sources","merger","current","mergeIf","_mergerIf","hasOwnProperty","_deprecated","scope","previous","console","warn","keyResolvers","v","x","o","y","_splitKey","parts","split","tmp","part","push","_getKeyResolver","obj","resolveObjectKey","resolver","_capitalize","str","charAt","toUpperCase","defined","isFunction","setsEqual","a","b","size","item","has","_isClickEvent","e","PI","Math","TAU","PITAU","INFINITY","POSITIVE_INFINITY","RAD_PER_DEG","HALF_PI","QUARTER_PI","TWO_THIRDS_PI","log10","sign","almostEquals","epsilon","abs","niceNum","range","roundedRange","round","niceRange","pow","floor","fraction","niceFraction","_factorize","result","sqrt","sort","pop","isNonPrimitive","n","Symbol","toPrimitive","isNumber","isNaN","almostWhole","rounded","_setMinAndMaxByKey","array","property","min","max","toRadians","degrees","toDegrees","radians","_decimalPlaces","isFiniteNumber","p","getAngleFromPoint","centrePoint","anglePoint","distanceFromXCenter","distanceFromYCenter","radialDistanceFromCenter","angle","atan2","distance","distanceBetweenPoints","pt1","pt2","_angleDiff","_normalizeAngle","_angleBetween","start","end","sameAngleIsFullCircle","s","angleToStart","angleToEnd","startToAngle","endToAngle","_limitValue","_int16Range","_isBetween","_lookup","table","cmp","hi","lo","mid","_lookupByKey","last","ti","_rlookupByKey","_filterBetween","values","arrayEvents","listenArrayEvents","listener","_chartjs","listeners","defineProperty","configurable","enumerable","forEach","method","base","res","object","unlistenArrayEvents","stub","splice","_arrayUnique","items","set","Set","from","fontString","pixelSize","fontStyle","fontFamily","requestAnimFrame","window","requestAnimationFrame","throttled","argsToUse","ticking","debounce","delay","timeout","clearTimeout","setTimeout","_toLeftRightCenter","align","_alignStartEnd","_textX","left","right","rtl","check","_getStartAndCountOfVisiblePoints","meta","points","animationsDisabled","pointCount","count","_sorted","iScale","vScale","_parsed","spanGaps","dataset","axis","minDefined","maxDefined","getUserBounds","getPixelForValue","distanceToDefinedLo","findIndex","point","distanceToDefinedHi","_scaleRangesChanged","xScale","yScale","_scaleRanges","newRanges","xmin","xmax","ymin","ymax","changed","assign","atEdge","t","elasticIn","sin","elasticOut","effects","linear","easeInQuad","easeOutQuad","easeInOutQuad","easeInCubic","easeOutCubic","easeInOutCubic","easeInQuart","easeOutQuart","easeInOutQuart","easeInQuint","easeOutQuint","easeInOutQuint","easeInSine","cos","easeOutSine","easeInOutSine","easeInExpo","easeOutExpo","easeInOutExpo","easeInCirc","easeOutCirc","easeInOutCirc","easeInElastic","easeOutElastic","easeInOutElastic","easeInBack","easeOutBack","easeInOutBack","easeInBounce","easeOutBounce","m","d","easeInOutBounce","isPatternOrGradient","color","Color","getHoverColor","saturate","darken","hexString","numbers","colors","applyAnimationsDefaults","defaults","duration","easing","loop","to","describe","_fallback","_indexable","_scriptable","name","properties","active","animation","resize","show","animations","visible","hide","applyLayoutsDefaults","autoPadding","padding","top","bottom","intlCache","Map","getNumberFormat","locale","cacheKey","JSON","stringify","formatter","get","Intl","NumberFormat","formatNumber","num","format","formatters","numeric","tickValue","ticks","chart","notation","delta","maxTick","calculateDelta","logDelta","numDecimal","minimumFractionDigits","maximumFractionDigits","logarithmic","remain","significand","includes","applyScaleDefaults","display","offset","beginAtZero","bounds","clip","grace","grid","lineWidth","drawOnChartArea","drawTicks","tickLength","tickWidth","_ctx","tickColor","border","dash","dashOffset","width","title","text","minRotation","maxRotation","mirror","textStrokeWidth","textStrokeColor","autoSkip","autoSkipPadding","labelOffset","Ticks","minor","major","crossAlign","showLabelBackdrop","backdropColor","backdropPadding","route","startsWith","overrides","descriptors","getScope","node","root","Defaults","constructor","_descriptors","_appliers","backgroundColor","borderColor","datasets","devicePixelRatio","context","platform","getDevicePixelRatio","elements","events","font","family","style","lineHeight","weight","hover","hoverBackgroundColor","ctx","hoverBorderColor","hoverColor","indexAxis","interaction","mode","intersect","includeInvisible","maintainAspectRatio","onHover","onClick","parsing","plugins","responsive","scale","scales","showLine","drawActiveElementsOnTop","override","targetScope","targetName","scopeObject","targetScopeObject","privateName","defineProperties","writable","local","appliers","toFontString","_measureText","data","gc","longest","string","textWidth","measureText","_longestText","arrayOfThings","cache","garbageCollect","save","j","jlen","thing","nestedThing","restore","gcLen","_alignPixel","pixel","currentDevicePixelRatio","halfWidth","clearCanvas","canvas","getContext","resetTransform","clearRect","height","drawPoint","drawPointLegend","w","xOffset","yOffset","cornerRadius","xOffsetW","yOffsetW","pointStyle","rotation","radius","rad","translate","rotate","drawImage","beginPath","ellipse","arc","closePath","moveTo","lineTo","SQRT1_2","rect","fill","borderWidth","stroke","_isPointInArea","area","margin","clipArea","unclipArea","_steppedLineTo","flip","midpoint","_bezierCurveTo","bezierCurveTo","cp1x","cp2x","cp1y","cp2y","setRenderOpts","opts","translation","fillStyle","textAlign","textBaseline","decorateText","line","strikethrough","underline","metrics","actualBoundingBoxLeft","actualBoundingBoxRight","actualBoundingBoxAscent","actualBoundingBoxDescent","yDecoration","strokeStyle","decorationWidth","drawBackdrop","oldColor","fillRect","renderText","lines","strokeWidth","strokeColor","backdrop","strokeText","maxWidth","fillText","addRoundedRectPath","h","topLeft","bottomLeft","bottomRight","topRight","LINE_HEIGHT","FONT_STYLE","toLineHeight","matches","match","numberOrZero","_readValueToProps","props","ret","objProps","read","prop","toTRBL","toTRBLCorners","toPadding","toFont","fallback","parseInt","resolve","inputs","info","cacheable","_addGrace","minmax","change","keepZero","add","createContext","parentContext","_createResolver","scopes","prefixes","rootScopes","getTarget","finalRootScopes","_resolve","toStringTag","_cacheable","_scopes","_rootScopes","_getTarget","Proxy","deleteProperty","_keys","_cached","_resolveWithPrefixes","getOwnPropertyDescriptor","Reflect","getPrototypeOf","getKeysFromAllScopes","ownKeys","storage","_storage","_attachContext","proxy","subProxy","descriptorDefaults","_proxy","_context","_subProxy","_stack","setContext","receiver","_resolveWithContext","allKeys","scriptable","indexable","_allKeys","isScriptable","isIndexable","readKey","prefix","needsSubResolver","_resolveScriptable","_resolveArray","getValue","Error","join","delete","createSubResolver","arr","filter","resolveFallback","parent","addScopes","parentScopes","parentFallback","allScopes","addScopesFromKey","subGetTarget","resolveKeysFromAllScopes","_parseObjectDataRadialScale","_parsing","parsed","r","parse","EPSILON","getPoint","skip","getValueAxis","splineCurve","firstPoint","middlePoint","afterPoint","next","d01","d12","s01","s12","fa","fb","monotoneAdjust","deltaK","mK","pointsLen","alphaK","betaK","tauK","squaredMagnitude","pointCurrent","pointAfter","monotoneCompute","valueAxis","pointBefore","iPixel","vPixel","splineCurveMonotone","slopeDelta","capControlPoint","pt","capBezierPoints","inArea","inAreaPrev","inAreaNext","_updateBezierControlPoints","controlPoints","cubicInterpolationMode","prev","tension","_isDomSupported","document","_getParentNode","domNode","parentNode","host","parseMaxStyle","styleValue","parentProperty","valueInPixels","getComputedStyle","element","ownerDocument","defaultView","getStyle","el","getPropertyValue","positions","getPositionedStyle","styles","suffix","pos","useOffsetPos","shadowRoot","getCanvasPosition","touches","offsetX","offsetY","box","getBoundingClientRect","clientX","clientY","getRelativePosition","event","borderBox","boxSizing","paddings","borders","getContainerSize","maxHeight","container","clientWidth","clientHeight","containerStyle","containerBorder","containerPadding","round1","getMaximumSize","bbWidth","bbHeight","aspectRatio","margins","containerSize","maintainHeight","retinaScale","forceRatio","forceStyle","pixelRatio","deviceHeight","deviceWidth","setTransform","supportsEventListenerOptions","passiveSupported","passive","addEventListener","removeEventListener","readUsedSize","_pointInLine","p1","p2","_steppedInterpolation","_bezierInterpolation","cp1","cp2","c","getRightToLeftAdapter","rectX","setWidth","xPlus","leftForLtr","itemWidth","getLeftToRightAdapter","_itemWidth","getRtlAdapter","overrideTextDirection","direction","original","getPropertyPriority","setProperty","prevTextDirection","restoreTextDirection","propertyFn","between","compare","normalize","normalizeSegment","getSegment","segment","startBound","endBound","_boundSegment","inside","subStart","prevValue","startIsBefore","endIsBefore","shouldStart","shouldStop","_boundSegments","segments","sub","findStartAndEnd","solidSegments","cur","stop","_computeSegments","segmentOptions","_loop","splitByStyles","completeLoop","_fullLoop","doSplitByStyles","chartContext","_chart","baseStyle","readStyle","_datasetIndex","prevStyle","addStyle","l","st","dir","p0","p0DataIndex","p1DataIndex","styleChanged","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","replacer","getSizeForArea","chartArea","field","getDatasetArea","getDatasetClipArea","_clip","disabled"],"mappings":"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"} \ No newline at end of file diff --git a/go/cmd/kjol-web/frontend/vendor/chart.js/package.json b/go/cmd/kjol-web/frontend/vendor/chart.js/package.json new file mode 100644 index 00000000..085ebf00 --- /dev/null +++ b/go/cmd/kjol-web/frontend/vendor/chart.js/package.json @@ -0,0 +1,139 @@ +{ + "name": "chart.js", + "homepage": "https://www.chartjs.org", + "description": "Simple HTML5 charts using the canvas element.", + "version": "4.5.1", + "license": "MIT", + "type": "module", + "sideEffects": [ + "./auto/auto.js", + "./auto/auto.cjs", + "./dist/chart.umd.min.js", + "./dist/chart.umd.js" + ], + "jsdelivr": "./dist/chart.umd.min.js", + "unpkg": "./dist/chart.umd.min.js", + "main": "./dist/chart.cjs", + "module": "./dist/chart.js", + "exports": { + ".": { + "types": "./dist/types.d.ts", + "import": "./dist/chart.js", + "require": "./dist/chart.cjs" + }, + "./auto": { + "types": "./auto/auto.d.ts", + "import": "./auto/auto.js", + "require": "./auto/auto.cjs" + }, + "./helpers": { + "types": "./helpers/helpers.d.ts", + "import": "./helpers/helpers.js", + "require": "./helpers/helpers.cjs" + } + }, + "types": "./dist/types.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/**", + "dist/**", + "!dist/docs/**", + "helpers/**" + ], + "scripts": { + "autobuild": "rollup -c -w", + "copyDeclarations": "node -e \"fs.cpSync('./src/types/', './dist/types/', {recursive:true})\"", + "emitDeclarations": "tsc --emitDeclarationOnly && pnpm copyDeclarations", + "build": "rollup -c && pnpm emitDeclarations", + "dev": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers chrome --grep", + "dev:ff": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers firefox --grep", + "docs": "pnpm run build && pnpm --filter \"./docs/**\" build", + "docs:dev": "pnpm run build && pnpm --filter \"./docs/**\" dev", + "lint-js": "eslint \"src/**/*.{js,ts}\" \"test/**/*.js\" \"docs/**/*.js\" --cache", + "lint-md": "eslint \"**/*.md\" --cache", + "lint-types": "pnpm build && node test/types/autogen.js && tsc -p test/types", + "lint": "concurrently \"pnpm:lint-*\"", + "test": "pnpm lint && pnpm test-ci", + "test-ci": "concurrently \"pnpm:test-ci-*\"", + "test-ci-karma": "cross-env NODE_ENV=test karma start ./karma.conf.cjs --auto-watch --single-run --coverage --grep", + "test-ci-integration": "pnpm --filter \"./test/integration/**\" test" + }, + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^23.0.2", + "@rollup/plugin-inject": "^5.0.2", + "@rollup/plugin-json": "^5.0.1", + "@rollup/plugin-node-resolve": "^15.0.1", + "@swc/core": "^1.3.18", + "@types/estree": "^1.0.0", + "@types/offscreencanvas": "^2019.7.0", + "@typescript-eslint/eslint-plugin": "^5.32.0", + "@typescript-eslint/parser": "^5.32.0", + "chartjs-adapter-luxon": "^1.2.0", + "chartjs-adapter-moment": "^1.0.0", + "chartjs-test-utils": "^0.4.0", + "concurrently": "^7.3.0", + "coveralls": "^3.1.1", + "cross-env": "^7.0.3", + "eslint": "^8.21.0", + "eslint-config-chartjs": "^0.3.0", + "eslint-plugin-es": "^4.1.0", + "eslint-plugin-html": "^7.1.0", + "eslint-plugin-markdown": "^3.0.0", + "esm": "^3.2.25", + "glob": "^8.0.3", + "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": "^3.0.1", + "moment": "^2.29.4", + "moment-timezone": "^0.5.34", + "pixelmatch": "^5.3.0", + "rollup": "^3.3.0", + "rollup-plugin-cleanup": "^3.2.1", + "rollup-plugin-istanbul": "^4.0.0", + "rollup-plugin-swc3": "^0.7.0", + "rollup-plugin-terser": "^7.0.2", + "typescript": "^4.7.4", + "yargs": "^17.5.1" + }, + "engines": { + "pnpm": ">=8" + }, + "packageManager": "pnpm@8.13.0", + "pnpm": { + "overrides": { + "html-entities": "1.4.0" + }, + "peerDependencyRules": { + "ignoreMissing": [ + "chart.js" + ] + } + } +} diff --git a/go/cmd/kjol-web/frontend/vendor/vendor.json b/go/cmd/kjol-web/frontend/vendor/vendor.json index e70f842e..e9188b80 100644 --- a/go/cmd/kjol-web/frontend/vendor/vendor.json +++ b/go/cmd/kjol-web/frontend/vendor/vendor.json @@ -5,8 +5,12 @@ "//3": "Only the files the bundler actually pins are vendored, not the whole npm packages — 3.5 MB rather than 62 MB of type definitions, CJS builds and documentation. If a subpath import is ever added that reaches outside dist/ or build/, this is the first place it will fail.", + "//4": "chart.js (and its @kurkle/color dependency) are here for the same reason: @ui/Chart imports chart.js at the top level and registers every controller on module load. Its wrapper is h-full with maintainAspectRatio:false, so the PARENT must have an explicit height or the canvas collapses to nothing.", + "entrypoints": { "pdf-lib": "pdf-lib/dist/pdf-lib.esm.js", - "pdfjs-dist": "pdfjs-dist/build/pdf.mjs" + "pdfjs-dist": "pdfjs-dist/build/pdf.mjs", + "chart.js": "chart.js/dist/chart.js", + "@kurkle/color": "@kurkle/color/dist/color.esm.js" } } diff --git a/go/cmd/kjol-web/grep.exe.stackdump b/go/cmd/kjol-web/grep.exe.stackdump new file mode 100644 index 00000000..8af53514 --- /dev/null +++ b/go/cmd/kjol-web/grep.exe.stackdump @@ -0,0 +1,31 @@ +Stack trace: +Frame Function Args +0007FFFFBD80 000210060304 (0007FFFFBF88, 0007FFFFCE00, 000000000002, 0007FFFFDC10) msys-2.0.dll+0x20304 +FFFFFFFFFFFEFEDF 00021006237D (0007FFFFC730, 000000000000, 00000000017C, 000000000000) msys-2.0.dll+0x2237D +0007FFFFC490 0002100C1394 (0007FFFFC6F0, 000000000001, 0007FFFFC700, 000000000000) msys-2.0.dll+0x81394 +000000000006 0002100BCE39 (000210221CCD, 000000000000, 000000000000, 7FF9520C00E8) msys-2.0.dll+0x7CE39 +0007FFFFC848 0002100BD23A (0007FFFFC858, 000A00000000, 0000000000B5, 0000000000B5) msys-2.0.dll+0x7D23A +0007FFFFC848 0002102130B8 (000000000000, 000A00000030, 000000000000, 000000000000) msys-2.0.dll+0x1D30B8 +0007FFFFC848 00010042A1E5 (0002100A76B3, 7FF900000000, 000210221CCD, 000100000001) grep.exe+0x2A1E5 +0007FFFFCB80 000100404B7C (00000000000D, 000000000000, 000000000000, 000000000000) grep.exe+0x4B7C +0007FFFFCB80 000100429678 (0002100455E0, 000000000000, 000000000148, 000000000000) grep.exe+0x29678 +0007FFFFCD30 000210047F01 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x7F01 +000000000000 000210045AC3 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x5AC3 +0007FFFFFFF0 000210045B74 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x5B74 +End of stack trace +Loaded modules: +000100400000 grep.exe +7FF952CB0000 ntdll.dll +7FF9520C0000 KERNEL32.DLL +7FF94FFF0000 KERNELBASE.dll +0005603F0000 msys-iconv-2.dll +000430B30000 msys-intl-8.dll +000210040000 msys-2.0.dll +0004C36D0000 msys-pcre-1.dll +7FF951F50000 advapi32.dll +7FF9525E0000 msvcrt.dll +7FF952010000 sechost.dll +7FF9503A0000 bcrypt.dll +7FF952380000 RPCRT4.dll +7FF94F720000 CRYPTBASE.DLL +7FF9508C0000 bcryptPrimitives.dll diff --git a/go/cmd/kjol-web/internal/handlers/public.go b/go/cmd/kjol-web/internal/handlers/public.go index 5daf12ca..81951cd3 100644 --- a/go/cmd/kjol-web/internal/handlers/public.go +++ b/go/cmd/kjol-web/internal/handlers/public.go @@ -1,4 +1,4 @@ -// Package handlers serves the server-rendered public pages of the Kjol JS Web +// Package handlers serves the server-rendered public pages of the Kjøl JS Web // section. // // This file is the APP side of a coupling inversion. kjol's bundler renders each diff --git a/go/cmd/kjol-web/server/main.go b/go/cmd/kjol-web/server/main.go index 4aeb02f4..e0c57303 100644 --- a/go/cmd/kjol-web/server/main.go +++ b/go/cmd/kjol-web/server/main.go @@ -21,27 +21,34 @@ import ( "kjol/webui" "kjolweb/app" - "kjolweb/buildsteps" + "kjolweb/build" "kjolweb/internal/handlers" ) func main() { addr := flag.String("addr", ":8085", "listen address") watch := flag.Bool("watch", true, "watch sources, rebuild wasm, hot-reload") + buildOnly := flag.Bool("build", false, "run the full build once and exit (no server)") flag.Parse() + if *buildOnly { + build.Cold() + return + } + log.Fatal(wasmdevserver.Serve(wasmdevserver.Config{ Addr: *addr, Dir: "./wwwroot", Watch: *watch, WatchDirs: []string{ - "app", "wasm", "css", // this app's Go/WASM half - "frontend", // its Solid half — a .tsx save rebuilds the JS bundle + "app", "wasm", "css", // the Go/WASM app + "frontend", // the Solid app — a .tsx save rebuilds the JS bundle "../../webui", "../../vdom", "../../wasmruntime", "../../rsc", // the wasm engine + "../../lexer", // the code-block highlighter "../../jsruntime/uikit", "../../jsruntime/styles", // the Solid kit + the shared theme }, - Build: buildsteps.All, - BuildCSS: buildsteps.Tailwind, // a .css save skips codegen+wasm and hot-swaps the stylesheet + Build: build.All, + BuildCSS: build.Tailwind, // a .css save skips codegen+wasm and hot-swaps the stylesheet Render: render, Document: document, Handle: routes, @@ -71,10 +78,10 @@ func routes(mux *http.ServeMux) { // document — the client router reads the URL and decides what to render, which is what // makes it a single-page app. // -// It carries no server-rendered markup, and that is a real difference from the Go/WASM -// half rather than an oversight: this section is a docs section behind a click, where a -// blank first frame costs nothing. Where it WOULD cost something, the public-page path -// (see internal/handlers) renders on the server instead — /js/ssr is that, and it is +// It carries no server-rendered markup, and that is a real difference from Kjøl Wasm Web +// rather than an oversight: this is a docs section behind a click, where a blank first +// frame costs nothing. Where it WOULD cost something, the public-page path (see +// internal/handlers) renders on the server instead — /js/ssr is that, and it is // registered above, so it never reaches this handler. func serveJSApp(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") @@ -83,7 +90,7 @@ func serveJSApp(w http.ResponseWriter, r *http.Request) { <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> -<title>Kjol JS Web +Kjøl JS Web `+webui.ThemeBootScript+` @@ -130,7 +137,7 @@ func document(inner string) string { -kjol — a shared base layer +kjøl — a shared base layer ` + webui.ThemeBootScript + ` diff --git a/go/cmd/kjol-web/wasm/main.go b/go/cmd/kjol-web/wasm/main.go index a5c3833b..a2bd6de8 100644 --- a/go/cmd/kjol-web/wasm/main.go +++ b/go/cmd/kjol-web/wasm/main.go @@ -37,5 +37,17 @@ func main() { // boot script put it there before the first paint — so this is not what makes the // page dark; it is what makes the SWITCH know which way it is pointing, and what // keeps the page following the OS if the user never touched the switch. + // + // The server rendered this button not knowing the theme (it cannot: the preference is + // in localStorage), so it drew the wrong icon. Init's signal write re-renders it, and + // the reconciler patches the one button. That mismatch is inherent, not a bug — it is + // the same reason the boot script has to exist at all. + // + // This line used to be unreachable: Hydrate ended in a `select {}` and never returned. + // See wasmruntime.Wait. app.Theme.Init() + + // Last line, always: a Go/wasm main that returns is a dead program, and every event + // handler on the page dies with it. + wasmruntime.Wait() } diff --git a/go/jsruntime/doc.go b/go/jsruntime/doc.go index 474585b0..340b6d25 100644 --- a/go/jsruntime/doc.go +++ b/go/jsruntime/doc.go @@ -1,4 +1,4 @@ -// Package jsruntime is the JS/TS half of kjol: the Solid component kit, the +// Package jsruntime is the JS/TS tree of kjol: the Solid component kit, the // vendored Solid runtime, the FontAwesome SVG source kit, the shared Tailwind // @theme scaffold, and the generic TS scaffolding (auth, hooks, ssr, utils). // diff --git a/go/jsruntime/icons/regular/bell.svg b/go/jsruntime/icons/regular/bell.svg new file mode 100644 index 00000000..cbceffa5 --- /dev/null +++ b/go/jsruntime/icons/regular/bell.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go/jsruntime/icons/regular/chart-column.svg b/go/jsruntime/icons/regular/chart-column.svg new file mode 100644 index 00000000..6199f6c4 --- /dev/null +++ b/go/jsruntime/icons/regular/chart-column.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go/jsruntime/icons/solid/bell.svg b/go/jsruntime/icons/solid/bell.svg new file mode 100644 index 00000000..297bda45 --- /dev/null +++ b/go/jsruntime/icons/solid/bell.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go/jsruntime/icons/solid/chart-column.svg b/go/jsruntime/icons/solid/chart-column.svg new file mode 100644 index 00000000..0315653d --- /dev/null +++ b/go/jsruntime/icons/solid/chart-column.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go/jsruntime/styles/theme.css b/go/jsruntime/styles/theme.css index 1120ad02..08b44987 100644 --- a/go/jsruntime/styles/theme.css +++ b/go/jsruntime/styles/theme.css @@ -70,7 +70,7 @@ The vocabulary is deliberately identical to the Go/WASM kit's (webui. ThemeTokens). Two kits, one contract: a designer changes `surface` once and - both halves of the site move together. */ + both layers of the site move together. */ /* Surfaces, in ascending prominence. */ --color-surface: #ffffff; @@ -96,12 +96,23 @@ --color-fill-neutral-hover: #262626; --color-on-fill-neutral: #ffffff; - /* The accent. `primary` is a FILL — it carries white text, so it must stay dark - enough for white to sit on it. Brand supplies the value; see the app's - style.css. These defaults keep the kit legible before an app has themed - anything, NOT so that it can skip theming. */ + /* The accent, in four parts, because a single "brand colour" cannot do four jobs. + Same names as the Go kit (webui.ThemeTokens) — one vocabulary, two kits. + + primary is a FILL: it carries white text, so it has to stay dark enough for white + to sit on it. accent is TEXT and icons: it has to stay readable ON the surface. + One value cannot be both, and in dark mode they diverge completely — the fill + stays put while the text has to climb. + + primary-subtle / primary-border are the tinted panel: a callout, a selected row. + + Brand supplies the values; see the app's style.css. These defaults keep the kit + legible before an app has themed anything, NOT so that it can skip theming. */ --color-primary: #404040; --color-primary-hover: #262626; + --color-primary-subtle: #f5f5f5; + --color-primary-border: #d4d4d4; + --color-accent: #262626; /* ---- deliberately-dark chrome (the kit's `onDark` prop) ---------------- Not the same thing as dark MODE. Several components take onDark, for when @@ -164,6 +175,13 @@ --color-fill-neutral: #e5e5e5; --color-fill-neutral-hover: #ffffff; --color-on-fill-neutral: #171717; + + /* The fill stays where it is — white text still has to be readable on it — but the + tinted panel and the accent TEXT both have to move, or they vanish into a near + black surface. This is exactly why they are separate tokens. */ + --color-primary-subtle: #1f1f24; + --color-primary-border: #3d3d45; + --color-accent: #e5e5e5; } /* The page's own background — painted before anything mounts, and behind it diff --git a/go/jsruntime/uikit/Theme.tsx b/go/jsruntime/uikit/Theme.tsx index 0e4e3db7..88ef520d 100644 --- a/go/jsruntime/uikit/Theme.tsx +++ b/go/jsruntime/uikit/Theme.tsx @@ -7,7 +7,7 @@ // the kit are none the wiser. // // The storage key is deliberately the SAME one the Go/WASM kit uses -// (webui.ThemeBootScript, webui.themeStorageKey). Both halves of kjol-web are served +// (webui.ThemeBootScript, webui.themeStorageKey). Both layers of kjol-web are served // from one origin, so they share a localStorage: choose dark in the /wasm section, // walk over to /js, and it is still dark. Two front-ends, one preference. diff --git a/go/lexer/lexer.go b/go/lexer/lexer.go new file mode 100644 index 00000000..35c9ed5b --- /dev/null +++ b/go/lexer/lexer.go @@ -0,0 +1,124 @@ +// Package lexer turns source code into syntax-highlighted HTML. +// +// It is a LEXER, not a parser: it classifies tokens and gives up gracefully on anything it +// does not understand. A highlighter that can fail to render is worse than one that +// occasionally paints an identifier the wrong colour — the reader can see past a wrong +// colour, and cannot see past a blank page. Unterminated strings and comments run to a +// sensible boundary rather than throwing, and a language nobody has written a lexer for +// comes back escaped and unpainted rather than not coming back. +// +// Output is HTML, and every run of source text passes through html.EscapeString on the way +// out. That is not a nicety: the input is source code, which is full of `<`, `>` and `&` — +// a C file is nothing but shifts and arrows, and one of the snippets kjøl's own docs +// display is literally a block of HTML. Unescaped, a snippet containing `
` renders a +// div, and half the line disappears into it. +// +// It lives beside webui rather than inside it because it has nothing to do with the DOM: +// it is a string in and a string out, it imports nothing but the standard library, and it +// is as usable from a static site generator or a terminal as from a component. webui just +// happened to be where it was first needed. +// +// The one thing tying it to a UI is the palette — the class names below are Tailwind's, so +// whatever compiles your CSS has to scan THIS package too, or the colours will not exist. +// (In kjøl's own site that is cmd/kjol-web/build.Tailwind's source globs.) +package lexer + +import ( + "html" + "strings" +) + +// A code block is dark in BOTH themes — a light code block on a light page is a different +// kind of thing, and switching it with the theme means the snippet you were reading changes +// colour under you. So these are fixed on-dark colours, not theme tokens: the surface they +// sit on never changes. +// +// They are shared by every language in the package, so that a Go snippet and a C snippet on +// the same page agree about what a string looks like. A reader should not have to relearn +// the palette per section. +const ( + CommentClass = "text-ink-faint" + StringClass = "text-emerald-300" + KeywordClass = "text-sky-300" + NumberClass = "text-amber-300" + FuncClass = "text-violet-300" + + // The two C needs and Go does not. C has a preprocessor, and C declarations are mostly + // TYPE — so painting `U64` the same colour as `static` throws away the one distinction + // that makes a header skimmable. + TypeClass = "text-teal-300" + DirectiveClass = "text-rose-300" +) + +// Highlight returns HTML for src, painted for the named language. +// +// The name is matched case-insensitively, and an UNKNOWN one is not an error: the source +// comes back escaped and unpainted. That is deliberate, and it is why callers can pass a +// caption's language label straight through. A docs page that showed nothing for a shell +// snippet because nobody has written a shell lexer would be trading a small loss of colour +// for a total loss of content — and painting it with the WRONG lexer (Jai through Go's +// keyword table) would be worse than either. +// +// The result is meant for a raw-HTML node inside a
: it contains no block elements and
+// preserves the source's whitespace exactly, so the 
 does the layout.
+func Highlight(lang, src string) string {
+	switch strings.ToLower(lang) {
+	case "go":
+		return HighlightGo(src)
+	case "c":
+		return HighlightC(src)
+	default:
+		return html.EscapeString(src)
+	}
+}
+
+// ---- the pieces every language in here shares ----------------------------
+
+// quoted consumes a quoted literal starting at i and writes it as a string span.
+// escapes reports whether a backslash escapes the next byte (false for Go's raw strings).
+func quoted(b *strings.Builder, src string, i int, quote byte, escapes bool) int {
+	j := i + 1
+	for j < len(src) {
+		if escapes && src[j] == '\\' && j+1 < len(src) {
+			j += 2
+			continue
+		}
+		if src[j] == quote {
+			j++
+			break
+		}
+		if escapes && src[j] == '\n' {
+			break // unterminated: stop at the line end rather than eating the file
+		}
+		j++
+	}
+	span(b, StringClass, src[i:j])
+	return j
+}
+
+// callAhead reports whether the next non-space byte at or after i is an opening paren —
+// which is the whole of the "is this identifier a function" test. It is a guess, and a good
+// one: an identifier immediately before a `(` is being called, declared, or converted to,
+// and all three are worth seeing at a glance.
+func callAhead(src string, i int) bool {
+	for i < len(src) && (src[i] == ' ' || src[i] == '\t') {
+		i++
+	}
+	return i < len(src) && src[i] == '('
+}
+
+func span(b *strings.Builder, class, text string) {
+	b.WriteString(``)
+	b.WriteString(html.EscapeString(text))
+	b.WriteString(``)
+}
+
+func isDigit(c byte) bool { return c >= '0' && c <= '9' }
+func isHexish(c byte) bool {
+	return c == '.' || c == 'x' || c == 'X' || c == '_' ||
+		(c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
+}
+func isIdentStart(c byte) bool { return c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') }
+func isIdentPart(c byte) bool  { return isIdentStart(c) || isDigit(c) }
diff --git a/go/lexer/lexer_c.go b/go/lexer/lexer_c.go
new file mode 100644
index 00000000..442f3c20
--- /dev/null
+++ b/go/lexer/lexer_c.go
@@ -0,0 +1,433 @@
+package lexer
+
+import (
+	"html"
+	"strings"
+)
+
+// C syntax highlighting. Same contract as the rest of the package (see lexer.go).
+//
+// The classification tables below MIRROR the ones in kjøl's own C layer, c/lexer/lexer_c.c
+// — a highlighter that colours the same source inside the editor it was written for. Two
+// highlighters for one language is one too many, but they cannot share: that one is C
+// compiled into an editor, this one is Go compiled to WebAssembly, and nothing is upstream
+// of both. So they are two tables that agree, and this comment is the thing that says they
+// have to. If you add a type there, add it here.
+//
+// That is also why U8/S32/F64/B32 are in the type table. They are not C types — they are
+// kjøl's — but this highlighter exists to render kjøl's C, and a page about base_core.h
+// that paints its own typedefs as bare identifiers has failed at the one job it has.
+
+// The reserved words. Order of the maps below is also the order they are tested in, and
+// the categories do not overlap.
+var cKeywords = map[string]bool{
+	"auto": true, "break": true, "case": true, "continue": true, "default": true,
+	"do": true, "else": true, "enum": true, "for": true, "goto": true, "if": true,
+	"inline": true, "restrict": true, "return": true, "sizeof": true, "struct": true,
+	"switch": true, "typedef": true, "union": true, "while": true,
+	"_Alignas": true, "alignas": true, "_Alignof": true, "alignof": true,
+	"_Atomic": true, "_Generic": true, "_Noreturn": true,
+	"static_assert": true, "_Static_assert": true,
+}
+
+// Storage class and qualifiers. They read as keywords and are coloured as keywords — the
+// C layer's lexer keeps them as a separate TOK_MODIFIER, but that distinction buys a
+// reader of a documentation page nothing, so it is not carried over.
+var cModifiers = map[string]bool{
+	"const": true, "constexpr": true, "extern": true, "register": true, "signed": true,
+	"static": true, "unsigned": true, "volatile": true,
+	"thread_local": true, "_Thread_local": true,
+	// Not C. base_core.h's three names for the three meanings of `static`, and the whole
+	// point of them is that they are visible — so they are coloured like what they are.
+	"internal": true, "global": true, "local_persist": true,
+}
+
+var cTypes = map[string]bool{
+	"char": true, "double": true, "float": true, "int": true, "long": true,
+	"short": true, "void": true, "bool": true, "_Bool": true,
+	"_Complex": true, "_Imaginary": true,
+	"int8_t": true, "int16_t": true, "int32_t": true, "int64_t": true,
+	"uint8_t": true, "uint16_t": true, "uint32_t": true, "uint64_t": true,
+	"size_t": true, "ssize_t": true, "ptrdiff_t": true,
+	"intptr_t": true, "uintptr_t": true, "nullptr_t": true, "FILE": true,
+	// kjøl's base layer (c/base/base_core.h).
+	"U8": true, "U16": true, "U32": true, "U64": true,
+	"S8": true, "S16": true, "S32": true, "S64": true,
+	"B32": true, "F32": true, "F64": true,
+}
+
+var cValues = map[string]bool{
+	"false": true, "true": true, "NULL": true, "nullptr": true,
+}
+
+// HighlightC turns C source into HTML with the tokens wrapped in coloured spans.
+//
+// The result is meant for vdom.Raw inside a 
: it contains no block elements and
+// preserves the source's whitespace exactly, so the 
 does the layout.
+func HighlightC(src string) string {
+	var b strings.Builder
+	b.Grow(len(src) * 2)
+
+	// The types this snippet declares about itself.
+	//
+	// C cannot be highlighted correctly in one pass, and this is the reason: `Arena *a` is
+	// a declaration and `a * b` is a multiplication, and they are the same three tokens.
+	// Telling them apart needs to know that Arena is a type — which is the oldest problem
+	// in parsing C, and the reason C compilers feed the symbol table back into the lexer.
+	//
+	// The alternative most highlighters take is to guess from the spacing, which is wrong
+	// as often as the author's style differs from theirs. So this does not guess. It reads
+	// the declarations first — `typedef struct Arena {…} Arena;` says Arena is a type, in
+	// so many words — and then highlights knowing what the snippet said. A type the
+	// snippet never declares stays an identifier, which is honest: nothing in the text the
+	// reader is looking at claims otherwise.
+	declared := cDeclaredTypes(src)
+
+	// Two pieces of state, and both exist for the preprocessor.
+	//
+	// lineStart tracks whether we have seen anything but whitespace since the last
+	// newline, because a `#` is only a directive at the start of a line — everywhere else
+	// it is the stringize or paste operator inside a macro body, and painting THAT as a
+	// directive turns the inside of every macro red.
+	//
+	// inInclude is set for the rest of the line after `#include`, and it is what makes
+	//  a header path rather than a less-than, an identifier, a dot and a
+	// greater-than. It is the one place in C where `<` does not mean what it usually does.
+	lineStart := true
+	inInclude := false
+
+	// prevWord is the last identifier-shaped token seen. After `struct`, `union` or `enum`
+	// the next identifier IS a type name — that one is syntax, not a guess, so it is worth
+	// tracking one word of history to get it right.
+	prevWord := ""
+
+	i := 0
+	for i < len(src) {
+		c := src[i]
+
+		switch {
+		case c == '\n':
+			b.WriteByte('\n')
+			lineStart = true
+			inInclude = false
+			prevWord = ""
+			i++
+			continue
+
+		case c == ' ' || c == '\t' || c == '\r':
+			b.WriteByte(c)
+			i++
+			continue // whitespace does not end lineStart, and does not clear prevWord
+
+		// Line comment. // is C99 and every C anyone writes today uses it.
+		case c == '/' && i+1 < len(src) && src[i+1] == '/':
+			end := strings.IndexByte(src[i:], '\n')
+			if end < 0 {
+				end = len(src)
+			} else {
+				end += i
+			}
+			span(&b, CommentClass, src[i:end])
+			i = end
+
+		// Block comment.
+		case c == '/' && i+1 < len(src) && src[i+1] == '*':
+			end := strings.Index(src[i+2:], "*/")
+			if end < 0 {
+				end = len(src)
+			} else {
+				end = i + 2 + end + 2
+			}
+			span(&b, CommentClass, src[i:end])
+			i = end
+
+		// A preprocessor directive: `#` and the word after it, and only at the start of a
+		// line. `#define`, `#include`, `#ifdef`, `#pragma`.
+		case c == '#' && lineStart:
+			j := i + 1
+			for j < len(src) && (src[j] == ' ' || src[j] == '\t') {
+				j++ // `#  define` is legal, and rare, and free to support
+			}
+			for j < len(src) && isIdentPart(src[j]) {
+				j++
+			}
+			word := src[i:j]
+			span(&b, DirectiveClass, word)
+			// The rest of an #include line reads differently. Nothing else does.
+			if strings.HasSuffix(word, "include") || strings.HasSuffix(word, "import") {
+				inInclude = true
+			}
+			i = j
+
+		//  — but ONLY on an #include line. Anywhere else this is an operator.
+		case c == '<' && inInclude:
+			end := i + 1
+			for end < len(src) && src[end] != '>' && src[end] != '\n' {
+				end++
+			}
+			if end < len(src) && src[end] == '>' {
+				end++ // include the closing bracket
+			}
+			span(&b, StringClass, src[i:end])
+			i = end
+
+		case c == '"':
+			i = quoted(&b, src, i, '"', true)
+
+		// A character literal. In C this is an INT, not a string — but every editor paints
+		// it like a string, and a reader looking for '\0' is looking for a literal.
+		case c == '\'':
+			i = quoted(&b, src, i, '\'', true)
+
+		case isDigit(c):
+			j := cNumberEnd(src, i)
+			span(&b, NumberClass, src[i:j])
+			i = j
+
+		case isIdentStart(c):
+			j := i
+			for j < len(src) && isIdentPart(src[j]) {
+				j++
+			}
+			word := src[i:j]
+
+			switch {
+			case cKeywords[word]:
+				span(&b, KeywordClass, word)
+			case cModifiers[word]:
+				span(&b, KeywordClass, word)
+			case cTypes[word]:
+				span(&b, TypeClass, word)
+			case cValues[word]:
+				span(&b, NumberClass, word) // NULL and true are constants; colour them as such
+			case declared[word]:
+				// The snippet said so itself, in a typedef or a struct tag. Not a guess.
+				span(&b, TypeClass, word)
+			case prevWord == "struct" || prevWord == "union" || prevWord == "enum":
+				// Syntax, not a guess: what follows one of these IS a type name.
+				span(&b, TypeClass, word)
+			case isTypeSuffixed(word):
+				span(&b, TypeClass, word)
+			case callAhead(src, j):
+				span(&b, FuncClass, word)
+			default:
+				b.WriteString(html.EscapeString(word))
+			}
+
+			prevWord = word
+			lineStart = false
+			i = j
+			continue
+
+		default:
+			b.WriteString(html.EscapeString(string(c)))
+			i++
+		}
+
+		lineStart = false
+		prevWord = ""
+	}
+	return b.String()
+}
+
+// cDeclaredTypes reads a snippet's type declarations and returns the names they introduce.
+//
+// It is a scan, not a parse, and it recognises exactly two shapes — the two that declare a
+// type name in C:
+//
+//	struct Arena / union Foo / enum Lang            the word after the tag is a type
+//	typedef ... Name ;                              the last word before the semicolon
+//	typedef ... (*Name)(...) ;                      unless it is a function pointer, whose
+//	                                                name hides inside the parens
+//
+// The typedef rule is why brace depth is tracked. `typedef struct Arena { U8 *base; } Arena;`
+// contains semicolons that do not end it, and taking the first one would declare a type
+// called `base`.
+//
+// Comments and string literals are skipped, or the word `struct` inside a comment would
+// declare the next word in the prose as a type.
+func cDeclaredTypes(src string) map[string]bool {
+	types := map[string]bool{}
+
+	inTypedef := false // inside a typedef statement, up to its semicolon
+	lastWord := ""     // last identifier seen in it — the name, for the common shape
+	fnPtrName := ""    // ...unless it is a function pointer, whose name is in (*Name)
+	prevWord := ""     // one word of history, for `struct Arena`
+	depth := 0         // brace depth: only a depth-0 semicolon ends the typedef
+
+	i := 0
+	for i < len(src) {
+		c := src[i]
+		switch {
+		case c == ' ' || c == '\t' || c == '\r' || c == '\n':
+			i++ // whitespace must NOT clear prevWord: `struct Arena` has a space in it
+
+		case c == '/' && i+1 < len(src) && src[i+1] == '/':
+			if end := strings.IndexByte(src[i:], '\n'); end < 0 {
+				i = len(src)
+			} else {
+				i += end
+			}
+
+		case c == '/' && i+1 < len(src) && src[i+1] == '*':
+			if end := strings.Index(src[i+2:], "*/"); end < 0 {
+				i = len(src)
+			} else {
+				i += 2 + end + 2
+			}
+
+		case c == '"' || c == '\'':
+			i = skipQuoted(src, i, c)
+
+		case c == '{':
+			depth++
+			i++
+
+		case c == '}':
+			depth--
+			i++
+
+		// (*Name)(...) — a function-pointer typedef. The name is here and nowhere else.
+		case c == '(' && i+1 < len(src) && src[i+1] == '*':
+			j := i + 2
+			for j < len(src) && (src[j] == ' ' || src[j] == '\t') {
+				j++
+			}
+			k := j
+			for k < len(src) && isIdentPart(src[k]) {
+				k++
+			}
+			if inTypedef && k > j {
+				fnPtrName = src[j:k]
+			}
+			i = k
+			prevWord = ""
+
+		case c == ';' && depth == 0:
+			if inTypedef {
+				name := fnPtrName
+				if name == "" {
+					name = lastWord
+				}
+				if name != "" {
+					types[name] = true
+				}
+			}
+			inTypedef, lastWord, fnPtrName, prevWord = false, "", "", ""
+			i++
+
+		case isIdentStart(c):
+			j := i
+			for j < len(src) && isIdentPart(src[j]) {
+				j++
+			}
+			word := src[i:j]
+
+			if word == "typedef" {
+				inTypedef, lastWord, fnPtrName = true, "", ""
+			} else {
+				if prevWord == "struct" || prevWord == "union" || prevWord == "enum" {
+					types[word] = true
+				}
+				if inTypedef {
+					lastWord = word
+				}
+			}
+			prevWord = word
+			i = j
+
+		default:
+			prevWord = ""
+			i++
+		}
+	}
+	return types
+}
+
+// skipQuoted returns the index one past a quoted literal, without emitting anything.
+func skipQuoted(src string, i int, quote byte) int {
+	j := i + 1
+	for j < len(src) {
+		if src[j] == '\\' && j+1 < len(src) {
+			j += 2
+			continue
+		}
+		if src[j] == quote {
+			return j + 1
+		}
+		if src[j] == '\n' {
+			return j // unterminated
+		}
+		j++
+	}
+	return j
+}
+
+// isTypeSuffixed reports whether a name ends in _t.
+//
+// This is a HEURISTIC, and the only one in this file. C has no way to know what is a type
+// without a symbol table, and building one to colour a documentation snippet would be
+// absurd — so the choice is between guessing and not guessing. `_t` is the one convention
+// universal enough to guess on: it is what the standard library does (size_t, uint32_t),
+// and a project that uses it for something that is not a type is doing so to be confusing.
+//
+// Everything else that is not in the tables above stays an identifier. A highlighter that
+// paints too little is quietly unhelpful; one that paints too much is actively misleading.
+func isTypeSuffixed(word string) bool {
+	return len(word) > 2 && strings.HasSuffix(word, "_t")
+}
+
+// cNumberEnd returns the index one past the number starting at i.
+//
+// It is its own function because C numbers are a small zoo: 0x25, 0b1011, 1024, 3.14f,
+// 1e9, 0xFFull. Go's number scanner (code.go) is a single character-class loop, which is
+// enough for Go and would swallow the `x` of `0xFF` into a hex-ish blur and then choke on
+// the `ull`.
+func cNumberEnd(src string, i int) int {
+	j := i
+
+	switch {
+	case src[j] == '0' && j+1 < len(src) && (src[j+1] == 'x' || src[j+1] == 'X'):
+		j += 2
+		for j < len(src) && isHexDigit(src[j]) {
+			j++
+		}
+	case src[j] == '0' && j+1 < len(src) && (src[j+1] == 'b' || src[j+1] == 'B'):
+		j += 2
+		for j < len(src) && (src[j] == '0' || src[j] == '1') {
+			j++
+		}
+	default:
+		for j < len(src) && (isDigit(src[j]) || src[j] == '.') {
+			j++
+		}
+		// An exponent, but only if it actually has digits after it — otherwise the `e` of
+		// `1end` is a number and `nd` is an identifier, which is nonsense.
+		if j < len(src) && (src[j] == 'e' || src[j] == 'E') {
+			k := j + 1
+			if k < len(src) && (src[k] == '+' || src[k] == '-') {
+				k++
+			}
+			if k < len(src) && isDigit(src[k]) {
+				for k < len(src) && isDigit(src[k]) {
+					k++
+				}
+				j = k
+			}
+		}
+	}
+
+	// u, U, l, L, f, F, in any order and any number: 0xFFull, 1.0f, 10UL.
+	for j < len(src) && isNumSuffix(src[j]) {
+		j++
+	}
+	return j
+}
+
+func isHexDigit(c byte) bool {
+	return isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
+}
+
+func isNumSuffix(c byte) bool {
+	return c == 'u' || c == 'U' || c == 'l' || c == 'L' || c == 'f' || c == 'F'
+}
diff --git a/go/lexer/lexer_c_test.go b/go/lexer/lexer_c_test.go
new file mode 100644
index 00000000..6fdbd150
--- /dev/null
+++ b/go/lexer/lexer_c_test.go
@@ -0,0 +1,198 @@
+package lexer
+
+import (
+	"strings"
+	"testing"
+)
+
+// The escaping test is the one that matters most, and C is worse than Go for it.
+//
+// A C header opens with `#include `. If that reaches the page unescaped, the
+// browser sees an unknown tag, swallows it, and the line silently loses its second half.
+// The same goes for `<<`, `->` and `&&`, which are on nearly every line of real C.
+func TestHighlightCEscapes(t *testing.T) {
+	got := HighlightC("#include \nx = a << 2 & b->c;\n")
+
+	for _, raw := range []string{"", "<<", "&&", "->"} {
+		// The only `<` in the output should be the ones opening our own spans.
+		if strings.Contains(stripSpans(got), raw) {
+			t.Errorf("%q survived unescaped into the HTML — the browser will eat it:\n%s", raw, got)
+		}
+	}
+	for _, want := range []string{"<stdio.h>", "<<", "->"} {
+		if !strings.Contains(got, want) {
+			t.Errorf("expected %q in the output, got:\n%s", want, got)
+		}
+	}
+}
+
+func TestHighlightCClassifies(t *testing.T) {
+	src := "typedef struct Arena { U8 *base; U64 pos; } Arena;\n" +
+		"\n" +
+		"internal U64 arena_push(Arena *a, U64 size) {\n" +
+		"    // bump\n" +
+		"    U64 pos = a->pos + 0x10;\n" +
+		"    return pos;\n" +
+		"}\n"
+	got := HighlightC(src)
+
+	cases := []struct{ class, text, why string }{
+		{KeywordClass, "internal", "base_core's name for a file-static reads as a storage class"},
+		{TypeClass, "U64", "kjøl's own base types are types, not bare identifiers"},
+		{TypeClass, "Arena", "and so is a struct the snippet declared"},
+		{FuncClass, "arena_push", "an identifier before ( is being declared or called"},
+		{NumberClass, "0x10", "hex is a number, not a 0 followed by an identifier"},
+		{CommentClass, "// bump", "a line comment"},
+		{KeywordClass, "return", "a keyword"},
+	}
+	for _, c := range cases {
+		want := `` + c.text + ``
+		if !strings.Contains(got, want) {
+			t.Errorf("%s: expected %s\ngot:\n%s", c.why, want, got)
+		}
+	}
+}
+
+// The pre-pass. This is what lets `Arena *a` be a declaration rather than a guess — so the
+// shapes it has to recognise are worth pinning down one at a time.
+func TestCDeclaredTypes(t *testing.T) {
+	cases := []struct {
+		src, want, why string
+	}{
+		{"typedef struct Arena { U8 *base; U64 pos; } Arena;", "Arena",
+			"the inner semicolons must not end the typedef — the first one would declare `base`"},
+		{"typedef enum Lang { LANG_C, LANG_GO } Lang;", "Lang",
+			"an enum typedef names its type the same way"},
+		{"typedef struct Arena Arena;", "Arena",
+			"a forward declaration still declares the name"},
+		{"struct Tokenizer { S32 at; };", "Tokenizer",
+			"a plain struct tag, with no typedef at all"},
+		{"typedef void (*LexerTokenizeFn)(const char *data, S32 len);", "LexerTokenizeFn",
+			"a function pointer hides its name in the parens — the last word is a parameter"},
+		{"typedef U64 Handle;", "Handle",
+			"the simple case"},
+	}
+	for _, c := range cases {
+		if got := cDeclaredTypes(c.src); !got[c.want] {
+			t.Errorf("%s\n  src:  %s\n  want: %q declared, got %v", c.why, c.src, c.want, keysOf(got))
+		}
+	}
+
+	// The word `struct` inside a comment or a string declares nothing.
+	for _, src := range []string{
+		"// struct Ghost is not real\n",
+		`const char *s = "struct Ghost";`,
+		"/* struct Ghost */",
+	} {
+		if got := cDeclaredTypes(src); got["Ghost"] {
+			t.Errorf("a type was declared from inside a comment or string: %s", src)
+		}
+	}
+}
+
+func keysOf(m map[string]bool) []string {
+	out := make([]string, 0, len(m))
+	for k := range m {
+		out = append(out, k)
+	}
+	return out
+}
+
+// `#` is a directive only at the start of a line. Inside a macro body it is the stringize
+// operator, and painting THAT as a directive turns the inside of every macro red.
+func TestHighlightCDirectivesOnlyAtLineStart(t *testing.T) {
+	got := HighlightC("#define STR(x) #x\n")
+
+	if !strings.Contains(got, `#define`) {
+		t.Errorf("#define should be a directive:\n%s", got)
+	}
+	// The second # (the stringize operator) must NOT be a directive span.
+	if strings.Count(got, ``) != 1 {
+		t.Errorf("the stringize # was painted as a directive too:\n%s", got)
+	}
+}
+
+// The one place in C where `<` is not an operator.
+func TestHighlightCAngleBracketsOnlyOnIncludeLines(t *testing.T) {
+	got := HighlightC("#include \nif (a < b && c > d) return;\n")
+
+	if !strings.Contains(got, `<stdio.h>`) {
+		t.Errorf("the header path should be a string span:\n%s", got)
+	}
+	// On the NEXT line, `<` is a comparison. If inInclude leaked past the newline, the
+	// rest of the file from `< b &&...` would be swallowed into one green string.
+	if strings.Contains(got, `< b`) {
+		t.Errorf("the include state leaked onto the following line:\n%s", got)
+	}
+}
+
+func TestHighlightCNumbers(t *testing.T) {
+	for _, n := range []string{"0x25", "0xFFull", "0b1011", "1024", "3.14f", "1e9", "10UL"} {
+		got := HighlightC("x = " + n + ";")
+		want := `` + n + ``
+		if !strings.Contains(got, want) {
+			t.Errorf("%q was not lexed as one number:\n%s", n, got)
+		}
+	}
+}
+
+// A snippet in a documentation page is a fragment. It gets cut off mid-string and
+// mid-comment all the time, and it must still render — a highlighter that can panic takes
+// the whole page with it.
+func TestHighlightCSurvivesMalformedInput(t *testing.T) {
+	for _, src := range []string{
+		`char *s = "unterminated`,
+		"/* unterminated block",
+		"'",
+		"#",
+		"#include `, "")
+	for {
+		i := strings.Index(s, ``)
+		if j < 0 {
+			return s
+		}
+		s = s[:i] + s[i+j+2:]
+	}
+}
diff --git a/go/lexer/lexer_go.go b/go/lexer/lexer_go.go
new file mode 100644
index 00000000..454fa906
--- /dev/null
+++ b/go/lexer/lexer_go.go
@@ -0,0 +1,99 @@
+package lexer
+
+import (
+	"html"
+	"strings"
+)
+
+var goKeywords = map[string]bool{
+	"break": true, "case": true, "chan": true, "const": true, "continue": true,
+	"default": true, "defer": true, "else": true, "fallthrough": true, "for": true,
+	"func": true, "go": true, "goto": true, "if": true, "import": true,
+	"interface": true, "map": true, "package": true, "range": true, "return": true,
+	"select": true, "struct": true, "switch": true, "type": true, "var": true,
+	// Not keywords to the Go spec — predeclared identifiers — but every editor colours
+	// them, and a reader looking for `nil` is looking for the same kind of thing.
+	"nil": true, "true": true, "false": true, "iota": true,
+	"string": true, "int": true, "int64": true, "float64": true, "bool": true,
+	"byte": true, "rune": true, "any": true, "error": true,
+}
+
+// HighlightGo turns Go source into HTML with the tokens wrapped in coloured spans.
+func HighlightGo(src string) string {
+	var b strings.Builder
+	b.Grow(len(src) * 2)
+
+	i := 0
+	for i < len(src) {
+		c := src[i]
+
+		switch {
+		// Line comment — including the //gowasm: directives, which are the most important
+		// line in several of the snippets this was written for.
+		case c == '/' && i+1 < len(src) && src[i+1] == '/':
+			end := strings.IndexByte(src[i:], '\n')
+			if end < 0 {
+				end = len(src)
+			} else {
+				end += i
+			}
+			span(&b, CommentClass, src[i:end])
+			i = end
+
+		// Block comment.
+		case c == '/' && i+1 < len(src) && src[i+1] == '*':
+			end := strings.Index(src[i+2:], "*/")
+			if end < 0 {
+				end = len(src)
+			} else {
+				end = i + 2 + end + 2
+			}
+			span(&b, CommentClass, src[i:end])
+			i = end
+
+		// Interpreted string. Ends at the closing quote or the line's end — an unterminated
+		// string is a typo in a snippet, not a reason to paint the rest of the file green.
+		case c == '"':
+			i = quoted(&b, src, i, '"', true)
+
+		// Raw string: no escapes, and it may span lines.
+		case c == '`':
+			i = quoted(&b, src, i, '`', false)
+
+		// Rune literal.
+		case c == '\'':
+			i = quoted(&b, src, i, '\'', true)
+
+		case isDigit(c):
+			j := i
+			for j < len(src) && (isDigit(src[j]) || isHexish(src[j])) {
+				j++
+			}
+			span(&b, NumberClass, src[i:j])
+			i = j
+
+		case isIdentStart(c):
+			j := i
+			for j < len(src) && isIdentPart(src[j]) {
+				j++
+			}
+			word := src[i:j]
+			switch {
+			case goKeywords[word]:
+				span(&b, KeywordClass, word)
+			case callAhead(src, j):
+				// Colouring the callee is what makes the shape of a snippet readable at a
+				// glance: you see what it DOES before you read what it says.
+				span(&b, FuncClass, word)
+			default:
+				b.WriteString(html.EscapeString(word))
+			}
+			i = j
+
+		default:
+			b.WriteString(html.EscapeString(string(c)))
+			i++
+		}
+	}
+	return b.String()
+}
diff --git a/go/webui/code_test.go b/go/lexer/lexer_go_test.go
similarity index 91%
rename from go/webui/code_test.go
rename to go/lexer/lexer_go_test.go
index 415c6c1a..f553167e 100644
--- a/go/webui/code_test.go
+++ b/go/lexer/lexer_go_test.go
@@ -1,4 +1,4 @@
-package webui
+package lexer
 
 import (
 	"strings"
@@ -26,10 +26,10 @@ func TestHighlightGoClassifies(t *testing.T) {
 	got := HighlightGo("func main() { x := 42 // note\n}")
 
 	for _, want := range []struct{ what, class, text string }{
-		{"keyword", goKeywordClass, "func"},
-		{"call", goFuncClass, "main"},
-		{"number", goNumberClass, "42"},
-		{"comment", goCommentClass, "// note"},
+		{"keyword", KeywordClass, "func"},
+		{"call", FuncClass, "main"},
+		{"number", NumberClass, "42"},
+		{"comment", CommentClass, "// note"},
 	} {
 		if !strings.Contains(got, ``+want.text+``) {
 			t.Errorf("%s %q was not highlighted:\n%s", want.what, want.text, got)
@@ -41,7 +41,7 @@ func TestHighlightGoClassifies(t *testing.T) {
 // comments, and must survive as such.
 func TestHighlightGoKeepsDirectives(t *testing.T) {
 	got := HighlightGo("//gowasm:page / static layout=public\nfunc HomePage() {}")
-	if !strings.Contains(got, `//gowasm:page / static layout=public`) {
+	if !strings.Contains(got, `//gowasm:page / static layout=public`) {
 		t.Errorf("the directive was not kept whole as a comment:\n%s", got)
 	}
 }
diff --git a/go/wasmruntime/mount.go b/go/wasmruntime/mount.go
index 403f2724..e8faf4e6 100644
--- a/go/wasmruntime/mount.go
+++ b/go/wasmruntime/mount.go
@@ -56,6 +56,7 @@ func Hydrate(component func() *vdom.VNode) {
 	finish(root)
 }
 
+// finish wires the hot-swap teardown hook. It RETURNS — see Wait.
 func finish(root js.Value) {
 	// Allow a JS loader to tear this instance down before an in-place hot swap:
 	// snapshot signal state (so the next instance restores it), then clear #app.
@@ -65,7 +66,29 @@ func finish(root js.Value) {
 		root.Set("innerHTML", "")
 		return nil
 	}))
-	select {} // keep the runtime alive for event callbacks
+}
+
+// Wait blocks forever, keeping the Go runtime alive so its event callbacks can fire.
+//
+// It is the LAST line of main, and it is not optional: a Go/wasm program whose main
+// returns is a dead program, and every handler you registered panics with "Go program has
+// already exited" the first time it is touched.
+//
+// This used to be the last line of Run and Hydrate instead — a `select {}` buried inside
+// them. That made both of them functions that never return, which is not what they look
+// like, and it quietly turned everything written after the mount call into dead code:
+//
+//	wasmruntime.Hydrate(render)
+//	app.Theme.Init()            // never ran. Not once.
+//
+// The theme controller sat there for the life of the project not knowing what theme the
+// page was in, so the first click of the switch did nothing at all. Anything you have to
+// do AFTER the first render — adopt a stored preference, start a poller, measure the
+// page — hits the same wall.
+//
+// So: mounting mounts, and blocking is its own line, where you can see it.
+func Wait() {
+	select {}
 }
 
 func scheduleRender() {
diff --git a/go/webui/autotable.go b/go/webui/autotable.go
index 7e425ebe..f15a53e1 100644
--- a/go/webui/autotable.go
+++ b/go/webui/autotable.go
@@ -3137,7 +3137,7 @@ const (
 
 	FORMULA_EDIT_BASE    = "block w-full h-[30px] font-mono text-sm p-1 rounded-default box-border whitespace-pre"
 	FORMULA_OVERLAY_CLS  = FORMULA_EDIT_BASE + " absolute inset-0 overflow-hidden pointer-events-none border border-transparent text-ink"
-	FORMULA_TEXTAREA_CLS = FORMULA_EDIT_BASE + " relative bg-transparent text-transparent caret-neutral-800 resize-none overflow-x-auto overflow-y-hidden shadow-xs border border-line-strong focus:border-sky-500 outline-hidden"
+	FORMULA_TEXTAREA_CLS = FORMULA_EDIT_BASE + " relative bg-transparent text-transparent caret-ink resize-none overflow-x-auto overflow-y-hidden shadow-xs border border-line-strong focus:border-sky-500 outline-hidden"
 
 	formulaPlaceholder = `e.g. [Revenue] / SUM({Revenue}) * 100`
 
diff --git a/go/webui/cards.go b/go/webui/cards.go
index 17a7aa28..c2fa5a2f 100644
--- a/go/webui/cards.go
+++ b/go/webui/cards.go
@@ -48,7 +48,7 @@ func BorderCutCornerCard(class string, children ...*vdom.VNode) *vdom.VNode {
 }
 
 const cardHeader = "text-xl tracking-tight text-ink mb-5"
-const cardHeaderHR = "text-neutral-200 mt-1 mb-3"
+const cardHeaderHR = "text-line mt-1 mb-3"
 
 // CardHeader renders a card title followed by a divider.
 func CardHeader(class string, children ...*vdom.VNode) *vdom.VNode {
diff --git a/go/webui/code.go b/go/webui/code.go
deleted file mode 100644
index 3845b8d1..00000000
--- a/go/webui/code.go
+++ /dev/null
@@ -1,174 +0,0 @@
-package webui
-
-import (
-	"html"
-	"strings"
-)
-
-// Go syntax highlighting, for the code samples a documentation page shows.
-//
-// It is a LEXER, not a parser: it classifies tokens and gives up gracefully on anything
-// it does not understand, because a highlighter that can fail to render is worse than
-// one that occasionally paints an identifier the wrong colour. Unterminated strings and
-// comments run to the end of the input rather than throwing.
-//
-// Output is HTML, and every run of source text passes through html.EscapeString on the
-// way out — the input is Go source, which is full of `<`, `>` and `&`, and one of the
-// snippets this is meant to display is literally a block of HTML.
-
-// A code block is dark in BOTH themes — a light code block on a light page is a
-// different kind of thing, and switching it with the theme means the snippet you were
-// reading changes colour under you. So these are fixed on-dark colours, not theme
-// tokens: the surface they sit on never changes.
-const (
-	goCommentClass = "text-neutral-400"
-	goStringClass  = "text-emerald-300"
-	goKeywordClass = "text-sky-300"
-	goNumberClass  = "text-amber-300"
-	goFuncClass    = "text-violet-300"
-)
-
-var goKeywords = map[string]bool{
-	"break": true, "case": true, "chan": true, "const": true, "continue": true,
-	"default": true, "defer": true, "else": true, "fallthrough": true, "for": true,
-	"func": true, "go": true, "goto": true, "if": true, "import": true,
-	"interface": true, "map": true, "package": true, "range": true, "return": true,
-	"select": true, "struct": true, "switch": true, "type": true, "var": true,
-	// Not keywords to the Go spec — predeclared identifiers — but every editor colours
-	// them, and a reader looking for `nil` is looking for the same kind of thing.
-	"nil": true, "true": true, "false": true, "iota": true,
-	"string": true, "int": true, "int64": true, "float64": true, "bool": true,
-	"byte": true, "rune": true, "any": true, "error": true,
-}
-
-// HighlightGo turns Go source into HTML with the tokens wrapped in coloured spans.
-//
-// The result is meant for vdom.Raw inside a 
: it contains no block elements and
-// preserves the source's whitespace exactly, so the 
 does the layout.
-func HighlightGo(src string) string {
-	var b strings.Builder
-	b.Grow(len(src) * 2)
-
-	i := 0
-	for i < len(src) {
-		c := src[i]
-
-		switch {
-		// Line comment — including the //gowasm: directives, which are the most
-		// important line in several of these snippets.
-		case c == '/' && i+1 < len(src) && src[i+1] == '/':
-			end := strings.IndexByte(src[i:], '\n')
-			if end < 0 {
-				end = len(src)
-			} else {
-				end += i
-			}
-			span(&b, goCommentClass, src[i:end])
-			i = end
-
-		// Block comment.
-		case c == '/' && i+1 < len(src) && src[i+1] == '*':
-			end := strings.Index(src[i+2:], "*/")
-			if end < 0 {
-				end = len(src)
-			} else {
-				end = i + 2 + end + 2
-			}
-			span(&b, goCommentClass, src[i:end])
-			i = end
-
-		// Interpreted string. Ends at the closing quote or the line's end — an
-		// unterminated string is a typo in a snippet, not a reason to paint the rest of
-		// the file green.
-		case c == '"':
-			i = quoted(&b, src, i, '"', true)
-
-		// Raw string: no escapes, and it may span lines.
-		case c == '`':
-			i = quoted(&b, src, i, '`', false)
-
-		// Rune literal.
-		case c == '\'':
-			i = quoted(&b, src, i, '\'', true)
-
-		case isDigit(c):
-			j := i
-			for j < len(src) && (isDigit(src[j]) || isHexish(src[j])) {
-				j++
-			}
-			span(&b, goNumberClass, src[i:j])
-			i = j
-
-		case isIdentStart(c):
-			j := i
-			for j < len(src) && isIdentPart(src[j]) {
-				j++
-			}
-			word := src[i:j]
-			switch {
-			case goKeywords[word]:
-				span(&b, goKeywordClass, word)
-			case callAhead(src, j):
-				// An identifier immediately followed by "(" is being called (or is a type
-				// being converted to). Colouring it is what makes the shape of a snippet
-				// readable at a glance.
-				span(&b, goFuncClass, word)
-			default:
-				b.WriteString(html.EscapeString(word))
-			}
-			i = j
-
-		default:
-			b.WriteString(html.EscapeString(string(c)))
-			i++
-		}
-	}
-	return b.String()
-}
-
-// quoted consumes a quoted literal starting at i and writes it as a string span.
-// escapes reports whether a backslash escapes the next byte (false for raw strings).
-func quoted(b *strings.Builder, src string, i int, quote byte, escapes bool) int {
-	j := i + 1
-	for j < len(src) {
-		if escapes && src[j] == '\\' && j+1 < len(src) {
-			j += 2
-			continue
-		}
-		if src[j] == quote {
-			j++
-			break
-		}
-		if escapes && src[j] == '\n' {
-			break // unterminated: stop at the line end rather than eating the file
-		}
-		j++
-	}
-	span(b, goStringClass, src[i:j])
-	return j
-}
-
-// callAhead reports whether the next non-space byte at or after i is an opening paren.
-func callAhead(src string, i int) bool {
-	for i < len(src) && (src[i] == ' ' || src[i] == '\t') {
-		i++
-	}
-	return i < len(src) && src[i] == '('
-}
-
-func span(b *strings.Builder, class, text string) {
-	b.WriteString(``)
-	b.WriteString(html.EscapeString(text))
-	b.WriteString(``)
-}
-
-// isDigit already exists in the package (autotable.go) — reused rather than shadowed.
-
-func isHexish(c byte) bool {
-	return c == '.' || c == 'x' || c == 'X' || c == '_' ||
-		(c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
-}
-func isIdentStart(c byte) bool { return c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') }
-func isIdentPart(c byte) bool  { return isIdentStart(c) || isDigit(c) }
diff --git a/go/webui/general.go b/go/webui/general.go
index a9b9add7..a9456d2c 100644
--- a/go/webui/general.go
+++ b/go/webui/general.go
@@ -11,7 +11,7 @@ func PageContainer(children ...*vdom.VNode) *vdom.VNode {
 }
 
 // Divider is a thin horizontal rule.
-func Divider() *vdom.VNode { return vdom.Hr(vdom.Attr("class", "text-neutral-200 mt-1 mb-3")) }
+func Divider() *vdom.VNode { return vdom.Hr(vdom.Attr("class", "text-line mt-1 mb-3")) }
 
 // CodeBox renders a dark monospace code block.
 func CodeBox(code, class string) *vdom.VNode {
@@ -25,7 +25,7 @@ func PageHeader(text, class string) *vdom.VNode {
 	return vdom.Header(vdom.Attr("class", class),
 		vdom.Div(vdom.Attr("class", "mt-1"),
 			vdom.H1(vdom.Attr("class", "text-center text-2xl font-light text-ink mb-2"), vdom.Text(text)),
-			vdom.Hr(vdom.Attr("class", "text-neutral-200 mb-2")),
+			vdom.Hr(vdom.Attr("class", "text-line mb-2")),
 		),
 	)
 }