PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.13
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.13
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 / code-comparison / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.13, at blocks/code-comparison/frontend.js

290 lines 13.7 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 var _typoKeys = {
5 family:'font-family', weight:'font-weight', style:'font-style',
6 transform:'text-transform', decoration:'text-decoration',
7 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
8 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
9 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
10 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
11 };
12 var _typoUnits = { size:'sizeUnit', lineHeight:'lineHeightUnit', letterSpacing:'letterSpacingUnit', wordSpacing:'wordSpacingUnit' };
13 var _typoUnitDefaults = { size:'px', lineHeight:'', letterSpacing:'px', wordSpacing:'px' };
14 function typoCssVarsForEl(el, obj, prefix) {
15 if (!obj || typeof obj !== 'object') return;
16 Object.keys(_typoKeys).forEach(function (k) {
17 var v = obj[k]; if (v === undefined || v === '') return;
18 var prop = _typoKeys[k];
19 var base = k.replace(/Desktop|Tablet|Mobile/, '');
20 var uKey = _typoUnits[base];
21 if (uKey && typeof v === 'number') v = v + (obj[uKey] || _typoUnitDefaults[base] || '');
22 el.style.setProperty(prefix + prop, v);
23 });
24 }
25
26 // ── Token colours ──────────────────────────────────────────────────────────
27 var TOKEN_COLOURS = {
28 plain: null,
29 keyword: '#c792ea',
30 string: '#c3e88d',
31 comment: '#546e7a',
32 number: '#f78c6c',
33 operator: '#89ddff',
34 function: '#82aaff',
35 tag: '#f07178',
36 variable: '#ffcb6b'
37 };
38
39 var LANG_KW = {
40 javascript: 'break case catch class const continue debugger default delete do else enum export extends false finally for from function if import in instanceof let new null of return static super switch this throw true try typeof undefined var void while with yield async await',
41 typescript: 'break case catch class const continue debugger default delete do else enum export extends false finally for from function if import in instanceof interface let namespace new null of return static super switch this throw true try type typeof undefined var void while with yield async await',
42 php: 'echo print class function return if else elseif for foreach while do switch case break continue true false null new public private protected static abstract final try catch finally throw namespace use extends implements interface trait',
43 python: 'False None True and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield',
44 css: 'important',
45 bash: 'if then else elif fi for while do done case esac in function return export readonly local true false',
46 sql: 'SELECT FROM WHERE JOIN LEFT RIGHT INNER OUTER ON UPDATE INSERT INTO VALUES DELETE CREATE DROP TABLE ALTER ADD COLUMN AS GROUP BY ORDER HAVING LIMIT DISTINCT WITH UNION ALL SET AND OR NOT NULL IS IN EXISTS',
47 ruby: 'BEGIN END __ENCODING__ __END__ __FILE__ __LINE__ alias and begin break case class def defined do else elsif end ensure false for if in module next nil not or raise redo rescue retry return self super then true undef unless until when while yield',
48 go: 'break case chan const continue default defer else fallthrough for func go goto if import interface map package range return select struct switch type var nil true false',
49 rust: 'as async await break const continue crate dyn else enum extern false fn for if impl in let loop match mod move mut pub ref return self Self static struct super trait true type unsafe use where while'
50 };
51
52 function tokenize(code, lang) {
53 if (!lang || lang === 'text') return [{ type: 'plain', text: code }];
54 var kw = (LANG_KW[lang] || '').split(' ').filter(Boolean);
55 var rules = [
56 { type: 'string', re: /("""[\s\S]*?"""|'''[\s\S]*?'''|`[\s\S]*?`|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/ },
57 { type: 'comment', re: /(\/\/[^\n]*|\/\*[\s\S]*?\*\/|#[^\n]*|--[^\n]*)/ },
58 { type: 'number', re: /(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b)/ },
59 { type: 'operator', re: /([+\-*/%=<>!&|^~?:;,.()\[\]{}])/ },
60 { type: 'keyword', re: new RegExp('\\b(' + (kw.length ? kw.join('|') : '__NONE__') + ')\\b') },
61 { type: 'function', re: /\b([a-zA-Z_$][a-zA-Z0-9_$]*)(?=\s*\()/ },
62 lang === 'html'
63 ? { type: 'tag', re: /(<\/?\w[^>]*>)/ }
64 : null,
65 lang === 'php'
66 ? { type: 'variable', re: /(\$[a-zA-Z_]\w*)/ }
67 : null
68 ].filter(Boolean);
69
70 var tokens = [];
71 var rest = code;
72 while (rest.length) {
73 var bestIdx = Infinity, bestLen = 0, bestType = 'plain', bestM = null;
74 for (var i = 0; i < rules.length; i++) {
75 var m = rules[i].re.exec(rest);
76 if (m && m.index < bestIdx) {
77 bestIdx = m.index; bestLen = m[0].length; bestType = rules[i].type; bestM = m;
78 }
79 }
80 if (!bestM) { tokens.push({ type: 'plain', text: rest }); break; }
81 if (bestIdx > 0) tokens.push({ type: 'plain', text: rest.slice(0, bestIdx) });
82 tokens.push({ type: bestType, text: rest.slice(bestIdx, bestIdx + bestLen) });
83 rest = rest.slice(bestIdx + bestLen);
84 }
85 return tokens;
86 }
87
88 function buildPaneDOM(code, lang, opts, side, diffLinesArr) {
89 var lines = code.split('\n');
90 var fragment = document.createDocumentFragment();
91
92 lines.forEach(function (line, i) {
93 var lineEl = document.createElement('div');
94 lineEl.className = 'bkbg-cc-line' + (opts.wrapLines ? ' bkbg-cc-wrap-lines' : '');
95
96 var isDiff = diffLinesArr.indexOf(i) !== -1;
97 if (isDiff && side === 'left') lineEl.style.backgroundColor = opts.diffRemBg;
98 if (isDiff && side === 'right') lineEl.style.backgroundColor = opts.diffAddBg;
99
100 if (opts.showLineNumbers) {
101 var numEl = document.createElement('span');
102 numEl.className = 'bkbg-cc-line-num';
103 numEl.style.color = opts.lineNumColor;
104 numEl.textContent = i + 1;
105 lineEl.appendChild(numEl);
106 }
107
108 if (opts.showDiffMarkers && isDiff) {
109 var marker = document.createElement('span');
110 marker.className = 'bkbg-cc-line-marker ' + (side === 'left' ? 'bkbg-cc-removed' : 'bkbg-cc-added');
111 marker.textContent = side === 'left' ? '' : '+';
112 lineEl.appendChild(marker);
113 }
114
115 var codeSpan = document.createElement('span');
116 codeSpan.className = 'bkbg-cc-line-code';
117 codeSpan.style.color = opts.codeColor;
118
119 var tokens = tokenize(line, lang);
120 tokens.forEach(function (tok) {
121 var span = document.createElement('span');
122 span.className = 'bkbg-cc-tok-' + tok.type;
123 var col = TOKEN_COLOURS[tok.type];
124 if (col) span.style.color = col;
125 span.textContent = tok.text;
126 codeSpan.appendChild(span);
127 });
128
129 lineEl.appendChild(codeSpan);
130 fragment.appendChild(lineEl);
131 });
132
133 return fragment;
134 }
135
136 function buildPane(code, lang, label, opts, side, diffLinesArr) {
137 var pane = document.createElement('div');
138 pane.className = 'bkbg-cc-pane';
139 pane.style.backgroundColor = side === 'left' ? opts.bgLeft : opts.bgRight;
140 pane.dataset.side = side;
141 pane.dataset.code = code;
142
143 // Header
144 var header = document.createElement('div');
145 header.className = 'bkbg-cc-header';
146 header.style.backgroundColor = side === 'left' ? opts.headerBgLeft : opts.headerBgRight;
147 header.style.color = opts.headerColor;
148
149 var titleEl = document.createElement('span');
150 titleEl.textContent = label;
151 header.appendChild(titleEl);
152
153 var meta = document.createElement('div');
154 meta.className = 'bkbg-cc-header-meta';
155
156 if (opts.showLanguageBadge) {
157 var badge = document.createElement('span');
158 badge.className = 'bkbg-cc-lang-badge';
159 badge.style.color = opts.headerColor;
160 badge.textContent = lang.toUpperCase();
161 meta.appendChild(badge);
162 }
163 if (opts.showCopyButtons) {
164 var copyBtn = document.createElement('button');
165 copyBtn.className = 'bkbg-cc-copy-btn';
166 copyBtn.style.color = opts.headerColor;
167 copyBtn.textContent = 'Copy';
168 copyBtn.addEventListener('click', function () {
169 navigator.clipboard.writeText(code).then(function () {
170 copyBtn.textContent = 'Copied!';
171 copyBtn.classList.add('bkbg-cc-copied');
172 setTimeout(function () {
173 copyBtn.textContent = 'Copy';
174 copyBtn.classList.remove('bkbg-cc-copied');
175 }, 1600);
176 }).catch(function () {
177 var ta = document.createElement('textarea');
178 ta.value = code;
179 document.body.appendChild(ta);
180 ta.select();
181 document.execCommand('copy');
182 document.body.removeChild(ta);
183 copyBtn.textContent = 'Copied!';
184 setTimeout(function () { copyBtn.textContent = 'Copy'; }, 1600);
185 });
186 });
187 meta.appendChild(copyBtn);
188 }
189
190 header.appendChild(meta);
191 pane.appendChild(header);
192
193 // Code body
194 var body = document.createElement('div');
195 body.className = 'bkbg-cc-code-body';
196 if (opts.maxHeight) body.style.maxHeight = opts.maxHeight + 'px';
197
198 body.appendChild(buildPaneDOM(code, lang, opts, side, diffLinesArr));
199 pane.appendChild(body);
200
201 return pane;
202 }
203
204 function initCodeComparison(appEl) {
205 var raw = appEl.dataset.opts;
206 if (!raw) return;
207 var opts;
208 try { opts = JSON.parse(raw); } catch (e) { return; }
209
210 // Parse diff lines
211 var diffLinesArr = [];
212 if (opts.diffLines && opts.diffLines.trim()) {
213 opts.diffLines.split(',').forEach(function (s) {
214 var n = parseInt(s.trim(), 10);
215 if (!isNaN(n)) diffLinesArr.push(n - 1);
216 });
217 }
218
219 var wrap = document.createElement('div');
220 wrap.className = 'bkbg-cc-wrap bkbg-cc-theme-' + (opts.theme || 'dark');
221 wrap.style.borderRadius = (opts.borderRadius || 12) + 'px';
222 wrap.style.overflow = 'hidden';
223 if (opts.typoCode) typoCssVarsForEl(wrap, opts.typoCode, '--bkbg-cc-cd-');
224
225 var inner = document.createElement('div');
226 inner.className = 'bkbg-cc-inner';
227
228 // Panes
229 var leftPaneEl = buildPane(opts.leftCode || '', opts.leftLang || 'javascript', opts.leftLabel || 'Before', opts, 'left', diffLinesArr);
230 var rightPaneEl = buildPane(opts.rightCode || '', opts.rightLang || 'javascript', opts.rightLabel || 'After', opts, 'right', diffLinesArr);
231
232 var splitL = opts.splitRatio || 50;
233 leftPaneEl.style.width = splitL + '%';
234 leftPaneEl.style.flexShrink = '0';
235 leftPaneEl.style.flexGrow = '0';
236 rightPaneEl.style.width = (100 - splitL) + '%';
237 rightPaneEl.style.flexShrink = '0';
238 rightPaneEl.style.flexGrow = '0';
239
240 inner.appendChild(leftPaneEl);
241
242 // Divider
243 var divider = document.createElement('div');
244 divider.className = 'bkbg-cc-divider';
245
246 if (opts.draggableSplit) {
247 var dragging = false, startX = 0, startL = splitL;
248
249 divider.addEventListener('mousedown', function (e) {
250 dragging = true;
251 startX = e.clientX;
252 startL = parseFloat(leftPaneEl.style.width);
253 divider.classList.add('bkbg-cc-dragging');
254 document.body.style.userSelect = 'none';
255 document.body.style.cursor = 'col-resize';
256 e.preventDefault();
257 });
258 document.addEventListener('mousemove', function (e) {
259 if (!dragging) return;
260 var dx = e.clientX - startX;
261 var totalW = inner.getBoundingClientRect().width;
262 var newL = Math.min(80, Math.max(20, startL + (dx / totalW * 100)));
263 leftPaneEl.style.width = newL + '%';
264 rightPaneEl.style.width = (100 - newL) + '%';
265 });
266 document.addEventListener('mouseup', function () {
267 if (!dragging) return;
268 dragging = false;
269 divider.classList.remove('bkbg-cc-dragging');
270 document.body.style.userSelect = '';
271 document.body.style.cursor = '';
272 });
273 }
274
275 inner.appendChild(divider);
276 inner.appendChild(rightPaneEl);
277 wrap.appendChild(inner);
278
279 appEl.parentNode.replaceChild(wrap, appEl);
280 }
281
282 document.addEventListener('DOMContentLoaded', function () {
283 document.querySelectorAll('.bkbg-cc-app').forEach(initCodeComparison);
284 });
285
286 if (document.readyState === 'complete' || document.readyState === 'interactive') {
287 document.querySelectorAll('.bkbg-cc-app').forEach(initCodeComparison);
288 }
289 })();
290