From 4e4a0c5de1b8fc78a1ebaa559d7f99f66d646d8a Mon Sep 17 00:00:00 2001 From: Yoshihiro OKUMURA Date: Mon, 25 May 2026 00:16:32 +0900 Subject: [PATCH] build: migrate from Create React App to Vite - Replace CRA with Vite as the build tool - Rename index.tsx to main.tsx (Vite convention) - Add vite.config.ts, tsconfig.app.json, tsconfig.node.json - Move index.html from public/ to project root - Replace yarn.lock with package-lock.json - Remove CRA-specific files (reportWebVitals, setupTests, react-app-env.d.ts) - Update package.json, tsconfig.json, .gitignore, and README.md - Adjust all source files for Vite compatibility --- .agents/skills/git-commit/SKILL.md | 38 + .../references/conventional_commits.md | 60 + .gitignore | 60 +- README.md | 74 +- biome.json | 28 + eslint.config.js | 42 + public/index.html => index.html | 11 +- package-lock.json | 3594 +++++++ package.json | 91 +- src/App.test.tsx | 10 - src/App.tsx | 30 +- src/brainexplorer/BrainExplorer.tsx | 46 +- .../BrainExplorerIndex.module.css | 52 +- src/brainexplorer/BrainExplorerIndex.tsx | 191 +- src/brainexplorer/lib/g3d.ts | 1975 ++-- src/brainexplorer/lib/m3d.ts | 1154 ++- src/common/AppRoot.tsx | 62 +- src/common/XoopsPathRedirect.tsx | 50 +- src/common/assets/xoops.css | 79 +- src/common/lib/LangFlag.tsx | 44 +- src/common/lib/Loading.tsx | 14 +- src/common/lib/NoticeSiteHasBeenArchived.tsx | 15 +- src/common/lib/PageNotFound.tsx | 81 +- src/common/lib/XoopsCode.tsx | 218 +- src/config.ts | 18 +- src/credits/Credits.tsx | 66 +- src/credits/CreditsIndex.tsx | 101 +- src/credits/CreditsPage.tsx | 105 +- src/credits/blocks/CreditsMenu.tsx | 75 +- src/credits/lib/CreditsUtils.ts | 102 +- src/custom-types.d.ts | 24 + src/custom/Footer.tsx | 100 +- src/custom/Header.tsx | 138 +- src/custom/Page.tsx | 246 +- src/custom/assets/style.css | 11 +- src/custom/blocks/BulletinBoard.tsx | 34 +- src/custom/blocks/HowToUseLinks.tsx | 66 +- src/custom/blocks/HowToUseVideo.tsx | 109 +- src/custom/blocks/Information.tsx | 42 +- src/custom/blocks/Questionnaire.tsx | 54 +- src/custom/blocks/RecentStatus.tsx | 24 +- src/functions.ts | 191 +- src/index.tsx | 18 - src/main.tsx | 10 + src/nimgcenter/NimgcenterBrainConv.tsx | 255 +- src/nimgcenter/NimgenterUtilities.tsx | 55 +- src/nimgcenter/assets/howtouse.json | 5 +- src/nimgcenter/assets/utilities.json | 5 +- src/nimgcenter/lib/BrainCoordinateUtil.ts | 163 +- src/nimgdocs/Nimgdocs.tsx | 102 +- src/nimgdocs/NimgdocsBulletinLog.tsx | 74 +- src/nimgdocs/NimgdocsTutorials.tsx | 4739 +++++---- src/nimgdocs/assets/3dbrowse.json | 5 +- src/nimgdocs/assets/3dbrowse_usage.json | 5 +- src/nimgdocs/assets/about.json | 5 +- src/nimgdocs/assets/additem.json | 5 +- src/nimgdocs/assets/bulletin.json | 187 +- src/nimgdocs/assets/introduction.json | 5 +- src/nimgdocs/assets/nimgcenter.json | 5 +- src/nimgdocs/assets/nimgsearch.json | 5 +- src/nimgsearch/Nimgsearch.module.css | 244 +- src/nimgsearch/Nimgsearch.tsx | 1553 +-- src/nimgsearch/assets/brainarea.json | 352 +- src/nimgsearch/lib/NimgsearchUtil.ts | 464 +- src/react-app-env.d.ts | 1 - src/reportWebVitals.ts | 15 - src/setupTests.ts | 5 - src/xoonips/Xoonips.module.css | 52 +- src/xoonips/Xoonips.tsx | 198 +- src/xoonips/XoonipsAdvancedSearch.tsx | 70 +- src/xoonips/XoonipsDetailItem.tsx | 92 +- .../XoonipsSearchByAdvancedKeyword.tsx | 50 +- src/xoonips/XoonipsSearchByIndexId.tsx | 106 +- src/xoonips/XoonipsSearchByItemType.tsx | 48 +- src/xoonips/XoonipsSearchByKeyword.tsx | 54 +- src/xoonips/XoonipsTop.module.css | 10 +- src/xoonips/XoonipsTop.tsx | 64 +- src/xoonips/XoonipsXoopsPathRedirect.tsx | 54 +- src/xoonips/assets/simpf-links.json | 2 +- src/xoonips/assets/tree.json | 501 +- src/xoonips/blocks/IndexTree.module.css | 101 +- src/xoonips/blocks/IndexTree.tsx | 200 +- src/xoonips/blocks/Rankings.module.css | 22 +- src/xoonips/blocks/Rankings.tsx | 336 +- src/xoonips/blocks/RecentContents.module.css | 22 +- src/xoonips/blocks/RecentContents.tsx | 124 +- src/xoonips/blocks/Search.tsx | 124 +- .../item-type/binder/BinderAdvancedSearch.tsx | 50 +- src/xoonips/item-type/binder/BinderDetail.tsx | 172 +- src/xoonips/item-type/binder/BinderList.tsx | 47 +- src/xoonips/item-type/binder/BinderTop.tsx | 22 +- src/xoonips/item-type/binder/index.tsx | 16 +- .../item-type/book/BookAdvancedSearch.tsx | 77 +- src/xoonips/item-type/book/BookDetail.tsx | 110 +- src/xoonips/item-type/book/BookList.tsx | 63 +- src/xoonips/item-type/book/BookTop.tsx | 22 +- src/xoonips/item-type/book/index.tsx | 16 +- .../conference/ConferenceAdvancedSearch.tsx | 103 +- .../item-type/conference/ConferenceDetail.tsx | 103 +- .../item-type/conference/ConferenceList.tsx | 68 +- .../item-type/conference/ConferenceTop.tsx | 26 +- .../item-type/conference/ConferenceUtil.tsx | 59 +- src/xoonips/item-type/conference/index.tsx | 16 +- .../item-type/data/DataAdvancedSearch.tsx | 106 +- src/xoonips/item-type/data/DataDetail.tsx | 151 +- src/xoonips/item-type/data/DataList.tsx | 63 +- src/xoonips/item-type/data/DataTop.tsx | 26 +- src/xoonips/item-type/data/DataUtil.tsx | 28 +- src/xoonips/item-type/data/index.tsx | 16 +- .../item-type/files/FilesAdvancedSearch.tsx | 61 +- src/xoonips/item-type/files/FilesDetail.tsx | 85 +- src/xoonips/item-type/files/FilesList.tsx | 53 +- src/xoonips/item-type/files/FilesTop.tsx | 26 +- src/xoonips/item-type/files/index.tsx | 16 +- src/xoonips/item-type/index.tsx | 312 +- .../item-type/lib/AdvancedSearchBase.tsx | 382 +- src/xoonips/item-type/lib/DetailBase.tsx | 81 +- src/xoonips/item-type/lib/ListBase.tsx | 79 +- src/xoonips/item-type/lib/TopBase.tsx | 89 +- src/xoonips/item-type/lib/field/Author.tsx | 38 +- src/xoonips/item-type/lib/field/ChangeLog.tsx | 48 +- .../item-type/lib/field/Contributer.tsx | 20 +- .../item-type/lib/field/CreativeCommons.tsx | 102 +- .../lib/field/CreativeCommonsUtil.ts | 20 + src/xoonips/item-type/lib/field/DateTime.tsx | 26 +- .../item-type/lib/field/Description.tsx | 20 +- .../lib/field/FileDownloadButton.module.css | 26 +- .../lib/field/FileDownloadButton.tsx | 75 +- src/xoonips/item-type/lib/field/FileSize.tsx | 18 +- .../item-type/lib/field/FreeKeyword.tsx | 20 +- src/xoonips/item-type/lib/field/ItemFile.tsx | 133 +- src/xoonips/item-type/lib/field/ItemIndex.tsx | 52 +- src/xoonips/item-type/lib/field/Language.tsx | 54 +- .../field/LicenseAgreementDialog.module.css | 48 +- .../lib/field/LicenseAgreementDialog.tsx | 221 +- .../item-type/lib/field/Preview.module.css | 24 +- src/xoonips/item-type/lib/field/Preview.tsx | 106 +- .../item-type/lib/field/PublicationDate.tsx | 22 +- src/xoonips/item-type/lib/field/Readme.tsx | 14 +- src/xoonips/item-type/lib/field/RelatedTo.tsx | 87 +- src/xoonips/item-type/lib/field/Rights.tsx | 31 +- .../item-type/lib/field/SimPFLinkIcon.tsx | 34 +- src/xoonips/item-type/lib/field/index.tsx | 68 +- .../item-type/memo/MemoAdvancedSearch.tsx | 54 +- src/xoonips/item-type/memo/MemoDetail.tsx | 85 +- src/xoonips/item-type/memo/MemoList.tsx | 51 +- src/xoonips/item-type/memo/MemoTop.tsx | 22 +- src/xoonips/item-type/memo/index.tsx | 16 +- .../item-type/model/ModelAdvancedSearch.tsx | 76 +- src/xoonips/item-type/model/ModelDetail.tsx | 139 +- src/xoonips/item-type/model/ModelList.tsx | 63 +- src/xoonips/item-type/model/ModelTop.tsx | 26 +- src/xoonips/item-type/model/ModelUtil.tsx | 28 +- src/xoonips/item-type/model/index.tsx | 16 +- .../nimgcenter/NimgcenterAdvancedSearch.tsx | 233 +- .../item-type/nimgcenter/NimgcenterDetail.tsx | 212 +- .../item-type/nimgcenter/NimgcenterList.tsx | 51 +- .../item-type/nimgcenter/NimgcenterTop.tsx | 22 +- .../item-type/nimgcenter/NimgcenterUtil.tsx | 14 +- .../item-type/nimgcenter/SearchQuery.ts | 303 +- src/xoonips/item-type/nimgcenter/index.tsx | 16 +- .../item-type/paper/PaperAdvancedSearch.tsx | 77 +- src/xoonips/item-type/paper/PaperDetail.tsx | 93 +- src/xoonips/item-type/paper/PaperList.tsx | 87 +- src/xoonips/item-type/paper/PaperTop.tsx | 22 +- src/xoonips/item-type/paper/PaperUtil.tsx | 30 +- src/xoonips/item-type/paper/index.tsx | 16 +- .../PresentationAdvancedSearch.tsx | 110 +- .../presentation/PresentationDetail.tsx | 134 +- .../presentation/PresentationList.tsx | 69 +- .../presentation/PresentationTop.tsx | 26 +- .../presentation/PresentationUtil.tsx | 28 +- src/xoonips/item-type/presentation/index.tsx | 16 +- .../simulator/SimulatorAdvancedSearch.tsx | 91 +- .../item-type/simulator/SimulatorDetail.tsx | 151 +- .../item-type/simulator/SimulatorList.tsx | 63 +- .../item-type/simulator/SimulatorTop.tsx | 26 +- .../item-type/simulator/SimulatorUtil.tsx | 28 +- src/xoonips/item-type/simulator/index.tsx | 16 +- .../stimulus/StimulusAdvancedSearch.tsx | 91 +- .../item-type/stimulus/StimulusDetail.tsx | 151 +- .../item-type/stimulus/StimulusList.tsx | 63 +- .../item-type/stimulus/StimulusTop.tsx | 26 +- .../item-type/stimulus/StimulusUtil.tsx | 28 +- src/xoonips/item-type/stimulus/index.tsx | 16 +- .../item-type/tool/ToolAdvancedSearch.tsx | 109 +- src/xoonips/item-type/tool/ToolDetail.tsx | 139 +- src/xoonips/item-type/tool/ToolList.tsx | 49 +- src/xoonips/item-type/tool/ToolTop.tsx | 26 +- src/xoonips/item-type/tool/ToolUtil.tsx | 28 +- src/xoonips/item-type/tool/index.tsx | 16 +- .../item-type/url/UrlAdvancedSearch.tsx | 53 +- src/xoonips/item-type/url/UrlDetail.tsx | 94 +- src/xoonips/item-type/url/UrlList.tsx | 53 +- src/xoonips/item-type/url/UrlTop.tsx | 22 +- src/xoonips/item-type/url/UrlUtil.tsx | 38 +- src/xoonips/item-type/url/index.tsx | 16 +- src/xoonips/lib/AdvancedSearchQuery.ts | 204 +- src/xoonips/lib/IndexUtil.ts | 142 +- src/xoonips/lib/ItemUtil.ts | 1380 +-- src/xoonips/lib/XoonipsListIndex.tsx | 90 +- src/xoonips/lib/XoonipsListItem.module.css | 6 +- src/xoonips/lib/XoonipsListItem.tsx | 399 +- tsconfig.app.json | 25 + tsconfig.json | 26 +- tsconfig.node.json | 24 + vite.config.ts | 10 + yarn.lock | 9200 ----------------- 208 files changed, 18557 insertions(+), 20365 deletions(-) create mode 100644 .agents/skills/git-commit/SKILL.md create mode 100644 .agents/skills/git-commit/references/conventional_commits.md create mode 100644 biome.json create mode 100644 eslint.config.js rename public/index.html => index.html (68%) create mode 100644 package-lock.json delete mode 100644 src/App.test.tsx create mode 100644 src/custom-types.d.ts delete mode 100644 src/index.tsx create mode 100644 src/main.tsx delete mode 100644 src/react-app-env.d.ts delete mode 100644 src/reportWebVitals.ts delete mode 100644 src/setupTests.ts create mode 100644 src/xoonips/item-type/lib/field/CreativeCommonsUtil.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts delete mode 100644 yarn.lock diff --git a/.agents/skills/git-commit/SKILL.md b/.agents/skills/git-commit/SKILL.md new file mode 100644 index 0000000..c20b2a3 --- /dev/null +++ b/.agents/skills/git-commit/SKILL.md @@ -0,0 +1,38 @@ +--- +description: Analyze repository changes, gather contextual intent from the current codebase status, and generate structurally compliant Git commit messages. +metadata: + github-path: skills/git-commit + github-ref: refs/heads/main + github-repo: https://github.com/orrisroot/agent-skills + github-tree-sha: 661413ef1afaece6071d57b938ef050d4862e4d4 +name: git-commit +--- +# Git Commit Operator + +## 1. Purpose +Analyze repository changes, gather contextual intent from the current codebase status, and generate structurally compliant Git commit messages. + +## 2. Goals +* **Atomic Evaluation:** Review diffs to ensure changes represent a single, focused utility. Suggest breaking up large, mixed changes into distinct, atomic commits. +* **Contextual Analysis:** Cross-reference physical changes with the recent discussion history to understand not just *what* changed, but *why* it changed. +* **Executable Output:** Provide ready-to-run terminal commands that apply the correct formatting flags, paragraph separators, and line-wrapping behaviors. + +## 3. Operational Workflow for Commits +You must execute the following sequential workflow whenever a commit task is initiated or modifications are targeted for staging: + +### Step 1: Diff and Intent Inspection +1. Run `git diff --cached` to evaluate the staged modifications. +2. Cross-reference the structural changes against recent code review comments, commit messages, or the active chat context to confirm the primary engineering objective. +3. If those sources do not clearly explain the change, ask one concise clarifying question before generating the draft. + +### Step 2: Message Draft Formulation +1. Construct the message draft strictly using the type classifications and syntax limits specified in `references/conventional_commits.md`. +2. Present the drafted structure to the user for validation, highlighting the assigned commit type and scope. + +### Step 3: Terminal Command Delivery +1. Upon user confirmation, output the explicit, single-line terminal command using at most three `-m` flags as specified in the formatting policy. + * **CRITICAL:** Use at most one `-m` flag per structural part (Subject, Body, and Footer). + * **NEVER** use multiple `-m` flags for individual lines or bullet points within the body. Doing so causes Git to insert unwanted blank lines. + * **CRITICAL:** For multi-line body or footer content, you must use a single double-quoted string containing **literal newlines (actual line breaks)** within the command. Do NOT use escape sequences like `\n` (which shell command execution will commit literally) or additional `-m` flags. + +Process each step in order and complete the current step before moving to the next. diff --git a/.agents/skills/git-commit/references/conventional_commits.md b/.agents/skills/git-commit/references/conventional_commits.md new file mode 100644 index 0000000..1d08004 --- /dev/null +++ b/.agents/skills/git-commit/references/conventional_commits.md @@ -0,0 +1,60 @@ +# Reference: Conventional Commits Guidelines + +When drafting, validating, or executing Git commits, you must strictly adhere to the specification and specific formatting rules defined below. + +## Message Format + +```plain +(): + +[optional body] + +[optional footer(s)] +``` + +## Allowed Types + +* **feat**: A new feature for the user. +* **fix**: A bug fix for the user. +* **docs**: Documentation only changes. +* **style**: Changes that do not affect the meaning of the code (formatting, missing semi-colons, etc). +* **refactor**: A code change that neither fixes a bug nor adds a feature. +* **perf**: A code change that improves performance. +* **test**: Adding missing tests or correcting existing tests. +* **build**: Changes that affect the build system or external dependencies. +* **ci**: Changes to CI configuration files and scripts. +* **chore**: Other changes that do not modify src or test files. +* **revert**: Reverts a previous commit. + +## Strict Constraints + +1. **Default Message Language**: + * **The commit message must be written exclusively in English** unless the user provides explicit, specific instructions to use another language for that particular commit. + +2. **Line Length Limits**: + * The subject line (first line) must not exceed 72 characters, ideally 50 characters or less. + * **Every single line within the commit message body must also be wrapped at 72 characters or less.** If an explanation runs longer, you must manually insert line breaks to keep each line under the 72-character threshold. + +3. **Part Separation via Multiple `-m` Flags (At Most Three)**: + * Use at most one `-m` flag per structural part: the subject line, the body, and the footer. Use at most three `-m` flags in total. + * **NEVER split body lines or bullet lists across multiple `-m` flags.** Doing so inserts unwanted blank lines because Git automatically treats each `-m` flag as a separate paragraph. + * **DO NOT use escape sequences like `\n` in double quotes.** Shells like bash will treat `\n` as literal backslash-n characters and commit them as-is. + * **Use literal newlines (actual line breaks)** inside double quotes to write multi-line bodies or footers. + * *Example:* + + ```bash + git commit -m "feat(auth): add jwt authentication" -m "Validate tokens on every incoming request. + Secure endpoints by rejecting expired credentials. + - Added middleware for token validation + - Removed legacy session handling" -m "BREAKING CHANGE: The old session-based cookie auth is deprecated." + ``` + + +4. **Co-authored-by Restriction**: + * **Do not include any `Co-authored-by:` trailers** in the commit message or footer unless the user explicitly requests you to add credit for a co-author. + +5. **Case and Punctuation**: + * The description line must be written in lowercase and must not end with a period. + +6. **Imperative Mood**: + * Always use the imperative mood in the description (e.g., use "add", "fix", "change" instead of "added", "fixes", "changed"). diff --git a/.gitignore b/.gitignore index 6b60312..544f1ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,44 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local -.eslintcache - +# Logs +logs +*.log npm-debug.log* yarn-debug.log* yarn-error.log* -# database contents +# Dependencies +node_modules +/.pnp +.pnp.js + +# Production builds +build +dist +dist-ssr + +# Testing +coverage + +# Environment variables +.env.local +.env.development.local +.env.test.local +.env.production.local +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Misc +.eslintcache + +# Custom - database contents /public/modules #/src/xoonips/assets/*.json diff --git a/README.md b/README.md index d6fd779..6d0a276 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,59 @@ # NeuroImaging Platform + React project for nimg.neuroinf.jp +## Tech Stack + +- **Build tool**: [Vite](https://vitejs.dev/) +- **Framework**: [React 19](https://react.dev/) +- **Language**: [TypeScript](https://www.typescriptlang.org/) +- **HTTP client**: [ky](https://github.com/sindresorhus/ky) +- **Formatter**: [Biome](https://biomejs.dev/) + +## Prerequisites + +- Node.js 24 + +## Getting Started + +```bash +npm install +npm run dev +``` + +Open [http://localhost:5173](http://localhost:5173) to view the app in the browser. + ## Available Scripts -In the project directory, you can run: +### `npm run dev` -### `npm start` - -Runs the app in the development mode.
-Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.
-You will also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.
-See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. +Starts the development server with hot module replacement. ### `npm run build` -Builds the app for production to the `build` folder.
-It correctly bundles React in production mode and optimizes the build for the best performance. +Builds the app for production to the `build` folder. -The build is minified and the filenames include the hashes.
-Your app is ready to be deployed! +### `npm run preview` -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. +Previews the production build locally. -### `npm run eject` +### `npm run format` -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** +Formats all source files using Biome. -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. +## Project Structure -Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. - -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). +``` +src/ +├── brainexplorer/ # Brain Explorer module +├── common/ # Shared components and utilities +├── config.ts # Application configuration +├── credits/ # Credits module +├── custom/ # Custom components (Header, Footer, Page) +├── functions.ts # Utility functions +├── index.tsx # Entry point +├── nimgcenter/ # NIMG Center module +├── nimgdocs/ # NIMG Docs module +├── nimgsearch/ # NIMG Search module +└── xoonips/ # XooNIps integration module +``` diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..760c836 --- /dev/null +++ b/biome.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": ["**", "!**/build", "!**/node_modules"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 120 + }, + "linter": { + "enabled": false + }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "all" + } + } +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..62c0f10 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,42 @@ +import js from '@eslint/js'; +import { defineConfig, globalIgnores } from 'eslint/config'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default defineConfig([ + globalIgnores(['build']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + }, + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-explicit-any': 'off', + 'react-hooks/set-state-in-effect': 'warn', + 'react-hooks/exhaustive-deps': 'warn', + 'react-hooks/refs': 'warn', + 'react-refresh/only-export-components': 'warn', + '@typescript-eslint/no-unused-expressions': 'warn', + 'no-irregular-whitespace': ['warn', { skipStrings: true, skipComments: true, skipTemplates: true }], + 'no-useless-assignment': 'warn', + }, + }, + { + files: ['src/nimgsearch/Nimgsearch.tsx', 'src/xoonips/item-type/lib/AdvancedSearchBase.tsx'], + rules: { + 'react-hooks/refs': 'off', + }, + }, +]); diff --git a/public/index.html b/index.html similarity index 68% rename from public/index.html rename to index.html index a787ada..5b32db3 100644 --- a/public/index.html +++ b/index.html @@ -1,19 +1,20 @@ - + - - - + + + - + NIMG - NeuroImaging Platform
+ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5c4a57b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3594 @@ +{ + "name": "nimg", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nimg", + "version": "1.0.0", + "dependencies": { + "@orrisroot/react-html-parser": "^3.0.0", + "@types/async-lock": "^1.4.2", + "@types/lokijs": "^1.5.14", + "@types/react": "^19.2.15", + "@types/react-dom": "^19.2.3", + "@types/react-router-dom": "^5.3.3", + "@types/react-router-hash-link": "^2.4.9", + "async-lock": "^1.4.1", + "ky": "^2.0.2", + "lokijs": "^1.5.12", + "moment": "^2.30.1", + "rc-tree": "^5.13.1", + "react": "^19.2.6", + "react-cookie": "^8.1.2", + "react-dom": "^19.2.6", + "react-ga4": "^3.0.1", + "react-helmet-async": "^3.0.0", + "react-overlays": "^5.2.1", + "react-router-dom": "^7.15.1", + "react-router-hash-link": "^2.4.3", + "react-spinner-material": "^1.4.0", + "split-string": "^6.1.0", + "xregexp": "^5.1.2", + "yet-another-react-lightbox": "^3.32.0" + }, + "devDependencies": { + "@biomejs/biome": "^2.4.15", + "@eslint/js": "^10.0.1", + "@types/node": "^24.12.4", + "@vitejs/plugin-react": "^6.0.2", + "eslint": "^10.4.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "typescript": "~6.0.3", + "typescript-eslint": "^8.59.4", + "vite": "^8.0.14" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz", + "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==", + "license": "MIT", + "dependencies": { + "core-js-pure": "^3.48.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.15.tgz", + "integrity": "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.4.15", + "@biomejs/cli-darwin-x64": "2.4.15", + "@biomejs/cli-linux-arm64": "2.4.15", + "@biomejs/cli-linux-arm64-musl": "2.4.15", + "@biomejs/cli-linux-x64": "2.4.15", + "@biomejs/cli-linux-x64-musl": "2.4.15", + "@biomejs/cli-win32-arm64": "2.4.15", + "@biomejs/cli-win32-x64": "2.4.15" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.15.tgz", + "integrity": "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.15.tgz", + "integrity": "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.15.tgz", + "integrity": "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.15.tgz", + "integrity": "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.15.tgz", + "integrity": "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.15.tgz", + "integrity": "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.15.tgz", + "integrity": "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.15.tgz", + "integrity": "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@orrisroot/react-html-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@orrisroot/react-html-parser/-/react-html-parser-3.0.0.tgz", + "integrity": "sha512-7XevyE8fuzQqdeVC5z0WflAw1tIPaOFvrhvbE4agjrbd2+6t/H4Gm7EpUjmEvioSHBbp8qv0Z9mOeFE/AO2Qug==", + "license": "MIT", + "dependencies": { + "htmlparser2": "^12.0.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.132.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", + "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", + "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", + "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", + "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", + "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", + "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", + "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", + "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", + "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz", + "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz", + "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", + "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", + "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/async-lock": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/async-lock/-/async-lock-1.4.2.tgz", + "integrity": "sha512-HlZ6Dcr205BmNhwkdXqrg2vkFMN2PluI7Lgr8In3B3wE5PiQHhjRqtW/lGdVU9gw+sM0JcIDx2AN+cW8oSWIcw==", + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "license": "MIT" + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", + "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lokijs": { + "version": "1.5.14", + "resolved": "https://registry.npmjs.org/@types/lokijs/-/lokijs-1.5.14.tgz", + "integrity": "sha512-4Fic47BX3Qxr8pd12KT6/T1XWU8dOlJBIp1jGoMbaDbiEvdv50rAii+B3z1b/J2pvMywcVP+DBPGP5/lgLOKGA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", + "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/@types/react-router-hash-link": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/react-router-hash-link/-/react-router-hash-link-2.4.9.tgz", + "integrity": "sha512-zl/VMj+lfJZhvjOAQXIlBVPNKSK+/fRG8AUHhlP9++LhlA2ziLeTmbRxIMJI3PCiCTS+W/FosEoDRoNOGH0OzA==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-dom": "^5.3.0" + } + }, + "node_modules/@types/warning": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.4.tgz", + "integrity": "sha512-CqN8MnISMwQbLJXO3doBAV4Yw9hx9/Pyr2rZ78+NfaCnhyRA/nKrpyk6E7mKw17ZOaQdLpK9GiUjrqLzBlN3sg==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.4", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", + "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/core-js-pure": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz", + "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-serializer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz", + "integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==", + "license": "MIT", + "dependencies": { + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0", + "entities": "^8.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz", + "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/domhandler": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz", + "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^3.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz", + "integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^3.0.0", + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.361", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-12.0.0.tgz", + "integrity": "sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0", + "domutils": "^4.0.2", + "entities": "^8.0.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/ky": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ky/-/ky-2.0.2.tgz", + "integrity": "sha512-/GmXpo9F9W+f8n4Ivr2iH+7h7wL7jLbLKWkMlpflcCRb6kGjBfTlASEXaZ9qUgNTn4VgS0P2pwxxzQ4EM6Ulgg==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lokijs": { + "version": "1.5.12", + "resolved": "https://registry.npmjs.org/lokijs/-/lokijs-1.5.12.tgz", + "integrity": "sha512-Q5ALD6JiS6xAUWCwX3taQmgwxyveCtIIuL08+ml0nHwT3k0S/GIFJN+Hd38b1qYIMaE5X++iqsqWVksz7SYW+Q==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/rc-motion": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz", + "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.44.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz", + "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.44.1", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tree": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz", + "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-virtual-list": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz", + "integrity": "sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-cookie": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-8.1.2.tgz", + "integrity": "sha512-S45Z1y1dHyYfLEI4bFKQICuP+SwJqTPWbdc2ZpE6aQSdjSVJAjUDfwTPq8B7BWieIsgyyEWMb/QOrudtwJMjXA==", + "license": "MIT", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.7", + "hoist-non-react-statics": "^3.3.2", + "universal-cookie": "^8.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-ga4": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-3.0.1.tgz", + "integrity": "sha512-GyCc01bSheWXjzGDyHsXMOqk/SP5Cf/JrcJTg4hcpKx4eeSwaJKpJUc+ipF4ffLTZkmabmf3ZGBv4OKHTXNXyA==", + "license": "MIT" + }, + "node_modules/react-helmet-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-3.0.0.tgz", + "integrity": "sha512-nA3IEZfXiclgrz4KLxAhqJqIfFDuvzQwlKwpdmzZIuC1KNSghDEIXmyU0TKtbM+NafnkICcwx8CECFrZ/sL/1w==", + "license": "Apache-2.0", + "dependencies": { + "invariant": "^2.2.4", + "react-fast-compare": "^3.2.2", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-overlays": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz", + "integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.8", + "@popperjs/core": "^2.11.6", + "@restart/hooks": "^0.4.7", + "@types/warning": "^3.0.0", + "dom-helpers": "^5.2.0", + "prop-types": "^15.7.2", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, + "node_modules/react-router": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.1.tgz", + "integrity": "sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.15.1.tgz", + "integrity": "sha512-AzF62gjY6U9rkMq4RfP/r2EVtQ7DMfNMjyOp/flLTCrtRylLiK4wT4pSq6O8rOXZ2eXdZYJPEYe+ifomiv+Igg==", + "license": "MIT", + "dependencies": { + "react-router": "7.15.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router-hash-link": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/react-router-hash-link/-/react-router-hash-link-2.4.3.tgz", + "integrity": "sha512-NU7GWc265m92xh/aYD79Vr1W+zAIXDWp3L2YZOYP4rCqPnJ6LI6vh3+rKgkidtYijozHclaEQTAHaAaMWPVI4A==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=15", + "react-router-dom": ">=4" + } + }, + "node_modules/react-spinner-material": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/react-spinner-material/-/react-spinner-material-1.4.0.tgz", + "integrity": "sha512-4EAL+QgU/b5ubCjX3Yxmy4i0OGncix2wzfFlo25d4PXdDKFPGc1y6I57YmJ7QZykLkUIk32YSGeYP59FAq6sRA==", + "license": "Unlicense" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", + "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.132.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.2", + "@rolldown/binding-darwin-arm64": "1.0.2", + "@rolldown/binding-darwin-x64": "1.0.2", + "@rolldown/binding-freebsd-x64": "1.0.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", + "@rolldown/binding-linux-arm64-gnu": "1.0.2", + "@rolldown/binding-linux-arm64-musl": "1.0.2", + "@rolldown/binding-linux-ppc64-gnu": "1.0.2", + "@rolldown/binding-linux-s390x-gnu": "1.0.2", + "@rolldown/binding-linux-x64-gnu": "1.0.2", + "@rolldown/binding-linux-x64-musl": "1.0.2", + "@rolldown/binding-openharmony-arm64": "1.0.2", + "@rolldown/binding-wasm32-wasi": "1.0.2", + "@rolldown/binding-win32-arm64-msvc": "1.0.2", + "@rolldown/binding-win32-x64-msvc": "1.0.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-6.1.0.tgz", + "integrity": "sha512-9UBdnmnvx2NLLd4bMs7CEKK+wSzbujVv3ONyorkP1o8M3pVJQtXDO1cN19xD1JJs6ltOrtPrkUND0HzLSinUcA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", + "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/universal-cookie": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-8.1.2.tgz", + "integrity": "sha512-kcKzTGNsxVytujrYOvQbvh//QyFrA53HrzCGyzh6i9ujCww5gfPrLK0tG+jJD40SIIldiEjBNPPSR8fBMS21GA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.1.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", + "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.2", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/xregexp": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-5.1.2.tgz", + "integrity": "sha512-6hGgEMCGhqCTFEJbqmWrNIPqfpdirdGWkqshu7fFZddmTSfgv5Sn9D2SaKloR79s5VUiUlpwzg3CM3G6D3VIlw==", + "license": "MIT", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.9" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yet-another-react-lightbox": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yet-another-react-lightbox/-/yet-another-react-lightbox-3.32.0.tgz", + "integrity": "sha512-FWODOMrE07i3O5MeWRcYlcnAUk518zkUYKAe307pVW5pkey3hKMcAIWn8yMIURzGvbd3m9eghWO9CocEZmQPIg==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/igordanchenko" + }, + "peerDependencies": { + "@types/react": "^16 || ^17 || ^18 || ^19", + "@types/react-dom": "^16 || ^17 || ^18 || ^19", + "react": "^16.8.0 || ^17 || ^18 || ^19", + "react-dom": "^16.8.0 || ^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/package.json b/package.json index c79d0c9..825a4a3 100644 --- a/package.json +++ b/package.json @@ -2,63 +2,52 @@ "name": "nimg", "version": "1.0.0", "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "format": "biome check --write .", + "format:check": "biome check .", + "preview": "vite preview" + }, "dependencies": { - "@orrisroot/react-html-parser": "^2.1.1", - "@testing-library/jest-dom": "^5.16.4", - "@testing-library/react": "^13.3.0", - "@testing-library/user-event": "^14.2.1", - "@types/async-lock": "^1.1.5", - "@types/jest": "^28.1.2", - "@types/lokijs": "^1.5.7", - "@types/node": "^18.0.0", - "@types/react": "^18.0.14", - "@types/react-dom": "^18.0.5", + "@orrisroot/react-html-parser": "^3.0.0", + "@types/async-lock": "^1.4.2", + "@types/lokijs": "^1.5.14", + "@types/react": "^19.2.15", + "@types/react-dom": "^19.2.3", "@types/react-router-dom": "^5.3.3", - "@types/react-router-hash-link": "^2.4.5", - "async-lock": "^1.3.1", - "axios": "^0.27.2", + "@types/react-router-hash-link": "^2.4.9", + "async-lock": "^1.4.1", + "ky": "^2.0.2", "lokijs": "^1.5.12", - "moment": "^2.29.3", - "rc-tree": "^5.6.5", - "react": "^18.2.0", - "react-app-polyfill": "^3.0.0", - "react-cookie": "^4.1.1", - "react-dom": "^18.2.0", - "react-ga4": "^1.4.1", - "react-helmet-async": "^1.3.0", - "react-image-lightbox": "^5.1.4", - "react-overlays": "^5.2.0", - "react-router-dom": "^6.3.0", + "moment": "^2.30.1", + "rc-tree": "^5.13.1", + "react": "^19.2.6", + "react-cookie": "^8.1.2", + "react-dom": "^19.2.6", + "react-ga4": "^3.0.1", + "react-helmet-async": "^3.0.0", + "react-overlays": "^5.2.1", + "react-router-dom": "^7.15.1", "react-router-hash-link": "^2.4.3", - "react-scripts": "5.0.1", "react-spinner-material": "^1.4.0", "split-string": "^6.1.0", - "typescript": "~4.7.4", - "web-vitals": "^2.1.4", - "xregexp": "^5.1.1" + "xregexp": "^5.1.2", + "yet-another-react-lightbox": "^3.32.0" }, - "resolutions": { - "@svgr/webpack": "^6.2.1" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] + "devDependencies": { + "@biomejs/biome": "^2.4.15", + "@eslint/js": "^10.0.1", + "@types/node": "^24.12.4", + "@vitejs/plugin-react": "^6.0.2", + "eslint": "^10.4.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "typescript": "~6.0.3", + "typescript-eslint": "^8.59.4", + "vite": "^8.0.14" } } diff --git a/src/App.test.tsx b/src/App.test.tsx deleted file mode 100644 index f70b349..0000000 --- a/src/App.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import React from "react"; -import App from "./App"; - -test("renders welcome message", () => { - window.scrollTo = () => {}; - render(); - const linkElement = screen.getByText(/NeuroImaging Platform/); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/App.tsx b/src/App.tsx index c70534a..89318c2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,20 +1,20 @@ -import React from "react"; -import { CookiesProvider } from "react-cookie"; -import { HelmetProvider } from "react-helmet-async"; -import { BrowserRouter } from "react-router-dom"; -import "./App.css"; -import AppRoot from "./common/AppRoot"; +import type React from 'react'; +import { CookiesProvider } from 'react-cookie'; +import { HelmetProvider } from 'react-helmet-async'; +import { BrowserRouter } from 'react-router-dom'; +import './App.css'; +import AppRoot from './common/AppRoot'; const App: React.FC = () => { - return ( - - - - - - - - ); + return ( + + + + + + + + ); }; export default App; diff --git a/src/brainexplorer/BrainExplorer.tsx b/src/brainexplorer/BrainExplorer.tsx index 22705db..fc41657 100644 --- a/src/brainexplorer/BrainExplorer.tsx +++ b/src/brainexplorer/BrainExplorer.tsx @@ -1,31 +1,31 @@ -import React from "react"; -import { Helmet } from "react-helmet-async"; -import { Route, Routes } from "react-router-dom"; -import PageNotFound from "../common/lib/PageNotFound"; -import { MultiLang } from "../config"; -import Functions from "../functions"; -import BrainExplorerIndex from "./BrainExplorerIndex"; +import type React from 'react'; +import { Helmet } from 'react-helmet-async'; +import { Route, Routes } from 'react-router-dom'; +import PageNotFound from '../common/lib/PageNotFound'; +import type { MultiLang } from '../config'; +import Functions from '../functions'; +import BrainExplorerIndex from './BrainExplorerIndex'; interface Props { - lang: MultiLang; + lang: MultiLang; } const BrainExplorer: React.FC = (props: Props) => { - const { lang } = props; - return ( - <> - - Brain Explorer - {Functions.siteTitle(lang)} - - - } /> - } /> - } /> - } /> - } /> - - - ); + const { lang } = props; + return ( + <> + + Brain Explorer - {Functions.siteTitle(lang)} + + + } /> + } /> + } /> + } /> + } /> + + + ); }; export default BrainExplorer; diff --git a/src/brainexplorer/BrainExplorerIndex.module.css b/src/brainexplorer/BrainExplorerIndex.module.css index 55bf3b5..3667b29 100644 --- a/src/brainexplorer/BrainExplorerIndex.module.css +++ b/src/brainexplorer/BrainExplorerIndex.module.css @@ -1,49 +1,49 @@ .panel { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: white; - z-index: 1; + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: white; + z-index: 1; } .panel::after { - content: ""; - display: block; - clear: both; + content: ""; + display: block; + clear: both; } .panelNavi { - width: 22%; - height: 100%; - border-right: 2px solid #cccccc; - float: left; + width: 22%; + height: 100%; + border-right: 2px solid #cccccc; + float: left; } .panelButtons { - border-bottom: 2px solid #cccccc; - padding: 5px 10px; + border-bottom: 2px solid #cccccc; + padding: 5px 10px; } .panelMenu { - padding: 5px 10px; + padding: 5px 10px; } .panelMenuAistLink { - text-align: center; + text-align: center; } .panelContent { - height: 100%; - overflow: auto; - float: right; - padding: 5px; - width: calc(100% - 22% - 2px); + height: 100%; + overflow: auto; + float: right; + padding: 5px; + width: calc(100% - 22% - 2px); } .panelContent iframe { - width: 100%; - height: 100%; - border: 0; + width: 100%; + height: 100%; + border: 0; } diff --git a/src/brainexplorer/BrainExplorerIndex.tsx b/src/brainexplorer/BrainExplorerIndex.tsx index 23631b4..821c2de 100644 --- a/src/brainexplorer/BrainExplorerIndex.tsx +++ b/src/brainexplorer/BrainExplorerIndex.tsx @@ -1,111 +1,114 @@ -import React, { useState } from "react"; -import { Modal } from "react-overlays"; -import { Link, useLocation } from "react-router-dom"; -import { MultiLang } from "../config"; -import Functions from "../functions"; -import imageLogoAist from "./assets/images/logo_aist.png"; -import imageNaviHome from "./assets/images/navi_home.png"; -import imageNaviInfo from "./assets/images/navi_info.png"; -import styles from "./BrainExplorerIndex.module.css"; +import type React from 'react'; +import { useState } from 'react'; +import { Modal } from 'react-overlays'; +import { Link, useLocation } from 'react-router-dom'; +import type { MultiLang } from '../config'; +import Functions from '../functions'; +import imageLogoAist from './assets/images/logo_aist.png'; +import imageNaviHome from './assets/images/navi_home.png'; +import imageNaviInfo from './assets/images/navi_info.png'; +import styles from './BrainExplorerIndex.module.css'; interface Props { - lang: MultiLang; - type: "jm" | "hu"; + lang: MultiLang; + type: 'jm' | 'hu'; } const title = { - en: "Brain Atlas Database of Japanese Monkey for WWW.", - ja: "脳画像データベース", + en: 'Brain Atlas Database of Japanese Monkey for WWW.', + ja: '脳画像データベース', }; const description = { - en: "Neuroscience researches for elucidation of brain functions require electrophysiological, pharmacological or anatomical operations into the brain. However, no brain atlas of a Japanese monkey, which is used widely in these experiments, was made except detail maps of the brain stem. We have made magnetic resonance images as well as histological sections of a Japanese monkey brain. For the purpose of the practical and efficient use of these images, we will construct computer database for WWW, in which we can display, magnify, and compare these images by easy mouse operations.", - ja: "脳の優れた機能を解明し工学に利用するために、ヒトに近い機能を持った霊長類の神経科学的研究が行われている。脳機能の解明のためには脳の内部に対してさまざまな処置を施す必要がある。例えば、脳の特定の位置に記録、刺激電極を刺入する、薬剤を注入する、局部的に破壊するなどの場合がある。これらの処置に必要な資料は、脳の組織標本である。しかし、研究で良く使われるニホンザルの脳の組織標本は脳幹部を除いては今まで利用できる資料はなかった。そこで脳のMRI(磁気共鳴映像法)連続画像を撮ると共に、高次機能を調べるうえで重要な脳皮質全体を含む組織標本の連続切片を作成した。実験上また文献を読むときなどの資料として有効かつ簡便に活用するために、それらの画像をデジタル化し、マウスの操作のみで簡便に参照できるデータベースを構築し、WWW上で公開する。", + en: 'Neuroscience researches for elucidation of brain functions require electrophysiological, pharmacological or anatomical operations into the brain. However, no brain atlas of a Japanese monkey, which is used widely in these experiments, was made except detail maps of the brain stem. We have made magnetic resonance images as well as histological sections of a Japanese monkey brain. For the purpose of the practical and efficient use of these images, we will construct computer database for WWW, in which we can display, magnify, and compare these images by easy mouse operations.', + ja: '脳の優れた機能を解明し工学に利用するために、ヒトに近い機能を持った霊長類の神経科学的研究が行われている。脳機能の解明のためには脳の内部に対してさまざまな処置を施す必要がある。例えば、脳の特定の位置に記録、刺激電極を刺入する、薬剤を注入する、局部的に破壊するなどの場合がある。これらの処置に必要な資料は、脳の組織標本である。しかし、研究で良く使われるニホンザルの脳の組織標本は脳幹部を除いては今まで利用できる資料はなかった。そこで脳のMRI(磁気共鳴映像法)連続画像を撮ると共に、高次機能を調べるうえで重要な脳皮質全体を含む組織標本の連続切片を作成した。実験上また文献を読むときなどの資料として有効かつ簡便に活用するために、それらの画像をデジタル化し、マウスの操作のみで簡便に参照できるデータベースを構築し、WWW上で公開する。', }; const BrainExplorerIndex: React.FC = (props: Props) => { - const { lang, type } = props; - const [showInfo, setShowInfo] = useState(false); - const location = useLocation(); - const parentUrl = location.pathname.substring(0, location.pathname.lastIndexOf("/")); + const { lang, type } = props; + const [showInfo, setShowInfo] = useState(false); + const location = useLocation(); + const parentUrl = location.pathname.substring(0, location.pathname.lastIndexOf('/')); - const getUrl = (type: string, lang: MultiLang) => { - const label = { en: "eng", ja: "jpn" }; - return `${parentUrl}/index_${type}_${label[lang]}.php`; - }; - - const renderShowInfo = () => { - return ( -
-

{title[lang]}

-
-

{description[lang]}

-
-
- ); - }; - - const renderBrainExplorerContent = () => { - const url = `./${type === "hu" ? "human" : "jmonkey"}.html?LANG=${lang === "en" ? "ENG" : "JPN"}`; - return ; - }; + const getUrl = (type: string, lang: MultiLang) => { + const label = { en: 'eng', ja: 'jpn' }; + return `${parentUrl}/index_${type}_${label[lang]}.php`; + }; + const renderShowInfo = () => { return ( - -
-
- -
-

Brain Explorer [MRI]

-
    -
  • - setShowInfo(false)}> - {Functions.mlang("[en]Japanese Monkey[/en][ja]ニホンザル[/ja]", lang)} -
    - (Macaca fuscata) -
    - {Functions.mlang("[en]Developmental Data[/en][ja]発達の様子[/ja]", lang)} - -
  • -
  • - setShowInfo(false)}> - {Functions.mlang("[en]Human[/en][ja]ヒト[/ja]", lang)} -
    - (Homo sapiens) - -
  • -
-
-
    -
  • - {Functions.mlang("[en]Japanese page.[/en][ja]English page.[/ja]", lang)} -
  • -
-
-
- - AIST - -
-
-
-
{showInfo ? renderShowInfo() : renderBrainExplorerContent()}
-
-
+
+

{title[lang]}

+
+

{description[lang]}

+
+
); + }; + + const renderBrainExplorerContent = () => { + const url = `./${type === 'hu' ? 'human' : 'jmonkey'}.html?LANG=${lang === 'en' ? 'ENG' : 'JPN'}`; + return ; + }; + + return ( + +
+
+ +
+

Brain Explorer [MRI]

+
    +
  • + setShowInfo(false)}> + {Functions.mlang('[en]Japanese Monkey[/en][ja]ニホンザル[/ja]', lang)} +
    + (Macaca fuscata) +
    + {Functions.mlang('[en]Developmental Data[/en][ja]発達の様子[/ja]', lang)} + +
  • +
  • + setShowInfo(false)}> + {Functions.mlang('[en]Human[/en][ja]ヒト[/ja]', lang)} +
    + (Homo sapiens) + +
  • +
+
+
    +
  • + + {Functions.mlang('[en]Japanese page.[/en][ja]English page.[/ja]', lang)} + +
  • +
+
+
+ + AIST + +
+
+
+
{showInfo ? renderShowInfo() : renderBrainExplorerContent()}
+
+
+ ); }; export default BrainExplorerIndex; diff --git a/src/brainexplorer/lib/g3d.ts b/src/brainexplorer/lib/g3d.ts index 9ce0441..2c5e83e 100644 --- a/src/brainexplorer/lib/g3d.ts +++ b/src/brainexplorer/lib/g3d.ts @@ -1,1035 +1,1096 @@ export interface G3DHeader { - magic: number; // MagicNumber GO3D[0x47 0x4f 0x33 0x44], GH3D [0x47 0x48 0x33 0x44] or CO3D[0x43 0x4f 0x33 0x44] - width: number; // surface area silce width in pixels - height: number; // surface area slice height in pixels - depth: number; // surface area slice depth in pixels - org_width: number; // silce width in pixels - org_height: number; // slice height in pixels - org_depth: number; // slice depth in pixels - datatype: number; // 1 (byte per voxel) for gray, 3 (byte for voxel) for color - unit: number; // km:3, m:0, mm:-3, micro m:-6, nano m:-9 - grid: number; // grid width in unit - vs_width: number; // surface voxel width in unit - v_width: number; // voxel width in unit - v_height: number; // voxel height in unit - v_depth: number; // voxel depth in unit - ax: number; // rotation angle x - ay: number; // rotation angle y - az: number; // rotation angle z - ox: number; // origin x in surface voxel coordinates - oy: number; // origin y in surface voxel coordinates - oz: number; // origin z in surface voxel coordinates - s_num: number; // number of surface voxels - s_th: number; // threshold of suface voxel - option: string; // option area for additional parameters - date: string; // free format - sample: 1970/01/01 00:00:00 - coordinate: string; // Talairach, Horsley-Clarke, etc - subject: string; // subject name bm85... etc - memo: string; // memo or something for extend + magic: number; // MagicNumber GO3D[0x47 0x4f 0x33 0x44], GH3D [0x47 0x48 0x33 0x44] or CO3D[0x43 0x4f 0x33 0x44] + width: number; // surface area silce width in pixels + height: number; // surface area slice height in pixels + depth: number; // surface area slice depth in pixels + org_width: number; // silce width in pixels + org_height: number; // slice height in pixels + org_depth: number; // slice depth in pixels + datatype: number; // 1 (byte per voxel) for gray, 3 (byte for voxel) for color + unit: number; // km:3, m:0, mm:-3, micro m:-6, nano m:-9 + grid: number; // grid width in unit + vs_width: number; // surface voxel width in unit + v_width: number; // voxel width in unit + v_height: number; // voxel height in unit + v_depth: number; // voxel depth in unit + ax: number; // rotation angle x + ay: number; // rotation angle y + az: number; // rotation angle z + ox: number; // origin x in surface voxel coordinates + oy: number; // origin y in surface voxel coordinates + oz: number; // origin z in surface voxel coordinates + s_num: number; // number of surface voxels + s_th: number; // threshold of suface voxel + option: string; // option area for additional parameters + date: string; // free format - sample: 1970/01/01 00:00:00 + coordinate: string; // Talairach, Horsley-Clarke, etc + subject: string; // subject name bm85... etc + memo: string; // memo or something for extend } interface G3DImageRenderingContext { - a1: number; - a2: number; - a3: number; - b1: number; - b2: number; - b3: number; - c1: number; - c2: number; - c3: number; - d1: number; - d2: number; - d3: number; - ra1: number; - ra1_2: number; - ra2: number; - ra2_2: number; - ra3: number; - ra3_2: number; - rb1: number; - rb1_2: number; - rb2: number; - rb2_2: number; - rb3: number; - rb3_2: number; - rc1: number; - rc1_2: number; - rc2: number; - rc2_2: number; - rc3: number; - rc3_2: number; - rd1: number; - rd2: number; - rd3: number; + a1: number; + a2: number; + a3: number; + b1: number; + b2: number; + b3: number; + c1: number; + c2: number; + c3: number; + d1: number; + d2: number; + d3: number; + ra1: number; + ra1_2: number; + ra2: number; + ra2_2: number; + ra3: number; + ra3_2: number; + rb1: number; + rb1_2: number; + rb2: number; + rb2_2: number; + rb3: number; + rb3_2: number; + rc1: number; + rc1_2: number; + rc2: number; + rc2_2: number; + rc3: number; + rc3_2: number; + rd1: number; + rd2: number; + rd3: number; } interface G3DColor { - r: number; - g: number; - b: number; + r: number; + g: number; + b: number; } -enum DrawingLineType { - VLine = 0, - VDottedLine = 1, - HLine = 2, - HDottedLine = 3, -} +const DrawingLineType = { + VLine: 0, + VDottedLine: 1, + HLine: 2, + HDottedLine: 3, +} as const; +type DrawingLineType = (typeof DrawingLineType)[keyof typeof DrawingLineType]; const LineBrightness = 200; const DottedLineBrightness = 128; const EXACTSWITCH: 0 | 1 = 1; const MaxIntensity = 200; class G3D { - private data: ArrayBuffer | null = null; - private header: G3DHeader | null = null; - private isLittleEndianFile = true; - private surfaceView: Int16Array | null = null; - private imgView: Uint8Array | null = null; - private imageSize = 0; - private centerX = 0; - private centerY = 0; - private centerZ = 0; - private ox = 0; - private oy = 0; - private oz = 0; + private header: G3DHeader | null = null; + private isLittleEndianFile = true; + private surfaceView: Int16Array | null = null; + private imgView: Uint8Array | null = null; + private imageSize = 0; + private centerX = 0; + private centerY = 0; + private centerZ = 0; + private ox = 0; + private oy = 0; + private oz = 0; - constructor(data: ArrayBuffer) { - this._initialize(data); + constructor(data: ArrayBuffer) { + this._initialize(data); + } + + getHeader(): G3DHeader | null { + return this.header; + } + + drawImage( + canvas: HTMLCanvasElement, + ax: number, + ay: number, + az: number, + cutdepth: number, + th: number, + is3d: boolean, + mesh: boolean, + skincolor: boolean, + smoothing: boolean, + ): boolean { + const irc: G3DImageRenderingContext = { + a1: 0, + a2: 0, + a3: 0, + b1: 0, + b2: 0, + b3: 0, + c1: 0, + c2: 0, + c3: 0, + d1: 0, + d2: 0, + d3: 0, + ra1: 0, + ra1_2: 0, + ra2: 0, + ra2_2: 0, + ra3: 0, + ra3_2: 0, + rb1: 0, + rb1_2: 0, + rb2: 0, + rb2_2: 0, + rb3: 0, + rb3_2: 0, + rc1: 0, + rc1_2: 0, + rc2: 0, + rc2_2: 0, + rc3: 0, + rc3_2: 0, + rd1: 0, + rd2: 0, + rd3: 0, + }; + // console.log(ax, ay, az, cutdepth, th, is3d, mesh, skincolor, smoothing); + if (this.header === null) { + return false; } - getHeader(): G3DHeader | null { - return this.header; + canvas.width = this.imageSize; + canvas.height = this.imageSize; + const ctx = canvas.getContext('2d'); + if (ctx === null) { + return false; } + ctx.fillStyle = 'rgb(0, 0, 0)'; + ctx.fillRect(0, 0, this.imageSize, this.imageSize); + const img = ctx.getImageData(0, 0, this.imageSize, this.imageSize); - drawImage(canvas: HTMLCanvasElement, ax: number, ay: number, az: number, cutdepth: number, th: number, is3d: boolean, mesh: boolean, skincolor: boolean, smoothing: boolean): boolean { - const irc: G3DImageRenderingContext = { - a1: 0, - a2: 0, - a3: 0, - b1: 0, - b2: 0, - b3: 0, - c1: 0, - c2: 0, - c3: 0, - d1: 0, - d2: 0, - d3: 0, - ra1: 0, - ra1_2: 0, - ra2: 0, - ra2_2: 0, - ra3: 0, - ra3_2: 0, - rb1: 0, - rb1_2: 0, - rb2: 0, - rb2_2: 0, - rb3: 0, - rb3_2: 0, - rc1: 0, - rc1_2: 0, - rc2: 0, - rc2_2: 0, - rc3: 0, - rc3_2: 0, - rd1: 0, - rd2: 0, - rd3: 0, - }; - // console.log(ax, ay, az, cutdepth, th, is3d, mesh, skincolor, smoothing); - if (this.header === null) { - return false; + this._setMatrix(ax, ay, az, irc); + switch (this.header.datatype) { + case 1: + if (is3d) { + const flag2d = [...Array(this.imageSize * this.imageSize)].map(() => false); + const ddimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); + this._makeDIMG8(img, cutdepth, th, flag2d, ddimg, irc); + this._makeShadingImage(img, smoothing, false, flag2d, ddimg); + } else { + this._makeSliceImage8(img, cutdepth, irc); } - - canvas.width = this.imageSize; - canvas.height = this.imageSize; - const ctx = canvas.getContext('2d'); - if (ctx === null) { - return false; + break; + case 3: + if (is3d) { + const flag2d = [...Array(this.imageSize * this.imageSize)].map(() => false); + const ddimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); + this._makeDIMG24(img, cutdepth, th, flag2d, ddimg, irc); + this._makeShadingImage(img, smoothing, skincolor, flag2d, ddimg); + } else { + this._makeSliceImage24(img, cutdepth, irc); } - ctx.fillStyle = 'rgb(0, 0, 0)'; - ctx.fillRect(0, 0, this.imageSize, this.imageSize); - const img = ctx.getImageData(0, 0, this.imageSize, this.imageSize); - - this._setMatrix(ax, ay, az, irc); - switch (this.header.datatype) { - case 1: - if (is3d) { - const flag2d = [...Array(this.imageSize * this.imageSize)].map(() => false); - const ddimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); - this._makeDIMG8(img, cutdepth, th, flag2d, ddimg, irc); - this._makeShadingImage(img, smoothing, false, flag2d, ddimg); - } else { - this._makeSliceImage8(img, cutdepth, irc); - } - break; - case 3: - if (is3d) { - const flag2d = [...Array(this.imageSize * this.imageSize)].map(() => false); - const ddimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); - this._makeDIMG24(img, cutdepth, th, flag2d, ddimg, irc); - this._makeShadingImage(img, smoothing, skincolor, flag2d, ddimg); - } else { - this._makeSliceImage24(img, cutdepth, irc); - } - break; - } - if (mesh) { - this._drawMesh(img, irc); - } - ctx.putImageData(img, 0, 0); - return true; + break; } - - printHeader(): void { - if (this.header === null) { - console.log('--- invalid g3d data ---'); - return; - } - let message = '--- g3d information ---\n'; - message += 'magic: : ' + this.header.magic.toString(16) + '\n'; - message += 'width [pixel]: ' + this.header.width.toString() + '\n'; - message += 'height [pixel]: ' + this.header.height.toString() + '\n'; - message += 'depth [pixel]: ' + this.header.depth.toString() + '\n'; - message += 'org_width [pixel]: ' + this.header.org_width.toString() + '\n'; - message += 'org_height [pixel]: ' + this.header.org_height.toString() + '\n'; - message += 'org_depth [pixel]: ' + this.header.org_depth.toString() + '\n'; - message += 'datatype [byte] : ' + this.header.datatype.toString() + '\n'; - message += 'unit [m|E] : ' + this.header.unit.toString() + '\n'; - message += 'grid [unit] : ' + this.header.grid.toString() + '\n'; - message += 'surface voxel[unit] : ' + this.header.vs_width.toString() + '\n'; - message += 'voxel width [unit] : ' + this.header.v_width.toString() + '\n'; - message += 'voxel height [unit] : ' + this.header.v_height.toString() + '\n'; - message += 'voxel depth [unit] : ' + this.header.v_depth.toString() + '\n'; - message += 'ax [deg] : ' + this.header.ax.toString() + '\n'; - message += 'ay [deg] : ' + this.header.ay.toString() + '\n'; - message += 'az [deg] : ' + this.header.az.toString() + '\n'; - message += 'ox [pixel]: ' + this.header.ox.toString() + '\n'; - message += 'oy [pixel]: ' + this.header.oy.toString() + '\n'; - message += 'oz [pixel]: ' + this.header.oz.toString() + '\n'; - message += 'ox_real [unit] : ' + (this.header.ox * this.header.vs_width).toString() + '\n'; - message += 'oy_real [unit] : ' + (this.header.oy * this.header.vs_width).toString() + '\n'; - message += 'oz_real [unit] : ' + (this.header.oz * this.header.vs_width).toString() + '\n'; - message += 'surface voxels : ' + this.header.s_num.toString() + '\n'; - message += 'threshold : ' + this.header.s_th.toString() + '\n'; - message += 'date : ' + this.header.date + '\n'; - message += 'coordinate : ' + this.header.coordinate + '\n'; - message += 'subject : ' + this.header.subject + '\n'; - message += 'memo : ' + this.header.memo + '\n'; - message += 'File Type : ' + (this.isLittleEndianFile ? 'Little Endian.' : 'Big Endian'); - console.log(message); + if (mesh) { + this._drawMesh(img, irc); } + ctx.putImageData(img, 0, 0); + return true; + } - private _makeDIMG8(img: ImageData, cutdepth: number, th: number, flag2d: boolean[], ddimg: number[], irc: G3DImageRenderingContext) { - if (this.header === null) { - return; - } - const pdimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); - const sw = th > this.header.s_th ? 2 : th < this.header.s_th ? 3 : EXACTSWITCH; - switch (sw) { - case 0: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - ddimg[pos] = pdimg[pos]; - let pix = this._getPixel(x, y, cutdepth, irc); - if (pix > th) { - flag2d[pos] = true; - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - ddimg[pos] = cutdepth; - continue; - } - pix = this._getPixel(x, y, pdimg[pos], irc); - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - } - } - break; - } - case 1: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - let pdPos = pdimg[pos]; - if (pdPos === 0) { - continue; - } - let pix = this._getPixel(x, y, cutdepth, irc); - if (pix > th) { - flag2d[pos] = true; - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - ddimg[pos] = cutdepth; - continue; - } - if (pdPos < cutdepth) { - pdPos = cutdepth; - } - ddimg[pos] = 0; - for (let z = pdPos; z < this.imageSize; z++) { - pix = this._getPixel(x, y, z, irc); - if (pix > th) { - ddimg[pos] = z; - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - break; - } - } - } - } - break; - } - case 2: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - let pdPos = pdimg[pos]; - if (pdPos === 0) { - continue; - } - let pix = this._getPixel(x, y, cutdepth, irc); - if (pix > th) { - flag2d[pos] = true; - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - ddimg[pos] = cutdepth; - continue; - } - if (pix > this.header.s_th || pdPos < cutdepth) { - pdPos = cutdepth; - } - ddimg[pos] = 0; - for (let z = pdPos; z < this.imageSize; z++) { - pix = this._getPixel(x, y, z, irc); - if (pix > th) { - ddimg[pos] = z; - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - break; - } - } - } - } - break; - } - case 3: { - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - ddimg[pos] = 0; - for (let z = cutdepth; z < this.imageSize; z++) { - const pix = this._getPixel(x, y, z, irc); - if (pix > th) { - if (z === cutdepth) { - flag2d[pos] = true; - } else { - ddimg[pos] = z; - } - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - break; - } - } - } - } - break; - } - } + printHeader(): void { + if (this.header === null) { + console.log('--- invalid g3d data ---'); + return; } + let message = '--- g3d information ---\n'; + message += 'magic: : ' + this.header.magic.toString(16) + '\n'; + message += 'width [pixel]: ' + this.header.width.toString() + '\n'; + message += 'height [pixel]: ' + this.header.height.toString() + '\n'; + message += 'depth [pixel]: ' + this.header.depth.toString() + '\n'; + message += 'org_width [pixel]: ' + this.header.org_width.toString() + '\n'; + message += 'org_height [pixel]: ' + this.header.org_height.toString() + '\n'; + message += 'org_depth [pixel]: ' + this.header.org_depth.toString() + '\n'; + message += 'datatype [byte] : ' + this.header.datatype.toString() + '\n'; + message += 'unit [m|E] : ' + this.header.unit.toString() + '\n'; + message += 'grid [unit] : ' + this.header.grid.toString() + '\n'; + message += 'surface voxel[unit] : ' + this.header.vs_width.toString() + '\n'; + message += 'voxel width [unit] : ' + this.header.v_width.toString() + '\n'; + message += 'voxel height [unit] : ' + this.header.v_height.toString() + '\n'; + message += 'voxel depth [unit] : ' + this.header.v_depth.toString() + '\n'; + message += 'ax [deg] : ' + this.header.ax.toString() + '\n'; + message += 'ay [deg] : ' + this.header.ay.toString() + '\n'; + message += 'az [deg] : ' + this.header.az.toString() + '\n'; + message += 'ox [pixel]: ' + this.header.ox.toString() + '\n'; + message += 'oy [pixel]: ' + this.header.oy.toString() + '\n'; + message += 'oz [pixel]: ' + this.header.oz.toString() + '\n'; + message += 'ox_real [unit] : ' + (this.header.ox * this.header.vs_width).toString() + '\n'; + message += 'oy_real [unit] : ' + (this.header.oy * this.header.vs_width).toString() + '\n'; + message += 'oz_real [unit] : ' + (this.header.oz * this.header.vs_width).toString() + '\n'; + message += 'surface voxels : ' + this.header.s_num.toString() + '\n'; + message += 'threshold : ' + this.header.s_th.toString() + '\n'; + message += 'date : ' + this.header.date + '\n'; + message += 'coordinate : ' + this.header.coordinate + '\n'; + message += 'subject : ' + this.header.subject + '\n'; + message += 'memo : ' + this.header.memo + '\n'; + message += 'File Type : ' + (this.isLittleEndianFile ? 'Little Endian.' : 'Big Endian'); + console.log(message); + } - private _makeDIMG24(img: ImageData, cutdepth: number, th: number, flag2d: boolean[], ddimg: number[], irc: G3DImageRenderingContext) { - if (this.header === null) { - return; - } - const pdimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); - const sw = th > this.header.s_th ? 2 : th < this.header.s_th ? 3 : EXACTSWITCH; - switch (sw) { - case 0: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - ddimg[pos] = pdimg[pos]; - const c = this._getPixelColor(x, y, cutdepth, irc); - if (c.r > th || c.g > th || c.b > th) { - flag2d[pos] = true; - this._drawPixel(img, pos, c); - ddimg[pos] = cutdepth; - continue; - } - this._drawPixel(img, pos, this._getPixelColor(x, y, pdimg[pos], irc)); - } - } - break; - } - case 1: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let j = (pos = 0); j < this.imageSize; j++) { - for (let i = 0; i < this.imageSize; i++, pos++) { - let pdPos = pdimg[pos]; - if (pdPos === 0) { - continue; - } - const c = this._getPixelColor(i, j, cutdepth, irc); - if (c.r > th || c.g > th || c.b > th) { - flag2d[pos] = true; - this._drawPixel(img, pos, c); - ddimg[pos] = cutdepth; - continue; - } - if (pdPos < cutdepth) { - pdPos = cutdepth; - } - ddimg[pos] = 0; - for (let z = pdPos; z < this.imageSize; z++) { - const c = this._getPixelColor(i, j, z, irc); - if (c.r > th || c.g > th || c.b > th) { - ddimg[pos] = z; - this._drawPixel(img, pos, c); - break; - } - } - } - } - break; - } - case 2: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - let pdPos = pdimg[pos]; - if (pdPos === 0) { - continue; - } - const c = this._getPixelColor(x, y, cutdepth, irc); - if (c.r > th || c.g > th || c.b > th) { - flag2d[pos] = true; - this._drawPixel(img, pos, c); - ddimg[pos] = cutdepth; - continue; - } - if (c.r > this.header.s_th || c.g > this.header.s_th || c.b > this.header.s_th || pdPos < cutdepth) { - pdPos = cutdepth; - } - ddimg[pos] = 0; - for (let z = pdPos; z < this.imageSize; z++) { - const c = this._getPixelColor(x, y, z, irc); - if (c.r > th || c.g > th || c.b > th) { - ddimg[pos] = z; - this._drawPixel(img, pos, c); - break; - } - } - } - } - break; - } - case 3: { - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - ddimg[pos] = 0; - for (let z = cutdepth; z < this.imageSize; z++) { - const c = this._getPixelColor(x, y, z, irc); - if (c.r > th || c.g > th || c.b > th) { - if (z === cutdepth) { - flag2d[pos] = true; - } else { - ddimg[pos] = z; - } - this._drawPixel(img, pos, c); - break; - } - } - } - } - break; - } - } + private _makeDIMG8( + img: ImageData, + cutdepth: number, + th: number, + flag2d: boolean[], + ddimg: number[], + irc: G3DImageRenderingContext, + ) { + if (this.header === null) { + return; } - - private _makeShadingImage(img: ImageData, smoothing: boolean, skincolor: boolean, flag2d: boolean[], ddimg: number[]) { + const pdimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); + const sw = th > this.header.s_th ? 2 : th < this.header.s_th ? 3 : EXACTSWITCH; + switch (sw) { + case 0: { + this._transSurface(cutdepth, pdimg, irc); let pos = 0; for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - let timg2d = 0; - if (smoothing) { - if (x < this.imageSize - 2 && y < this.imageSize - 2) { - if (ddimg[pos] !== 0) { - const nx = (ddimg[pos] + ddimg[pos + this.imageSize] - ddimg[pos + 2] - ddimg[pos + 2 + this.imageSize]) / 4.0; - const ny = (ddimg[pos] + ddimg[pos + 1] - ddimg[pos + this.imageSize * 2] - ddimg[pos + 1 + this.imageSize * 2]) / 4.0; - timg2d = Math.round(MaxIntensity / Math.sqrt(nx * nx + ny * ny + 1)); - } - } - } else { - if (x < this.imageSize - 1 && y < this.imageSize - 1) { - if (ddimg[pos] !== 0) { - const nx = ddimg[pos] - ddimg[pos + 1]; - const ny = ddimg[pos] - ddimg[pos + this.imageSize]; - timg2d = Math.round(MaxIntensity / Math.sqrt(nx * nx + ny * ny + 1)); - } - } - } - if (!flag2d[pos]) { - if (skincolor) { - const base = pos * 4; - const c: G3DColor = { r: img.data[base], g: img.data[base + 1], b: img.data[base + 2] }; - const tmp = timg2d / Math.max(c.r, c.g, c.b); - this._drawPixel(img, pos, { r: c.r * tmp, g: c.g * tmp, b: c.b * tmp }); - } else { - const pix = timg2d; - this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); - } - } + for (let x = 0; x < this.imageSize; x++, pos++) { + ddimg[pos] = pdimg[pos]; + let pix = this._getPixel(x, y, cutdepth, irc); + if (pix > th) { + flag2d[pos] = true; + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + ddimg[pos] = cutdepth; + continue; } + pix = this._getPixel(x, y, pdimg[pos], irc); + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + } } - } - - private _makeSliceImage8(img: ImageData, cutdepth: number, irc: G3DImageRenderingContext) { + break; + } + case 1: { + this._transSurface(cutdepth, pdimg, irc); let pos = 0; for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - const pix = this._getPixel(x, y, cutdepth, irc); + for (let x = 0; x < this.imageSize; x++, pos++) { + let pdPos = pdimg[pos]; + if (pdPos === 0) { + continue; + } + let pix = this._getPixel(x, y, cutdepth, irc); + if (pix > th) { + flag2d[pos] = true; + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + ddimg[pos] = cutdepth; + continue; + } + if (pdPos < cutdepth) { + pdPos = cutdepth; + } + ddimg[pos] = 0; + for (let z = pdPos; z < this.imageSize; z++) { + pix = this._getPixel(x, y, z, irc); + if (pix > th) { + ddimg[pos] = z; this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + break; + } } + } } - } - - private _makeSliceImage24(img: ImageData, cutdepth: number, irc: G3DImageRenderingContext) { + break; + } + case 2: { + this._transSurface(cutdepth, pdimg, irc); let pos = 0; for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - const c = this._getPixelColor(x, y, cutdepth, irc); + for (let x = 0; x < this.imageSize; x++, pos++) { + let pdPos = pdimg[pos]; + if (pdPos === 0) { + continue; + } + let pix = this._getPixel(x, y, cutdepth, irc); + if (pix > th) { + flag2d[pos] = true; + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + ddimg[pos] = cutdepth; + continue; + } + if (pix > this.header.s_th || pdPos < cutdepth) { + pdPos = cutdepth; + } + ddimg[pos] = 0; + for (let z = pdPos; z < this.imageSize; z++) { + pix = this._getPixel(x, y, z, irc); + if (pix > th) { + ddimg[pos] = z; + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + break; + } + } + } + } + break; + } + case 3: { + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + ddimg[pos] = 0; + for (let z = cutdepth; z < this.imageSize; z++) { + const pix = this._getPixel(x, y, z, irc); + if (pix > th) { + if (z === cutdepth) { + flag2d[pos] = true; + } else { + ddimg[pos] = z; + } + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + break; + } + } + } + } + break; + } + } + } + + private _makeDIMG24( + img: ImageData, + cutdepth: number, + th: number, + flag2d: boolean[], + ddimg: number[], + irc: G3DImageRenderingContext, + ) { + if (this.header === null) { + return; + } + const pdimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); + const sw = th > this.header.s_th ? 2 : th < this.header.s_th ? 3 : EXACTSWITCH; + switch (sw) { + case 0: { + this._transSurface(cutdepth, pdimg, irc); + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + ddimg[pos] = pdimg[pos]; + const c = this._getPixelColor(x, y, cutdepth, irc); + if (c.r > th || c.g > th || c.b > th) { + flag2d[pos] = true; + this._drawPixel(img, pos, c); + ddimg[pos] = cutdepth; + continue; + } + this._drawPixel(img, pos, this._getPixelColor(x, y, pdimg[pos], irc)); + } + } + break; + } + case 1: { + this._transSurface(cutdepth, pdimg, irc); + let pos = 0; + for (let j = 0; j < this.imageSize; j++) { + for (let i = 0; i < this.imageSize; i++, pos++) { + let pdPos = pdimg[pos]; + if (pdPos === 0) { + continue; + } + const c = this._getPixelColor(i, j, cutdepth, irc); + if (c.r > th || c.g > th || c.b > th) { + flag2d[pos] = true; + this._drawPixel(img, pos, c); + ddimg[pos] = cutdepth; + continue; + } + if (pdPos < cutdepth) { + pdPos = cutdepth; + } + ddimg[pos] = 0; + for (let z = pdPos; z < this.imageSize; z++) { + const c = this._getPixelColor(i, j, z, irc); + if (c.r > th || c.g > th || c.b > th) { + ddimg[pos] = z; this._drawPixel(img, pos, c); - } - } - } - - private _drawMesh(img: ImageData, irc: G3DImageRenderingContext) { - if (this.header === null) { - return; - } - const origx = this._fround(this.header.ox * irc.ra1 + this.header.oy * irc.rb1 + this.header.oz * irc.rc1 + irc.rd1, 5); - const origy = this._fround(this.header.ox * irc.ra2 + this.header.oy * irc.rb2 + this.header.oz * irc.rc2 + irc.rd2, 5); - - const band = this.header.grid / this.header.vs_width; - const c = { r: 0, g: 0, b: LineBrightness }; - const cs = { r: 0, g: 0, b: DottedLineBrightness }; - this._drawLine(img, Math.round(origx), c, DrawingLineType.VLine); - this._drawLine(img, Math.round(origy), c, DrawingLineType.HLine); - for (let pos = Math.round(origx) + band; pos < this.imageSize; pos += band) { - this._drawLine(img, Math.round(pos), cs, DrawingLineType.VDottedLine); - } - for (let pos = Math.round(origx) - band; pos >= 0; pos -= band) { - this._drawLine(img, Math.round(pos), cs, DrawingLineType.VDottedLine); - } - for (let pos = Math.round(origy) + band; pos < this.imageSize; pos += band) { - this._drawLine(img, Math.round(pos), cs, DrawingLineType.HDottedLine); - } - for (let pos = Math.round(origy) - band; pos >= 0; pos -= band) { - this._drawLine(img, Math.round(pos), cs, DrawingLineType.HDottedLine); - } - } - - private _drawLine(img: ImageData, value: number, c: G3DColor, type: DrawingLineType) { - switch (type) { - case DrawingLineType.VLine: { - for (let y = 0; y < this.imageSize; y++) { - this._drawPixel(img, value + y * this.imageSize, c); - } break; + } } - case DrawingLineType.VDottedLine: { - for (let y = 0; y < this.imageSize; y += 2) { - this._drawPixel(img, value + y * this.imageSize, c); - } + } + } + break; + } + case 2: { + this._transSurface(cutdepth, pdimg, irc); + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + let pdPos = pdimg[pos]; + if (pdPos === 0) { + continue; + } + const c = this._getPixelColor(x, y, cutdepth, irc); + if (c.r > th || c.g > th || c.b > th) { + flag2d[pos] = true; + this._drawPixel(img, pos, c); + ddimg[pos] = cutdepth; + continue; + } + if (c.r > this.header.s_th || c.g > this.header.s_th || c.b > this.header.s_th || pdPos < cutdepth) { + pdPos = cutdepth; + } + ddimg[pos] = 0; + for (let z = pdPos; z < this.imageSize; z++) { + const c = this._getPixelColor(x, y, z, irc); + if (c.r > th || c.g > th || c.b > th) { + ddimg[pos] = z; + this._drawPixel(img, pos, c); break; + } } - case DrawingLineType.HLine: { - let pos = value * this.imageSize; - for (let x = 0; x < this.imageSize; x++, pos++) { - this._drawPixel(img, pos, c); + } + } + break; + } + case 3: { + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + ddimg[pos] = 0; + for (let z = cutdepth; z < this.imageSize; z++) { + const c = this._getPixelColor(x, y, z, irc); + if (c.r > th || c.g > th || c.b > th) { + if (z === cutdepth) { + flag2d[pos] = true; + } else { + ddimg[pos] = z; } + this._drawPixel(img, pos, c); break; + } } - case DrawingLineType.HDottedLine: { - let pos = value * this.imageSize; - for (let x = 0; x < this.imageSize; x += 2, pos += 2) { - this._drawPixel(img, pos, c); - } - break; + } + } + break; + } + } + } + + private _makeShadingImage( + img: ImageData, + smoothing: boolean, + skincolor: boolean, + flag2d: boolean[], + ddimg: number[], + ) { + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + let timg2d = 0; + if (smoothing) { + if (x < this.imageSize - 2 && y < this.imageSize - 2) { + if (ddimg[pos] !== 0) { + const nx = + (ddimg[pos] + ddimg[pos + this.imageSize] - ddimg[pos + 2] - ddimg[pos + 2 + this.imageSize]) / 4.0; + const ny = + (ddimg[pos] + ddimg[pos + 1] - ddimg[pos + this.imageSize * 2] - ddimg[pos + 1 + this.imageSize * 2]) / + 4.0; + timg2d = Math.round(MaxIntensity / Math.sqrt(nx * nx + ny * ny + 1)); } - } - } - - private _drawPixel(img: ImageData, pos: number, c: G3DColor) { - const base = pos * 4; - img.data[base] = c.r; - img.data[base + 1] = c.g; - img.data[base + 2] = c.b; - img.data[base + 3] = 255; - } - - private _getPixelColor(x: number, y: number, z: number, irc: G3DImageRenderingContext): G3DColor { - const c: G3DColor = { r: 0, g: 0, b: 0 }; - if (this.header === null || this.imgView === null) { - return c; - } - - const tx = x * irc.a1 + y * irc.b1 + z * irc.c1 + irc.d1; - const ty = x * irc.a2 + y * irc.b2 + z * irc.c2 + irc.d2; - const tz = x * irc.a3 + y * irc.b3 + z * irc.c3 + irc.d3; - - if (tx < 0 || ty < 0 || tz < 0) return c; - if (tx > this.header.org_width - 1) return c; - if (ty > this.header.org_height - 1) return c; - if (tz > this.header.org_depth - 1) return c; - - const ix0 = Math.floor(tx); - const iy0 = Math.floor(ty); - const iz0 = Math.floor(tz); - const wix1 = tx - ix0; - const wix0 = 1.0 - wix1; - const wiy1 = ty - iy0; - const wiy0 = 1.0 - wiy1; - const wiz1 = tz - iz0; - const wiz0 = 1.0 - wiz1; - const pixel = this.header.datatype; - const line = this.header.org_width * pixel; - const area = this.header.org_height * line; - - let offset = ix0 * pixel + iy0 * line + iz0 * area; - let tmp1 = offset; - let tmp2 = offset; - const mx0 = [0, 0, 0]; - const mxy0 = [0, 0, 0]; - const mx1 = [0, 0, 0]; - const mxy1 = [0, 0, 0]; - - if (ix0 + 1 === this.header.org_width) { - tmp1 = offset; - mx0[0] = this.imgView[tmp1++]; - mx0[1] = this.imgView[tmp1++]; - mx0[2] = this.imgView[tmp1]; + } } else { - tmp1 = offset; - tmp2 = tmp1 + pixel; - mx0[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx0[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx0[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - } - if (iy0 + 1 === this.header.org_height) { - mxy0[0] = mx0[0]; - mxy0[1] = mx0[1]; - mxy0[2] = mx0[2]; - } else { - if (ix0 + 1 === this.header.org_width) { - tmp1 = offset + line; - mx1[0] = this.imgView[tmp1++]; - mx1[1] = this.imgView[tmp1++]; - mx1[2] = this.imgView[tmp1]; - } else { - tmp1 = offset + line; - tmp2 = tmp1 + pixel; - mx1[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx1[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx1[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + if (x < this.imageSize - 1 && y < this.imageSize - 1) { + if (ddimg[pos] !== 0) { + const nx = ddimg[pos] - ddimg[pos + 1]; + const ny = ddimg[pos] - ddimg[pos + this.imageSize]; + timg2d = Math.round(MaxIntensity / Math.sqrt(nx * nx + ny * ny + 1)); } - mxy0[0] = mx0[0] * wiy0 + mx1[0] * wiy1; - mxy0[1] = mx0[1] * wiy0 + mx1[1] * wiy1; - mxy0[2] = mx0[2] * wiy0 + mx1[2] * wiy1; + } } - if (iz0 + 1 === this.header.org_depth) { - c.b = Math.round(mxy0[0]); - c.g = Math.round(mxy0[1]); - c.r = Math.round(mxy0[2]); - return c; + if (!flag2d[pos]) { + if (skincolor) { + const base = pos * 4; + const c: G3DColor = { r: img.data[base], g: img.data[base + 1], b: img.data[base + 2] }; + const tmp = timg2d / Math.max(c.r, c.g, c.b); + this._drawPixel(img, pos, { r: c.r * tmp, g: c.g * tmp, b: c.b * tmp }); + } else { + const pix = timg2d; + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + } } - offset = offset + area; - if (ix0 + 1 === this.header.org_width) { - tmp1 = offset; - mx0[0] = this.imgView[tmp1++]; - mx0[1] = this.imgView[tmp1++]; - mx0[2] = this.imgView[tmp1]; - } else { - tmp1 = offset; - tmp2 = offset + pixel; - mx0[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx0[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx0[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + } + } + } + + private _makeSliceImage8(img: ImageData, cutdepth: number, irc: G3DImageRenderingContext) { + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + const pix = this._getPixel(x, y, cutdepth, irc); + this._drawPixel(img, pos, { r: pix, g: pix, b: pix }); + } + } + } + + private _makeSliceImage24(img: ImageData, cutdepth: number, irc: G3DImageRenderingContext) { + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + const c = this._getPixelColor(x, y, cutdepth, irc); + this._drawPixel(img, pos, c); + } + } + } + + private _drawMesh(img: ImageData, irc: G3DImageRenderingContext) { + if (this.header === null) { + return; + } + const origx = this._fround( + this.header.ox * irc.ra1 + this.header.oy * irc.rb1 + this.header.oz * irc.rc1 + irc.rd1, + 5, + ); + const origy = this._fround( + this.header.ox * irc.ra2 + this.header.oy * irc.rb2 + this.header.oz * irc.rc2 + irc.rd2, + 5, + ); + + const band = this.header.grid / this.header.vs_width; + const c = { r: 0, g: 0, b: LineBrightness }; + const cs = { r: 0, g: 0, b: DottedLineBrightness }; + this._drawLine(img, Math.round(origx), c, DrawingLineType.VLine); + this._drawLine(img, Math.round(origy), c, DrawingLineType.HLine); + for (let pos = Math.round(origx) + band; pos < this.imageSize; pos += band) { + this._drawLine(img, Math.round(pos), cs, DrawingLineType.VDottedLine); + } + for (let pos = Math.round(origx) - band; pos >= 0; pos -= band) { + this._drawLine(img, Math.round(pos), cs, DrawingLineType.VDottedLine); + } + for (let pos = Math.round(origy) + band; pos < this.imageSize; pos += band) { + this._drawLine(img, Math.round(pos), cs, DrawingLineType.HDottedLine); + } + for (let pos = Math.round(origy) - band; pos >= 0; pos -= band) { + this._drawLine(img, Math.round(pos), cs, DrawingLineType.HDottedLine); + } + } + + private _drawLine(img: ImageData, value: number, c: G3DColor, type: DrawingLineType) { + switch (type) { + case DrawingLineType.VLine: { + for (let y = 0; y < this.imageSize; y++) { + this._drawPixel(img, value + y * this.imageSize, c); } - if (iy0 + 1 === this.header.org_height) { - mxy1[0] = mx0[0]; - mxy1[1] = mx0[1]; - mxy1[2] = mx0[2]; - } else { - if (ix0 + 1 === this.header.org_width) { - tmp1 = offset + line; - mx1[0] = this.imgView[tmp1++]; - mx1[1] = this.imgView[tmp1++]; - mx1[2] = this.imgView[tmp1]; - } else { - tmp1 = offset + line; - tmp2 = tmp1 + pixel; - mx1[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx1[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - tmp1++; - tmp2++; - mx1[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; - } - mxy1[0] = mx0[0] * wiy0 + mx1[0] * wiy1; - mxy1[1] = mx0[1] * wiy0 + mx1[1] * wiy1; - mxy1[2] = mx0[2] * wiy0 + mx1[2] * wiy1; + break; + } + case DrawingLineType.VDottedLine: { + for (let y = 0; y < this.imageSize; y += 2) { + this._drawPixel(img, value + y * this.imageSize, c); } - c.b = Math.round(mxy0[0] * wiz0 + mxy1[0] * wiz1); - c.g = Math.round(mxy0[1] * wiz0 + mxy1[1] * wiz1); - c.r = Math.round(mxy0[2] * wiz0 + mxy1[2] * wiz1); - return c; + break; + } + case DrawingLineType.HLine: { + let pos = value * this.imageSize; + for (let x = 0; x < this.imageSize; x++, pos++) { + this._drawPixel(img, pos, c); + } + break; + } + case DrawingLineType.HDottedLine: { + let pos = value * this.imageSize; + for (let x = 0; x < this.imageSize; x += 2, pos += 2) { + this._drawPixel(img, pos, c); + } + break; + } + } + } + + private _drawPixel(img: ImageData, pos: number, c: G3DColor) { + const base = pos * 4; + img.data[base] = c.r; + img.data[base + 1] = c.g; + img.data[base + 2] = c.b; + img.data[base + 3] = 255; + } + + private _getPixelColor(x: number, y: number, z: number, irc: G3DImageRenderingContext): G3DColor { + const c: G3DColor = { r: 0, g: 0, b: 0 }; + if (this.header === null || this.imgView === null) { + return c; } - private _getPixel(x: number, y: number, z: number, irc: G3DImageRenderingContext): number { - if (this.header === null || this.imgView === null) { - return 0; - } + const tx = x * irc.a1 + y * irc.b1 + z * irc.c1 + irc.d1; + const ty = x * irc.a2 + y * irc.b2 + z * irc.c2 + irc.d2; + const tz = x * irc.a3 + y * irc.b3 + z * irc.c3 + irc.d3; - const tx = x * irc.a1 + y * irc.b1 + z * irc.c1 + irc.d1; - const ty = x * irc.a2 + y * irc.b2 + z * irc.c2 + irc.d2; - const tz = x * irc.a3 + y * irc.b3 + z * irc.c3 + irc.d3; + if (tx < 0 || ty < 0 || tz < 0) return c; + if (tx > this.header.org_width - 1) return c; + if (ty > this.header.org_height - 1) return c; + if (tz > this.header.org_depth - 1) return c; - if (tx < 0 || ty < 0 || tz < 0 || tx > this.header.org_width - 1 || ty > this.header.org_height - 1 || tz > this.header.org_depth - 1) { - return 0; - } + const ix0 = Math.floor(tx); + const iy0 = Math.floor(ty); + const iz0 = Math.floor(tz); + const wix1 = tx - ix0; + const wix0 = 1.0 - wix1; + const wiy1 = ty - iy0; + const wiy0 = 1.0 - wiy1; + const wiz1 = tz - iz0; + const wiz0 = 1.0 - wiz1; + const pixel = this.header.datatype; + const line = this.header.org_width * pixel; + const area = this.header.org_height * line; - const ix0 = Math.floor(tx); - const iy0 = Math.floor(ty); - const iz0 = Math.floor(tz); - const wix1 = tx - ix0; - const wix0 = 1.0 - wix1; - const wiy1 = ty - iy0; - const wiy0 = 1.0 - wiy1; - const wiz1 = tz - iz0; - const wiz0 = 1.0 - wiz1; - const pixel = this.header.datatype; - const line = this.header.org_width * pixel; - const area = this.header.org_height * line; + let offset = ix0 * pixel + iy0 * line + iz0 * area; + let tmp1: number; + let tmp2: number; + const mx0 = [0, 0, 0]; + const mxy0 = [0, 0, 0]; + const mx1 = [0, 0, 0]; + const mxy1 = [0, 0, 0]; - let mx0: number; - let mx1: number; - let mxy0: number; - let mxy1: number; + if (ix0 + 1 === this.header.org_width) { + tmp1 = offset; + mx0[0] = this.imgView[tmp1++]; + mx0[1] = this.imgView[tmp1++]; + mx0[2] = this.imgView[tmp1]; + } else { + tmp1 = offset; + tmp2 = tmp1 + pixel; + mx0[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx0[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx0[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + } + if (iy0 + 1 === this.header.org_height) { + mxy0[0] = mx0[0]; + mxy0[1] = mx0[1]; + mxy0[2] = mx0[2]; + } else { + if (ix0 + 1 === this.header.org_width) { + tmp1 = offset + line; + mx1[0] = this.imgView[tmp1++]; + mx1[1] = this.imgView[tmp1++]; + mx1[2] = this.imgView[tmp1]; + } else { + tmp1 = offset + line; + tmp2 = tmp1 + pixel; + mx1[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx1[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx1[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + } + mxy0[0] = mx0[0] * wiy0 + mx1[0] * wiy1; + mxy0[1] = mx0[1] * wiy0 + mx1[1] * wiy1; + mxy0[2] = mx0[2] * wiy0 + mx1[2] * wiy1; + } + if (iz0 + 1 === this.header.org_depth) { + c.b = Math.round(mxy0[0]); + c.g = Math.round(mxy0[1]); + c.r = Math.round(mxy0[2]); + return c; + } + offset = offset + area; + if (ix0 + 1 === this.header.org_width) { + tmp1 = offset; + mx0[0] = this.imgView[tmp1++]; + mx0[1] = this.imgView[tmp1++]; + mx0[2] = this.imgView[tmp1]; + } else { + tmp1 = offset; + tmp2 = offset + pixel; + mx0[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx0[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx0[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + } + if (iy0 + 1 === this.header.org_height) { + mxy1[0] = mx0[0]; + mxy1[1] = mx0[1]; + mxy1[2] = mx0[2]; + } else { + if (ix0 + 1 === this.header.org_width) { + tmp1 = offset + line; + mx1[0] = this.imgView[tmp1++]; + mx1[1] = this.imgView[tmp1++]; + mx1[2] = this.imgView[tmp1]; + } else { + tmp1 = offset + line; + tmp2 = tmp1 + pixel; + mx1[0] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx1[1] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + tmp1++; + tmp2++; + mx1[2] = this.imgView[tmp1] * wix0 + this.imgView[tmp2] * wix1; + } + mxy1[0] = mx0[0] * wiy0 + mx1[0] * wiy1; + mxy1[1] = mx0[1] * wiy0 + mx1[1] * wiy1; + mxy1[2] = mx0[2] * wiy0 + mx1[2] * wiy1; + } + c.b = Math.round(mxy0[0] * wiz0 + mxy1[0] * wiz1); + c.g = Math.round(mxy0[1] * wiz0 + mxy1[1] * wiz1); + c.r = Math.round(mxy0[2] * wiz0 + mxy1[2] * wiz1); + return c; + } - let offset = ix0 * pixel + iy0 * line + iz0 * area; - if (ix0 + 1 === this.header.org_width) { - mx0 = this.imgView[offset]; - } else { - mx0 = this.imgView[offset] * wix0 + this.imgView[offset + pixel] * wix1; - } - if (iy0 + 1 === this.header.org_height) { - mxy0 = mx0; - } else { - if (ix0 + 1 === this.header.org_width) { - mx1 = this.imgView[offset + line]; - } else { - mx1 = this.imgView[offset + line] * wix0 + this.imgView[offset + line + pixel] * wix1; - } - mxy0 = mx0 * wiy0 + mx1 * wiy1; - } - - if (iz0 + 1 === this.header.org_depth) { - return Math.round(mxy0); - } - - offset = offset + area; - if (ix0 + 1 === this.header.org_width) { - mx0 = this.imgView[offset]; - } else { - mx0 = this.imgView[offset] * wix0 + this.imgView[offset + pixel] * wix1; - } - if (iy0 + 1 === this.header.org_height) { - mxy1 = mx0; - } else { - if (ix0 + 1 === this.header.org_width) { - mx1 = this.imgView[offset + line]; - } else { - mx1 = this.imgView[offset + line] * wix0 + this.imgView[offset + line + pixel] * wix1; - } - mxy1 = mx0 * wiy0 + mx1 * wiy1; - } - - return Math.round(mxy0 * wiz0 + mxy1 * wiz1); + private _getPixel(x: number, y: number, z: number, irc: G3DImageRenderingContext): number { + if (this.header === null || this.imgView === null) { + return 0; } - private _transSurface(cutdepth: number, pdimg: number[], irc: G3DImageRenderingContext) { - if (this.header === null || this.surfaceView === null) { - return; + const tx = x * irc.a1 + y * irc.b1 + z * irc.c1 + irc.d1; + const ty = x * irc.a2 + y * irc.b2 + z * irc.c2 + irc.d2; + const tz = x * irc.a3 + y * irc.b3 + z * irc.c3 + irc.d3; + + if ( + tx < 0 || + ty < 0 || + tz < 0 || + tx > this.header.org_width - 1 || + ty > this.header.org_height - 1 || + tz > this.header.org_depth - 1 + ) { + return 0; + } + + const ix0 = Math.floor(tx); + const iy0 = Math.floor(ty); + const iz0 = Math.floor(tz); + const wix1 = tx - ix0; + const wix0 = 1.0 - wix1; + const wiy1 = ty - iy0; + const wiy0 = 1.0 - wiy1; + const wiz1 = tz - iz0; + const wiz0 = 1.0 - wiz1; + const pixel = this.header.datatype; + const line = this.header.org_width * pixel; + const area = this.header.org_height * line; + + let mx0: number; + let mx1: number; + let mxy0: number; + let mxy1: number; + + let offset = ix0 * pixel + iy0 * line + iz0 * area; + if (ix0 + 1 === this.header.org_width) { + mx0 = this.imgView[offset]; + } else { + mx0 = this.imgView[offset] * wix0 + this.imgView[offset + pixel] * wix1; + } + if (iy0 + 1 === this.header.org_height) { + mxy0 = mx0; + } else { + if (ix0 + 1 === this.header.org_width) { + mx1 = this.imgView[offset + line]; + } else { + mx1 = this.imgView[offset + line] * wix0 + this.imgView[offset + line + pixel] * wix1; + } + mxy0 = mx0 * wiy0 + mx1 * wiy1; + } + + if (iz0 + 1 === this.header.org_depth) { + return Math.round(mxy0); + } + + offset = offset + area; + if (ix0 + 1 === this.header.org_width) { + mx0 = this.imgView[offset]; + } else { + mx0 = this.imgView[offset] * wix0 + this.imgView[offset + pixel] * wix1; + } + if (iy0 + 1 === this.header.org_height) { + mxy1 = mx0; + } else { + if (ix0 + 1 === this.header.org_width) { + mx1 = this.imgView[offset + line]; + } else { + mx1 = this.imgView[offset + line] * wix0 + this.imgView[offset + line + pixel] * wix1; + } + mxy1 = mx0 * wiy0 + mx1 * wiy1; + } + + return Math.round(mxy0 * wiz0 + mxy1 * wiz1); + } + + private _transSurface(cutdepth: number, pdimg: number[], irc: G3DImageRenderingContext) { + if (this.header === null || this.surfaceView === null) { + return; + } + + const ix = [...Array(7)].map(() => 0); + const iy = [...Array(7)].map(() => 0); + const iz = [...Array(7)].map(() => 0); + + for (let i = 0; i < this.header.s_num * 3; i++) { + const x = this.surfaceView[i++]; + const y = this.surfaceView[i++]; + const z = this.surfaceView[i]; + + ix[0] = x * irc.ra1 + y * irc.rb1 + z * irc.rc1 + irc.rd1; + iy[0] = x * irc.ra2 + y * irc.rb2 + z * irc.rc2 + irc.rd2; + iz[0] = x * irc.ra3 + y * irc.rb3 + z * irc.rc3 + irc.rd3; + ix[1] = ix[0] + irc.ra1_2; + iy[1] = iy[0] + irc.ra2_2; + iz[1] = iz[0] + irc.ra3_2; + ix[2] = ix[0] + irc.rb1_2; + iy[2] = iy[0] + irc.rb2_2; + iz[2] = iz[0] + irc.rb3_2; + ix[3] = ix[0] + irc.rc1_2; + iy[3] = iy[0] + irc.rc2_2; + iz[3] = iz[0] + irc.rc3_2; + ix[4] = ix[1] + irc.rb1_2; + iy[4] = iy[1] + irc.rb2_2; + iz[4] = iz[1] + irc.rb3_2; + ix[5] = ix[1] + irc.rc1_2; + iy[5] = iy[1] + irc.rc2_2; + iz[5] = iz[1] + irc.rc3_2; + ix[6] = ix[2] + irc.rc1_2; + iy[6] = iy[2] + irc.rc2_2; + iz[6] = iz[2] + irc.rc3_2; + + for (let j = 0; j < 7; j++) { + const xi = Math.round(ix[j]); + const yi = Math.round(iy[j]); + if ( + xi < 0 || + xi >= this.imageSize || + yi < 0 || + yi >= this.imageSize || + iz[j] < cutdepth || + iz[j] >= this.imageSize + ) { + continue; } - - const ix = [...Array(7)].map(() => 0); - const iy = [...Array(7)].map(() => 0); - const iz = [...Array(7)].map(() => 0); - - for (let i = 0; i < this.header.s_num * 3; i++) { - const x = this.surfaceView[i++]; - const y = this.surfaceView[i++]; - const z = this.surfaceView[i]; - - ix[0] = x * irc.ra1 + y * irc.rb1 + z * irc.rc1 + irc.rd1; - iy[0] = x * irc.ra2 + y * irc.rb2 + z * irc.rc2 + irc.rd2; - iz[0] = x * irc.ra3 + y * irc.rb3 + z * irc.rc3 + irc.rd3; - ix[1] = ix[0] + irc.ra1_2; - iy[1] = iy[0] + irc.ra2_2; - iz[1] = iz[0] + irc.ra3_2; - ix[2] = ix[0] + irc.rb1_2; - iy[2] = iy[0] + irc.rb2_2; - iz[2] = iz[0] + irc.rb3_2; - ix[3] = ix[0] + irc.rc1_2; - iy[3] = iy[0] + irc.rc2_2; - iz[3] = iz[0] + irc.rc3_2; - ix[4] = ix[1] + irc.rb1_2; - iy[4] = iy[1] + irc.rb2_2; - iz[4] = iz[1] + irc.rb3_2; - ix[5] = ix[1] + irc.rc1_2; - iy[5] = iy[1] + irc.rc2_2; - iz[5] = iz[1] + irc.rc3_2; - ix[6] = ix[2] + irc.rc1_2; - iy[6] = iy[2] + irc.rc2_2; - iz[6] = iz[2] + irc.rc3_2; - - for (let j = 0; j < 7; j++) { - const xi = Math.round(ix[j]); - const yi = Math.round(iy[j]); - if (xi < 0 || xi >= this.imageSize || yi < 0 || yi >= this.imageSize || iz[j] < cutdepth || iz[j] >= this.imageSize) { - continue; - } - const pos = xi + yi * this.imageSize; - if (pdimg[pos] === 0 || pdimg[pos] > iz[j]) { - pdimg[pos] = iz[j]; - } - } + const pos = xi + yi * this.imageSize; + if (pdimg[pos] === 0 || pdimg[pos] > iz[j]) { + pdimg[pos] = iz[j]; } + } } + } - private _setMatrix(x: number, y: number, z: number, irc: G3DImageRenderingContext) { - if (this.header === null) { - return; - } - const xtr = [...Array(9)].map(() => 0); - const ytr = [...Array(9)].map(() => 0); - const ztr = [...Array(9)].map(() => 0); - const tmp = [...Array(9)].map(() => 0); - const tr = [...Array(9)].map(() => 0); - this._setXtr(xtr, (x * Math.PI) / 180.0); - this._setYtr(ytr, (y * Math.PI) / 180.0); - this._setZtr(ztr, (z * Math.PI) / 180.0); - this._multi(ytr, xtr, tmp); - this._multi(ztr, tmp, tr); - irc.a1 = tr[0]; - irc.b1 = tr[1]; - irc.c1 = tr[2]; - irc.a2 = tr[3]; - irc.b2 = tr[4]; - irc.c2 = tr[5]; - irc.a3 = tr[6]; - irc.b3 = tr[7]; - irc.c3 = tr[8]; - irc.d1 = -irc.a1 * this.centerX - irc.b1 * this.centerY - irc.c1 * this.centerZ + this.ox; - irc.d2 = -irc.a2 * this.centerX - irc.b2 * this.centerY - irc.c2 * this.centerZ + this.oy; - irc.d3 = -irc.a3 * this.centerX - irc.b3 * this.centerY - irc.c3 * this.centerZ + this.oz; - - const sx = this.header.org_width / this.header.width; - const sy = this.header.org_height / this.header.height; - const sz = this.header.org_depth / this.header.depth; - irc.a1 = sx * irc.a1; - irc.b1 = sx * irc.b1; - irc.c1 = sx * irc.c1; - irc.d1 = sx * irc.d1; - irc.a2 = sy * irc.a2; - irc.b2 = sy * irc.b2; - irc.c2 = sy * irc.c2; - irc.d2 = sy * irc.d2; - irc.a3 = sz * irc.a3; - irc.b3 = sz * irc.b3; - irc.c3 = sz * irc.c3; - irc.d3 = sz * irc.d3; - - this._setXtr(xtr, (-x * Math.PI) / 180.0); - this._setYtr(ytr, (-y * Math.PI) / 180.0); - this._setZtr(ztr, (-z * Math.PI) / 180.0); - this._multi(ytr, ztr, tmp); - this._multi(xtr, tmp, tr); - irc.ra1 = tr[0]; - irc.rb1 = tr[1]; - irc.rc1 = tr[2]; - irc.ra2 = tr[3]; - irc.rb2 = tr[4]; - irc.rc2 = tr[5]; - irc.ra3 = tr[6]; - irc.rb3 = tr[7]; - irc.rc3 = tr[8]; - irc.rd1 = -irc.ra1 * this.ox - irc.rb1 * this.oy - irc.rc1 * this.oz + this.centerX; - irc.rd2 = -irc.ra2 * this.ox - irc.rb2 * this.oy - irc.rc2 * this.oz + this.centerY; - irc.rd3 = -irc.ra3 * this.ox - irc.rb3 * this.oy - irc.rc3 * this.oz + this.centerZ; - irc.ra1_2 = irc.ra1 / 2; - irc.rb1_2 = irc.rb1 / 2; - irc.rc1_2 = irc.rc1 / 2; - irc.ra2_2 = irc.ra2 / 2; - irc.rb2_2 = irc.rb2 / 2; - irc.rc2_2 = irc.rc2 / 2; - irc.ra3_2 = irc.ra3 / 2; - irc.rb3_2 = irc.rb3 / 2; - irc.rc3_2 = irc.rc3 / 2; + private _setMatrix(x: number, y: number, z: number, irc: G3DImageRenderingContext) { + if (this.header === null) { + return; } + const xtr = [...Array(9)].map(() => 0); + const ytr = [...Array(9)].map(() => 0); + const ztr = [...Array(9)].map(() => 0); + const tmp = [...Array(9)].map(() => 0); + const tr = [...Array(9)].map(() => 0); + this._setXtr(xtr, (x * Math.PI) / 180.0); + this._setYtr(ytr, (y * Math.PI) / 180.0); + this._setZtr(ztr, (z * Math.PI) / 180.0); + this._multi(ytr, xtr, tmp); + this._multi(ztr, tmp, tr); + irc.a1 = tr[0]; + irc.b1 = tr[1]; + irc.c1 = tr[2]; + irc.a2 = tr[3]; + irc.b2 = tr[4]; + irc.c2 = tr[5]; + irc.a3 = tr[6]; + irc.b3 = tr[7]; + irc.c3 = tr[8]; + irc.d1 = -irc.a1 * this.centerX - irc.b1 * this.centerY - irc.c1 * this.centerZ + this.ox; + irc.d2 = -irc.a2 * this.centerX - irc.b2 * this.centerY - irc.c2 * this.centerZ + this.oy; + irc.d3 = -irc.a3 * this.centerX - irc.b3 * this.centerY - irc.c3 * this.centerZ + this.oz; - private _setXtr(xtr: number[], ang: number) { - xtr[0] = 1; - xtr[1] = 0; - xtr[2] = 0; - xtr[3] = 0; - xtr[4] = Math.cos(ang); - xtr[5] = -Math.sin(ang); - xtr[6] = 0; - xtr[7] = Math.sin(ang); - xtr[8] = Math.cos(ang); - } + const sx = this.header.org_width / this.header.width; + const sy = this.header.org_height / this.header.height; + const sz = this.header.org_depth / this.header.depth; + irc.a1 = sx * irc.a1; + irc.b1 = sx * irc.b1; + irc.c1 = sx * irc.c1; + irc.d1 = sx * irc.d1; + irc.a2 = sy * irc.a2; + irc.b2 = sy * irc.b2; + irc.c2 = sy * irc.c2; + irc.d2 = sy * irc.d2; + irc.a3 = sz * irc.a3; + irc.b3 = sz * irc.b3; + irc.c3 = sz * irc.c3; + irc.d3 = sz * irc.d3; - private _setYtr(ytr: number[], ang: number) { - ytr[0] = Math.cos(ang); - ytr[1] = 0; - ytr[2] = Math.sin(ang); - ytr[3] = 0; - ytr[4] = 1; - ytr[5] = 0; - ytr[6] = -Math.sin(ang); - ytr[7] = 0; - ytr[8] = Math.cos(ang); - } + this._setXtr(xtr, (-x * Math.PI) / 180.0); + this._setYtr(ytr, (-y * Math.PI) / 180.0); + this._setZtr(ztr, (-z * Math.PI) / 180.0); + this._multi(ytr, ztr, tmp); + this._multi(xtr, tmp, tr); + irc.ra1 = tr[0]; + irc.rb1 = tr[1]; + irc.rc1 = tr[2]; + irc.ra2 = tr[3]; + irc.rb2 = tr[4]; + irc.rc2 = tr[5]; + irc.ra3 = tr[6]; + irc.rb3 = tr[7]; + irc.rc3 = tr[8]; + irc.rd1 = -irc.ra1 * this.ox - irc.rb1 * this.oy - irc.rc1 * this.oz + this.centerX; + irc.rd2 = -irc.ra2 * this.ox - irc.rb2 * this.oy - irc.rc2 * this.oz + this.centerY; + irc.rd3 = -irc.ra3 * this.ox - irc.rb3 * this.oy - irc.rc3 * this.oz + this.centerZ; + irc.ra1_2 = irc.ra1 / 2; + irc.rb1_2 = irc.rb1 / 2; + irc.rc1_2 = irc.rc1 / 2; + irc.ra2_2 = irc.ra2 / 2; + irc.rb2_2 = irc.rb2 / 2; + irc.rc2_2 = irc.rc2 / 2; + irc.ra3_2 = irc.ra3 / 2; + irc.rb3_2 = irc.rb3 / 2; + irc.rc3_2 = irc.rc3 / 2; + } - private _setZtr(ztr: number[], ang: number) { - ztr[0] = Math.cos(ang); - ztr[1] = -Math.sin(ang); - ztr[2] = 0; - ztr[3] = Math.sin(ang); - ztr[4] = Math.cos(ang); - ztr[5] = 0; - ztr[6] = 0; - ztr[7] = 0; - ztr[8] = 1; - } + private _setXtr(xtr: number[], ang: number) { + xtr[0] = 1; + xtr[1] = 0; + xtr[2] = 0; + xtr[3] = 0; + xtr[4] = Math.cos(ang); + xtr[5] = -Math.sin(ang); + xtr[6] = 0; + xtr[7] = Math.sin(ang); + xtr[8] = Math.cos(ang); + } - private _multi(l: number[], r: number[], a: number[]) { - a[0] = l[0] * r[0] + l[1] * r[3] + l[2] * r[6]; - a[3] = l[3] * r[0] + l[4] * r[3] + l[5] * r[6]; - a[6] = l[6] * r[0] + l[7] * r[3] + l[8] * r[6]; - a[1] = l[0] * r[1] + l[1] * r[4] + l[2] * r[7]; - a[4] = l[3] * r[1] + l[4] * r[4] + l[5] * r[7]; - a[7] = l[6] * r[1] + l[7] * r[4] + l[8] * r[7]; - a[2] = l[0] * r[2] + l[1] * r[5] + l[2] * r[8]; - a[5] = l[3] * r[2] + l[4] * r[5] + l[5] * r[8]; - a[8] = l[6] * r[2] + l[7] * r[5] + l[8] * r[8]; - } + private _setYtr(ytr: number[], ang: number) { + ytr[0] = Math.cos(ang); + ytr[1] = 0; + ytr[2] = Math.sin(ang); + ytr[3] = 0; + ytr[4] = 1; + ytr[5] = 0; + ytr[6] = -Math.sin(ang); + ytr[7] = 0; + ytr[8] = Math.cos(ang); + } - private _fround(x: number, n: number) { - return Math.round(x * Math.pow(10, n)) / Math.pow(10, n); - } + private _setZtr(ztr: number[], ang: number) { + ztr[0] = Math.cos(ang); + ztr[1] = -Math.sin(ang); + ztr[2] = 0; + ztr[3] = Math.sin(ang); + ztr[4] = Math.cos(ang); + ztr[5] = 0; + ztr[6] = 0; + ztr[7] = 0; + ztr[8] = 1; + } - private _initialize(data: ArrayBuffer): boolean { - // check browser endianness - const isBigEndian = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x11; - // check magic header and file endianness - const view = new DataView(data); - const magic = view.getInt32(0, true); - let needSwap = false; - switch (magic) { - case 0x474f3344: - case 0x47483344: - case 0x434f3344: - break; - case 0x44334f47: - case 0x44334847: - case 0x44334f43: - needSwap = true; - break; - default: - console.log('invalid magic header found, data is not g3d format.'); - return false; - } - this.isLittleEndianFile = isBigEndian ? needSwap : !needSwap; - // read header - const decoder = new TextDecoder('utf-8'); - let headerOffset = 0; - this.header = { - magic: view.getInt32(headerOffset, this.isLittleEndianFile), - width: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - height: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - depth: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - org_width: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - org_height: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - org_depth: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - datatype: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - unit: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - grid: view.getFloat64((headerOffset += 4), this.isLittleEndianFile), - vs_width: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - v_width: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - v_height: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - v_depth: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - ax: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - ay: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - az: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - ox: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - oy: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - oz: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), - s_num: view.getInt32((headerOffset += 8), this.isLittleEndianFile), - s_th: view.getInt32((headerOffset += 4), this.isLittleEndianFile), - option: decoder.decode(new Uint8Array(data, (headerOffset += 4), 256)).replace(/\0+$/, ''), - date: decoder.decode(new Uint8Array(data, (headerOffset += 256), 32)).replace(/\0+$/, ''), - coordinate: decoder.decode(new Uint8Array(data, (headerOffset += 32), 32)).replace(/\0+$/, ''), - subject: decoder.decode(new Uint8Array(data, (headerOffset += 32), 256)).replace(/\0+$/, ''), - memo: decoder.decode(new Uint8Array(data, (headerOffset += 256), 256)).replace(/\0+$/, ''), - }; - // set surface and image views - const surfaceOffset = 964; - const surfaceLength = this.header.s_num * 3; - this.surfaceView = new Int16Array(data, surfaceOffset, surfaceLength); - if (needSwap) { - // fix endianness - let offset = surfaceOffset; - for (let i = 0; i < surfaceLength; i++) { - const ui8 = new Uint8Array(data, offset, 2); - ui8.reverse(); - offset += 2; - } - } - const imgOffset = surfaceOffset + surfaceLength * 2; - const imgLength = this.header.org_width * this.header.org_height * this.header.org_depth * this.header.datatype; - this.imgView = new Uint8Array(data, imgOffset, imgLength); - // set offset for image rendering - this.imageSize = Math.ceil(Math.sqrt(this.header.width * this.header.width + this.header.height * this.header.height + this.header.depth * this.header.depth)) + 1; - this.centerX = (this.imageSize - 1) / 2.0; - this.centerY = (this.imageSize - 1) / 2.0; - this.centerZ = (this.imageSize - 1) / 2.0; - this.ox = (this.header.width - 1) / 2.0; - this.oy = (this.header.height - 1) / 2.0; - this.oz = (this.header.depth - 1) / 2.0; - // set data - this.data = data; - return true; + private _multi(l: number[], r: number[], a: number[]) { + a[0] = l[0] * r[0] + l[1] * r[3] + l[2] * r[6]; + a[3] = l[3] * r[0] + l[4] * r[3] + l[5] * r[6]; + a[6] = l[6] * r[0] + l[7] * r[3] + l[8] * r[6]; + a[1] = l[0] * r[1] + l[1] * r[4] + l[2] * r[7]; + a[4] = l[3] * r[1] + l[4] * r[4] + l[5] * r[7]; + a[7] = l[6] * r[1] + l[7] * r[4] + l[8] * r[7]; + a[2] = l[0] * r[2] + l[1] * r[5] + l[2] * r[8]; + a[5] = l[3] * r[2] + l[4] * r[5] + l[5] * r[8]; + a[8] = l[6] * r[2] + l[7] * r[5] + l[8] * r[8]; + } + + private _fround(x: number, n: number) { + return Math.round(x * 10 ** n) / 10 ** n; + } + + private _initialize(data: ArrayBuffer): boolean { + // check browser endianness + const isBigEndian = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x11; + // check magic header and file endianness + const view = new DataView(data); + const magic = view.getInt32(0, true); + let needSwap = false; + switch (magic) { + case 0x474f3344: + case 0x47483344: + case 0x434f3344: + break; + case 0x44334f47: + case 0x44334847: + case 0x44334f43: + needSwap = true; + break; + default: + console.log('invalid magic header found, data is not g3d format.'); + return false; } + this.isLittleEndianFile = isBigEndian ? needSwap : !needSwap; + // read header + const decoder = new TextDecoder('utf-8'); + let headerOffset = 0; + this.header = { + magic: view.getInt32(headerOffset, this.isLittleEndianFile), + width: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + height: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + depth: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + org_width: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + org_height: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + org_depth: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + datatype: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + unit: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + grid: view.getFloat64((headerOffset += 4), this.isLittleEndianFile), + vs_width: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + v_width: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + v_height: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + v_depth: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + ax: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + ay: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + az: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + ox: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + oy: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + oz: view.getFloat64((headerOffset += 8), this.isLittleEndianFile), + s_num: view.getInt32((headerOffset += 8), this.isLittleEndianFile), + s_th: view.getInt32((headerOffset += 4), this.isLittleEndianFile), + option: decoder.decode(new Uint8Array(data, (headerOffset += 4), 256)).replace(/\0+$/, ''), + date: decoder.decode(new Uint8Array(data, (headerOffset += 256), 32)).replace(/\0+$/, ''), + coordinate: decoder.decode(new Uint8Array(data, (headerOffset += 32), 32)).replace(/\0+$/, ''), + subject: decoder.decode(new Uint8Array(data, (headerOffset += 32), 256)).replace(/\0+$/, ''), + memo: decoder.decode(new Uint8Array(data, (headerOffset += 256), 256)).replace(/\0+$/, ''), + }; + void headerOffset; // last use + // set surface and image views + const surfaceOffset = 964; + const surfaceLength = this.header.s_num * 3; + this.surfaceView = new Int16Array(data, surfaceOffset, surfaceLength); + if (needSwap) { + // fix endianness + let offset = surfaceOffset; + for (let i = 0; i < surfaceLength; i++) { + const ui8 = new Uint8Array(data, offset, 2); + ui8.reverse(); + offset += 2; + } + } + const imgOffset = surfaceOffset + surfaceLength * 2; + const imgLength = this.header.org_width * this.header.org_height * this.header.org_depth * this.header.datatype; + this.imgView = new Uint8Array(data, imgOffset, imgLength); + // set offset for image rendering + this.imageSize = + Math.ceil( + Math.sqrt( + this.header.width * this.header.width + + this.header.height * this.header.height + + this.header.depth * this.header.depth, + ), + ) + 1; + this.centerX = (this.imageSize - 1) / 2.0; + this.centerY = (this.imageSize - 1) / 2.0; + this.centerZ = (this.imageSize - 1) / 2.0; + this.ox = (this.header.width - 1) / 2.0; + this.oy = (this.header.height - 1) / 2.0; + this.oz = (this.header.depth - 1) / 2.0; + + return true; + } } export default G3D; diff --git a/src/brainexplorer/lib/m3d.ts b/src/brainexplorer/lib/m3d.ts index 550aad5..5bc8380 100644 --- a/src/brainexplorer/lib/m3d.ts +++ b/src/brainexplorer/lib/m3d.ts @@ -1,616 +1,640 @@ export interface M3DHeader { - magic: number; - x: number; - y: number; - z: number; - filename: string; - subject: string; - direction: string; - date: number; - fov: number; - weight: number; - ax: number; - ay: number; - az: number; - ox: number; - oy: number; - oz: number; - dlen: number; - dth: number; - memo: string; + magic: number; + x: number; + y: number; + z: number; + filename: string; + subject: string; + direction: string; + date: number; + fov: number; + weight: number; + ax: number; + ay: number; + az: number; + ox: number; + oy: number; + oz: number; + dlen: number; + dth: number; + memo: string; } interface M3DImageRenderingContext { - a1: number; - a2: number; - a3: number; - b1: number; - b2: number; - b3: number; - c1: number; - c2: number; - c3: number; - d1: number; - d2: number; - d3: number; - ra1: number; - ra1_2: number; - ra2: number; - ra2_2: number; - ra3: number; - ra3_2: number; - rb1: number; - rb1_2: number; - rb2: number; - rb2_2: number; - rb3: number; - rb3_2: number; - rc1: number; - rc1_2: number; - rc2: number; - rc2_2: number; - rc3: number; - rc3_2: number; - rd1: number; - rd2: number; - rd3: number; + a1: number; + a2: number; + a3: number; + b1: number; + b2: number; + b3: number; + c1: number; + c2: number; + c3: number; + d1: number; + d2: number; + d3: number; + ra1: number; + ra1_2: number; + ra2: number; + ra2_2: number; + ra3: number; + ra3_2: number; + rb1: number; + rb1_2: number; + rb2: number; + rb2_2: number; + rb3: number; + rb3_2: number; + rc1: number; + rc1_2: number; + rc2: number; + rc2_2: number; + rc3: number; + rc3_2: number; + rd1: number; + rd2: number; + rd3: number; } /* MeshLength unit is same as fov unit */ const MeshLength = 10; -enum DrawingLineType { - VLine = 0, - VDottedLine = 1, - HLine = 2, - HDottedLine = 3, -} +const DrawingLineType = { + VLine: 0, + VDottedLine: 1, + HLine: 2, + HDottedLine: 3, +} as const; +type DrawingLineType = (typeof DrawingLineType)[keyof typeof DrawingLineType]; const LineBrightness = 200; const DottedLineBrightness = 128; const EXACTSWITCH: 0 | 1 = 1; const MaxIntensity = 200; class M3D { - private data: ArrayBuffer; - private header: M3DHeader | null = null; - private xposView: Int32Array | null = null; - private zposView: Int32Array | null = null; - private yposView: Int32Array | null = null; - private img3dView: Uint8ClampedArray | null = null; - private imageSize = 0; // imagesize size : max (size_x, size_y, size_z) - private centerX = 0; // camera coordinates origin : x - private centerY = 0; // camera coordinates origin : y - private centerZ = 0; // camera coordinates origin : z - private ox = 0; // object coordinates origin : x - private oy = 0; // object coordinates origin : y - private oz = 0; // object coordinates origin : z + private data: ArrayBuffer; + private header: M3DHeader | null = null; + private xposView: Int32Array | null = null; + private zposView: Int32Array | null = null; + private yposView: Int32Array | null = null; + private img3dView: Uint8ClampedArray | null = null; + private imageSize = 0; // imagesize size : max (size_x, size_y, size_z) + private centerX = 0; // camera coordinates origin : x + private centerY = 0; // camera coordinates origin : y + private centerZ = 0; // camera coordinates origin : z + private ox = 0; // object coordinates origin : x + private oy = 0; // object coordinates origin : y + private oz = 0; // object coordinates origin : z - constructor(data: ArrayBuffer) { - this.data = data; - this._initialize(); + constructor(data: ArrayBuffer) { + this.data = data; + this._initialize(); + } + + isBigEndian(): boolean { + return new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x11; + } + + getHeader(): M3DHeader | null { + return this.header; + } + + drawImage( + canvas: HTMLCanvasElement, + ax: number, + ay: number, + az: number, + cutdepth: number, + th: number, + is3d: boolean, + mesh: boolean, + ): boolean { + const irc: M3DImageRenderingContext = { + a1: 0, + a2: 0, + a3: 0, + b1: 0, + b2: 0, + b3: 0, + c1: 0, + c2: 0, + c3: 0, + d1: 0, + d2: 0, + d3: 0, + ra1: 0, + ra1_2: 0, + ra2: 0, + ra2_2: 0, + ra3: 0, + ra3_2: 0, + rb1: 0, + rb1_2: 0, + rb2: 0, + rb2_2: 0, + rb3: 0, + rb3_2: 0, + rc1: 0, + rc1_2: 0, + rc2: 0, + rc2_2: 0, + rc3: 0, + rc3_2: 0, + rd1: 0, + rd2: 0, + rd3: 0, + }; + // console.log(ax, ay, az, cutdepth, th, dim, mesh) + if (this.header === null) { + return false; } - isBigEndian(): boolean { - return new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x11; + canvas.width = this.imageSize; + canvas.height = this.imageSize; + const ctx = canvas.getContext('2d'); + if (ctx === null) { + return false; } + ctx.fillStyle = 'rgb(0, 0, 0)'; + ctx.fillRect(0, 0, this.imageSize, this.imageSize); + const img = ctx.getImageData(0, 0, this.imageSize, this.imageSize); - getHeader(): M3DHeader | null { - return this.header; + this._setMatrix(ax, ay, az, irc); + if (is3d) { + const ddimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); + this._makeDIMG(img, cutdepth, th, ddimg, irc); + this._makeShadingImage(img, ddimg); + } else { + this._makeSliceImage(img, cutdepth, irc); } - - drawImage(canvas: HTMLCanvasElement, ax: number, ay: number, az: number, cutdepth: number, th: number, is3d: boolean, mesh: boolean): boolean { - const irc: M3DImageRenderingContext = { - a1: 0, - a2: 0, - a3: 0, - b1: 0, - b2: 0, - b3: 0, - c1: 0, - c2: 0, - c3: 0, - d1: 0, - d2: 0, - d3: 0, - ra1: 0, - ra1_2: 0, - ra2: 0, - ra2_2: 0, - ra3: 0, - ra3_2: 0, - rb1: 0, - rb1_2: 0, - rb2: 0, - rb2_2: 0, - rb3: 0, - rb3_2: 0, - rc1: 0, - rc1_2: 0, - rc2: 0, - rc2_2: 0, - rc3: 0, - rc3_2: 0, - rd1: 0, - rd2: 0, - rd3: 0, - }; - // console.log(ax, ay, az, cutdepth, th, dim, mesh) - if (this.header === null) { - return false; - } - - canvas.width = this.imageSize; - canvas.height = this.imageSize; - const ctx = canvas.getContext('2d'); - if (ctx === null) { - return false; - } - ctx.fillStyle = 'rgb(0, 0, 0)'; - ctx.fillRect(0, 0, this.imageSize, this.imageSize); - const img = ctx.getImageData(0, 0, this.imageSize, this.imageSize); - - this._setMatrix(ax, ay, az, irc); - if (is3d) { - const ddimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); - this._makeDIMG(img, cutdepth, th, ddimg, irc); - this._makeShadingImage(img, ddimg); - } else { - this._makeSliceImage(img, cutdepth, irc); - } - if (mesh) { - this._drawMesh(img, irc); - } - ctx.putImageData(img, 0, 0); - return true; + if (mesh) { + this._drawMesh(img, irc); } + ctx.putImageData(img, 0, 0); + return true; + } - private _makeDIMG(img: ImageData, cutdepth: number, th: number, ddimg: number[], irc: M3DImageRenderingContext) { - if (this.header === null) { - return; - } - const pdimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); - const sw = th > this.header.dth ? 1 : th < this.header.dth ? 2 : EXACTSWITCH; - switch (sw) { - case 0: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let j = 0; j < this.imageSize; j++) { - for (let i = 0; i < this.imageSize; i++, pos++) { - ddimg[pos] = pdimg[pos]; - const pix = this._getPixel(i, j, cutdepth, irc); - if (pix > th) { - this._drawPixel(img, pos, pix, pix, pix); - ddimg[pos] = cutdepth; - continue; - } - } - } - break; - } - case 1: { - this._transSurface(cutdepth, pdimg, irc); - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - let pdPos = (ddimg[pos] = pdimg[pos]); - if (pdPos === 0) { - continue; - } - const pix = this._getPixel(x, y, cutdepth, irc); - if (pix > th) { - this._drawPixel(img, pos, pix, pix, pix); - ddimg[pos] = cutdepth; - continue; - } - if (pdPos < cutdepth) { - pdPos = cutdepth; - } - ddimg[pos] = 0; - for (let z = pdPos; z < this.imageSize; z++) { - const pix = this._getPixel(x, y, z, irc); - if (pix > th) { - ddimg[pos] = z; - break; - } - } - } - } - break; - } - case 2: { - let pos = 0; - for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - ddimg[pos] = 0; - for (let z = cutdepth; z < this.imageSize; z++) { - const pix = this._getPixel(x, y, z, irc); - if (pix > th) { - if (z === cutdepth) { - this._drawPixel(img, pos, pix, pix, pix); - } else { - ddimg[pos] = z; - } - break; - } - } - } - } - break; - } - } + private _makeDIMG(img: ImageData, cutdepth: number, th: number, ddimg: number[], irc: M3DImageRenderingContext) { + if (this.header === null) { + return; } - - private _makeShadingImage(img: ImageData, ddimg: number[]) { + const pdimg = [...Array(this.imageSize * this.imageSize)].map(() => 0); + const sw = th > this.header.dth ? 1 : th < this.header.dth ? 2 : EXACTSWITCH; + switch (sw) { + case 0: { + this._transSurface(cutdepth, pdimg, irc); let pos = 0; - for (let x = 0; x < this.imageSize; x++) { - for (let y = 0; y < this.imageSize; y++, pos++) { - const base = pos * 4; - if (img.data[base] === 0) { - let timg2d = 0; - if (x < this.imageSize - 2 && y < this.imageSize - 2) { - if (ddimg[pos] !== 0) { - const nx = (ddimg[pos] + ddimg[pos + this.imageSize] - ddimg[pos + 2] - ddimg[pos + 2 + this.imageSize]) / 4.0; - const ny = (ddimg[pos] + ddimg[pos + 1] - ddimg[pos + this.imageSize * 2] - ddimg[pos + 1 + this.imageSize * 2]) / 4.0; - const n = Math.sqrt(nx * nx + ny * ny + 1); - timg2d = Math.round(MaxIntensity / n); - } - } - this._drawPixel(img, pos, timg2d, timg2d, timg2d); - } + for (let j = 0; j < this.imageSize; j++) { + for (let i = 0; i < this.imageSize; i++, pos++) { + ddimg[pos] = pdimg[pos]; + const pix = this._getPixel(i, j, cutdepth, irc); + if (pix > th) { + this._drawPixel(img, pos, pix, pix, pix); + ddimg[pos] = cutdepth; } + } } - } - - private _makeSliceImage(img: ImageData, cutdepth: number, irc: M3DImageRenderingContext) { + break; + } + case 1: { + this._transSurface(cutdepth, pdimg, irc); let pos = 0; for (let y = 0; y < this.imageSize; y++) { - for (let x = 0; x < this.imageSize; x++, pos++) { - const pix = this._getPixel(x, y, cutdepth, irc); - this._drawPixel(img, pos, pix, pix, pix); + for (let x = 0; x < this.imageSize; x++, pos++) { + let pdPos = (ddimg[pos] = pdimg[pos]); + if (pdPos === 0) { + continue; } + const pix = this._getPixel(x, y, cutdepth, irc); + if (pix > th) { + this._drawPixel(img, pos, pix, pix, pix); + ddimg[pos] = cutdepth; + continue; + } + if (pdPos < cutdepth) { + pdPos = cutdepth; + } + ddimg[pos] = 0; + for (let z = pdPos; z < this.imageSize; z++) { + const pix = this._getPixel(x, y, z, irc); + if (pix > th) { + ddimg[pos] = z; + break; + } + } + } } - } - - private _drawMesh(img: ImageData, irc: M3DImageRenderingContext) { - let origx: number; - let origy: number; - if (this.header === null) { - return; - } - if (this.header.ox !== 0 && this.header.oy !== 0) { - origx = this.header.ox * irc.ra1 + this.header.oy * irc.rb1 + this.header.oz * irc.rc1 + irc.rd1; - origy = this.header.ox * irc.ra2 + this.header.oy * irc.rb2 + this.header.oz * irc.rc2 + irc.rd2; - } else { - origx = this.centerX; - origy = this.centerY; - } - origx = this._fround(origx, 5); - origy = this._fround(origy, 5); - - const band = (this.header.x * MeshLength) / this.header.fov; - const r = 0; - const g = 0; - const b = LineBrightness; - const sr = 0; - const sg = 0; - const sb = DottedLineBrightness; - this._drawLine(img, Math.round(origx), r, g, b, DrawingLineType.VLine); - this._drawLine(img, Math.round(origy), r, g, b, DrawingLineType.HLine); - for (let pos = Math.round(origx) + band; pos < this.imageSize; pos += band) { - this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.VDottedLine); - } - for (let pos = Math.round(origx) - band; pos >= 0; pos -= band) { - this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.VDottedLine); - } - for (let pos = Math.round(origy) + band; pos < this.imageSize; pos += band) { - this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.HDottedLine); - } - for (let pos = Math.round(origy) - band; pos >= 0; pos -= band) { - this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.HDottedLine); - } - } - - private _drawLine(img: ImageData, value: number, r: number, g: number, b: number, type: DrawingLineType) { - switch (type) { - case DrawingLineType.VLine: { - let pos = value; - for (let y = 0; y < this.imageSize; y++, pos += this.imageSize) { - this._drawPixel(img, pos, r, g, b); + break; + } + case 2: { + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + ddimg[pos] = 0; + for (let z = cutdepth; z < this.imageSize; z++) { + const pix = this._getPixel(x, y, z, irc); + if (pix > th) { + if (z === cutdepth) { + this._drawPixel(img, pos, pix, pix, pix); + } else { + ddimg[pos] = z; } break; + } } - case DrawingLineType.VDottedLine: { - let pos = value; - for (let y = 0; y < this.imageSize; y += 2, pos += this.imageSize * 2) { - this._drawPixel(img, pos, r, g, b); - } - break; + } + } + break; + } + } + } + + private _makeShadingImage(img: ImageData, ddimg: number[]) { + let pos = 0; + for (let x = 0; x < this.imageSize; x++) { + for (let y = 0; y < this.imageSize; y++, pos++) { + const base = pos * 4; + if (img.data[base] === 0) { + let timg2d = 0; + if (x < this.imageSize - 2 && y < this.imageSize - 2) { + if (ddimg[pos] !== 0) { + const nx = + (ddimg[pos] + ddimg[pos + this.imageSize] - ddimg[pos + 2] - ddimg[pos + 2 + this.imageSize]) / 4.0; + const ny = + (ddimg[pos] + ddimg[pos + 1] - ddimg[pos + this.imageSize * 2] - ddimg[pos + 1 + this.imageSize * 2]) / + 4.0; + const n = Math.sqrt(nx * nx + ny * ny + 1); + timg2d = Math.round(MaxIntensity / n); } - case DrawingLineType.HLine: { - let pos = value * this.imageSize; - for (let x = 0; x < this.imageSize; x++, pos++) { - this._drawPixel(img, pos, r, g, b); - } - break; - } - case DrawingLineType.HDottedLine: { - let pos = value * this.imageSize; - for (let x = 0; x < this.imageSize; x += 2, pos += 2) { - this._drawPixel(img, pos, r, g, b); - } - break; - } - default: - break; + } + this._drawPixel(img, pos, timg2d, timg2d, timg2d); } + } } + } - private _drawPixel(img: ImageData, pos: number, r: number, g: number, b: number) { - let base = pos * 4; - img.data[base++] = r; - img.data[base++] = g; - img.data[base++] = b; - img.data[base] = 255; + private _makeSliceImage(img: ImageData, cutdepth: number, irc: M3DImageRenderingContext) { + let pos = 0; + for (let y = 0; y < this.imageSize; y++) { + for (let x = 0; x < this.imageSize; x++, pos++) { + const pix = this._getPixel(x, y, cutdepth, irc); + this._drawPixel(img, pos, pix, pix, pix); + } } + } - private _getPixel(x: number, y: number, z: number, irc: M3DImageRenderingContext): number { - const len = [...Array(8)].map(() => 0); - const ratio = [...Array(8)].map(() => 0); + private _drawMesh(img: ImageData, irc: M3DImageRenderingContext) { + let origx: number; + let origy: number; + if (this.header === null) { + return; + } + if (this.header.ox !== 0 && this.header.oy !== 0) { + origx = this.header.ox * irc.ra1 + this.header.oy * irc.rb1 + this.header.oz * irc.rc1 + irc.rd1; + origy = this.header.ox * irc.ra2 + this.header.oy * irc.rb2 + this.header.oz * irc.rc2 + irc.rd2; + } else { + origx = this.centerX; + origy = this.centerY; + } + origx = this._fround(origx, 5); + origy = this._fround(origy, 5); - const tx = x * irc.a1 + y * irc.b1 + z * irc.c1 + irc.d1; - const ty = x * irc.a2 + y * irc.b2 + z * irc.c2 + irc.d2; - const tz = x * irc.a3 + y * irc.b3 + z * irc.c3 + irc.d3; + const band = (this.header.x * MeshLength) / this.header.fov; + const r = 0; + const g = 0; + const b = LineBrightness; + const sr = 0; + const sg = 0; + const sb = DottedLineBrightness; + this._drawLine(img, Math.round(origx), r, g, b, DrawingLineType.VLine); + this._drawLine(img, Math.round(origy), r, g, b, DrawingLineType.HLine); + for (let pos = Math.round(origx) + band; pos < this.imageSize; pos += band) { + this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.VDottedLine); + } + for (let pos = Math.round(origx) - band; pos >= 0; pos -= band) { + this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.VDottedLine); + } + for (let pos = Math.round(origy) + band; pos < this.imageSize; pos += band) { + this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.HDottedLine); + } + for (let pos = Math.round(origy) - band; pos >= 0; pos -= band) { + this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.HDottedLine); + } + } - let ix = Math.floor(tx); - let iy = Math.floor(ty); - let iz = Math.floor(tz); - - if (this.header === null || this.img3dView === null) { - return 0; + private _drawLine(img: ImageData, value: number, r: number, g: number, b: number, type: DrawingLineType) { + switch (type) { + case DrawingLineType.VLine: { + let pos = value; + for (let y = 0; y < this.imageSize; y++, pos += this.imageSize) { + this._drawPixel(img, pos, r, g, b); } - if (ix < 0 || ix + 1 >= this.header.x || iy < 0 || iy + 1 >= this.header.y || iz < 0 || iz + 1 >= this.header.z) { - return 0; + break; + } + case DrawingLineType.VDottedLine: { + let pos = value; + for (let y = 0; y < this.imageSize; y += 2, pos += this.imageSize * 2) { + this._drawPixel(img, pos, r, g, b); } - - const ax1 = Math.abs(tx - ix); - const ax2 = Math.abs(1 - ax1); - const ay1 = Math.abs(ty - iy); - const ay2 = Math.abs(1 - ay1); - const az1 = Math.abs(tz - iz); - const az2 = Math.abs(1 - az1); - - let sumlen1 = 0; - for (let i = 0; i < 8; i++) { - len[i] = i & 1 ? ax2 : ax1; - len[i] += i & 2 ? ay2 : ay1; - len[i] += i & 4 ? az2 : az1; - if (len[i] < 0.001) { - ix += i & 1 ? 1 : 0; - iy += i & 2 ? 1 : 0; - iz += i & 4 ? 1 : 0; - return this.img3dView[ix + iy * this.header.x + iz * this.header.x * this.header.y]; - } - sumlen1 += ratio[i] = 1 / len[i]; + break; + } + case DrawingLineType.HLine: { + let pos = value * this.imageSize; + for (let x = 0; x < this.imageSize; x++, pos++) { + this._drawPixel(img, pos, r, g, b); } - let pos = ix + iy * this.header.x + iz * this.header.x * this.header.y; - let sumlen2 = this.img3dView[pos] * ratio[0]; - sumlen2 += this.img3dView[pos + 1] * ratio[1]; - sumlen2 += this.img3dView[pos + this.header.x] * ratio[2]; - sumlen2 += this.img3dView[pos + 1 + this.header.x] * ratio[3]; - pos += this.header.x * this.header.y; - sumlen2 += this.img3dView[pos] * ratio[4]; - sumlen2 += this.img3dView[pos + 1] * ratio[5]; - sumlen2 += this.img3dView[pos + this.header.x] * ratio[6]; - sumlen2 += this.img3dView[pos + 1 + this.header.x] * ratio[7]; - - return Math.round(sumlen2 / sumlen1); - } - - private _transSurface(cutdepth: number, pdimg: number[], irc: M3DImageRenderingContext) { - if (this.header === null || this.xposView === null || this.yposView === null || this.zposView === null) { - return; + break; + } + case DrawingLineType.HDottedLine: { + let pos = value * this.imageSize; + for (let x = 0; x < this.imageSize; x += 2, pos += 2) { + this._drawPixel(img, pos, r, g, b); } - const ix = [...Array(8)].map(() => 0); - const iy = [...Array(8)].map(() => 0); - const iz = [...Array(8)].map(() => 0); - for (let i = 0; i < this.header.dlen; i++) { - const x = this.xposView[i]; - const y = this.yposView[i]; - const z = this.zposView[i]; - ix[0] = x * irc.ra1 + y * irc.rb1 + z * irc.rc1 + irc.rd1; - iy[0] = x * irc.ra2 + y * irc.rb2 + z * irc.rc2 + irc.rd2; - iz[0] = x * irc.ra3 + y * irc.rb3 + z * irc.rc3 + irc.rd3; - ix[1] = ix[0] + irc.ra1_2; - iy[1] = iy[0] + irc.ra2_2; - iz[1] = iz[0] + irc.ra3_2; - ix[2] = ix[0] + irc.rb1_2; - iy[2] = iy[0] + irc.rb2_2; - iz[2] = iz[0] + irc.rb3_2; - ix[3] = ix[0] + irc.rc1_2; - iy[3] = iy[0] + irc.rc2_2; - iz[3] = iz[0] + irc.rc3_2; - ix[4] = ix[1] + irc.rb1_2; - iy[4] = iy[1] + irc.rb2_2; - iz[4] = iz[1] + irc.rb3_2; - ix[5] = ix[1] + irc.rc1_2; - iy[5] = iy[1] + irc.rc2_2; - iz[5] = iz[1] + irc.rc3_2; - ix[6] = ix[2] + irc.rc1_2; - iy[6] = iy[2] + irc.rc2_2; - iz[6] = iz[2] + irc.rc3_2; - ix[7] = ix[4] + irc.rc1_2; - iy[7] = iy[4] + irc.rc2_2; - iz[7] = iz[4] + irc.rc3_2; - for (let j = 0; j < 8; j++) { - const xi = Math.round(ix[j]); - const yi = Math.round(iy[j]); - if (xi < 0 || xi >= this.imageSize || yi < 0 || yi >= this.imageSize || iz[j] < cutdepth || iz[j] >= this.imageSize) { - continue; - } - const pos = xi + yi * this.imageSize; - if (pdimg[pos] === 0 || pdimg[pos] > iz[j]) { - pdimg[pos] = iz[j]; - } - } + break; + } + default: + break; + } + } + + private _drawPixel(img: ImageData, pos: number, r: number, g: number, b: number) { + let base = pos * 4; + img.data[base++] = r; + img.data[base++] = g; + img.data[base++] = b; + img.data[base] = 255; + } + + private _getPixel(x: number, y: number, z: number, irc: M3DImageRenderingContext): number { + const len = [...Array(8)].map(() => 0); + const ratio = [...Array(8)].map(() => 0); + + const tx = x * irc.a1 + y * irc.b1 + z * irc.c1 + irc.d1; + const ty = x * irc.a2 + y * irc.b2 + z * irc.c2 + irc.d2; + const tz = x * irc.a3 + y * irc.b3 + z * irc.c3 + irc.d3; + + let ix = Math.floor(tx); + let iy = Math.floor(ty); + let iz = Math.floor(tz); + + if (this.header === null || this.img3dView === null) { + return 0; + } + if (ix < 0 || ix + 1 >= this.header.x || iy < 0 || iy + 1 >= this.header.y || iz < 0 || iz + 1 >= this.header.z) { + return 0; + } + + const ax1 = Math.abs(tx - ix); + const ax2 = Math.abs(1 - ax1); + const ay1 = Math.abs(ty - iy); + const ay2 = Math.abs(1 - ay1); + const az1 = Math.abs(tz - iz); + const az2 = Math.abs(1 - az1); + + let sumlen1 = 0; + for (let i = 0; i < 8; i++) { + len[i] = i & 1 ? ax2 : ax1; + len[i] += i & 2 ? ay2 : ay1; + len[i] += i & 4 ? az2 : az1; + if (len[i] < 0.001) { + ix += i & 1 ? 1 : 0; + iy += i & 2 ? 1 : 0; + iz += i & 4 ? 1 : 0; + return this.img3dView[ix + iy * this.header.x + iz * this.header.x * this.header.y]; + } + sumlen1 += ratio[i] = 1 / len[i]; + } + let pos = ix + iy * this.header.x + iz * this.header.x * this.header.y; + let sumlen2 = this.img3dView[pos] * ratio[0]; + sumlen2 += this.img3dView[pos + 1] * ratio[1]; + sumlen2 += this.img3dView[pos + this.header.x] * ratio[2]; + sumlen2 += this.img3dView[pos + 1 + this.header.x] * ratio[3]; + pos += this.header.x * this.header.y; + sumlen2 += this.img3dView[pos] * ratio[4]; + sumlen2 += this.img3dView[pos + 1] * ratio[5]; + sumlen2 += this.img3dView[pos + this.header.x] * ratio[6]; + sumlen2 += this.img3dView[pos + 1 + this.header.x] * ratio[7]; + + return Math.round(sumlen2 / sumlen1); + } + + private _transSurface(cutdepth: number, pdimg: number[], irc: M3DImageRenderingContext) { + if (this.header === null || this.xposView === null || this.yposView === null || this.zposView === null) { + return; + } + const ix = [...Array(8)].map(() => 0); + const iy = [...Array(8)].map(() => 0); + const iz = [...Array(8)].map(() => 0); + for (let i = 0; i < this.header.dlen; i++) { + const x = this.xposView[i]; + const y = this.yposView[i]; + const z = this.zposView[i]; + ix[0] = x * irc.ra1 + y * irc.rb1 + z * irc.rc1 + irc.rd1; + iy[0] = x * irc.ra2 + y * irc.rb2 + z * irc.rc2 + irc.rd2; + iz[0] = x * irc.ra3 + y * irc.rb3 + z * irc.rc3 + irc.rd3; + ix[1] = ix[0] + irc.ra1_2; + iy[1] = iy[0] + irc.ra2_2; + iz[1] = iz[0] + irc.ra3_2; + ix[2] = ix[0] + irc.rb1_2; + iy[2] = iy[0] + irc.rb2_2; + iz[2] = iz[0] + irc.rb3_2; + ix[3] = ix[0] + irc.rc1_2; + iy[3] = iy[0] + irc.rc2_2; + iz[3] = iz[0] + irc.rc3_2; + ix[4] = ix[1] + irc.rb1_2; + iy[4] = iy[1] + irc.rb2_2; + iz[4] = iz[1] + irc.rb3_2; + ix[5] = ix[1] + irc.rc1_2; + iy[5] = iy[1] + irc.rc2_2; + iz[5] = iz[1] + irc.rc3_2; + ix[6] = ix[2] + irc.rc1_2; + iy[6] = iy[2] + irc.rc2_2; + iz[6] = iz[2] + irc.rc3_2; + ix[7] = ix[4] + irc.rc1_2; + iy[7] = iy[4] + irc.rc2_2; + iz[7] = iz[4] + irc.rc3_2; + for (let j = 0; j < 8; j++) { + const xi = Math.round(ix[j]); + const yi = Math.round(iy[j]); + if ( + xi < 0 || + xi >= this.imageSize || + yi < 0 || + yi >= this.imageSize || + iz[j] < cutdepth || + iz[j] >= this.imageSize + ) { + continue; } - } - - private _setMatrix(x: number, y: number, z: number, irc: M3DImageRenderingContext) { - const xtr = [...Array(9)].map(() => 0); - const ytr = [...Array(9)].map(() => 0); - const ztr = [...Array(9)].map(() => 0); - const tmp = [...Array(9)].map(() => 0); - const tr = [...Array(9)].map(() => 0); - this._setXtr(xtr, (x * Math.PI) / 180.0); - this._setYtr(ytr, (y * Math.PI) / 180.0); - this._setZtr(ztr, (z * Math.PI) / 180.0); - this._multi(ytr, xtr, tmp); - this._multi(ztr, tmp, tr); - irc.a1 = tr[0]; - irc.b1 = tr[1]; - irc.c1 = tr[2]; - irc.a2 = tr[3]; - irc.b2 = tr[4]; - irc.c2 = tr[5]; - irc.a3 = tr[6]; - irc.b3 = tr[7]; - irc.c3 = tr[8]; - irc.d1 = -irc.a1 * this.centerX - irc.b1 * this.centerY - irc.c1 * this.centerZ + this.ox; - irc.d2 = -irc.a2 * this.centerX - irc.b2 * this.centerY - irc.c2 * this.centerZ + this.oy; - irc.d3 = -irc.a3 * this.centerX - irc.b3 * this.centerY - irc.c3 * this.centerZ + this.oz; - - this._setXtr(xtr, (-x * Math.PI) / 180.0); - this._setYtr(ytr, (-y * Math.PI) / 180.0); - this._setZtr(ztr, (-z * Math.PI) / 180.0); - this._multi(ytr, ztr, tmp); - this._multi(xtr, tmp, tr); - irc.ra1 = tr[0]; - irc.rb1 = tr[1]; - irc.rc1 = tr[2]; - irc.ra2 = tr[3]; - irc.rb2 = tr[4]; - irc.rc2 = tr[5]; - irc.ra3 = tr[6]; - irc.rb3 = tr[7]; - irc.rc3 = tr[8]; - irc.rd1 = -irc.ra1 * this.ox - irc.rb1 * this.oy - irc.rc1 * this.oz + this.centerX; - irc.rd2 = -irc.ra2 * this.ox - irc.rb2 * this.oy - irc.rc2 * this.oz + this.centerY; - irc.rd3 = -irc.ra3 * this.ox - irc.rb3 * this.oy - irc.rc3 * this.oz + this.centerZ; - irc.ra1_2 = irc.ra1 / 2; - irc.rb1_2 = irc.rb1 / 2; - irc.rc1_2 = irc.rc1 / 2; - irc.ra2_2 = irc.ra2 / 2; - irc.rb2_2 = irc.rb2 / 2; - irc.rc2_2 = irc.rc2 / 2; - irc.ra3_2 = irc.ra3 / 2; - irc.rb3_2 = irc.rb3 / 2; - irc.rc3_2 = irc.rc3 / 2; - } - - private _setXtr(xtr: number[], ang: number) { - xtr[0] = 1; - xtr[1] = 0; - xtr[2] = 0; - xtr[3] = 0; - xtr[4] = Math.cos(ang); - xtr[5] = -Math.sin(ang); - xtr[6] = 0; - xtr[7] = Math.sin(ang); - xtr[8] = Math.cos(ang); - } - - private _setYtr(ytr: number[], ang: number) { - ytr[0] = Math.cos(ang); - ytr[1] = 0; - ytr[2] = Math.sin(ang); - ytr[3] = 0; - ytr[4] = 1; - ytr[5] = 0; - ytr[6] = -Math.sin(ang); - ytr[7] = 0; - ytr[8] = Math.cos(ang); - } - - private _setZtr(ztr: number[], ang: number) { - ztr[0] = Math.cos(ang); - ztr[1] = -Math.sin(ang); - ztr[2] = 0; - ztr[3] = Math.sin(ang); - ztr[4] = Math.cos(ang); - ztr[5] = 0; - ztr[6] = 0; - ztr[7] = 0; - ztr[8] = 1; - } - - private _multi(l: number[], r: number[], a: number[]) { - a[0] = l[0] * r[0] + l[1] * r[3] + l[2] * r[6]; - a[3] = l[3] * r[0] + l[4] * r[3] + l[5] * r[6]; - a[6] = l[6] * r[0] + l[7] * r[3] + l[8] * r[6]; - a[1] = l[0] * r[1] + l[1] * r[4] + l[2] * r[7]; - a[4] = l[3] * r[1] + l[4] * r[4] + l[5] * r[7]; - a[7] = l[6] * r[1] + l[7] * r[4] + l[8] * r[7]; - a[2] = l[0] * r[2] + l[1] * r[5] + l[2] * r[8]; - a[5] = l[3] * r[2] + l[4] * r[5] + l[5] * r[8]; - a[8] = l[6] * r[2] + l[7] * r[5] + l[8] * r[8]; - } - - private _fround(x: number, n: number) { - return Math.round(x * Math.pow(10, n)) / Math.pow(10, n); - } - - private _initialize(): void { - const view = new DataView(this.data); - const magic = view.getInt32(0, true); - const isLittleEndianFile = magic === 0x4d523344; - if (!isLittleEndianFile && magic !== 0x4433524d) { - console.log('invalid data format, this data is not m3d format'); - return; + const pos = xi + yi * this.imageSize; + if (pdimg[pos] === 0 || pdimg[pos] > iz[j]) { + pdimg[pos] = iz[j]; } - let offset = 0; - const decoder = new TextDecoder('utf-8'); - this.header = { - magic: view.getInt32(offset, isLittleEndianFile), - x: view.getInt32((offset += 4), isLittleEndianFile), - y: view.getInt32((offset += 4), isLittleEndianFile), - z: view.getInt32((offset += 4), isLittleEndianFile), - filename: decoder.decode(new Uint8Array(this.data, (offset += 4), 256)).replace(/\0+$/, ''), - subject: decoder.decode(new Uint8Array(this.data, (offset += 256), 256)).replace(/\0+$/, ''), - direction: decoder.decode(new Uint8Array(this.data, (offset += 256), 4)).replace(/\0+$/, ''), - date: view.getUint32((offset += 4), isLittleEndianFile), - fov: view.getFloat64((offset += 4), isLittleEndianFile), - weight: view.getFloat64((offset += 8), isLittleEndianFile), - ax: view.getFloat64((offset += 8), isLittleEndianFile), - ay: view.getFloat64((offset += 8), isLittleEndianFile), - az: view.getFloat64((offset += 8), isLittleEndianFile), - ox: view.getFloat64((offset += 8), isLittleEndianFile), - oy: view.getFloat64((offset += 8), isLittleEndianFile), - oz: view.getFloat64((offset += 8), isLittleEndianFile), - dlen: view.getInt32((offset += 8), isLittleEndianFile), - dth: view.getInt32((offset += 4), isLittleEndianFile), - memo: decoder.decode(new Uint8Array(this.data, (offset += 4), 256)).replace(/\0+$/, ''), - }; - // swap byte order if endian is not matched - if ((this.isBigEndian() && isLittleEndianFile) || (!this.isBigEndian() && !isLittleEndianFile)) { - const start = 864; - const end = start + this.header.dlen * 4 * 3; - for (let i = start; i < end; i += 4) { - const data = new Uint8Array(this.data, i, 4); - data.reverse(); - } - } - this.xposView = new Int32Array(this.data, 864, this.header.dlen); - this.yposView = new Int32Array(this.data, 864 + this.header.dlen * 4, this.header.dlen); - this.zposView = new Int32Array(this.data, 864 + this.header.dlen * 8, this.header.dlen); - this.img3dView = new Uint8ClampedArray(this.data, 864 + this.header.dlen * 12, this.header.x * this.header.y * this.header.z); - // set image generation offset - this.imageSize = Math.ceil(Math.max(this.header.x, this.header.y, this.header.z) * Math.sqrt(3.0)) + 1; - this.centerX = (this.imageSize - 1) / 2.0; - this.centerY = (this.imageSize - 1) / 2.0; - this.centerZ = (this.imageSize - 1) / 2.0; - this.ox = (this.header.x - 1) / 2.0; - this.oy = (this.header.y - 1) / 2.0; - this.oz = (this.header.z - 1) / 2.0; + } } + } + + private _setMatrix(x: number, y: number, z: number, irc: M3DImageRenderingContext) { + const xtr = [...Array(9)].map(() => 0); + const ytr = [...Array(9)].map(() => 0); + const ztr = [...Array(9)].map(() => 0); + const tmp = [...Array(9)].map(() => 0); + const tr = [...Array(9)].map(() => 0); + this._setXtr(xtr, (x * Math.PI) / 180.0); + this._setYtr(ytr, (y * Math.PI) / 180.0); + this._setZtr(ztr, (z * Math.PI) / 180.0); + this._multi(ytr, xtr, tmp); + this._multi(ztr, tmp, tr); + irc.a1 = tr[0]; + irc.b1 = tr[1]; + irc.c1 = tr[2]; + irc.a2 = tr[3]; + irc.b2 = tr[4]; + irc.c2 = tr[5]; + irc.a3 = tr[6]; + irc.b3 = tr[7]; + irc.c3 = tr[8]; + irc.d1 = -irc.a1 * this.centerX - irc.b1 * this.centerY - irc.c1 * this.centerZ + this.ox; + irc.d2 = -irc.a2 * this.centerX - irc.b2 * this.centerY - irc.c2 * this.centerZ + this.oy; + irc.d3 = -irc.a3 * this.centerX - irc.b3 * this.centerY - irc.c3 * this.centerZ + this.oz; + + this._setXtr(xtr, (-x * Math.PI) / 180.0); + this._setYtr(ytr, (-y * Math.PI) / 180.0); + this._setZtr(ztr, (-z * Math.PI) / 180.0); + this._multi(ytr, ztr, tmp); + this._multi(xtr, tmp, tr); + irc.ra1 = tr[0]; + irc.rb1 = tr[1]; + irc.rc1 = tr[2]; + irc.ra2 = tr[3]; + irc.rb2 = tr[4]; + irc.rc2 = tr[5]; + irc.ra3 = tr[6]; + irc.rb3 = tr[7]; + irc.rc3 = tr[8]; + irc.rd1 = -irc.ra1 * this.ox - irc.rb1 * this.oy - irc.rc1 * this.oz + this.centerX; + irc.rd2 = -irc.ra2 * this.ox - irc.rb2 * this.oy - irc.rc2 * this.oz + this.centerY; + irc.rd3 = -irc.ra3 * this.ox - irc.rb3 * this.oy - irc.rc3 * this.oz + this.centerZ; + irc.ra1_2 = irc.ra1 / 2; + irc.rb1_2 = irc.rb1 / 2; + irc.rc1_2 = irc.rc1 / 2; + irc.ra2_2 = irc.ra2 / 2; + irc.rb2_2 = irc.rb2 / 2; + irc.rc2_2 = irc.rc2 / 2; + irc.ra3_2 = irc.ra3 / 2; + irc.rb3_2 = irc.rb3 / 2; + irc.rc3_2 = irc.rc3 / 2; + } + + private _setXtr(xtr: number[], ang: number) { + xtr[0] = 1; + xtr[1] = 0; + xtr[2] = 0; + xtr[3] = 0; + xtr[4] = Math.cos(ang); + xtr[5] = -Math.sin(ang); + xtr[6] = 0; + xtr[7] = Math.sin(ang); + xtr[8] = Math.cos(ang); + } + + private _setYtr(ytr: number[], ang: number) { + ytr[0] = Math.cos(ang); + ytr[1] = 0; + ytr[2] = Math.sin(ang); + ytr[3] = 0; + ytr[4] = 1; + ytr[5] = 0; + ytr[6] = -Math.sin(ang); + ytr[7] = 0; + ytr[8] = Math.cos(ang); + } + + private _setZtr(ztr: number[], ang: number) { + ztr[0] = Math.cos(ang); + ztr[1] = -Math.sin(ang); + ztr[2] = 0; + ztr[3] = Math.sin(ang); + ztr[4] = Math.cos(ang); + ztr[5] = 0; + ztr[6] = 0; + ztr[7] = 0; + ztr[8] = 1; + } + + private _multi(l: number[], r: number[], a: number[]) { + a[0] = l[0] * r[0] + l[1] * r[3] + l[2] * r[6]; + a[3] = l[3] * r[0] + l[4] * r[3] + l[5] * r[6]; + a[6] = l[6] * r[0] + l[7] * r[3] + l[8] * r[6]; + a[1] = l[0] * r[1] + l[1] * r[4] + l[2] * r[7]; + a[4] = l[3] * r[1] + l[4] * r[4] + l[5] * r[7]; + a[7] = l[6] * r[1] + l[7] * r[4] + l[8] * r[7]; + a[2] = l[0] * r[2] + l[1] * r[5] + l[2] * r[8]; + a[5] = l[3] * r[2] + l[4] * r[5] + l[5] * r[8]; + a[8] = l[6] * r[2] + l[7] * r[5] + l[8] * r[8]; + } + + private _fround(x: number, n: number) { + return Math.round(x * 10 ** n) / 10 ** n; + } + + private _initialize(): void { + const view = new DataView(this.data); + const magic = view.getInt32(0, true); + const isLittleEndianFile = magic === 0x4d523344; + if (!isLittleEndianFile && magic !== 0x4433524d) { + console.log('invalid data format, this data is not m3d format'); + return; + } + let offset = 0; + const decoder = new TextDecoder('utf-8'); + this.header = { + magic: view.getInt32(offset, isLittleEndianFile), + x: view.getInt32((offset += 4), isLittleEndianFile), + y: view.getInt32((offset += 4), isLittleEndianFile), + z: view.getInt32((offset += 4), isLittleEndianFile), + filename: decoder.decode(new Uint8Array(this.data, (offset += 4), 256)).replace(/\0+$/, ''), + subject: decoder.decode(new Uint8Array(this.data, (offset += 256), 256)).replace(/\0+$/, ''), + direction: decoder.decode(new Uint8Array(this.data, (offset += 256), 4)).replace(/\0+$/, ''), + date: view.getUint32((offset += 4), isLittleEndianFile), + fov: view.getFloat64((offset += 4), isLittleEndianFile), + weight: view.getFloat64((offset += 8), isLittleEndianFile), + ax: view.getFloat64((offset += 8), isLittleEndianFile), + ay: view.getFloat64((offset += 8), isLittleEndianFile), + az: view.getFloat64((offset += 8), isLittleEndianFile), + ox: view.getFloat64((offset += 8), isLittleEndianFile), + oy: view.getFloat64((offset += 8), isLittleEndianFile), + oz: view.getFloat64((offset += 8), isLittleEndianFile), + dlen: view.getInt32((offset += 8), isLittleEndianFile), + dth: view.getInt32((offset += 4), isLittleEndianFile), + memo: decoder.decode(new Uint8Array(this.data, (offset += 4), 256)).replace(/\0+$/, ''), + }; + void offset; // last use + // swap byte order if endian is not matched + if ((this.isBigEndian() && isLittleEndianFile) || (!this.isBigEndian() && !isLittleEndianFile)) { + const start = 864; + const end = start + this.header.dlen * 4 * 3; + for (let i = start; i < end; i += 4) { + const data = new Uint8Array(this.data, i, 4); + data.reverse(); + } + } + this.xposView = new Int32Array(this.data, 864, this.header.dlen); + this.yposView = new Int32Array(this.data, 864 + this.header.dlen * 4, this.header.dlen); + this.zposView = new Int32Array(this.data, 864 + this.header.dlen * 8, this.header.dlen); + this.img3dView = new Uint8ClampedArray( + this.data, + 864 + this.header.dlen * 12, + this.header.x * this.header.y * this.header.z, + ); + // set image generation offset + this.imageSize = Math.ceil(Math.max(this.header.x, this.header.y, this.header.z) * Math.sqrt(3.0)) + 1; + this.centerX = (this.imageSize - 1) / 2.0; + this.centerY = (this.imageSize - 1) / 2.0; + this.centerZ = (this.imageSize - 1) / 2.0; + this.ox = (this.header.x - 1) / 2.0; + this.oy = (this.header.y - 1) / 2.0; + this.oz = (this.header.z - 1) / 2.0; + } } export default M3D; diff --git a/src/common/AppRoot.tsx b/src/common/AppRoot.tsx index 117aa45..1dea78a 100644 --- a/src/common/AppRoot.tsx +++ b/src/common/AppRoot.tsx @@ -1,41 +1,43 @@ -import React, { useEffect, useState } from "react"; -import { useCookies } from "react-cookie"; -import ReactGA from "react-ga4"; -import { useLocation } from "react-router-dom"; -import Config, { MultiLang } from "../config"; -import Page from "../custom/Page"; +import type React from 'react'; +import { useEffect, useState } from 'react'; +import { useCookies } from 'react-cookie'; +import ReactGA from 'react-ga4'; +import { useLocation } from 'react-router-dom'; +import Config, { type MultiLang } from '../config'; +import Page from '../custom/Page'; const AppMain: React.FC = () => { - const [cookies, setCookie] = useCookies(); - const [lang, setLang] = useState(["en", "ja"].includes(cookies.ml_lang) ? cookies.ml_lang : "en"); - const location = useLocation(); + const [cookies, setCookie] = useCookies(); + const [lang, setLang] = useState(['en', 'ja'].includes(cookies.ml_lang) ? cookies.ml_lang : 'en'); + const location = useLocation(); - useEffect(() => { - if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== "") { - ReactGA.send("pageview"); - } - const params = new URLSearchParams(location.search); - const ml_lang = params.get("ml_lang"); - if (ml_lang != null && ["en", "ja"].includes(ml_lang)) { - if (cookies.ml_lang !== ml_lang) { - setCookie("ml_lang", ml_lang); - } - if (lang !== ml_lang) { - setLang(ml_lang as MultiLang); - } - } - window.scrollTo(0, 0); - }, [cookies, setCookie, lang, location]); + useEffect(() => { + if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') { + ReactGA.send('pageview'); + } + const params = new URLSearchParams(location.search); + const ml_lang = params.get('ml_lang'); + if (ml_lang != null && ['en', 'ja'].includes(ml_lang)) { + if (cookies.ml_lang !== ml_lang) { + setCookie('ml_lang', ml_lang); + } + if (lang !== ml_lang) { + // eslint-disable-next-line react-hooks/set-state-in-effect + setLang(ml_lang as MultiLang); + } + } + window.scrollTo(0, 0); + }, [cookies, setCookie, lang, location]); - return ; + return ; }; const AppRoot: React.FC = () => { - if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== "") { - ReactGA.initialize(Config.GOOGLE_ANALYTICS_TRACKING_ID); - } + if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') { + ReactGA.initialize(Config.GOOGLE_ANALYTICS_TRACKING_ID); + } - return ; + return ; }; export default AppRoot; diff --git a/src/common/XoopsPathRedirect.tsx b/src/common/XoopsPathRedirect.tsx index 2fa3259..012cb48 100644 --- a/src/common/XoopsPathRedirect.tsx +++ b/src/common/XoopsPathRedirect.tsx @@ -1,35 +1,35 @@ -import React from "react"; -import { Navigate } from "react-router"; -import { useLocation } from "react-router-dom"; -import { MultiLang } from "../config"; -import PageNotFound from "./lib/PageNotFound"; +import type React from 'react'; +import { Navigate } from 'react-router'; +import { useLocation } from 'react-router-dom'; +import type { MultiLang } from '../config'; +import PageNotFound from './lib/PageNotFound'; interface Props { - lang: MultiLang; + lang: MultiLang; } const XoopsPathRedirect: React.FC = (props: Props) => { - const { lang } = props; - const location = useLocation(); + const { lang } = props; + const location = useLocation(); - const getRedirectUrl = () => { - const { pathname } = location; - switch (pathname || "") { - case "/index.php": { - return "/"; - } - } - return ""; - }; + const getRedirectUrl = () => { + const { pathname } = location; + switch (pathname || '') { + case '/index.php': { + return '/'; + } + } + return ''; + }; - if (location.pathname === "/") { - return null; - } - const url = getRedirectUrl(); - if (url === "") { - return ; - } - return ; + if (location.pathname === '/') { + return null; + } + const url = getRedirectUrl(); + if (url === '') { + return ; + } + return ; }; export default XoopsPathRedirect; diff --git a/src/common/assets/xoops.css b/src/common/assets/xoops.css index 88f768a..4bdb3bb 100644 --- a/src/common/assets/xoops.css +++ b/src/common/assets/xoops.css @@ -1,17 +1,62 @@ -img {border: 0;} - -#xoopsHiddenText {visibility: hidden; color: #000000; font-weight: normal; font-style: normal; text-decoration: none;} - -.pagneutral {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/pagneutral.gif);} -.pagact {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/pagact.gif);} -.paginact {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/paginact.gif);} - - -#mainmenu a {text-align:left; display: block; margin: 0; padding: 4px;} -#mainmenu a.menuTop {padding-left: 3px;} -#mainmenu a.menuMain {padding-left: 3px;} -#mainmenu a.menuSub {padding-left: 9px;} - -#usermenu a {text-align:left; display: block; margin: 0; padding: 4px;} -#usermenu a.menuTop {} -#usermenu a.highlight {color: #0000ff; background-color: #fcc;} +img { + border: 0; +} + +#xoopsHiddenText { + visibility: hidden; + color: #000000; + font-weight: normal; + font-style: normal; + text-decoration: none; +} + +.pagneutral { + font-size: 10px; + width: 16px; + height: 19px; + text-align: center; + background-image: url(./images/pagneutral.gif); +} +.pagact { + font-size: 10px; + width: 16px; + height: 19px; + text-align: center; + background-image: url(./images/pagact.gif); +} +.paginact { + font-size: 10px; + width: 16px; + height: 19px; + text-align: center; + background-image: url(./images/paginact.gif); +} + +#mainmenu a { + text-align: left; + display: block; + margin: 0; + padding: 4px; +} +#mainmenu a.menuTop { + padding-left: 3px; +} +#mainmenu a.menuMain { + padding-left: 3px; +} +#mainmenu a.menuSub { + padding-left: 9px; +} + +#usermenu a { + text-align: left; + display: block; + margin: 0; + padding: 4px; +} +#usermenu a.menuTop { +} +#usermenu a.highlight { + color: #0000ff; + background-color: #fcc; +} diff --git a/src/common/lib/LangFlag.tsx b/src/common/lib/LangFlag.tsx index 32e7469..761f945 100644 --- a/src/common/lib/LangFlag.tsx +++ b/src/common/lib/LangFlag.tsx @@ -1,33 +1,33 @@ -import React from "react"; -import { Link, useLocation } from "react-router-dom"; -import { MultiLang } from "../../config"; -import mlangEnglish from "../assets/images/mlang_english.gif"; -import mlangJapanese from "../assets/images/mlang_japanese.gif"; +import type React from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import type { MultiLang } from '../../config'; +import mlangEnglish from '../assets/images/mlang_english.gif'; +import mlangJapanese from '../assets/images/mlang_japanese.gif'; interface Props { - lang: MultiLang; - className?: string; - image?: string; + lang: MultiLang; + className?: string; + image?: string; } const langResources = { - en: { image: mlangEnglish, title: "English" }, - ja: { image: mlangJapanese, title: "Japanese" }, + en: { image: mlangEnglish, title: 'English' }, + ja: { image: mlangJapanese, title: 'Japanese' }, }; const LangFlag: React.FC = (props: Props) => { - const { lang, className, image } = props; - const location = useLocation(); - const params = new URLSearchParams(location.search); - const flagLang = lang === "en" ? "ja" : "en"; - params.set("ml_lang", flagLang); - const url = location.pathname + "?" + params.toString(); - const imageSrc = image || langResources[flagLang].image; - return ( - - {langResources[flagLang].title} - - ); + const { lang, className, image } = props; + const location = useLocation(); + const params = new URLSearchParams(location.search); + const flagLang = lang === 'en' ? 'ja' : 'en'; + params.set('ml_lang', flagLang); + const url = location.pathname + '?' + params.toString(); + const imageSrc = image || langResources[flagLang].image; + return ( + + {langResources[flagLang].title} + + ); }; export default LangFlag; diff --git a/src/common/lib/Loading.tsx b/src/common/lib/Loading.tsx index c3dce88..f9c327a 100644 --- a/src/common/lib/Loading.tsx +++ b/src/common/lib/Loading.tsx @@ -1,12 +1,12 @@ -import React from "react"; -import Spinner from "react-spinner-material"; +import type React from 'react'; +import Spinner from 'react-spinner-material'; const Loading: React.FC = () => { - return ( -
- -
- ); + return ( +
+ +
+ ); }; export default Loading; diff --git a/src/common/lib/NoticeSiteHasBeenArchived.tsx b/src/common/lib/NoticeSiteHasBeenArchived.tsx index 0a0615b..af53ee1 100644 --- a/src/common/lib/NoticeSiteHasBeenArchived.tsx +++ b/src/common/lib/NoticeSiteHasBeenArchived.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const NoticeSiteHasBeenArchived: React.FC = (props: Props) => { - const { lang } = props; - const notice = "[en]This site has been archived since FY2019 and is no longer updated.[/en][ja]このサイトは、2019年度よりアーカイブサイトとして運用されています。[/ja]"; - return

{Functions.mlang(notice, lang)}

; + const { lang } = props; + const notice = + '[en]This site has been archived since FY2019 and is no longer updated.[/en][ja]このサイトは、2019年度よりアーカイブサイトとして運用されています。[/ja]'; + return

{Functions.mlang(notice, lang)}

; }; export default NoticeSiteHasBeenArchived; diff --git a/src/common/lib/PageNotFound.tsx b/src/common/lib/PageNotFound.tsx index 9db5c9d..706325a 100644 --- a/src/common/lib/PageNotFound.tsx +++ b/src/common/lib/PageNotFound.tsx @@ -1,50 +1,51 @@ -import React, { useEffect } from "react"; -import { Helmet } from "react-helmet-async"; -import { useLocation, useNavigate } from "react-router-dom"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import { useEffect } from 'react'; +import { Helmet } from 'react-helmet-async'; +import { useLocation, useNavigate } from 'react-router-dom'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const PageNotFound: React.FC = (props) => { - const { lang } = props; - const location = useLocation(); - const navigate = useNavigate(); - const url = "/"; + const { lang } = props; + const location = useLocation(); + const navigate = useNavigate(); + const url = '/'; - useEffect(() => { - let timer: NodeJS.Timeout | null = null; - if (location.pathname !== "/" && process.env.NODE_ENV === "production") { - if (timer === null) { - timer = setTimeout(() => { - navigate(url); - }, 5000); - } - } - return () => { - if (timer !== null) { - clearTimeout(timer); - timer = null; - } - }; - }, [location, navigate]); + useEffect(() => { + let timer: ReturnType | null = null; + if (location.pathname !== '/' && import.meta.env.PROD) { + if (timer === null) { + timer = setTimeout(() => { + navigate(url); + }, 5000); + } + } + return () => { + if (timer !== null) { + clearTimeout(timer); + timer = null; + } + }; + }, [location, navigate]); - return ( -
- - Page Not Found - {Functions.siteTitle(lang)} - -

Page Not Found

-
-

The page you were trying to access doesn't exist.

-

- If the page does not automatically reload, please click here -

-
-
- ); + return ( +
+ + Page Not Found - {Functions.siteTitle(lang)} + +

Page Not Found

+
+

The page you were trying to access doesn't exist.

+

+ If the page does not automatically reload, please click here +

+
+
+ ); }; export default PageNotFound; diff --git a/src/common/lib/XoopsCode.tsx b/src/common/lib/XoopsCode.tsx index 1538945..5b0ebe8 100644 --- a/src/common/lib/XoopsCode.tsx +++ b/src/common/lib/XoopsCode.tsx @@ -1,132 +1,150 @@ -import ReactHtmlParser, { convertNodeToElement, DomElement, DomNode, Transform } from "@orrisroot/react-html-parser"; -import React from "react"; -import { HashLink } from "react-router-hash-link"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import ReactHtmlParser, { + convertNodeToElement, + type DomElement, + type DomNode, + type Transform, +} from '@orrisroot/react-html-parser'; +import type React from 'react'; +import { HashLink } from 'react-router-hash-link'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; - text: string; - dohtml?: boolean; - dosmiley?: boolean; - doxcode?: boolean; - doimage?: boolean; - dobr?: boolean; + lang: MultiLang; + text: string; + dohtml?: boolean; + dosmiley?: boolean; + doxcode?: boolean; + doimage?: boolean; + dobr?: boolean; } const preConvertXCode = (text: string, doxcode: boolean): string => { - if (doxcode) { - return text.replace(/\[code\](.*)\[\/code\]/gs, (m0, m1) => { - return "[code]" + Functions.base64Encode(m1) + "[/code]"; - }); - } - return text; + if (doxcode) { + return text.replace(/\[code\](.*)\[\/code\]/gs, (_m0, m1) => { + return '[code]' + Functions.base64Encode(m1) + '[/code]'; + }); + } + return text; }; const postConvertXCode = (text: string, doxcode: boolean, doimage: boolean): string => { - if (doxcode) { - return text.replace(/\[code\](.*)\[\/code\]/gs, (m0, m1) => { - const text = convertXCode(Functions.htmlspecialchars(Functions.base64Decode(m1)), doimage); - return '
' + text + "
"; - }); - } - return text; + if (doxcode) { + return text.replace(/\[code\](.*)\[\/code\]/gs, (_m0, m1) => { + const text = convertXCode(Functions.htmlspecialchars(Functions.base64Decode(m1)), doimage); + return '
' + text + '
'; + }); + } + return text; }; const convertClickable = (text: string) => { - text = text.replace(/(^|[^\]_a-zA-Z0-9-="'/]+)((?:https?|ftp)(?::\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[a-zA-Z0-9=]))/g, (...matches) => { - return matches[1] + '' + matches[2] + ""; - }); - text = text.replace(/(^|[^\]_a-zA-Z0-9-="'/:.]+)([a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)/g, (...matches) => { - return matches[1] + '' + matches[2] + ""; - }); - return text; + text = text.replace( + /(^|[^\]_a-zA-Z0-9-="'/]+)((?:https?|ftp)(?::\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[a-zA-Z0-9=]))/g, + (...matches) => { + return ( + matches[1] + + '' + + matches[2] + + '' + ); + }, + ); + text = text.replace( + /(^|[^\]_a-zA-Z0-9-="'/:.]+)([a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)/g, + (...matches) => { + return matches[1] + '' + matches[2] + ''; + }, + ); + return text; }; -const convertXCode = (text: string, doimage: boolean): string => { - // TODO: implement - return text; +const convertXCode = (text: string, _doimage: boolean): string => { + // TODO: implement + return text; }; const convertSmiley = (text: string) => { - // TODO: implement - return text; + // TODO: implement + return text; }; const convertBr = (text: string): string => { - return text.replace(/(\r?\n|\r)/g, "
"); + return text.replace(/(\r?\n|\r)/g, '
'); }; const cssConvert = (text: string): object => { - const ret: any = {}; - text.split(";").forEach((line) => { - const line_ = line.trim(); - if (line.length === 0) { - return; - } - const kv = line_.split(":"); - const key = Functions.camelCase(kv[0].trim()); - const value = kv[1].trim(); - ret[key] = value; - }); - return ret; + const ret: any = {}; + text.split(';').forEach((line) => { + const line_ = line.trim(); + if (line.length === 0) { + return; + } + const kv = line_.split(':'); + const key = Functions.camelCase(kv[0].trim()); + const value = kv[1].trim(); + ret[key] = value; + }); + return ret; }; const xoopsTransform: Transform = (node: DomNode, index: number | string, transform?: Transform): React.ReactNode => { - if (node.type === "tag") { - const node_ = node as DomElement; - if (node_.name === "a") { - const url = node_.attribs?.["href"] || "/"; - const download = (node_.attribs && node_.attribs["download"]) || ""; - const rel = (node_.attribs && node_.attribs["rel"]) || ""; - const klass = (node_.attribs && node_.attribs["class"]) || ""; - const isFile = download !== "" || /\.(zip|pdf|png|gif|jpg)$/.test(url); - const isExternal = /external/.test(rel) || /external/.test(klass) || /^(mailto|https?:?\/\/)/.test(url); - if (!isFile && !isExternal) { - const style = node_.attribs?.["style"] || ""; - const title = node_.attribs?.["title"]; - return ( - - {node_.children.map((value: DomNode, index: number) => { - return convertNodeToElement(value, index, transform); - })} - - ); - } - } - if (node_.name === "img") { - const src = (node_.attribs && node_.attribs["src"]) || ""; - node_.attribs["src"] = src.replace("`XOOPS_URL`", process.env.PUBLIC_URL); - return convertNodeToElement(node_, index, transform); - } + if (node.type === 'tag') { + const node_ = node as DomElement; + if (node_.name === 'a') { + const url = node_.attribs?.['href'] || '/'; + const download = (node_.attribs && node_.attribs['download']) || ''; + const rel = (node_.attribs && node_.attribs['rel']) || ''; + const klass = (node_.attribs && node_.attribs['class']) || ''; + const isFile = download !== '' || /\.(zip|pdf|png|gif|jpg)$/.test(url); + const isExternal = /external/.test(rel) || /external/.test(klass) || /^(mailto|https?:?\/\/)/.test(url); + if (!isFile && !isExternal) { + const style = node_.attribs?.['style'] || ''; + const title = node_.attribs?.['title']; + return ( + + {node_.children.map((value: DomNode, index: number) => { + return convertNodeToElement(value, index, transform); + })} + + ); + } } + if (node_.name === 'img') { + const src = (node_.attribs && node_.attribs['src']) || ''; + node_.attribs['src'] = src.replace('`XOOPS_URL`', ''); + return convertNodeToElement(node_, index, transform); + } + } }; const XoopsCode: React.FC = (props: Props) => { - const { lang } = props; - let text = props.text; - const dohtml = !!props.dohtml; - const dosmiley = !!props.dosmiley; - const doxcode = !!props.doxcode; - const doimage = !!props.doimage; - const dobr = !!props.dobr; - text = preConvertXCode(text, doxcode); - if (!dohtml) { - text = Functions.htmlspecialchars(text); - text = convertClickable(text); - } - if (dosmiley) { - text = convertSmiley(text); - } - if (doxcode) { - text = convertXCode(text, doimage); - } - if (dobr) { - text = convertBr(text); - } - text = postConvertXCode(text, doxcode, doimage); - text = Functions.mlang(text, lang); - return
{ReactHtmlParser(text, { transform: xoopsTransform })}
; + const { lang } = props; + let text = props.text; + const dohtml = !!props.dohtml; + const dosmiley = !!props.dosmiley; + const doxcode = !!props.doxcode; + const doimage = !!props.doimage; + const dobr = !!props.dobr; + text = preConvertXCode(text, doxcode); + if (!dohtml) { + text = Functions.htmlspecialchars(text); + text = convertClickable(text); + } + if (dosmiley) { + text = convertSmiley(text); + } + if (doxcode) { + text = convertXCode(text, doimage); + } + if (dobr) { + text = convertBr(text); + } + text = postConvertXCode(text, doxcode, doimage); + text = Functions.mlang(text, lang); + return
{ReactHtmlParser(text, { transform: xoopsTransform })}
; }; export default XoopsCode; diff --git a/src/config.ts b/src/config.ts index f9f9e50..76d14f0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,15 +1,15 @@ -const SITE_TITLE = "NeuroImaging Platform"; -const SITE_SLOGAN = ""; -const GOOGLE_ANALYTICS_TRACKING_ID = "UA-3757403-1"; -const XOONIPS_ITEMTYPES = ["book", "paper", "presentation", "data", "tool", "nimgcenter", "model", "url"]; +const SITE_TITLE = 'NeuroImaging Platform'; +const SITE_SLOGAN = ''; +const GOOGLE_ANALYTICS_TRACKING_ID = 'UA-3757403-1'; +const XOONIPS_ITEMTYPES = ['book', 'paper', 'presentation', 'data', 'tool', 'nimgcenter', 'model', 'url']; -export type MultiLang = "en" | "ja"; +export type MultiLang = 'en' | 'ja'; const Config = { - SITE_TITLE, - SITE_SLOGAN, - GOOGLE_ANALYTICS_TRACKING_ID, - XOONIPS_ITEMTYPES, + SITE_TITLE, + SITE_SLOGAN, + GOOGLE_ANALYTICS_TRACKING_ID, + XOONIPS_ITEMTYPES, }; export default Config; diff --git a/src/credits/Credits.tsx b/src/credits/Credits.tsx index df34ade..3864451 100644 --- a/src/credits/Credits.tsx +++ b/src/credits/Credits.tsx @@ -1,47 +1,47 @@ -import React from "react"; -import { Navigate, Route, Routes, useLocation } from "react-router-dom"; -import PageNotFound from "../common/lib/PageNotFound"; -import { MultiLang } from "../config"; -import CreditsIndex from "./CreditsIndex"; -import CreditsPage from "./CreditsPage"; +import type React from 'react'; +import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; +import PageNotFound from '../common/lib/PageNotFound'; +import type { MultiLang } from '../config'; +import CreditsIndex from './CreditsIndex'; +import CreditsPage from './CreditsPage'; interface Props { - lang: MultiLang; + lang: MultiLang; } const CreditsTop: React.FC = (props: Props) => { - const { lang } = props; - const location = useLocation(); - const params = new URLSearchParams(location.search); - const paramId = params.get("id"); - if (null === paramId) { - return ; - } - if (/^[1-9][0-9]*$/.test(paramId)) { - const id = parseInt(paramId, 10); - return ; - } - return ; + const { lang } = props; + const location = useLocation(); + const params = new URLSearchParams(location.search); + const paramId = params.get('id'); + if (null === paramId) { + return ; + } + if (/^[1-9][0-9]*$/.test(paramId)) { + const id = parseInt(paramId, 10); + return ; + } + return ; }; const CreditsRedirectToTop: React.FC = () => { - const location = useLocation(); - const url = location.pathname + "index.php" + location.search + location.hash; - return ; + const location = useLocation(); + const url = location.pathname + 'index.php' + location.search + location.hash; + return ; }; const Credits: React.FC = (props: Props) => { - const { lang } = props; - return ( - <> - - } /> - } /> - } /> - } /> - - - ); + const { lang } = props; + return ( + <> + + } /> + } /> + } /> + } /> + + + ); }; export default Credits; diff --git a/src/credits/CreditsIndex.tsx b/src/credits/CreditsIndex.tsx index 1d8c436..ac834c4 100644 --- a/src/credits/CreditsIndex.tsx +++ b/src/credits/CreditsIndex.tsx @@ -1,59 +1,60 @@ -import React, { useEffect, useState } from "react"; -import { Helmet } from "react-helmet-async"; -import { Link } from "react-router-dom"; -import Loading from "../common/lib/Loading"; -import NoticeSiteHasBeenArchived from "../common/lib/NoticeSiteHasBeenArchived"; -import PageNotFound from "../common/lib/PageNotFound"; -import { MultiLang } from "../config"; -import Functions from "../functions"; -import CreditsUtils, { CreditsIndexData } from "./lib/CreditsUtils"; +import type React from 'react'; +import { useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import { Link } from 'react-router-dom'; +import Loading from '../common/lib/Loading'; +import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived'; +import PageNotFound from '../common/lib/PageNotFound'; +import type { MultiLang } from '../config'; +import Functions from '../functions'; +import CreditsUtils, { type CreditsIndexData } from './lib/CreditsUtils'; interface Props { - lang: MultiLang; + lang: MultiLang; } const CreditsIndex: React.FC = (props: Props) => { - const { lang } = props; - const [loading, setLoading] = useState(true); - const [index, setIndex] = useState(null); + const { lang } = props; + const [loading, setLoading] = useState(true); + const [index, setIndex] = useState(null); - useEffect(() => { - const updatePage = async () => { - const index = await CreditsUtils.getIndex(); - setLoading(false); - setIndex(index); - }; - updatePage(); - }, [lang]); + useEffect(() => { + const updatePage = async () => { + const index = await CreditsUtils.getIndex(); + setLoading(false); + setIndex(index); + }; + updatePage(); + }, [lang]); - if (loading) { - return ; - } - if (null === index) { - return ; - } - const mtitle = Functions.mlang(index.name, lang); - return ( - <> - - - {mtitle} - {Functions.siteTitle(lang)} - - -

{mtitle}

- -
    - {index.pages.map((page) => { - const title = Functions.mlang(page.title, lang); - const url = CreditsUtils.getPageUrl(page.id); - return ( -
  • - {title} -
  • - ); - })} -
- - ); + if (loading) { + return ; + } + if (null === index) { + return ; + } + const mtitle = Functions.mlang(index.name, lang); + return ( + <> + + + {mtitle} - {Functions.siteTitle(lang)} + + +

{mtitle}

+ +
    + {index.pages.map((page) => { + const title = Functions.mlang(page.title, lang); + const url = CreditsUtils.getPageUrl(page.id); + return ( +
  • + {title} +
  • + ); + })} +
+ + ); }; export default CreditsIndex; diff --git a/src/credits/CreditsPage.tsx b/src/credits/CreditsPage.tsx index 503beae..7d1654b 100644 --- a/src/credits/CreditsPage.tsx +++ b/src/credits/CreditsPage.tsx @@ -1,62 +1,63 @@ -import React, { useEffect, useState } from "react"; -import { Helmet } from "react-helmet-async"; -import Loading from "../common/lib/Loading"; -import NoticeSiteHasBeenArchived from "../common/lib/NoticeSiteHasBeenArchived"; -import PageNotFound from "../common/lib/PageNotFound"; -import XoopsCode from "../common/lib/XoopsCode"; -import { MultiLang } from "../config"; -import Functions from "../functions"; -import CreditsUtils, { CreditsIndexData, CreditsPageDetailData } from "./lib/CreditsUtils"; +import type React from 'react'; +import { useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import Loading from '../common/lib/Loading'; +import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived'; +import PageNotFound from '../common/lib/PageNotFound'; +import XoopsCode from '../common/lib/XoopsCode'; +import type { MultiLang } from '../config'; +import Functions from '../functions'; +import CreditsUtils, { type CreditsIndexData, type CreditsPageDetailData } from './lib/CreditsUtils'; interface Props { - lang: MultiLang; - id: number; + lang: MultiLang; + id: number; } const CreditsPage: React.FC = (props: Props) => { - const { lang, id } = props; - const [loading, setLoading] = useState(true); - const [index, setIndex] = useState(null); - const [page, setPage] = useState(null); + const { lang, id } = props; + const [loading, setLoading] = useState(true); + const [index, setIndex] = useState(null); + const [page, setPage] = useState(null); - useEffect(() => { - const updatePage = async () => { - const index = await CreditsUtils.getIndex(); - const page = await CreditsUtils.getPage(id); - setLoading(false); - setIndex(index); - setPage(page); - }; - updatePage(); - }, [id]); + useEffect(() => { + const updatePage = async () => { + const index = await CreditsUtils.getIndex(); + const page = await CreditsUtils.getPage(id); + setLoading(false); + setIndex(index); + setPage(page); + }; + updatePage(); + }, [id]); - if (loading) { - return ; - } - if (page === null || index === null) { - return ; - } + if (loading) { + return ; + } + if (page === null || index === null) { + return ; + } - const mtitle = Functions.mlang(index.name, lang); - const title = Functions.mlang(page.title, lang); - const lastupdate = page.lastupdate === 0 ? "" : Functions.formatDate(page.lastupdate, "MMMM Do, YYYY"); - return ( - <> - - - {title} - {mtitle} - {Functions.siteTitle(lang)} - - -

{title}

- - {"" !== lastupdate && ( -
- {Functions.mlang("[en]Last Update[/en][ja]最終更新日[/ja]", lang)} : {lastupdate} -
- )} -
- - - ); + const mtitle = Functions.mlang(index.name, lang); + const title = Functions.mlang(page.title, lang); + const lastupdate = page.lastupdate === 0 ? '' : Functions.formatDate(page.lastupdate, 'MMMM Do, YYYY'); + return ( + <> + + + {title} - {mtitle} - {Functions.siteTitle(lang)} + + +

{title}

+ + {'' !== lastupdate && ( +
+ {Functions.mlang('[en]Last Update[/en][ja]最終更新日[/ja]', lang)} : {lastupdate} +
+ )} +
+ + + ); }; export default CreditsPage; diff --git a/src/credits/blocks/CreditsMenu.tsx b/src/credits/blocks/CreditsMenu.tsx index c8def03..1ee0d7f 100644 --- a/src/credits/blocks/CreditsMenu.tsx +++ b/src/credits/blocks/CreditsMenu.tsx @@ -1,47 +1,48 @@ -import React, { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; -import Loading from "../../common/lib/Loading"; -import PageNotFound from "../../common/lib/PageNotFound"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; -import CreditsUtils, { CreditsMenuData } from "../lib/CreditsUtils"; +import type React from 'react'; +import { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import Loading from '../../common/lib/Loading'; +import PageNotFound from '../../common/lib/PageNotFound'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; +import CreditsUtils, { type CreditsMenuData } from '../lib/CreditsUtils'; interface Props { - lang: MultiLang; + lang: MultiLang; } const CreditsMenu: React.FC = (props: Props) => { - const { lang } = props; - const [loading, setLoading] = useState(true); - const [menu, setMenu] = useState(null); + const { lang } = props; + const [loading, setLoading] = useState(true); + const [menu, setMenu] = useState(null); - useEffect(() => { - const updatePage = async () => { - const menu = await CreditsUtils.getMenu(); - setLoading(false); - setMenu(menu); - }; - updatePage(); - }, [lang]); + useEffect(() => { + const updatePage = async () => { + const menu = await CreditsUtils.getMenu(); + setLoading(false); + setMenu(menu); + }; + updatePage(); + }, [lang]); - if (loading) { - return ; - } - if (menu === null || menu.length === 0) { - return ; - } - const links = menu.map((item, idx) => { - const title = Functions.mlang(item.title, lang); - const style = idx === 0 ? "menuTop" : "menuMain"; - return ( -
  • - - {title} - -
  • - ); - }); - return
      {links}
    ; + if (loading) { + return ; + } + if (menu === null || menu.length === 0) { + return ; + } + const links = menu.map((item, idx) => { + const title = Functions.mlang(item.title, lang); + const style = idx === 0 ? 'menuTop' : 'menuMain'; + return ( +
  • + + {title} + +
  • + ); + }); + return
      {links}
    ; }; export default CreditsMenu; diff --git a/src/credits/lib/CreditsUtils.ts b/src/credits/lib/CreditsUtils.ts index 62d0616..339395c 100644 --- a/src/credits/lib/CreditsUtils.ts +++ b/src/credits/lib/CreditsUtils.ts @@ -1,77 +1,75 @@ -import axios from "axios"; +import ky from 'ky'; export interface CreditsPageData { - id: number; - title: string; + id: number; + title: string; } export interface CreditsPageDetailData extends CreditsPageData { - content: string; - lastupdate: number; + content: string; + lastupdate: number; } export interface CreditsIndexData { - name: string; - pages: CreditsPageData[]; + name: string; + pages: CreditsPageData[]; } export interface CreditsMenuData { - title: string; - link: string; + title: string; + link: string; } class CreditsUtils { - private pathname: string; - private index?: CreditsIndexData | null; + private pathname: string; + private index?: CreditsIndexData | null; - constructor(path: string) { - this.pathname = path; - } + constructor(path: string) { + this.pathname = path; + } - getIndexUrl(): string { - return this.pathname + "/"; - } + getIndexUrl(): string { + return this.pathname + '/'; + } - getPageUrl(id: number): string { - return this.getIndexUrl() + (0 === id ? "aboutus.php" : "?id=" + id); - } + getPageUrl(id: number): string { + return this.getIndexUrl() + (0 === id ? 'aboutus.php' : '?id=' + id); + } - async getIndex(): Promise { - if (typeof this.index === "undefined") { - try { - const url = this.getIndexUrl() + "index.json"; - const response = await axios.get(url); - const index = response.data as CreditsIndexData; - this.index = index; - } catch (err) { - this.index = null; - } - } - return this.index; + async getIndex(): Promise { + if (typeof this.index === 'undefined') { + try { + const url = this.getIndexUrl() + 'index.json'; + const index = (await ky.get(url).json()) as CreditsIndexData; + this.index = index; + } catch (_err) { + this.index = null; + } } + return this.index; + } - async getMenu(): Promise { - const menu: CreditsMenuData[] = []; - const index = await this.getIndex(); - if (index !== null) { - index.pages.forEach((page) => { - menu.push({ title: page.title, link: this.getPageUrl(page.id) }); - }); - } - return menu; + async getMenu(): Promise { + const menu: CreditsMenuData[] = []; + const index = await this.getIndex(); + if (index !== null) { + index.pages.forEach((page) => { + menu.push({ title: page.title, link: this.getPageUrl(page.id) }); + }); } + return menu; + } - async getPage(id: number): Promise { - let page = null; - try { - const url = this.getIndexUrl() + id + ".json"; - const response = await axios.get(url); - page = response.data as CreditsPageDetailData; - } catch (err) { - // ignore - } - return page; + async getPage(id: number): Promise { + let page = null; + try { + const url = this.getIndexUrl() + id + '.json'; + page = (await ky.get(url).json()) as CreditsPageDetailData; + } catch (_err) { + // ignore } + return page; + } } -export default new CreditsUtils("/modules/credits"); +export default new CreditsUtils('/modules/credits'); diff --git a/src/custom-types.d.ts b/src/custom-types.d.ts new file mode 100644 index 0000000..be4749a --- /dev/null +++ b/src/custom-types.d.ts @@ -0,0 +1,24 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.png' { + const src: string; + export default src; +} + +declare module '*.gif' { + const src: string; + export default src; +} + +declare module '*.jpg' { + const src: string; + export default src; +} + +declare module '*.jpeg' { + const src: string; + export default src; +} diff --git a/src/custom/Footer.tsx b/src/custom/Footer.tsx index 2f6a73b..e73e043 100644 --- a/src/custom/Footer.tsx +++ b/src/custom/Footer.tsx @@ -1,52 +1,62 @@ -import React from "react"; -import { useLocation } from "react-router-dom"; -import { HashLink } from "react-router-hash-link"; -import { MultiLang } from "../config"; -import imageBannerNiu from "./assets/images/banner-niu.png"; -import imageBannerRikenCbs from "./assets/images/banner-riken-cbs.png"; -import imageBannerRiken from "./assets/images/banner-riken.png"; -import imageBannerXoonips from "./assets/images/banner-xoonips.png"; +import type React from 'react'; +import { useLocation } from 'react-router-dom'; +import { HashLink } from 'react-router-hash-link'; +import type { MultiLang } from '../config'; +import imageBannerNiu from './assets/images/banner-niu.png'; +import imageBannerRiken from './assets/images/banner-riken.png'; +import imageBannerRikenCbs from './assets/images/banner-riken-cbs.png'; +import imageBannerXoonips from './assets/images/banner-xoonips.png'; interface Props { - lang: MultiLang; + lang: MultiLang; } -const Footer: React.FC = (props: Props) => { - const location = useLocation(); - return ( -
    -
    - - Go Page Top - -
    -
    - - - RIKEN - - - - - RIKEN Center for Brain Science - - - - - Neuroinformatics Unit, RIKEN Center for Brain Science - - - - - XooNIps - - -
    -
    - Copyright (C) 2018 Neuroinformatics Unit, RIKEN Center for Brain Science -
    -
    - ); +const Footer: React.FC = (_props: Props) => { + const location = useLocation(); + return ( +
    +
    + + Go Page Top + +
    +
    + + + RIKEN + + + + + RIKEN Center for Brain Science + + + + + Neuroinformatics Unit, RIKEN Center for Brain Science + + + + + XooNIps + + +
    +
    + Copyright (C) 2018 Neuroinformatics Unit, RIKEN Center for Brain Science +
    +
    + ); }; export default Footer; diff --git a/src/custom/Header.tsx b/src/custom/Header.tsx index 5d2da08..9d99cfc 100644 --- a/src/custom/Header.tsx +++ b/src/custom/Header.tsx @@ -1,73 +1,81 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import LangFlag from "../common/lib/LangFlag"; -import Config, { MultiLang } from "../config"; -import imageJnode from "./assets/images/jnode.png"; +import type React from 'react'; +import { Link } from 'react-router-dom'; +import LangFlag from '../common/lib/LangFlag'; +import Config, { type MultiLang } from '../config'; +import imageJnode from './assets/images/jnode.png'; interface Props { - lang: MultiLang; + lang: MultiLang; } -const Header: React.FC = (props: Props) => { - return ( - + ); }; export default Header; diff --git a/src/custom/Page.tsx b/src/custom/Page.tsx index ed9c149..2e63a92 100644 --- a/src/custom/Page.tsx +++ b/src/custom/Page.tsx @@ -1,148 +1,148 @@ -import React from "react"; -import { Outlet, Route, Routes } from "react-router-dom"; -import BrainExplorerIndex from "../brainexplorer/BrainExplorer"; -import XoopsPathRedirect from "../common/XoopsPathRedirect"; -import { MultiLang } from "../config"; -import CreditsMenu from "../credits/blocks/CreditsMenu"; -import Credits from "../credits/Credits"; -import NimgcenterUtilities from "../nimgcenter/NimgenterUtilities"; -import Nimgdocs from "../nimgdocs/Nimgdocs"; -import Nimgsearch from "../nimgsearch/Nimgsearch"; -import IndexTree from "../xoonips/blocks/IndexTree"; -import Ranking from "../xoonips/blocks/Rankings"; -import RecentContents from "../xoonips/blocks/RecentContents"; -import Search2 from "../xoonips/blocks/Search"; -import Xoonips from "../xoonips/Xoonips"; -import BulletinBoard from "./blocks/BulletinBoard"; -import HowToUseLinks from "./blocks/HowToUseLinks"; -import HowToUseVideo from "./blocks/HowToUseVideo"; -import Information from "./blocks/Information"; -import Questionnaire from "./blocks/Questionnaire"; -import RecentStatus from "./blocks/RecentStatus"; -import Footer from "./Footer"; -import Header from "./Header"; +import type React from 'react'; +import { Outlet, Route, Routes } from 'react-router-dom'; +import BrainExplorerIndex from '../brainexplorer/BrainExplorer'; +import XoopsPathRedirect from '../common/XoopsPathRedirect'; +import type { MultiLang } from '../config'; +import CreditsMenu from '../credits/blocks/CreditsMenu'; +import Credits from '../credits/Credits'; +import NimgcenterUtilities from '../nimgcenter/NimgenterUtilities'; +import Nimgdocs from '../nimgdocs/Nimgdocs'; +import Nimgsearch from '../nimgsearch/Nimgsearch'; +import IndexTree from '../xoonips/blocks/IndexTree'; +import Ranking from '../xoonips/blocks/Rankings'; +import RecentContents from '../xoonips/blocks/RecentContents'; +import Search2 from '../xoonips/blocks/Search'; +import Xoonips from '../xoonips/Xoonips'; +import BulletinBoard from './blocks/BulletinBoard'; +import HowToUseLinks from './blocks/HowToUseLinks'; +import HowToUseVideo from './blocks/HowToUseVideo'; +import Information from './blocks/Information'; +import Questionnaire from './blocks/Questionnaire'; +import RecentStatus from './blocks/RecentStatus'; +import Footer from './Footer'; +import Header from './Header'; interface Props { - lang: MultiLang; + lang: MultiLang; } -const MainContent: React.FC = (props: Props) => { - return ( -
    -
    - -
    -
    - ); +const MainContent: React.FC = (_props: Props) => { + return ( +
    +
    + +
    +
    + ); }; const CenterColumn: React.FC = (props: Props) => { - const { lang } = props; - return ( -
    - - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - -
    - ); + const { lang } = props; + return ( +
    + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + +
    + ); }; const CenterColumnBlock: React.FC = (props: Props) => { - const { lang } = props; - return ( - <> -
    - - - - - - + const { lang } = props; + return ( + <> +
    + + + + + + +
    +
    +
    +
    +
    XooNIps Ranking
    +
    +
    -
    -
    -
    -
    XooNIps Ranking
    -
    - -
    -
    -
    -
    -
    -
    XooNIps Update
    -
    - -
    -
    -
    +
    +
    +
    +
    +
    XooNIps Update
    +
    +
    - - ); +
    +
    +
    + + ); }; const LeftColumn: React.FC = (props: Props) => { - const { lang } = props; - return ( -
    - -
    -
    Index Tree
    -
    - -
    -
    -
    -
    Site Information
    -
    - -
    -
    + const { lang } = props; + return ( +
    + +
    +
    Index Tree
    +
    +
    - ); +
    +
    +
    Site Information
    +
    + +
    +
    +
    + ); }; const XoopsSearchBlock: React.FC = (props: Props) => { - const { lang } = props; - return ( -
    -
    Item Search
    -
    - -
    -
    - ); + const { lang } = props; + return ( +
    +
    Item Search
    +
    + +
    +
    + ); }; const Page: React.FC = (props: Props) => { - const { lang } = props; - return ( -
    -
    -
    - - }> - } /> - - - - }> - } /> - - -
    -
    -
    - ); + const { lang } = props; + return ( +
    +
    +
    + + }> + } /> + + + + }> + } /> + + +
    +
    +
    + ); }; export default Page; diff --git a/src/custom/assets/style.css b/src/custom/assets/style.css index ed1a3f8..70eccce 100644 --- a/src/custom/assets/style.css +++ b/src/custom/assets/style.css @@ -354,7 +354,8 @@ tr.odd > td, height: 30px; padding: 8px 10px 0 28px; border-radius: 3px 3px 3px 3px; - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5), + box-shadow: + 2px 2px 2px rgba(0, 0, 0, 0.5), 0 0 10px rgba(255, 255, 255, 0.5) inset; color: #fdfdfd; background: url(images/blockTitleIndent16.png) no-repeat 8px center #ef6e00; @@ -387,10 +388,10 @@ tr.odd > td, } #main h1 { - background:url(images/h1_indent.png) no-repeat left center; - padding: 5px 0 5px 33px; + background: url(images/h1_indent.png) no-repeat left center; + padding: 5px 0 5px 33px; } #main h2 { - background:url(images/h2_background.png); + background: url(images/h2_background.png); padding: 5px; -} \ No newline at end of file +} diff --git a/src/custom/blocks/BulletinBoard.tsx b/src/custom/blocks/BulletinBoard.tsx index 4760417..bc530c3 100644 --- a/src/custom/blocks/BulletinBoard.tsx +++ b/src/custom/blocks/BulletinBoard.tsx @@ -1,23 +1,27 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import { Link } from 'react-router-dom'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const BulletinBoard: React.FC = (props: Props) => { - const { lang } = props; - return ( -
    -
    {Functions.mlang("[en]Bulletin Board[/en][ja]掲示板[/ja]", lang)}
    -
    -

    {Functions.mlang("[en]There is no post yet.[/en][ja]まだ投稿がありません[/ja]", lang)}

    - {Functions.mlang("[en]View expired posts[/en][ja]掲示期間が終了した投稿を見る[/ja]", lang)} -
    -
    - ); + const { lang } = props; + return ( +
    +
    {Functions.mlang('[en]Bulletin Board[/en][ja]掲示板[/ja]', lang)}
    +
    +

    + {Functions.mlang('[en]There is no post yet.[/en][ja]まだ投稿がありません[/ja]', lang)} +

    + + {Functions.mlang('[en]View expired posts[/en][ja]掲示期間が終了した投稿を見る[/ja]', lang)} + +
    +
    + ); }; export default BulletinBoard; diff --git a/src/custom/blocks/HowToUseLinks.tsx b/src/custom/blocks/HowToUseLinks.tsx index 3229143..319e01d 100644 --- a/src/custom/blocks/HowToUseLinks.tsx +++ b/src/custom/blocks/HowToUseLinks.tsx @@ -1,40 +1,42 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import { Link } from 'react-router-dom'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const HowToUseLinks: React.FC = (props: Props) => { - const { lang } = props; - const title = "[en]How to use NIMG-PF[/en][ja]NIMG-PFの使い方[/ja]"; - const content = { - en: ( - - ), - ja: ( - - ), - }; - return ( -
    -
    {Functions.mlang(title, lang)}
    -
    {content[lang]}
    -
    - ); + const { lang } = props; + const title = '[en]How to use NIMG-PF[/en][ja]NIMG-PFの使い方[/ja]'; + const content = { + en: ( + + ), + ja: ( + + ), + }; + return ( +
    +
    {Functions.mlang(title, lang)}
    +
    {content[lang]}
    +
    + ); }; export default HowToUseLinks; diff --git a/src/custom/blocks/HowToUseVideo.tsx b/src/custom/blocks/HowToUseVideo.tsx index 6c36d10..331ff14 100644 --- a/src/custom/blocks/HowToUseVideo.tsx +++ b/src/custom/blocks/HowToUseVideo.tsx @@ -1,66 +1,69 @@ -import React, { useState } from "react"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import { useState } from 'react'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const HowToUseVideo: React.FC = (props) => { - const { lang } = props; - const [skip, setSkip] = useState(false); + const { lang } = props; + const [skip, setSkip] = useState(false); - const onClickShowMovie = (e: React.MouseEvent) => { - e.preventDefault(); - setSkip(false); - }; + const onClickShowMovie = (e: React.MouseEvent) => { + e.preventDefault(); + setSkip(false); + }; - const onClickSkipMovie = (e: React.MouseEvent) => { - e.preventDefault(); - setSkip(true); - }; + const onClickSkipMovie = (e: React.MouseEvent) => { + e.preventDefault(); + setSkip(true); + }; - const url = "/modules/nimgdocs/docs/viewlet/"; - const name = "ty-02" + (lang === "en" ? "eng" : "jap"); - const preview = `${url}/${name}_small.png`; - const poster = `${url}/${name}.png`; - const movieWebm = `${url}/${name}.webm`; - const movieMp4 = `${url}/${name}.mp4`; + const url = '/modules/nimgdocs/docs/viewlet/'; + const name = 'ty-02' + (lang === 'en' ? 'eng' : 'jap'); + const preview = `${url}/${name}_small.png`; + const poster = `${url}/${name}.png`; + const movieWebm = `${url}/${name}.webm`; + const movieMp4 = `${url}/${name}.mp4`; - return ( -
    -
    -
    -
    {Functions.mlang("[en]Function and how to use NIMG-PF[/en][ja]NIMG-PFの機能と使い方[/ja]", lang)}
    - {skip ? ( - <> - - How to Use - - ) : ( - <> - - - - )} -
    -
    + return ( +
    +
    +
    +
    + {Functions.mlang('[en]Function and how to use NIMG-PF[/en][ja]NIMG-PFの機能と使い方[/ja]', lang)} +
    + {skip ? ( + <> + + How to Use + + ) : ( + <> + + + + )}
    - ); +
    +
    + ); }; export default HowToUseVideo; diff --git a/src/custom/blocks/Information.tsx b/src/custom/blocks/Information.tsx index f20bcf5..c2104f3 100644 --- a/src/custom/blocks/Information.tsx +++ b/src/custom/blocks/Information.tsx @@ -1,29 +1,29 @@ -import React from "react"; -import NoticeSiteHasBeenArchived from "../../common/lib/NoticeSiteHasBeenArchived"; -import XoopsCode from "../../common/lib/XoopsCode"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import NoticeSiteHasBeenArchived from '../../common/lib/NoticeSiteHasBeenArchived'; +import XoopsCode from '../../common/lib/XoopsCode'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const Information: React.FC = (props: Props) => { - const { lang } = props; - const title = "[en]Information[/en][ja]お知らせ[/ja]"; - const content = { - en: "This site was opened to the public at 23 Jun 2009. When you submit an application form of user registration, please write it correctly. The application may not be approved, if it includes incorrect information or only abbreviation for address, organization, and division name. (Currently, user registration is closed.)", - ja: "このサイトは2009年6月23日に一般公開され、一般ユーザの登録を受け付けています。ユーザ登録されると、データ登録やダウンロードが可能になるなど、より多くの機能をご利用出来ます。ユーザ登録の際には、正確な情報を入力していただくようお願いします。不正確な情報が記入されていたり、住所・機関・所属などが短縮形だけであるなどの場合は、登録が承認されないことがあります。 (現在、新規登録を受け付けておりません。)", - }; - return ( -
    -
    {Functions.mlang(title, lang)}
    -
    - - -
    -
    - ); + const { lang } = props; + const title = '[en]Information[/en][ja]お知らせ[/ja]'; + const content = { + en: 'This site was opened to the public at 23 Jun 2009. When you submit an application form of user registration, please write it correctly. The application may not be approved, if it includes incorrect information or only abbreviation for address, organization, and division name. (Currently, user registration is closed.)', + ja: 'このサイトは2009年6月23日に一般公開され、一般ユーザの登録を受け付けています。ユーザ登録されると、データ登録やダウンロードが可能になるなど、より多くの機能をご利用出来ます。ユーザ登録の際には、正確な情報を入力していただくようお願いします。不正確な情報が記入されていたり、住所・機関・所属などが短縮形だけであるなどの場合は、登録が承認されないことがあります。 (現在、新規登録を受け付けておりません。)', + }; + return ( +
    +
    {Functions.mlang(title, lang)}
    +
    + + +
    +
    + ); }; export default Information; diff --git a/src/custom/blocks/Questionnaire.tsx b/src/custom/blocks/Questionnaire.tsx index 23ddb0a..7849d49 100644 --- a/src/custom/blocks/Questionnaire.tsx +++ b/src/custom/blocks/Questionnaire.tsx @@ -1,31 +1,41 @@ -import React from "react"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const Questionnaire: React.FC = (props: Props) => { - const { lang } = props; - const title = "[en]Questionnaire and Contact address[/en][ja]NIMG-PFの使い方[/ja]"; - return ( -
    -
    {Functions.mlang(title, lang)}
    -
    -
    - {Functions.mlang("[en]Please fill out the questionnaire.[/en][ja]アンケートにご協力ください[/ja]", lang)} ⇒{" "} - - {Functions.mlang("[en]Go to the page of questionnaire.[/en][ja]アンケートのページへ[/ja]", lang)} - -
    -
    {Functions.mlang("[en]Our e-mail address is as follows. If you have any questions, please let me know.[/en][ja]NIMG-PFについてご質問等ありましたら、以下までお願いします。[/ja]", lang)}
    -

    - {Functions.mlang("[en]Contact information of NIMG-PF[/en][ja]NIMG-PFについてのご連絡先[/ja]", lang)}: NIMG-info@ml.nict.go.jp -

    -
    + const { lang } = props; + const title = '[en]Questionnaire and Contact address[/en][ja]NIMG-PFの使い方[/ja]'; + return ( +
    +
    {Functions.mlang(title, lang)}
    +
    +
    + {Functions.mlang('[en]Please fill out the questionnaire.[/en][ja]アンケートにご協力ください[/ja]', lang)} ⇒{' '} + + {Functions.mlang('[en]Go to the page of questionnaire.[/en][ja]アンケートのページへ[/ja]', lang)} +
    - ); +
    + {Functions.mlang( + '[en]Our e-mail address is as follows. If you have any questions, please let me know.[/en][ja]NIMG-PFについてご質問等ありましたら、以下までお願いします。[/ja]', + lang, + )} +
    +

    + {Functions.mlang('[en]Contact information of NIMG-PF[/en][ja]NIMG-PFについてのご連絡先[/ja]', lang)}:{' '} + NIMG-info@ml.nict.go.jp +

    +
    +
    + ); }; export default Questionnaire; diff --git a/src/custom/blocks/RecentStatus.tsx b/src/custom/blocks/RecentStatus.tsx index 670076f..c09eaa0 100644 --- a/src/custom/blocks/RecentStatus.tsx +++ b/src/custom/blocks/RecentStatus.tsx @@ -1,20 +1,20 @@ -import React from "react"; -import { MultiLang } from "../../config"; -import Functions from "../../functions"; +import type React from 'react'; +import type { MultiLang } from '../../config'; +import Functions from '../../functions'; interface Props { - lang: MultiLang; + lang: MultiLang; } const RecentStatus: React.FC = (props: Props) => { - const { lang } = props; - const title = "[en]NIMG-PF recent status[/en][ja]NIMG-PF更新情報[/ja]"; - return ( -
    -
    {Functions.mlang(title, lang)}
    -
    -
    - ); + const { lang } = props; + const title = '[en]NIMG-PF recent status[/en][ja]NIMG-PF更新情報[/ja]'; + return ( +
    +
    {Functions.mlang(title, lang)}
    +
    +
    + ); }; export default RecentStatus; diff --git a/src/functions.ts b/src/functions.ts index 0138891..a8d0427 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -1,136 +1,143 @@ -import moment from "moment"; -import XRegExp from "xregexp"; -import Config, { MultiLang } from "./config"; +import moment from 'moment'; +import XRegExp from 'xregexp'; +import Config, { type MultiLang } from './config'; const escape = (str: string): string => { - return encodeURIComponent(str).replace(/[!'()*]/g, (c) => { - return "%" + c.charCodeAt(0).toString(16); - }); + return encodeURIComponent(str).replace(/[!'()*]/g, (c) => { + return '%' + c.charCodeAt(0).toString(16); + }); }; const unescape = (str: string): string => { - return decodeURIComponent(str); + return decodeURIComponent(str); }; const htmlspecialchars = (str: string): string => { - return str.replace(/(<|>|&|'|")/g, (match) => { - switch (match) { - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - case "'": - return "'"; - case '"': - return """; - } - return ""; - }); + return str.replace(/(<|>|&|'|")/g, (match) => { + switch (match) { + case '<': + return '<'; + case '>': + return '>'; + case '&': + return '&'; + case "'": + return '''; + case '"': + return '"'; + } + return ''; + }); }; const camelCase = (str: string): string => { - return str.replace(/[-_](.)/g, (...matches) => { - return matches[1].toUpperCase(); - }); + return str.replace(/[-_](.)/g, (...matches) => { + return matches[1].toUpperCase(); + }); }; const snakeCase = (str: string): string => { - var camel = camelCase(str); - return camel.replace(/[A-Z]/g, (...matches) => { - return "_" + matches[0].charAt(0).toLowerCase(); - }); + const camel = camelCase(str); + return camel.replace(/[A-Z]/g, (...matches) => { + return '_' + matches[0].charAt(0).toLowerCase(); + }); }; const pascalCase = (str: string): string => { - var camel = camelCase(str); - return camel.charAt(0).toUpperCase() + camel.slice(1); + const camel = camelCase(str); + return camel.charAt(0).toUpperCase() + camel.slice(1); }; const base64Encode = (str: string): string => { - return btoa(unescape(encodeURIComponent(str))); + return btoa(unescape(encodeURIComponent(str))); }; const base64Decode = (str: string): string => { - return decodeURIComponent(escape(atob(str))); + return decodeURIComponent(escape(atob(str))); }; const formatDate = (timestamp: number, format: string): string => { - return moment(new Date(timestamp * 1000)).format(format); + return moment(new Date(timestamp * 1000)).format(format); }; const ordinal = (num: number): string => { - const hundred = num % 100; - if (hundred >= 10 && hundred <= 20) { - return "th"; - } - const ten = num % 10; - const suffix = ["st", "nd", "rd"]; - return ten > 0 && ten < 4 ? suffix[ten - 1] : "th"; + const hundred = num % 100; + if (hundred >= 10 && hundred <= 20) { + return 'th'; + } + const ten = num % 10; + const suffix = ['st', 'nd', 'rd']; + return ten > 0 && ten < 4 ? suffix[ten - 1] : 'th'; }; const mlang = (str: string, lang: MultiLang): string => { - const mlLangs = ["en", "ja"]; - // escape brackets inside of - let text = str.replace(/(]*)(>)/gis, (whole, m1, m2, m3) => { - if (m2.match(/type=["']?(?:text|hidden)["']?/is)) { - return m1 + m2.replace(/\[/g, "__ml[ml__") + m3; - } - return whole; - }); - // escape brackets inside of - text = text.replace(/(]*>)(.*)(<\/textarea>)/gis, (whole, m1, m2, m3) => { - return m1 + m2.replace(/\[/g, "__ml[ml__") + m3; - }); - // simple pattern to strip selected lang_tags - const re = new RegExp("\\[/?(?:[^\\]]+\\|)?" + lang + "(?:\\|[^\\]]+)?\\](?:
    )?", "g"); - text = text.replace(re, ""); - // eliminate description between the other language tags. - mlLangs.forEach((mlLang) => { - if (mlLang !== lang) { - const re = XRegExp("\\[(?:[^/][^\\]]+\\|)?" + mlLang + "(?:\\|[^\\]]+)?\\].*?\\[/(?:[^\\]]+\\|)?" + mlLang + "(?:\\|[^\\]]+)?\\](?:
    )?", "isg"); - text = text.replace(re, (whole) => { - return whole.match(/<\/table>/) ? whole : ""; - }); - } - }); - // unescape brackets inside of - text = text.replace(/(]*>)(.*)(<\/textarea>)/gis, (whole, m1, m2, m3) => { - return m1 + m2.replace(/__ml\[ml__/g, "[") + m3; - }); - // unescape brackets inside of - text = text.replace(/(]*)(>)/gis, (whole, m1, m2, m3) => { - if (m2.match(/type=["']?(?:text|hidden)["']?/is)) { - return m1 + m2.replace(/__ml\[ml__/g, "[") + m3; - } - return whole; - }); - return text; + const mlLangs = ['en', 'ja']; + // escape brackets inside of + let text = str.replace(/(]*)(>)/gis, (whole, m1, m2, m3) => { + if (m2.match(/type=["']?(?:text|hidden)["']?/is)) { + return m1 + m2.replace(/\[/g, '__ml[ml__') + m3; + } + return whole; + }); + // escape brackets inside of + text = text.replace(/(]*>)(.*)(<\/textarea>)/gis, (_whole, m1, m2, m3) => { + return m1 + m2.replace(/\[/g, '__ml[ml__') + m3; + }); + // simple pattern to strip selected lang_tags + const re = new RegExp('\\[/?(?:[^\\]]+\\|)?' + lang + '(?:\\|[^\\]]+)?\\](?:
    )?', 'g'); + text = text.replace(re, ''); + // eliminate description between the other language tags. + mlLangs.forEach((mlLang) => { + if (mlLang !== lang) { + const re = XRegExp( + '\\[(?:[^/][^\\]]+\\|)?' + + mlLang + + '(?:\\|[^\\]]+)?\\].*?\\[/(?:[^\\]]+\\|)?' + + mlLang + + '(?:\\|[^\\]]+)?\\](?:
    )?', + 'isg', + ); + text = text.replace(re, (whole) => { + return whole.match(/<\/table>/) ? whole : ''; + }); + } + }); + // unescape brackets inside of + text = text.replace(/(]*>)(.*)(<\/textarea>)/gis, (_whole, m1, m2, m3) => { + return m1 + m2.replace(/__ml\[ml__/g, '[') + m3; + }); + // unescape brackets inside of + text = text.replace(/(]*)(>)/gis, (whole, m1, m2, m3) => { + if (m2.match(/type=["']?(?:text|hidden)["']?/is)) { + return m1 + m2.replace(/__ml\[ml__/g, '[') + m3; + } + return whole; + }); + return text; }; const siteTitle = (lang: MultiLang): string => { - return mlang(Config.SITE_TITLE, lang); + return mlang(Config.SITE_TITLE, lang); }; const siteSlogan = (lang: MultiLang): string => { - return mlang(Config.SITE_SLOGAN, lang); + return mlang(Config.SITE_SLOGAN, lang); }; const Functions = { - escape, - unescape, - htmlspecialchars, - camelCase, - snakeCase, - pascalCase, - base64Encode, - base64Decode, - formatDate, - ordinal, - mlang, - siteTitle, - siteSlogan, + escape, + unescape, + htmlspecialchars, + camelCase, + snakeCase, + pascalCase, + base64Encode, + base64Decode, + formatDate, + ordinal, + mlang, + siteTitle, + siteSlogan, }; export default Functions; diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index 0d9fff5..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react"; -import "react-app-polyfill/stable"; -import ReactDOM from "react-dom/client"; -import App from "./App"; -import "./index.css"; -import reportWebVitals from "./reportWebVitals"; - -const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..2239905 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App.tsx'; + +createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/src/nimgcenter/NimgcenterBrainConv.tsx b/src/nimgcenter/NimgcenterBrainConv.tsx index e959965..ffa5810 100644 --- a/src/nimgcenter/NimgcenterBrainConv.tsx +++ b/src/nimgcenter/NimgcenterBrainConv.tsx @@ -1,142 +1,151 @@ -import React, { useState } from "react"; -import { Link } from "react-router-dom"; -import { MultiLang } from "../config"; -import BrainCoordinateUtil, { BrainCoordinate, ConversionType } from "./lib/BrainCoordinateUtil"; +import type React from 'react'; +import { useState } from 'react'; +import { Link } from 'react-router-dom'; +import type { MultiLang } from '../config'; +import BrainCoordinateUtil, { type BrainCoordinate, type ConversionType } from './lib/BrainCoordinateUtil'; interface Props { - lang: MultiLang; + lang: MultiLang; } -const NimgcenterBrainConv: React.FC = (props: Props) => { - const defaultInputFormat = "\\d+\\.?\\d*"; - const defaultOutputFormat = "%x,%y,%z\\n"; +const NimgcenterBrainConv: React.FC = (_props: Props) => { + const defaultInputFormat = '\\d+\\.?\\d*'; + const defaultOutputFormat = '%x,%y,%z\\n'; - const [inputFormat, setInputFormat] = useState(defaultInputFormat); - const [inputArea, setInputArea] = useState(""); - const [outputFormat, setOutputFormat] = useState(defaultOutputFormat); - const [outputArea, setOutputArea] = useState(""); - const [conversionType, setConversionType] = useState("icbm_spm2tal"); + const [inputFormat, setInputFormat] = useState(defaultInputFormat); + const [inputArea, setInputArea] = useState(''); + const [outputFormat, setOutputFormat] = useState(defaultOutputFormat); + const [outputArea, setOutputArea] = useState(''); + const [conversionType, setConversionType] = useState('icbm_spm2tal'); - const parseInput = (input: string, format: string): BrainCoordinate[] => { - const ret: BrainCoordinate[] = []; - const regObj = new RegExp(format, "g"); - const values = input.match(regObj) || []; - for (let i = 0; i + 2 < values.length; i += 3) { - ret.push({ x: parseFloat(values[i]), y: parseFloat(values[i + 1]), z: parseFloat(values[i + 2]) }); - } - return ret; - }; + const parseInput = (input: string, format: string): BrainCoordinate[] => { + const ret: BrainCoordinate[] = []; + const regObj = new RegExp(format, 'g'); + const values = input.match(regObj) || []; + for (let i = 0; i + 2 < values.length; i += 3) { + ret.push({ x: parseFloat(values[i]), y: parseFloat(values[i + 1]), z: parseFloat(values[i + 2]) }); + } + return ret; + }; - const formatNumber = (value: number): string => { - const digit = 4; - const width = 7; - const ret = value.toFixed(digit); - return ret.length < width ? " ".repeat(width - ret.length) + ret : ret; - }; + const formatNumber = (value: number): string => { + const digit = 4; + const width = 7; + const ret = value.toFixed(digit); + return ret.length < width ? ' '.repeat(width - ret.length) + ret : ret; + }; - const onChangeInputFormat = (event: React.ChangeEvent) => { - setInputFormat(event.target.value); - }; + const onChangeInputFormat = (event: React.ChangeEvent) => { + setInputFormat(event.target.value); + }; - const onClickDefaultInputFormatButton = (event: React.MouseEvent) => { - setInputFormat(defaultInputFormat); - }; + const onClickDefaultInputFormatButton = (_event: React.MouseEvent) => { + setInputFormat(defaultInputFormat); + }; - const onChangeInputArea = (event: React.ChangeEvent) => { - setInputArea(event.target.value); - }; + const onChangeInputArea = (event: React.ChangeEvent) => { + setInputArea(event.target.value); + }; - const onChangeOutputFormat = (event: React.ChangeEvent) => { - setOutputFormat(event.target.value); - }; + const onChangeOutputFormat = (event: React.ChangeEvent) => { + setOutputFormat(event.target.value); + }; - const onClickDefaultOutputFormatButton = (event: React.MouseEvent) => { - setOutputFormat(defaultOutputFormat); - }; + const onClickDefaultOutputFormatButton = (_event: React.MouseEvent) => { + setOutputFormat(defaultOutputFormat); + }; - const onChangeMatrixType = (event: React.ChangeEvent) => { - if (BrainCoordinateUtil.isConversionType(event.target.value)) { - setConversionType(event.target.value as ConversionType); - } - }; + const onChangeMatrixType = (event: React.ChangeEvent) => { + if (BrainCoordinateUtil.isConversionType(event.target.value)) { + setConversionType(event.target.value as ConversionType); + } + }; - const onClickClearButton = (event: React.MouseEvent) => { - setInputArea(""); - setOutputArea(""); - }; + const onClickClearButton = (_event: React.MouseEvent) => { + setInputArea(''); + setOutputArea(''); + }; - const onClickConvertButton = (event: React.MouseEvent) => { - const inputValues = parseInput(inputArea, inputFormat); - const outputValues = inputValues.map((inputValue) => BrainCoordinateUtil.convertCoordinate(conversionType, inputValue)); - const outputTextArea = outputValues - .map((value) => { - return outputFormat.replace("%x", formatNumber(value.x)).replace("%y", formatNumber(value.y)).replace("%z", formatNumber(value.z)).replace("\\n", "\n"); - }) - .join(""); - setOutputArea(outputTextArea); - }; - - const matrix = BrainCoordinateUtil.getConversionMatrix(conversionType); - const matrixArea = "Conversion Matrix:\n" + matrix.map((vect) => " " + vect.map((value) => formatNumber(value)).join(", ")).join("\n"); - return ( -
    -
    - Brain Coordinate Converter is able to convert list of brain coordinate. How To Use -
    - - - - - - - -
    - Input format: (Regular expression). -
    - - -
    - Output format: (%x,%y,%z is replaced. and \n) -
    - - -
    - Input: -
    -