(function () { 'use strict'; function ready(fn) { if (document.readyState !== 'loading') { fn(); } else { document.addEventListener('DOMContentLoaded', fn); } } function el(tag, cls) { var node = document.createElement(tag); if (cls) { node.className = cls; } return node; } function text(tag, cls, value) { var node = el(tag, cls); node.textContent = value || ''; return node; } function parseLabels(raw) { try { return JSON.parse(raw || '{}') || {}; } catch (e) { return {}; } } function setProgress(root, current, total, strings) { var fill = root.querySelector('[data-meetlet-progress-fill]'); var label = root.querySelector('[data-meetlet-progress-label]'); var pct = total ? Math.round((current / total) * 100) : 0; if (fill) { fill.style.width = pct + '%'; } if (label) { label.textContent = strings.progress .replace('{$a->current}', String(current)) .replace('{$a->total}', String(total)) .replace('{$a->percent}', String(pct)); } } function buildCard(index, total, step, label, strings) { var wrap = el('div', 'meetlet-conntest__card meetlet-conntest__card--pending'); wrap.setAttribute('data-step', step); wrap.setAttribute('data-index', String(index)); var top = el('div', 'meetlet-conntest__rowtop'); top.appendChild(text('span', 'meetlet-conntest__num', String(index + 1))); top.appendChild(el('span', 'meetlet-dot meetlet-dot--pending')); top.appendChild(text('span', 'meetlet-conntest__label', label)); top.appendChild(text('span', 'meetlet-pill meetlet-pill--pending', strings.waiting)); wrap.appendChild(top); wrap.appendChild(text('div', 'meetlet-conntest__detail meetlet-conntest__card-detail', '')); return wrap; } function setCardState(card, state, strings, detail) { card.className = 'meetlet-conntest__card meetlet-conntest__card--' + state; var dot = card.querySelector('.meetlet-dot'); var pill = card.querySelector('.meetlet-pill'); var detailNode = card.querySelector('.meetlet-conntest__card-detail'); if (dot) { dot.className = 'meetlet-dot meetlet-dot--' + state; } if (pill) { pill.className = 'meetlet-pill meetlet-pill--' + state; if (state === 'pending') { pill.textContent = strings.waiting; } else if (state === 'running') { pill.textContent = strings.runningstep; } else if (state === 'ok') { pill.textContent = strings.ok; } else if (state === 'fail') { pill.textContent = strings.fail; } else if (state === 'skipped') { pill.textContent = strings.skipped; } } if (detailNode) { detailNode.textContent = detail || ''; detailNode.style.display = detail ? '' : 'none'; } } function setSubResults(card, subresults, strings) { var wrap = card.querySelector('[data-meetlet-subresults]'); if (!wrap) { wrap = el('div', 'meetlet-conntest__subs'); wrap.setAttribute('data-meetlet-subresults', '1'); card.appendChild(wrap); } wrap.innerHTML = ''; if (!subresults || !subresults.length) { wrap.style.display = 'none'; return; } wrap.style.display = ''; subresults.forEach(function (sub) { var substate = 'fail'; var pillText = strings.fail; if (sub.skipped) { substate = 'skipped'; pillText = strings.skipped; } else if (sub.ok) { substate = 'ok'; pillText = strings.ok; } var subcard = el('div', 'meetlet-conntest__sub meetlet-conntest__sub--' + substate); var top = el('div', 'meetlet-conntest__rowtop'); top.appendChild(el('span', 'meetlet-dot meetlet-dot--' + substate)); top.appendChild(text('span', 'meetlet-conntest__sublabel', sub.label || sub.id || '')); top.appendChild(text('span', 'meetlet-pill meetlet-pill--' + substate, pillText)); subcard.appendChild(top); if (sub.detail) { subcard.appendChild(text('div', 'meetlet-conntest__detail', sub.detail)); } wrap.appendChild(subcard); }); } function sleep(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } ready(function () { var SAVE_ICON = ''; document.querySelectorAll('#adminsettings .offset-sm-3').forEach(function (el) { if (!el.querySelector('button.btn-primary, input.btn-primary[type="submit"]')) { return; } el.classList.remove('offset-sm-3', 'col-sm-3', 'col-md-3'); el.classList.add('col-12', 'meetlet-savebar'); }); document.querySelectorAll( '#adminsettings button.btn-primary[type="submit"],' + ' #adminsettings input.btn-primary[type="submit"]' ).forEach(function (btn) { if (btn.getAttribute('data-meetlet-save-icon') === '1') { return; } btn.setAttribute('data-meetlet-save-icon', '1'); btn.classList.add('meetlet-btn-with-icon'); if (btn.tagName === 'INPUT') { var replacement = document.createElement('button'); replacement.type = 'submit'; replacement.name = btn.name || 'submitbutton'; replacement.value = btn.value || ''; replacement.id = btn.id || ''; replacement.className = btn.className; replacement.setAttribute('data-meetlet-save-icon', '1'); replacement.innerHTML = SAVE_ICON + '' + (btn.value || btn.getAttribute('value') || 'Save changes') + ''; btn.parentNode.replaceChild(replacement, btn); return; } if (btn.querySelector('svg')) { return; } var label = (btn.textContent || '').trim() || 'Save changes'; btn.textContent = ''; btn.insertAdjacentHTML('afterbegin', SAVE_ICON); var span = document.createElement('span'); span.className = 'meetlet-btn-with-icon__label'; span.textContent = label; btn.appendChild(span); }); var root = document.getElementById('meetlet-acb-conntest'); if (!root) { return; } var btn = root.querySelector('[data-meetlet-run-test]'); var grid = root.querySelector('[data-meetlet-test-grid]'); var status = root.querySelector('[data-meetlet-test-status]'); var progressWrap = root.querySelector('[data-meetlet-progress]'); if (!btn || !grid) { return; } var endpoint = root.getAttribute('data-endpoint'); var sesskey = root.getAttribute('data-sesskey'); var steps = (root.getAttribute('data-steps') || 'login,folders,user,sso,cleanup') .split(',') .map(function (s) { return s.trim(); }) .filter(Boolean); var labels = parseLabels(root.getAttribute('data-step-labels')); var strings = { ok: root.getAttribute('data-str-ok') || 'OK', fail: root.getAttribute('data-str-fail') || 'Fail', running: root.getAttribute('data-str-running') || 'Running…', idle: root.getAttribute('data-str-idle') || '', waiting: root.getAttribute('data-str-waiting') || 'Waiting', runningstep: root.getAttribute('data-str-runningstep') || 'Testing', skipped: root.getAttribute('data-str-skipped') || 'Skipped', progress: root.getAttribute('data-str-progress') || 'Step {$a->current} of {$a->total} ({$a->percent}%)' }; btn.addEventListener('click', function () { if (btn.disabled) { return; } btn.disabled = true; grid.innerHTML = ''; if (progressWrap) { progressWrap.hidden = false; } if (status) { status.textContent = strings.running; } var cards = {}; steps.forEach(function (step, index) { var label = labels[step] || step; var card = buildCard(index, steps.length, step, label, strings); setCardState(card, 'pending', strings, ''); grid.appendChild(card); cards[step] = card; }); setProgress(root, 0, steps.length, strings); (async function () { var skipToCleanup = false; var completed = 0; for (var i = 0; i < steps.length; i++) { var step = steps[i]; var card = cards[step]; if (skipToCleanup && step !== 'cleanup') { setCardState(card, 'skipped', strings, ''); completed++; setProgress(root, completed, steps.length, strings); continue; } setCardState(card, 'running', strings, ''); setProgress(root, completed, steps.length, strings); var url = endpoint + (endpoint.indexOf('?') === -1 ? '?' : '&') + 'step=' + encodeURIComponent(step) + '&sesskey=' + encodeURIComponent(sesskey); var response = await fetch(url, { credentials: 'same-origin', headers: { 'Accept': 'application/json' } }); var result = await response.json(); setCardState( card, result.ok ? 'ok' : 'fail', strings, result.detail || '' ); if (result.subresults && result.subresults.length) { setSubResults(card, result.subresults, strings); } completed++; setProgress(root, completed, steps.length, strings); await sleep(220); if (result.abort || (result.done && step !== 'cleanup')) { skipToCleanup = true; if (result.done && step !== 'cleanup') { // Mark remaining non-cleanup as skipped, then stop if no cleanup needed. for (var j = i + 1; j < steps.length; j++) { if (steps[j] === 'cleanup') { continue; } setCardState(cards[steps[j]], 'skipped', strings, ''); completed++; } // Still run cleanup if present later in loop via skipToCleanup. } if (step === 'cleanup') { break; } } } setProgress(root, steps.length, steps.length, strings); })().catch(function (err) { var firstPending = grid.querySelector('.meetlet-conntest__card--pending, .meetlet-conntest__card--running'); if (firstPending) { setCardState(firstPending, 'fail', strings, String(err && err.message ? err.message : err)); } }).finally(function () { btn.disabled = false; if (status) { status.textContent = strings.idle; } }); }); }); })();