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

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

476 lines 18.4 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 WORD_POOLS = {
5 animals: {
6 easy: ['BEAR','WOLF','FROG','DUCK','LION','GOAT','CRAB','MOLE','NEWT','WREN'],
7 medium: ['PARROT','JAGUAR','DONKEY','RABBIT','MONKEY','TOUCAN','WALRUS','FALCON'],
8 hard: ['ALLIGATOR','CHAMELEON','RHINOCEROS','CHIMPANZEE','CATERPILLAR','HUMMINGBIRD']
9 },
10 countries: {
11 easy: ['PERU','CUBA','IRAN','IRAQ','MALI','LAOS','OMAN','FIJI','CHAD','TOGO'],
12 medium: ['FRANCE','BRAZIL','CANADA','MEXICO','GREECE','NORWAY','SWEDEN','TURKEY'],
13 hard: ['AUSTRALIA','INDONESIA','SWITZERLAND','NETHERLANDS','MOZAMBIQUE','PHILIPPINES']
14 },
15 foods: {
16 easy: ['RICE','BEEF','CORN','MILK','CAKE','TACO','SOUP','PLUM','LIME','BEET'],
17 medium: ['POTATO','CARROT','TOMATO','SALMON','MUFFIN','QUINOA','SHRIMP','PAPAYA'],
18 hard: ['CROISSANT','ARTICHOKE','BRUSCHETTA','QUESADILLA','GUACAMOLE','PROSCIUTTO']
19 },
20 tech: {
21 easy: ['CODE','DATA','WIFI','BYTE','CHIP','NODE','LOOP','BOOT','HTML','JSON'],
22 medium: ['PYTHON','DOCKER','GITHUB','KERNEL','CURSOR','SYNTAX','BINARY','ROUTER'],
23 hard: ['ALGORITHM','JAVASCRIPT','ENCRYPTION','KUBERNETES','BLOCKCHAIN','CRYPTOCURRENCY']
24 }
25 };
26
27 var CATEGORIES = { animals: 'Animals', countries: 'Countries', foods: 'Foods', tech: 'Technology' };
28 var DIFFICULTIES = { easy: 'Easy', medium: 'Medium', hard: 'Hard' };
29
30 // Points per correct answer
31 var PTS = { easy: 10, medium: 20, hard: 35 };
32
33 function shuffle(arr) {
34 var a = arr.slice();
35 for (var i = a.length - 1; i > 0; i--) {
36 var j = Math.floor(Math.random() * (i + 1));
37 var t = a[i]; a[i] = a[j]; a[j] = t;
38 }
39 return a;
40 }
41
42 function scrambleWord(word) {
43 var letters = word.split('');
44 var s = shuffle(letters).join('');
45 if (s === word && word.length > 1) return scrambleWord(word);
46 return s;
47 }
48
49 function initBlock(root) {
50 var opts = {};
51 try { opts = JSON.parse(root.getAttribute('data-opts') || '{}'); } catch (e) {}
52
53 var accentColor = opts.accentColor || '#f59e0b';
54 var correctColor = opts.correctColor || '#22c55e';
55 var wrongColor = opts.wrongColor || '#ef4444';
56 var sectionBg = opts.sectionBg || '#fffbeb';
57 var cardBg = opts.cardBg || '#ffffff';
58 var titleColor = opts.titleColor || '#92400e';
59 var buttonBg = opts.buttonBg || accentColor;
60 var showTimer = opts.showTimer !== false;
61 var showScore = opts.showScore !== false;
62 var showStreak = opts.showStreak !== false;
63 var hintsAllowed = parseInt(opts.hintsAllowed || 3, 10);
64 var timeLimit = parseInt(opts.timeLimit || 60, 10);
65 var fontSize = opts.fontSize || 28;
66 var subtitleSize = opts.subtitleSize || 14;
67
68 // Apply root styling
69 root.style.background = sectionBg;
70 root.style.borderRadius = '16px';
71 root.style.padding = '28px 20px';
72 root.style.textAlign = 'center';
73 root.style.setProperty('--bkbg-wsc-accent', accentColor);
74
75 // Style existing title/subtitle rendered by save()
76 var titleEl = root.querySelector('.bkbg-wsc-title');
77 var subEl = root.querySelector('.bkbg-wsc-subtitle');
78 if (titleEl) { titleEl.style.color = titleColor; titleEl.style.margin = '0 0 4px'; }
79 if (subEl) { subEl.style.color = titleColor + 'bb'; subEl.style.margin = '0 0 16px'; }
80
81 // Game state
82 var currentCat = opts.defaultCategory || 'animals';
83 var currentDiff = opts.defaultDifficulty || 'medium';
84 var wordQueue = [];
85 var currentWord = '';
86 var scrambled = '';
87 var score = 0;
88 var streak = 0;
89 var hintsLeft = hintsAllowed;
90 var timeLeft = timeLimit;
91 var timer = null;
92 var revealed = []; // indices revealed by hints
93 var gameActive = false;
94
95 // Root inner container (after title/subtitle)
96 var inner = document.createElement('div');
97 root.appendChild(inner);
98
99 // ---- DOM refs ----
100 var scoreValEl, streakValEl, timerValEl, timerBarEl;
101 var categoryRow, diffRow;
102 var cardEl, categoryLbl, tilesRow, inputEl, feedbackEl;
103 var hintBtn, skipBtn;
104 var timerBarWrap;
105
106 function buildUI() {
107 inner.innerHTML = '';
108
109 // Stats
110 var stats = document.createElement('div');
111 stats.className = 'bkbg-wsc-stats';
112
113 if (showScore) {
114 var scoreBox = mkStatBox(accentColor);
115 scoreValEl = scoreBox.val;
116 scoreValEl.textContent = score;
117 scoreBox.lbl.textContent = 'Score';
118 stats.appendChild(scoreBox.el);
119 }
120 if (showStreak) {
121 var streakBox = mkStatBox('#f97316');
122 streakValEl = streakBox.val;
123 streakValEl.textContent = '🔥 ' + streak;
124 streakBox.lbl.textContent = 'Streak';
125 stats.appendChild(streakBox.el);
126 }
127 if (showTimer) {
128 var timerBox = mkStatBox('#6366f1');
129 timerValEl = timerBox.val;
130 timerValEl.textContent = timeLeft + 's';
131 timerBox.lbl.textContent = 'Time';
132 stats.appendChild(timerBox.el);
133 }
134 inner.appendChild(stats);
135
136 // Timer bar
137 if (showTimer) {
138 timerBarWrap = document.createElement('div');
139 timerBarWrap.className = 'bkbg-wsc-timer-bar-wrap';
140 timerBarEl = document.createElement('div');
141 timerBarEl.className = 'bkbg-wsc-timer-bar';
142 timerBarEl.style.width = '100%';
143 timerBarEl.style.background = accentColor;
144 timerBarWrap.appendChild(timerBarEl);
145 inner.appendChild(timerBarWrap);
146 }
147
148 // Category tabs
149 categoryRow = document.createElement('div');
150 categoryRow.className = 'bkbg-wsc-tab-row';
151 Object.keys(CATEGORIES).forEach(function (cat) {
152 var btn = document.createElement('button');
153 btn.className = 'bkbg-wsc-tab';
154 btn.textContent = CATEGORIES[cat];
155 styleTab(btn, cat === currentCat);
156 btn.addEventListener('click', function () { currentCat = cat; newRound(); });
157 categoryRow.appendChild(btn);
158 });
159 inner.appendChild(categoryRow);
160
161 // Difficulty tabs
162 diffRow = document.createElement('div');
163 diffRow.className = 'bkbg-wsc-tab-row';
164 Object.keys(DIFFICULTIES).forEach(function (d) {
165 var btn = document.createElement('button');
166 btn.className = 'bkbg-wsc-tab';
167 btn.textContent = DIFFICULTIES[d];
168 styleTab(btn, d === currentDiff);
169 btn.addEventListener('click', function () { currentDiff = d; newRound(); });
170 diffRow.appendChild(btn);
171 });
172 inner.appendChild(diffRow);
173
174 // Card
175 cardEl = document.createElement('div');
176 cardEl.className = 'bkbg-wsc-card';
177 cardEl.style.background = cardBg;
178
179 categoryLbl = document.createElement('div');
180 categoryLbl.className = 'bkbg-wsc-category';
181 categoryLbl.style.color = titleColor + '88';
182 cardEl.appendChild(categoryLbl);
183
184 tilesRow = document.createElement('div');
185 tilesRow.className = 'bkbg-wsc-tiles';
186 cardEl.appendChild(tilesRow);
187
188 inputEl = document.createElement('input');
189 inputEl.type = 'text';
190 inputEl.className = 'bkbg-wsc-input';
191 inputEl.placeholder = 'Type your answer…';
192 inputEl.setAttribute('autocomplete', 'off');
193 inputEl.addEventListener('keydown', function (e) { if (e.key === 'Enter') checkAnswer(); });
194 cardEl.appendChild(inputEl);
195
196 feedbackEl = document.createElement('div');
197 feedbackEl.className = 'bkbg-wsc-feedback';
198 cardEl.appendChild(feedbackEl);
199
200 inner.appendChild(cardEl);
201
202 // Actions
203 var actions = document.createElement('div');
204 actions.className = 'bkbg-wsc-actions';
205
206 var checkBtn = document.createElement('button');
207 checkBtn.className = 'bkbg-wsc-btn';
208 checkBtn.style.background = accentColor;
209 checkBtn.textContent = 'Check';
210 checkBtn.addEventListener('click', checkAnswer);
211 actions.appendChild(checkBtn);
212
213 hintBtn = document.createElement('button');
214 hintBtn.className = 'bkbg-wsc-btn bkbg-wsc-btn-outline';
215 hintBtn.style.borderColor = accentColor;
216 hintBtn.style.color = accentColor;
217 hintBtn.addEventListener('click', useHint);
218 actions.appendChild(hintBtn);
219
220 skipBtn = document.createElement('button');
221 skipBtn.className = 'bkbg-wsc-btn bkbg-wsc-btn-outline';
222 skipBtn.style.borderColor = '#6b7280';
223 skipBtn.style.color = '#6b7280';
224 skipBtn.textContent = 'Skip';
225 skipBtn.addEventListener('click', function () {
226 streak = 0;
227 updateStreak();
228 newRound();
229 });
230 actions.appendChild(skipBtn);
231
232 inner.appendChild(actions);
233 }
234
235 function mkStatBox(color) {
236 var box = document.createElement('div');
237 box.className = 'bkbg-wsc-stat';
238 box.style.background = cardBg;
239 box.style.borderTop = '4px solid ' + color;
240 var val = document.createElement('div');
241 val.className = 'bkbg-wsc-stat-val';
242 val.style.color = color;
243 var lbl = document.createElement('div');
244 lbl.className = 'bkbg-wsc-stat-lbl';
245 lbl.style.color = titleColor;
246 box.appendChild(val);
247 box.appendChild(lbl);
248 return { el: box, val: val, lbl: lbl };
249 }
250
251 function styleTab(btn, active) {
252 btn.style.borderColor = accentColor;
253 btn.style.background = active ? accentColor : 'transparent';
254 btn.style.color = active ? '#fff' : accentColor;
255 }
256
257 function getWordPool() {
258 var pool = (WORD_POOLS[currentCat] || WORD_POOLS['animals'])[currentDiff] || WORD_POOLS['animals']['medium'];
259 return shuffle(pool.slice());
260 }
261
262 function newRound() {
263 clearInterval(timer);
264 if (!wordQueue.length) wordQueue = getWordPool();
265 currentWord = wordQueue.shift();
266 scrambled = scrambleWord(currentWord);
267 revealed = [];
268 hintsLeft = hintsAllowed;
269 timeLeft = timeLimit;
270 gameActive = true;
271
272 // Refresh tabs
273 refreshTabs();
274
275 // Category label
276 if (categoryLbl) {
277 categoryLbl.textContent = CATEGORIES[currentCat] + '' + DIFFICULTIES[currentDiff];
278 }
279
280 // Tiles
281 renderTiles();
282
283 // Input
284 if (inputEl) {
285 inputEl.value = '';
286 inputEl.className = 'bkbg-wsc-input';
287 inputEl.focus();
288 }
289
290 // Feedback
291 if (feedbackEl) feedbackEl.textContent = '';
292
293 // Hint button
294 updateHintBtn();
295
296 // Timer
297 if (showTimer) {
298 updateTimerDisplay();
299 timer = setInterval(function () {
300 if (!gameActive) { clearInterval(timer); return; }
301 timeLeft--;
302 updateTimerDisplay();
303 if (timeLeft <= 0) {
304 clearInterval(timer);
305 gameActive = false;
306 showGameOver();
307 }
308 }, 1000);
309 }
310 }
311
312 function refreshTabs() {
313 if (!categoryRow || !diffRow) return;
314 var catBtns = categoryRow.querySelectorAll('.bkbg-wsc-tab');
315 var catKeys = Object.keys(CATEGORIES);
316 catBtns.forEach(function (btn, i) { styleTab(btn, catKeys[i] === currentCat); });
317
318 var diffBtns = diffRow.querySelectorAll('.bkbg-wsc-tab');
319 var diffKeys = Object.keys(DIFFICULTIES);
320 diffBtns.forEach(function (btn, i) { styleTab(btn, diffKeys[i] === currentDiff); });
321 }
322
323 function renderTiles() {
324 if (!tilesRow) return;
325 tilesRow.innerHTML = '';
326 scrambled.split('').forEach(function (ch, i) {
327 var tile = document.createElement('div');
328 tile.className = 'bkbg-wsc-tile';
329 tile.style.borderColor = accentColor;
330 tile.style.color = accentColor;
331 tile.style.background = sectionBg;
332 // If hint revealed this index in the ANSWER, mark it
333 if (revealed.indexOf(i) !== -1) {
334 tile.classList.add('bkbg-wsc-hint');
335 }
336 tile.textContent = ch;
337 tilesRow.appendChild(tile);
338 });
339 }
340
341 function updateTimerDisplay() {
342 if (timerValEl) timerValEl.textContent = timeLeft + 's';
343 if (timerBarEl) {
344 var pct = (timeLeft / timeLimit) * 100;
345 timerBarEl.style.width = pct + '%';
346 timerBarEl.style.background = pct > 50 ? accentColor : (pct > 25 ? '#f97316' : '#ef4444');
347 }
348 }
349
350 function updateHintBtn() {
351 if (!hintBtn) return;
352 hintBtn.textContent = '💡 Hint (' + hintsLeft + ')';
353 hintBtn.disabled = (hintsLeft <= 0);
354 hintBtn.style.opacity = hintsLeft > 0 ? '1' : '0.4';
355 }
356
357 function updateScore() { if (scoreValEl) scoreValEl.textContent = score; }
358 function updateStreak() { if (streakValEl) streakValEl.textContent = '🔥 ' + streak; }
359
360 function checkAnswer() {
361 if (!gameActive) return;
362 var ans = (inputEl.value || '').trim().toUpperCase();
363 if (!ans) return;
364
365 if (ans === currentWord) {
366 var pts = PTS[currentDiff] || 10;
367 var hintPenalty = (hintsAllowed - hintsLeft) * 2;
368 score += Math.max(1, pts - hintPenalty);
369 streak++;
370 updateScore();
371 updateStreak();
372 feedback('�
373 Correct! +' + Math.max(1, pts - hintPenalty) + ' pts', correctColor);
374 inputEl.className = 'bkbg-wsc-input bkbg-wsc-correct';
375 gameActive = false;
376 clearInterval(timer);
377 setTimeout(function () {
378 inputEl.className = 'bkbg-wsc-input';
379 newRound();
380 }, 900);
381 } else {
382 feedback('❌ Try again!', wrongColor);
383 inputEl.className = 'bkbg-wsc-input bkbg-wsc-wrong';
384 setTimeout(function () { inputEl.className = 'bkbg-wsc-input'; }, 500);
385 streak = 0;
386 updateStreak();
387 }
388 }
389
390 function useHint() {
391 if (hintsLeft <= 0 || !gameActive) return;
392 // Reveal next letter in the answer that hasn't been revealed yet
393 for (var i = 0; i < currentWord.length; i++) {
394 if (revealed.indexOf(i) === -1) {
395 revealed.push(i);
396 hintsLeft--;
397 break;
398 }
399 }
400 // Show revealed letters in tiles
401 // Re-map: scrambled[i] → answer hint overlay
402 // Actually we show a "cheat" version: replace tile at returned index with answer letter
403 var tiles = tilesRow.querySelectorAll('.bkbg-wsc-tile');
404 // Find a tile showing a letter that doesn't match its answer position
405 // Simpler: replace revealed.length-th tile with answer char
406 var ri = revealed[revealed.length - 1];
407 if (tiles[ri]) {
408 tiles[ri].textContent = currentWord[ri];
409 tiles[ri].classList.add('bkbg-wsc-hint');
410 }
411 updateHintBtn();
412 feedback('💡 Hint used! Letter revealed.', accentColor);
413 }
414
415 function feedback(msg, color) {
416 if (!feedbackEl) return;
417 feedbackEl.textContent = msg;
418 feedbackEl.style.color = color;
419 }
420
421 function showGameOver() {
422 clearInterval(timer);
423 inner.innerHTML = '';
424
425 var go = document.createElement('div');
426 go.className = 'bkbg-wsc-gameover';
427
428 var emoji = document.createElement('div');
429 emoji.className = 'bkbg-wsc-gameover-emoji';
430 emoji.textContent = streak >= 5 ? '🏆' : score > 0 ? '🎉' : '�
431 ';
432
433 var t = document.createElement('div');
434 t.className = 'bkbg-wsc-gameover-title';
435 t.style.color = titleColor;
436 t.textContent = 'Time\'s Up!';
437
438 var s = document.createElement('div');
439 s.className = 'bkbg-wsc-gameover-score';
440 s.style.color = accentColor;
441 s.textContent = 'Your score: ' + score + ' pts | Best streak: ' + streak;
442
443 var replayBtn = document.createElement('button');
444 replayBtn.className = 'bkbg-wsc-btn';
445 replayBtn.style.background = accentColor;
446 replayBtn.textContent = 'Play Again';
447 replayBtn.addEventListener('click', function () {
448 score = 0; streak = 0; timeLeft = timeLimit; wordQueue = [];
449 buildUI();
450 newRound();
451 });
452
453 go.appendChild(emoji);
454 go.appendChild(t);
455 go.appendChild(s);
456 go.appendChild(replayBtn);
457 inner.appendChild(go);
458 }
459
460 buildUI();
461 newRound();
462 }
463
464 function init() {
465 document.querySelectorAll('.bkbg-wsc-app').forEach(function (root) {
466 initBlock(root);
467 });
468 }
469
470 if (document.readyState === 'loading') {
471 document.addEventListener('DOMContentLoaded', init);
472 } else {
473 init();
474 }
475 })();
476