Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 1.0.0 - 2025-06-06

* Migrate to Manifest V3
* Update (most) dependencies to their latest versions

# 0.18.1 - 2020-12-22

* Update viewer-custom.scss #267 (fix blank space)
Expand Down
80 changes: 62 additions & 18 deletions extension/manifest.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,82 @@
{
"name": "JSON Viewer",
"version": "0.18.1",
"manifest_version": 2,
"version": "1.0.0",
"manifest_version": 3,
"author": "Tulio Ornelas <ornelas.tulio@gmail.com>",
"description": "The most beautiful and customizable JSON/JSONP highlighter that your eyes have ever seen. Open source at https://goo.gl/fmphc7",
"homepage_url": "https://github.com/tulios/json-viewer",
"minimum_chrome_version": "21",
"minimum_chrome_version": "88",
"icons": {
"128": "icons/128.png",
"32": "icons/32.png",
"16": "icons/16.png"
"128": "icons/128.png",
"32": "icons/32.png",
"16": "icons/16.png"
},
"web_accessible_resources": [
"assets/viewer.css",
"assets/viewer-alert.css",
"pages/options.html",
"pages/omnibox.html"
{
"resources": [
"assets/viewer.css",
"assets/viewer-alert.css",
"themes/dark/3024-night.css",
"themes/dark/ambiance.css",
"themes/dark/base16-dark.css",
"themes/light/base16-light.css",
"themes/dark/cobalt.css",
"themes/light/coy.css",
"themes/dark/dark.css",
"themes/dark/dracula.css",
"themes/dark/dracula-custom.css",
"themes/light/funky.css",
"themes/dark/jellybeans.css",
"themes/dark/material.css",
"themes/dark/mbo.css",
"themes/light/mdn-like.css",
"themes/dark/mehdi.css",
"themes/dark/midnight.css",
"themes/dark/monokai.css",
"themes/light/neo.css",
"themes/dark/okaidia.css",
"themes/dark/panda-syntax.css",
"themes/dark/solarized.css",
"themes/light/solarized.css",
"themes/dark/tomorrow.css",
"themes/dark/twilight.css",
"themes/light/yeti.css",
"themes/dark/zenburn.css",
"pages/options.html",
"pages/omnibox.html"
],
"matches": [
"<all_urls>"
]
}
],
"offline_enabled": true,
"omnibox": { "keyword" : "json-viewer" },
"omnibox": {
"keyword": "json-viewer"
},
"options_page": "pages/options.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["assets/viewer.js"],
"matches": [
"<all_urls>"
],
"js": [
"assets/viewer.js"
],
"run_at": "document_start"
}
],
"background": {
"scripts": ["assets/backend.js", "assets/omnibox.js"],
"persistent": false
"service_worker": "assets/backend.js"
},
"permissions": [
"host_permissions": [
"*://*/*",
"<all_urls>"
]
}
],
"permissions": [
"storage"
],
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
}
33 changes: 29 additions & 4 deletions extension/src/backend.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
var chrome = require('chrome-framework');
var Storage = require('./json-viewer/storage');
const chrome = require('chrome-framework');
const Storage = require('./json-viewer/storage');

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
try {
if (request.action === "GET_OPTIONS") {
sendResponse({err: null, value: Storage.load()});
Storage.load((options) => {
sendResponse({ err: null, value: options });
});
}
} catch(e) {
console.error('[JSONViewer] error: ' + e.message, e);
sendResponse({err: e});
}

// Manifest V3 requirement
// Always return true to handle async responses
return true;
});

chrome.omnibox.onInputEntered.addListener(function (text) {
const url = chrome.runtime.getURL("pages/omnibox.html?scratch-page=true");
chrome.tabs.create({ url: url });
});

chrome.omnibox.onInputChanged.addListener(function (text, suggest) {
console.log('[JSONViewer] inputChanged: ' + text);
suggest([
{
content: "Format JSON",
description: "(Format JSON) Open a page with json highlighted"
},
{
content: "Scratch pad",
description: "(Scratch pad) Area to write and format/highlight JSON"
}
]);
});
1 change: 0 additions & 1 deletion extension/src/json-viewer/content-extractor.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
var Promise = require('promise');
var jsonFormater = require('./jsl-format');
var extractJSON = require('./extract-json');

Expand Down
11 changes: 4 additions & 7 deletions extension/src/json-viewer/highlight-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ function oversizedJSON(pre, options, outsideViewer) {
var container = document.createElement("div");

var message = document.createElement("div");
message.innerHTML = "[JSONViewer] Content not highlighted due to oversize. " +
message.textContent = "[JSONViewer] Content not highlighted due to oversize. " +
"Take a look at the console log for more information.";
container.appendChild(message);

var highlightAnyway = document.createElement("a");
highlightAnyway.href = "#";
highlightAnyway.title = "Highlight anyway!";
highlightAnyway.innerHTML = "Highlight anyway!";
highlightAnyway.textContent = "Highlight anyway!";
highlightAnyway.onclick = function(e) {
e.preventDefault();
pre.hidden = true;
Expand All @@ -54,7 +54,6 @@ function oversizedJSON(pre, options, outsideViewer) {

function prependHeader(options, outsideViewer, jsonText) {
if (!outsideViewer && options.addons.prependHeader) {
options.structure.firstLineNumber = options.structure.firstLineNumber - 3
var header = "// " + timestamp() + "\n";
header += "// " + document.location.href + "\n\n";
jsonText = header + jsonText;
Expand All @@ -63,7 +62,7 @@ function prependHeader(options, outsideViewer, jsonText) {
return jsonText;
}

function highlightContent(pre, outsideViewer, ignoreLimit) {
async function highlightContent(pre, outsideViewer, ignoreLimit) {
getOptions().then(function(options) {
if (!ignoreLimit && oversizedJSON(pre, options, outsideViewer)) {
return pre.hidden = false;
Expand Down Expand Up @@ -94,9 +93,7 @@ function highlightContent(pre, outsideViewer, ignoreLimit) {
)
}

// "awaysFold" was a typo but to avoid any problems I'll keep it
// a while
if (options.addons.alwaysFold || options.addons.awaysFold) {
if (options.addons.alwaysFold || options.addons.alwaysFold) {
highlighter.fold();
}

Expand Down
65 changes: 23 additions & 42 deletions extension/src/json-viewer/load-css.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,30 @@
var Promise = require('promise');
var chrome = require("chrome-framework");
var MAX_WAIT = 20;

function loadCSS(opts) {
var url = chrome.extension.getURL(opts.path);

var link = document.createElement("link");
var sheets = document.styleSheets;
link.rel = "stylesheet";
link.href = url;
if (opts.id) link.id = opts.id;

document.head.appendChild(link);

var checkElement = document.createElement("div");
checkElement.setAttribute("class", opts.checkClass);
document.body.appendChild(checkElement);

var scheduleId = null;
var attempts = 0;

return new Promise(function(resolve, reject) {
function scheduleCheck() {
var content = window.
getComputedStyle(checkElement, ":before").
getPropertyValue("content");

if (attempts > MAX_WAIT) {
return reject(
Error("fail to load css: '" + url + "', content loaded: " + content)
);
}

if (/loaded/.test(content)) {
cancelAnimationFrame(scheduleId);
document.body.removeChild(checkElement);
try {
const url = chrome.runtime.getURL(opts.path);
const link = document.createElement("link");
link.href = url;
link.type = "text/css";
link.rel = "stylesheet";
link.id = opts.id;

link.onload = function() {
resolve();

} else {
attempts++;
scheduleId = requestAnimationFrame(scheduleCheck, 1);
}
};

link.onerror = function(e) {
console.error("[JSONViewer] Error loading CSS:", opts.path, e);
// Resolve anyway to continue with the process
resolve();
};

document.getElementsByTagName("head")[0].appendChild(link);

} catch(e) {
console.error("[JSONViewer] Exception in loadCSS:", e);
// Resolve anyway to continue with the process
resolve();
}

scheduleCheck();
});
}

Expand Down
102 changes: 51 additions & 51 deletions extension/src/json-viewer/storage.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
var defaults = require('./options/defaults');
var merge = require('./merge');
const defaults = require('./options/defaults');
const merge = require('./merge');

var OLD_NAMESPACE = "options";
var NAMESPACE = "v2.options";
const NAMESPACE = "v2.options";
const chrome = require('chrome-framework');

module.exports = {
save: function(obj) {
localStorage.setItem(NAMESPACE, JSON.stringify(obj));
},

load: function() {
var optionsStr = localStorage.getItem(NAMESPACE);
optionsStr = this.restoreOldOptions(optionsStr);

options = optionsStr ? JSON.parse(optionsStr) : {};
options.theme = options.theme || defaults.theme;
options.addons = options.addons ? JSON.parse(options.addons) : {};
options.addons = merge({}, defaults.addons, options.addons)
options.structure = options.structure ? JSON.parse(options.structure) : defaults.structure;
options.style = options.style && options.style.length > 0 ? options.style : defaults.style;
return options;
save: function (obj) {
chrome.storage.local.set({ [NAMESPACE]: JSON.stringify(obj) }, () => {
if (chrome.runtime.lastError) {
console.error("[JSONViewer] Error saving options:", chrome.runtime.lastError);
} else {
console.log("[JSONViewer] Options saved successfully.");
}
});
},

restoreOldOptions: function(optionsStr) {
var oldOptions = localStorage.getItem(OLD_NAMESPACE);
var options = null;

if (optionsStr === null && oldOptions !== null) {
try {
oldOptions = JSON.parse(oldOptions);
if(!oldOptions || typeof oldOptions !== "object") oldOptions = {};

options = {};
options.theme = oldOptions.theme;
options.addons = {
prependHeader: JSON.parse(oldOptions.prependHeader || defaults.addons.prependHeader),
maxJsonSize: parseInt(oldOptions.maxJsonSize || defaults.addons.maxJsonSize, 10)
}
load: function (callback) {
const self = this
const options = this._createDefaultOptions();

// Update to at least the new max value
if (options.addons.maxJsonSize < defaults.addons.maxJsonSize) {
options.addons.maxJsonSize = defaults.addons.maxJsonSize;
}
// For MV3, we need to handle async storage but return defaults synchronously
if (!chrome?.storage?.local) return options

options.addons = JSON.stringify(options.addons);
options.structure = JSON.stringify(defaults.structure);
options.style = defaults.style;
this.save(options);
chrome.storage.local.get(NAMESPACE, function (result) {
if (chrome.runtime.lastError) {
console.error("[JSONViewer] Error loading options:", chrome.runtime.lastError);
callback(options)
return;
}

optionsStr = JSON.stringify(options);
const storedOptions = JSON.parse(result[NAMESPACE] ?? '{}');
Object.assign(options, self._processOptions(storedOptions));
callback(options)
});

} catch(e) {
console.error('[JSONViewer] error: ' + e.message, e);
},

} finally {
localStorage.removeItem(OLD_NAMESPACE);
}
}
_createDefaultOptions: function () {
return {
theme: defaults.theme,
addons: merge({}, defaults.addons),
structure: defaults.structure,
style: defaults.style
};
},

return optionsStr;
_processOptions: function (options) {
if (!options) return this._createDefaultOptions();

const processed = {};
processed.theme = options.theme || defaults.theme;
processed.addons = options.addons ?
(typeof options.addons === 'string' ? JSON.parse(options.addons) : options.addons) :
{};
processed.addons = merge({}, defaults.addons, processed.addons);
processed.structure = options.structure ?
(typeof options.structure === 'string' ? JSON.parse(options.structure) : options.structure) :
defaults.structure;
processed.style = options.style && options.style.length > 0 ? options.style : defaults.style;
return processed;
}
}
}
Loading