代码: 全选
Here are the specific modifications required to merge the features from `domu1.html` into `domchar5741.html` according to your requirements. All code and comments are provided in English.
### 1. HTML Modifications
**Find this block:**
```html
<div id="output"></div>
<div id="download"></div>
<br />
```
**Replace with:**
```html
<div id="dashboardOverview" class="dashboard-grid" style="display: none;">
<div class="stat-card">
<div class="stat-title">Single Char Count</div>
<div class="stat-value" id="dashCharDef">0</div>
</div>
<div class="stat-card">
<div class="stat-title">Yinmo Dedup</div>
<div class="stat-value" id="dashYinmo">0</div>
</div>
<div class="stat-card">
<div class="stat-title">Chinese Punc</div>
<div class="stat-value" id="dashChiPunc">0</div>
</div>
<div class="stat-card">
<div class="stat-title">English Sym</div>
<div class="stat-value" id="dashEngSym">0</div>
</div>
<div class="stat-card">
<div class="stat-title">Phrase Count</div>
<div class="stat-value" id="dashPhraseCount">0</div>
</div>
<div class="stat-card">
<div class="stat-title">Phrase Dedup</div>
<div class="stat-value" id="dashPhraseDedup">0</div>
</div>
</div>
<div id="downloadContainer" style="display: none; margin-top: 15px;"></div>
```
**Find this block:**
```html
<div id="inputArea">
<label for="codeInput" style="font-weight: bold">Encode Input: </label>
<input type="text" id="codeInput" placeholder="Enter code (e.g. zhy) or word" />
<button id="submitCode" onclick="handleEditSubmit()">Submit</button>
</div>
```
**Replace with:**
```html
<div id="inputArea">
<label for="codeInput" style="font-weight: bold">Input: </label>
<select id="modeSelect" class="mode-dropdown">
<option value="auto">Auto</option>
<option value="char">Char</option>
<option value="phrase">Phrase</option>
</select>
<input type="text" id="codeInput" placeholder="Enter code or word" />
<button id="submitCode" onclick="handleEditSubmit()">Submit</button>
</div>
```
**Find this block:**
```html
<div class="stats-buttons" id="statsBtnContainer">
<button class="stats-btn" id="btnMcc1All" onclick="renderStatsSection('mcc1all', '二碼高頻', this)">
```
**Replace with:**
```html
<div class="stats-buttons" id="statsBtnContainer">
<button class="stats-btn active" id="btnDupStats" onclick="showDupStatsSection(this)">
重碼統計
</button>
<button class="stats-btn" id="btnMcc1All" onclick="renderStatsSection('mcc1all', '二碼高頻', this)">
```
### 2. CSS Modifications
**Add the following CSS rules inside your `<style>` block:**
```css
/* Dashboard Overview Styling */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin: 20px 0;
}
.stat-card {
background: linear-gradient(135deg, #ffffff 0%, #f5f7fa 100%);
border: 1px solid #e4e7eb;
border-radius: 10px;
padding: 15px;
text-align: center;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0,0,0,0.1);
}
.stat-title {
font-size: 13px;
color: #6b7280;
margin-bottom: 8px;
font-weight: bold;
}
.stat-value {
font-size: 24px;
color: #1f2937;
font-weight: 900;
}
/* Beautiful Download Button */
.modern-download-btn {
display: inline-block;
width: 100%;
padding: 12px 24px;
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
color: white !important;
text-decoration: none;
text-align: center;
font-weight: bold;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3);
transition: all 0.3s ease;
}
.modern-download-btn:hover {
background: linear-gradient(135deg, #4338ca 0%, #6d28d9 100%);
box-shadow: 0 6px 20px rgba(124, 58, 237, 0.4);
transform: translateY(-1px);
}
/* Dropdown styling */
.mode-dropdown {
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
font-size: 14px;
margin-right: 6px;
outline: none;
}
.mode-dropdown:disabled {
background-color: #f3f4f6;
cursor: not-allowed;
}
/* Char mode specific elements */
.empty-slot {
display: inline-flex;
justify-content: center;
align-items: center;
width: 30px;
height: 30px;
margin: 4px;
border: 2px dashed #9ca3af;
border-radius: 4px;
color: #9ca3af;
cursor: pointer;
font-weight: bold;
background: #f9fafb;
transition: all 0.2s;
vertical-align: middle;
}
.empty-slot:hover {
background: #e5e7eb;
border-color: #6b7280;
color: #4b5563;
}
/* Beautified Dup Stats Area */
.dup-stats-container {
display: flex;
flex-direction: column;
gap: 20px;
}
.dup-box {
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
.dup-box h3 {
margin-top: 0;
margin-bottom: 10px;
font-size: 16px;
color: #374151;
border-bottom: 2px solid #f3f4f6;
padding-bottom: 8px;
}
.dup-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.dup-tag {
background: #f3f4f6;
padding: 4px 10px;
border-radius: 12px;
font-size: 13px;
color: #4b5563;
}
.dup-tag strong {
color: #1f2937;
}
.modern-textarea {
width: 100%;
height: 120px;
border: 1px solid #d1d5db;
border-radius: 6px;
padding: 10px;
font-family: monospace;
resize: vertical;
box-sizing: border-box;
}
.highlighted-title {
color: #d32f2f !important;
background: #ffebee;
padding: 2px 6px;
border-radius: 4px;
}
```
### 3. JavaScript Modifications
**Inject these Global Variables near the top of the `<script>` tag:**
```javascript
// --- New Globals for Char Mode & Dup Stats ---
let globalCodeMap = new Map();
let rawCharDefLines = [];
let activeMode = 'phrase'; // 'char' or 'phrase'
let generatedDupStatsHTML = '';
// ---------------------------------------------
```
**Find the `resetPage` function. Add these state resets:**
```javascript
// Add inside resetPage() before mcc0update.style.display = 'none';
globalCodeMap.clear();
rawCharDefLines = [];
generatedDupStatsHTML = '';
document.getElementById('dashboardOverview').style.display = 'none';
document.getElementById('downloadContainer').style.display = 'none';
document.getElementById('modeSelect').disabled = false;
document.getElementById('modeSelect').value = 'auto';
```
**Find `handleFile(event)`. Replace the reader.onload block:**
```javascript
reader.onload = async function (e) {
filecontent = e.target.result;
await runAnalyzeFileAndDashboard(); // Execute analyze logic first
processContent(); // Then pass to phrase processing
};
```
**Insert this new function `runAnalyzeFileAndDashboard` anywhere in the script:**
```javascript
async function runAnalyzeFileAndDashboard() {
const lines = filecontent.split('\n').map(l => l.replace(/\r$/, ''));
let inChardef = false;
let charDefStartIndex = -1;
let charDefEndIndex = lines.length;
let zStartCount = 0;
let yinmoCount = 0;
let chiPuncCount = 0;
let engSymCount = 0;
globalCodeMap.clear();
rawCharDefLines = [];
// Parse metrics and build code map
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line === '%chardef begin') {
inChardef = true;
charDefStartIndex = i;
continue;
}
if (!inChardef) continue;
if (line === '%chardef end') {
charDefEndIndex = i;
break;
}
rawCharDefLines.push(line);
const parts = line.split('\t');
const code = parts[0];
const char = parts.length > 1 ? parts[1].trim() : '';
if (code) {
// Collect code map for Char mode & Stats
if (char && /^[a-z]{1,4}$/.test(code)) {
if (!globalCodeMap.has(code)) globalCodeMap.set(code, []);
globalCodeMap.get(code).push(char);
}
// Dashboard conditions
if (code.startsWith('z')) zStartCount++;
if (!code.startsWith('z') && !code.startsWith('x') && code.includes('z')) yinmoCount++;
if (code.startsWith('x') && code[1] !== 'z' && code.length >= 2) chiPuncCount++;
if (code.startsWith('xz')) engSymCount++;
}
}
// Calculate duplicate stats logic (from domu1.html)
const sortedCodeMap = new Map([...globalCodeMap.entries()].sort());
const dupStats = new Map();
for (const chars of sortedCodeMap.values()) {
const dupCount = chars.length - 1; // 1 char = 0 dup, 2 chars = 1 dup...
if (dupCount >= 0) dupStats.set(dupCount, (dupStats.get(dupCount) || 0) + 1);
}
const lenStats = [new Map(), new Map(), new Map(), new Map()];
for (const [code, chars] of sortedCodeMap) {
const lenIdx = Math.min(code.length - 1, 3);
const dupCount = chars.length - 1;
if (dupCount >= 0) {
const map = lenStats[lenIdx];
map.set(dupCount, (map.get(dupCount) || 0) + 1);
}
}
// Build HTML for Stats Tab
let statsHtml = `<div class="dup-stats-container">`;
// Total Dup Stats
statsHtml += `<div class="dup-box"><h3>總重碼統計 (Total Dup Stats)</h3><div class="dup-row">`;
const maxDup = Math.max(...dupStats.keys(), 0);
for (let i = 0; i <= maxDup; i++) {
statsHtml += `<span class="dup-tag"><strong>${i}重:</strong> ${dupStats.get(i) || 0}</span>`;
}
statsHtml += `</div></div>`;
// Category Dup Stats
statsHtml += `<div class="dup-box"><h3>分類重碼統計 (Category Dup Stats)</h3>`;
lenStats.forEach((map, i) => {
const maxDupForLen = Math.max(...map.keys(), 0);
statsHtml += `<div style="margin-bottom:8px; font-size:14px; font-weight:bold;">碼長 ${i + 1}:</div><div class="dup-row" style="margin-bottom:15px;">`;
for (let j = 0; j <= maxDupForLen; j++) {
statsHtml += `<span class="dup-tag"><strong>${j}重:</strong> ${map.get(j) || 0}</span>`;
}
statsHtml += `</div>`;
});
statsHtml += `</div>`;
// Dup & Dedup Results TextAreas
const collapseLines = [];
const gzhahaLines = [];
const outOfBoundsLines = [];
for (const [code, chars] of sortedCodeMap) {
if (chars.length > 1) collapseLines.push(`${code}\t${chars.join('')}`);
if (chars.length <= 1) continue;
const len = code.length;
chars.forEach((char, idx) => {
if (idx === 0) return;
if (len === 1 || len === 2) {
const suffix = ['z', 'zz', 'zx', 'xz'][idx - 1] || '';
if (suffix) gzhahaLines.push(`${code}${suffix}\t${char}`);
else outOfBoundsLines.push(`${code}\t${char}`);
} else if (len === 3) {
if (idx === 1) gzhahaLines.push(`${code}z\t${char}`);
else if (idx === 2) gzhahaLines.push(`${code.slice(0, 2)}z${code.slice(2)}\t${char}`);
else if (idx === 3) gzhahaLines.push(`${code.slice(0, 1)}z${code.slice(1)}\t${char}`);
else outOfBoundsLines.push(`${code}\t${char}`);
} else if (len === 4) {
outOfBoundsLines.push(`${code}\t${char}`);
}
});
}
statsHtml += `<div class="dup-box"><h3>重碼結果 (${collapseLines.length})</h3><textarea class="modern-textarea" readonly>${collapseLines.join('\n')}</textarea></div>`;
statsHtml += `<div class="dup-box"><h3>去重結果 (${gzhahaLines.length})</h3><textarea class="modern-textarea" readonly>${gzhahaLines.join('\n')}</textarea></div>`;
statsHtml += `<div class="dup-box"><h3>超出字合集 (${outOfBoundsLines.length})</h3><textarea class="modern-textarea" readonly>${outOfBoundsLines.join('\n')}</textarea></div>`;
statsHtml += `</div>`;
generatedDupStatsHTML = statsHtml;
// Pass calculated metrics to global scope for processContent to use
window._parsedMetrics = { charDefLinesLen: rawCharDefLines.length, yinmoCount, chiPuncCount, engSymCount, zStartCount };
}
function showDupStatsSection(btnElement) {
document.querySelectorAll('#statsBtnContainer .stats-btn').forEach(btn => btn.classList.remove('active'));
if (btnElement) btnElement.classList.add('active');
document.getElementById('statsDisplayContainer').innerHTML = generatedDupStatsHTML;
}
```
**Find `processContent` and modify the dashboard updates:**
Replace the standard `document.getElementById('output').innerHTML = ...` injections with:
```javascript
// Inside processContent, replace the first document.getElementById('output') with:
document.getElementById('dashboardOverview').style.display = 'grid';
if (window._parsedMetrics) {
document.getElementById('dashCharDef').textContent = window._parsedMetrics.charDefLinesLen;
document.getElementById('dashYinmo').textContent = window._parsedMetrics.yinmoCount;
document.getElementById('dashChiPunc').textContent = window._parsedMetrics.chiPuncCount;
document.getElementById('dashEngSym').textContent = window._parsedMetrics.engSymCount;
}
// ... lower down, replace the second document.getElementById('output') line with:
document.getElementById('dashPhraseCount').textContent = mcc0Lines.length;
if (window._parsedMetrics) {
document.getElementById('dashPhraseDedup').textContent = window._parsedMetrics.zStartCount - mcc0Lines.length;
}
```
**Find the download link generation in `processContent`:**
```javascript
// Replace the download injection:
document.getElementById('download').innerHTML =
`<a href="${url}" download="haha_${timestamp}.txt">下載文件 (${size} bytes)</a>`;
```
**With:**
```javascript
document.getElementById('downloadContainer').style.display = 'block';
document.getElementById('downloadContainer').innerHTML =
`<a href="${url}" download="haha_${timestamp}.txt" class="modern-download-btn">下載碼表 Download File (${size} bytes)</a>`;
```
**Find `handleEditSubmit()` and replace it with this mode-aware version:**
```javascript
function handleEditSubmit() {
let val = document.getElementById('codeInput').value.trim();
if (!val) return;
let selectedMode = document.getElementById('modeSelect').value;
const isChinese = /[\u4e00-\u9fa5]/.test(val);
// Auto logic assignment
if (selectedMode === 'auto') {
if ((!isChinese && !val.toLowerCase().startsWith('z')) || (isChinese && val.length === 1)) {
selectedMode = 'char';
} else {
selectedMode = 'phrase';
}
document.getElementById('modeSelect').value = selectedMode;
}
// Lock the mode
document.getElementById('modeSelect').disabled = true;
activeMode = selectedMode;
document.getElementById('codeInput').value = '';
selectedEditItem = null;
justMovedItem = null;
pendingAction = null;
if (activeMode === 'char') {
// Initialize char mode state tracking
if (undoStack.length === 0) {
undoStack.push({ state: JSON.stringify(rawCharDefLines), desc: 'Baseline Char State', mode: 'char' });
}
currentEditCode = val.toLowerCase();
// Extract base code if a character was entered
if (isChinese) {
let foundCode = null;
for (const [code, chars] of globalCodeMap) {
if (chars.includes(val)) { foundCode = code; break; }
}
if (foundCode) {
// Extract prefix for displaying the sequence
currentEditCode = foundCode[0];
}
}
document.getElementById('inputArea').style.display = 'block';
document.getElementById('editArea').style.display = 'block';
document.getElementById('btnMoveEnd').style.display = 'none'; // Hide in char mode
document.getElementById('btnDelete').style.display = 'none'; // Hide in char mode
renderCharEditArea(currentEditCode, val);
return;
}
// --- Original Phrase Mode Logic Follows ---
document.getElementById('btnMoveEnd').style.display = 'inline-block';
document.getElementById('btnDelete').style.display = 'inline-block';
// ... (Keep existing code from here down to renderEditArea() for phrase mode logic)
if (/^[a-zA-Z]+$/.test(val)) {
if (val[0].toLowerCase() !== 'z') val = 'z' + val;
val = val.substring(0, 4).toLowerCase();
currentEditCode = val;
currentTargetWord = '';
} else {
currentTargetWord = val;
let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
if (!lines.includes(val)) {
if (!originalSnapshotState || originalSnapshotState.size === 0) {
originalSnapshotState = getSystemState();
}
saveStateForUndo(`Added new word: ${val}`);
lines.push(val);
processContent(JSON.stringify(lines));
}
let code = 'z';
let c1 = ccharfirst.get(val[0]) || '';
if (val.length === 1) {
code += c1;
} else if (val.length === 2) {
let c2 = ccharfirst.get(val[1]) || '';
let c3 = ccharsecond.get(val[1]) || 'x';
code += c1 + c2 + c3;
} else {
let c2 = ccharfirst.get(val[1]) || '';
let c3 = ccharfirst.get(val[2]) || '';
code += c1 + c2 + c3;
}
currentEditCode = code.substring(0, 4);
}
if (currentEditCode.length >= 1) activeEditKeys.add('z');
if (currentEditCode.length >= 2) activeEditKeys.add(currentEditCode.substring(0, 2));
if (currentEditCode.length >= 3) activeEditKeys.add(currentEditCode.substring(0, 3));
if (currentEditCode.length >= 4) activeEditKeys.add(currentEditCode);
if (!originalSnapshotState || originalSnapshotState.size === 0) {
originalSnapshotState = getSystemState();
}
document.getElementById('inputArea').style.display = 'block';
document.getElementById('editArea').style.display = 'block';
renderEditArea();
}
```
**Insert this rendering logic for Char Mode anywhere:**
```javascript
function renderCharEditArea(baseCode, targetInput) {
let activeContainer = document.getElementById('editRows');
let originalContainer = document.getElementById('originalRows');
activeContainer.innerHTML = '';
originalContainer.innerHTML = '';
// Derive sequences (j, jj, jjj, jjjj) from the first character of the code
const firstLetter = baseCode[0];
const targetCodes = [firstLetter, firstLetter.repeat(2), firstLetter.repeat(3), firstLetter.repeat(4)];
targetCodes.forEach(codeKey => {
let rowDiv = document.createElement('div');
rowDiv.className = 'edit-row';
let title = document.createElement('div');
title.textContent = codeKey;
title.style.fontWeight = 'bold';
title.style.marginBottom = '8px';
if (codeKey === targetInput) title.className = 'highlighted-title'; // Highlight if exact code match
rowDiv.appendChild(title);
const charsForCode = globalCodeMap.get(codeKey) || [];
if (charsForCode.length === 0) {
// Show empty drop zone
let emptyBtn = document.createElement('span');
emptyBtn.className = 'empty-slot';
emptyBtn.textContent = '+';
emptyBtn.onclick = () => handleCharClick('+', codeKey);
rowDiv.appendChild(emptyBtn);
} else {
charsForCode.forEach((char, idx) => {
let span = document.createElement('span');
span.textContent = char;
span.className = `edit-item`;
if (char === selectedEditItem) span.classList.add('selected');
if (char === justMovedItem) span.classList.add('just-moved');
// Target character highlighting
if (char === targetInput) {
span.classList.add('target-word');
title.className = 'highlighted-title'; // Highlight title if row contains target char
}
span.onclick = () => handleCharClick(char, codeKey);
rowDiv.appendChild(span);
});
// Always append an empty slot at the end of populated rows for trailing insertion
let emptyBtn = document.createElement('span');
emptyBtn.className = 'empty-slot';
emptyBtn.textContent = '+';
emptyBtn.onclick = () => handleCharClick('+', codeKey);
rowDiv.appendChild(emptyBtn);
}
activeContainer.appendChild(rowDiv);
});
}
function handleCharClick(clickedChar, targetCode) {
if (!selectedEditItem) {
if (clickedChar === '+') return; // Cannot select empty slot
selectedEditItem = clickedChar;
justMovedItem = null;
renderCharEditArea(currentEditCode[0], ''); // Rerender to show selection
return;
}
// Swap logic
if (selectedEditItem === clickedChar) {
selectedEditItem = null;
renderCharEditArea(currentEditCode[0], '');
return;
}
// Save State
undoStack.push({ state: JSON.stringify(rawCharDefLines), desc: `${selectedEditItem} -> ${targetCode}`, mode: 'char' });
redoStack = [];
renderActionLogs();
// Perform swap/move in raw lines array
let sourceLineIdx = rawCharDefLines.findIndex(l => l.endsWith('\t' + selectedEditItem));
if (sourceLineIdx !== -1) {
rawCharDefLines.splice(sourceLineIdx, 1); // remove from old position
}
let newRowString = `${targetCode}\t${selectedEditItem}`;
if (clickedChar === '+') {
// Insert at the end of the block for that code, or just append
let lastIdx = -1;
for(let i = rawCharDefLines.length - 1; i >= 0; i--) {
if (rawCharDefLines[i].startsWith(targetCode + '\t')) { lastIdx = i; break; }
}
if (lastIdx !== -1) rawCharDefLines.splice(lastIdx + 1, 0, newRowString);
else rawCharDefLines.push(newRowString);
} else {
// Insert relative to clicked item
let targetLineIdx = rawCharDefLines.findIndex(l => l === `${targetCode}\t${clickedChar}`);
if (targetLineIdx !== -1) {
if (sourceLineIdx > targetLineIdx) {
rawCharDefLines.splice(targetLineIdx, 0, newRowString); // Insert before
} else {
rawCharDefLines.splice(targetLineIdx + 1, 0, newRowString); // Insert after
}
} else {
rawCharDefLines.push(newRowString);
}
}
justMovedItem = selectedEditItem;
selectedEditItem = null;
// Rebuild filecontent structure and re-process to cascade updates
rebuildAndProcessFromCharLines();
}
function rebuildAndProcessFromCharLines() {
const lines = filecontent.split('\n').map(l => l.replace(/\r$/, ''));
const start = lines.indexOf('%chardef begin');
const end = lines.indexOf('%chardef end');
if (start !== -1 && end !== -1) {
const newFileContentArr = [
...lines.slice(0, start + 1),
...rawCharDefLines,
...lines.slice(end)
];
filecontent = newFileContentArr.join('\n');
runAnalyzeFileAndDashboard();
processContent(); // Refreshes UI, generates new download
renderCharEditArea(currentEditCode[0], '');
}
}
```
**Find `handleUndo()` and `handleRedo()` and add mode branching:**
```javascript
function handleUndo() {
if (undoStack.length === 0) return;
let prevState = undoStack.pop();
if (prevState.mode === 'char') {
redoStack.push({ state: JSON.stringify(rawCharDefLines), desc: `Undo: ${prevState.desc}`, mode: 'char' });
rawCharDefLines = JSON.parse(prevState.state);
rebuildAndProcessFromCharLines();
} else {
// Original phrase logic
let currentLines = mcc0string.split('\n').filter(l => l.trim() !== '');
redoStack.push({ lines: currentLines, desc: `Undo: ${prevState.desc}`, mode: 'phrase' });
let oldState = getSystemState();
processContent(JSON.stringify(prevState.lines));
detectAndRenderChanges(oldState, getSystemState());
}
justMovedItem = null;
renderActionLogs();
}
function handleRedo() {
if (redoStack.length === 0) return;
let nextState = redoStack.pop();
if (nextState.mode === 'char') {
undoStack.push({ state: JSON.stringify(rawCharDefLines), desc: nextState.desc, mode: 'char' });
rawCharDefLines = JSON.parse(nextState.state);
rebuildAndProcessFromCharLines();
} else {
let currentLines = mcc0string.split('\n').filter(l => l.trim() !== '');
undoStack.push({ lines: currentLines, desc: nextState.desc, mode: 'phrase' });
let oldState = getSystemState();
processContent(JSON.stringify(nextState.lines));
detectAndRenderChanges(oldState, getSystemState());
}
justMovedItem = null;
renderActionLogs();
}
```
**Find `handleEditCloseOnly()` and ensure unlock logic is present:**
```javascript
function handleEditCloseOnly() {
document.getElementById('inputArea').style.display = 'block';
document.getElementById('editArea').style.display = 'none';
document.getElementById('codeInput').value = '';
document.getElementById('modeSelect').disabled = false; // Unlock dropdown
// ... keep rest of handleEditCloseOnly
```
**Find `switchTab(tab)`. Add the default Stats redirect logic:**
```javascript
if (tab === 'stats') {
// Determine which filter button is active or fallback to Dup Stats default
const activeBtn =
document.querySelector('#statsBtnContainer .stats-btn.active') || document.getElementById('btnDupStats');
if (activeBtn) {
if (activeBtn.id === 'btnDupStats') { showDupStatsSection(activeBtn); }
else { activeBtn.click(); }
}
}
```