<!--https://mods.authweb.ru/modifikaciya-kopirovaniya-->
<!--Для работы корректной работы модификации, используйте класс aw_copy-->
<style>
/* Базовый вид clickable-элемента */
.aw_copy {
cursor: pointer;
display: flex !important;
align-items: center;
}
/* Маленькая иконка копии (добавляется скриптом) */
.aw_copy__icon {
display: inline-block;
width: 1em;
height: 1em;
vertical-align: -0.15em;
margin-left: 0.35em;
opacity: 0.7;
}
.aw_copy__icon svg { width: 1em; height: 1em; display: block; }
/* Тост рядом с элементом */
#awcopy__toast {
position: absolute; /* позиционируем относительно окна, но ставим рядом с элементом через координаты */
z-index: 99999;
background: rgba(24,24,24,0.92);
color: #fff;
font: 14px/1.35 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
padding: 8px 10px;
border-radius: 8px;
pointer-events: none;
transform: translateY(6px); /* начальное смещение для slide */
opacity: 0;
transition: transform 0.1s ease, opacity 0.1s ease; /* slide 0.2s */
box-shadow: 0 6px 18px rgba(0,0,0,0.18);
white-space: nowrap;
}
/* Показ тоста */
#awcopy__toast.is-visible {
opacity: 1;
transform: translateY(0);
}
/* Небольшой треугольник-указатель */
#awcopy__toast::after {
content: "";
position: absolute;
top: -6px;
left: 12px;
border: 6px solid transparent;
border-bottom-color: rgba(24,24,24,0.92);
}
</style>
<script>
(function () {
'use strict';
// ====== Настройки по умолчанию ======
const DEFAULTS = {
feedback: 'Скопировано',
toast: true,
behavior: 'copy-only',
durationMs: 1000,
};
// ====== Метрики (опционально) ======
function trackCopy(value) {
try {
if (Array.isArray(window.dataLayer)) {
window.dataLayer.push({ event: 'awcopy_success', awcopy_value: value });
}
if (typeof window.gtag === 'function') {
window.gtag('event', 'awcopy_success', { value });
}
if (typeof window.ym === 'function' && window.AW_YM_ID) {
window.ym(window.AW_YM_ID, 'reachGoal', 'awcopy_success', { value });
}
} catch (_) {}
}
// ====== Clipboard ======
async function copyToClipboard(text) {
if (!text) throw new Error('Empty copy value');
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(text);
return true;
}
const ta = document.createElement('textarea');
ta.value = text;
ta.setAttribute('readonly', '');
ta.style.position = 'fixed';
ta.style.top = '-9999px';
document.body.appendChild(ta);
ta.select();
try {
document.execCommand('copy');
document.body.removeChild(ta);
return true;
} catch (e) {
document.body.removeChild(ta);
throw e;
}
}
// ====== Toast (singleton) ======
let toastEl = null;
let toastTimer = null;
function ensureToast() {
if (toastEl && document.body.contains(toastEl)) return toastEl;
toastEl = document.createElement('div');
toastEl.id = 'awcopy__toast';
toastEl.setAttribute('role', 'status');
toastEl.setAttribute('aria-live', 'polite');
document.body.appendChild(toastEl);
return toastEl;
}
function showToastNear(targetEl, text, durationMs) {
const toast = ensureToast();
toast.textContent = text || DEFAULTS.feedback;
const rect = targetEl.getBoundingClientRect();
const scrollX = window.scrollX || window.pageXOffset;
const scrollY = window.scrollY || window.pageYOffset;
const margin = 10;
const top = rect.bottom + scrollY + margin;
let left = rect.left + scrollX;
toast.classList.remove('is-visible');
toast.style.top = top + 'px';
toast.style.left = left + 'px';
requestAnimationFrame(() => {
const tRect = toast.getBoundingClientRect();
const overflowRight = (tRect.right > document.documentElement.clientWidth - 8);
if (overflowRight) {
const delta = tRect.right - document.documentElement.clientWidth + 8;
left = Math.max(8, left - delta);
toast.style.left = left + 'px';
}
requestAnimationFrame(() => {
toast.classList.add('is-visible');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
toast.classList.remove('is-visible');
}, Math.max(500, durationMs || DEFAULTS.durationMs));
});
});
}
// ====== Helpers ======
function getMailtoAnchor(el) {
if (!el) return null;
if (el.tagName === 'A' && /^mailto:/i.test(el.getAttribute('href') || '')) return el;
return el.closest && el.closest('a[href^="mailto:"]');
}
function getCopyValueFrom(triggerEl) {
const data = triggerEl.getAttribute('data-copy');
if (data && data.trim()) return data.trim();
const anchor = getMailtoAnchor(triggerEl);
if (anchor) {
const email = (anchor.getAttribute('href') || '').replace(/^mailto:/i, '').trim();
if (email) return email;
}
const txtTrigger = (triggerEl.textContent || '').trim();
if (txtTrigger) return txtTrigger;
if (anchor) {
const txtA = (anchor.textContent || '').trim();
if (txtA) return txtA;
}
return '';
}
function getBehavior(el) {
return (el.getAttribute('data-behavior') || DEFAULTS.behavior).toLowerCase();
}
function getFeedback(el) {
return el.getAttribute('data-feedback') || DEFAULTS.feedback;
}
function shouldShowToast(el) {
const attr = el.getAttribute('data-toast');
return attr == null ? DEFAULTS.toast : String(attr).toLowerCase() !== 'false';
}
function makeAccessible(el) {
const isInteractive = /^(A|BUTTON|INPUT|TEXTAREA)$/i.test(el.tagName);
if (!isInteractive) {
el.setAttribute('role', 'button');
if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
}
if (!el.hasAttribute('aria-label')) {
el.setAttribute('aria-label', 'Скопировать в буфер обмена');
}
}
function injectCopyIcon(el) {
if (el.querySelector('.aw_copy__icon')) return;
const span = document.createElement('span');
span.className = 'aw_copy__icon';
span.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M16 1H4a2 2 0 0 0-2 2v12h2V3h12V1zm3 4H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2zm0 16H8V7h11v14z"/></svg>';
el.appendChild(span);
}
function initAwCopy(root = document) {
const nodes = root.querySelectorAll('.aw_copy');
nodes.forEach(el => {
makeAccessible(el);
injectCopyIcon(el);
});
}
// ====== Guard от двойной инициализации ======
if (window.__awcopy_bound) return;
window.__awcopy_bound = true;
// Первичная инициализация
document.addEventListener('DOMContentLoaded', () => initAwCopy());
document.addEventListener('tilda:load', () => initAwCopy());
document.addEventListener('tilda:changed', () => initAwCopy());
// ====== ЕДИНСТВЕННЫЙ перехватчик кликов (делегирование) ======
document.addEventListener('click', async (e) => {
const trigger = e.target.closest && e.target.closest('.aw_copy');
if (!trigger) return;
const behavior = getBehavior(trigger);
const anchor = getMailtoAnchor(trigger);
// если есть mailto и режим copy-only — жёстко глушим навигацию ДО обработки другими слушателями
if (anchor && behavior === 'copy-only') {
e.preventDefault();
if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();
e.stopPropagation();
}
const value = getCopyValueFrom(trigger);
try {
await copyToClipboard(value);
trigger.dispatchEvent(new CustomEvent('awcopy:success', { bubbles: true, detail: { value } }));
trackCopy(value);
if (shouldShowToast(trigger)) showToastNear(trigger, getFeedback(trigger), DEFAULTS.durationMs);
} catch (err) {
trigger.dispatchEvent(new CustomEvent('awcopy:error', { bubbles: true, detail: { reason: String(err) } }));
if (shouldShowToast(trigger)) showToastNear(trigger, 'Нажмите Ctrl+C / ⌘C', DEFAULTS.durationMs);
}
}, { passive: false, capture: true }); // ВАЖНО: capture=true
// Поддержка клавиатуры
document.addEventListener('keydown', async (e) => {
if (e.key !== 'Enter' && e.key !== ' ') return;
const el = document.activeElement;
if (!el || !el.classList || !el.classList.contains('aw_copy')) return;
e.preventDefault();
const behavior = getBehavior(el);
// если это <a href="mailto:">, навигацию не запускаем (режим copy-only)
const value = getCopyValueFrom(el);
try {
await copyToClipboard(value);
el.dispatchEvent(new CustomEvent('awcopy:success', { bubbles: true, detail: { value } }));
trackCopy(value);
if (shouldShowToast(el)) showToastNear(el, getFeedback(el), DEFAULTS.durationMs);
} catch (err) {
el.dispatchEvent(new CustomEvent('awcopy:error', { bubbles: true, detail: { reason: String(err) } }));
if (shouldShowToast(el)) showToastNear(el, 'Нажмите Ctrl+C / ⌘C', DEFAULTS.durationMs);
}
});
})();
</script>