fix: prevent duplicate PIN copy handlers from multiple modules
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 49s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 28s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 49s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 28s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
- Guard with window._mokoPinBound and data-bound to prevent multi-module pages from attaching N click handlers per badge - Extract bindCopy() for reuse after request-then-copy flow - Ensure title="Click to copy" tooltip on all PIN elements
This commit is contained in:
@@ -181,8 +181,14 @@ class SupportPinHelper
|
||||
{
|
||||
return <<<'JS'
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.mokosuiteclient-pin-copy').forEach(function(el) {
|
||||
(function() {
|
||||
if (window._mokoPinBound) return;
|
||||
window._mokoPinBound = true;
|
||||
|
||||
function bindCopy(el) {
|
||||
if (el.dataset.bound) return;
|
||||
el.dataset.bound = '1';
|
||||
if (!el.title) el.title = 'Click to copy';
|
||||
el.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -194,62 +200,60 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
var orig = textEl.textContent;
|
||||
textEl.textContent = 'Copied!';
|
||||
setTimeout(function() { textEl.textContent = orig; }, 30000);
|
||||
} else {
|
||||
Joomla.renderMessages({message: ['PIN copied: ' + pin]});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.mokosuiteclient-pin-request').forEach(function(el) {
|
||||
el.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (this.dataset.busy) return;
|
||||
this.dataset.busy = '1';
|
||||
var btn = this;
|
||||
var textEl = btn.querySelector('.mokosuiteclient-pin-text');
|
||||
var origHtml = btn.innerHTML;
|
||||
if (textEl) { textEl.textContent = '...'; } else { btn.textContent = '...'; }
|
||||
var fd = new FormData();
|
||||
fd.append(btn.dataset.token, '1');
|
||||
fetch(btn.dataset.url, {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.success && data.pin) {
|
||||
btn.classList.remove('mokosuiteclient-pin-request');
|
||||
btn.classList.add('mokosuiteclient-pin-copy');
|
||||
btn.dataset.pin = data.pin;
|
||||
btn.title = 'Click to copy';
|
||||
if (textEl) {
|
||||
textEl.textContent = data.pin;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.mokosuiteclient-pin-copy').forEach(bindCopy);
|
||||
document.querySelectorAll('.mokosuiteclient-pin-request').forEach(function(el) {
|
||||
if (el.dataset.bound) return;
|
||||
el.dataset.bound = '1';
|
||||
if (!el.title) el.title = 'Request a support PIN';
|
||||
el.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (this.dataset.busy) return;
|
||||
this.dataset.busy = '1';
|
||||
var btn = this;
|
||||
var textEl = btn.querySelector('.mokosuiteclient-pin-text');
|
||||
var origHtml = btn.innerHTML;
|
||||
if (textEl) { textEl.textContent = '...'; } else { btn.textContent = '...'; }
|
||||
var fd = new FormData();
|
||||
fd.append(btn.dataset.token, '1');
|
||||
fetch(btn.dataset.url, {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.success && data.pin) {
|
||||
btn.classList.remove('mokosuiteclient-pin-request');
|
||||
btn.classList.add('mokosuiteclient-pin-copy');
|
||||
btn.dataset.pin = data.pin;
|
||||
btn.title = 'Click to copy';
|
||||
if (textEl) {
|
||||
textEl.textContent = data.pin;
|
||||
} else {
|
||||
btn.className = 'badge bg-dark mokosuiteclient-pin-copy';
|
||||
btn.style = 'font-family:monospace;letter-spacing:0.08em;cursor:pointer;';
|
||||
btn.innerHTML = '<span class="icon-key small me-1" aria-hidden="true"></span><span class="mokosuiteclient-pin-text">' + data.pin + '</span>';
|
||||
}
|
||||
btn.dataset.bound = '';
|
||||
bindCopy(btn);
|
||||
} else {
|
||||
btn.className = 'badge bg-dark mokosuiteclient-pin-copy';
|
||||
btn.style = 'font-family:monospace;letter-spacing:0.08em;cursor:pointer;';
|
||||
btn.innerHTML = '<span class="icon-key small me-1" aria-hidden="true"></span><span class="mokosuiteclient-pin-text">' + data.pin + '</span>';
|
||||
Joomla.renderMessages({error: [data.message || 'Failed to generate PIN']});
|
||||
btn.innerHTML = origHtml;
|
||||
}
|
||||
btn.addEventListener('click', function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
navigator.clipboard.writeText(data.pin).then(function() {
|
||||
var t = btn.querySelector('.mokosuiteclient-pin-text');
|
||||
if (t) { var o = t.textContent; t.textContent = 'Copied!'; setTimeout(function() { t.textContent = o; }, 30000); }
|
||||
else { Joomla.renderMessages({message: ['PIN copied: ' + data.pin]}); }
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Joomla.renderMessages({error: [data.message || 'Failed to generate PIN']});
|
||||
delete btn.dataset.busy;
|
||||
})
|
||||
.catch(function() {
|
||||
Joomla.renderMessages({error: ['Network error']});
|
||||
btn.innerHTML = origHtml;
|
||||
}
|
||||
delete btn.dataset.busy;
|
||||
})
|
||||
.catch(function() {
|
||||
Joomla.renderMessages({error: ['Network error']});
|
||||
btn.innerHTML = origHtml;
|
||||
delete btn.dataset.busy;
|
||||
});
|
||||
delete btn.dataset.busy;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
JS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user