diff options
| author | Fuwn <[email protected]> | 2025-12-02 00:18:58 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-12-02 00:18:58 -0800 |
| commit | 0d8bf5520876d2dc600cc0a45cf59543c56cd99c (patch) | |
| tree | 17e8f398e1da3430270a4b3256877e82c907f60b | |
| parent | fix(analysis.js): Improve analysis engine (diff) | |
| download | rysk-0d8bf5520876d2dc600cc0a45cf59543c56cd99c.tar.xz rysk-0d8bf5520876d2dc600cc0a45cf59543c56cd99c.zip | |
feat(index.html): Restyle
| -rw-r--r-- | index.html | 1077 | ||||
| -rw-r--r-- | js/index.js | 4 |
2 files changed, 745 insertions, 336 deletions
@@ -1,6 +1,5 @@ <!DOCTYPE html> -<!-- updated index.html v2024-05-20 --> -<html> +<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> @@ -10,389 +9,799 @@ <script src="js/face-landmarks-detection.js"></script> <script src="js/bootstrap.bundle.min.js"></script> <link href="css/bootstrap.min.css" rel="stylesheet" /> - <title>Incel Solutions</title> - + <link rel="preconnect" href="https://fonts.googleapis.com" /> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> + <link + href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" + rel="stylesheet" + /> + <title>Facial Analysis</title> <style> - html, - body, - body > div { - height: 100%; + *, + *::before, + *::after { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + :root { + --bg: #0a0a0a; + --surface: #141414; + --surface-elevated: #1a1a1a; + --border: rgba(255, 255, 255, 0.08); + --text-primary: #ffffff; + --text-secondary: rgba(255, 255, 255, 0.7); + --text-tertiary: rgba(255, 255, 255, 0.5); + --accent: #8b5cf6; + --accent-hover: #a78bfa; + --success: #22c55e; + --warning: #eab308; + --error: #ef4444; + --radius: 12px; + --radius-lg: 16px; + --spacing-xs: 0.5rem; + --spacing-sm: 0.75rem; + --spacing-md: 1rem; + --spacing-lg: 1.5rem; + --spacing-xl: 2rem; + --spacing-2xl: 3rem; + } + + html { + font-size: 16px; + -webkit-text-size-adjust: 100%; + } + + body { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, "Helvetica Neue", Arial, sans-serif; + background: var(--bg); + color: var(--text-primary); + line-height: 1.5; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + min-height: 100vh; + overflow-x: hidden; } - .loading { + /* Layout */ + .app { + min-height: 100vh; display: flex; - align-items: center; + flex-direction: column; } - .loading > div { - flex-grow: 1; + + .main-content { + flex: 1; + max-width: 1200px; + width: 100%; + margin: 0 auto; + padding: var(--spacing-xl) var(--spacing-md); } - .loading > .spinner-border { - height: 8em !important; - width: 8em !important; - flex-grow: 0; + + @media (min-width: 768px) { + .main-content { + padding: var(--spacing-2xl) var(--spacing-xl); + } } - img { - height: auto; - width: 25% !important; + + /* Header */ + .header { + text-align: center; + margin-bottom: var(--spacing-2xl); } - .perfect { - color: springgreen; + .header h1 { + font-size: clamp(1.75rem, 4vw, 2.5rem); + font-weight: 700; + letter-spacing: -0.02em; + margin-bottom: var(--spacing-sm); + background: linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } - .deviation-0 { - color: darkkhaki; + + .header p { + font-size: clamp(0.875rem, 2vw, 1rem); + color: var(--text-secondary); + font-weight: 400; } - .deviation-1 { - color: darkorange; + + /* Input Section */ + .input-container { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: var(--spacing-xl); + margin-bottom: var(--spacing-xl); + backdrop-filter: blur(10px); } - .deviation-2 { - color: darksalmon; + + .input-grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--spacing-lg); } - .deviation-3 { - color: coral; + + @media (min-width: 640px) { + .input-grid { + grid-template-columns: 1fr 1fr; + } } - .deviation-4 { - color: orangered; + + .input-field { + display: flex; + flex-direction: column; + } + + .input-field label { + font-size: 0.875rem; + font-weight: 500; + color: var(--text-secondary); + margin-bottom: var(--spacing-xs); + letter-spacing: 0.01em; + } + + .input-field input { + width: 100%; + padding: var(--spacing-md) var(--spacing-lg); + background: var(--surface-elevated); + border: 1px solid var(--border); + border-radius: var(--radius); + color: var(--text-primary); + font-size: 0.9375rem; + font-family: inherit; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + } + + .input-field input:hover { + border-color: rgba(255, 255, 255, 0.12); + } + + .input-field input:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1); + background: var(--surface); } - /* 👉 Paste the following rules here 👇 */ + + .input-field input::placeholder { + color: var(--text-tertiary); + } + + /* Table Container */ + .table-container { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + overflow: hidden; + margin-bottom: var(--spacing-xl); + } + + .table-wrapper { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + table { - table-layout: auto; width: 100%; + border-collapse: collapse; + font-size: 0.875rem; + } + + thead { + background: var(--surface-elevated); + border-bottom: 1px solid var(--border); + } + + th { + padding: var(--spacing-md) var(--spacing-lg); + text-align: left; + font-weight: 600; + font-size: 0.75rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; + } + + th:first-child { + width: 48px; + text-align: center; + padding: var(--spacing-md); + } + + tbody tr { + border-bottom: 1px solid var(--border); + transition: background-color 0.15s ease; + } + + tbody tr:last-child { + border-bottom: none; + } + + tbody tr:hover { + background: var(--surface-elevated); } - th, td { - padding: 0.5rem; + padding: var(--spacing-md) var(--spacing-lg); vertical-align: middle; - white-space: nowrap; + color: var(--text-primary); } - /* Column 1: checkbox (tightest fit) */ - th:first-child, td:first-child { - width: 1%; + text-align: center; + padding: var(--spacing-md); } - /* Column 2: feature (expand just enough for longest label) */ - th:nth-child(2), - td:nth-child(2) { - white-space: nowrap; + tfoot { + background: var(--surface-elevated); + border-top: 2px solid var(--border); } - /* Column 3: rating (auto resize as needed) */ - th:nth-child(3), - td:nth-child(3) { - white-space: nowrap; + tfoot td { + font-weight: 600; + padding: var(--spacing-lg); } - /* Columns 4 & 5: measurement and ideal (fixed narrow width) */ - th:nth-child(4), - td:nth-child(4), - th:nth-child(5), - td:nth-child(5) { - width: 120px; - text-align: center; + /* Checkboxes */ + .checkbox { + width: 18px; + height: 18px; + border-radius: 4px; + border: 2px solid var(--border); + background: var(--surface-elevated); + cursor: pointer; + transition: all 0.2s ease; + appearance: none; + -webkit-appearance: none; + position: relative; + flex-shrink: 0; + } + + .checkbox:checked { + background: var(--accent); + border-color: var(--accent); + } + + .checkbox:checked::after { + content: ""; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) rotate(45deg); + width: 4px; + height: 8px; + border: solid white; + border-width: 0 2px 2px 0; + } + + .checkbox:focus { + outline: none; + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.2); + } + + /* Rating Colors */ + .perfect { + color: var(--success); + font-weight: 500; } + .deviation-0 { + color: var(--warning); + } + + .deviation-1 { + color: #f59e0b; + } + + .deviation-2 { + color: #f97316; + } + + .deviation-3 { + color: #ef4444; + } + + .deviation-4 { + color: #dc2626; + font-weight: 600; + } + + /* Badge */ .badge { - display: inline-block; + display: inline-flex; + align-items: center; + padding: 0.375rem 0.75rem; + border-radius: 6px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + line-height: 1; + } + + .badge.bg-success { + background: var(--success); + color: white; + } + + .badge.bg-warning { + background: var(--warning); + color: #000; + } + + .badge.bg-danger { + background: var(--error); + color: white; + } + + /* Loading */ + .loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 400px; + gap: var(--spacing-lg); + } + + .spinner { + width: 40px; + height: 40px; + border: 3px solid var(--border); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + } + + .loading-text { + color: var(--text-secondary); + font-size: 0.9375rem; + font-weight: 500; + } + + /* Canvas */ + .canvas-container { + display: flex; + justify-content: center; + margin-top: var(--spacing-xl); + padding: 0 var(--spacing-md); + } + + #canvas { max-width: 100%; + height: auto; + border-radius: var(--radius); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + background: var(--surface-elevated); + display: block; + } + + /* Sections */ + #introduction { + text-align: center; + padding: var(--spacing-2xl) var(--spacing-md); + color: var(--text-secondary); + min-height: 200px; + display: flex; + align-items: center; + justify-content: center; + } + + #introduction:empty { + display: none; + } + + #analyzing { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 300px; + padding: var(--spacing-2xl); + gap: var(--spacing-lg); + } + + #analyzing-status { + color: var(--text-secondary); + font-size: 0.9375rem; + font-weight: 500; + } + + #render:empty { + display: none; + } + + /* Responsive */ + @media (max-width: 768px) { + .main-content { + padding: var(--spacing-lg) var(--spacing-md); + } + + .input-container { + padding: var(--spacing-lg); + } + + .table-container { + border-radius: var(--radius); + } + + table { + font-size: 0.8125rem; + } + + th, + td { + padding: var(--spacing-sm) var(--spacing-md); + } + + th { + font-size: 0.6875rem; + } + } + + @media (max-width: 480px) { + th:nth-child(5), + td:nth-child(5) { + display: none; + } + + .header h1 { + font-size: 1.5rem; + } + + .input-container { + padding: var(--spacing-md); + } + + table { + font-size: 0.75rem; + } + + th, + td { + padding: var(--spacing-xs) var(--spacing-sm); + } + } + + /* Scrollbar */ + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + ::-webkit-scrollbar-track { + background: var(--surface); + } + + ::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 4px; + } + + ::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.15); + } + + /* Utilities */ + .d-none { + display: none !important; + } + + .visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); white-space: nowrap; - text-overflow: ellipsis; + border-width: 0; + } + + /* Smooth transitions */ + * { + transition-property: color, background-color, border-color; + transition-duration: 0.15s; + transition-timing-function: ease; } </style> </head> <body> - <div id="loading" class="loading" style="display: none"> - <div></div> - <div class="spinner-border"> - <span class="visually-hidden">Loading...</span> - </div> - <div></div> - </div> - - <div class="container"> - <div class="row"> - <div class="col-md"> - <label for="image-file"> Choose an image to analyze </label> - <input type="file" id="image-file" class="form-control" /> - </div> - <div class="col-md"> - <label for="image-url"> or paste an URL </label> - <input type="text" id="image-url" class="form-control" /> - </div> - </div> - - <div class="row"> - <div class="col"> - <br /> + <div class="app"> + <div id="loading" class="loading-container" style="display: none"> + <div class="spinner"> + <span class="visually-hidden">Loading...</span> </div> </div> - <div class="container"> - <table class="table table-bordered"> - <thead> - <tr> - <th style="width: 50px"> - <input - type="checkbox" - id="select-all" - class="form-check-input" - /> - </th> - <th>Feature</th> - <th>Rating</th> - <th>Measurement</th> - <th>Ideal</th> - </tr> - </thead> - - <tfoot> - <tr> - <td> - <div class="form-check form-switch"> - <input - class="form-check-input" - type="checkbox" - id="grading-toggle" - checked="" - /> - </div> - </td> - <td><strong>Total</strong></td> - <td id="total-score"></td> - <td id="total-breakdown"></td> - <td id="total-psl"></td> - </tr> - </tfoot> - - <tbody> - <tr> - <td> - <input - type="checkbox" - id="toggle-midface-ratio" - class="form-check-input" - checked="" - /> - </td> - <td>Midface ratio</td> - <td id="assessment-midface-ratio"></td> - <td id="value-midface-ratio"></td> - <td id="ideal-midface-ratio"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-facial-width-to-height-ratio" - class="form-check-input" - checked="" - /> - </td> - <td>Facial width to height ratio</td> - <td id="assessment-facial-width-to-height-ratio"></td> - <td id="value-facial-width-to-height-ratio"></td> - <td id="ideal-facial-width-to-height-ratio"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-chin-to-philtrum-ratio" - class="form-check-input" - checked="" - /> - </td> - <td>Chin to philtrum ratio</td> - <td id="assessment-chin-to-philtrum-ratio"></td> - <td id="value-chin-to-philtrum-ratio"></td> - <td id="ideal-chin-to-philtrum-ratio"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-canthal-tilt" - class="form-check-input" - checked="" - /> - </td> - <td>Canthal tilt</td> - <td id="assessment-canthal-tilt"></td> - <td id="value-canthal-tilt"></td> - <td id="ideal-canthal-tilt"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-mouth-to-nose-ratio" - class="form-check-input" - checked="" - /> - </td> - <td>Mouth to nose ratio</td> - <td id="assessment-mouth-to-nose-ratio"></td> - <td id="value-mouth-to-nose-ratio"></td> - <td id="ideal-mouth-to-nose-ratio"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-bigonial-width" - class="form-check-input" - checked="" - /> - </td> - <td>Bigonial width</td> - <td id="assessment-bigonial-width"></td> - <td id="value-bigonial-width"></td> - <td id="ideal-bigonial-width"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-lip-ratio" - class="form-check-input" - checked="" - /> - </td> - <td>Lip ratio</td> - <td id="assessment-lip-ratio"></td> - <td id="value-lip-ratio"></td> - <td id="ideal-lip-ratio"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-eye-separation-ratio" - class="form-check-input" - checked="" - /> - </td> - <td>Eye separation ratio</td> - <td id="assessment-eye-separation-ratio"></td> - <td id="value-eye-separation-ratio"></td> - <td id="ideal-eye-separation-ratio"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-eye-to-mouth-angle" - class="form-check-input" - checked="" - /> - </td> - <td>Eye to mouth angle</td> - <td id="assessment-eye-to-mouth-angle"></td> - <td id="value-eye-to-mouth-angle"></td> - <td id="ideal-eye-to-mouth-angle"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-lower-third-height" - class="form-check-input" - checked="" - /> - </td> - <td>Lower third height</td> - <td id="assessment-lower-third-height"></td> - <td id="value-lower-third-height"></td> - <td id="ideal-lower-third-height"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-palpebral-fissure-length" - class="form-check-input" - checked="" - /> - </td> - <td>Palpebral fissure length</td> - <td id="assessment-palpebral-fissure-length"></td> - <td id="value-palpebral-fissure-length"></td> - <td id="ideal-palpebral-fissure-length"></td> - </tr> - <tr> - <td> - <input - type="checkbox" - id="toggle-eye-color" - class="form-check-input" - checked="" - /> - </td> - <td>Eye color</td> - <td id="assessment-eye-color"></td> - <td id="value-eye-color"> - <canvas height="0" width="0"></canvas> - <canvas height="0" width="0"></canvas> - </td> - <td id="ideal-eye-color"></td> - </tr> - </tbody> - </table> - </div> - - <!-- JavaScript for Select All --> - <script> - // Select All Checkbox - const selectAllCheckbox = document.getElementById("select-all"); - - // Get all checkboxes in the table body - const checkboxes = document.querySelectorAll( - 'tbody input[type="checkbox"]' - ); - - // Event Listener for Select All - selectAllCheckbox.addEventListener("change", function () { - checkboxes.forEach((checkbox) => { - checkbox.checked = selectAllCheckbox.checked; // Toggle all checkboxes - }); - }); + <main class="main-content"> + <header class="header"> + <h1>Facial Analysis</h1> + <p>Upload an image or paste a URL to analyse facial features</p> + </header> - // Update "Select All" if any checkbox is unchecked - checkboxes.forEach((checkbox) => { - checkbox.addEventListener("change", function () { - selectAllCheckbox.checked = [...checkboxes].every( - (cb) => cb.checked - ); - }); - }); - </script> + <section class="input-container"> + <div class="input-grid"> + <div class="input-field"> + <label for="image-file">Upload Image</label> + <input + type="file" + id="image-file" + accept="image/*" + class="form-control" + /> + </div> + <div class="input-field"> + <label for="image-url">Or Paste URL</label> + <input + type="text" + id="image-url" + placeholder="https://example.com/image.jpg" + class="form-control" + /> + </div> + </div> + </section> - <div id="introduction" class="row mt-3"></div> + <section class="table-container"> + <div class="table-wrapper"> + <table> + <thead> + <tr> + <th> + <input + type="checkbox" + id="select-all" + class="checkbox" + title="Select All" + /> + </th> + <th>Feature</th> + <th>Rating</th> + <th>Measurement</th> + <th>Ideal</th> + </tr> + </thead> - <div class="row d-none mt-5" id="analyzing"> - <div class="col text-center"> - <div> - <div class="loading"> - <div></div> + <tfoot> + <tr> + <td> + <input + type="checkbox" + id="grading-toggle" + class="checkbox" + checked + /> + </td> + <td><strong>Total</strong></td> + <td id="total-score"></td> + <td id="total-breakdown"></td> + <td id="total-psl"></td> + </tr> + </tfoot> - <div class="spinner-border"></div> + <tbody> + <tr> + <td> + <input + type="checkbox" + id="toggle-midface-ratio" + class="checkbox" + checked + /> + </td> + <td>Midface ratio</td> + <td id="assessment-midface-ratio"></td> + <td id="value-midface-ratio"></td> + <td id="ideal-midface-ratio"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-facial-width-to-height-ratio" + class="checkbox" + checked + /> + </td> + <td>Facial width to height ratio</td> + <td id="assessment-facial-width-to-height-ratio"></td> + <td id="value-facial-width-to-height-ratio"></td> + <td id="ideal-facial-width-to-height-ratio"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-chin-to-philtrum-ratio" + class="checkbox" + checked + /> + </td> + <td>Chin to philtrum ratio</td> + <td id="assessment-chin-to-philtrum-ratio"></td> + <td id="value-chin-to-philtrum-ratio"></td> + <td id="ideal-chin-to-philtrum-ratio"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-canthal-tilt" + class="checkbox" + checked + /> + </td> + <td>Canthal tilt</td> + <td id="assessment-canthal-tilt"></td> + <td id="value-canthal-tilt"></td> + <td id="ideal-canthal-tilt"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-mouth-to-nose-ratio" + class="checkbox" + checked + /> + </td> + <td>Mouth to nose ratio</td> + <td id="assessment-mouth-to-nose-ratio"></td> + <td id="value-mouth-to-nose-ratio"></td> + <td id="ideal-mouth-to-nose-ratio"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-bigonial-width" + class="checkbox" + checked + /> + </td> + <td>Bigonial width</td> + <td id="assessment-bigonial-width"></td> + <td id="value-bigonial-width"></td> + <td id="ideal-bigonial-width"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-lip-ratio" + class="checkbox" + checked + /> + </td> + <td>Lip ratio</td> + <td id="assessment-lip-ratio"></td> + <td id="value-lip-ratio"></td> + <td id="ideal-lip-ratio"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-eye-separation-ratio" + class="checkbox" + checked + /> + </td> + <td>Eye separation ratio</td> + <td id="assessment-eye-separation-ratio"></td> + <td id="value-eye-separation-ratio"></td> + <td id="ideal-eye-separation-ratio"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-eye-to-mouth-angle" + class="checkbox" + checked + /> + </td> + <td>Eye to mouth angle</td> + <td id="assessment-eye-to-mouth-angle"></td> + <td id="value-eye-to-mouth-angle"></td> + <td id="ideal-eye-to-mouth-angle"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-lower-third-height" + class="checkbox" + checked + /> + </td> + <td>Lower third height</td> + <td id="assessment-lower-third-height"></td> + <td id="value-lower-third-height"></td> + <td id="ideal-lower-third-height"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-palpebral-fissure-length" + class="checkbox" + checked + /> + </td> + <td>Palpebral fissure length</td> + <td id="assessment-palpebral-fissure-length"></td> + <td id="value-palpebral-fissure-length"></td> + <td id="ideal-palpebral-fissure-length"></td> + </tr> + <tr> + <td> + <input + type="checkbox" + id="toggle-eye-color" + class="checkbox" + checked + /> + </td> + <td>Eye colour</td> + <td id="assessment-eye-color"></td> + <td id="value-eye-color"> + <canvas height="0" width="0"></canvas> + <canvas height="0" width="0"></canvas> + </td> + <td id="ideal-eye-color"></td> + </tr> + </tbody> + </table> + </div> + </section> - <div></div> - </div> + <div id="introduction"></div> - <div class="mt-3 fs-5"> - <div id="analyzing-status"></div> - </div> - </div> + <div class="d-none" id="analyzing"> + <div class="spinner"></div> + <div id="analyzing-status" class="loading-text"></div> </div> - </div> - <div class="row" id="render"> - <div class="col"><canvas id="canvas"></canvas></div> - </div> + <div class="canvas-container" id="render"> + <canvas id="canvas"></canvas> + </div> + </main> </div> + + <script> + // Select All Checkbox + const selectAllCheckbox = document.getElementById("select-all"); + const checkboxes = document.querySelectorAll( + 'tbody input[type="checkbox"]' + ); + + selectAllCheckbox.addEventListener("change", function () { + checkboxes.forEach((checkbox) => { + checkbox.checked = selectAllCheckbox.checked; + }); + }); + + checkboxes.forEach((checkbox) => { + checkbox.addEventListener("change", function () { + selectAllCheckbox.checked = [...checkboxes].every((cb) => cb.checked); + }); + }); + </script> + <script src="js/analysis.js"></script> <script src="js/index.js"></script> <script> diff --git a/js/index.js b/js/index.js index 3881d38..f1d4210 100644 --- a/js/index.js +++ b/js/index.js @@ -205,7 +205,7 @@ if (gradingToggle) { }); async function onChange(url) { - setStatus("Analyzing"); + setStatus("Analysing"); let analysis = await analyze(canvas, ctx, url); @@ -368,7 +368,7 @@ async function analyze(canvas, ctx, url) { ctx.lineWidth = Math.sqrt((image.width * image.height) / 100000); ctx.arcRadius = Math.sqrt((image.width * image.height) / 100000); - setStatus("Analyzing"); + setStatus("Analysing"); const model = await faceLandmarksDetection.load(faceLandmarksDetection.SupportedPackages.mediapipeFacemesh, { maxFaces: 1 |