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 / regex-tester / frontend.js

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

196 lines 11.0 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 function tryRegex(pattern, flags) {
5 try {
6 if (!pattern) return { re: null, error: null };
7 return { re: new RegExp(pattern, flags), error: null };
8 } catch(e) {
9 return { re: null, error: e.message };
10 }
11 }
12
13 function getMatches(re, str) {
14 if (!re || !str) return [];
15 var matches = [], m;
16 var safeFlags = re.flags;
17 if (!safeFlags.includes('g')) safeFlags += 'g';
18 var r;
19 try { r = new RegExp(re.source, safeFlags); } catch(e) { return []; }
20 var watchdog = 0;
21 while ((m = r.exec(str)) !== null && watchdog++ < 1000) {
22 matches.push({ index: m.index, length: m[0].length, value: m[0], groups: Array.from(m).slice(1) });
23 if (m[0].length === 0) r.lastIndex++;
24 }
25 return matches;
26 }
27
28 function escapeHtml(str) {
29 return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
30 }
31
32 function buildHighlightedHtml(str, matches, matchBg, matchBorder, accentColor) {
33 if (!matches.length) return '<span>' + escapeHtml(str) + '</span>';
34 var html = '';
35 var cursor = 0;
36 matches.forEach(function(m) {
37 if (m.index > cursor) html += escapeHtml(str.slice(cursor, m.index));
38 html += '<mark class="bkbg-rt-match" style="background:' + matchBg + ';border-bottom-color:' + matchBorder + ';" title="Match: ' + escapeHtml(m.value) + '">' + escapeHtml(m.value) + '</mark>';
39 cursor = m.index + m.length;
40 });
41 if (cursor < str.length) html += escapeHtml(str.slice(cursor));
42 return html;
43 }
44
45 var _typoKeys = {
46 family:'font-family', weight:'font-weight', style:'font-style',
47 decoration:'text-decoration', transform:'text-transform',
48 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
49 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
50 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
51 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
52 };
53 function typoCssVarsForEl(el, obj, prefix) {
54 if (!obj || typeof obj !== 'object') return;
55 Object.keys(_typoKeys).forEach(function(k) {
56 var v = obj[k];
57 if (v === undefined || v === '' || v === null) return;
58 if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px');
59 else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || '');
60 else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px');
61 else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px');
62 el.style.setProperty(prefix + _typoKeys[k], String(v));
63 });
64 }
65
66 document.querySelectorAll('.bkbg-rt-app').forEach(function (root) {
67 var opts = {};
68 try { opts = JSON.parse(root.getAttribute('data-opts') || '{}'); } catch(e) {}
69
70 var accentColor = opts.accentColor || '#f59e0b';
71 var matchBg = opts.matchBg || '#fef3c7';
72 var matchBorder = opts.matchBorder || '#f59e0b';
73 var editorBg = opts.editorBg || '#1e1e2e';
74 var editorText = opts.editorText || '#cdd6f4';
75 var cardBg = opts.cardBg || '#ffffff';
76 var sectionBg = opts.sectionBg || '#f9fafb';
77 var titleColor = opts.titleColor || '#111827';
78 var subtitleColor = opts.subtitleColor || '#6b7280';
79 var labelColor = opts.labelColor || '#374151';
80 var errorColor = opts.errorColor || '#dc2626';
81 var showGroups = opts.showGroups !== false;
82 var showCheatsheet = opts.showCheatsheet === true;
83
84 var state = {
85 pattern: opts.defaultPattern || '',
86 flags: opts.defaultFlags || 'gi',
87 test: opts.defaultTestStr || ''
88 };
89
90 var FLAG_INFO = { g: 'global', i: 'ignore case', m: 'multiline', s: 'dotAll' };
91
92 function render() {
93 var result = tryRegex(state.pattern, state.flags);
94 var matches = result.re ? getMatches(result.re, state.test) : [];
95 var hasError = !!result.error;
96 var flagsArr = ['g','i','m','s'];
97
98 root.innerHTML =
99 '<div class="bkbg-rt-card" style="background:' + cardBg + ';max-width:' + (opts.contentMaxWidth||780) + 'px;">' +
100 '<h2 class="bkbg-rt-title" style="color:' + titleColor + ';">' + (opts.title || 'Regex Tester') + '</h2>' +
101 '<p class="bkbg-rt-subtitle" style="color:' + subtitleColor + ';">' + (opts.subtitle || 'Test regular expressions with live match highlighting') + '</p>' +
102
103 '<div class="bkbg-rt-pattern-row">' +
104 '<div class="bkbg-rt-pattern-wrap" style="background:' + editorBg + ';">' +
105 '<span class="bkbg-rt-slash" style="color:' + accentColor + ';">/</span>' +
106 '<input type="text" class="bkbg-rt-pattern-input" id="rt-pattern" value="' + escapeHtml(state.pattern) + '" placeholder="Enter pattern…" style="color:' + editorText + ';background:transparent;" spellcheck="false">' +
107 '<span class="bkbg-rt-slash" style="color:' + accentColor + ';">/</span>' +
108 '<input type="text" class="bkbg-rt-flags-input" id="rt-flags" value="' + escapeHtml(state.flags) + '" placeholder="gi" maxlength="6" style="color:' + accentColor + ';background:transparent;" spellcheck="false">' +
109 '</div>' +
110 (hasError ? '<div class="bkbg-rt-error" style="color:' + errorColor + ';">⚠ ' + escapeHtml(result.error) + '</div>' : '') +
111 '<div class="bkbg-rt-flags-row">' +
112 flagsArr.map(function(f) {
113 var active = state.flags.includes(f);
114 return '<button class="bkbg-rt-flag-btn" data-flag="' + f + '" style="background:' + (active ? accentColor : '#f3f4f6') + ';color:' + (active ? '#fff' : labelColor) + ';"><strong>' + f + '</strong><span>' + FLAG_INFO[f] + '</span></button>';
115 }).join('') +
116 '</div>' +
117 '</div>' +
118
119 '<div class="bkbg-rt-section">' +
120 '<label class="bkbg-rt-label" style="color:' + labelColor + ';">Test String</label>' +
121 '<textarea class="bkbg-rt-textarea" id="rt-test" style="background:' + sectionBg + ';color:' + labelColor + ';min-height:' + (opts.editorHeight||160) + 'px;" spellcheck="false">' + escapeHtml(state.test) + '</textarea>' +
122 '</div>' +
123
124 '<div class="bkbg-rt-stats" style="background:' + sectionBg + ';color:' + labelColor + ';">' +
125 (hasError
126 ? '<span style="color:' + errorColor + ';">⚠ Invalid regex pattern</span>'
127 : '<span>Matches: <strong style="color:' + accentColor + ';">' + matches.length + '</strong></span>' +
128 (matches.length > 0 ? '<span>Groups per match: <strong>' + Math.max(0, matches[0].groups.length) + '</strong></span>' : '') +
129 '<span>Test length: <strong>' + state.test.length + '</strong> chars</span>'
130 ) +
131 '</div>' +
132
133 (!hasError && state.test ?
134 '<div class="bkbg-rt-section">' +
135 '<label class="bkbg-rt-label" style="color:' + labelColor + ';">Highlighted Output</label>' +
136 '<div class="bkbg-rt-output" style="background:' + sectionBg + ';font-size:' + (opts.editorFontSize||14) + 'px;">' +
137 buildHighlightedHtml(state.test, matches, matchBg, matchBorder, accentColor) +
138 '</div>' +
139 '</div>' : '') +
140
141 (showGroups && matches.length > 0 ?
142 '<div class="bkbg-rt-matches">' +
143 '<div class="bkbg-rt-matches-title" style="color:' + labelColor + ';">Matches (' + matches.length + ')</div>' +
144 matches.slice(0, 30).map(function(m, i) {
145 return '<div class="bkbg-rt-match-badge" style="border-color:' + accentColor + '55;background:' + accentColor + '0e;">' +
146 '<span class="bkbg-rt-match-idx" style="color:' + accentColor + ';">#' + (i+1) + '</span>' +
147 '<span class="bkbg-rt-match-val" style="color:' + labelColor + ';">' + escapeHtml(m.value) + '</span>' +
148 (m.groups.some(function(g){ return g !== undefined; })
149 ? '<span class="bkbg-rt-match-groups">Groups: ' + m.groups.map(function(g,gi){ return 'G'+(gi+1)+': '+(g||''); }).join(' | ') + '</span>'
150 : '') +
151 '</div>';
152 }).join('') +
153 (matches.length > 30 ? '<div class="bkbg-rt-more" style="color:#9ca3af;">+ ' + (matches.length-30) + ' more matches</div>' : '') +
154 '</div>' : '') +
155 '</div>';
156
157 var card = root.querySelector('.bkbg-rt-card');
158 if (card) {
159 typoCssVarsForEl(card, opts.titleTypo || {}, '--bkbgrt-tt-');
160 typoCssVarsForEl(card, opts.editorTypo || {}, '--bkbgrt-et-');
161 }
162
163 bindEvents();
164 }
165
166 function bindEvents() {
167 var patInput = root.querySelector('#rt-pattern');
168 var flagInput = root.querySelector('#rt-flags');
169 var testArea = root.querySelector('#rt-test');
170
171 function update() { render(); }
172
173 if (patInput) patInput.addEventListener('input', function() { state.pattern = this.value; update(); });
174 if (flagInput) flagInput.addEventListener('input', function() {
175 state.flags = this.value.replace(/[^gimsuy]/g,''); update();
176 });
177 if (testArea) testArea.addEventListener('input', function() { state.test = this.value; update(); });
178
179 root.querySelectorAll('.bkbg-rt-flag-btn[data-flag]').forEach(function(btn) {
180 btn.addEventListener('click', function() {
181 var f = this.getAttribute('data-flag');
182 if (state.flags.includes(f)) {
183 state.flags = state.flags.replace(f, '');
184 } else {
185 state.flags += f;
186 }
187 update();
188 });
189 });
190 }
191
192 render();
193 });
194
195 })();
196