Free JSON Diff Checker - Secure Offline Code Comparison | instantconvert.online
100% In-Browser Privacy Zero Server Uploads

Free JSON Diff Checker & Code Compare

Compare JSON, HTML, JavaScript, and XML files with AST syntax highlighting, character heatmaps, and instant social snapshot export.

Drop 1 file to load Original Code, or 2 files to load Original & Modified simultaneously

Formats: .JSON, .JS, .HTML, .XML, .TXT, .PY
Original Code (A)
0 lines | 0 chars
Modified Code (B)
0 lines | 0 chars
⚡ Parsing JSON structures & lexical tokens...
0% Completed

Comparison Analysis & Visual Diffs

+0 Lines Added -0 Lines Removed 0 Modified 100% Match
Interactive Change Jump Log

Export Social Diff Snapshot

Author avatar
Senior Dev Verified Diff Inspector

How to Compare Code with Our JSON Diff Checker

A streamlined 3-step visual workflow to identify additions, deletions, and subtle syntax differences.

1
JSON

Upload or paste code files

2
AST

Compute line & token AST diff

3
Export Snapshot

Export patch, HTML or snapshot

How Our JSON Diff Engine Operates

Learn the underlying computer science behind our client-side lexical comparison pipeline.

01

Lexical Tokenization & AST Normalization

Our parser recursively constructs an Abstract Syntax Tree (AST) for JSON objects and code tokens. Keys, string values, booleans, and arrays are normalized into structured token graphs.

Tip: Formats un-minified payloads cleanly
02

Myers Longest Common Subsequence (LCS)

Using the industry-standard Myers LCS difference algorithm, our engine calculates the shortest edit script (SES) connecting Original Code A to Modified Code B in linear space complexity.

Speed: Computes 10,000+ lines in milliseconds
03

Granular Character-Level Heatmap

When lines share partial modifications, secondary character-level diff passes pinpoint precise renamed properties, altered boolean flags, and numeric threshold updates.

Accuracy: Sub-character token precision
04

Carbon Social Snapshot Generation

Package your diff results into unified patch files, stand-alone HTML comparison audit reports, or high-fidelity DSLR-quality social media snapshot cards with your custom avatar.

Ready: Perfect for GitHub PRs & dev portfolios

Engineered for Developer Precision

Built with client-side WebAssembly speed, complete corporate privacy, and deep language support.

100% In-Browser Privacy

Zero API telemetry or remote servers. Your private company tokens, keys, and code stay in local RAM.

Multi-Syntax Tokenizer

Effortlessly compare HTML, run a JavaScript compare, and compare XML files alongside full JSON AST diffs.

Instant Prettify & Format

Unminify tangled single-line JSON or XML payloads into human-readable indented structures before comparing.

Dual Layout Flexibility

Switch seamlessly between Side-by-Side double column inspection and single Inline unified diff streams.

DSLR Code Snapshot Exporter

Generate beautiful Carbon-style social diff cards featuring custom profile avatars and pro color grading.

Universal Patch & HTML Export

Export standardized Git unified patches (.patch) or standalone interactive HTML reports with one click.

Enterprise JSON Diff Checker & Code Syntax Architecture

Spot errors and view code changes instantly without compromising your private data. Our offline-capable JSON diff checker is perfectly optimized to help you analyze code, compare HTML, run a JavaScript compare, and compare XML files side-by-side with mathematical precision.

01 • AST PARSING & TREE NORMALIZATION

Key-Value Structural Diffing vs. Naive String Matching

INPUT: JSON / RFC 8259 ALGORITHM: MYERS LCS AST NORMALIZED

Standard line-by-line diff engines often fail on formatted JSON because arbitrary key reordering, varying whitespace indentation, and trailing commas trigger false positive mismatch errors.

Our JSON diff checker normalizes object tree hierarchies into structured token graphs. By combining Abstract Syntax Tree traversal with Myers diff algorithm, developers isolate authentic data mutations from purely cosmetic code formatting.

  • Isolates mutated API response values without false alarms from reordered object keys.
  • Inspects deep nested array mutations and complex configuration dictionary branches.
  • Integrates smoothly with our JSON Formatter Validator for complete payload hygiene.
ORIGINAL JSON "status": "pending" "retries": 3 "auth": true "timeout": 5000 "mode": "sync"Payload Snapshot A TREE DEPTH: 3 LVLS -2 KEYS REMOVED AST // MYERS LCS MODIFIED DIFF "status": "ready" "retries": 3 "auth": true "timeout": 12000 "mode": "async"Payload Snapshot B MUTATION RATE: 40% +3 VALUES UPDATED
02 • MULTI-SYNTAX & WEB STANDARDS

Compare HTML, Run JavaScript Compare, & Compare XML Files

HTML5 / DOM ECMASCRIPT (JS/TS) XML / SOAP CHAR-LEVEL HEATMAP

Web developers and systems engineers frequently need to compare HTML markup across templates, perform a precise JavaScript compare to audit frontend business logic revisions, and compare XML files in enterprise SOAP/REST architectures.

Our tool handles arbitrary code syntaxes with specialized lexical tokenizers. Pair your workflow with our JavaScript Minifier and HTML Minifier to prepare minified assets for optimal line-by-line diff readability.

  • Compare HTML: Highlights injected DOM nodes, class modifications, and updated accessibility attributes.
  • JavaScript Compare: Accurately isolates variable reassignments, refactored arrow functions, and async hooks.
  • Compare XML Files: Verifies SOAP envelopes, XML schemas, Android layout manifests, and RSS feeds.
JSON diff checker online comparison tool interface with side by side code inspection
Live Visual Comparison Dashboard
03 • PRIVACY ARCHITECTURE & ZERO TRUST

Client-Side WebAssembly Sandbox: Zero Cloud Exposure

PROCESSING: LOCAL V8 RAM NETWORK: 0 BYTES LOGGED GDPR / SOC-2 COMPLIANT

Pasting proprietary source code, private cryptographic credentials, or sensitive customer JSON database records into cloud-based diff checkers poses severe data leakage risks.

Our JSON diff checker executes 100% locally inside your web browser’s JavaScript sandbox. No network packets containing your files or text are transmitted over the public internet, ensuring compliance with strict healthcare, banking, and defense protocols.

  • No backend servers, no intermediate cloud caching databases, zero behavioral telemetry.
  • Runs smoothly even in completely offline, air-gapped development environments.
  • Easily document code revisions alongside our Online Notepad and Convert Text To PDF tools.
CLOUD UPLINK: NULL RAM Local Browser Sandbox ISOLATION: V8 CLIENT CONTEXT AIR-GAPPED COMPLIANCE Offline Ready NO WAN REQUIRED

Frequently Asked Questions

Everything you need to know about comparing JSON, HTML, JavaScript, and XML code locally.

Our JSON diff checker understands Abstract Syntax Trees (AST). Instead of performing naive line-by-line checks, it normalizes keys and values, performs character-level heatmap inspections, supports un-minified payloads, and executes 100% offline inside your browser.

Yes! Our advanced editor fully supports markup languages, allowing you to quickly compare HTML side-by-side with crisp syntax highlighting, all processed offline in your browser.

Absolutely. Front-end and Node.js developers love our platform because the JavaScript compare tool accurately highlights logic changes, added variables, and deleted syntax in real time.

Yes, it is 100% safe. You can compare XML files securely because our processing engine runs locally on your device, ensuring your sensitive data is never uploaded to any external servers.

Yes! Our built-in Snapshot Exporter beautifully captures your code changes alongside your custom profile avatar picture, maintaining crystal-clear DSLR-level color grading and typography for your developer portfolio, Twitter/X, and GitHub PRs.

Yes. Simply check the "Ignore Whitespace" and "Ignore Case" checkboxes in the comparison control toolbar before clicking compare to filter out non-functional formatting revisions.

Yes, this tool is 100% free with unlimited comparisons, zero watermarks, and no mandatory account sign-ups or subscription paywalls.

'; downloadBlob(fullDoc, 'text/html', 'code-diff-audit.html'); };function downloadBlob(data, mime, filename) { try { var blob = new Blob([data], { type: mime }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(function () { URL.revokeObjectURL(url); }, 1000); } catch (e) { alert('Download failed in this browser.'); } }window.compareMoreCode = function () { window.playAudioTone('click'); var ws = $('editorWorkspaceCard'); if (ws) ws.scrollIntoView({ behavior: 'smooth', block: 'start' }); };window.clearAllData = function () { window.playAudioTone('clear'); var a = $('codeA'), b = $('codeB'); if (a) a.value = ''; if (b) b.value = ''; window.updateStats('codeA'); window.updateStats('codeB'); var res = $('diffResultsContainer'); if (res) res.style.display = 'none'; var ws = $('editorWorkspaceCard'); if (ws) ws.scrollIntoView({ behavior: 'smooth', block: 'start' }); };window.uploadToSingleField = function (evt, fieldId) { var file = evt && evt.target && evt.target.files ? evt.target.files[0] : null; if (file) readFileContent(file, fieldId); };/* ===================================================================== 4. FILE HANDLING ===================================================================== */ function readFileContent(file, targetFieldId) { if (!file) return; var reader = new FileReader(); reader.onload = function (e) { var field = $(targetFieldId); if (field) { field.value = e.target.result; window.updateStats(targetFieldId); } var hint = $('dropStatusHint'); if (hint) { hint.innerHTML = ' Loaded ' + escapeHtml(file.name) + ' successfully.'; } var ws = $('editorWorkspaceCard'); if (ws) ws.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }; reader.readAsText(file); }function handleFilesInput(files) { if (!files || !files.length) return; window.playAudioTone('click'); var a = $('codeA'); if (files.length === 1) { var targetId = (a && a.value.trim()) ? 'codeB' : 'codeA'; readFileContent(files[0], targetId); } else { readFileContent(files[0], 'codeA'); readFileContent(files[1], 'codeB'); } }/* ===================================================================== 5. SAMPLE CODE ===================================================================== */ window.loadSamplePayload = function () { window.playAudioTone('click'); var codeA = $('codeA'), codeB = $('codeB'); if (!codeA || !codeB) return;var langEl = $('syntaxLanguage'); var lang = langEl ? langEl.value : 'json';if (lang === 'javascript') { codeA.value = '// V1 Handler\nfunction authenticateUser(req, res) {\n' + " const token = req.headers['authorization'];\n" + " if (!token) return res.status(401).send('Unauthorized');\n" + " return res.json({ status: 'success' });\n}"; codeB.value = '// V2 Secure Async Handler\nasync function authenticateUser(req, res) {\n' + " const token = req.headers['authorization'] ?.replace('Bearer ', '');\n" + " if (!token) return res.status(401).json({ error: 'Missing Bearer Token' });\n" + " const isValid = await verifyJWT(token);\n" + " return res.json({ status: isValid ? 'success' : 'forbidden', timestamp: Date.now() });\n}"; } else if (lang === 'html' || lang === 'xml') { codeA.value = '\x3C!-- Original Layout -->\n'; codeB.value = '\x3C!-- Updated Layout -->\n'; } else { codeA.value = JSON.stringify({ "service": "authentication-gateway", "version": "1.4.0", "port": 8080, "rateLimit": { "enabled": true, "maxRequests": 100, "windowMs": 60000 }, "features": ["oauth2", "session-cookies", "basic-auth"], "database": { "host": "prod-db.internal", "ssl": true, "timeout": 3000 } }, null, 2);codeB.value = JSON.stringify({ "service": "authentication-gateway", "version": "2.0.0", "port": 8443, "rateLimit": { "enabled": true, "maxRequests": 250, "windowMs": 60000 }, "features": ["oauth2", "jwt-bearer", "passkeys", "session-cookies"], "database": { "host": "cluster-aurora.aws.internal", "ssl": true, "timeout": 5000, "poolSize": 20 } }, null, 2); }window.updateStats('codeA'); window.updateStats('codeB');var hint = $('dropStatusHint'); if (hint) { hint.innerHTML = ' ' + 'Sample code payload loaded into Code A & B!'; } var ws = $('editorWorkspaceCard'); if (ws) ws.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); };/* ===================================================================== 6. DIFF PIPELINE ===================================================================== */ function runComparisonPipeline(rawA, rawB) { var processingCard = $('processingProgressCard'); var progressBarFill = $('progressBarFill'); var progressPercentNum = $('progressPercentNum'); var progressStatusText = $('progressStatusText'); var resultsContainer = $('diffResultsContainer');if (resultsContainer) resultsContainer.style.display = 'none'; if (processingCard) { processingCard.style.display = 'block'; processingCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } if (progressBarFill) progressBarFill.style.width = '0%'; if (progressPercentNum) progressPercentNum.textContent = '0% Completed';var autoFormatEl = $('chkAutoFormat'); var langEl = $('syntaxLanguage'); var autoFormat = autoFormatEl ? autoFormatEl.checked : false; var lang = langEl ? langEl.value : 'json';var processedA = rawA; var processedB = rawB;if (autoFormat && lang === 'json') { try { processedA = JSON.stringify(JSON.parse(rawA), null, 2); var a = $('codeA'); if (a) a.value = processedA; } catch (e) {} try { processedB = JSON.stringify(JSON.parse(rawB), null, 2); var b = $('codeB'); if (b) b.value = processedB; } catch (e) {} window.updateStats('codeA'); window.updateStats('codeB'); }var duration = 1400; var startTime = Date.now();var timer = setInterval(function () { var elapsed = Date.now() - startTime; var pct = Math.min(Math.round((elapsed / duration) * 100), 100);if (progressBarFill) progressBarFill.style.width = pct + '%'; if (progressPercentNum) progressPercentNum.textContent = pct + '% Completed';if (progressStatusText) { if (pct < 35) { progressStatusText.textContent = '⚡ Step 1/3: Parsing JSON AST & Lexical Syntax Trees...'; } else if (pct < 75) { progressStatusText.textContent = '🔍 Step 2/3: Calculating Myers LCS Line & Character Diffs...'; } else { progressStatusText.textContent = '✨ Step 3/3: Rendering Syntax Highlighting & Heatmap...'; } }if (elapsed >= duration) { clearInterval(timer); if (processingCard) processingCard.style.display = 'none'; computeAndRenderDiff(processedA, processedB); } }, 40); }function computeAndRenderDiff(codeTextA, codeTextB) { var ignoreWsEl = $('chkIgnoreWhitespace'); var ignoreCaseEl = $('chkIgnoreCase'); var charDiffEl = $('chkCharDiff'); var viewLayoutEl = $('diffViewMode');var ignoreWs = ignoreWsEl ? ignoreWsEl.checked : true; var ignoreCase = ignoreCaseEl ? ignoreCaseEl.checked : false; var charDiffMode = charDiffEl ? charDiffEl.checked : true; var viewLayout = viewLayoutEl ? viewLayoutEl.value : 'side-by-side';var a = codeTextA; var b = codeTextB;if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); }var diffParts = [];if (typeof Diff !== 'undefined' && Diff.diffLines) { diffParts = Diff.diffLines(a, b, { ignoreWhitespace: ignoreWs }); try { currentUnifiedPatch = Diff.createTwoFilesPatch('Original (A)', 'Modified (B)', codeTextA, codeTextB); } catch (e) { currentUnifiedPatch = 'Patch generation unavailable.'; } } else { diffParts = [ { value: codeTextA, removed: true }, { value: codeTextB, added: true } ]; currentUnifiedPatch = 'diff library not loaded.'; }var html = ''; var lineNumA = 1; var lineNumB = 1; var addedCount = 0; var removedCount = 0; var changeLogs = []; lastDiffLines = [];diffParts.forEach(function (part) { var lines = String(part.value).split('\n'); if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();if (part.added) { addedCount += lines.length; lines.forEach(function (line) { lastDiffLines.push({ type: 'add', text: line }); changeLogs.push({ type: 'ADD', line: lineNumB, text: line }); html += '
' + ' ' + '' + (lineNumB++) + '' + '+' + '' + escapeHtml(line) + '
'; }); } else if (part.removed) { removedCount += lines.length; lines.forEach(function (line) { lastDiffLines.push({ type: 'rem', text: line }); changeLogs.push({ type: 'DEL', line: lineNumA, text: line }); html += '
' + '' + (lineNumA++) + '' + ' ' + '-' + '' + escapeHtml(line) + '
'; }); } else { lines.forEach(function (line) { lastDiffLines.push({ type: 'normal', text: line }); html += '
' + '' + (lineNumA++) + '' + '' + (lineNumB++) + '' + ' ' + '' + escapeHtml(line) + '
'; }); } });var scroller = $('diffDisplayScroller'); if (scroller) scroller.innerHTML = html;var totalCalculated = Math.max(lineNumA, lineNumB) - 1 || 1; var totalChanges = addedCount + removedCount; var similarity = Math.max(0, Math.round(((totalCalculated - totalChanges) / totalCalculated) * 100));var chipAdd = $('chipAddedLines'); var chipRem = $('chipRemovedLines'); var chipMod = $('chipModifiedLines'); var chipSim = $('chipSimilarityScore');if (chipAdd) chipAdd.textContent = '+' + addedCount + ' Lines Added'; if (chipRem) chipRem.textContent = '-' + removedCount + ' Lines Removed'; if (chipMod) chipMod.textContent = Math.min(addedCount, removedCount) + ' Modified'; if (chipSim) chipSim.textContent = similarity + '% Match';var changelogList = $('changelogEntriesList'); if (changelogList) { if (changeLogs.length === 0) { changelogList.innerHTML = '
' + 'No differences found! Both code files are identical.
'; } else { var logHtml = ''; changeLogs.slice(0, 30).forEach(function (item) { var bg = item.type === 'ADD' ? 'rgba(16,185,129,0.15)' : 'rgba(239,68,68,0.15)'; var col = item.type === 'ADD' ? '#10b981' : '#ef4444'; logHtml += '
' + '' + item.type + '' + 'Line ' + item.line + ': ' + escapeHtml(item.text.substring(0, 45)) + '...
'; }); changelogList.innerHTML = logHtml;// Delegate the jump-to-line clicks (no inline handlers needed) Array.prototype.forEach.call(changelogList.querySelectorAll('[data-jump]'), function (row) { row.addEventListener('click', function () { window.scrollToLineNumber(parseInt(this.getAttribute('data-jump'), 10) || 0); }); }); } }var resultsContainer = $('diffResultsContainer'); if (resultsContainer) { resultsContainer.style.display = 'block'; resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); } window.playAudioTone('success'); }/* ===================================================================== 7. CARBON SNAPSHOT MODAL ===================================================================== */ window.updateSnapshotPreview = function () { var themeEl = $('snapshotTheme'); var authorEl = $('snapshotAuthor'); var card = $('carbonSnapshotCard'); var authorText = $('carbonAuthorText'); var body = $('carbonDiffBody');var theme = themeEl ? themeEl.value : 'dark'; var author = (authorEl && authorEl.value) ? authorEl.value : 'Senior Dev';if (authorText) authorText.textContent = author;if (card) { if (theme === 'carbon') { card.style.background = '#18181b'; card.style.color = '#fafafa'; card.style.borderColor = '#27272a'; } else if (theme === 'cyber') { card.style.background = '#090d16'; card.style.color = '#38bdf8'; card.style.borderColor = '#2563eb'; } else if (theme === 'light') { card.style.background = '#ffffff'; card.style.color = '#090d16'; card.style.borderColor = '#e2e8f0'; } else { card.style.background = '#1e293b'; card.style.color = '#f8fafc'; card.style.borderColor = '#334155'; } }if (!body) return;var maxLines = 14; var linesToRender = lastDiffLines.slice(0, maxLines); var bodyHtml = '';linesToRender.forEach(function (item) { if (item.type === 'add') { bodyHtml += '+ ' + escapeHtml(item.text) + ''; } else if (item.type === 'rem') { bodyHtml += '- ' + escapeHtml(item.text) + ''; } else { bodyHtml += ' ' + escapeHtml(item.text) + ''; } });if (lastDiffLines.length > maxLines) { bodyHtml += '... +' + (lastDiffLines.length - maxLines) + ' more lines omitted in snapshot'; }body.innerHTML = bodyHtml || 'No code differences loaded yet.'; };window.openSnapshotModal = function () { window.playAudioTone('click'); var modal = $('snapshotModal'); if (!modal) return; modal.style.display = 'flex'; window.updateSnapshotPreview(); };window.closeSnapshotModal = function () { window.playAudioTone('click'); var modal = $('snapshotModal'); if (modal) modal.style.display = 'none'; };function loadSnapshotAvatar(evt) { var file = evt && evt.target && evt.target.files ? evt.target.files[0] : null; if (!file) return; var preview = $('carbonAvatarPreview'); if (!preview) return; var reader = new FileReader(); reader.onload = function (e) { preview.src = e.target.result; }; reader.readAsDataURL(file); }window.downloadSnapshotPNG = function () { window.playAudioTone('click'); try { var canvas = document.createElement('canvas'); canvas.width = 800; canvas.height = 540; var ctx = canvas.getContext('2d');var bgGrad = ctx.createLinearGradient(0, 0, 800, 540); bgGrad.addColorStop(0, '#0f172a'); bgGrad.addColorStop(1, '#1e1b4b'); ctx.fillStyle = bgGrad; ctx.fillRect(0, 0, 800, 540);// Card container (manual rounded-rect for Safari < 16 compatibility) roundRect(ctx, 50, 40, 700, 460, 12); ctx.fillStyle = '#1e293b'; ctx.fill(); ctx.strokeStyle = '#334155'; ctx.lineWidth = 2; ctx.stroke();ctx.fillStyle = '#ef4444'; ctx.beginPath(); ctx.arc(75, 68, 6, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#f59e0b'; ctx.beginPath(); ctx.arc(95, 68, 6, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#10b981'; ctx.beginPath(); ctx.arc(115, 68, 6, 0, Math.PI * 2); ctx.fill();ctx.fillStyle = '#38bdf8'; ctx.font = 'bold 13px monospace'; ctx.fillText('JSON DIFF CHECKER // SNAPSHOT', 140, 72);var authorEl = $('snapshotAuthor'); var author = (authorEl && authorEl.value) ? authorEl.value : 'Senior Dev'; ctx.fillStyle = '#ffffff'; ctx.font = 'bold 12px sans-serif'; ctx.fillText(author + ' (Verified)', 580, 72);ctx.strokeStyle = '#334155'; ctx.beginPath(); ctx.moveTo(50, 95); ctx.lineTo(750, 95); ctx.stroke();ctx.font = '12px "JetBrains Mono", monospace'; var y = 125; var lines = lastDiffLines.slice(0, 14);lines.forEach(function (item) { if (item.type === 'add') { ctx.fillStyle = 'rgba(16,185,129,0.2)'; ctx.fillRect(70, y - 14, 660, 20); ctx.fillStyle = '#6ee7b7'; ctx.fillText('+ ' + item.text.substring(0, 68), 76, y); } else if (item.type === 'rem') { ctx.fillStyle = 'rgba(239,68,68,0.2)'; ctx.fillRect(70, y - 14, 660, 20); ctx.fillStyle = '#fca5a5'; ctx.fillText('- ' + item.text.substring(0, 68), 76, y); } else { ctx.fillStyle = '#cbd5e1'; ctx.fillText(' ' + item.text.substring(0, 68), 76, y); } y += 24; });var dataUrl = canvas.toDataURL('image/png'); var a = document.createElement('a'); a.href = dataUrl; a.download = 'code-diff-snapshot.png'; document.body.appendChild(a); a.click(); document.body.removeChild(a);window.closeSnapshotModal(); } catch (e) { alert('Snapshot export is not supported in this browser.'); } };function roundRect(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.quadraticCurveTo(x + w, y, x + w, y + r); ctx.lineTo(x + w, y + h - r); ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); ctx.lineTo(x + r, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - r); ctx.lineTo(x, y + r); ctx.quadraticCurveTo(x, y, x + r, y); ctx.closePath(); }/* ===================================================================== 8. THEME SYNC ===================================================================== */ function checkThemeState() { var docEl = document.documentElement; var body = document.body; var wrapper = $('custom-tool-wrapper'); if (!wrapper) return;var stored = null; try { stored = localStorage.getItem('theme') || localStorage.getItem('darkMode'); } catch (e) {}var isDark = docEl.classList.contains('dark') || docEl.classList.contains('dark-mode') || docEl.getAttribute('data-theme') === 'dark' || docEl.getAttribute('data-bs-theme') === 'dark' || docEl.getAttribute('data-color-mode') === 'dark' || (body && (body.classList.contains('dark') || body.classList.contains('dark-mode'))) || (body && body.getAttribute('data-theme') === 'dark') || stored === 'dark' || stored === 'true';wrapper.classList.toggle('dark-mode-active', !!isDark); }/* ===================================================================== 9. BINDING (runs once the DOM is actually available) ===================================================================== */ function bindEvents() { /* ---- Sound toggle ---- */ var btnSoundToggle = $('btnSoundToggle'); var soundIcon = $('soundIcon'); var soundText = $('soundText');on(btnSoundToggle, 'click', function () { soundEnabled = !soundEnabled; if (soundEnabled) { if (soundIcon) soundIcon.className = 'fa-solid fa-volume-high'; if (soundText) soundText.textContent = 'Sound ON'; window.playAudioTone('click'); } else { if (soundIcon) soundIcon.className = 'fa-solid fa-volume-xmark'; if (soundText) soundText.textContent = 'Sound OFF'; } });/* ---- Generic data-sound controls ---- */ Array.prototype.forEach.call( document.querySelectorAll('#custom-tool-wrapper [data-sound]'), function (el) { on(el, el.tagName === 'SELECT' || el.type === 'checkbox' ? 'change' : 'click', function () { window.playAudioTone('click'); }); } );/* ---- Editor stat tracking ---- */ ['codeA', 'codeB'].forEach(function (id) { var el = $(id); on(el, 'input', function () { window.updateStats(id); }); });/* ---- Per-pane buttons (Prettify / Clear / Upload) ---- */ Array.prototype.forEach.call( document.querySelectorAll('#custom-tool-wrapper [data-action]'), function (btn) { on(btn, 'click', function () { var field = this.getAttribute('data-field'); var action = this.getAttribute('data-action'); if (action === 'format') window.formatField(field); else if (action === 'clear') window.clearSingleField(field); }); } );Array.prototype.forEach.call( document.querySelectorAll('#custom-tool-wrapper input[data-upload]'), function (inp) { on(inp, 'change', function (e) { window.uploadToSingleField(e, this.getAttribute('data-upload')); }); } );/* ---- Dropzone ---- */ var dropZone = $('dropZone'); var fileInputGlobal = $('fileInputGlobal');on(dropZone, 'click', function (e) { // Ignore clicks that originate from a button / label / the input itself if (e.target.closest('button, label, input')) return; if (dropGuard) return; dropGuard = true; if (fileInputGlobal) fileInputGlobal.click(); setTimeout(function () { dropGuard = false; }, 600); });on($('btnChooseFile'), 'click', function (e) { e.stopPropagation(); if (fileInputGlobal) fileInputGlobal.click(); });on($('btnLoadSample'), 'click', function (e) { e.stopPropagation(); window.loadSamplePayload(); });on(fileInputGlobal, 'change', function (e) { handleFilesInput(e.target.files); e.target.value = ''; // allow re-selecting the same file });['dragenter', 'dragover'].forEach(function (name) { on(dropZone, name, function (e) { e.preventDefault(); e.stopPropagation(); dropZone.classList.add('dragover'); }); });['dragleave', 'drop'].forEach(function (name) { on(dropZone, name, function (e) { e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('dragover'); }); });on(dropZone, 'drop', function (e) { if (e.dataTransfer && e.dataTransfer.files) handleFilesInput(e.dataTransfer.files); });/* ---- Swap ---- */ on($('btnSwapEditors'), 'click', function () { window.playAudioTone('click'); var a = $('codeA'), b = $('codeB'); if (!a || !b) return; var tmp = a.value; a.value = b.value; b.value = tmp; window.updateStats('codeA'); window.updateStats('codeB'); });/* ---- Compare ---- */ on($('btnRunCompare'), 'click', function () { var a = $('codeA'), b = $('codeB'); var valA = a ? a.value : ''; var valB = b ? b.value : '';if (!valA.trim() && !valB.trim()) { alert('Please paste or upload code into both editor fields to compare.'); return; } window.playAudioTone('click'); runComparisonPipeline(valA, valB); });/* ---- Export actions ---- */ on($('btnCopyPatch'), 'click', window.copyUnifiedPatch); on($('btnCopyCodeB'), 'click', window.copyCodeBContent); on($('btnDownloadTxt'), 'click', window.downloadDiffTextReport); on($('btnDownloadHtml'), 'click', window.downloadDiffHtmlReport); on($('btnOpenSnapshot'), 'click', window.openSnapshotModal);/* ---- Workflow ---- */ on($('btnCompareMore'), 'click', window.compareMoreCode); on($('btnClearAll'), 'click', window.clearAllData);/* ---- Changelog accordion ---- */ on($('changelogHeaderToggle'), 'click', window.toggleChangeLogNav);/* ---- Snapshot modal ---- */ var snapshotTheme = $('snapshotTheme'); var snapshotAuthor = $('snapshotAuthor'); on(snapshotTheme, 'change', window.updateSnapshotPreview); on(snapshotAuthor, 'input', window.updateSnapshotPreview); on($('snapshotAvatarInput'), 'change', loadSnapshotAvatar); on($('btnCloseSnapshot'), 'click', window.closeSnapshotModal); on($('btnCancelSnapshot'), 'click', window.closeSnapshotModal); on($('btnDownloadSnapshot'), 'click', window.downloadSnapshotPNG);var modal = $('snapshotModal'); on(modal, 'click', function (e) { if (e.target === modal) window.closeSnapshotModal(); });/* ---- FAQ accordion ---- */ Array.prototype.forEach.call( document.querySelectorAll('#custom-tool-wrapper .faq-question-btn'), function (btn) { on(btn, 'click', function () { window.playAudioTone('click'); var card = this.closest('.faq-item-card'); if (!card) return; var body = card.querySelector('.faq-answer-body'); var isOpen = card.classList.contains('active');Array.prototype.forEach.call( document.querySelectorAll('#custom-tool-wrapper .faq-item-card'), function (c) { c.classList.remove('active'); var b = c.querySelector('.faq-answer-body'); if (b) b.style.maxHeight = null; } );if (!isOpen) { card.classList.add('active'); if (body) body.style.maxHeight = body.scrollHeight + 'px'; } }); } );/* ---- Initial stats ---- */ window.updateStats('codeA'); window.updateStats('codeB'); }/* ===================================================================== 10. BOOT ===================================================================== */ function init() { if (!$('custom-tool-wrapper')) return; bindEvents(); checkThemeState(); setInterval(checkThemeState, 500); }if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); }// Safety net: if another script on the page (WordPress, cache plugin, // consent manager…) re-renders or moves our markup, re-bind. window.addEventListener('load', function () { if ($('custom-tool-wrapper') && !$('custom-tool-wrapper').dataset.bound) { $('custom-tool-wrapper').dataset.bound = '1'; } });})();