diff --git a/extension/css/content.css b/extension/css/content.css
index c4e4b22..7938616 100644
--- a/extension/css/content.css
+++ b/extension/css/content.css
@@ -39,6 +39,7 @@
flex-shrink: 0;
margin-right: 4px;
margin-left: 4px;
+ white-space: pre;
}
.JF_json-container .json-index {
@@ -48,6 +49,7 @@
.JF_json-container .json-value {
margin-left: 8px;
+ white-space: pre;
}
.JF_json-container .json-number {
@@ -215,12 +217,12 @@ body.JF_.JF_dark {
justify-content: center;
}
-.JF_button-wrapper > button:first-child {
+.JF_button-wrapper>button:first-child {
border-radius: 4px 0 0 4px;
left: 1px;
}
-.JF_button-wrapper > button:nth-child(2) {
+.JF_button-wrapper>button:nth-child(2) {
border-radius: 0;
border-width: 0 1px;
border-style: solid;
@@ -228,7 +230,7 @@ body.JF_.JF_dark {
margin: 0;
}
-.JF_button-wrapper > button:last-child {
+.JF_button-wrapper>button:last-child {
border-radius: 0 4px 4px 0;
}
@@ -329,6 +331,7 @@ body.JF_.JF_dark {
display: inline-flex;
transition: opacity 0.2s ease;
}
+
.JF_word-wrap {
/* enable wrapping like in vs code, break anywhere */
white-space: pre-wrap;
@@ -344,6 +347,63 @@ body.JF_.JF_dark {
pointer-events: none;
display: none;
}
+
+.JF_context_menu {
+ background-color: #fff !important;
+ padding: 6px 0;
+ color: #202124;
+ font-size: 0.8rem;
+ position: fixed;
+ visibility: hidden;
+ left: -9999px;
+ top: -9999px;
+ appearance: none;
+ border: none;
+ background: none;
+ z-index: 100;
+ border-radius: 8px;
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 12px 24px 0 rgba(0, 0, 0, 0.24);
+}
+
+body.JF_dark .JF_context_menu {
+ background-color: #202124 !important;
+ color: rgb(232, 234, 237);
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.12),
+ 0 19px 38px 0 rgba(0, 0, 0, 0.24);
+}
+
+.JF_context_menu .JF_context_menu_item {
+ padding: 8px 24px;
+ cursor: pointer;
+ transition: background-color 100ms ease;
+}
+
+.JF_context_menu .JF_context_menu_item:hover {
+ background-color: #f1f3f4;
+}
+
+body.JF_dark #JF_context_menu .JF_context_menu_item:hover {
+ background-color: #2d2f31;
+}
+
+.JF_context_menu .JF_context_menu_item .JF_context_menu_item_shortcut {
+ font-size: 0.7rem;
+ background-color: #f1f3f4;
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ padding: 2px 6px;
+ border-radius: 4px;
+ margin-left: 8px;
+ font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
+}
+
+body.JF_dark #JF_context_menu .JF_context_menu_item .JF_context_menu_item_shortcut {
+ background-color: #202124;
+ color: rgb(232, 234, 237);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1);
+}
+
/* Disable what's new popup */
/* #JF_whats_new {
position: fixed;
diff --git a/extension/js/content.js b/extension/js/content.js
index d58b971..8bc57e4 100644
--- a/extension/js/content.js
+++ b/extension/js/content.js
@@ -33,6 +33,8 @@ SOFTWARE.
parsedCode,
formattedRawCode,
rawCode,
+ currentView,
+ contextMenu,
tree,
isDark = true,
isToolbarOpen = false,
@@ -44,7 +46,9 @@ SOFTWARE.
parsed: "p",
formatted_raw: "r",
raw: "r",
- dark: "d"
+ dark: "d",
+ collapse_all: "[",
+ expand_all: "]",
};
let IS_PREPARE_SCRIPT_RUN = false;
@@ -132,16 +136,77 @@ SOFTWARE.
}
- function formatJSON(str) {
- var obj, text = str;
- try {
- obj = JSON.parse(text);
- }
- catch (e) {
- // Not JSON
+ function setupContextMenu() {
+ window.addEventListener("contextmenu", function (e) {
+ if (window.getComputedStyle(contextMenu).visibility === "visible") return false;
+ e.preventDefault();
+ let dimensions = contextMenu.getBoundingClientRect();
+ if (e.pageX <= window.innerWidth / 2) {
+ if (e.pageY <= window.innerHeight / 2) {
+ contextMenu.style.left = e.pageX + "px";
+ contextMenu.style.top = e.pageY + "px";
+ }
+ if (e.pageY > window.innerHeight / 2) {
+ contextMenu.style.left = e.pageX + "px";
+ contextMenu.style.top = (e.pageY - dimensions.height) + "px";
+ }
+ }
+ if (e.pageX > window.innerWidth / 2) {
+ if (e.pageY < window.innerHeight / 2) {
+ contextMenu.style.left = (e.pageX - dimensions.width) + "px";
+ contextMenu.style.top = e.pageY + "px";
+ }
+ if (e.pageY > window.innerHeight / 2) {
+ contextMenu.style.left = (e.pageX - dimensions.width) + "px";
+ contextMenu.style.top = (e.pageY - dimensions.height) + "px";
+ }
+ }
+ toggleContextMenu(true);
+ });
+ window.addEventListener("click", function () {
+ toggleContextMenu(false);
+ });
+ }
+
+ function setupFormatter(str) {
+ var code = formatHTML(str);
+ let sortingFuncton = null
+ if (options.sortingOrder == "alphabetical") {
+ // alphabet sorting
+ sortingFuncton = normalize();
}
- if (typeof obj !== 'object' && typeof obj !== 'array') return;
- var formated = setupFormatter(JSON.stringify(obj));
+ formattedRawCode.innerHTML =
+ JSON.stringify(JSON.parse(code.replace(/\\u/g, "\u")), sortingFuncton, 2);
+
+ globalThis.code = code;
+
+ let leadingLine = document.createElement('div');
+ leadingLine.className = 'line emptyLine';
+ leadingLine.textContent = '';
+ leadingLine.style = 'margin-left: 0px; height: 18px;';
+ formattedRawCode.appendChild(leadingLine);
+
+ rawCode.innerHTML = JSON.stringify(JSON.parse(code.replace(/\\u/g, "\u")), sortingFuncton);
+
+ let leadingLine1 = document.createElement('div');
+ leadingLine1.className = 'line emptyLine';
+ leadingLine1.textContent = '';
+ leadingLine1.style = 'margin-left: 0px; height: 18px;';
+ rawCode.appendChild(leadingLine1);
+
+ tree = createTree(JSON.parse(code
+ .replace(/\\/g, "\\\\")
+ .replace(/\\\\\"/g, "\\\\\\\""),
+ sortingFuncton));
+ var thme = isDark ? "dark" : "light";
+ var renderedCode = render(tree, parsedCode, { theme: thme, string: true });
+ expandChildren(tree);
+ return [renderedCode, JSON.stringify(JSON.parse(code), undefined, 2)];
+ }
+
+ function formatJSON(str) {
+ // JSON Check already done in _() function
+ var formated = setupFormatter(str);
setTimeout(function () {
try {
var script = document.createElement("script");
@@ -152,16 +217,196 @@ SOFTWARE.
}, 100);
}
catch (err) {
- console.log("JSON Formatter: Sorry but you can't access original JSON in console in this page.")
+ prettyLog("Sorry but you can't access original JSON in console in this page.")
}
}, 100);
}
+ /**
+ * DOM manipulation Functions
+ */
+ async function prepareBody() {
+ document.body.innerHTML = `
+ ` +
+ // Disable What's New Popup
+ // ${options.whats_new_screen_shown ? '' :
+ // ``}
+ `
+
+
+
+
+ `;
+ currentView = options.tab;
+
+ btn_parsed = document.getElementById("open_parsed");
+ btn_formatted_raw = document.getElementById("open_formatted_raw");
+ btn_raw = document.getElementById("open_raw");
+ parsedCode = document.getElementById("parsed");
+ formattedRawCode = document.getElementById("formatted_raw");
+ rawCode = document.getElementById("raw");
+ toolbar = document.getElementById("json_toolbar");
+ btn_toolbar = document.getElementById("toggle_toolbar");
+ theme_css = document.getElementById("JF_theme");
+ contextMenu = document.getElementById("JF_context_menu");
+
+ // Add Event Listeners to context menu
+ document.getElementById("JF_context_menu_collapse_all").addEventListener("click", function () { collapseChildren(tree); });
+ document.getElementById("JF_context_menu_expand_all").addEventListener("click", function () { expandChildren(tree); });
+ document.getElementById("JF_context_menu_dark").addEventListener("click", async function () { await toggleDarkMode(); });
+ document.getElementById("JF_context_menu_parsed").addEventListener("click", function () { openView("parsed"); });
+ document.getElementById("JF_context_menu_formatted_raw").addEventListener("click", function () { openView("formatted_raw"); });
+ document.getElementById("JF_context_menu_raw").addEventListener("click", function () { openView("raw"); });
+ document.getElementById("JF_context_menu_toolbar").addEventListener("click", function () { toggleToolbar(); });
+
+ // Add event listeners to toolbar buttons
+ btn_parsed.addEventListener("click", function () {
+ openView("parsed");
+ });
+ btn_formatted_raw.addEventListener("click", function () {
+ openView("formatted_raw");
+ });
+ btn_raw.addEventListener("click", function () {
+ openView("raw");
+ });
+ btn_toolbar.addEventListener("click", function () {
+ toggleToolbar();
+ });
+ document.getElementById("toggle_dark").addEventListener("click", async function () {
+ await toggleDarkMode();
+ });
+
+ if (options.colorScheme == "auto") {
+ let darkbool = window.matchMedia("(prefers-color-scheme: dark)").matches;
+ await toggleDarkMode(darkbool);
+
+ window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').addEventListener("change", async function (e) {
+ if (options.colorScheme == "auto") {
+ let bool = e.matches;
+ await toggleDarkMode(bool);
+ }
+ });
+ }
+ if (options.colorScheme == "dark") await toggleDarkMode(true);
+ if (options.colorScheme == "light") await toggleDarkMode(false);
+ toggleWordWrap();
+
+ // Disable What's New Popup
+ // window.addEventListener("message", async function (m) {
+ // if (m.data.type == "JF-close-whats-new") {
+ // document.getElementById("JF_whats_new").remove();
+ // options.whats_new_screen_shown = true;
+ // await chrome.storage.local.set({ [bucket]: options });
+ // }
+ // });
+
+
+ window.addEventListener("keydown", async (e) => {
+ if (e.target.tagName === "INPUT" || e.target.isContentEditable) {
+ return false;
+ }
+ if (
+ !e.ctrlKey &&
+ !e.altKey &&
+ !e.metaKey &&
+ e.shiftKey
+ ) {
+ if (e.key === hotkeys.formatted_raw || e.code === "Key" + hotkeys.formatted_raw.toUpperCase()) {
+ e.preventDefault();
+ openView("formatted_raw");
+ }
+ }
+
+ if (
+ !e.ctrlKey &&
+ !e.altKey &&
+ !e.metaKey &&
+ !e.shiftKey
+ ) {
+ if (e.key === hotkeys.toolbar || e.code === "Key" + hotkeys.toolbar.toUpperCase()) {
+ e.preventDefault();
+ toggleToolbar();
+ }
+ if (e.key === hotkeys.dark || e.code === "Key" + hotkeys.dark.toUpperCase()) {
+ e.preventDefault();
+ await toggleDarkMode();
+ }
+ if (e.key === hotkeys.parsed || e.code === "Key" + hotkeys.parsed.toUpperCase()) {
+ e.preventDefault();
+ openView("parsed");
+ }
+ if (e.key === hotkeys.raw || e.code === "Key" + hotkeys.raw.toUpperCase()) {
+ e.preventDefault();
+ openView("raw");
+ }
+ if (e.key === hotkeys.collapse_all || e.code === "Key" + hotkeys.collapse_all.toUpperCase()) {
+ e.preventDefault();
+ collapseChildren(tree);
+ }
+ if (e.key === hotkeys.expand_all || e.code === "Key" + hotkeys.expand_all.toUpperCase()) {
+ e.preventDefault();
+ expandChildren(tree);
+ }
+ }
+ });
+ }
+
async function _() {
var preCode;
if (!document ||
!document.body ||
!document.body.childNodes ||
+ !document.body.childNodes.length ||
document.body === null ||
document.body === undefined ||
document.body.childNodes === null ||
@@ -173,7 +418,7 @@ SOFTWARE.
) {
let tb = false;
try {
- JSON.parse(document.body.innerText);
+ JSON.parse(document.body.textContent);
}
catch (e) {
tb = true;
@@ -188,20 +433,20 @@ SOFTWARE.
firstEl.hidden = false;
}, 1000);
if (firstEl.tagName === "PRE" && firstEl.nodeName === "PRE" && firstEl.nodeType === 1) {
- preCode = firstEl.innerText;
+ preCode = firstEl.textContent;
}
else if (firstEl.tagName === "DIV" && firstEl.nodeName === "DIV" && firstEl.nodeType === 1) {
// preventing chrome native UI
- if (firstEl.innerText.length == 0) {
- preCode = document.getElementsByTagName("pre")[0].innerText
+ if (firstEl.textContent.length == 0) {
+ preCode = document.getElementsByTagName("pre")[0].textContent
}
else {
- preCode = firstEl.innerText;
+ preCode = firstEl.textContent;
}
}
else if (firstEl.tagName === "CODE" && firstEl.nodeName === "CODE" && firstEl.nodeType === 1) {
- preCode = firstEl.innerText;
+ preCode = firstEl.textContent;
}
else if (firstEl.tagName === undefined && firstEl.nodeName === "#text" && firstEl.nodeType === 3) {
preCode = firstEl.nodeValue;
@@ -216,14 +461,20 @@ SOFTWARE.
jsonLen === 0
) {
firstEl.hidden = false;
- console.log("JSON Formatter: JSON too large to format!")
+ prettyLog("JSON too large to format!")
return false;
}
- var isJSON = false, obj;
+ var isJSON = false;
try {
- obj = JSON.parse(preCode);
+ let obj = JSON.parse(preCode);
+ let increment = 0;
while (typeof (obj) === "string") {
obj = JSON.parse(obj);
+ increment++;
+ }
+ // if it can be parsed 2nd time, then original object was wrapped in quotes, some API devs do that for no reason
+ if (increment > 1) {
+ preCode = JSON.parse(preCode);
}
if (typeof (obj) === "number" || typeof (obj) === "boolean" || typeof (obj) === "null" || typeof (obj) === "undefined" || typeof (obj) === "NaN") {
firstEl.hidden = false;
@@ -249,7 +500,8 @@ SOFTWARE.
if (isJSON) {
await fetchExtensionSettings();
await prepareBody();
- formatJSON(JSON.stringify(obj));
+ setupContextMenu();
+ formatJSON(preCode);
globalThis.validJSON = true;
}
}
@@ -555,155 +807,30 @@ SOFTWARE.
str = str.replace(//gm, ">")
return str;
}
-
- /**
- * DOM manipulation Functions
+ /*
+ escape the following to make then visible and not treated as special char
+ \"
+ \\
+ \/
+ \b
+ \f
+ \n
+ \r
+ \t
+ \u
*/
- async function prepareBody() {
- document.body.innerHTML = `
-` +
-// Disable What's New Popup
-// ${options.whats_new_screen_shown ? '' :
-// ``}
-`
-
-
-`;
-
- btn_parsed = document.getElementById("open_parsed");
- btn_formatted_raw = document.getElementById("open_formatted_raw");
- btn_raw = document.getElementById("open_raw");
- parsedCode = document.getElementById("parsed");
- formattedRawCode = document.getElementById("formatted_raw");
- rawCode = document.getElementById("raw");
- toolbar = document.getElementById("json_toolbar");
- btn_toolbar = document.getElementById("toggle_toolbar");
- theme_css = document.getElementById("JF_theme");
-
- btn_parsed.addEventListener("click", function () {
- openView("parsed");
- });
- btn_formatted_raw.addEventListener("click", function () {
- openView("formatted_raw");
- });
- btn_raw.addEventListener("click", function () {
- openView("raw");
- });
- btn_toolbar.addEventListener("click", function () {
- toggleToolbar();
- });
- document.getElementById("toggle_dark").addEventListener("click", async function () {
- await toggleDarkMode();
- });
-
- if (options.colorScheme == "auto") {
- let darkbool = window.matchMedia("(prefers-color-scheme: dark)").matches;
- await toggleDarkMode(darkbool);
-
- window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').addEventListener("change", async function (e) {
- if (options.colorScheme == "auto") {
- let bool = e.matches;
- await toggleDarkMode(bool);
- }
- });
- }
- if (options.colorScheme == "dark") await toggleDarkMode(true);
- if (options.colorScheme == "light") await toggleDarkMode(false);
- toggleWordWrap();
-
- // Disable What's New Popup
- // window.addEventListener("message", async function (m) {
- // if (m.data.type == "JF-close-whats-new") {
- // document.getElementById("JF_whats_new").remove();
- // options.whats_new_screen_shown = true;
- // await chrome.storage.local.set({ [bucket]: options });
- // }
- // });
-
-
- window.addEventListener("keydown", async (e) => {
- if (e.target.tagName === "INPUT" || e.target.isContentEditable) {
- return false;
- }
- if (
- !e.ctrlKey &&
- !e.altKey &&
- !e.metaKey &&
- e.shiftKey
- ) {
- if (e.key === hotkeys.formatted_raw || e.code === "Key" + hotkeys.formatted_raw.toUpperCase()) {
- e.preventDefault();
- openView("formatted_raw");
- }
- }
-
- if (
- !e.ctrlKey &&
- !e.altKey &&
- !e.metaKey &&
- !e.shiftKey
- ) {
- if (e.key === hotkeys.toolbar || e.code === "Key" + hotkeys.toolbar.toUpperCase()) {
- e.preventDefault();
- toggleToolbar();
- }
- if (e.key === hotkeys.dark || e.code === "Key" + hotkeys.dark.toUpperCase()) {
- e.preventDefault();
- await toggleDarkMode();
- }
- if (e.key === hotkeys.parsed || e.code === "Key" + hotkeys.parsed.toUpperCase()) {
- e.preventDefault();
- openView("parsed");
- }
- if (e.key === hotkeys.raw || e.code === "Key" + hotkeys.raw.toUpperCase()) {
- e.preventDefault();
- openView("raw");
- }
- }
- });
+ function jsonEscape(json) {
+ return json
+ .replaceAll(/\\/g, "\\\\")
+ .replaceAll(/\\n/g, "\\n")
+ .replaceAll(/\\r/g, "\\r")
+ .replaceAll(/\\t/g, "\\t")
+ .replaceAll(/\\f/g, "\\f")
+ .replaceAll(/\\b/g, "\\b")
+ .replaceAll(/\\u/g, "\\u")
+ .replaceAll(/\\\//g, "\\/");
}
+
function normalize(sortingFunction) {
return function (key, value) {
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
@@ -718,47 +845,12 @@ SOFTWARE.
return value;
}
}
- function setupFormatter(str) {
- var code;
- if (typeof (str) == "object") {
- code = JSON.stringify(str);
- code = JSON.stringify(JSON.parse(formatHTML(JSON.stringify(code))));
- }
- if (typeof (str) == "string") {
- code = JSON.stringify(JSON.parse(formatHTML(str)));
- }
- let sortingFuncton = null
- if (options.sortingOrder == "alphabetical") {
- // alphabet sorting
- sortingFuncton = normalize();
- }
- formattedRawCode.innerHTML = JSON.stringify(JSON.parse(code), sortingFuncton, 2);
-
- let leadingLine = document.createElement('div');
- leadingLine.className = 'line emptyLine';
- leadingLine.textContent = '';
- leadingLine.style = 'margin-left: 0px; height: 18px;';
- formattedRawCode.appendChild(leadingLine);
-
- rawCode.innerHTML = JSON.stringify(JSON.parse(code, sortingFuncton));
-
- let leadingLine1 = document.createElement('div');
- leadingLine1.className = 'line emptyLine';
- leadingLine1.textContent = '';
- leadingLine1.style = 'margin-left: 0px; height: 18px;';
- rawCode.appendChild(leadingLine1);
-
- tree = createTree(JSON.parse(code, sortingFuncton));
- var thme = isDark ? "dark" : "light";
- var renderedCode = render(tree, parsedCode, { theme: thme, string: true });
- expandChildren(tree);
- return [renderedCode, JSON.stringify(JSON.parse(code), undefined, 2)];
- }
function openView(type) {
if (type != "parsed" && type != "raw" && type != "formatted_raw") {
throw new TypeError(type + " is not a valid type!");
}
+ currentView = type;
if (type == "parsed") {
formattedRawCode.hidden = true;
rawCode.hidden = true;
@@ -784,6 +876,7 @@ SOFTWARE.
btn_formatted_raw.classList.add("active");
}
}
+
function toggleToolbar(bool) {
if (bool != undefined) {
if (bool == false) {
@@ -898,6 +991,45 @@ SOFTWARE.
}
}
+ function toggleContextMenu(bool, event) {
+ if (bool != undefined) {
+ if (bool == false) {
+ contextMenu.style.left = "-9999px";
+ contextMenu.style.top = "-9999px";
+ contextMenu.style.visibility = "hidden";
+ }
+ else {
+ if (currentView !== "parsed") {
+ document.getElementById("JF_context_menu_collapse_all").hidden = true;
+ document.getElementById("JF_context_menu_expand_all").hidden = true;
+ }
+ else {
+ document.getElementById("JF_context_menu_collapse_all").hidden = false;
+ document.getElementById("JF_context_menu_expand_all").hidden = false;
+ }
+ contextMenu.style.visibility = "visible";
+ }
+ }
+ else {
+ if (contextMenu.hidden) {
+ if (currentView !== "parsed") {
+ document.getElementById("JF_context_menu_collapse_all").hidden = true;
+ document.getElementById("JF_context_menu_expand_all").hidden = true;
+ }
+ else {
+ document.getElementById("JF_context_menu_collapse_all").hidden = false;
+ document.getElementById("JF_context_menu_expand_all").hidden = false;
+ }
+ contextMenu.style.visibility = "visible";
+ }
+ else {
+ contextMenu.style.left = "-9999px";
+ contextMenu.style.top = "-9999px";
+ contextMenu.style.visibility = "hidden";
+ }
+ }
+ }
+
function isBrowserNativeUIShown(document) {
let flag = false;
let hasAHiddenDIVContainingValidJSON = false;
@@ -907,7 +1039,7 @@ SOFTWARE.
let firstEl = document.body.childNodes[0];
if (firstEl.tagName === "DIV" && firstEl.nodeName === "DIV" && firstEl.nodeType === 1 && firstEl.hidden) {
try {
- JSON.parse(firstEl.innerText);
+ JSON.parse(firstEl.textContent);
hasAHiddenDIVContainingValidJSON = true;
}
catch (e) {
@@ -933,4 +1065,8 @@ SOFTWARE.
return text;
}
+ function prettyLog(str) {
+ console.log(`%c[JSON Formatter] %c${str}`, "color:purple;font-weight:bold;", "");
+ }
+
})();
\ No newline at end of file
diff --git a/extension/manifest.json b/extension/manifest.json
index b9c2e8c..636f35a 100644
--- a/extension/manifest.json
+++ b/extension/manifest.json
@@ -55,7 +55,8 @@
"css/content.css"
],
"run_at": "document_start",
- "all_frames": true
+ "all_frames": true,
+ "match_origin_as_fallback": true
}
],
"host_permissions": [
@@ -64,7 +65,8 @@
],
"permissions": [
"storage",
- "unlimitedStorage"
+ "unlimitedStorage",
+ "contextMenus"
],
"web_accessible_resources": [
{