New to the studio? Your first class is just $10.Start today →
/* MBC · HUB TEST PAGE behaviour · wp-content/uploads/mbc/mbc-test.js Loaded site-wide from the WPCode Footer box; does nothing unless #mbc-test is on the page. Job: point the page at a hub address, load that hub's embed.js, fill the links, and report plainly what rendered and what broke. No styling here — the Oxygen block carries it. */ (function () { 'use strict'; var root = document.getElementById('mbc-test'); if (!root) return; var KEY = 'mbc-test-hub'; var DEFAULT_HUB = 'http://localhost:3100'; var $ = function (id) { return document.getElementById(id); }; var originInput = $('mbct-origin'), status = $('mbct-status'), mixed = $('mbct-mixed'), logEl = $('mbct-log'); var scriptEl = null, loadTimer = null; /* ── log ─────────────────────────────────────────────────────────────── */ var lines = []; function log(msg, cls) { var t = new Date(); var hh = ('0' + t.getHours()).slice(-2) + ':' + ('0' + t.getMinutes()).slice(-2) + ':' + ('0' + t.getSeconds()).slice(-2); lines.push('' + hh + ' ' + (cls ? '' : '') + esc(msg) + (cls ? '' : '')); if (lines.length > 200) lines.shift(); logEl.innerHTML = lines.join('\n'); logEl.scrollTop = logEl.scrollHeight; } function esc(s) { return String(s).replace(/[&<>]/g, function (c) { return { '&': '&', '<': '<', '>': '>' }[c]; }); } function setStatus(text, cls) { status.className = 'status' + (cls ? ' ' + cls : ''); status.lastElementChild.textContent = text; } window.addEventListener('error', function (e) { var src = e.filename ? ' (' + e.filename.replace(/^.*\//, '') + ':' + e.lineno + ')' : ''; log('Page error: ' + (e.message || 'unknown') + src, 'e'); }); window.addEventListener('unhandledrejection', function (e) { var r = e.reason; log('Promise rejected: ' + (r && r.message ? r.message : String(r)), 'e'); }); /* ── hub address ─────────────────────────────────────────────────────── */ function clean(u) { u = (u || '').trim().replace(/\/+$/, ''); if (u && !/^https?:\/\//i.test(u)) u = 'https://' + u; return u; } function currentHub() { var q = null; try { q = new URLSearchParams(location.search).get('hub'); } catch (_) {} var saved = null; try { saved = localStorage.getItem(KEY); } catch (_) {} return clean(q || saved || DEFAULT_HUB); } function remember(u) { try { localStorage.setItem(KEY, u); } catch (_) {} } /* ── links ───────────────────────────────────────────────────────────── */ function fillLinks(hub) { var as = root.querySelectorAll('a[data-path]'); for (var i = 0; i < as.length; i++) as[i].href = hub + as[i].getAttribute('data-path'); } /* ── placeholders turn green when the hub fills them ─────────────────── */ var boxes = root.querySelectorAll('.box[data-kind]'); function checkBoxes() { for (var i = 0; i < boxes.length; i++) { var box = boxes[i], filled = false; var slots = box.querySelectorAll('[data-mbc]'); for (var j = 0; j < slots.length; j++) { var el = slots[j]; if (el.children.length || (el.textContent || '').trim() || el.getAttribute('data-mbc-rendered') !== null) { filled = true; break; } } if (box.getAttribute('data-kind') === 'custom') { var host = $('mbct-custom'); filled = !!(host && host.querySelector('[data-mbc]') && (host.querySelector('[data-mbc]').children.length || (host.textContent || '').trim())); } if (filled && !box.classList.contains('rendered')) { box.classList.add('rendered'); box.querySelector('.st').textContent = 'rendered'; log('Rendered: ' + box.querySelector('.box-head b').textContent, 'g'); } else if (!filled && box.classList.contains('rendered')) { box.classList.remove('rendered'); box.querySelector('.st').textContent = 'waiting'; } } } try { new MutationObserver(function () { checkBoxes(); }).observe(root, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['data-mbc-rendered'] }); } catch (_) {} /* ── load the hub's embed.js ─────────────────────────────────────────── */ function unload() { if (scriptEl && scriptEl.parentNode) scriptEl.parentNode.removeChild(scriptEl); scriptEl = null; if (loadTimer) { clearTimeout(loadTimer); loadTimer = null; } } function loadEmbed(hub, why) { unload(); var pageHttps = location.protocol === 'https:'; var hubHttp = /^http:\/\//i.test(hub); mixed.className = 'hint' + (pageHttps && hubHttp ? ' show' : ''); var url = hub + '/embed.js?t=' + Date.now(); setStatus('Loading ' + hub + ' …'); log((why || 'Loading') + ' ' + url); if (pageHttps && hubHttp) log('Heads up: https page + http hub = Chrome blocks the script unless insecure content is allowed for mbcyoga.com.', 'e'); scriptEl = document.createElement('script'); scriptEl.async = true; scriptEl.src = url; scriptEl.onload = function () { clearTimeout(loadTimer); loadTimer = null; setStatus('embed.js loaded from ' + hub, 'ok'); log('embed.js loaded. Now watching the boxes.', 'g'); // If the runtime exposes a render hook, poke it so a re-load re-draws. try { var api = window.MBC || window.mbc || window.MBCEmbed; if (api && typeof api.render === 'function') { api.render(); log('Called the runtime’s render().'); } } catch (e) { log('render() threw: ' + e.message, 'e'); } setTimeout(checkBoxes, 800); setTimeout(checkBoxes, 3000); }; scriptEl.onerror = function () { clearTimeout(loadTimer); loadTimer = null; setStatus('Could not load embed.js from ' + hub, 'bad'); log('embed.js failed to load. Either the hub isn’t reachable at that address, /embed.js isn’t built yet (404), or the browser blocked it (mixed content / CORS). Check the browser console for the exact reason.', 'e'); }; loadTimer = setTimeout(function () { if (status.className.indexOf('ok') < 0 && status.className.indexOf('bad') < 0) { setStatus('Still waiting on ' + hub + ' …'); log('No answer from the hub after 10s. Is it running?', 'e'); } }, 10000); document.head.appendChild(scriptEl); } /* ── placement ids typed into a box ──────────────────────────────────── */ var idInputs = root.querySelectorAll('input[data-embed-for]'); for (var k = 0; k < idInputs.length; k++) { (function (inp) { var kind = inp.getAttribute('data-embed-for'); var stored = null; try { stored = localStorage.getItem(KEY + ':' + kind); } catch (_) {} if (stored) { inp.value = stored; apply(stored); } function apply(v) { var target = root.querySelector('.box[data-kind="' + kind + '"] [data-mbc="' + kind + '"]'); if (!target) return; target.setAttribute('data-embed', v); target.innerHTML = ''; try { localStorage.setItem(KEY + ':' + kind, v); } catch (_) {} } inp.addEventListener('change', function () { var v = inp.value.trim(); apply(v); log('Set ' + kind + ' placement id to "' + v + '" — reloading widgets.'); loadEmbed(currentHub(), 'Reloading for'); }); })(idInputs[k]); } /* ── paste-any-snippet box ───────────────────────────────────────────── */ $('mbct-render').addEventListener('click', function () { var html = ($('mbct-snippet').value || '').trim(); var host = $('mbct-custom'); if (!html) { log('Nothing to render — paste a snippet first.', 'e'); return; } // Keep it to the hub's placeholders: strip any
Internal · not linked from the site

Hub test page

Every link and every embed the hub can put on the website, on one page, drawn with the real site stylesheet. Point it at a hub address and see what renders.

Waiting…
This page is https and the hub address is http, so Chrome blocks it (“mixed content”). To test against your local hub: open chrome://settings/content/siteDetails?site=https://mbcyoga.com, find Insecure content, set it to Allow, then reload this page. Or use an https address (a tunnel, or the live portal).
Tip: ?hub=https://… on the address bar sets the hub address for that visit. The last address you connected is remembered on this browser.
Links

Pages the website sends people to

Each opens on the hub in a new tab. A dead one means that route isn’t built yet.

Embeds

Widgets the hub draws onto the site

A dashed box is waiting. It turns solid green the moment the hub puts something inside it. Product widgets work from the product’s slug; the others need the placement ID from Settings › Website embeds — type it into the box.

Price cardwaiting
<div data-mbc="price-card" data-item="unlimited"></div>
Buy buttonwaiting
<div data-mbc="buy-button" data-item="unlimited"></div>
Inline pricewaiting

Unlimited membership is a month. Drop-in is .

<span data-mbc="price" data-item="unlimited"></span>
Gift cardswaiting
<div data-mbc="gift-cards" data-embed="…"></div>
Price listwaiting
<div data-mbc="price-list" data-embed="…"></div>
Schedulewaiting
<div data-mbc="schedule" data-embed="…"></div>
Formwaiting
<div data-mbc="form" data-embed="…"></div>
Class typeswaiting
<div data-mbc="class-types" data-embed="…"></div>
Teacherswaiting
<div data-mbc="teachers" data-embed="…"></div>
Lightboxwaiting

A lightbox pops over the page, so nothing shows here. Watch for it after its trigger (a delay, a scroll, or leaving the page).

<div data-mbc="lightbox" data-embed="…"></div>
Paste any snippet from the hubwaiting
Log

What happened

Waiting for mbc-test.js…
If this box never changes, the footer line that loads mbc-test.js isn’t in place yet.
hello world!
hello world!