@jjlmoya/utils-forensic-science 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +69 -0
- package/scripts/crystal-lattice-catalog-translations.mjs +459 -0
- package/scripts/postinstall.mjs +27 -0
- package/scripts/sync-crystal-lattice-i18n.mjs +50 -0
- package/src/category/i18n/de.ts +55 -0
- package/src/category/i18n/en.ts +55 -0
- package/src/category/i18n/es.ts +55 -0
- package/src/category/i18n/fr.ts +55 -0
- package/src/category/i18n/id.ts +55 -0
- package/src/category/i18n/it.ts +55 -0
- package/src/category/i18n/ja.ts +56 -0
- package/src/category/i18n/ko.ts +56 -0
- package/src/category/i18n/nl.ts +55 -0
- package/src/category/i18n/pl.ts +55 -0
- package/src/category/i18n/pt.ts +55 -0
- package/src/category/i18n/ru.ts +55 -0
- package/src/category/i18n/sv.ts +55 -0
- package/src/category/i18n/tr.ts +55 -0
- package/src/category/i18n/zh.ts +56 -0
- package/src/category/index.ts +48 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +10 -0
- package/src/entries.ts +48 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +29 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +162 -0
- package/src/pages/[locale].astro +261 -0
- package/src/pages/index.astro +3 -0
- package/src/tests/diacritics_density.test.ts +140 -0
- package/src/tests/faq_count.test.ts +19 -0
- package/src/tests/i18n_coverage.test.ts +36 -0
- package/src/tests/inverted_punctuation.test.ts +84 -0
- package/src/tests/locale_completeness.test.ts +24 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_en_dash.test.ts +70 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/script_density.test.ts +94 -0
- package/src/tests/seo_length.test.ts +46 -0
- package/src/tests/shared-test-helpers.ts +56 -0
- package/src/tests/slug_language_code_format.test.ts +23 -0
- package/src/tests/slug_uniqueness.test.ts +81 -0
- package/src/tests/title_quality.test.ts +55 -0
- package/src/tests/tool_exports.test.ts +34 -0
- package/src/tests/tool_validation.test.ts +16 -0
- package/src/tests/widmark.test.ts +28 -0
- package/src/tool/forensic-age-estimator/bibliography.astro +14 -0
- package/src/tool/forensic-age-estimator/bibliography.ts +16 -0
- package/src/tool/forensic-age-estimator/component.astro +321 -0
- package/src/tool/forensic-age-estimator/dental-skeletal-third-molar-age-estimator.css +634 -0
- package/src/tool/forensic-age-estimator/entry.ts +32 -0
- package/src/tool/forensic-age-estimator/i18n/de.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/en.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/es.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/fr.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/id.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/it.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/ja.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/ko.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/nl.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/pl.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/pt.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/ru.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/sv.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/tr.ts +245 -0
- package/src/tool/forensic-age-estimator/i18n/zh.ts +246 -0
- package/src/tool/forensic-age-estimator/index.ts +11 -0
- package/src/tool/forensic-age-estimator/logic.ts +81 -0
- package/src/tool/forensic-age-estimator/seo.astro +15 -0
- package/src/tool/forensic-blood-test-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-blood-test-simulator/bibliography.ts +12 -0
- package/src/tool/forensic-blood-test-simulator/component.astro +168 -0
- package/src/tool/forensic-blood-test-simulator/dom-utils.ts +40 -0
- package/src/tool/forensic-blood-test-simulator/entry.ts +32 -0
- package/src/tool/forensic-blood-test-simulator/forensic-presumptive-blood-testing-luminol-kastle-meyer-simulator.css +532 -0
- package/src/tool/forensic-blood-test-simulator/helpers.ts +16 -0
- package/src/tool/forensic-blood-test-simulator/i18n/de.ts +185 -0
- package/src/tool/forensic-blood-test-simulator/i18n/en.ts +185 -0
- package/src/tool/forensic-blood-test-simulator/i18n/es.ts +185 -0
- package/src/tool/forensic-blood-test-simulator/i18n/fr.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/id.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/it.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/ja.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/ko.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/nl.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/pl.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/pt.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/ru.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/sv.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/tr.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/i18n/zh.ts +220 -0
- package/src/tool/forensic-blood-test-simulator/index.ts +11 -0
- package/src/tool/forensic-blood-test-simulator/logic.ts +174 -0
- package/src/tool/forensic-blood-test-simulator/script.ts +192 -0
- package/src/tool/forensic-blood-test-simulator/seo.astro +15 -0
- package/src/tool/forensic-fiber-comparison-microscope/bibliography.astro +6 -0
- package/src/tool/forensic-fiber-comparison-microscope/bibliography.ts +16 -0
- package/src/tool/forensic-fiber-comparison-microscope/component.astro +146 -0
- package/src/tool/forensic-fiber-comparison-microscope/entry.ts +32 -0
- package/src/tool/forensic-fiber-comparison-microscope/forensic-fiber-comparison-microscope.css +629 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/de.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/en.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/es.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/fr.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/id.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/it.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/ja.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/ko.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/nl.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/pl.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/pt.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/ru.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/sv.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/tr.ts +246 -0
- package/src/tool/forensic-fiber-comparison-microscope/i18n/zh.ts +250 -0
- package/src/tool/forensic-fiber-comparison-microscope/index.ts +11 -0
- package/src/tool/forensic-fiber-comparison-microscope/logic.ts +244 -0
- package/src/tool/forensic-fiber-comparison-microscope/render.ts +265 -0
- package/src/tool/forensic-fiber-comparison-microscope/seo.astro +15 -0
- package/src/tool/forensic-fiber-comparison-microscope/view.ts +267 -0
- package/src/tool/forensic-glass-becke-line-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-glass-becke-line-simulator/bibliography.ts +16 -0
- package/src/tool/forensic-glass-becke-line-simulator/component.astro +81 -0
- package/src/tool/forensic-glass-becke-line-simulator/entry.ts +32 -0
- package/src/tool/forensic-glass-becke-line-simulator/forensic-glass-becke-line-simulator.css +392 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/de.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/en.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/es.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/fr.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/id.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/it.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/ja.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/ko.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/nl.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/pl.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/pt.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/ru.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/sv.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/tr.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/i18n/zh.ts +231 -0
- package/src/tool/forensic-glass-becke-line-simulator/index.ts +11 -0
- package/src/tool/forensic-glass-becke-line-simulator/logic.ts +100 -0
- package/src/tool/forensic-glass-becke-line-simulator/seo.astro +15 -0
- package/src/tool/forensic-glass-becke-line-simulator/view.ts +281 -0
- package/src/tool/forensic-image-authenticity-analyzer/bibliography.astro +9 -0
- package/src/tool/forensic-image-authenticity-analyzer/bibliography.ts +7 -0
- package/src/tool/forensic-image-authenticity-analyzer/component.astro +250 -0
- package/src/tool/forensic-image-authenticity-analyzer/entry.ts +29 -0
- package/src/tool/forensic-image-authenticity-analyzer/forensic-image-metadata-authenticity-analyzer.css +679 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/de.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/en.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/es.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/fr.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/id.ts +76 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/it.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/ja.ts +72 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/ko.ts +72 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/nl.ts +75 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/pl.ts +75 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/pt.ts +105 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/ru.ts +75 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/sv.ts +76 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/tr.ts +76 -0
- package/src/tool/forensic-image-authenticity-analyzer/i18n/zh.ts +71 -0
- package/src/tool/forensic-image-authenticity-analyzer/index.ts +11 -0
- package/src/tool/forensic-image-authenticity-analyzer/logic.ts +283 -0
- package/src/tool/forensic-image-authenticity-analyzer/seo.astro +10 -0
- package/src/tool/forensic-microcrystal-drug-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-microcrystal-drug-simulator/bibliography.ts +12 -0
- package/src/tool/forensic-microcrystal-drug-simulator/component.astro +240 -0
- package/src/tool/forensic-microcrystal-drug-simulator/entry.ts +32 -0
- package/src/tool/forensic-microcrystal-drug-simulator/forensic-microcrystal-drug-simulator.css +430 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/de.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/en.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/es.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/fr.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/id.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/it.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/ja.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/ko.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/nl.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/pl.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/pt.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/ru.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/sv.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/tr.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/i18n/zh.ts +244 -0
- package/src/tool/forensic-microcrystal-drug-simulator/index.ts +11 -0
- package/src/tool/forensic-microcrystal-drug-simulator/logic.ts +189 -0
- package/src/tool/forensic-microcrystal-drug-simulator/seo.astro +15 -0
- package/src/tool/forensic-sex-determinator/bibliography.astro +14 -0
- package/src/tool/forensic-sex-determinator/bibliography.ts +12 -0
- package/src/tool/forensic-sex-determinator/component.astro +463 -0
- package/src/tool/forensic-sex-determinator/entry.ts +32 -0
- package/src/tool/forensic-sex-determinator/forensic-sex-determinator.css +413 -0
- package/src/tool/forensic-sex-determinator/i18n/de.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/en.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/es.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/fr.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/id.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/it.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/ja.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/ko.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/nl.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/pl.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/pt.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/ru.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/sv.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/tr.ts +211 -0
- package/src/tool/forensic-sex-determinator/i18n/zh.ts +211 -0
- package/src/tool/forensic-sex-determinator/index.ts +11 -0
- package/src/tool/forensic-sex-determinator/logic.ts +89 -0
- package/src/tool/forensic-sex-determinator/seo.astro +15 -0
- package/src/tool/forensic-stature-estimator/bibliography.astro +14 -0
- package/src/tool/forensic-stature-estimator/bibliography.ts +12 -0
- package/src/tool/forensic-stature-estimator/component.astro +49 -0
- package/src/tool/forensic-stature-estimator/components/EstimationPanel.astro +65 -0
- package/src/tool/forensic-stature-estimator/components/OsteometricBoard.astro +39 -0
- package/src/tool/forensic-stature-estimator/components/OsteometricSelector.astro +109 -0
- package/src/tool/forensic-stature-estimator/dom-utils.ts +71 -0
- package/src/tool/forensic-stature-estimator/entry.ts +32 -0
- package/src/tool/forensic-stature-estimator/forensic-stature-estimator.css +689 -0
- package/src/tool/forensic-stature-estimator/helpers.ts +51 -0
- package/src/tool/forensic-stature-estimator/i18n/de.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/en.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/es.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/fr.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/id.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/it.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/ja.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/ko.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/nl.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/pl.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/pt.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/ru.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/sv.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/tr.ts +196 -0
- package/src/tool/forensic-stature-estimator/i18n/zh.ts +196 -0
- package/src/tool/forensic-stature-estimator/index.ts +11 -0
- package/src/tool/forensic-stature-estimator/logic.ts +119 -0
- package/src/tool/forensic-stature-estimator/script.ts +288 -0
- package/src/tool/forensic-stature-estimator/seo.astro +15 -0
- package/src/tool/forensic-tlc-ink-simulator/bibliography.astro +6 -0
- package/src/tool/forensic-tlc-ink-simulator/bibliography.ts +16 -0
- package/src/tool/forensic-tlc-ink-simulator/component.astro +245 -0
- package/src/tool/forensic-tlc-ink-simulator/entry.ts +32 -0
- package/src/tool/forensic-tlc-ink-simulator/forensic-tlc-ink-simulator.css +462 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/de.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/en.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/es.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/fr.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/id.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/it.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/ja.ts +235 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/ko.ts +235 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/nl.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/pl.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/pt.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/ru.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/sv.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/tr.ts +243 -0
- package/src/tool/forensic-tlc-ink-simulator/i18n/zh.ts +235 -0
- package/src/tool/forensic-tlc-ink-simulator/index.ts +11 -0
- package/src/tool/forensic-tlc-ink-simulator/logic.ts +152 -0
- package/src/tool/forensic-tlc-ink-simulator/seo.astro +15 -0
- package/src/tool/gsr-dispersion-calculator/bibliography.astro +6 -0
- package/src/tool/gsr-dispersion-calculator/bibliography.ts +16 -0
- package/src/tool/gsr-dispersion-calculator/component.astro +294 -0
- package/src/tool/gsr-dispersion-calculator/entry.ts +32 -0
- package/src/tool/gsr-dispersion-calculator/gsr-dispersion-calculator.css +305 -0
- package/src/tool/gsr-dispersion-calculator/i18n/de.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/en.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/es.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/fr.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/id.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/it.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/ja.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/ko.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/nl.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/pl.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/pt.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/ru.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/sv.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/tr.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/i18n/zh.ts +272 -0
- package/src/tool/gsr-dispersion-calculator/index.ts +11 -0
- package/src/tool/gsr-dispersion-calculator/logic.ts +148 -0
- package/src/tool/gsr-dispersion-calculator/seo.astro +15 -0
- package/src/tool/widmark-alcohol-simulator/bibliography.astro +14 -0
- package/src/tool/widmark-alcohol-simulator/bibliography.ts +12 -0
- package/src/tool/widmark-alcohol-simulator/component.astro +453 -0
- package/src/tool/widmark-alcohol-simulator/entry.ts +32 -0
- package/src/tool/widmark-alcohol-simulator/i18n/de.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/en.ts +206 -0
- package/src/tool/widmark-alcohol-simulator/i18n/es.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/fr.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/id.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/it.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/ja.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/ko.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/nl.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/pl.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/pt.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/ru.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/sv.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/tr.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/i18n/zh.ts +193 -0
- package/src/tool/widmark-alcohol-simulator/index.ts +11 -0
- package/src/tool/widmark-alcohol-simulator/logic.ts +97 -0
- package/src/tool/widmark-alcohol-simulator/seo.astro +15 -0
- package/src/tool/widmark-alcohol-simulator/widmark-alcohol-simulator.css +386 -0
- package/src/tools.ts +27 -0
- package/src/types.ts +70 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
|
|
4
|
+
const slug = 'dental-skeletal-third-molar-age-estimator';
|
|
5
|
+
const title = '牙齿、骨骼与第三磨牙年龄估算器';
|
|
6
|
+
const description = '基于牙齿发育、骨骼成熟度和第三磨牙指标进行法医年龄区间估算,辅助案件筛查与分诊。';
|
|
7
|
+
|
|
8
|
+
const howTo = [
|
|
9
|
+
{
|
|
10
|
+
name: '选择证据侧重点',
|
|
11
|
+
text: '选择"混合证据"以进行综合分诊;若牙齿证据更为详实,请选择"牙齿优先";若骨骼X射线指标更为可靠,请选择"骨骼优先"。',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: '评估各成熟度指标',
|
|
15
|
+
text: '将每个指标设定在0到8的阶段。较低阶段代表发育早期,中间阶段代表青少年期成熟,较高阶段则代表成熟或接近成熟。',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: '参考区间而非单一年龄',
|
|
19
|
+
text: '应综合考量最小值、可能值和最大值。由于生物发育存在个体差异,法医年龄评估应始终以区间形式呈现。',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: '核实各指标间的一致性',
|
|
23
|
+
text: '若牙齿、骨骼和第三磨牙阶段指向不同的年龄范围,请将结果视为"探索性",并在做出最终判断前收集更多证据。',
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const faq = [
|
|
28
|
+
{
|
|
29
|
+
question: '该法医年龄估算器能否判定法定成年年龄?',
|
|
30
|
+
answer: '不能。本工具仅用于教学和分诊筛查。法定年龄判定必须遵循当地经认可的规程,并由具备资质的专家结合身份档案及司法管辖区特定标准进行评估。',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
question: '为什么结果显示的是一个年龄区间?',
|
|
34
|
+
answer: '牙齿萌出、骨骼融合及第三磨牙发育受血统、营养、健康状况、性别、参照人群及影像检查方法等多种因素影响。以区间形式呈现比单一精确年龄更符合科学严谨性。',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
question: '"混合证据"是什么意思?',
|
|
38
|
+
answer: '"混合证据"模式综合考量了牙齿发育、骨骼成熟度和第三磨牙阶段。当没有单一指标在特定案件中表现出显著的可靠性优势时,建议使用此模式。',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
question: '第三磨牙在所有案例中都可靠吗?',
|
|
42
|
+
answer: '第三磨牙对于青少年后期和青年期的评估很有价值,但其个体变异很大。牙齿先天缺失、病理变化、人口学差异以及影像质量都会限制其诊断价值。',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
question: '该工具能否直接用于实际法律案件?',
|
|
46
|
+
answer: '仅可作为初步辅助参考。实际案件评估必须使用经同行评审的方法、校准后的参照数据,并由独立专家进行审查,同时需透明地报告结果的不确定性和局限性。',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
export const content: ToolLocaleContent = {
|
|
51
|
+
slug,
|
|
52
|
+
title,
|
|
53
|
+
description,
|
|
54
|
+
ui: {
|
|
55
|
+
caseBoard: '法医案件面板',
|
|
56
|
+
controls: '法医年龄估算控制',
|
|
57
|
+
results: '法医年龄估算结果',
|
|
58
|
+
maturityProfile: '动态成熟度概况',
|
|
59
|
+
screeningOnly: '筛查估算',
|
|
60
|
+
evidenceType: '证据侧重点',
|
|
61
|
+
mixedEvidence: '混合证据',
|
|
62
|
+
dentalPriority: '牙齿优先',
|
|
63
|
+
skeletalPriority: '骨骼优先',
|
|
64
|
+
dentalStage: '牙齿发育阶段',
|
|
65
|
+
epiphysealStage: '骨骺融合阶段',
|
|
66
|
+
thirdMolarStage: '第三磨牙阶段',
|
|
67
|
+
minimumAge: '最小值',
|
|
68
|
+
likelyAge: '可能值',
|
|
69
|
+
maximumAge: '最大值',
|
|
70
|
+
maturityScore: '成熟度评分',
|
|
71
|
+
stageLabel: '阶段',
|
|
72
|
+
ageUnit: '岁',
|
|
73
|
+
dentalShort: '牙齿',
|
|
74
|
+
boneShort: '骨骼',
|
|
75
|
+
molarShort: '磨牙',
|
|
76
|
+
dentalStageHelp: '牙冠、牙根及根尖形成情况。',
|
|
77
|
+
epiphysealStageHelp: '生长板闭合与骨融合进度。',
|
|
78
|
+
molarStageHelp: '第三磨牙矿化与根尖闭合情况。',
|
|
79
|
+
dentalStage0: '缺失',
|
|
80
|
+
dentalStage1: '牙尖形成',
|
|
81
|
+
dentalStage2: '牙冠形成',
|
|
82
|
+
dentalStage3: '牙根起始',
|
|
83
|
+
dentalStage4: '牙根达一半',
|
|
84
|
+
dentalStage5: '牙根长成',
|
|
85
|
+
dentalStage6: '根尖狭窄',
|
|
86
|
+
dentalStage7: '根尖闭合中',
|
|
87
|
+
dentalStage8: '发育完成',
|
|
88
|
+
epiphysealStage0: '开放',
|
|
89
|
+
epiphysealStage1: '边缘模糊',
|
|
90
|
+
epiphysealStage2: '变窄',
|
|
91
|
+
epiphysealStage3: '桥接形成',
|
|
92
|
+
epiphysealStage4: '一半融合',
|
|
93
|
+
epiphysealStage5: '大部分融合',
|
|
94
|
+
epiphysealStage6: '仅剩痕迹',
|
|
95
|
+
epiphysealStage7: '接近闭合',
|
|
96
|
+
epiphysealStage8: '闭合',
|
|
97
|
+
molarStage0: '缺失',
|
|
98
|
+
molarStage1: '牙尖形成',
|
|
99
|
+
molarStage2: '牙冠起始',
|
|
100
|
+
molarStage3: '牙冠完整',
|
|
101
|
+
molarStage4: '牙根起始',
|
|
102
|
+
molarStage5: '牙根达一半',
|
|
103
|
+
molarStage6: '牙根长成',
|
|
104
|
+
molarStage7: '根尖闭合中',
|
|
105
|
+
molarStage8: '发育完成',
|
|
106
|
+
confidenceexploratory: '探索性',
|
|
107
|
+
confidencemoderate: '中等',
|
|
108
|
+
confidencestrong: '高度可靠',
|
|
109
|
+
noteTriage: '仅作筛查估算参考,不可替代法定年龄判定。',
|
|
110
|
+
noteFinalAssessment: '最终法医评估需结合影像学检查、临床牙科评估、病史记录及司法辖区特定标准。',
|
|
111
|
+
noteWideInterval: '各指标间存在超过四年的偏差,区间已自动放大。',
|
|
112
|
+
},
|
|
113
|
+
seo: [
|
|
114
|
+
{
|
|
115
|
+
type: 'title',
|
|
116
|
+
text: '法医年龄估算器:牙齿、骨骼与第三磨牙筛查计算工具',
|
|
117
|
+
level: 2,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'paragraph',
|
|
121
|
+
html: '当您需要在正式评估前快速、透明地整理生物成熟度证据时,请使用本法医年龄估算器。该工具整合了牙齿发育、骨骺融合及第三磨牙发育阶段,为您提供估算的年龄区间。它旨在响应<strong>法医年龄估算计算器</strong>、<strong>牙科年龄评估</strong>、<strong>骨骼成熟度年龄估算</strong>以及<strong>第三磨牙年龄估算</strong>等专业查询需求。用户可借此深入了解各项指标的含义、组合方式及结果的不确定性度量。',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
type: 'paragraph',
|
|
125
|
+
html: '估算结果被明确表述为<strong>最小年龄、可能年龄和最大年龄</strong>。由于个体发育速度的差异,单一的精确数字往往具有误导性。营养、健康、血统、参照人群、成像质量及观察者差异均会影响结果的准确性。本计算器旨在提供结构化的筛查支持,不应视作司法裁判依据。',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: 'title',
|
|
129
|
+
text: '三大成熟度指标解析',
|
|
130
|
+
level: 3,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'paragraph',
|
|
134
|
+
html: '0至8阶段的滑块设定将复杂的发育过程抽象为标准化数值。阶段0代表成熟度极低或缺失,阶段8代表发育成熟。这种抽象化处理不仅能够辅助初学者快速上手,还能让专家直观对比牙齿、骨骼及第三磨牙指标在区间估算中的权重表现。',
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
{
|
|
138
|
+
type: 'table',
|
|
139
|
+
headers: ['指标', '描述', '应用场景', '注意事项'],
|
|
140
|
+
rows: [
|
|
141
|
+
['牙齿发育', '除第三磨牙外牙齿的形成与萌出。', '儿童及青少年的主要评估指标。', '牙病、拔牙及种族差异可能引入偏差。'],
|
|
142
|
+
['骨骺融合', '骨骼系统中生长板的进行性闭合。', '针对青少年及青年期的重要指标。', '受特定骨骼部位、性别及内分泌水平影响。'],
|
|
143
|
+
['第三磨牙', '智齿的形成与矿化进程。', '其他牙齿发育完成后的补充评估指标。', '个体变异极大,需评估有无先天缺失及阻生。'],
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'title',
|
|
148
|
+
text: '如何解读年龄区间',
|
|
149
|
+
level: 3,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'paragraph',
|
|
153
|
+
html: '请综合考量三个年龄数值。<strong>可能年龄</strong>是基于您选择的证据侧重点计算出的加权中心点。<strong>最小值</strong>和<strong>最大值</strong>展示了实际操作中的不确定性范围。当各项指标趋于一致时,范围会收窄;而当牙齿、骨骼及第三磨牙结论不一,范围会扩大,此时置信度标签将相应降低。',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'table',
|
|
157
|
+
headers: ['结果状态', '含义', '建议操作'],
|
|
158
|
+
rows: [
|
|
159
|
+
['窄区间/置信度高', '各项指标吻合,发育阶段一致。', '记录原始数据,评估是否需要更高级别的司法验证。'],
|
|
160
|
+
['中等区间', '存在轻微分歧或权重设置影响了中心点。', '校准影像质量及阶段评估标准。'],
|
|
161
|
+
['宽区间/探索性', '指标冲突明显,指向不同的生理阶段。', '切勿仅凭该估算下结论;请参考补充文档或请求专家会诊。'],
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: 'title',
|
|
166
|
+
text: '证据模式选择指南',
|
|
167
|
+
level: 3,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: 'paragraph',
|
|
171
|
+
html: '<strong>混合证据</strong>模式是筛查的默认设置,适用于综合评估。若牙齿记录详尽且无可争议,建议选择<strong>牙齿优先</strong>;若骨骼放射学检查更为全面权威,则建议选择<strong>骨骼优先</strong>。这些模式仅调整数学权重,并不能提升原数据本身的基础质量。',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'list',
|
|
175
|
+
items: [
|
|
176
|
+
'<strong>牙齿优先:</strong>牙齿分期清晰且第三磨牙数据完整时使用。',
|
|
177
|
+
'<strong>骨骼优先:</strong>当骨骺融合提供了最详尽的成熟度证据时使用。',
|
|
178
|
+
'<strong>混合证据:</strong>理想的初步快速评估模式。',
|
|
179
|
+
'<strong>置信度标签:</strong>请密切关注,以在做出判断前警惕数据间的冲突。',
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
type: 'title',
|
|
184
|
+
text: '法医年龄评估的局限性',
|
|
185
|
+
level: 3,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: 'paragraph',
|
|
189
|
+
html: '年龄评估仅能作为辅助支持材料,不能等同于身份确定。在法律保护、刑事责任判定、灾难识别及移民身份等领域,必须采用多学科综合评估。规范的报告应当详尽陈述使用的方法学、参照人群、影像来源、专家资质及已知的边界限制。',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
type: 'paragraph',
|
|
193
|
+
html: '本工具定位于<strong>教育培训、临床分诊及透明化沟通</strong>。我们强调:在面对法律意义重大的年龄阈值判定(例如18周岁)时,不应仅依赖任何简化的计算工具。',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: 'title',
|
|
197
|
+
text: '法医报告自检清单',
|
|
198
|
+
level: 3,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
type: 'list',
|
|
202
|
+
items: [
|
|
203
|
+
'<strong>证据溯源:</strong>注明影像类型(如口内牙科片、全景片、手腕骨骨龄片或锁骨CT等)。',
|
|
204
|
+
'<strong>缺失/非典型数据:</strong>明确标注先天缺失、低画质或解剖发育变异情况。',
|
|
205
|
+
'<strong>方法论合法性:</strong>确保所用评估方法在当前司法管辖区内是被认可的。',
|
|
206
|
+
'<strong>不确定性呈现:</strong>始终提供范围区间,避免使用暗示"绝对年龄"的表述。',
|
|
207
|
+
'<strong>专家复核:</strong>涉及重大法律影响的案件,必须交由专业法医牙科专家、放射科医生或人类学家复核。',
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
faq,
|
|
212
|
+
bibliography,
|
|
213
|
+
howTo,
|
|
214
|
+
schemas: [
|
|
215
|
+
{
|
|
216
|
+
'@context': 'https://schema.org',
|
|
217
|
+
'@type': 'SoftwareApplication',
|
|
218
|
+
name: title,
|
|
219
|
+
description,
|
|
220
|
+
applicationCategory: 'ForensicApplication',
|
|
221
|
+
operatingSystem: 'Any',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
'@context': 'https://schema.org',
|
|
225
|
+
'@type': 'FAQPage',
|
|
226
|
+
mainEntity: faq.map((item) => ({
|
|
227
|
+
'@type': 'Question',
|
|
228
|
+
name: item.question,
|
|
229
|
+
acceptedAnswer: {
|
|
230
|
+
'@type': 'Answer',
|
|
231
|
+
text: item.answer,
|
|
232
|
+
},
|
|
233
|
+
})),
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
'@context': 'https://schema.org',
|
|
237
|
+
'@type': 'HowTo',
|
|
238
|
+
name: title,
|
|
239
|
+
step: howTo.map((step) => ({
|
|
240
|
+
'@type': 'HowToStep',
|
|
241
|
+
name: step.name,
|
|
242
|
+
text: step.text,
|
|
243
|
+
})),
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { forensicAgeEstimator } from './entry';
|
|
2
|
+
import type { ToolDefinition } from '../../types';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
|
|
6
|
+
export const FORENSIC_AGE_ESTIMATOR_TOOL: ToolDefinition = {
|
|
7
|
+
entry: forensicAgeEstimator,
|
|
8
|
+
Component: () => import('./component.astro'),
|
|
9
|
+
SEOComponent: () => import('./seo.astro'),
|
|
10
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
11
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export type EvidenceType = 'dental' | 'skeletal' | 'mixed';
|
|
2
|
+
|
|
3
|
+
export interface AgeEstimatorInput {
|
|
4
|
+
dentalStage: number;
|
|
5
|
+
epiphysealStage: number;
|
|
6
|
+
thirdMolarStage: number;
|
|
7
|
+
evidenceType: EvidenceType;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AgeEstimatorResult {
|
|
11
|
+
minimumAge: number;
|
|
12
|
+
likelyAge: number;
|
|
13
|
+
maximumAge: number;
|
|
14
|
+
confidence: 'exploratory' | 'moderate' | 'strong';
|
|
15
|
+
maturityScore: number;
|
|
16
|
+
notes: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const dentalAnchors = [6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18];
|
|
20
|
+
const epiphysealAnchors = [10, 11.5, 13, 14.5, 16, 17.5, 19, 20.5, 22];
|
|
21
|
+
const thirdMolarAnchors = [13, 14.5, 16, 17.5, 19, 20.5, 22, 23.5, 25];
|
|
22
|
+
const evidenceWeights: Record<EvidenceType, [number, number, number]> = {
|
|
23
|
+
dental: [0.55, 0.15, 0.3],
|
|
24
|
+
skeletal: [0.2, 0.6, 0.2],
|
|
25
|
+
mixed: [0.36, 0.36, 0.28],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function clampStage(value: number): number {
|
|
29
|
+
return Math.min(8, Math.max(0, Math.round(value)));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function anchorFor(anchors: number[], stage: number): number {
|
|
33
|
+
return anchors[clampStage(stage)] ?? anchors[0]!;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function weightedAverage(values: Array<{ value: number; weight: number }>): number {
|
|
37
|
+
const totalWeight = values.reduce((sum, item) => sum + item.weight, 0);
|
|
38
|
+
return values.reduce((sum, item) => sum + item.value * item.weight, 0) / totalWeight;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function confidenceFor(spread: number): AgeEstimatorResult['confidence'] {
|
|
42
|
+
if (spread <= 2) return 'strong';
|
|
43
|
+
if (spread <= 4) return 'moderate';
|
|
44
|
+
return 'exploratory';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function notesFor(spread: number): string[] {
|
|
48
|
+
const notes = ['noteTriage', 'noteFinalAssessment'];
|
|
49
|
+
|
|
50
|
+
if (spread > 4) notes.push('noteWideInterval');
|
|
51
|
+
return notes;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function estimateForensicAge(input: AgeEstimatorInput): AgeEstimatorResult {
|
|
55
|
+
const dentalStage = clampStage(input.dentalStage);
|
|
56
|
+
const epiphysealStage = clampStage(input.epiphysealStage);
|
|
57
|
+
const thirdMolarStage = clampStage(input.thirdMolarStage);
|
|
58
|
+
const dentalAge = anchorFor(dentalAnchors, dentalStage);
|
|
59
|
+
const epiphysealAge = anchorFor(epiphysealAnchors, epiphysealStage);
|
|
60
|
+
const thirdMolarAge = anchorFor(thirdMolarAnchors, thirdMolarStage);
|
|
61
|
+
const [dentalWeight, epiphysealWeight, thirdMolarWeight] = evidenceWeights[input.evidenceType];
|
|
62
|
+
const weights = [
|
|
63
|
+
{ value: dentalAge, weight: dentalWeight },
|
|
64
|
+
{ value: epiphysealAge, weight: epiphysealWeight },
|
|
65
|
+
{ value: thirdMolarAge, weight: thirdMolarWeight },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const likelyAge = weightedAverage(weights);
|
|
69
|
+
const spread = Math.max(dentalAge, epiphysealAge, thirdMolarAge) - Math.min(dentalAge, epiphysealAge, thirdMolarAge);
|
|
70
|
+
const uncertainty = input.evidenceType === 'mixed' ? 1.8 + spread * 0.18 : 2.3 + spread * 0.22;
|
|
71
|
+
const maturityScore = Math.round(((dentalStage + epiphysealStage + thirdMolarStage) / 24) * 100);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
minimumAge: Math.max(0, Number((likelyAge - uncertainty).toFixed(1))),
|
|
75
|
+
likelyAge: Number(likelyAge.toFixed(1)),
|
|
76
|
+
maximumAge: Number((likelyAge + uncertainty).toFixed(1)),
|
|
77
|
+
confidence: confidenceFor(spread),
|
|
78
|
+
maturityScore,
|
|
79
|
+
notes: notesFor(spread),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { forensicAgeEstimator } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await forensicAgeEstimator.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Kastle-Meyer test - Wikipedia',
|
|
6
|
+
url: 'https://en.wikipedia.org/wiki/Kastle%E2%80%93Meyer_test'
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'Grodsky, M., Wright, K. & Kirk, P. L. (1951). Simplified preliminary blood testing. An improved technique and comparative study of methods. Journal of Criminal Law and Criminology.',
|
|
10
|
+
url: 'https://scholarlycommons.law.northwestern.edu/jclc/vol42/iss1/10/'
|
|
11
|
+
}
|
|
12
|
+
];
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
import './forensic-presumptive-blood-testing-luminol-kastle-meyer-simulator.css';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
<div class="forensic-chemical-card">
|
|
11
|
+
|
|
12
|
+
<div class="chemical-workbench">
|
|
13
|
+
<div class="workbench-left">
|
|
14
|
+
<div class="specimen-rack-container">
|
|
15
|
+
<h4 class="section-title">{ui.sampleLabel}</h4>
|
|
16
|
+
<div class="specimen-rack">
|
|
17
|
+
<button class="specimen-tube selected" data-sample="blood">
|
|
18
|
+
<span class="tube-glass">
|
|
19
|
+
<span class="tube-liquid tube-liquid-blood"></span>
|
|
20
|
+
</span>
|
|
21
|
+
<span class="tube-label">{ui.sampleBlood}</span>
|
|
22
|
+
</button>
|
|
23
|
+
|
|
24
|
+
<button class="specimen-tube" data-sample="rust">
|
|
25
|
+
<span class="tube-glass">
|
|
26
|
+
<span class="tube-liquid tube-liquid-rust"></span>
|
|
27
|
+
</span>
|
|
28
|
+
<span class="tube-label">{ui.sampleRust}</span>
|
|
29
|
+
</button>
|
|
30
|
+
|
|
31
|
+
<button class="specimen-tube" data-sample="bleach">
|
|
32
|
+
<span class="tube-glass">
|
|
33
|
+
<span class="tube-liquid tube-liquid-bleach"></span>
|
|
34
|
+
</span>
|
|
35
|
+
<span class="tube-label">{ui.sampleBleach}</span>
|
|
36
|
+
</button>
|
|
37
|
+
|
|
38
|
+
<button class="specimen-tube" data-sample="potato">
|
|
39
|
+
<span class="tube-glass">
|
|
40
|
+
<span class="tube-liquid tube-liquid-potato"></span>
|
|
41
|
+
</span>
|
|
42
|
+
<span class="tube-label">{ui.samplePotato}</span>
|
|
43
|
+
</button>
|
|
44
|
+
|
|
45
|
+
<button class="specimen-tube" data-sample="saliva">
|
|
46
|
+
<span class="tube-glass">
|
|
47
|
+
<span class="tube-liquid tube-liquid-saliva"></span>
|
|
48
|
+
</span>
|
|
49
|
+
<span class="tube-label">{ui.sampleSaliva}</span>
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="reagents-shelf-container">
|
|
55
|
+
<h4 class="section-title">{ui.configTitle}</h4>
|
|
56
|
+
<div class="reagents-shelf">
|
|
57
|
+
<div class="reagent-bottle">
|
|
58
|
+
<div class="bottle-visual bottle-km">
|
|
59
|
+
<div class="bottle-neck"></div>
|
|
60
|
+
<div class="bottle-body">
|
|
61
|
+
<div class="bottle-liquid"></div>
|
|
62
|
+
<div class="bottle-label-sticker">KM</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="reagent-details">
|
|
66
|
+
<span class="reagent-name">{ui.reagentKM}</span>
|
|
67
|
+
<span class="reagent-desc">{ui.descKM}</span>
|
|
68
|
+
</div>
|
|
69
|
+
<button class="btn-dispense-fluid" data-reagent="km">
|
|
70
|
+
{ui.dropReagentBtn}
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div class="reagent-bottle">
|
|
75
|
+
<div class="bottle-visual bottle-h2o2">
|
|
76
|
+
<div class="bottle-neck"></div>
|
|
77
|
+
<div class="bottle-body">
|
|
78
|
+
<div class="bottle-liquid"></div>
|
|
79
|
+
<div class="bottle-label-sticker">H₂O₂</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="reagent-details">
|
|
83
|
+
<span class="reagent-name">{ui.reagentH2O2}</span>
|
|
84
|
+
<span class="reagent-desc">{ui.descH2O2}</span>
|
|
85
|
+
</div>
|
|
86
|
+
<button class="btn-dispense-fluid" data-reagent="h2o2">
|
|
87
|
+
{ui.dropReagentBtn}
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div class="reagent-bottle">
|
|
92
|
+
<div class="bottle-visual bottle-luminol">
|
|
93
|
+
<div class="bottle-neck"></div>
|
|
94
|
+
<div class="bottle-body">
|
|
95
|
+
<div class="bottle-liquid"></div>
|
|
96
|
+
<div class="bottle-label-sticker">LUM</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="reagent-details">
|
|
100
|
+
<span class="reagent-name">{ui.reagentLuminol}</span>
|
|
101
|
+
<span class="reagent-desc">{ui.descLuminol}</span>
|
|
102
|
+
</div>
|
|
103
|
+
<button class="btn-dispense-fluid" data-reagent="luminol">
|
|
104
|
+
{ui.dropReagentBtn}
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div class="workbench-right">
|
|
112
|
+
<div class="workbench-viewport" id="workbench-viewport">
|
|
113
|
+
<button class="btn-toggle-lights" id="btn-toggle-lights" type="button" title={ui.lightsLabel}>
|
|
114
|
+
<span class="light-switch-icon"></span>
|
|
115
|
+
</button>
|
|
116
|
+
<div class="petri-dish" id="petri-dish">
|
|
117
|
+
<div class="petri-specimen" id="petri-specimen"></div>
|
|
118
|
+
<div class="specimen-texture-overlay" id="specimen-texture-overlay"></div>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="chemical-drop" id="chemical-drop"></div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="analysis-console">
|
|
124
|
+
<div class="console-header">
|
|
125
|
+
<div class="status-indicator-group">
|
|
126
|
+
<span class="console-label">{ui.statusLabel}</span>
|
|
127
|
+
<div class="status-badge" id="status-badge"></div>
|
|
128
|
+
</div>
|
|
129
|
+
<button class="btn-clear-lab" id="btn-reset-workbench">
|
|
130
|
+
{ui.btnReset}
|
|
131
|
+
</button>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div class="console-body">
|
|
135
|
+
<div class="indicator-row">
|
|
136
|
+
<span class="console-label">{ui.reactionLabel}</span>
|
|
137
|
+
<div class="reaction-explanation-panel" id="explanation-text"></div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<div class="stats-grid">
|
|
141
|
+
<div class="stat-cell">
|
|
142
|
+
<span class="stat-label">{ui.activeCompounds}</span>
|
|
143
|
+
<div class="stat-value" id="val-compounds">-</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="stat-cell">
|
|
146
|
+
<span class="stat-label">{ui.reactionSpeed}</span>
|
|
147
|
+
<div class="stat-value" id="val-speed">-</div>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="stat-cell">
|
|
150
|
+
<span class="stat-label">{ui.reactionDuration}</span>
|
|
151
|
+
<div class="stat-value" id="val-duration">-</div>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="stat-cell">
|
|
154
|
+
<span class="stat-label">{ui.specimenBase}</span>
|
|
155
|
+
<div class="stat-value" id="val-specimen-base">-</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<script id="blood-test-ui-data" type="application/json" set:html={JSON.stringify(ui)}></script>
|
|
165
|
+
|
|
166
|
+
<script>
|
|
167
|
+
import './script';
|
|
168
|
+
</script>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface PetriReactionOptions {
|
|
2
|
+
color: string;
|
|
3
|
+
glow: boolean;
|
|
4
|
+
glowColor: string;
|
|
5
|
+
opacity: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function animateDrop(dropEl: HTMLElement | null, dishEl: HTMLElement | null, onFinish: () => void) {
|
|
9
|
+
if (!dropEl || !dishEl) {
|
|
10
|
+
onFinish();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
dropEl.classList.remove('animating');
|
|
15
|
+
void dropEl.offsetWidth;
|
|
16
|
+
|
|
17
|
+
dropEl.classList.add('animating');
|
|
18
|
+
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
dropEl.classList.remove('animating');
|
|
21
|
+
onFinish();
|
|
22
|
+
}, 600);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function applyPetriReaction(
|
|
26
|
+
dishEl: HTMLElement | null,
|
|
27
|
+
options: PetriReactionOptions
|
|
28
|
+
) {
|
|
29
|
+
if (!dishEl) return;
|
|
30
|
+
dishEl.style.backgroundColor = options.color;
|
|
31
|
+
if (options.glow) {
|
|
32
|
+
dishEl.style.boxShadow = `inset 0 0 40px ${options.glowColor}, 0 0 50px ${options.glowColor}`;
|
|
33
|
+
dishEl.style.borderColor = options.glowColor;
|
|
34
|
+
dishEl.style.opacity = String(options.opacity);
|
|
35
|
+
} else {
|
|
36
|
+
dishEl.style.boxShadow = 'none';
|
|
37
|
+
dishEl.style.borderColor = '';
|
|
38
|
+
dishEl.style.opacity = '1';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ScienceToolEntry, ToolLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export interface BloodTestUI {
|
|
4
|
+
[key: string]: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type BloodTestLocaleContent = ToolLocaleContent<BloodTestUI>;
|
|
8
|
+
|
|
9
|
+
export const forensicBloodTestSimulator: ScienceToolEntry<BloodTestUI> = {
|
|
10
|
+
id: 'forensic-blood-test-simulator',
|
|
11
|
+
icons: {
|
|
12
|
+
bg: 'mdi:flask-outline',
|
|
13
|
+
fg: 'mdi:water-percent'
|
|
14
|
+
},
|
|
15
|
+
i18n: {
|
|
16
|
+
de: () => import('./i18n/de').then((m) => m.content),
|
|
17
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
18
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
19
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
20
|
+
id: () => import('./i18n/id').then((m) => m.content),
|
|
21
|
+
it: () => import('./i18n/it').then((m) => m.content),
|
|
22
|
+
ja: () => import('./i18n/ja').then((m) => m.content),
|
|
23
|
+
ko: () => import('./i18n/ko').then((m) => m.content),
|
|
24
|
+
nl: () => import('./i18n/nl').then((m) => m.content),
|
|
25
|
+
pl: () => import('./i18n/pl').then((m) => m.content),
|
|
26
|
+
pt: () => import('./i18n/pt').then((m) => m.content),
|
|
27
|
+
ru: () => import('./i18n/ru').then((m) => m.content),
|
|
28
|
+
sv: () => import('./i18n/sv').then((m) => m.content),
|
|
29
|
+
tr: () => import('./i18n/tr').then((m) => m.content),
|
|
30
|
+
zh: () => import('./i18n/zh').then((m) => m.content)
|
|
31
|
+
}
|
|
32
|
+
};
|