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

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

335 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global document */
2 (function () {
3 'use strict';
4
5 /* ── Bracket helpers (mirrors index.js logic) ──────────────── */
6 function getRounds(bracketSize) {
7 var rounds = [];
8 var n = bracketSize;
9 while (n >= 2) { rounds.push(n / 2); n = n / 2; }
10 return rounds;
11 }
12
13 function getMatchKey(r, m) { return 'r' + r + '_m' + m; }
14
15 function getMatchTeams(r, m, teams, winners) {
16 if (r === 0) {
17 return [teams[m * 2] || '?', teams[m * 2 + 1] || '?'];
18 }
19 return [
20 winners[getMatchKey(r - 1, m * 2)] || '?',
21 winners[getMatchKey(r - 1, m * 2 + 1)] || '?'
22 ];
23 }
24
25 function setWinner(winners, r, m, team) {
26 var key = getMatchKey(r, m);
27 clearDownstream(winners, r, m);
28 winners[key] = team;
29 }
30
31 function clearDownstream(winners, r, m) {
32 var nextR = r + 1;
33 var nextM = Math.floor(m / 2);
34 var nextKey = getMatchKey(nextR, nextM);
35 if (winners[nextKey] !== undefined) {
36 delete winners[nextKey];
37 clearDownstream(winners, nextR, nextM);
38 }
39 }
40
41 /* ── DOM helpers ─────────────────────────────────────────── */
42 function el(tag, cls, styles) {
43 var d = document.createElement(tag);
44 if (cls) d.className = cls;
45 applyStyles(d, styles);
46 return d;
47 }
48
49 function applyStyles(node, styles) {
50 if (!styles) return;
51 Object.keys(styles).forEach(function (k) { node.style[k] = styles[k]; });
52 }
53
54 function typoCssVarsForEl(typo, prefix, el) {
55 if (!typo) return;
56 if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif");
57 if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight);
58 if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform);
59 if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style);
60 if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration);
61 var su = typo.sizeUnit || 'px';
62 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su);
63 if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su);
64 if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su);
65 var lhu = typo.lineHeightUnit || '';
66 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu);
67 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu);
68 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu);
69 var lsu = typo.letterSpacingUnit || 'px';
70 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu);
71 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu);
72 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu);
73 var wsu = typo.wordSpacingUnit || 'px';
74 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu);
75 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu);
76 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu);
77 }
78
79 /* ── Render one bracket instance ─────────────────────────── */
80 function renderBracket(appEl) {
81 if (appEl.dataset.rendered) return;
82 appEl.dataset.rendered = '1';
83
84 var opts; try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; }
85
86 /* Apply defaults */
87 var defaultTeams = ['Team 1','Team 2','Team 3','Team 4','Team 5','Team 6','Team 7','Team 8'];
88 var o = {
89 bracketTitle: opts.bracketTitle || '',
90 showTitle: opts.showTitle !== false,
91 bracketSize: opts.bracketSize || 8,
92 teams: opts.teams || defaultTeams,
93 winners: opts.winners || {},
94 roundLabels: opts.roundLabels || [],
95 winnerLabel: opts.winnerLabel || '\uD83C\uDFC6 Champion',
96 showSeeds: opts.showSeeds || false,
97 showRoundLabels: opts.showRoundLabels !== false,
98 borderRadius: opts.borderRadius !== undefined ? opts.borderRadius : 8,
99 matchPadding: opts.matchPadding !== undefined ? opts.matchPadding : 8,
100 bgColor: opts.bgColor || '#f8fafc',
101 borderColor: opts.borderColor || '#e2e8f0',
102 matchBg: opts.matchBg || '#ffffff',
103 matchBorderColor: opts.matchBorderColor || '#e2e8f0',
104 teamColor: opts.teamColor || '#1e293b',
105 winnerBg: opts.winnerBg || '#fefce8',
106 winnerColor: opts.winnerColor || '#854d0e',
107 winnerBorderColor: opts.winnerBorderColor || '#fbbf24',
108 loserColor: opts.loserColor || '#94a3b8',
109 connectorColor: opts.connectorColor || '#cbd5e1',
110 roundLabelColor: opts.roundLabelColor || '#64748b',
111 accentColor: opts.accentColor || '#6366f1',
112 titleColor: opts.titleColor || '#1e293b',
113 championBg: opts.championBg || '#fefce8',
114 championBorderColor:opts.championBorderColor || '#f59e0b',
115 championColor: opts.championColor || '#78350f'
116 };
117
118 /* Mutable winners copy (for click interactions) */
119 var winners = JSON.parse(JSON.stringify(o.winners));
120 var rounds = getRounds(o.bracketSize);
121
122 /* ── Outer block ─────────────────────────────────── */
123 var block = el('div', 'bkbg-bk-block');
124 applyStyles(block, {
125 background: o.bgColor,
126 borderColor: o.borderColor,
127 padding: '24px',
128 boxSizing: 'border-box',
129 overflowX: 'auto'
130 });
131 block.style.setProperty('--bkbg-bk-connector', o.connectorColor);
132 typoCssVarsForEl(opts.typoTitle, '--bkbg-bkt-title-', block);
133 typoCssVarsForEl(opts.typoTeam, '--bkbg-bkt-team-', block);
134 typoCssVarsForEl(opts.typoLabel, '--bkbg-bkt-label-', block);
135
136 /* ── Title ───────────────────────────────────────── */
137 if (o.showTitle && o.bracketTitle) {
138 var titleEl = el('div', 'bkbg-bk-title');
139 titleEl.textContent = o.bracketTitle;
140 applyStyles(titleEl, { color: o.titleColor });
141 block.appendChild(titleEl);
142 }
143
144 /* ── Grid ────────────────────────────────────────── */
145 var grid = el('div', 'bkbg-bk-grid');
146 block.appendChild(grid);
147
148 /* Build a round column, returns { col, redraw } */
149 function buildRound(rIdx, matchCount) {
150 var col = el('div', 'bkbg-bk-round');
151
152 /* Round label */
153 if (o.showRoundLabels) {
154 var label = el('div', 'bkbg-bk-round-label');
155 var defaultLabel = rounds.length > 1
156 ? (rIdx === rounds.length - 1 ? 'Final'
157 : rIdx === rounds.length - 2 ? 'Semifinals'
158 : 'Round ' + (rIdx + 1))
159 : 'Match';
160 label.textContent = o.roundLabels[rIdx] || defaultLabel;
161 applyStyles(label, { color: o.roundLabelColor });
162 col.appendChild(label);
163 }
164
165 var matchesContainer = el('div', 'bkbg-bk-matches');
166 col.appendChild(matchesContainer);
167
168 function redraw() {
169 matchesContainer.innerHTML = '';
170 for (var mi = 0; mi < matchCount; mi++) {
171 (function (matchIdx) {
172 var pair = getMatchTeams(rIdx, matchIdx, o.teams, winners);
173 var winKey = getMatchKey(rIdx, matchIdx);
174 var curWinner = winners[winKey];
175 var isLastRound = (rIdx === rounds.length - 1);
176
177 var matchCard = el('div', 'bkbg-bk-match');
178 applyStyles(matchCard, {
179 background: o.matchBg,
180 borderColor: o.matchBorderColor,
181 borderRadius: o.borderRadius + 'px'
182 });
183
184 pair.forEach(function (teamName, tIdx) {
185 var isWinner = curWinner && curWinner === teamName;
186 var isLoser = curWinner && curWinner !== teamName;
187 var isUnknown = teamName === '?';
188 var isAvailable = !isUnknown && !curWinner;
189
190 var teamRow = el('div', 'bkbg-bk-team'
191 + (isWinner ? ' is-winner' : '')
192 + (isLoser ? ' is-loser' : '')
193 + (isAvailable ? ' is-available' : ''));
194
195 if (isWinner) {
196 applyStyles(teamRow, {
197 background: o.winnerBg,
198 color: o.winnerColor,
199 borderColor: o.winnerBorderColor,
200 padding: o.matchPadding + 'px 10px',
201 cursor: 'default'
202 });
203 var icon = el('span', 'bkbg-bk-win-icon');
204 icon.textContent = '\u2605';
205 icon.style.color = o.winnerBorderColor;
206 teamRow.appendChild(icon);
207 } else if (isLoser) {
208 applyStyles(teamRow, {
209 color: o.loserColor,
210 padding: o.matchPadding + 'px 10px',
211 cursor: 'default'
212 });
213 } else {
214 applyStyles(teamRow, {
215 color: isUnknown ? o.loserColor : o.teamColor,
216 padding: o.matchPadding + 'px 10px',
217 cursor: (isAvailable && !isUnknown && !isLastRound === false) ? 'pointer' : (isUnknown ? 'default' : 'pointer')
218 });
219 }
220
221 /* Seed number */
222 if (o.showSeeds && rIdx === 0) {
223 var seed = el('span', '');
224 seed.textContent = '#' + (matchIdx * 2 + tIdx + 1);
225 applyStyles(seed, {
226 fontSize: '10px',
227 color: o.loserColor,
228 flexShrink: '0',
229 minWidth: '24px'
230 });
231 teamRow.appendChild(seed);
232 }
233
234 var nameSpan = el('span', 'bkbg-bk-team-name');
235 nameSpan.textContent = teamName;
236 teamRow.appendChild(nameSpan);
237
238 /* Click to set winner */
239 if (!isUnknown && !isWinner) {
240 teamRow.addEventListener('click', function () {
241 setWinner(winners, rIdx, matchIdx, teamName);
242 redrawAll();
243 });
244 } else if (isWinner) {
245 /* Click winner again = undo */
246 teamRow.addEventListener('click', function () {
247 clearDownstream(winners, rIdx, matchIdx);
248 delete winners[winKey];
249 redrawAll();
250 });
251 }
252
253 matchCard.appendChild(teamRow);
254 });
255
256 matchesContainer.appendChild(matchCard);
257 })(mi);
258 }
259 }
260
261 return { col: col, redraw: redraw };
262 }
263
264 /* Build champion column */
265 function buildChampionCol() {
266 var col = el('div', 'bkbg-bk-champion-col');
267
268 /* Placeholder for label alignment */
269 if (o.showRoundLabels) {
270 var spacer = el('div', 'bkbg-bk-round-label');
271 spacer.textContent = '\u00A0';
272 col.appendChild(spacer);
273 }
274
275 var matches = el('div', 'bkbg-bk-matches');
276 col.appendChild(matches);
277
278 function redraw() {
279 matches.innerHTML = '';
280 var lastRoundKey = getMatchKey(rounds.length - 1, 0);
281 var champion = winners[lastRoundKey] || '\u2014';
282
283 var champBox = el('div', 'bkbg-bk-champion');
284 applyStyles(champBox, {
285 background: o.championBg,
286 borderColor: o.championBorderColor,
287 borderRadius: o.borderRadius + 'px',
288 color: o.championColor
289 });
290
291 var champLabel = el('div', 'bkbg-bk-champion-label');
292 champLabel.textContent = o.winnerLabel;
293 champBox.appendChild(champLabel);
294
295 var champName = el('div', 'bkbg-bk-champion-name');
296 champName.textContent = champion;
297 champBox.appendChild(champName);
298
299 matches.appendChild(champBox);
300 }
301
302 return { col: col, redraw: redraw };
303 }
304
305 /* Build all round columns */
306 var roundCols = rounds.map(function (matchCount, rIdx) {
307 return buildRound(rIdx, matchCount);
308 });
309 var championCol = buildChampionCol();
310
311 /* Append columns to grid */
312 roundCols.forEach(function (rc) { grid.appendChild(rc.col); });
313 grid.appendChild(championCol.col);
314
315 /* Draw all */
316 function redrawAll() {
317 roundCols.forEach(function (rc) { rc.redraw(); });
318 championCol.redraw();
319 }
320 redrawAll();
321
322 /* Insert block before the hidden data element */
323 appEl.parentNode.insertBefore(block, appEl);
324 appEl.style.display = 'none';
325 }
326
327 /* ── Bootstrap ───────────────────────────────────────────── */
328 function init() {
329 document.querySelectorAll('.bkbg-bk-app').forEach(renderBracket);
330 }
331
332 if (document.readyState !== 'loading') { init(); }
333 else { document.addEventListener('DOMContentLoaded', init); }
334 })();
335