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 / split-flap-display / frontend.js

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

311 lines 13.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 var CHAR_SETS = {
5 alpha: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ',
6 alnum: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
7 full: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?:/-+@#$%&()',
8 numeric: ' 0123456789'
9 };
10
11 var FONT_FAMILIES = {
12 mono: '"Courier New","Lucida Console",monospace',
13 sans: '"Arial","Helvetica Neue",sans-serif',
14 slab: '"Rockwell","Georgia",serif'
15 };
16
17 var _typoKeys = [
18 ['family','font-family'],['weight','font-weight'],['style','font-style'],
19 ['decoration','text-decoration'],['transform','text-transform'],
20 ['sizeDesktop','font-size-d'],['sizeTablet','font-size-t'],['sizeMobile','font-size-m'],
21 ['lineHeightDesktop','line-height-d'],['lineHeightTablet','line-height-t'],['lineHeightMobile','line-height-m'],
22 ['letterSpacingDesktop','letter-spacing-d'],['letterSpacingTablet','letter-spacing-t'],['letterSpacingMobile','letter-spacing-m'],
23 ['wordSpacingDesktop','word-spacing-d'],['wordSpacingTablet','word-spacing-t'],['wordSpacingMobile','word-spacing-m']
24 ];
25 var _sizeKeys = {'font-size-d':1,'font-size-t':1,'font-size-m':1};
26 var _lhKeys = {'line-height-d':1,'line-height-t':1,'line-height-m':1};
27 var _lsKeys = {'letter-spacing-d':1,'letter-spacing-t':1,'letter-spacing-m':1};
28 var _wsKeys = {'word-spacing-d':1,'word-spacing-t':1,'word-spacing-m':1};
29
30 function typoCssVarsForEl(el, obj, prefix) {
31 if (!obj) return;
32 _typoKeys.forEach(function (pair) {
33 var v = obj[pair[0]];
34 if (v === undefined || v === '' || v === null) return;
35 var unit = '';
36 if (_sizeKeys[pair[1]]) unit = obj.sizeUnit || 'px';
37 else if (_lhKeys[pair[1]]) unit = obj.lineHeightUnit || '';
38 else if (_lsKeys[pair[1]]) unit = obj.letterSpacingUnit || 'px';
39 else if (_wsKeys[pair[1]]) unit = obj.wordSpacingUnit || 'px';
40 el.style.setProperty(prefix + pair[1], (typeof v === 'number' ? v + unit : v));
41 });
42 }
43
44 function initSplitFlap(appEl) {
45 var raw = appEl.dataset.opts;
46 if (!raw) return;
47 var opts;
48 try { opts = JSON.parse(raw); } catch (e) { return; }
49
50 var messages = (opts.messages || ['DEPARTURES']).map(function (m) { return m.toUpperCase(); });
51 var charSet = CHAR_SETS[opts.charSet || 'alpha'];
52 var font = FONT_FAMILIES[opts.fontFamily || 'mono'];
53
54 var cw = opts.charWidth || 48;
55 var ch = opts.charHeight || 64;
56 var cg = opts.charGap || 4;
57 var rg = opts.rowGap || 8;
58 var fs = opts.fontSize || 28;
59 var fw = opts.fontWeight || 700;
60 var br = opts.borderRadius || 6;
61 var speed = opts.flipSpeed || 60;
62 var stagger = opts.flipStagger || 30;
63
64 // Pad all messages to the same length (max length)
65 var maxLen = 0;
66 messages.forEach(function (m) { if (m.length > maxLen) maxLen = m.length; });
67 messages = messages.map(function (m) {
68 while (m.length < maxLen) m += ' ';
69 return m;
70 });
71
72 var msgIdx = 0;
73 var flipping = false;
74
75 // Build the board DOM
76 var wrap = document.createElement('div');
77 wrap.className = 'bkbg-sfd-wrap';
78
79 var board = document.createElement('div');
80 board.className = 'bkbg-sfd-board';
81 board.style.backgroundColor = opts.boardBg || '#1a1a1a';
82 board.style.borderRadius = (opts.boardRadius || 12) + 'px';
83 board.style.padding = (opts.boardPadding || 24) + 'px';
84 board.style.display = 'inline-block';
85
86 if (opts.showLabel) {
87 var lbl = document.createElement('div');
88 lbl.className = 'bkbg-sfd-label';
89 lbl.textContent = opts.label || 'FLIGHT INFORMATION';
90 lbl.style.color = opts.labelColor || '#888';
91 board.appendChild(lbl);
92 }
93
94 var rowsEl = document.createElement('div');
95 rowsEl.className = 'bkbg-sfd-rows';
96 rowsEl.style.gap = rg + 'px';
97
98 // Single display row (shows one message at a time)
99 var rowEl = document.createElement('div');
100 rowEl.className = 'bkbg-sfd-row';
101 rowEl.style.gap = cg + 'px';
102
103 // Current displayed chars
104 var currentChars = new Array(maxLen).fill(' ');
105 var charEls = [];
106
107 function makeCharEl(ch) {
108 var tile = document.createElement('div');
109 tile.className = 'bkbg-sfd-char';
110 tile.style.width = cw + 'px';
111 tile.style.height = ch + 'px';
112 tile.style.backgroundColor = ch === ' ' ? 'transparent' : (opts.charBg || '#2a2a2a');
113 tile.style.borderRadius = br + 'px';
114 tile.style.perspective = '400px';
115 tile.style.setProperty('--sfd-speed', speed + 'ms');
116 if (opts.glowColor) tile.style.boxShadow = '0 0 12px ' + opts.glowColor;
117
118 // Top half
119 var topEl = document.createElement('div');
120 topEl.className = 'bkbg-sfd-top';
121 topEl.style.backgroundColor = opts.charBg || '#2a2a2a';
122 var topTxt = document.createElement('span');
123 topTxt.className = 'bkbg-sfd-char-text';
124 topTxt.style.color = opts.charColor || '#f5f5dc';
125 topTxt.style.paddingBottom = '0';
126 topTxt.style.lineHeight = (ch * 2) + 'px'; // so bottom half is cropped
127 topTxt.textContent = ch;
128 topEl.appendChild(topTxt);
129 tile.appendChild(topEl);
130
131 // Bottom half
132 var botEl = document.createElement('div');
133 botEl.className = 'bkbg-sfd-bottom';
134 botEl.style.backgroundColor = opts.flapHalfBg || '#222';
135 var botTxt = document.createElement('span');
136 botTxt.className = 'bkbg-sfd-char-text';
137 botTxt.style.color = opts.charColor || '#f5f5dc';
138 botTxt.style.lineHeight = '0'; // top half crops in
139 botTxt.textContent = ch;
140 botEl.appendChild(botTxt);
141 tile.appendChild(botEl);
142
143 // Divider
144 var divEl = document.createElement('div');
145 divEl.className = 'bkbg-sfd-divider';
146 divEl.style.background = opts.dividerColor || '#111';
147 tile.appendChild(divEl);
148
149 tile._topTxt = topTxt;
150 tile._botTxt = botTxt;
151 tile._topEl = topEl;
152 tile._botEl = botEl;
153
154 return tile;
155 }
156
157 for (var i = 0; i < maxLen; i++) {
158 var tile = makeCharEl(messages[0][i] || ' ');
159 currentChars[i] = messages[0][i] || ' ';
160 charEls.push(tile);
161 rowEl.appendChild(tile);
162 }
163
164 rowsEl.appendChild(rowEl);
165 board.appendChild(rowsEl);
166
167 // Dots (ticker indicator)
168 var dotsEl = null;
169 if (messages.length > 1) {
170 dotsEl = document.createElement('div');
171 dotsEl.className = 'bkbg-sfd-dots';
172 messages.forEach(function (_, di) {
173 var dot = document.createElement('div');
174 dot.className = 'bkbg-sfd-dot' + (di === 0 ? ' bkbg-sfd-dot-active' : '');
175 dot.style.backgroundColor = opts.charColor || '#f5f5dc';
176 dotsEl.appendChild(dot);
177 });
178 board.appendChild(dotsEl);
179 }
180
181 wrap.appendChild(board);
182
183 /* ── Apply typography CSS vars on wrap ──────────────────────── */
184 wrap.style.setProperty('--bksfd-ch-font-family', font);
185 wrap.style.setProperty('--bksfd-ch-font-size-d', fs + 'px');
186 wrap.style.setProperty('--bksfd-ch-font-weight', String(fw));
187 wrap.style.setProperty('--bksfd-lb-font-family', font);
188 typoCssVarsForEl(wrap, opts.charTypo, '--bksfd-ch-');
189 typoCssVarsForEl(wrap, opts.labelTypo, '--bksfd-lb-');
190
191 appEl.parentNode.replaceChild(wrap, appEl);
192
193 // ── Flip animation ─────────────────────────────────────────────
194 function flipCharTo(tile, fromChar, toChar, delay, onDone) {
195 if (fromChar === toChar) { if (onDone) onDone(); return; }
196 setTimeout(function () {
197 var idx = charSet.indexOf(fromChar);
198 if (idx === -1) idx = 0;
199
200 // Create flipping top flap (shows fromChar top, folds down)
201 var flipTopEl = document.createElement('div');
202 flipTopEl.className = 'bkbg-sfd-flip-top';
203 flipTopEl.style.backgroundColor = opts.charBg || '#2a2a2a';
204 flipTopEl.style.setProperty('--sfd-speed', speed + 'ms');
205 var ftTxt = document.createElement('span');
206 ftTxt.className = 'bkbg-sfd-char-text';
207 ftTxt.style.color = opts.charColor || '#f5f5dc';
208 ftTxt.style.lineHeight = (ch * 2) + 'px';
209 ftTxt.textContent = fromChar;
210 flipTopEl.appendChild(ftTxt);
211
212 // Create flipping bottom flap (shows toChar bottom, unfolds)
213 var flipBotEl = document.createElement('div');
214 flipBotEl.className = 'bkbg-sfd-flip-bottom';
215 flipBotEl.style.backgroundColor = opts.flapHalfBg || '#222';
216 flipBotEl.style.setProperty('--sfd-speed', speed + 'ms');
217 var fbTxt = document.createElement('span');
218 fbTxt.className = 'bkbg-sfd-char-text';
219 fbTxt.style.color = opts.charColor || '#f5f5dc';
220 fbTxt.style.lineHeight = '0';
221 fbTxt.textContent = toChar;
222 flipBotEl.appendChild(fbTxt);
223
224 // Update underlying halves immediately to target char
225 tile._topTxt.textContent = toChar;
226 tile._botTxt.textContent = toChar;
227
228 tile.appendChild(flipTopEl);
229 tile.appendChild(flipBotEl);
230
231 // Remove after animation
232 setTimeout(function () {
233 if (flipTopEl.parentNode) flipTopEl.parentNode.removeChild(flipTopEl);
234 if (flipBotEl.parentNode) flipBotEl.parentNode.removeChild(flipBotEl);
235 if (onDone) onDone();
236 }, speed + 20);
237 }, delay);
238 }
239
240 function flipToMessage(target) {
241 if (flipping) return;
242 flipping = true;
243 var remaining = maxLen;
244
245 for (var i = 0; i < maxLen; i++) {
246 (function (ci) {
247 var from = currentChars[ci] || ' ';
248 var to = (target[ci] || ' ');
249 flipCharTo(charEls[ci], from, to, ci * stagger, function () {
250 remaining--;
251 if (remaining === 0) {
252 flipping = false;
253 currentChars = target.split('');
254 }
255 });
256 })(i);
257 }
258 }
259
260 // ── Ticker logic ───────────────────────────────────────────────
261 function doTicker() {
262 if (messages.length < 2) return;
263 msgIdx = (msgIdx + 1) % messages.length;
264 flipToMessage(messages[msgIdx]);
265
266 // Update dots
267 if (dotsEl) {
268 var dots = dotsEl.querySelectorAll('.bkbg-sfd-dot');
269 dots.forEach(function (d, di) {
270 d.classList.toggle('bkbg-sfd-dot-active', di === msgIdx);
271 });
272 }
273 }
274
275 var tickerTimer = null;
276
277 function startTicker() {
278 if (tickerTimer) return;
279 tickerTimer = setInterval(doTicker, opts.tickerDelay || 3000);
280 }
281
282 // ── Scroll trigger ─────────────────────────────────────────────
283 if (opts.flipOnScroll) {
284 var played = false;
285 var observer = new IntersectionObserver(function (entries) {
286 if (entries[0].isIntersecting && !played) {
287 played = true;
288 flipToMessage(messages[0]);
289 if (opts.tickerMode) {
290 setTimeout(startTicker, (opts.tickerDelay || 3000));
291 }
292 }
293 }, { threshold: 0.3 });
294 observer.observe(wrap);
295 } else if (opts.autoPlay || opts.tickerMode) {
296 flipToMessage(messages[0]);
297 if (opts.tickerMode) {
298 setTimeout(startTicker, opts.tickerDelay || 3000);
299 }
300 }
301 }
302
303 document.addEventListener('DOMContentLoaded', function () {
304 document.querySelectorAll('.bkbg-sfd-app').forEach(initSplitFlap);
305 });
306
307 if (document.readyState === 'complete' || document.readyState === 'interactive') {
308 document.querySelectorAll('.bkbg-sfd-app').forEach(initSplitFlap);
309 }
310 })();
311