Skip to content

Commit f691271

Browse files
authored
Add JSON formatting toggle to cors-fetch tool (#182)
> Modify the cors-fetch tool such that if the response cleanly parses using JSON.parse it is automatically displayed pretty indented with a toggle at the top to switch between formatted and original When the response body is valid JSON, automatically pretty-print it with 2-space indentation. A toggle switch appears above the body output to switch between formatted and original views. The toggle defaults to formatted and resets when a new JSON response is received. https://gistpreview.github.io/?21b21ef0b16f9cc78b8f51a70986b1dd/index.html
1 parent f3411f0 commit f691271

File tree

1 file changed

+109
-1
lines changed

1 file changed

+109
-1
lines changed

cors-fetch.html

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,53 @@
444444
font-weight: 600;
445445
}
446446

447+
/* JSON format toggle */
448+
.format-toggle-row {
449+
display: flex;
450+
align-items: center;
451+
gap: 8px;
452+
margin-bottom: 8px;
453+
}
454+
455+
.format-toggle-row label {
456+
display: flex;
457+
align-items: center;
458+
gap: 6px;
459+
font-weight: 400;
460+
font-size: 0.85rem;
461+
cursor: pointer;
462+
}
463+
464+
.toggle-switch {
465+
position: relative;
466+
width: 36px;
467+
height: 20px;
468+
background: #d1d5db;
469+
border-radius: 999px;
470+
cursor: pointer;
471+
transition: background 0.2s;
472+
}
473+
474+
.toggle-switch.active {
475+
background: #2563eb;
476+
}
477+
478+
.toggle-switch::after {
479+
content: "";
480+
position: absolute;
481+
top: 2px;
482+
left: 2px;
483+
width: 16px;
484+
height: 16px;
485+
background: #fff;
486+
border-radius: 50%;
487+
transition: transform 0.2s;
488+
}
489+
490+
.toggle-switch.active::after {
491+
transform: translateX(16px);
492+
}
493+
447494
@media (max-width: 640px) {
448495
.card {
449496
padding: 14px 14px 16px;
@@ -617,6 +664,12 @@ <h1>CORS Fetch Tester</h1>
617664
Body
618665
<small>Shown as text (UTF-8)</small>
619666
</div>
667+
<div id="format-toggle-container" class="format-toggle-row hidden">
668+
<label>
669+
<span id="format-toggle" class="toggle-switch active"></span>
670+
<span>Format JSON</span>
671+
</label>
672+
</div>
620673
<pre id="body-output">// Response body will appear here</pre>
621674
</div>
622675
</div>
@@ -655,16 +708,67 @@ <h1>CORS Fetch Tester</h1>
655708
const headersContainer = document.getElementById("headers-container");
656709
const bodyOutput = document.getElementById("body-output");
657710

711+
// JSON format toggle
712+
const formatToggleContainer = document.getElementById("format-toggle-container");
713+
const formatToggle = document.getElementById("format-toggle");
714+
658715
// State
659716
let kvPairs = [];
660717
let headerPairs = [];
718+
let responseBodyRaw = "";
719+
let responseBodyFormatted = "";
720+
let responseIsJson = false;
721+
let showFormatted = true;
661722

662723
// Headers collapsible
663724
headersToggle.addEventListener("click", () => {
664725
headersToggle.classList.toggle("collapsed");
665726
headersEditorContainer.classList.toggle("collapsed");
666727
});
667728

729+
// JSON format toggle
730+
formatToggle.addEventListener("click", () => {
731+
showFormatted = !showFormatted;
732+
formatToggle.classList.toggle("active", showFormatted);
733+
updateBodyDisplay();
734+
});
735+
736+
function updateBodyDisplay() {
737+
if (responseIsJson) {
738+
bodyOutput.textContent = showFormatted ? responseBodyFormatted : responseBodyRaw;
739+
} else {
740+
bodyOutput.textContent = responseBodyRaw;
741+
}
742+
}
743+
744+
function setResponseBody(text) {
745+
responseBodyRaw = text;
746+
responseIsJson = false;
747+
responseBodyFormatted = "";
748+
749+
// Try to parse as JSON
750+
if (text) {
751+
try {
752+
const parsed = JSON.parse(text);
753+
responseBodyFormatted = JSON.stringify(parsed, null, 2);
754+
responseIsJson = true;
755+
} catch {
756+
// Not valid JSON
757+
}
758+
}
759+
760+
// Show/hide toggle based on whether response is JSON
761+
formatToggleContainer.classList.toggle("hidden", !responseIsJson);
762+
763+
// Reset to formatted view when new JSON response comes in
764+
if (responseIsJson) {
765+
showFormatted = true;
766+
formatToggle.classList.add("active");
767+
}
768+
769+
updateBodyDisplay();
770+
}
771+
668772
function updateHeadersCount() {
669773
const count = headerPairs.filter(h => h.key.trim()).length;
670774
headersCountEl.textContent = count;
@@ -1214,6 +1318,7 @@ <h1>CORS Fetch Tester</h1>
12141318
setStatus("Pending request…", null);
12151319
effectiveUrlEl.textContent = "";
12161320
headersContainer.innerHTML = '<div class="hint">Awaiting response…</div>';
1321+
formatToggleContainer.classList.add("hidden");
12171322
bodyOutput.textContent = "";
12181323

12191324
updateFragmentFromState();
@@ -1249,6 +1354,7 @@ <h1>CORS Fetch Tester</h1>
12491354
renderResponseHeaders(response.headers);
12501355

12511356
if (method === "HEAD") {
1357+
formatToggleContainer.classList.add("hidden");
12521358
bodyOutput.textContent = "// HEAD requests do not return a body.";
12531359
} else {
12541360
let bodyText;
@@ -1259,9 +1365,10 @@ <h1>CORS Fetch Tester</h1>
12591365
}
12601366

12611367
if (!bodyText) {
1368+
formatToggleContainer.classList.add("hidden");
12621369
bodyOutput.textContent = "// Empty response body (or body already consumed).";
12631370
} else {
1264-
bodyOutput.textContent = bodyText;
1371+
setResponseBody(bodyText);
12651372
}
12661373
}
12671374
} catch (err) {
@@ -1273,6 +1380,7 @@ <h1>CORS Fetch Tester</h1>
12731380
String(err);
12741381
headersContainer.innerHTML =
12751382
'<div class="hint">No headers: the browser blocked access to the response.</div>';
1383+
formatToggleContainer.classList.add("hidden");
12761384
bodyOutput.textContent =
12771385
"// No body: the browser blocked access to the response.\n" +
12781386
"// This usually means the server has not enabled CORS for this origin.";

0 commit comments

Comments
 (0)