PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / json-formatter / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/json-formatter/frontend.js

316 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 /* ---- Syntax highlighter ---- */
5 function syntaxHighlight(json, colors) {
6 var keyC = colors.key || '#0ea5e9';
7 var strC = colors.string || '#16a34a';
8 var numC = colors.number || '#f59e0b';
9 var boolC = colors.boolNull || '#ec4899';
10 var punctC = colors.punct || '#9ca3af';
11 var textC = colors.text || '#cdd6f4';
12
13 /* Tokenise */
14 var html = '';
15 var regex = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?|[{}\[\],:])/g;
16 json.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(regex, function (match, offset, fullStr) {
17 return match; /* replaced below */
18 });
19
20 var escaped = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
21 html = escaped.replace(regex, function (match) {
22 var color = textC;
23 var isPunct = /^[{}\[\],:]$/.test(match);
24 var isBool = /^(true|false|null)$/.test(match);
25 var isNum = !isNaN(match) && match.trim() !== '';
26 var isKey = /^"/.test(match) && /:$/.test(match.trimEnd());
27 var isStr = /^"/.test(match) && !isKey;
28
29 if (isPunct) color = punctC;
30 else if (isBool) color = boolC;
31 else if (isNum) color = numC;
32 else if (isKey) color = keyC;
33 else if (isStr) color = strC;
34
35 return '<span style="color:' + color + '">' + match + '</span>';
36 });
37 return html;
38 }
39
40 /* ---- Stats ---- */
41 function countKeys(obj, depth) {
42 depth = depth || 0;
43 if (typeof obj !== 'object' || obj === null) return { keys: 0, depth: depth };
44 var keys = 0; var maxD = depth;
45 var ks = Array.isArray(obj) ? obj : Object.values(obj);
46 ks.forEach(function (v) {
47 if (typeof v === 'object' && v !== null) {
48 var sub = countKeys(v, depth + 1);
49 keys += sub.keys;
50 if (sub.depth > maxD) maxD = sub.depth;
51 }
52 });
53 if (!Array.isArray(obj)) keys += Object.keys(obj).length;
54 return { keys: keys, depth: maxD };
55 }
56
57 /* ---- DOM ---- */
58 function typoCssVarsForEl(typo, prefix, el) {
59 if (!typo || typeof typo !== 'object') return;
60 var map = {
61 family:'font-family', weight:'font-weight', style:'font-style',
62 decoration:'text-decoration', transform:'text-transform',
63 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
64 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
65 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
66 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
67 };
68 Object.keys(map).forEach(function(k) {
69 if (typo[k] !== undefined && typo[k] !== '') {
70 var v = typo[k];
71 if (['sizeDesktop','sizeTablet','sizeMobile'].indexOf(k) !== -1) v = v + (typo.sizeUnit || 'px');
72 else if (['lineHeightDesktop','lineHeightTablet','lineHeightMobile'].indexOf(k) !== -1) v = v + (typo.lineHeightUnit || '');
73 else if (['letterSpacingDesktop','letterSpacingTablet','letterSpacingMobile'].indexOf(k) !== -1) v = v + (typo.letterSpacingUnit || 'px');
74 else if (['wordSpacingDesktop','wordSpacingTablet','wordSpacingMobile'].indexOf(k) !== -1) v = v + (typo.wordSpacingUnit || 'px');
75 el.style.setProperty(prefix + map[k], String(v));
76 }
77 });
78 }
79
80 function initApp(app) {
81 var opts;
82 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch (e) { return; }
83
84 var accent = opts.accentColor || '#0ea5e9';
85 var editorBg = opts.editorBg || '#1e1e2e';
86 var editorText = opts.editorText || '#cdd6f4';
87 var lineNumBg = opts.lineNumBg || '#181825';
88 var lineNumClr = opts.lineNumColor || '#6c7086';
89 var errorBg = opts.errorBg || '#fee2e2';
90 var errorClr = opts.errorColor || '#dc2626';
91 var successClr = opts.successColor || '#16a34a';
92 var cardR = (opts.cardRadius || 16) + 'px';
93 var edR = (opts.editorRadius || 10) + 'px';
94 var maxW = (opts.maxWidth || 800) + 'px';
95 var fs = (opts.editorFontSize|| 13) + 'px';
96 var edH = (opts.editorHeight || 320) + 'px';
97 var indent = opts.indentSize !== undefined ? opts.indentSize : 2;
98
99 app.style.paddingTop = (opts.paddingTop || 60) + 'px';
100 app.style.paddingBottom = (opts.paddingBottom || 60) + 'px';
101 if (opts.sectionBg) app.style.background = opts.sectionBg;
102 typoCssVarsForEl(opts.titleTypo, '--bkbg-jf-tt-', app);
103
104 var card = document.createElement('div');
105 card.className = 'bkbg-jf-card';
106 Object.assign(card.style, { background: opts.cardBg || '#fff', borderRadius: cardR, maxWidth: maxW });
107 app.appendChild(card);
108
109 if (opts.showTitle && opts.title) {
110 var ttl = document.createElement('div'); ttl.className = 'bkbg-jf-title';
111 ttl.textContent = opts.title;
112 if (opts.titleColor) ttl.style.color = opts.titleColor;
113 card.appendChild(ttl);
114 }
115 if (opts.showSubtitle && opts.subtitle) {
116 var sub = document.createElement('div'); sub.className = 'bkbg-jf-subtitle';
117 sub.textContent = opts.subtitle;
118 if (opts.subtitleColor) sub.style.color = opts.subtitleColor;
119 card.appendChild(sub);
120 }
121
122 /* Toolbar */
123 var toolbar = document.createElement('div'); toolbar.className = 'bkbg-jf-toolbar'; card.appendChild(toolbar);
124
125 function mkBtn(label, primary, onClick) {
126 var b = document.createElement('button'); b.className = 'bkbg-jf-btn';
127 b.textContent = label;
128 if (primary) { b.style.background = accent; b.style.color = '#fff'; }
129 else { b.classList.add('bkbg-jf-btn-secondary'); }
130 b.addEventListener('click', onClick);
131 return b;
132 }
133
134 var statusEl = document.createElement('div'); statusEl.className = 'bkbg-jf-status';
135 statusEl.style.marginLeft = 'auto';
136
137 function setStatus(ok, msg) {
138 statusEl.textContent = (ok ? '' : '') + msg;
139 statusEl.style.background = ok ? successClr + '18' : errorBg;
140 statusEl.style.color = ok ? successClr : errorClr;
141 }
142 setStatus(true, 'Ready');
143
144 /* Split: input | output */
145 var split = document.createElement('div'); split.className = 'bkbg-jf-split'; card.appendChild(split);
146
147 function mkEditorPane(label) {
148 var wrap = document.createElement('div');
149 var lbl = document.createElement('div'); lbl.className = 'bkbg-jf-pane-label'; lbl.textContent = label;
150 var ewrap = document.createElement('div'); ewrap.className = 'bkbg-jf-editor-wrap';
151 ewrap.style.background = editorBg; ewrap.style.borderRadius = edR; ewrap.style.height = edH;
152 var inner = document.createElement('div'); inner.className = 'bkbg-jf-editor-inner'; inner.style.height = edH;
153 wrap.appendChild(lbl); ewrap.appendChild(inner); wrap.appendChild(ewrap);
154 return { el: wrap, inner: inner };
155 }
156
157 /* Input pane */
158 var inPane = mkEditorPane('Input JSON');
159 var lnWrapIn = document.createElement('div'); lnWrapIn.className = 'bkbg-jf-line-nums';
160 lnWrapIn.style.background = lineNumBg; lnWrapIn.style.color = lineNumClr; lnWrapIn.style.fontSize = fs;
161 var taIn = document.createElement('textarea'); taIn.className = 'bkbg-jf-textarea';
162 taIn.value = opts.defaultJson || '{}';
163 taIn.style.color = editorText; taIn.style.fontSize = fs;
164 taIn.spellcheck = false; taIn.autocorrect = 'off'; taIn.autocapitalize = 'off';
165 inPane.inner.appendChild(lnWrapIn); inPane.inner.appendChild(taIn);
166
167 /* Output pane */
168 var outPane = mkEditorPane('Formatted Output');
169 var lnWrapOut = document.createElement('div'); lnWrapOut.className = 'bkbg-jf-line-nums';
170 lnWrapOut.style.background = lineNumBg; lnWrapOut.style.color = lineNumClr; lnWrapOut.style.fontSize = fs;
171 var outDiv = document.createElement('div'); outDiv.className = 'bkbg-jf-output';
172 outDiv.style.color = editorText; outDiv.style.fontSize = fs; outDiv.style.background = editorBg; outDiv.style.borderRadius = edR; outDiv.style.height = edH;
173 if (opts.showLineNums !== false) { outPane.inner.appendChild(lnWrapOut); }
174 outPane.inner.appendChild(outDiv);
175
176 split.appendChild(inPane.el);
177 split.appendChild(outPane.el);
178
179 /* Error bar */
180 var errorBar = document.createElement('div'); errorBar.className = 'bkbg-jf-error-bar';
181 errorBar.style.background = errorBg; errorBar.style.color = errorClr; errorBar.style.display = 'none';
182 card.appendChild(errorBar);
183
184 /* Stats bar */
185 var statsBar = document.createElement('div'); statsBar.className = 'bkbg-jf-stats-bar';
186 if (opts.showStats !== false) card.appendChild(statsBar);
187
188 function updateLineNums(text, wrap) {
189 if (opts.showLineNums === false) return;
190 var lines = text.split('\n').length;
191 var html = '';
192 for (var i = 1; i <= lines; i++) html += i + '\n';
193 wrap.textContent = html;
194 }
195
196 function updateStats(text, parsed) {
197 statsBar.innerHTML = '';
198 var size = new Blob([text]).size;
199 var lines = text.split('\n').length;
200 var sizeStr = size < 1024 ? size + ' B' : (size / 1024).toFixed(1) + ' KB';
201 var stats = [{ label: 'Size', val: sizeStr }, { label: 'Lines', val: lines }];
202 if (parsed) {
203 var info = countKeys(parsed, 0);
204 stats.push({ label: 'Keys', val: info.keys });
205 stats.push({ label: 'Depth', val: info.depth });
206 }
207 stats.forEach(function (s) {
208 var d = document.createElement('div'); d.className = 'bkbg-jf-stat-item';
209 d.innerHTML = s.label + ': <strong>' + s.val + '</strong>';
210 statsBar.appendChild(d);
211 });
212 }
213
214 function showError(msg) {
215 errorBar.style.display = 'flex'; errorBar.innerHTML = '⚠️ ' + msg;
216 }
217 function hideError() { errorBar.style.display = 'none'; }
218
219 var currentOutput = '';
220
221 function format() {
222 try {
223 var parsed = JSON.parse(taIn.value);
224 var pretty = JSON.stringify(parsed, null, indent);
225 currentOutput = pretty;
226 outDiv.innerHTML = syntaxHighlight(pretty, {
227 key: opts.keyColor,
228 string: opts.stringColor,
229 number: opts.numberColor,
230 boolNull: opts.boolNullColor,
231 punct: opts.punctColor,
232 text: editorText
233 });
234 updateLineNums(pretty, lnWrapOut);
235 updateStats(pretty, parsed);
236 hideError(); setStatus(true, 'Valid JSON');
237 lnWrapOut.scrollTop = 0; outDiv.scrollTop = 0;
238 } catch (e) {
239 showError(e.message); setStatus(false, 'Invalid JSON');
240 outDiv.innerHTML = ''; lnWrapOut.textContent = ''; statsBar.innerHTML = '';
241 }
242 }
243
244 function minify() {
245 try {
246 var parsed = JSON.parse(taIn.value);
247 var min = JSON.stringify(parsed);
248 currentOutput = min;
249 outDiv.innerHTML = syntaxHighlight(min, { key: opts.keyColor, string: opts.stringColor, number: opts.numberColor, boolNull: opts.boolNullColor, punct: opts.punctColor, text: editorText });
250 updateLineNums(min, lnWrapOut);
251 updateStats(min, parsed);
252 hideError(); setStatus(true, 'Minified');
253 } catch (e) {
254 showError(e.message); setStatus(false, 'Invalid JSON');
255 }
256 }
257
258 function validate() {
259 try {
260 JSON.parse(taIn.value);
261 hideError(); setStatus(true, 'Valid JSON ✓');
262 } catch (e) {
263 showError(e.message); setStatus(false, 'Invalid JSON');
264 }
265 }
266
267 var copiedEl = document.createElement('span'); copiedEl.className = 'bkbg-jf-copied'; copiedEl.textContent = 'Copied!';
268
269 var fmtBtn = mkBtn('Format', true, format);
270 var minBtn = mkBtn('Minify', true, minify);
271 var valBtn = mkBtn('Validate', false, validate);
272 var cpyBtn = mkBtn('📋 Copy', false, function () {
273 var text = currentOutput || taIn.value;
274 navigator.clipboard.writeText(text).then(function () {
275 copiedEl.style.display = 'inline-block';
276 setTimeout(function () { copiedEl.style.display = 'none'; }, 1500);
277 }).catch(function () {});
278 });
279 var clrBtn = mkBtn('Clear', false, function () {
280 taIn.value = ''; outDiv.innerHTML = ''; lnWrapIn.textContent = '1'; lnWrapOut.textContent = '';
281 statsBar.innerHTML = ''; hideError(); setStatus(true, 'Ready'); currentOutput = '';
282 });
283
284 [fmtBtn, minBtn, valBtn, cpyBtn, clrBtn, copiedEl, statusEl].forEach(function (b) { toolbar.appendChild(b); });
285
286 /* Live line nums on input */
287 taIn.addEventListener('input', function () {
288 updateLineNums(taIn.value, lnWrapIn);
289 hideError(); setStatus(true, 'Editing…');
290 currentOutput = '';
291 });
292
293 /* Sync scroll */
294 function syncScroll(from, to) { from.addEventListener('scroll', function () { to.scrollTop = from.scrollTop; }); }
295 syncScroll(taIn, lnWrapIn); syncScroll(lnWrapIn, taIn);
296 syncScroll(outDiv, lnWrapOut); syncScroll(lnWrapOut, outDiv);
297
298 /* Tab key support */
299 taIn.addEventListener('keydown', function (e) {
300 if (e.key === 'Tab') {
301 e.preventDefault();
302 var s = taIn.selectionStart; var end = taIn.selectionEnd;
303 taIn.value = taIn.value.substring(0, s) + ' ' + taIn.value.substring(end);
304 taIn.selectionStart = taIn.selectionEnd = s + 2;
305 }
306 if (e.key === 'Enter' && e.ctrlKey) { e.preventDefault(); format(); }
307 });
308
309 /* Initial */
310 updateLineNums(taIn.value, lnWrapIn);
311 format();
312 }
313
314 document.querySelectorAll('.bkbg-jf-app').forEach(initApp);
315 })();
316