imaging: copy button HTTP fallback (execCommand)
navigator.clipboard.writeText is gated on isSecureContext - HTTPS
or localhost only. PXE dashboard is served over plain HTTP
(10.9.100.1:9009) so the API was undefined and the chain threw
before .catch fired - user saw nothing. Wrap clipboard write in
copyText() that prefers the modern API and falls back to the
classic invisible-textarea + document.execCommand('copy') path
which works on HTTP. Visual flash logic moved into flashCopied()
for reuse.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -138,14 +138,36 @@ Content-Type: application/json
|
||||
|
||||
{% block extra_scripts %}
|
||||
<script>
|
||||
document.addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('.copy-btn');
|
||||
if (!btn) return;
|
||||
var text = btn.getAttribute('data-copy-text');
|
||||
if (!text) return;
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
var origText = btn.dataset.origText || btn.textContent;
|
||||
btn.dataset.origText = origText;
|
||||
function copyText(text) {
|
||||
// Modern path - only works over HTTPS or localhost
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
return navigator.clipboard.writeText(text);
|
||||
}
|
||||
// Legacy fallback for plain HTTP
|
||||
return new Promise(function(resolve, reject) {
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.left = '-9999px';
|
||||
ta.style.top = '0';
|
||||
document.body.appendChild(ta);
|
||||
ta.focus();
|
||||
ta.select();
|
||||
try {
|
||||
var ok = document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
if (ok) resolve(); else reject(new Error('execCommand returned false'));
|
||||
} catch (err) {
|
||||
document.body.removeChild(ta);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function flashCopied(btn, success) {
|
||||
var origText = btn.dataset.origText || btn.textContent;
|
||||
btn.dataset.origText = origText;
|
||||
if (success) {
|
||||
btn.textContent = 'copied!';
|
||||
btn.classList.remove('btn-outline-secondary');
|
||||
btn.classList.add('btn-success');
|
||||
@@ -156,16 +178,25 @@ document.addEventListener('click', function(e) {
|
||||
btn.classList.add('btn-outline-secondary');
|
||||
btn.style.transform = 'scale(1)';
|
||||
}, 1200);
|
||||
}).catch(function(err) {
|
||||
} else {
|
||||
btn.textContent = 'failed';
|
||||
btn.classList.remove('btn-outline-secondary');
|
||||
btn.classList.add('btn-danger');
|
||||
setTimeout(function() {
|
||||
btn.textContent = btn.dataset.origText || 'copy';
|
||||
btn.textContent = origText;
|
||||
btn.classList.remove('btn-danger');
|
||||
btn.classList.add('btn-outline-secondary');
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('.copy-btn');
|
||||
if (!btn) return;
|
||||
var text = btn.getAttribute('data-copy-text');
|
||||
if (!text) return;
|
||||
copyText(text).then(function() { flashCopied(btn, true); })
|
||||
.catch(function(err) { console.error('copy failed:', err); flashCopied(btn, false); });
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user