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

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

332 lines 15.2 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 /* ── Typography CSS-var helper ── */
5 var _typoKeys = {
6 family:'font-family', weight:'font-weight', style:'font-style',
7 transform:'text-transform', decoration:'text-decoration',
8 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
9 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
10 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
11 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
12 };
13 var _typoUnits = { size:'sizeUnit', lineHeight:'lineHeightUnit', letterSpacing:'letterSpacingUnit', wordSpacing:'wordSpacingUnit' };
14 var _typoUnitDefaults = { size:'px', lineHeight:'', letterSpacing:'px', wordSpacing:'px' };
15 function typoCssVarsForEl(el, obj, prefix) {
16 if (!obj || typeof obj !== 'object') return;
17 Object.keys(_typoKeys).forEach(function (k) {
18 var v = obj[k]; if (v === undefined || v === '') return;
19 var prop = _typoKeys[k];
20 var base = k.replace(/Desktop|Tablet|Mobile/, '');
21 var uKey = _typoUnits[base];
22 if (uKey && typeof v === 'number') v = v + (obj[uKey] || _typoUnitDefaults[base] || '');
23 el.style.setProperty(prefix + prop, v);
24 });
25 }
26
27 /* ── Theme presets ── */
28 var THEMES = {
29 'dark': { headerBg: '#1e1e2e', codeBg: '#13131f', codeColor: '#e0e0f0', activeBg: '#6366f1', activeColor: '#ffffff', idleBg: '#2d2d3f', idleColor: '#a0a0c0', lineNum: '#4a4a6a', copyBg: '#2d2d3f', copyColor: '#a0a0c0' },
30 'monokai': { headerBg: '#272822', codeBg: '#1e1f1c', codeColor: '#f8f8f2', activeBg: '#a6e22e', activeColor: '#272822', idleBg: '#3e3d32', idleColor: '#75715e', lineNum: '#49483e', copyBg: '#3e3d32', copyColor: '#75715e' },
31 'dracula': { headerBg: '#21222c', codeBg: '#282a36', codeColor: '#f8f8f2', activeBg: '#bd93f9', activeColor: '#282a36', idleBg: '#343746', idleColor: '#6272a4', lineNum: '#44475a', copyBg: '#343746', copyColor: '#6272a4' },
32 'github-dark': { headerBg: '#161b22', codeBg: '#0d1117', codeColor: '#c9d1d9', activeBg: '#388bfd', activeColor: '#ffffff', idleBg: '#21262d', idleColor: '#8b949e', lineNum: '#30363d', copyBg: '#21262d', copyColor: '#8b949e' },
33 'nord': { headerBg: '#2e3440', codeBg: '#242933', codeColor: '#d8dee9', activeBg: '#88c0d0', activeColor: '#2e3440', idleBg: '#3b4252', idleColor: '#4c566a', lineNum: '#434c5e', copyBg: '#3b4252', copyColor: '#4c566a' },
34 'light': { headerBg: '#f3f4f6', codeBg: '#ffffff', codeColor: '#1f2328', activeBg: '#6366f1', activeColor: '#ffffff', idleBg: '#e5e7eb', idleColor: '#374151', lineNum: '#9ca3af', copyBg: '#e5e7eb', copyColor: '#374151' },
35 'github-light': { headerBg: '#f6f8fa', codeBg: '#ffffff', codeColor: '#1f2328', activeBg: '#0969da', activeColor: '#ffffff', idleBg: '#eaeef2', idleColor: '#57606a', lineNum: '#8c959f', copyBg: '#eaeef2', copyColor: '#57606a' },
36 'solarized-light': { headerBg: '#eee8d5', codeBg: '#fdf6e3', codeColor: '#073642', activeBg: '#268bd2', activeColor: '#fdf6e3', idleBg: '#e0dac8', idleColor: '#586e75', lineNum: '#93a1a1', copyBg: '#e0dac8', copyColor: '#586e75' }
37 };
38
39 function esc(str) {
40 return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
41 }
42
43 function applyCSS(el, styles) {
44 Object.keys(styles).forEach(function (k) { el.style[k] = styles[k]; });
45 }
46
47 function init() {
48 document.querySelectorAll('.bkbg-ctabs-app').forEach(function (appEl) {
49 if (appEl.dataset.rendered) return;
50 appEl.dataset.rendered = '1';
51
52 var opts;
53 try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; }
54
55 var o = Object.assign({
56 tabs: [],
57 theme: 'dark',
58 tabStyle: 'pills',
59 tabAlign: 'left',
60 showLineNumbers: true,
61 showCopyButton: true,
62 showFileName: true,
63 showLangBadge: true,
64 fontSize: 13,
65 lineHeight: 1.65,
66 maxHeight: 0,
67 borderRadius: 10,
68 paddingTop: 0,
69 paddingBottom: 0,
70 bgColor: '',
71 headerBg: '',
72 codeBg: '',
73 tabActiveBg: '',
74 tabActiveColor: '',
75 tabIdleBg: '',
76 tabIdleColor: '',
77 lineNumColor: '',
78 copyBg: '',
79 copyColor: ''
80 }, opts);
81
82 var themeColors = THEMES[o.theme] || THEMES['dark'];
83
84 /* Resolve final colors (user override → theme default) */
85 function col(userVal, themeKey) {
86 return (userVal && userVal !== '') ? userVal : themeColors[themeKey];
87 }
88
89 var hBg = col(o.headerBg, 'headerBg');
90 var cBg = col(o.codeBg, 'codeBg');
91 var cColor = themeColors.codeColor;
92 var aTabBg = col(o.tabActiveBg, 'activeBg');
93 var aTabC = col(o.tabActiveColor, 'activeColor');
94 var iTabBg = col(o.tabIdleBg, 'idleBg');
95 var iTabC = col(o.tabIdleColor, 'idleColor');
96 var lnC = col(o.lineNumColor, 'lineNum');
97 var cpBg = col(o.copyBg, 'copyBg');
98 var cpC = col(o.copyColor, 'copyColor');
99
100 var tabs = o.tabs.length ? o.tabs :
101 [{ label: 'example.js', lang: 'javascript', fileName: 'example.js', code: 'console.log("Hello, world!");' }];
102
103 var activeIdx = 0;
104
105 /* ── Outer wrap ── */
106 var wrap = document.createElement('div');
107 wrap.className = 'bkbg-ctabs-wrap theme-' + o.theme;
108 wrap.dataset.tabStyle = o.tabStyle;
109 applyCSS(wrap, {
110 borderRadius: o.borderRadius + 'px',
111 overflow: 'hidden',
112 paddingTop: o.paddingTop + 'px',
113 paddingBottom: o.paddingBottom + 'px',
114 background: o.bgColor || ''
115 });
116
117 typoCssVarsForEl(wrap, o.typoCode, '--bkbg-ctabs-cd-');
118
119 /* ── Chrome header ── */
120 var chrome = document.createElement('div');
121 chrome.className = 'bkbg-ctabs-chrome';
122 applyCSS(chrome, { background: hBg });
123
124 /* Window dots */
125 var dotsEl = document.createElement('div');
126 dotsEl.className = 'bkbg-ctabs-dots';
127 [['#ff5f57'], ['#febc2e'], ['#28c840']].forEach(function (c) {
128 var d = document.createElement('div');
129 d.className = 'bkbg-ctabs-dot';
130 d.style.background = c[0];
131 dotsEl.appendChild(d);
132 });
133 chrome.appendChild(dotsEl);
134
135 /* Tab bar */
136 var tabBar = document.createElement('div');
137 tabBar.className = 'bkbg-ctabs-tabbar is-' + o.tabAlign;
138
139 /* Tab buttons */
140 var tabBtns = [];
141
142 function updateTabs(nextIdx) {
143 tabBtns.forEach(function (btn, i) {
144 var isActive = i === nextIdx;
145 applyCSS(btn, {
146 background: o.tabStyle === 'underline' ? 'transparent' : (isActive ? aTabBg : iTabBg),
147 color: isActive ? aTabC : iTabC,
148 fontWeight: isActive ? '600' : '400',
149 borderBottom: o.tabStyle === 'underline' ? ('2px solid ' + (isActive ? aTabBg : 'transparent')) : ''
150 });
151 btn.setAttribute('aria-selected', isActive ? 'true' : 'false');
152 });
153 panels.forEach(function (panel, i) {
154 panel.classList.toggle('is-active', i === nextIdx);
155 });
156
157 /* Update copy button data */
158 copyBtn.dataset.code = tabs[nextIdx].code || '';
159 activeIdx = nextIdx;
160 }
161
162 tabs.forEach(function (tab, idx) {
163 var btn = document.createElement('button');
164 btn.className = 'bkbg-ctabs-tab';
165 btn.setAttribute('role', 'tab');
166 btn.setAttribute('aria-selected', idx === 0 ? 'true' : 'false');
167 btn.textContent = tab.label || ('Tab ' + (idx + 1));
168 applyCSS(btn, {
169 background: o.tabStyle === 'underline' ? 'transparent' : (idx === 0 ? aTabBg : iTabBg),
170 color: idx === 0 ? aTabC : iTabC,
171 fontWeight: idx === 0 ? '600' : '400',
172 fontSize: '12px',
173 borderBottom: o.tabStyle === 'underline' ? ('2px solid ' + (idx === 0 ? aTabBg : 'transparent')) : ''
174 });
175 btn.addEventListener('click', function () { updateTabs(idx); });
176 tabBar.appendChild(btn);
177 tabBtns.push(btn);
178 });
179
180 chrome.appendChild(tabBar);
181
182 /* Filename */
183 var fileEl = null;
184 if (o.showFileName) {
185 fileEl = document.createElement('span');
186 fileEl.className = 'bkbg-ctabs-filename';
187 fileEl.style.color = iTabC;
188 fileEl.textContent = tabs[0].fileName || '';
189 chrome.appendChild(fileEl);
190 }
191
192 /* Lang badge */
193 var langEl = null;
194 if (o.showLangBadge) {
195 langEl = document.createElement('span');
196 langEl.className = 'bkbg-ctabs-lang';
197 langEl.style.color = iTabC;
198 langEl.textContent = (tabs[0].lang || 'text').toUpperCase();
199 chrome.appendChild(langEl);
200 }
201
202 wrap.appendChild(chrome);
203
204 /* ── Code body ── */
205 var codeBody = document.createElement('div');
206 codeBody.className = 'bkbg-ctabs-body';
207 applyCSS(codeBody, {
208 background: cBg,
209 borderRadius: '0 0 ' + o.borderRadius + 'px ' + o.borderRadius + 'px'
210 });
211 if (o.maxHeight > 0) {
212 applyCSS(codeBody, { maxHeight: o.maxHeight + 'px', overflowY: 'auto' });
213 }
214
215 /* Copy button */
216 var copyBtn = document.createElement('button');
217 copyBtn.className = 'bkbg-ctabs-copy';
218 copyBtn.textContent = 'Copy';
219 copyBtn.dataset.code = tabs[0].code || '';
220 applyCSS(copyBtn, { background: cpBg, color: cpC });
221 copyBtn.addEventListener('click', function () {
222 var code = copyBtn.dataset.code;
223 if (navigator.clipboard && navigator.clipboard.writeText) {
224 navigator.clipboard.writeText(code).then(function () {
225 copyBtn.textContent = 'Copied!';
226 copyBtn.classList.add('is-copied');
227 setTimeout(function () {
228 copyBtn.textContent = 'Copy';
229 copyBtn.classList.remove('is-copied');
230 }, 2000);
231 }).catch(function () {});
232 } else {
233 /* Fallback */
234 var ta = document.createElement('textarea');
235 ta.value = code;
236 ta.style.position = 'absolute';
237 ta.style.left = '-9999px';
238 document.body.appendChild(ta);
239 ta.select();
240 try { document.execCommand('copy'); } catch (e) {}
241 document.body.removeChild(ta);
242 copyBtn.textContent = 'Copied!';
243 setTimeout(function () { copyBtn.textContent = 'Copy'; }, 2000);
244 }
245 });
246
247 if (o.showCopyButton) codeBody.appendChild(copyBtn);
248
249 /* Inner scroll wrapper */
250 var inner = document.createElement('div');
251 inner.className = 'bkbg-ctabs-inner';
252
253 /* ── Panels ── */
254 var panels = [];
255
256 tabs.forEach(function (tab, idx) {
257 var panel = document.createElement('div');
258 panel.className = 'bkbg-ctabs-panel' + (idx === 0 ? ' is-active' : '');
259
260 var lines = (tab.code || '').split('\n');
261 lines.forEach(function (line, li) {
262 var row = document.createElement('div');
263 row.className = 'bkbg-ctabs-line';
264
265 if (o.showLineNumbers) {
266 var numEl = document.createElement('span');
267 numEl.className = 'bkbg-ctabs-linenum';
268 numEl.textContent = li + 1;
269 applyCSS(numEl, {
270 color: lnC
271 });
272 row.appendChild(numEl);
273 }
274
275 var codeEl = document.createElement('span');
276 codeEl.className = 'bkbg-ctabs-code';
277 codeEl.textContent = line;
278 applyCSS(codeEl, {
279 color: cColor
280 });
281 row.appendChild(codeEl);
282 panel.appendChild(row);
283 });
284
285 inner.appendChild(panel);
286 panels.push(panel);
287 });
288
289 codeBody.appendChild(inner);
290 wrap.appendChild(codeBody);
291
292 /* ── Tab switching also updates filename / lang ── */
293 tabBtns.forEach(function (btn, idx) {
294 btn.addEventListener('click', function () {
295 if (fileEl) fileEl.textContent = tabs[idx].fileName || '';
296 if (langEl) langEl.textContent = (tabs[idx].lang || 'text').toUpperCase();
297 });
298 });
299
300 /* ── Keyboard navigation (arrow keys) ── */
301 tabBar.setAttribute('role', 'tablist');
302 tabBar.addEventListener('keydown', function (e) {
303 var key = e.key;
304 if (key === 'ArrowRight') {
305 e.preventDefault();
306 var next = (activeIdx + 1) % tabs.length;
307 updateTabs(next);
308 tabBtns[next].focus();
309 if (fileEl) fileEl.textContent = tabs[next].fileName || '';
310 if (langEl) langEl.textContent = (tabs[next].lang || 'text').toUpperCase();
311 } else if (key === 'ArrowLeft') {
312 e.preventDefault();
313 var prev = (activeIdx - 1 + tabs.length) % tabs.length;
314 updateTabs(prev);
315 tabBtns[prev].focus();
316 if (fileEl) fileEl.textContent = tabs[prev].fileName || '';
317 if (langEl) langEl.textContent = (tabs[prev].lang || 'text').toUpperCase();
318 }
319 });
320
321 appEl.parentNode.insertBefore(wrap, appEl);
322 appEl.style.display = 'none';
323 });
324 }
325
326 if (document.readyState !== 'loading') {
327 init();
328 } else {
329 document.addEventListener('DOMContentLoaded', init);
330 }
331 })();
332