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

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

349 lines 13.9 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 THEMES = {
5 animals: ['🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🐨','🐯','🦁','🐸'],
6 food: ['🍕','🍔','🍟','🌮','🌯','🍜','🍣','🍩','🍪','🎂','🍦','🍫'],
7 nature: ['🌸','🌺','🌻','🌹','🌷','🍀','🍁','🍂','🌿','🎋','🌵','🌴'],
8 space: ['🚀','🌍','🌙','','☀️','🪐','🌟','💫','🌌','🔭','👨‍🚀','🛸'],
9 sports: ['','🏀','🏈','','🎾','🏐','🏉','🎱','🏓','🏸','🥊','🎯'],
10 symbols: ['❤️','','🔥','💎','🎵','🌈','💡','🔑','🎭','🎨','🎪','🎲']
11 };
12
13 var DIFFICULTIES = {
14 easy: { pairs: 6, cols: 3 },
15 medium: { pairs: 8, cols: 4 },
16 hard: { pairs: 12, cols: 6 }
17 };
18
19 var STORAGE_KEY = 'bkbg_memory_best';
20
21 function getBest(uid, diff) {
22 try {
23 var raw = localStorage.getItem(STORAGE_KEY + '_' + uid + '_' + diff);
24 return raw ? parseInt(raw) : null;
25 } catch (e) { return null; }
26 }
27
28 function setBest(uid, diff, moves) {
29 try { localStorage.setItem(STORAGE_KEY + '_' + uid + '_' + diff, moves); } catch (e) {}
30 }
31
32 function shuffle(arr) {
33 var a = arr.slice();
34 for (var i = a.length - 1; i > 0; i--) {
35 var j = Math.floor(Math.random() * (i + 1));
36 var tmp = a[i]; a[i] = a[j]; a[j] = tmp;
37 }
38 return a;
39 }
40
41 function formatTime(secs) {
42 var m = Math.floor(secs / 60), s = secs % 60;
43 return m + ':' + (s < 10 ? '0' : '') + s;
44 }
45
46 function initBlock(root) {
47 var opts;
48 try { opts = JSON.parse(root.getAttribute('data-opts')); } catch (e) { return; }
49 var a = opts;
50
51 var _typoKeys = { family:'font-family', weight:'font-weight', style:'font-style', decoration:'text-decoration', transform:'text-transform', sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' };
52 function typoCssVarsForEl(el, typo, prefix) {
53 if (!typo || typeof typo !== 'object') return;
54 Object.keys(_typoKeys).forEach(function (k) {
55 if (typo[k] !== undefined && typo[k] !== '') el.style.setProperty(prefix + _typoKeys[k], typo[k]);
56 });
57 }
58 var uid = root.getAttribute('data-uid') || ('mem' + Math.random().toString(36).slice(2));
59 root.setAttribute('data-uid', uid);
60
61 typoCssVarsForEl(root, a.titleTypo, '--bkbg-mem-tt-');
62 typoCssVarsForEl(root, a.subtitleTypo, '--bkbg-mem-st-');
63
64 var difficulty = a.defaultDifficulty || 'medium';
65 var theme = a.defaultTheme || 'animals';
66 var flipDelay = parseInt(a.flipDelay) || 900;
67
68 /* State */
69 var cards = [];
70 var flipped = [];
71 var matched = [];
72 var moves = 0;
73 var seconds = 0;
74 var timerInterval = null;
75 var locked = false;
76 var gameStarted = false;
77
78 root.innerHTML = '';
79
80 var wrap = document.createElement('div');
81 wrap.className = 'bkbg-mem-wrap';
82 wrap.style.cssText = 'background:' + a.sectionBg + ';max-width:' + a.contentMaxWidth + 'px;margin:0 auto;';
83 root.appendChild(wrap);
84
85 /* Title */
86 if (a.showTitle) {
87 var h = document.createElement('div');
88 h.className = 'bkbg-mem-title';
89 h.style.color = a.titleColor;
90 h.textContent = a.title;
91 wrap.appendChild(h);
92 }
93 if (a.showSubtitle) {
94 var sub = document.createElement('div');
95 sub.className = 'bkbg-mem-subtitle';
96 sub.style.color = a.subtitleColor;
97 sub.textContent = a.subtitle;
98 wrap.appendChild(sub);
99 }
100
101 /* Win banner */
102 var winBanner = document.createElement('div');
103 winBanner.className = 'bkbg-mem-win';
104 winBanner.style.background = a.cardMatched;
105 winBanner.innerHTML = '<span class="bkbg-mem-win-icon">🎉</span>' +
106 '<div class="bkbg-mem-win-title">You Win!</div>' +
107 '<div class="bkbg-mem-win-stats" id="bkbg-mem-winstats-' + uid + '"></div>';
108 wrap.appendChild(winBanner);
109
110 /* Stats */
111 var statsRow = document.createElement('div');
112 statsRow.className = 'bkbg-mem-stats';
113 wrap.appendChild(statsRow);
114
115 function makeStat(id, label) {
116 var box = document.createElement('div');
117 box.className = 'bkbg-mem-stat';
118 box.style.background = a.cardBg;
119 var val = document.createElement('div');
120 val.className = 'bkbg-mem-stat-val';
121 val.id = 'bkbg-mem-' + id + '-' + uid;
122 val.style.color = a.accentColor;
123 val.textContent = id === 'time' ? '0:00' : (id === 'best' ? '' : '0');
124 var lbl = document.createElement('div');
125 lbl.className = 'bkbg-mem-stat-lbl';
126 lbl.style.color = a.subtitleColor;
127 lbl.textContent = label;
128 box.appendChild(val);
129 box.appendChild(lbl);
130 statsRow.appendChild(box);
131 return val;
132 }
133
134 var timeEl = a.showTimer ? makeStat('time', 'Time') : null;
135 var movesEl = a.showMoves ? makeStat('moves', 'Moves') : null;
136 var bestEl = a.showBestScore ? makeStat('best', 'Best') : null;
137
138 function updateBestDisplay() {
139 if (!bestEl) return;
140 var b = getBest(uid, difficulty);
141 bestEl.textContent = b !== null ? b + ' moves' : '';
142 }
143 updateBestDisplay();
144
145 /* Difficulty selectors */
146 var diffRow = document.createElement('div');
147 diffRow.className = 'bkbg-mem-selectors';
148 wrap.appendChild(diffRow);
149
150 ['easy','medium','hard'].forEach(function (d) {
151 var btn = document.createElement('button');
152 btn.className = 'bkbg-mem-sel-btn';
153 btn.textContent = d.charAt(0).toUpperCase() + d.slice(1);
154 setDiffBtnStyle(btn, d === difficulty);
155 btn.addEventListener('click', function () {
156 if (gameStarted && matched.length < cards.length && !confirm('Start new game?')) return;
157 difficulty = d;
158 updateDiffButtons();
159 resetGame();
160 });
161 diffRow.appendChild(btn);
162 });
163
164 /* Theme selectors */
165 var themeNames = Object.keys(THEMES);
166 var themeRow = document.createElement('div');
167 themeRow.className = 'bkbg-mem-selectors';
168 themeRow.style.marginTop = '-4px';
169 wrap.appendChild(themeRow);
170
171 themeNames.forEach(function (t) {
172 var btn = document.createElement('button');
173 btn.className = 'bkbg-mem-sel-btn';
174 btn.style.fontSize = '11px';
175 btn.textContent = t.charAt(0).toUpperCase() + t.slice(1) + ' ' + THEMES[t][0];
176 setThemeBtnStyle(btn, t === theme);
177 btn.addEventListener('click', function () {
178 if (gameStarted && matched.length < cards.length && !confirm('Change theme?')) return;
179 theme = t;
180 updateThemeButtons();
181 resetGame();
182 });
183 themeRow.appendChild(btn);
184 });
185
186 function setDiffBtnStyle(btn, active) {
187 btn.style.cssText = 'border-color:' + a.accentColor + ';background:' + (active ? a.accentColor : 'transparent') + ';color:' + (active ? '#fff' : a.accentColor) + ';padding:5px 14px;border-radius:20px;border:2px solid;font-size:13px;font-weight:600;cursor:pointer;';
188 }
189 function setThemeBtnStyle(btn, active) {
190 btn.style.cssText = 'border-color:' + a.accentColor + ';background:' + (active ? a.accentColor + '22' : 'transparent') + ';color:' + a.accentColor + ';padding:4px 10px;border-radius:20px;border:2px solid;font-size:11px;font-weight:600;cursor:pointer;' + (active ? 'outline:2px solid ' + a.accentColor + ';' : '');
191 }
192
193 function updateDiffButtons() {
194 diffRow.querySelectorAll('.bkbg-mem-sel-btn').forEach(function (btn, i) {
195 setDiffBtnStyle(btn, ['easy','medium','hard'][i] === difficulty);
196 });
197 }
198 function updateThemeButtons() {
199 themeRow.querySelectorAll('.bkbg-mem-sel-btn').forEach(function (btn, i) {
200 setThemeBtnStyle(btn, themeNames[i] === theme);
201 });
202 }
203
204 /* Card grid */
205 var grid = document.createElement('div');
206 grid.className = 'bkbg-mem-grid';
207 wrap.appendChild(grid);
208
209 /* Actions */
210 var actionsEl = document.createElement('div');
211 actionsEl.className = 'bkbg-mem-actions';
212 wrap.appendChild(actionsEl);
213
214 var startBtn = document.createElement('button');
215 startBtn.className = 'bkbg-mem-btn';
216 startBtn.style.background = a.accentColor;
217 startBtn.textContent = '▶ New Game';
218 startBtn.addEventListener('click', function () { resetGame(); });
219 actionsEl.appendChild(startBtn);
220
221 function resetGame() {
222 clearInterval(timerInterval);
223 var cfg = DIFFICULTIES[difficulty] || DIFFICULTIES.medium;
224 var pool = (THEMES[theme] || THEMES.animals).slice(0, cfg.pairs);
225 var all = shuffle(pool.concat(pool));
226
227 cards = [];
228 flipped = [];
229 matched = [];
230 moves = 0;
231 seconds = 0;
232 locked = false;
233 gameStarted = false;
234
235 if (timeEl) timeEl.textContent = '0:00';
236 if (movesEl) movesEl.textContent = '0';
237 updateBestDisplay();
238 winBanner.classList.remove('bkbg-mem-win-show');
239
240 grid.style.gridTemplateColumns = 'repeat(' + cfg.cols + ', 1fr)';
241 grid.innerHTML = '';
242
243 all.forEach(function (emoji, idx) {
244 var card = document.createElement('div');
245 card.className = 'bkbg-mem-card';
246 card.dataset.idx = idx;
247 card.dataset.emoji = emoji;
248
249 var inner = document.createElement('div');
250 inner.className = 'bkbg-mem-card-inner';
251
252 var back = document.createElement('div');
253 back.className = 'bkbg-mem-card-back';
254 back.style.background = a.cardBack;
255 back.textContent = '';
256
257 var front = document.createElement('div');
258 front.className = 'bkbg-mem-card-front';
259 front.style.background = a.cardFront;
260 front.textContent = emoji;
261
262 inner.appendChild(back);
263 inner.appendChild(front);
264 card.appendChild(inner);
265
266 card.addEventListener('click', function () { onCardClick(card); });
267 grid.appendChild(card);
268 cards.push(card);
269 });
270 }
271
272 function startTimer() {
273 if (timerInterval) return;
274 timerInterval = setInterval(function () {
275 seconds++;
276 if (timeEl) timeEl.textContent = formatTime(seconds);
277 }, 1000);
278 }
279
280 function onCardClick(card) {
281 if (locked) return;
282 if (card.classList.contains('bkbg-mem-flipped')) return;
283 if (card.classList.contains('bkbg-mem-matched')) return;
284 if (flipped.length >= 2) return;
285
286 if (!gameStarted) { gameStarted = true; startTimer(); }
287
288 card.classList.add('bkbg-mem-flipped');
289 flipped.push(card);
290
291 if (flipped.length === 2) {
292 moves++;
293 if (movesEl) movesEl.textContent = moves;
294 checkMatch();
295 }
296 }
297
298 function checkMatch() {
299 var a2 = flipped[0].dataset.emoji;
300 var b2 = flipped[1].dataset.emoji;
301 if (a2 === b2) {
302 /* Match */
303 flipped.forEach(function (c) {
304 c.classList.add('bkbg-mem-matched');
305 c.querySelector('.bkbg-mem-card-front').style.background = a.cardMatched;
306 matched.push(c);
307 });
308 flipped = [];
309 if (matched.length === cards.length) { onWin(); }
310 } else {
311 /* No match — shake and flip back */
312 locked = true;
313 flipped.forEach(function (c) { c.classList.add('bkbg-mem-wrong'); });
314 setTimeout(function () {
315 flipped.forEach(function (c) {
316 c.classList.remove('bkbg-mem-flipped', 'bkbg-mem-wrong');
317 });
318 flipped = [];
319 locked = false;
320 }, flipDelay);
321 }
322 }
323
324 function onWin() {
325 clearInterval(timerInterval);
326 timerInterval = null;
327 /* Best score */
328 var prev = getBest(uid, difficulty);
329 if (prev === null || moves < prev) { setBest(uid, difficulty, moves); }
330 updateBestDisplay();
331 var statsNode = root.querySelector('#bkbg-mem-winstats-' + uid);
332 if (statsNode) statsNode.textContent = moves + ' moves · ' + formatTime(seconds);
333 winBanner.classList.add('bkbg-mem-win-show');
334 /* Scroll to banner */
335 winBanner.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
336 }
337
338 resetGame();
339 }
340
341 function init() {
342 document.querySelectorAll('.bkbg-mem-app').forEach(initBlock);
343 }
344
345 document.readyState === 'loading'
346 ? document.addEventListener('DOMContentLoaded', init)
347 : init();
348 })();
349