defensive: type="button" on all 43 <button> elements in static HTML

The HTML had 59 <button> elements; 16 already declared type="button" but the remaining 43 relied on the browser default. The default for <button> without a type attribute (and outside a <form>) is type="submit" — harmless today because index.html has no <form> elements, but the moment a future refactor wraps any section in a form, every unmarked button suddenly becomes a submit button. That's a latent footgun.

Added type="button" to all 43 unmarked buttons via a regex replacement. Now any <button> in the markup is explicitly non-submit — no behaviour change today, but the markup no longer depends on the absence of a form element to behave correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-05-11 08:31:53 +02:00
parent b251d795dc
commit 5ea763d79b

View File

@ -15,12 +15,12 @@
<div id="updateProgressBar" class="update-banner-progress-bar"></div>
</div>
</div>
<button id="updateButton" onclick="downloadUpdate()">Jetzt herunterladen</button>
<button type="button" id="updateButton" onclick="downloadUpdate()">Jetzt herunterladen</button>
</div>
<div class="modal-overlay" id="updateModal" role="dialog" aria-modal="true" aria-labelledby="updateModalTitle" onclick="handleUpdateModalOverlayClick(event)">
<div class="modal update-modal">
<button class="modal-close modal-close-localizable" aria-label="Close" onclick="dismissUpdateModal()">x</button>
<button type="button" class="modal-close modal-close-localizable" aria-label="Close" onclick="dismissUpdateModal()">x</button>
<div class="update-modal-eyebrow" id="updateModalEyebrow">Updates</div>
<h2 id="updateModalTitle">Update verfugbar</h2>
<p class="update-modal-message" id="updateModalMessage">Version 0.0.0 ist verfugbar. Jetzt herunterladen?</p>
@ -48,7 +48,7 @@
<!-- Clip Dialog Modal -->
<div class="modal-overlay" id="clipModal" role="dialog" aria-modal="true" aria-labelledby="clipDialogTitle">
<div class="modal clip-modal">
<button class="modal-close modal-close-localizable" aria-label="Close" onclick="closeClipDialog()">x</button>
<button type="button" class="modal-close modal-close-localizable" aria-label="Close" onclick="closeClipDialog()">x</button>
<h2 class="clip-modal-title" id="clipDialogTitle">VOD zuschneiden</h2>
<div class="clip-modal-field">
@ -103,12 +103,12 @@
<input type="text" id="clipFilenameTemplate" value="{date}_{part}.mp4" placeholder="{date}_{part}.mp4" class="clip-modal-template-input" oninput="updateFilenameExamples()">
<div id="clipTemplateHelp" class="clip-modal-hint">Platzhalter: {title} {id} {channel} {date} {part} {trim_start} {trim_end} {trim_length} {date_custom="yyyy-MM-dd"}</div>
<div id="clipTemplateLint" class="template-lint ok">Template-Check: OK</div>
<button class="btn-secondary" id="clipTemplateGuideBtn" onclick="openTemplateGuide('clip')">Template Guide</button>
<button type="button" class="btn-secondary" id="clipTemplateGuideBtn" onclick="openTemplateGuide('clip')">Template Guide</button>
</div>
</div>
<div class="clip-modal-actions">
<button class="btn-pill success" id="clipDialogConfirmBtn" style="padding: 12px 30px;" onclick="confirmClipDialog()">Zur Queue hinzufugen</button>
<button type="button" class="btn-pill success" id="clipDialogConfirmBtn" style="padding: 12px 30px;" onclick="confirmClipDialog()">Zur Queue hinzufugen</button>
</div>
</div>
</div>
@ -116,7 +116,7 @@
<!-- Events Viewer Modal -->
<div class="modal-overlay" id="eventsViewerModal" role="dialog" aria-modal="true" aria-labelledby="eventsViewerTitle">
<div class="modal viewer-modal viewer-modal-events">
<button class="modal-close modal-close-localizable" aria-label="Close" onclick="closeEventsViewer()">x</button>
<button type="button" class="modal-close modal-close-localizable" aria-label="Close" onclick="closeEventsViewer()">x</button>
<h2 id="eventsViewerTitle" class="viewer-modal-title"></h2>
<div id="eventsViewerStatus" class="viewer-modal-status"></div>
<div id="eventsViewerList" class="viewer-modal-list"></div>
@ -126,7 +126,7 @@
<!-- Chat Replay Viewer Modal -->
<div class="modal-overlay" id="chatViewerModal" role="dialog" aria-modal="true" aria-labelledby="chatViewerTitle">
<div class="modal viewer-modal viewer-modal-chat">
<button class="modal-close modal-close-localizable" aria-label="Close" onclick="closeChatViewer()">x</button>
<button type="button" class="modal-close modal-close-localizable" aria-label="Close" onclick="closeChatViewer()">x</button>
<h2 id="chatViewerTitle" class="viewer-modal-title"></h2>
<div class="viewer-modal-filter-row">
<input type="text" id="chatViewerFilter" class="viewer-modal-filter-input" placeholder="Filter..." oninput="onChatViewerFilterChange()">
@ -139,14 +139,14 @@
<!-- Template Guide Modal -->
<div class="modal-overlay" id="templateGuideModal" role="dialog" aria-modal="true" aria-labelledby="templateGuideTitle">
<div class="modal template-guide-modal">
<button class="modal-close modal-close-localizable" aria-label="Close" onclick="closeTemplateGuide()">x</button>
<button type="button" class="modal-close modal-close-localizable" aria-label="Close" onclick="closeTemplateGuide()">x</button>
<h2 id="templateGuideTitle">Template Guide</h2>
<p id="templateGuideIntro" class="template-guide-intro">Nutze Variablen fur Dateinamen und prufe das Ergebnis als Live-Vorschau.</p>
<div class="template-guide-actions">
<button class="btn-secondary" id="templateGuideUseVod" onclick="setTemplateGuidePreset('vod')">VOD Template</button>
<button class="btn-secondary" id="templateGuideUseParts" onclick="setTemplateGuidePreset('parts')">VOD Part Template</button>
<button class="btn-secondary" id="templateGuideUseClip" onclick="setTemplateGuidePreset('clip')">Clip Template</button>
<button type="button" class="btn-secondary" id="templateGuideUseVod" onclick="setTemplateGuidePreset('vod')">VOD Template</button>
<button type="button" class="btn-secondary" id="templateGuideUseParts" onclick="setTemplateGuidePreset('parts')">VOD Part Template</button>
<button type="button" class="btn-secondary" id="templateGuideUseClip" onclick="setTemplateGuidePreset('clip')">Clip Template</button>
</div>
<label id="templateGuideTemplateLabel" for="templateGuideInput" class="template-guide-label">Template</label>
@ -173,7 +173,7 @@
</div>
<div class="template-guide-footer">
<button class="btn-secondary" id="templateGuideCloseBtn" onclick="closeTemplateGuide()">Schliessen</button>
<button type="button" class="btn-secondary" id="templateGuideCloseBtn" onclick="closeTemplateGuide()">Schliessen</button>
</div>
</div>
</div>
@ -233,10 +233,10 @@
</div>
<div class="queue-list" id="queueList"></div>
<div class="queue-actions">
<button class="btn btn-start" id="btnStart" onclick="toggleDownload()">Start</button>
<button class="btn btn-merge-group" id="btnMergeGroup" onclick="createMergeGroupFromSelection()" style="display:none">Merge &amp; Split</button>
<button class="btn btn-retry" id="btnRetryFailed" onclick="retryFailedDownloads()" title="Nur fehlgeschlagene Downloads erneut starten">Wiederholen</button>
<button class="btn btn-clear" id="btnClear" onclick="clearCompleted()">Leeren</button>
<button type="button" class="btn btn-start" id="btnStart" onclick="toggleDownload()">Start</button>
<button type="button" class="btn btn-merge-group" id="btnMergeGroup" onclick="createMergeGroupFromSelection()" style="display:none">Merge &amp; Split</button>
<button type="button" class="btn btn-retry" id="btnRetryFailed" onclick="retryFailedDownloads()" title="Nur fehlgeschlagene Downloads erneut starten">Wiederholen</button>
<button type="button" class="btn btn-clear" id="btnClear" onclick="clearCompleted()">Leeren</button>
</div>
</div>
<div class="stats-bar" id="statsBar"></div>
@ -250,7 +250,7 @@
<input type="text" id="newStreamer" placeholder="Streamer hinzufugen..." onkeypress="if(event.key==='Enter')addStreamer()">
<button id="btnAddStreamer" type="button" onclick="addStreamer()" aria-label="Add streamer" title="Add streamer">+</button>
</div>
<button class="btn-icon" onclick="refreshVODs()">
<button type="button" class="btn-icon" onclick="refreshVODs()">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
<span id="refreshText">Aktualisieren</span>
</button>
@ -263,7 +263,7 @@
<div id="streamerProfileHeader" class="streamer-profile-header" style="display:none;"></div>
<div class="vod-filter-row">
<input type="text" id="vodFilterInput" class="filter-input" placeholder="Filter VODs..." oninput="onVodFilterInput()">
<button id="vodFilterClearBtn" class="btn-close" onclick="clearVodFilter()" title="Clear filter" style="display:none;">x</button>
<button type="button" id="vodFilterClearBtn" class="btn-close" onclick="clearVodFilter()" title="Clear filter" style="display:none;">x</button>
<label id="vodSortLabel" for="vodSortSelect" class="form-sublabel vod-sort-label">Sort:</label>
<select id="vodSortSelect" class="select-compact" onchange="onVodSortChange()">
<option value="date_desc">Newest first</option>
@ -300,7 +300,7 @@
<div class="clip-input">
<h2 id="clipsHeading">Twitch Clip-Download</h2>
<input type="text" id="clipUrl" placeholder="https://clips.twitch.tv/... oder https://www.twitch.tv/.../clip/...">
<button class="btn-primary" onclick="downloadClip()" id="btnClip">Clip herunterladen</button>
<button type="button" class="btn-primary" onclick="downloadClip()" id="btnClip">Clip herunterladen</button>
<div class="clip-status" id="clipStatus"></div>
</div>
@ -323,7 +323,7 @@
<h3 id="cutterSelectTitle">Video auswahlen</h3>
<div class="form-row">
<input type="text" id="cutterFilePath" readonly placeholder="Keine Datei ausgewahlt...">
<button class="btn-secondary" id="cutterBrowseBtn" onclick="selectCutterVideo()">Durchsuchen</button>
<button type="button" class="btn-secondary" id="cutterBrowseBtn" onclick="selectCutterVideo()">Durchsuchen</button>
</div>
</div>
@ -379,7 +379,7 @@
</div>
<div class="cutter-actions">
<button class="btn-primary" id="btnCut" onclick="startCutting()" disabled>Schneiden</button>
<button type="button" class="btn-primary" id="btnCut" onclick="startCutting()" disabled>Schneiden</button>
</div>
</div>
</div>
@ -393,7 +393,7 @@
Wahle mehrere Videos aus um sie zu einem Video zusammenzufugen.
Die Reihenfolge kann per Drag & Drop geandert werden.
</p>
<button class="btn-secondary" id="mergeAddBtn" onclick="addMergeFiles()">+ Videos hinzufugen</button>
<button type="button" class="btn-secondary" id="mergeAddBtn" onclick="addMergeFiles()">+ Videos hinzufugen</button>
</div>
<div class="file-list" id="mergeFileList">
@ -411,7 +411,7 @@
</div>
<div class="merge-actions">
<button class="btn-primary" id="btnMerge" onclick="startMerging()" disabled>Zusammenfugen</button>
<button type="button" class="btn-primary" id="btnMerge" onclick="startMerging()" disabled>Zusammenfugen</button>
</div>
</div>
</div>
@ -423,7 +423,7 @@
<h3 id="statsTitle">Archiv-Statistik</h3>
<div class="section-header-actions">
<span id="statsLastScannedLabel" class="form-sublabel"></span>
<button class="btn-secondary" id="btnStatsRefresh" onclick="refreshArchiveStats()">Aktualisieren</button>
<button type="button" class="btn-secondary" id="btnStatsRefresh" onclick="refreshArchiveStats()">Aktualisieren</button>
</div>
</div>
<p id="statsIntro" class="card-intro flush">Aggregiert ueber den Download-Ordner. Live-Aufnahmen liegen unter <code>{streamer}/live/</code>, VOD-Downloads direkt unter <code>{streamer}/</code>. Lade-Zeit skaliert mit der Anzahl Dateien.</p>
@ -472,7 +472,7 @@
<option value="size_asc">Kleinste zuerst</option>
<option value="name_asc">Name (A-Z)</option>
</select>
<button class="btn-secondary" id="btnArchiveSearch" onclick="performArchiveSearch()">Suchen</button>
<button type="button" class="btn-secondary" id="btnArchiveSearch" onclick="performArchiveSearch()">Suchen</button>
</div>
<div id="archiveSearchSummary" class="form-sublabel"></div>
</div>
@ -528,7 +528,7 @@
<label id="clientSecretLabel" for="clientSecret">Client Secret</label>
<input type="password" id="clientSecret" placeholder="Twitch Client Secret">
</div>
<button class="btn-primary" id="saveSettingsBtn" onclick="saveSettings()">Speichern & Verbinden</button>
<button type="button" class="btn-primary" id="saveSettingsBtn" onclick="saveSettings()">Speichern & Verbinden</button>
</div>
<div class="settings-card">
@ -537,8 +537,8 @@
<label id="storageLabel" for="downloadPath">Speicherort</label>
<div class="form-row">
<input type="text" id="downloadPath" readonly>
<button class="btn-secondary" onclick="selectFolder()">Ordner</button>
<button class="btn-secondary" id="openFolderBtn" onclick="openFolder()">Offnen</button>
<button type="button" class="btn-secondary" onclick="selectFolder()">Ordner</button>
<button type="button" class="btn-secondary" id="openFolderBtn" onclick="openFolder()">Offnen</button>
</div>
</div>
<div class="form-group">
@ -661,7 +661,7 @@
<div class="settings-card">
<h3 id="updateTitle">Updates</h3>
<p id="versionInfo" class="card-intro">Version: v4.1.13</p>
<button class="btn-secondary" id="checkUpdateBtn" onclick="checkUpdate()">Nach Updates suchen</button>
<button type="button" class="btn-secondary" id="checkUpdateBtn" onclick="checkUpdate()">Nach Updates suchen</button>
</div>
<div class="settings-card">
@ -670,8 +670,8 @@
<span class="health-badge unknown" id="healthBadge">System: Unbekannt</span>
</div>
<div class="form-row" style="margin-bottom: 10px;">
<button class="btn-secondary" id="btnPreflightRun" onclick="runPreflight(false)">Check ausfuhren</button>
<button class="btn-secondary" id="btnPreflightFix" onclick="runPreflight(true)">Auto-Fix Tools</button>
<button type="button" class="btn-secondary" id="btnPreflightRun" onclick="runPreflight(false)">Check ausfuhren</button>
<button type="button" class="btn-secondary" id="btnPreflightFix" onclick="runPreflight(true)">Auto-Fix Tools</button>
</div>
<pre id="preflightResult" class="log-panel">Noch kein Check ausgefuhrt.</pre>
</div>
@ -679,8 +679,8 @@
<div class="settings-card">
<h3 id="debugLogTitle">Live Debug-Log</h3>
<div class="form-row aligned">
<button class="btn-secondary" id="btnRefreshLog" onclick="refreshDebugLog()">Aktualisieren</button>
<button class="btn-secondary" id="btnOpenDebugLogFile" onclick="openDebugLogFile()">Log-Datei oeffnen</button>
<button type="button" class="btn-secondary" id="btnRefreshLog" onclick="refreshDebugLog()">Aktualisieren</button>
<button type="button" class="btn-secondary" id="btnOpenDebugLogFile" onclick="openDebugLogFile()">Log-Datei oeffnen</button>
<label class="inline-toggle">
<input type="checkbox" id="debugAutoRefresh" onchange="toggleDebugAutoRefresh(this.checked)">
<span id="autoRefreshText">Auto-Refresh</span>
@ -692,7 +692,7 @@
<div class="settings-card">
<div class="form-row section-header">
<h3 id="storageCardTitle">Storage</h3>
<button class="btn-secondary" id="btnRefreshStorage" onclick="refreshStorageStats()">Aktualisieren</button>
<button type="button" class="btn-secondary" id="btnRefreshStorage" onclick="refreshStorageStats()">Aktualisieren</button>
</div>
<p id="storageCardIntro" class="card-intro">Disk-Verbrauch pro Streamer im aktuellen Download-Ordner. Live-Aufnahmen werden separat ausgewiesen.</p>
<div id="storageSummary" class="form-sublabel" style="margin-bottom:8px;"></div>
@ -726,8 +726,8 @@
</label>
</div>
<div class="form-row" style="margin-bottom: 8px; gap: 8px;">
<button class="btn-secondary" id="btnCleanupDryRun" onclick="runCleanupDryRun()">Vorschau</button>
<button class="btn-secondary" id="btnCleanupRunNow" onclick="runCleanupNow()">Jetzt ausfuehren</button>
<button type="button" class="btn-secondary" id="btnCleanupDryRun" onclick="runCleanupDryRun()">Vorschau</button>
<button type="button" class="btn-secondary" id="btnCleanupRunNow" onclick="runCleanupNow()">Jetzt ausfuehren</button>
</div>
<div id="cleanupReport" class="form-note"></div>
</div>
@ -769,8 +769,8 @@
<input type="number" id="autoVodMaxAgeHours" min="1" max="720" value="24" class="input-narrow">
</div>
<div class="form-row" style="align-items: center; gap: 12px; flex-wrap: wrap;">
<button class="btn-secondary" id="btnAutoVodScanNow" onclick="triggerManualAutoVodScan()">Jetzt scannen</button>
<button class="btn-secondary" id="btnAutoRecordScanNow" onclick="triggerManualAutoRecordScan()">Live-Status pruefen</button>
<button type="button" class="btn-secondary" id="btnAutoVodScanNow" onclick="triggerManualAutoVodScan()">Jetzt scannen</button>
<button type="button" class="btn-secondary" id="btnAutoRecordScanNow" onclick="triggerManualAutoRecordScan()">Live-Status pruefen</button>
<span id="autoVodStatusLine" class="form-sublabel"></span>
</div>
</div>
@ -779,17 +779,17 @@
<h3 id="backupCardTitle">Sicherung &amp; Wartung</h3>
<p id="backupCardIntro" class="card-intro">Konfiguration sichern, auf einem anderen Geraet wiederherstellen, oder die Liste der bereits heruntergeladenen VODs zuruecksetzen.</p>
<div class="form-row" style="margin-bottom: 10px; flex-wrap: wrap;">
<button class="btn-secondary" id="btnExportConfig" onclick="exportConfigToFile()">Konfiguration exportieren</button>
<button class="btn-secondary" id="btnImportConfig" onclick="importConfigFromFile()">Konfiguration importieren</button>
<button class="btn-secondary" id="btnResetDownloadedIds" onclick="resetDownloadedIds()">Downloaded-VODs zuruecksetzen</button>
<button type="button" class="btn-secondary" id="btnExportConfig" onclick="exportConfigToFile()">Konfiguration exportieren</button>
<button type="button" class="btn-secondary" id="btnImportConfig" onclick="importConfigFromFile()">Konfiguration importieren</button>
<button type="button" class="btn-secondary" id="btnResetDownloadedIds" onclick="resetDownloadedIds()">Downloaded-VODs zuruecksetzen</button>
</div>
</div>
<div class="settings-card">
<h3 id="runtimeMetricsTitle">Runtime Metrics</h3>
<div class="form-row aligned">
<button class="btn-secondary" id="btnRefreshMetrics" onclick="refreshRuntimeMetrics()">Aktualisieren</button>
<button class="btn-secondary" id="btnExportMetrics" onclick="exportRuntimeMetrics()">Export JSON</button>
<button type="button" class="btn-secondary" id="btnRefreshMetrics" onclick="refreshRuntimeMetrics()">Aktualisieren</button>
<button type="button" class="btn-secondary" id="btnExportMetrics" onclick="exportRuntimeMetrics()">Export JSON</button>
<label class="inline-toggle">
<input type="checkbox" id="runtimeMetricsAutoRefresh" onchange="toggleRuntimeMetricsAutoRefresh(this.checked)">
<span id="runtimeMetricsAutoRefreshText">Auto-Refresh</span>