@vandenberghinc/volt 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vrepo +28 -0
- package/.vscode/tasks.json +87 -0
- package/README.md +67 -0
- package/backend/dist/cjs/blacklist.d.ts +10 -0
- package/backend/dist/cjs/blacklist.js +53 -0
- package/backend/dist/cjs/cli.d.ts +2 -0
- package/backend/dist/cjs/cli.js +263 -0
- package/backend/dist/cjs/database.d.ts +364 -0
- package/backend/dist/cjs/database.js +1962 -0
- package/backend/dist/cjs/endpoint.d.ts +57 -0
- package/backend/dist/cjs/endpoint.js +425 -0
- package/backend/dist/cjs/file_watcher.d.ts +44 -0
- package/backend/dist/cjs/file_watcher.js +348 -0
- package/backend/dist/cjs/frontend.d.ts +13 -0
- package/backend/dist/cjs/frontend.js +30 -0
- package/backend/dist/cjs/image_endpoint.d.ts +24 -0
- package/backend/dist/cjs/image_endpoint.js +210 -0
- package/backend/dist/cjs/logger.d.ts +5 -0
- package/backend/dist/cjs/logger.js +16 -0
- package/backend/dist/cjs/meta.d.ts +50 -0
- package/backend/dist/cjs/meta.js +153 -0
- package/backend/dist/cjs/mutex.d.ts +24 -0
- package/backend/dist/cjs/mutex.js +52 -0
- package/backend/dist/cjs/package.json +1 -0
- package/backend/dist/cjs/payments/paddle.d.ts +161 -0
- package/backend/dist/cjs/payments/paddle.js +2301 -0
- package/backend/dist/cjs/plugins/browser.d.ts +36 -0
- package/backend/dist/cjs/plugins/browser.js +183 -0
- package/backend/dist/cjs/plugins/communication.d.ts +70 -0
- package/backend/dist/cjs/plugins/communication.js +177 -0
- package/backend/dist/cjs/plugins/css.d.ts +10 -0
- package/backend/dist/cjs/plugins/css.js +71 -0
- package/backend/dist/cjs/plugins/mail.d.ts +277 -0
- package/backend/dist/cjs/plugins/mail.js +1419 -0
- package/backend/dist/cjs/plugins/pdf.d.ts +757 -0
- package/backend/dist/cjs/plugins/pdf.js +1694 -0
- package/backend/dist/cjs/plugins/thread_monitor.d.ts +18 -0
- package/backend/dist/cjs/plugins/thread_monitor.js +127 -0
- package/backend/dist/cjs/plugins/ts/compiler.d.ts +132 -0
- package/backend/dist/cjs/plugins/ts/compiler.js +944 -0
- package/backend/dist/cjs/plugins/ts/preprocessing.d.ts +14 -0
- package/backend/dist/cjs/plugins/ts/preprocessing.js +762 -0
- package/backend/dist/cjs/rate_limit.d.ts +65 -0
- package/backend/dist/cjs/rate_limit.js +463 -0
- package/backend/dist/cjs/request.deprc.d.ts +48 -0
- package/backend/dist/cjs/request.deprc.js +572 -0
- package/backend/dist/cjs/response.deprc.d.ts +55 -0
- package/backend/dist/cjs/response.deprc.js +275 -0
- package/backend/dist/cjs/server.d.ts +311 -0
- package/backend/dist/cjs/server.js +3475 -0
- package/backend/dist/cjs/splash_screen.d.ts +35 -0
- package/backend/dist/cjs/splash_screen.js +152 -0
- package/backend/dist/cjs/status.d.ts +60 -0
- package/backend/dist/cjs/status.js +199 -0
- package/backend/dist/cjs/stream.d.ts +75 -0
- package/backend/dist/cjs/stream.js +954 -0
- package/backend/dist/cjs/users.d.ts +111 -0
- package/backend/dist/cjs/users.js +1945 -0
- package/backend/dist/cjs/utils.d.ts +27 -0
- package/backend/dist/cjs/utils.js +329 -0
- package/backend/dist/cjs/view.d.ts +52 -0
- package/backend/dist/cjs/view.js +568 -0
- package/backend/dist/cjs/vinc.d.ts +2 -0
- package/backend/dist/cjs/vinc.dev.d.ts +2 -0
- package/backend/dist/cjs/vinc.dev.js +42 -0
- package/backend/dist/cjs/vinc.js +42 -0
- package/backend/dist/cjs/volt.d.ts +15 -0
- package/backend/dist/cjs/volt.js +64 -0
- package/backend/dist/css/adyen.css +92 -0
- package/backend/dist/css/volt.css +65 -0
- package/backend/dist/esm/blacklist.d.ts +10 -0
- package/backend/dist/esm/blacklist.js +49 -0
- package/backend/dist/esm/cli.d.ts +2 -0
- package/backend/dist/esm/cli.js +228 -0
- package/backend/dist/esm/database.d.ts +364 -0
- package/backend/dist/esm/database.js +1957 -0
- package/backend/dist/esm/endpoint.d.ts +57 -0
- package/backend/dist/esm/endpoint.js +421 -0
- package/backend/dist/esm/file_watcher.d.ts +44 -0
- package/backend/dist/esm/file_watcher.js +313 -0
- package/backend/dist/esm/frontend.d.ts +13 -0
- package/backend/dist/esm/frontend.js +27 -0
- package/backend/dist/esm/image_endpoint.d.ts +24 -0
- package/backend/dist/esm/image_endpoint.js +206 -0
- package/backend/dist/esm/logger.d.ts +5 -0
- package/backend/dist/esm/logger.js +13 -0
- package/backend/dist/esm/meta.d.ts +50 -0
- package/backend/dist/esm/meta.js +149 -0
- package/backend/dist/esm/mutex.d.ts +24 -0
- package/backend/dist/esm/mutex.js +48 -0
- package/backend/dist/esm/payments/paddle.d.ts +161 -0
- package/backend/dist/esm/payments/paddle.js +2261 -0
- package/backend/dist/esm/plugins/browser.d.ts +36 -0
- package/backend/dist/esm/plugins/browser.js +176 -0
- package/backend/dist/esm/plugins/communication.d.ts +70 -0
- package/backend/dist/esm/plugins/communication.js +169 -0
- package/backend/dist/esm/plugins/css.d.ts +10 -0
- package/backend/dist/esm/plugins/css.js +64 -0
- package/backend/dist/esm/plugins/mail.d.ts +277 -0
- package/backend/dist/esm/plugins/mail.js +1403 -0
- package/backend/dist/esm/plugins/pdf.d.ts +757 -0
- package/backend/dist/esm/plugins/pdf.js +1694 -0
- package/backend/dist/esm/plugins/thread_monitor.d.ts +18 -0
- package/backend/dist/esm/plugins/thread_monitor.js +120 -0
- package/backend/dist/esm/plugins/ts/compiler.d.ts +132 -0
- package/backend/dist/esm/plugins/ts/compiler.js +907 -0
- package/backend/dist/esm/plugins/ts/preprocessing.d.ts +14 -0
- package/backend/dist/esm/plugins/ts/preprocessing.js +724 -0
- package/backend/dist/esm/rate_limit.d.ts +65 -0
- package/backend/dist/esm/rate_limit.js +425 -0
- package/backend/dist/esm/request.deprc.d.ts +48 -0
- package/backend/dist/esm/request.deprc.js +572 -0
- package/backend/dist/esm/response.deprc.d.ts +55 -0
- package/backend/dist/esm/response.deprc.js +275 -0
- package/backend/dist/esm/server.d.ts +311 -0
- package/backend/dist/esm/server.js +3435 -0
- package/backend/dist/esm/splash_screen.d.ts +35 -0
- package/backend/dist/esm/splash_screen.js +148 -0
- package/backend/dist/esm/status.d.ts +60 -0
- package/backend/dist/esm/status.js +196 -0
- package/backend/dist/esm/stream.d.ts +75 -0
- package/backend/dist/esm/stream.js +947 -0
- package/backend/dist/esm/users.d.ts +111 -0
- package/backend/dist/esm/users.js +1908 -0
- package/backend/dist/esm/utils.d.ts +27 -0
- package/backend/dist/esm/utils.js +324 -0
- package/backend/dist/esm/view.d.ts +52 -0
- package/backend/dist/esm/view.js +561 -0
- package/backend/dist/esm/vinc.d.ts +2 -0
- package/backend/dist/esm/vinc.dev.d.ts +2 -0
- package/backend/dist/esm/vinc.dev.js +6 -0
- package/backend/dist/esm/vinc.js +6 -0
- package/backend/dist/esm/volt.d.ts +15 -0
- package/backend/dist/esm/volt.js +23 -0
- package/backend/dist/esm-dev/blacklist.d.ts +10 -0
- package/backend/dist/esm-dev/blacklist.js +49 -0
- package/backend/dist/esm-dev/cli.d.ts +2 -0
- package/backend/dist/esm-dev/cli.js +228 -0
- package/backend/dist/esm-dev/database.d.ts +364 -0
- package/backend/dist/esm-dev/database.js +1957 -0
- package/backend/dist/esm-dev/endpoint.d.ts +57 -0
- package/backend/dist/esm-dev/endpoint.js +421 -0
- package/backend/dist/esm-dev/file_watcher.d.ts +44 -0
- package/backend/dist/esm-dev/file_watcher.js +313 -0
- package/backend/dist/esm-dev/frontend.d.ts +13 -0
- package/backend/dist/esm-dev/frontend.js +27 -0
- package/backend/dist/esm-dev/image_endpoint.d.ts +24 -0
- package/backend/dist/esm-dev/image_endpoint.js +206 -0
- package/backend/dist/esm-dev/logger.d.ts +5 -0
- package/backend/dist/esm-dev/logger.js +13 -0
- package/backend/dist/esm-dev/meta.d.ts +50 -0
- package/backend/dist/esm-dev/meta.js +149 -0
- package/backend/dist/esm-dev/mutex.d.ts +24 -0
- package/backend/dist/esm-dev/mutex.js +48 -0
- package/backend/dist/esm-dev/payments/paddle.d.ts +161 -0
- package/backend/dist/esm-dev/payments/paddle.js +2261 -0
- package/backend/dist/esm-dev/plugins/browser.d.ts +36 -0
- package/backend/dist/esm-dev/plugins/browser.js +176 -0
- package/backend/dist/esm-dev/plugins/communication.d.ts +70 -0
- package/backend/dist/esm-dev/plugins/communication.js +169 -0
- package/backend/dist/esm-dev/plugins/css.d.ts +10 -0
- package/backend/dist/esm-dev/plugins/css.js +64 -0
- package/backend/dist/esm-dev/plugins/mail.d.ts +277 -0
- package/backend/dist/esm-dev/plugins/mail.js +1403 -0
- package/backend/dist/esm-dev/plugins/pdf.d.ts +757 -0
- package/backend/dist/esm-dev/plugins/pdf.js +1694 -0
- package/backend/dist/esm-dev/plugins/thread_monitor.d.ts +18 -0
- package/backend/dist/esm-dev/plugins/thread_monitor.js +120 -0
- package/backend/dist/esm-dev/plugins/ts/compiler.d.ts +132 -0
- package/backend/dist/esm-dev/plugins/ts/compiler.js +907 -0
- package/backend/dist/esm-dev/plugins/ts/preprocessing.d.ts +14 -0
- package/backend/dist/esm-dev/plugins/ts/preprocessing.js +724 -0
- package/backend/dist/esm-dev/rate_limit.d.ts +65 -0
- package/backend/dist/esm-dev/rate_limit.js +425 -0
- package/backend/dist/esm-dev/request.deprc.d.ts +48 -0
- package/backend/dist/esm-dev/request.deprc.js +572 -0
- package/backend/dist/esm-dev/response.deprc.d.ts +55 -0
- package/backend/dist/esm-dev/response.deprc.js +275 -0
- package/backend/dist/esm-dev/server.d.ts +311 -0
- package/backend/dist/esm-dev/server.js +3435 -0
- package/backend/dist/esm-dev/splash_screen.d.ts +35 -0
- package/backend/dist/esm-dev/splash_screen.js +148 -0
- package/backend/dist/esm-dev/status.d.ts +60 -0
- package/backend/dist/esm-dev/status.js +196 -0
- package/backend/dist/esm-dev/stream.d.ts +75 -0
- package/backend/dist/esm-dev/stream.js +947 -0
- package/backend/dist/esm-dev/users.d.ts +111 -0
- package/backend/dist/esm-dev/users.js +1908 -0
- package/backend/dist/esm-dev/utils.d.ts +27 -0
- package/backend/dist/esm-dev/utils.js +324 -0
- package/backend/dist/esm-dev/view.d.ts +52 -0
- package/backend/dist/esm-dev/view.js +561 -0
- package/backend/dist/esm-dev/vinc.d.ts +2 -0
- package/backend/dist/esm-dev/vinc.dev.d.ts +2 -0
- package/backend/dist/esm-dev/vinc.dev.js +6 -0
- package/backend/dist/esm-dev/vinc.js +6 -0
- package/backend/dist/esm-dev/volt.d.ts +15 -0
- package/backend/dist/esm-dev/volt.js +23 -0
- package/backend/src/blacklist.ts +69 -0
- package/backend/src/cli.js +245 -0
- package/backend/src/database.ts +2241 -0
- package/backend/src/endpoint.ts +494 -0
- package/backend/src/file_watcher.ts +359 -0
- package/backend/src/frontend.ts +35 -0
- package/backend/src/globals.d.ts +8 -0
- package/backend/src/image_endpoint.ts +258 -0
- package/backend/src/logger.ts +18 -0
- package/backend/src/meta.ts +202 -0
- package/backend/src/mutex.ts +51 -0
- package/backend/src/payments/paddle.ts +2659 -0
- package/backend/src/plugins/browser.ts +188 -0
- package/backend/src/plugins/communication.ts +204 -0
- package/backend/src/plugins/css.ts +84 -0
- package/backend/src/plugins/fonts/Menlo-Bold.ttf +0 -0
- package/backend/src/plugins/fonts/Menlo-Regular.ttf +0 -0
- package/backend/src/plugins/mail.ts +1720 -0
- package/backend/src/plugins/pdf.js +1932 -0
- package/backend/src/plugins/thread_monitor.ts +164 -0
- package/backend/src/plugins/ts/compiler.ts +1242 -0
- package/backend/src/plugins/ts/preprocessing.ts +812 -0
- package/backend/src/rate_limit.ts +503 -0
- package/backend/src/request.deprc.js +626 -0
- package/backend/src/response.deprc.js +354 -0
- package/backend/src/server.ts +4149 -0
- package/backend/src/splash_screen.ts +192 -0
- package/backend/src/status.ts +199 -0
- package/backend/src/stream.ts +1070 -0
- package/backend/src/users.ts +2077 -0
- package/backend/src/utils.ts +359 -0
- package/backend/src/view.ts +655 -0
- package/backend/src/vinc.dev.js +6 -0
- package/backend/src/vinc.ts +6 -0
- package/backend/src/volt.js +25 -0
- package/backend/tsconfig.cjs.json +29 -0
- package/backend/tsconfig.esm.dev.json +34 -0
- package/backend/tsconfig.esm.json +30 -0
- package/backend/tsconfig.json +2 -0
- package/frontend/compile.js +436 -0
- package/frontend/dist/elements/base.d.ts +9891 -0
- package/frontend/dist/elements/base.js +8818 -0
- package/frontend/dist/elements/module.d.ts +16 -0
- package/frontend/dist/elements/module.js +178 -0
- package/frontend/dist/modules/array.d.ts +37 -0
- package/frontend/dist/modules/array.js +284 -0
- package/frontend/dist/modules/auth.d.ts +45 -0
- package/frontend/dist/modules/auth.js +138 -0
- package/frontend/dist/modules/colors.d.ts +26 -0
- package/frontend/dist/modules/colors.js +340 -0
- package/frontend/dist/modules/compression.d.ts +6 -0
- package/frontend/dist/modules/compression.js +999 -0
- package/frontend/dist/modules/cookies.d.ts +17 -0
- package/frontend/dist/modules/cookies.js +166 -0
- package/frontend/dist/modules/date.d.ts +142 -0
- package/frontend/dist/modules/date.js +493 -0
- package/frontend/dist/modules/events.d.ts +7 -0
- package/frontend/dist/modules/events.js +90 -0
- package/frontend/dist/modules/google.d.ts +10 -0
- package/frontend/dist/modules/google.js +53 -0
- package/frontend/dist/modules/meta.d.ts +9 -0
- package/frontend/dist/modules/meta.js +45 -0
- package/frontend/dist/modules/mutex.d.ts +8 -0
- package/frontend/dist/modules/mutex.js +52 -0
- package/frontend/dist/modules/number.d.ts +12 -0
- package/frontend/dist/modules/number.js +8 -0
- package/frontend/dist/modules/object.d.ts +50 -0
- package/frontend/dist/modules/object.js +147 -0
- package/frontend/dist/modules/paddle.d.ts +1403 -0
- package/frontend/dist/modules/paddle.js +2641 -0
- package/frontend/dist/modules/scheme.d.ts +207 -0
- package/frontend/dist/modules/scheme.js +649 -0
- package/frontend/dist/modules/settings.d.ts +3 -0
- package/frontend/dist/modules/settings.js +4 -0
- package/frontend/dist/modules/statics.d.ts +4 -0
- package/frontend/dist/modules/statics.js +45 -0
- package/frontend/dist/modules/string.d.ts +163 -0
- package/frontend/dist/modules/string.js +291 -0
- package/frontend/dist/modules/support.d.ts +18 -0
- package/frontend/dist/modules/support.js +102 -0
- package/frontend/dist/modules/themes.d.ts +8 -0
- package/frontend/dist/modules/themes.js +17 -0
- package/frontend/dist/modules/user.d.ts +58 -0
- package/frontend/dist/modules/user.js +279 -0
- package/frontend/dist/modules/utils.d.ts +58 -0
- package/frontend/dist/modules/utils.js +1159 -0
- package/frontend/dist/types/gradient.d.ts +12 -0
- package/frontend/dist/types/gradient.js +79 -0
- package/frontend/dist/ui/border_button.d.ts +177 -0
- package/frontend/dist/ui/border_button.js +235 -0
- package/frontend/dist/ui/button.d.ts +42 -0
- package/frontend/dist/ui/button.js +114 -0
- package/frontend/dist/ui/canvas.d.ts +56 -0
- package/frontend/dist/ui/canvas.js +411 -0
- package/frontend/dist/ui/checkbox.d.ts +72 -0
- package/frontend/dist/ui/checkbox.js +277 -0
- package/frontend/dist/ui/code.d.ts +232 -0
- package/frontend/dist/ui/code.js +977 -0
- package/frontend/dist/ui/color.d.ts +1 -0
- package/frontend/dist/ui/color.js +110 -0
- package/frontend/dist/ui/context_menu.d.ts +30 -0
- package/frontend/dist/ui/context_menu.js +211 -0
- package/frontend/dist/ui/css.d.ts +10 -0
- package/frontend/dist/ui/css.js +44 -0
- package/frontend/dist/ui/divider.d.ts +18 -0
- package/frontend/dist/ui/divider.js +82 -0
- package/frontend/dist/ui/dropdown.d.ts +115 -0
- package/frontend/dist/ui/dropdown.js +446 -0
- package/frontend/dist/ui/for_each.d.ts +38 -0
- package/frontend/dist/ui/for_each.js +97 -0
- package/frontend/dist/ui/form.d.ts +25 -0
- package/frontend/dist/ui/form.js +227 -0
- package/frontend/dist/ui/frame_modes.d.ts +28 -0
- package/frontend/dist/ui/frame_modes.js +116 -0
- package/frontend/dist/ui/google_map.d.ts +31 -0
- package/frontend/dist/ui/google_map.js +111 -0
- package/frontend/dist/ui/gradient.d.ts +24 -0
- package/frontend/dist/ui/gradient.js +115 -0
- package/frontend/dist/ui/image.d.ts +138 -0
- package/frontend/dist/ui/image.js +570 -0
- package/frontend/dist/ui/input.d.ts +316 -0
- package/frontend/dist/ui/input.js +1187 -0
- package/frontend/dist/ui/link.d.ts +39 -0
- package/frontend/dist/ui/link.js +146 -0
- package/frontend/dist/ui/list.d.ts +33 -0
- package/frontend/dist/ui/list.js +161 -0
- package/frontend/dist/ui/loader_button.d.ts +108 -0
- package/frontend/dist/ui/loader_button.js +207 -0
- package/frontend/dist/ui/loaders.d.ts +60 -0
- package/frontend/dist/ui/loaders.js +150 -0
- package/frontend/dist/ui/popup.d.ts +84 -0
- package/frontend/dist/ui/popup.js +331 -0
- package/frontend/dist/ui/pseudo.d.ts +16 -0
- package/frontend/dist/ui/pseudo.js +81 -0
- package/frontend/dist/ui/scroller.d.ts +131 -0
- package/frontend/dist/ui/scroller.js +1251 -0
- package/frontend/dist/ui/slider.d.ts +35 -0
- package/frontend/dist/ui/slider.js +203 -0
- package/frontend/dist/ui/spacer.d.ts +20 -0
- package/frontend/dist/ui/spacer.js +83 -0
- package/frontend/dist/ui/span.d.ts +11 -0
- package/frontend/dist/ui/span.js +75 -0
- package/frontend/dist/ui/stack.d.ts +123 -0
- package/frontend/dist/ui/stack.js +344 -0
- package/frontend/dist/ui/steps.d.ts +72 -0
- package/frontend/dist/ui/steps.js +306 -0
- package/frontend/dist/ui/style.d.ts +12 -0
- package/frontend/dist/ui/style.js +78 -0
- package/frontend/dist/ui/switch.d.ts +44 -0
- package/frontend/dist/ui/switch.js +280 -0
- package/frontend/dist/ui/table.d.ts +118 -0
- package/frontend/dist/ui/table.js +411 -0
- package/frontend/dist/ui/tabs.d.ts +85 -0
- package/frontend/dist/ui/tabs.js +392 -0
- package/frontend/dist/ui/text.d.ts +19 -0
- package/frontend/dist/ui/text.js +88 -0
- package/frontend/dist/ui/theme.d.ts +25 -0
- package/frontend/dist/ui/theme.js +237 -0
- package/frontend/dist/ui/title.d.ts +36 -0
- package/frontend/dist/ui/title.js +127 -0
- package/frontend/dist/ui/ui.d.ts +38 -0
- package/frontend/dist/ui/ui.js +41 -0
- package/frontend/dist/ui/view.d.ts +25 -0
- package/frontend/dist/ui/view.js +93 -0
- package/frontend/dist/volt.d.ts +22 -0
- package/frontend/dist/volt.js +27 -0
- package/frontend/exports.json +1340 -0
- package/frontend/src/css/adyen.css +92 -0
- package/frontend/src/css/volt.css +65 -0
- package/frontend/src/elements/base.ts +16790 -0
- package/frontend/src/elements/module.ts +184 -0
- package/frontend/src/elements/types.d.ts +155 -0
- package/frontend/src/modules/array.ts +366 -0
- package/frontend/src/modules/auth.ts +188 -0
- package/frontend/src/modules/colors.ts +449 -0
- package/frontend/src/modules/compression.ts +67 -0
- package/frontend/src/modules/cookies.ts +182 -0
- package/frontend/src/modules/date.js +535 -0
- package/frontend/src/modules/date.ts +583 -0
- package/frontend/src/modules/events.ts +96 -0
- package/frontend/src/modules/google.ts +60 -0
- package/frontend/src/modules/meta.ts +59 -0
- package/frontend/src/modules/mutex.ts +59 -0
- package/frontend/src/modules/number.ts +20 -0
- package/frontend/src/modules/object.ts +212 -0
- package/frontend/src/modules/paddle.ts +2990 -0
- package/frontend/src/modules/scheme.ts +740 -0
- package/frontend/src/modules/settings.ts +5 -0
- package/frontend/src/modules/statics.ts +47 -0
- package/frontend/src/modules/string.ts +500 -0
- package/frontend/src/modules/support.ts +118 -0
- package/frontend/src/modules/themes.ts +24 -0
- package/frontend/src/modules/user.ts +321 -0
- package/frontend/src/modules/utils.ts +1260 -0
- package/frontend/src/static/admin/admin.png +0 -0
- package/frontend/src/static/admin/password.webp +0 -0
- package/frontend/src/static/icons/copy.webp +0 -0
- package/frontend/src/static/payments/arrow.long.webp +0 -0
- package/frontend/src/static/payments/arrow.long2.webp +0 -0
- package/frontend/src/static/payments/cancelled.webp +0 -0
- package/frontend/src/static/payments/check.sign.webp +0 -0
- package/frontend/src/static/payments/check.webp +0 -0
- package/frontend/src/static/payments/close.webp +0 -0
- package/frontend/src/static/payments/error.webp +0 -0
- package/frontend/src/static/payments/exclamation.webp +0 -0
- package/frontend/src/static/payments/minus.webp +0 -0
- package/frontend/src/static/payments/party.webp +0 -0
- package/frontend/src/static/payments/plus.webp +0 -0
- package/frontend/src/static/payments/shopping_cart.webp +0 -0
- package/frontend/src/static/payments/trash.webp +0 -0
- package/frontend/src/types/global.d.ts +4 -0
- package/frontend/src/types/gradient.ts +87 -0
- package/frontend/src/ui/any_element.d.ts +5 -0
- package/frontend/src/ui/border_button.ts +320 -0
- package/frontend/src/ui/button.ts +62 -0
- package/frontend/src/ui/canvas.ts +431 -0
- package/frontend/src/ui/checkbox.ts +284 -0
- package/frontend/src/ui/code.ts +1049 -0
- package/frontend/src/ui/color.ts +117 -0
- package/frontend/src/ui/context_menu.ts +194 -0
- package/frontend/src/ui/css.ts +57 -0
- package/frontend/src/ui/divider.ts +28 -0
- package/frontend/src/ui/dropdown.ts +503 -0
- package/frontend/src/ui/for_each.ts +71 -0
- package/frontend/src/ui/form.ts +208 -0
- package/frontend/src/ui/frame_modes.ts +140 -0
- package/frontend/src/ui/google_map.ts +70 -0
- package/frontend/src/ui/gradient.ts +73 -0
- package/frontend/src/ui/image.ts +587 -0
- package/frontend/src/ui/input.ts +1284 -0
- package/frontend/src/ui/link.ts +77 -0
- package/frontend/src/ui/list.ts +88 -0
- package/frontend/src/ui/loader_button.ts +192 -0
- package/frontend/src/ui/loaders.ts +126 -0
- package/frontend/src/ui/popup.ts +370 -0
- package/frontend/src/ui/pseudo.ts +33 -0
- package/frontend/src/ui/scroller.ts +1324 -0
- package/frontend/src/ui/slider.ts +215 -0
- package/frontend/src/ui/spacer.ts +29 -0
- package/frontend/src/ui/span.ts +23 -0
- package/frontend/src/ui/stack.ts +238 -0
- package/frontend/src/ui/steps.ts +334 -0
- package/frontend/src/ui/style.ts +26 -0
- package/frontend/src/ui/switch.ts +286 -0
- package/frontend/src/ui/table.ts +323 -0
- package/frontend/src/ui/tabs.ts +441 -0
- package/frontend/src/ui/text.ts +38 -0
- package/frontend/src/ui/theme.ts +279 -0
- package/frontend/src/ui/title.ts +64 -0
- package/frontend/src/ui/ui.ts +47 -0
- package/frontend/src/ui/view.ts +44 -0
- package/frontend/src/volt.ts +31 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1260 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Author: Daan van den Bergh
|
|
3
|
+
* Copyright: © 2022 - 2024 Daan van den Bergh.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Utils module.
|
|
7
|
+
const Utils = {
|
|
8
|
+
is_apple: navigator.vendor.includes('Apple') as boolean,
|
|
9
|
+
is_safari: navigator.vendor.includes('Apple') as boolean,
|
|
10
|
+
|
|
11
|
+
/* @docs:
|
|
12
|
+
@nav: Frontend
|
|
13
|
+
@chapter: Utils
|
|
14
|
+
@title: Check if value is a string
|
|
15
|
+
@desc: Determine whether the provided value is a string.
|
|
16
|
+
@param:
|
|
17
|
+
@name: value
|
|
18
|
+
@description The value to check.
|
|
19
|
+
@type: any
|
|
20
|
+
@return:
|
|
21
|
+
@description Returns true if the value is a string, otherwise false.
|
|
22
|
+
@type: boolean
|
|
23
|
+
*/
|
|
24
|
+
is_string(value: any): value is string {
|
|
25
|
+
return typeof value === 'string' || value instanceof String;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/* @docs:
|
|
29
|
+
@nav: Frontend
|
|
30
|
+
@chapter: Utils
|
|
31
|
+
@title: Check if value is numeric
|
|
32
|
+
@desc: Determine whether the provided value is a finite number.
|
|
33
|
+
@param:
|
|
34
|
+
@name: value
|
|
35
|
+
@description The value to check.
|
|
36
|
+
@type: any
|
|
37
|
+
@return:
|
|
38
|
+
@description Returns true if the value is a finite number, otherwise false.
|
|
39
|
+
@type: boolean
|
|
40
|
+
*/
|
|
41
|
+
is_numeric(value: any): value is number {
|
|
42
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/* @docs:
|
|
46
|
+
@nav: Frontend
|
|
47
|
+
@chapter: Utils
|
|
48
|
+
@title: Check if value is an integer
|
|
49
|
+
@desc: Determine whether the provided value is an integer.
|
|
50
|
+
@param:
|
|
51
|
+
@name: value
|
|
52
|
+
@description The value to check.
|
|
53
|
+
@type: any
|
|
54
|
+
@return:
|
|
55
|
+
@description Returns true if the value is an integer, otherwise false.
|
|
56
|
+
@type: boolean
|
|
57
|
+
*/
|
|
58
|
+
is_int(value: any): value is number {
|
|
59
|
+
return typeof value === 'number' && Number.isInteger(value);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/* @docs:
|
|
63
|
+
@nav: Frontend
|
|
64
|
+
@chapter: Utils
|
|
65
|
+
@title: Check if value is a float
|
|
66
|
+
@desc: Determine whether the provided value is a floating-point number.
|
|
67
|
+
@param:
|
|
68
|
+
@name: value
|
|
69
|
+
@description The value to check.
|
|
70
|
+
@type: any
|
|
71
|
+
@return:
|
|
72
|
+
@description Returns true if the value is a float, otherwise false.
|
|
73
|
+
@type: boolean
|
|
74
|
+
*/
|
|
75
|
+
is_float(value: any): value is number {
|
|
76
|
+
return typeof value === 'number' && !Number.isNaN(value) && !Number.isInteger(value);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/* @docs:
|
|
80
|
+
@nav: Frontend
|
|
81
|
+
@chapter: Utils
|
|
82
|
+
@title: Check if value is a function
|
|
83
|
+
@desc: Determine whether the provided value is a function.
|
|
84
|
+
@param:
|
|
85
|
+
@name: value
|
|
86
|
+
@description The value to check.
|
|
87
|
+
@type: any
|
|
88
|
+
@return:
|
|
89
|
+
@description Returns true if the value is a function, otherwise false.
|
|
90
|
+
@type: boolean
|
|
91
|
+
*/
|
|
92
|
+
is_func(value: any): value is Function {
|
|
93
|
+
return typeof value === 'function';
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/* @docs:
|
|
97
|
+
@nav: Frontend
|
|
98
|
+
@chapter: Utils
|
|
99
|
+
@title: Check if value is an array
|
|
100
|
+
@desc: Determine whether the provided value is an array.
|
|
101
|
+
@param:
|
|
102
|
+
@name: value
|
|
103
|
+
@description The value to check.
|
|
104
|
+
@type: any
|
|
105
|
+
@return:
|
|
106
|
+
@description Returns true if the value is an array, otherwise false.
|
|
107
|
+
@type: boolean
|
|
108
|
+
*/
|
|
109
|
+
is_array(value: any): value is Array<any> {
|
|
110
|
+
return Array.isArray(value);
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/* @docs:
|
|
114
|
+
@nav: Frontend
|
|
115
|
+
@chapter: Utils
|
|
116
|
+
@title: Check if value is an object
|
|
117
|
+
@desc: Determine whether the provided value is a non-array object.
|
|
118
|
+
@param:
|
|
119
|
+
@name: value
|
|
120
|
+
@description The value to check.
|
|
121
|
+
@type: any
|
|
122
|
+
@return:
|
|
123
|
+
@description Returns true if the value is an object and not an array, otherwise false.
|
|
124
|
+
@type: boolean
|
|
125
|
+
*/
|
|
126
|
+
is_obj(value: any): value is object {
|
|
127
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/* @docs:
|
|
131
|
+
@nav: Frontend
|
|
132
|
+
@chapter: Utils
|
|
133
|
+
@title: Check if number is even
|
|
134
|
+
@desc: Determine whether the provided number is even.
|
|
135
|
+
@param:
|
|
136
|
+
@name: number
|
|
137
|
+
@description The number to check.
|
|
138
|
+
@type: number
|
|
139
|
+
@return:
|
|
140
|
+
@description Returns true if the number is even, otherwise false.
|
|
141
|
+
@type: boolean
|
|
142
|
+
*/
|
|
143
|
+
is_even(number: number): boolean {
|
|
144
|
+
return number % 2 === 0;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
/* @docs:
|
|
148
|
+
@nav: Frontend
|
|
149
|
+
@chapter: Utils
|
|
150
|
+
@title: Is Mobile
|
|
151
|
+
@desc: Check if the user agent is a mobile device.
|
|
152
|
+
*/
|
|
153
|
+
is_mobile(): boolean {
|
|
154
|
+
return (
|
|
155
|
+
!!navigator.userAgent.match(/Android/i) ||
|
|
156
|
+
!!navigator.userAgent.match(/webOS/i) ||
|
|
157
|
+
!!navigator.userAgent.match(/iPhone/i) ||
|
|
158
|
+
!!navigator.userAgent.match(/iPad/i) ||
|
|
159
|
+
!!navigator.userAgent.match(/iPod/i) ||
|
|
160
|
+
!!navigator.userAgent.match(/BlackBerry/i) ||
|
|
161
|
+
!!navigator.userAgent.match(/Windows Phone/i)
|
|
162
|
+
);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/* @docs:
|
|
166
|
+
@nav: Frontend
|
|
167
|
+
@chapter: Utils
|
|
168
|
+
@title: Make Immutable
|
|
169
|
+
@desc:
|
|
170
|
+
Make all objects of an array or object immutable. All nested objects will also be made immutable recursively.
|
|
171
|
+
@param:
|
|
172
|
+
@name: object
|
|
173
|
+
@desc: The array or object to freeze.
|
|
174
|
+
@type: array | object
|
|
175
|
+
*/
|
|
176
|
+
make_immutable(object: any): any {
|
|
177
|
+
if (Array.isArray(object)) {
|
|
178
|
+
object.forEach((item, index) => {
|
|
179
|
+
if (item !== null && typeof item === "object") {
|
|
180
|
+
object[index] = Utils.make_immutable(item);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
Object.freeze(object);
|
|
184
|
+
}
|
|
185
|
+
else if (object !== null && typeof object === "object") {
|
|
186
|
+
Object.keys(object).forEach((key) => {
|
|
187
|
+
if (object[key] !== null && typeof object[key] === "object") {
|
|
188
|
+
object[key] = Utils.make_immutable(object[key]);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
Object.freeze(object);
|
|
192
|
+
}
|
|
193
|
+
return object;
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
/* @docs:
|
|
197
|
+
@nav: Frontend
|
|
198
|
+
@chapter: Utils
|
|
199
|
+
@title: Is child
|
|
200
|
+
@desc:
|
|
201
|
+
Check if an element is a direct child of an element or the parent element itself.
|
|
202
|
+
@param:
|
|
203
|
+
@name: parent
|
|
204
|
+
@desc: The parent element to test.
|
|
205
|
+
@type: Node | Element
|
|
206
|
+
@param:
|
|
207
|
+
@name: target
|
|
208
|
+
@desc: The target element to test.
|
|
209
|
+
@type: Node | Element
|
|
210
|
+
*/
|
|
211
|
+
is_child(parent: any, target: any): boolean {
|
|
212
|
+
for (let i = 0; i < parent.children.length; i++) {
|
|
213
|
+
if (target === parent.children[i]) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
/* @docs:
|
|
221
|
+
@nav: Frontend
|
|
222
|
+
@chapter: Utils
|
|
223
|
+
@title: Is child recursively
|
|
224
|
+
@desc:
|
|
225
|
+
Check if an element is a recursively nested child of an element or the parent element itself.
|
|
226
|
+
@param:
|
|
227
|
+
@name: parent
|
|
228
|
+
@desc: The parent element to test.
|
|
229
|
+
@type: Node | Element
|
|
230
|
+
@param:
|
|
231
|
+
@name: target
|
|
232
|
+
@desc: The target element to test.
|
|
233
|
+
@type: Node | Element
|
|
234
|
+
@param:
|
|
235
|
+
@name: stop_node
|
|
236
|
+
@desc: A node at which to stop checking if target is a parent of the current element.
|
|
237
|
+
@type: Node | Element | null
|
|
238
|
+
*/
|
|
239
|
+
is_nested_child(parent: any, target: any, stop_node: any = null): boolean {
|
|
240
|
+
let e: Element | null = target instanceof Element ? target : null;
|
|
241
|
+
while (e != null) {
|
|
242
|
+
if (e === parent) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
else if (e === stop_node) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
e = e.parentElement;
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
// Equals.
|
|
254
|
+
// eq(x, y) { return x == y; }
|
|
255
|
+
// not_eq(x, y) { return x != y; }
|
|
256
|
+
|
|
257
|
+
// Greater than.
|
|
258
|
+
// gt(x, y) { return x > y; }
|
|
259
|
+
// gt_eq(x, y) { return x >= y; }
|
|
260
|
+
|
|
261
|
+
// Lesser than.
|
|
262
|
+
// lt(x, y) { return x < y; }
|
|
263
|
+
// lt_eq(x, y) { return x <= y; }
|
|
264
|
+
|
|
265
|
+
/* @docs:
|
|
266
|
+
@nav: Frontend
|
|
267
|
+
@chapter: Utils
|
|
268
|
+
@title: Round to decimals
|
|
269
|
+
@desc: Round a number to a specified number of decimal places.
|
|
270
|
+
@param:
|
|
271
|
+
@name: value
|
|
272
|
+
@desc: The number to round.
|
|
273
|
+
@type: number
|
|
274
|
+
@name: decimals
|
|
275
|
+
@desc: The number of decimal places.
|
|
276
|
+
@type: number
|
|
277
|
+
@return:
|
|
278
|
+
@desc: The rounded number.
|
|
279
|
+
@type: number
|
|
280
|
+
*/
|
|
281
|
+
round(value: number, decimals: number): number {
|
|
282
|
+
const factor = 10 ** decimals;
|
|
283
|
+
return Math.round(value * factor) / factor;
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
/* @docs:
|
|
287
|
+
@nav: Frontend
|
|
288
|
+
@chapter: Utils
|
|
289
|
+
@title: Get device width
|
|
290
|
+
@desc: Get the width of the device's viewport.
|
|
291
|
+
@return:
|
|
292
|
+
@desc: The width of the device's viewport.
|
|
293
|
+
@type: number
|
|
294
|
+
*/
|
|
295
|
+
device_width(): number {
|
|
296
|
+
return (window.innerWidth > 0) ? window.innerWidth : screen.width;
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
/* @docs:
|
|
300
|
+
@nav: Frontend
|
|
301
|
+
@chapter: Utils
|
|
302
|
+
@title: Get device height
|
|
303
|
+
@desc: Get the height of the device's viewport.
|
|
304
|
+
@return:
|
|
305
|
+
@desc: The height of the device's viewport.
|
|
306
|
+
@type: number
|
|
307
|
+
*/
|
|
308
|
+
device_height(): number {
|
|
309
|
+
return (window.innerHeight > 0) ? window.innerHeight : screen.height;
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
/* @docs:
|
|
313
|
+
@nav: Frontend
|
|
314
|
+
@chapter: Utils
|
|
315
|
+
@title: Get endpoint
|
|
316
|
+
@desc: Get the endpoint sub URL of a full domain URL. When parameter "url" is undefined, it uses the current URL.
|
|
317
|
+
@param:
|
|
318
|
+
@name: url
|
|
319
|
+
@desc: The full domain URL.
|
|
320
|
+
@type: string | null
|
|
321
|
+
@return:
|
|
322
|
+
@desc: The endpoint sub URL.
|
|
323
|
+
@type: string
|
|
324
|
+
*/
|
|
325
|
+
endpoint(url: string | null = null): string {
|
|
326
|
+
if (url == null) {
|
|
327
|
+
return Utils.endpoint(window.location.href);
|
|
328
|
+
} else {
|
|
329
|
+
// Strip http:// or https://
|
|
330
|
+
let endpoint = url.replace(/^https?:\/\//, "");
|
|
331
|
+
|
|
332
|
+
// Remove domain.
|
|
333
|
+
const firstSlash = endpoint.indexOf('/');
|
|
334
|
+
endpoint = firstSlash !== -1 ? endpoint.substring(firstSlash) : '/';
|
|
335
|
+
|
|
336
|
+
// Strip query.
|
|
337
|
+
const queryIndex = endpoint.indexOf("?");
|
|
338
|
+
if (queryIndex !== -1) {
|
|
339
|
+
endpoint = endpoint.substring(0, queryIndex);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Clean.
|
|
343
|
+
endpoint = endpoint.replaceAll("//", "/");
|
|
344
|
+
|
|
345
|
+
// Remove trailing slashes.
|
|
346
|
+
if (endpoint.length === 0) {
|
|
347
|
+
return '/';
|
|
348
|
+
} else {
|
|
349
|
+
while (endpoint.length > 1 && endpoint.endsWith('/')) {
|
|
350
|
+
endpoint = endpoint.slice(0, -1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return endpoint;
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
// Get style name for vendor prefix.
|
|
358
|
+
// get_vendor_prefix_property(property: string, style: CSSStyleDeclaration): string {
|
|
359
|
+
// if (Utils.vendor_prefix_cache[property]) {
|
|
360
|
+
// return Utils.vendor_prefix_cache[property];
|
|
361
|
+
// }
|
|
362
|
+
// const vendors = ['webkit', 'moz', 'ms', 'o'];
|
|
363
|
+
// for (let i = 0; i < vendors.length; i++) {
|
|
364
|
+
// let vendor_property = "-";
|
|
365
|
+
// vendor_property += vendors[i];
|
|
366
|
+
// vendor_property += "-";
|
|
367
|
+
// vendor_property += property;
|
|
368
|
+
// if (property in style) {
|
|
369
|
+
// Utils.vendor_prefix_cache[property] = vendor_property;
|
|
370
|
+
// return vendor_property;
|
|
371
|
+
// }
|
|
372
|
+
// }
|
|
373
|
+
// Utils.vendor_prefix_cache[property] = property;
|
|
374
|
+
// return property;
|
|
375
|
+
// }
|
|
376
|
+
|
|
377
|
+
/* @docs:
|
|
378
|
+
@nav: Frontend
|
|
379
|
+
@chapter: Utils
|
|
380
|
+
@title: Redirect
|
|
381
|
+
@desc: Redirect to a specified URL, optionally forcing the redirect even if the endpoint is the same.
|
|
382
|
+
@param:
|
|
383
|
+
@name: url
|
|
384
|
+
@desc: The URL to redirect to.
|
|
385
|
+
@type: string
|
|
386
|
+
@name: forced
|
|
387
|
+
@desc: Whether to force the redirect even if the current endpoint is the same as the target URL.
|
|
388
|
+
@type: boolean
|
|
389
|
+
*/
|
|
390
|
+
redirect(url: string, forced: boolean = false): void {
|
|
391
|
+
if (forced || Utils.endpoint() !== url) {
|
|
392
|
+
window.location.href = url;
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
/* @docs:
|
|
397
|
+
@nav: Frontend
|
|
398
|
+
@chapter: Utils
|
|
399
|
+
@title: Delay
|
|
400
|
+
@desc: Delay the execution of a function by a specified number of milliseconds.
|
|
401
|
+
@param:
|
|
402
|
+
@name: mseconds
|
|
403
|
+
@desc: The number of milliseconds to delay.
|
|
404
|
+
@type: number
|
|
405
|
+
@name: func
|
|
406
|
+
@desc: The function to execute after the delay.
|
|
407
|
+
@type: () => void
|
|
408
|
+
*/
|
|
409
|
+
delay(mseconds: number, func: () => void): void {
|
|
410
|
+
setTimeout(() => func(), mseconds);
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
/* @docs:
|
|
414
|
+
@nav: Frontend
|
|
415
|
+
@chapter: Utils
|
|
416
|
+
@title: Get URL parameter
|
|
417
|
+
@desc: Get a URL parameter by name, with an optional default value.
|
|
418
|
+
@param:
|
|
419
|
+
@name: name
|
|
420
|
+
@desc: The name of the URL parameter.
|
|
421
|
+
@type: string
|
|
422
|
+
@name: def
|
|
423
|
+
@desc: The default value to return if the parameter is not found.
|
|
424
|
+
@type: any | null
|
|
425
|
+
@return:
|
|
426
|
+
@desc: The value of the URL parameter or the default value.
|
|
427
|
+
@type: any | null
|
|
428
|
+
*/
|
|
429
|
+
url_param(name: string, def: any = null): any | null {
|
|
430
|
+
const params = new URLSearchParams(window.location.search);
|
|
431
|
+
const param = params.get(name);
|
|
432
|
+
if (param == null || param === "") {
|
|
433
|
+
return def;
|
|
434
|
+
}
|
|
435
|
+
switch (param.toLowerCase()) {
|
|
436
|
+
case "true": return true;
|
|
437
|
+
case "false": return false;
|
|
438
|
+
case "null": return null;
|
|
439
|
+
default: return param;
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
/* @docs:
|
|
444
|
+
@nav: Frontend
|
|
445
|
+
@chapter: Utils
|
|
446
|
+
@title: URL Encode
|
|
447
|
+
@desc: Encode an object into a URL-encoded query string.
|
|
448
|
+
@param:
|
|
449
|
+
@name: params
|
|
450
|
+
@desc: The parameters to encode.
|
|
451
|
+
@type: Record<string, any>
|
|
452
|
+
@return:
|
|
453
|
+
@desc: The URL-encoded query string.
|
|
454
|
+
@type: string
|
|
455
|
+
*/
|
|
456
|
+
url_encode(params: Record<string, any>): string {
|
|
457
|
+
const encodedParams: string[] = [];
|
|
458
|
+
Object.keys(params).forEach((key) => {
|
|
459
|
+
const encodedKey = encodeURIComponent(key);
|
|
460
|
+
const encodedValue = encodeURIComponent(params[key]);
|
|
461
|
+
encodedParams.push(`${encodedKey}=${encodedValue}`);
|
|
462
|
+
});
|
|
463
|
+
return encodedParams.join('&');
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
/* @docs:
|
|
467
|
+
@nav: Frontend
|
|
468
|
+
@chapter: Utils
|
|
469
|
+
@title: Copy to Clipboard
|
|
470
|
+
@desc: Copy text to the clipboard.
|
|
471
|
+
@param:
|
|
472
|
+
@name: text
|
|
473
|
+
@desc: The text to copy.
|
|
474
|
+
@type: string
|
|
475
|
+
@return:
|
|
476
|
+
@desc: A Promise that resolves when the text is copied.
|
|
477
|
+
@type: Promise<void>
|
|
478
|
+
*/
|
|
479
|
+
async copy_to_clipboard(text: string): Promise<void> {
|
|
480
|
+
return new Promise<void>((resolve, reject) => {
|
|
481
|
+
navigator.clipboard.writeText(text)
|
|
482
|
+
.then(() => {
|
|
483
|
+
resolve();
|
|
484
|
+
})
|
|
485
|
+
.catch((err) => {
|
|
486
|
+
reject(err);
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
// Get the brightness of a hex color (0.0 white 1.0 dark).
|
|
492
|
+
// @deprecated moved to `Colors`
|
|
493
|
+
hex_brightness(color: string): number {
|
|
494
|
+
// Remove the hash symbol if present
|
|
495
|
+
color = color.replace(/^#/, '');
|
|
496
|
+
|
|
497
|
+
// Convert hex to RGB
|
|
498
|
+
const bigint = parseInt(color, 16);
|
|
499
|
+
const r = (bigint >> 16) & 255;
|
|
500
|
+
const g = (bigint >> 8) & 255;
|
|
501
|
+
const b = bigint & 255;
|
|
502
|
+
|
|
503
|
+
// Calculate perceived brightness using the relative luminance formula
|
|
504
|
+
const brightness = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
505
|
+
return brightness;
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
// Hex to rgbA.
|
|
509
|
+
// @deprecated moved to `Colors`
|
|
510
|
+
hex_to_rgb(hex: string): { r: number, g: number, b: number, a: number } {
|
|
511
|
+
let index = hex.indexOf("#");
|
|
512
|
+
if (index !== -1) {
|
|
513
|
+
hex = hex.substr(index + 1);
|
|
514
|
+
}
|
|
515
|
+
let r = parseInt(hex.substring(0, 2), 16);
|
|
516
|
+
let g = parseInt(hex.substring(2, 4), 16);
|
|
517
|
+
let b = parseInt(hex.substring(4, 6), 16);
|
|
518
|
+
let a = 1;
|
|
519
|
+
if (hex.length > 6) {
|
|
520
|
+
a = parseInt(hex.substring(6, 8)) / 100;
|
|
521
|
+
}
|
|
522
|
+
return { r, g, b, a };
|
|
523
|
+
},
|
|
524
|
+
|
|
525
|
+
/* @docs:
|
|
526
|
+
@nav: Frontend
|
|
527
|
+
@chapter: Utils
|
|
528
|
+
@title: Deep copy
|
|
529
|
+
@desc: Perform a deep copy on any type, it does not support classes, only primitive objects.
|
|
530
|
+
@param:
|
|
531
|
+
@name: obj
|
|
532
|
+
@desc: The object to deep copy.
|
|
533
|
+
@type: any
|
|
534
|
+
@return:
|
|
535
|
+
@desc: The deep copied object.
|
|
536
|
+
@type: any
|
|
537
|
+
*/
|
|
538
|
+
deep_copy(obj: any): any {
|
|
539
|
+
if (Array.isArray(obj)) {
|
|
540
|
+
const copy: any[] = [];
|
|
541
|
+
obj.forEach((item) => {
|
|
542
|
+
copy.push(Utils.deep_copy(item));
|
|
543
|
+
});
|
|
544
|
+
return copy;
|
|
545
|
+
}
|
|
546
|
+
else if (obj !== null && obj instanceof String) {
|
|
547
|
+
return new String(obj.toString());
|
|
548
|
+
}
|
|
549
|
+
else if (obj !== null && typeof obj === "object") {
|
|
550
|
+
const copy: Record<string, any> = {};
|
|
551
|
+
const keys = Object.keys(obj);
|
|
552
|
+
const values = Object.values(obj);
|
|
553
|
+
for (let i = 0; i < keys.length; i++) {
|
|
554
|
+
copy[keys[i]] = Utils.deep_copy(values[i]);
|
|
555
|
+
}
|
|
556
|
+
return copy;
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
return obj;
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
/* @docs:
|
|
564
|
+
@nav: Frontend
|
|
565
|
+
@chapter: Utils
|
|
566
|
+
@title: Request
|
|
567
|
+
@desc: Make an HTTP request with the specified options.
|
|
568
|
+
@param:
|
|
569
|
+
@name: options
|
|
570
|
+
@desc: The request options.
|
|
571
|
+
@type: {
|
|
572
|
+
method?: string,
|
|
573
|
+
url?: string | null,
|
|
574
|
+
data?: any,
|
|
575
|
+
json?: boolean,
|
|
576
|
+
credentials?: 'include' | 'same-origin' | 'omit',
|
|
577
|
+
headers?: Record<string, string>,
|
|
578
|
+
}
|
|
579
|
+
@return:
|
|
580
|
+
@desc: A Promise that resolves with the response data.
|
|
581
|
+
@type: Promise<any>
|
|
582
|
+
*/
|
|
583
|
+
request(options: {
|
|
584
|
+
method?: string,
|
|
585
|
+
url?: string | null,
|
|
586
|
+
data?: any,
|
|
587
|
+
json?: boolean,
|
|
588
|
+
credentials?: 'include' | 'same-origin' | 'omit',
|
|
589
|
+
headers?: Record<string, string>,
|
|
590
|
+
}): Promise<any> {
|
|
591
|
+
const {
|
|
592
|
+
method = "GET",
|
|
593
|
+
url = null,
|
|
594
|
+
data = null,
|
|
595
|
+
json = true,
|
|
596
|
+
credentials = "same-origin",
|
|
597
|
+
headers = {},
|
|
598
|
+
} = options;
|
|
599
|
+
|
|
600
|
+
// Set headers.
|
|
601
|
+
// Host and User-Agent headers are restricted and set by the browser itself.
|
|
602
|
+
if (json && data !== null && headers['Content-Type'] == null) {
|
|
603
|
+
headers['Content-Type'] = 'application/json';
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Handle data.
|
|
607
|
+
let finalUrl = url;
|
|
608
|
+
let bodyData: string | undefined = data !== null ? data : undefined;
|
|
609
|
+
|
|
610
|
+
if (data !== null && typeof data === "object") {
|
|
611
|
+
if (method.toUpperCase() === "GET") {
|
|
612
|
+
finalUrl = `${url}?${new URLSearchParams(data).toString()}`;
|
|
613
|
+
bodyData = undefined;
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
// Stringify.
|
|
617
|
+
bodyData = JSON.stringify(data);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Define options.
|
|
622
|
+
const fetchOptions: RequestInit = {
|
|
623
|
+
method,
|
|
624
|
+
credentials,
|
|
625
|
+
headers,
|
|
626
|
+
body: bodyData,
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
return new Promise<any>((resolve, reject) => {
|
|
630
|
+
fetch(finalUrl!, fetchOptions)
|
|
631
|
+
.then(response => {
|
|
632
|
+
// Handle error code.
|
|
633
|
+
if (!response.ok) {
|
|
634
|
+
// Parse as json.
|
|
635
|
+
if (json) {
|
|
636
|
+
const clone = response.clone();
|
|
637
|
+
response.json().then(data => {
|
|
638
|
+
if (data.status === undefined) {
|
|
639
|
+
data.status = response.status;
|
|
640
|
+
}
|
|
641
|
+
reject(data);
|
|
642
|
+
}).catch(err => {
|
|
643
|
+
clone.text()
|
|
644
|
+
.then(data => {
|
|
645
|
+
reject({
|
|
646
|
+
error: data,
|
|
647
|
+
status: response.status
|
|
648
|
+
});
|
|
649
|
+
})
|
|
650
|
+
.catch(text_err => {
|
|
651
|
+
reject({
|
|
652
|
+
error: err,
|
|
653
|
+
status: response.status
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
// Reject.
|
|
659
|
+
else {
|
|
660
|
+
reject({
|
|
661
|
+
error: response.statusText,
|
|
662
|
+
status: response.status
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
return; // stop.
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Successful response.
|
|
669
|
+
if (json) {
|
|
670
|
+
response.json().then(data => {
|
|
671
|
+
resolve(data);
|
|
672
|
+
}).catch(err => {
|
|
673
|
+
console.log("Response:", response);
|
|
674
|
+
reject({
|
|
675
|
+
error: 'Failed to parse JSON response: ' + err.message,
|
|
676
|
+
status: response.status
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
} else {
|
|
680
|
+
response.text().then(data => {
|
|
681
|
+
resolve(data);
|
|
682
|
+
}).catch(err => {
|
|
683
|
+
console.log("Response:", response);
|
|
684
|
+
reject({
|
|
685
|
+
error: 'Failed to parse text response: ' + err.message,
|
|
686
|
+
status: response.status
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
})
|
|
691
|
+
.catch(error => {
|
|
692
|
+
reject({ error: error.message }); // Reject with error message if fetch fails
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
},
|
|
696
|
+
|
|
697
|
+
/*
|
|
698
|
+
request(options: {
|
|
699
|
+
method?: string,
|
|
700
|
+
url?: string | null,
|
|
701
|
+
data?: any,
|
|
702
|
+
async?: boolean,
|
|
703
|
+
success?: (status: number, data: any) => void,
|
|
704
|
+
error?: (status: number, error: any) => void,
|
|
705
|
+
before?: () => void,
|
|
706
|
+
}): Promise<any> {
|
|
707
|
+
// Original commented out implementation.
|
|
708
|
+
}
|
|
709
|
+
*/
|
|
710
|
+
|
|
711
|
+
/* @docs:
|
|
712
|
+
@nav: Frontend
|
|
713
|
+
@chapter: Utils
|
|
714
|
+
@title: On load
|
|
715
|
+
@desc: Execute a function when the content is loaded, optionally handling a splash screen.
|
|
716
|
+
@param:
|
|
717
|
+
@name: func
|
|
718
|
+
@desc: The function to execute when the content is loaded.
|
|
719
|
+
@type: () => HTMLElement | Promise<HTMLElement> | null
|
|
720
|
+
@return:
|
|
721
|
+
@desc: void
|
|
722
|
+
@type: void
|
|
723
|
+
*/
|
|
724
|
+
async on_load(func: () => HTMLElement | Promise<HTMLElement> | null): Promise<void> {
|
|
725
|
+
// document.addEventListener("DOMContentLoaded", async () => {
|
|
726
|
+
const splash = document.getElementById("__volt_splash_screen");
|
|
727
|
+
if (splash != null) {
|
|
728
|
+
splash.remove();
|
|
729
|
+
}
|
|
730
|
+
let e = func();
|
|
731
|
+
if (e instanceof Promise) {
|
|
732
|
+
try {
|
|
733
|
+
e = await e;
|
|
734
|
+
} catch (err) {
|
|
735
|
+
console.error(err);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
if (e != null && e instanceof HTMLElement) {
|
|
740
|
+
document.body.appendChild(e);
|
|
741
|
+
}
|
|
742
|
+
// });
|
|
743
|
+
},
|
|
744
|
+
|
|
745
|
+
/* @docs:
|
|
746
|
+
@nav: Frontend
|
|
747
|
+
@chapter: Utils
|
|
748
|
+
@title: Unix to Date
|
|
749
|
+
@desc: Convert a Unix timestamp in seconds or milliseconds to the user's date format.
|
|
750
|
+
@param:
|
|
751
|
+
@name: unix
|
|
752
|
+
@desc: The Unix timestamp.
|
|
753
|
+
@type: number
|
|
754
|
+
@name: mseconds
|
|
755
|
+
@desc: Optional. Whether the Unix timestamp is in milliseconds.
|
|
756
|
+
@type: boolean | null
|
|
757
|
+
@return:
|
|
758
|
+
@desc: The formatted date string.
|
|
759
|
+
@type: string
|
|
760
|
+
*/
|
|
761
|
+
unix_to_date(unix: number, mseconds: boolean | null = null): string {
|
|
762
|
+
// Guess msec or sec.
|
|
763
|
+
if (mseconds === null) {
|
|
764
|
+
// As of now, Unix time in milliseconds is 13 digits and in seconds is 10 digits
|
|
765
|
+
const str = unix.toString();
|
|
766
|
+
if (str.length === 13) {
|
|
767
|
+
mseconds = true;
|
|
768
|
+
} else if (str.length === 10) {
|
|
769
|
+
mseconds = false;
|
|
770
|
+
} else {
|
|
771
|
+
// Future-proofing: When second-based timestamps eventually reach 11 digits
|
|
772
|
+
if (str.length > 10 && str.length < 13) {
|
|
773
|
+
// Check if adding three zeroes (to simulate milliseconds) results in a plausible future date
|
|
774
|
+
// This is a rough estimation and might not be accurate
|
|
775
|
+
const futureCheck = new Date(parseInt(str + "000", 10));
|
|
776
|
+
if (futureCheck.getFullYear() > new Date().getFullYear() && futureCheck.getFullYear() < 3000) {
|
|
777
|
+
mseconds = false;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Format.
|
|
784
|
+
const date = new Date(mseconds ? unix : unix * 1000);
|
|
785
|
+
const lang = navigator.language || (navigator as any).userLanguage;
|
|
786
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
787
|
+
let options: Intl.DateTimeFormatOptions = {
|
|
788
|
+
year: "numeric",
|
|
789
|
+
month: "2-digit",
|
|
790
|
+
day: "2-digit",
|
|
791
|
+
timeZone: tz,
|
|
792
|
+
};
|
|
793
|
+
const date_format = new Intl.DateTimeFormat(lang, options).format(date);
|
|
794
|
+
options = {
|
|
795
|
+
hour: "2-digit",
|
|
796
|
+
minute: "2-digit",
|
|
797
|
+
second: "2-digit",
|
|
798
|
+
hour12: lang.toLowerCase().includes("en"),
|
|
799
|
+
timeZone: tz,
|
|
800
|
+
};
|
|
801
|
+
const time_format = new Intl.DateTimeFormat(lang, options).format(date);
|
|
802
|
+
return `${date_format} ${time_format}`;
|
|
803
|
+
},
|
|
804
|
+
|
|
805
|
+
/* @docs:
|
|
806
|
+
@nav: Frontend
|
|
807
|
+
@chapter: Utils
|
|
808
|
+
@title: Fuzzy Search
|
|
809
|
+
@description:
|
|
810
|
+
Perform a fuzzy similarity match between a query and an array of targets.
|
|
811
|
+
@type: number
|
|
812
|
+
@return:
|
|
813
|
+
Returns an array with the targets sorted from best match to lowest match, unless parameter `get_matches` is enabled.
|
|
814
|
+
@param:
|
|
815
|
+
@name: query
|
|
816
|
+
@description: The search query.
|
|
817
|
+
@type: string
|
|
818
|
+
@name: targets
|
|
819
|
+
@description:
|
|
820
|
+
The targets to search through.
|
|
821
|
+
When the nested items are objects, define the parameter `key` to specify the query string key.
|
|
822
|
+
When the nested items are arrays, the first value of the array will be used as the query string.
|
|
823
|
+
@type: Array<string | object | any[]>
|
|
824
|
+
@name: limit
|
|
825
|
+
@description: Limit the number of results. Define the limit as `null` or `-1` to set no limit.
|
|
826
|
+
@type: number
|
|
827
|
+
@name: case_match
|
|
828
|
+
@description:
|
|
829
|
+
When the `case_match` flag is enabled, the similarity match is case sensitive.
|
|
830
|
+
@type: boolean
|
|
831
|
+
@name: allow_exceeding_chars
|
|
832
|
+
@description:
|
|
833
|
+
Allow matches where the single character count of the search query exceeds that of the target.
|
|
834
|
+
For example, when the query is "aa" and the target is "a", no match will be given since the "a" count of the target (2) is higher than the query (1).
|
|
835
|
+
@type: boolean
|
|
836
|
+
@name: get_matches
|
|
837
|
+
@description:
|
|
838
|
+
When the `get_matches` flag is enabled, the function returns an array with nested arrays containing the similarity match `[similarity <number>, <target>]`.
|
|
839
|
+
@type: boolean
|
|
840
|
+
@name: key
|
|
841
|
+
@description: The key for the query string when the array's target items are objects. The key may also be an array with keys to use the best match of the key's value.
|
|
842
|
+
@type: string | string[] | null
|
|
843
|
+
@name: nested_key
|
|
844
|
+
@description:
|
|
845
|
+
When the target items are objects and the object may have nested children that also should be searched, define the `nested_key` parameter to specify the key used for the nested children.
|
|
846
|
+
The value for the nested key should also be an array of objects and use the same structure for the `key` parameter, otherwise it will cause undefined behavior.
|
|
847
|
+
The nested key will be ignored if the nested key does not exist in a target object.
|
|
848
|
+
@type: string | null
|
|
849
|
+
*/
|
|
850
|
+
fuzzy_search({
|
|
851
|
+
query,
|
|
852
|
+
targets = [],
|
|
853
|
+
limit = 25,
|
|
854
|
+
case_match = false,
|
|
855
|
+
allow_exceeding_chars = true,
|
|
856
|
+
get_matches = false,
|
|
857
|
+
key = null,
|
|
858
|
+
nested_key = null,
|
|
859
|
+
}: {
|
|
860
|
+
query: string,
|
|
861
|
+
targets?: Array<string | object | any[]>,
|
|
862
|
+
limit?: number,
|
|
863
|
+
case_match?: boolean,
|
|
864
|
+
allow_exceeding_chars?: boolean,
|
|
865
|
+
get_matches?: boolean,
|
|
866
|
+
key?: string | string[] | null,
|
|
867
|
+
nested_key?: string | null,
|
|
868
|
+
}): Array<any> | Array<[number, any]> {
|
|
869
|
+
// Checks.
|
|
870
|
+
if (query == null) {
|
|
871
|
+
throw new Error("Define parameter \"query\".");
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// Vars.
|
|
875
|
+
const is_obj = targets.length > 0 && typeof targets[0] === "object" && !Array.isArray(targets[0]);
|
|
876
|
+
const is_array = targets.length > 0 && Array.isArray(targets[0]);
|
|
877
|
+
if (is_obj && key == null) { key = "query"; }
|
|
878
|
+
const is_key_array = Array.isArray(key);
|
|
879
|
+
const results: Array<[number, any]> = [];
|
|
880
|
+
if (!case_match) { query = query.toLowerCase(); }
|
|
881
|
+
|
|
882
|
+
// Calculate the similarities.
|
|
883
|
+
const calc_sims = (current_targets: Array<any> = []) => {
|
|
884
|
+
current_targets.forEach(target => {
|
|
885
|
+
let match: number | null;
|
|
886
|
+
if (is_array) {
|
|
887
|
+
match = Utils.fuzzy_match(
|
|
888
|
+
query,
|
|
889
|
+
case_match ? target[0] : (typeof target[0] === 'string' ? target[0].toLowerCase() : target[0]),
|
|
890
|
+
allow_exceeding_chars
|
|
891
|
+
);
|
|
892
|
+
} else if (is_obj) {
|
|
893
|
+
if (is_key_array && Array.isArray(key)) {
|
|
894
|
+
let min_match: number | null = null;
|
|
895
|
+
(key as string[]).forEach(k => {
|
|
896
|
+
if (target[k] == null) { return; }
|
|
897
|
+
const current_match = Utils.fuzzy_match(
|
|
898
|
+
query,
|
|
899
|
+
case_match ? target[k] : (typeof target[k] === 'string' ? target[k].toLowerCase() : target[k]),
|
|
900
|
+
allow_exceeding_chars
|
|
901
|
+
);
|
|
902
|
+
if (current_match != null && (min_match === null || current_match < min_match)) {
|
|
903
|
+
min_match = current_match;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
match = min_match;
|
|
907
|
+
} else {
|
|
908
|
+
if (target[key as string] == null) { return; }
|
|
909
|
+
match = Utils.fuzzy_match(
|
|
910
|
+
query,
|
|
911
|
+
case_match ? target[key as string] : (typeof target[key as string] === 'string' ? target[key as string].toLowerCase() : target[key as string]),
|
|
912
|
+
allow_exceeding_chars
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
if (nested_key !== null && (target as any)[nested_key] != null) {
|
|
916
|
+
calc_sims((target as any)[nested_key]);
|
|
917
|
+
}
|
|
918
|
+
} else {
|
|
919
|
+
if (target == null) { return; }
|
|
920
|
+
match = Utils.fuzzy_match(
|
|
921
|
+
query,
|
|
922
|
+
case_match ? target : (typeof target === 'string' ? target.toLowerCase() : target),
|
|
923
|
+
allow_exceeding_chars
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
if (match !== null) {
|
|
927
|
+
results.push([match, target]);
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
// Calculate the similarities.
|
|
933
|
+
calc_sims(targets);
|
|
934
|
+
|
|
935
|
+
// Sort the results.
|
|
936
|
+
results.sort((a, b) => a[0] - b[0]); // Lower score is better
|
|
937
|
+
|
|
938
|
+
// Limit the results.
|
|
939
|
+
if (limit !== null && limit >= 0 && results.length > limit) {
|
|
940
|
+
results.length = limit;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Convert the results to targets only.
|
|
944
|
+
if (!get_matches) {
|
|
945
|
+
return results.map(item => item[1]);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Return the results.
|
|
949
|
+
return results;
|
|
950
|
+
},
|
|
951
|
+
|
|
952
|
+
/* @docs:
|
|
953
|
+
@nav: Frontend
|
|
954
|
+
@chapter: Utils
|
|
955
|
+
@title: Fuzzy Match
|
|
956
|
+
@desc: Perform a fuzzy similarity match between a query and a target.
|
|
957
|
+
@param:
|
|
958
|
+
@name: search
|
|
959
|
+
@desc: The search query.
|
|
960
|
+
@type: string
|
|
961
|
+
@name: target
|
|
962
|
+
@desc: The target string.
|
|
963
|
+
@type: string
|
|
964
|
+
@name: allow_exceeding_chars
|
|
965
|
+
@desc:
|
|
966
|
+
Allow matches where the single character count of the search query exceeds that of the target.
|
|
967
|
+
So when the query is "aa" and the target is "a" then no match will be given since the "a" count of the target (2) is higher than the query (1).
|
|
968
|
+
The function returns `null` when this flag is enabled and detected.
|
|
969
|
+
@type: boolean
|
|
970
|
+
@return:
|
|
971
|
+
@desc: Returns a floating number indicating the similarity; a lower value represents a better match.
|
|
972
|
+
@type: number | null
|
|
973
|
+
*/
|
|
974
|
+
fuzzy_match(search: string, target: string, allow_exceeding_chars: boolean = true): number | null {
|
|
975
|
+
// Check exceeding chars.
|
|
976
|
+
if (!allow_exceeding_chars) {
|
|
977
|
+
// Exceeding length.
|
|
978
|
+
if (search.length > target.length) {
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Create the target count.
|
|
983
|
+
const text_count: Record<string, number> = {};
|
|
984
|
+
for (let i = 0; i < target.length; i++) {
|
|
985
|
+
const c = target.charAt(i);
|
|
986
|
+
text_count[c] = (text_count[c] || 0) + 1;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// Create the query count.
|
|
990
|
+
const query_count: Record<string, number> = {};
|
|
991
|
+
for (let i = 0; i < search.length; i++) {
|
|
992
|
+
const c = search.charAt(i);
|
|
993
|
+
query_count[c] = (query_count[c] || 0) + 1;
|
|
994
|
+
if (text_count[c] == null || query_count[c] > text_count[c]) {
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// Wrappers.
|
|
1001
|
+
const get_search_code = (index: number): number => {
|
|
1002
|
+
if (index >= 0 && index < search.length) {
|
|
1003
|
+
return search.charCodeAt(index);
|
|
1004
|
+
}
|
|
1005
|
+
return -1;
|
|
1006
|
+
};
|
|
1007
|
+
const get_target_code = (index: number): number => {
|
|
1008
|
+
if (index >= 0 && index < target.length) {
|
|
1009
|
+
return target.charCodeAt(index);
|
|
1010
|
+
}
|
|
1011
|
+
return -1;
|
|
1012
|
+
};
|
|
1013
|
+
const prepareBeginningIndexes = (targetStr: string): number[] => {
|
|
1014
|
+
const targetLen = targetStr.length;
|
|
1015
|
+
const beginningIndexes: number[] = [];
|
|
1016
|
+
let wasUpper = false;
|
|
1017
|
+
let wasAlphanum = false;
|
|
1018
|
+
for (let i = 0; i < targetLen; ++i) {
|
|
1019
|
+
const targetCode = targetStr.charCodeAt(i);
|
|
1020
|
+
const isUpper = targetCode >= 65 && targetCode <= 90;
|
|
1021
|
+
const isAlphanum = isUpper || (targetCode >= 97 && targetCode <= 122) || (targetCode >= 48 && targetCode <= 57);
|
|
1022
|
+
const isBeginning = (isUpper && !wasUpper) || !wasAlphanum || !isAlphanum;
|
|
1023
|
+
wasUpper = isUpper;
|
|
1024
|
+
wasAlphanum = isAlphanum;
|
|
1025
|
+
if (isBeginning) beginningIndexes.push(i);
|
|
1026
|
+
}
|
|
1027
|
+
return beginningIndexes;
|
|
1028
|
+
};
|
|
1029
|
+
const prepareNextBeginningIndexes = (targetStr: string): number[] => {
|
|
1030
|
+
const targetLen = targetStr.length;
|
|
1031
|
+
const beginningIndexes = prepareBeginningIndexes(targetStr);
|
|
1032
|
+
const nextBeginningIndexes: number[] = [];
|
|
1033
|
+
let lastIsBeginning = beginningIndexes[0];
|
|
1034
|
+
let lastIsBeginningI = 0;
|
|
1035
|
+
for (let i = 0; i < targetLen; ++i) {
|
|
1036
|
+
if (lastIsBeginning > i) {
|
|
1037
|
+
nextBeginningIndexes[i] = lastIsBeginning;
|
|
1038
|
+
} else {
|
|
1039
|
+
lastIsBeginning = beginningIndexes[++lastIsBeginningI];
|
|
1040
|
+
nextBeginningIndexes[i] = lastIsBeginning === undefined ? targetLen : lastIsBeginning;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return nextBeginningIndexes;
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
// Vars.
|
|
1047
|
+
let searchI = 0;
|
|
1048
|
+
const searchLen = search.length;
|
|
1049
|
+
let searchCode = get_search_code(searchI);
|
|
1050
|
+
let searchLower = search.toLowerCase();
|
|
1051
|
+
let targetI = 0;
|
|
1052
|
+
const targetLen = target.length;
|
|
1053
|
+
let targetCode = get_target_code(targetI);
|
|
1054
|
+
const targetLower = target.toLowerCase();
|
|
1055
|
+
const matchesSimple: number[] = [];
|
|
1056
|
+
let matchesSimpleLen = 0;
|
|
1057
|
+
let successStrict = false;
|
|
1058
|
+
const matchesStrict: number[] = [];
|
|
1059
|
+
let matchesStrictLen = 0;
|
|
1060
|
+
|
|
1061
|
+
// Very basic fuzzy match; to remove non-matching targets ASAP!
|
|
1062
|
+
// Walk through target. Find sequential matches.
|
|
1063
|
+
// If all chars aren't found then exit
|
|
1064
|
+
while (true) {
|
|
1065
|
+
const isMatch = searchCode === get_target_code(targetI);
|
|
1066
|
+
if (isMatch) {
|
|
1067
|
+
matchesSimple[matchesSimpleLen++] = targetI;
|
|
1068
|
+
searchI++;
|
|
1069
|
+
if (searchI === searchLen) break;
|
|
1070
|
+
searchCode = get_search_code(searchI);
|
|
1071
|
+
}
|
|
1072
|
+
targetI++;
|
|
1073
|
+
if (targetI >= targetLen) {
|
|
1074
|
+
return null; // Failed to find searchI
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
searchI = 0;
|
|
1079
|
+
targetI = 0;
|
|
1080
|
+
|
|
1081
|
+
const nextBeginningIndexes = prepareNextBeginningIndexes(target);
|
|
1082
|
+
targetI = matchesSimple[0] === 0 ? 0 : (nextBeginningIndexes[matchesSimple[0] - 1] || 0);
|
|
1083
|
+
|
|
1084
|
+
// More advanced and strict test to improve the score
|
|
1085
|
+
let backtrackCount = 0;
|
|
1086
|
+
if (targetI !== targetLen) {
|
|
1087
|
+
while (true) {
|
|
1088
|
+
if (targetI >= targetLen) {
|
|
1089
|
+
// We failed to find a good spot for this search char, go back to the previous search char and force it forward
|
|
1090
|
+
if (searchI <= 0) break; // We failed to push chars forward for a better match
|
|
1091
|
+
backtrackCount++;
|
|
1092
|
+
if (backtrackCount > 200) break; // Prevent excessive backtracking
|
|
1093
|
+
searchI--;
|
|
1094
|
+
const lastMatch = matchesStrict[--matchesStrictLen];
|
|
1095
|
+
targetI = nextBeginningIndexes[lastMatch] || 0;
|
|
1096
|
+
} else {
|
|
1097
|
+
const isMatch = get_search_code(searchI) === get_target_code(targetI);
|
|
1098
|
+
if (isMatch) {
|
|
1099
|
+
matchesStrict[matchesStrictLen++] = targetI;
|
|
1100
|
+
searchI++;
|
|
1101
|
+
if (searchI === searchLen) { successStrict = true; break; }
|
|
1102
|
+
targetI++;
|
|
1103
|
+
} else {
|
|
1104
|
+
targetI = nextBeginningIndexes[targetI] || targetLen;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// Check if it's a substring match
|
|
1111
|
+
const substringIndex = targetLower.indexOf(searchLower, matchesSimple[0]); // Performance: this is slow
|
|
1112
|
+
const isSubstring = substringIndex !== -1;
|
|
1113
|
+
let isSubstringBeginning = false;
|
|
1114
|
+
if (isSubstring && !successStrict) { // Rewrite the indexes from basic to the substring
|
|
1115
|
+
for (let i = 0; i < matchesSimpleLen; ++i) {
|
|
1116
|
+
matchesSimple[i] = substringIndex + i;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
if (isSubstring) {
|
|
1120
|
+
isSubstringBeginning = nextBeginningIndexes[substringIndex - 1] === substringIndex;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// Tally up the score & keep track of matches for highlighting later
|
|
1124
|
+
let score = 0;
|
|
1125
|
+
let matchesBest: number[];
|
|
1126
|
+
let matchesBestLen: number;
|
|
1127
|
+
|
|
1128
|
+
if (successStrict) {
|
|
1129
|
+
matchesBest = matchesStrict;
|
|
1130
|
+
matchesBestLen = matchesStrictLen;
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
matchesBest = matchesSimple;
|
|
1134
|
+
matchesBestLen = matchesSimpleLen;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
let extraMatchGroupCount = 0;
|
|
1138
|
+
for (let i = 1; i < searchLen; ++i) {
|
|
1139
|
+
if (matchesBest[i] - matchesBest[i - 1] !== 1) {
|
|
1140
|
+
score -= matchesBest[i];
|
|
1141
|
+
extraMatchGroupCount++;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
const unmatchedDistance = matchesBest[searchLen - 1] - matchesBest[0] - (searchLen - 1);
|
|
1145
|
+
score -= (12 + unmatchedDistance) * extraMatchGroupCount; // Penalty for more groups
|
|
1146
|
+
|
|
1147
|
+
if (matchesBest[0] !== 0) score -= Math.pow(matchesBest[0], 2) * 0.2; // Penalty for not starting near the beginning
|
|
1148
|
+
|
|
1149
|
+
if (!successStrict) {
|
|
1150
|
+
score *= 1000;
|
|
1151
|
+
} else {
|
|
1152
|
+
// successStrict on a target with too many beginning indexes loses points for being a bad target
|
|
1153
|
+
let uniqueBeginningIndexes = 1;
|
|
1154
|
+
for (let i = nextBeginningIndexes[0]; i < targetLen; i = nextBeginningIndexes[i]) {
|
|
1155
|
+
uniqueBeginningIndexes++;
|
|
1156
|
+
}
|
|
1157
|
+
if (uniqueBeginningIndexes > 24) score *= (uniqueBeginningIndexes - 24) * 10; // Arbitrary penalty
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (isSubstring) score /= 1 + Math.pow(searchLen, 2); // Bonus for being a full substring
|
|
1161
|
+
if (isSubstringBeginning) score /= 1 + Math.pow(searchLen, 2); // Bonus for substring starting on a beginningIndex
|
|
1162
|
+
|
|
1163
|
+
score -= targetLen - searchLen; // Penalty for longer targets
|
|
1164
|
+
|
|
1165
|
+
return score;
|
|
1166
|
+
},
|
|
1167
|
+
|
|
1168
|
+
/* @docs:
|
|
1169
|
+
@nav: Frontend
|
|
1170
|
+
@chapter: Utils
|
|
1171
|
+
@title: Debounce
|
|
1172
|
+
@desc: Create a debounced version of a function that delays invoking it until after a specified delay.
|
|
1173
|
+
@param:
|
|
1174
|
+
@name: delay
|
|
1175
|
+
@desc: The number of milliseconds to delay.
|
|
1176
|
+
@type: number
|
|
1177
|
+
@name: func
|
|
1178
|
+
@desc: The function to debounce.
|
|
1179
|
+
@type: (...args: any[]) => void
|
|
1180
|
+
@return:
|
|
1181
|
+
@desc: The debounced function.
|
|
1182
|
+
@type: (...args: any[]) => void
|
|
1183
|
+
*/
|
|
1184
|
+
debounce(delay: number, func: (...args: any[]) => void): (...args: any[]) => void {
|
|
1185
|
+
let timeout: number | undefined;
|
|
1186
|
+
return function(this: any, ...args: any[]) {
|
|
1187
|
+
if (timeout !== undefined) {
|
|
1188
|
+
clearTimeout(timeout);
|
|
1189
|
+
}
|
|
1190
|
+
timeout = window.setTimeout(() => func.apply(this, args), delay);
|
|
1191
|
+
};
|
|
1192
|
+
},
|
|
1193
|
+
|
|
1194
|
+
// Create the on render observer.
|
|
1195
|
+
on_render_observer: new ResizeObserver(
|
|
1196
|
+
(entries, observer) => {
|
|
1197
|
+
entries.forEach(entry => {
|
|
1198
|
+
const target = entry.target as any
|
|
1199
|
+
if (!target.rendered) {
|
|
1200
|
+
target._on_render_callbacks.iterate((func) => {func(entry.target)});
|
|
1201
|
+
target.rendered = true;
|
|
1202
|
+
(Utils.on_render_observer as ResizeObserver).unobserve(entry.target);
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
},
|
|
1206
|
+
),
|
|
1207
|
+
|
|
1208
|
+
// Create the on resize observer.
|
|
1209
|
+
on_resize_observer: new ResizeObserver(
|
|
1210
|
+
(entries, observer) => {
|
|
1211
|
+
entries.forEach(entry => {
|
|
1212
|
+
(entry.target as any)._on_resize_callbacks.iterate((func) => {func(entry.target)});
|
|
1213
|
+
});
|
|
1214
|
+
},
|
|
1215
|
+
),
|
|
1216
|
+
|
|
1217
|
+
// Aggregate multiple classes into a single class, can be used to extend more than one class.
|
|
1218
|
+
// aggregate(
|
|
1219
|
+
// BaseClass: new (...args: any[]) => any,
|
|
1220
|
+
// ...Mixins: Array<new (...args: any[]) => any>
|
|
1221
|
+
// ) {
|
|
1222
|
+
// class AggregatedClass extends BaseClass {
|
|
1223
|
+
// constructor(...args: any[]) {
|
|
1224
|
+
// super(...args);
|
|
1225
|
+
// // Additional initialization if needed
|
|
1226
|
+
// }
|
|
1227
|
+
// }
|
|
1228
|
+
|
|
1229
|
+
// // Copy methods and properties from mixin classes to the AggregatedClass prototype
|
|
1230
|
+
// Mixins.forEach(MixinClass => {
|
|
1231
|
+
|
|
1232
|
+
// // Copy instance methods and properties
|
|
1233
|
+
// Object.getOwnPropertyNames(MixinClass.prototype).forEach(name => {
|
|
1234
|
+
// if (name !== 'constructor') {
|
|
1235
|
+
// Object.defineProperty(
|
|
1236
|
+
// AggregatedClass.prototype,
|
|
1237
|
+
// name,
|
|
1238
|
+
// Object.getOwnPropertyDescriptor(MixinClass.prototype, name)!
|
|
1239
|
+
// );
|
|
1240
|
+
// }
|
|
1241
|
+
// });
|
|
1242
|
+
|
|
1243
|
+
// // Copy static methods and properties if needed
|
|
1244
|
+
// Object.getOwnPropertyNames(MixinClass).forEach(name => {
|
|
1245
|
+
// if (name !== 'prototype' && name !== 'name' && name !== 'length') {
|
|
1246
|
+
// Object.defineProperty(
|
|
1247
|
+
// AggregatedClass,
|
|
1248
|
+
// name,
|
|
1249
|
+
// Object.getOwnPropertyDescriptor(MixinClass, name)!
|
|
1250
|
+
// );
|
|
1251
|
+
// }
|
|
1252
|
+
// });
|
|
1253
|
+
// });
|
|
1254
|
+
|
|
1255
|
+
// return AggregatedClass;
|
|
1256
|
+
// },
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1259
|
+
// Export.
|
|
1260
|
+
export { Utils };
|