Быстрое копирование описяния продукта на 3D Маркетплейсах.
// ==UserScript==
// @name Click-to-Copy Ultimate Pro
// @namespace http://tampermonkey.net/
// @version 24.1
// @description Быстрое копирование описяния продукта на 3D Маркетплейсах.
// @author Bogus
// @match *://*/*
// @grant GM_setClipboard
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
// --- НАСТРОЙКИ ---
const configurations = [
{ container: '.description .std', blocks: 'p', needsPreprocessing: false },
{ container: '.prodInfo', blocks: 'span', needsPreprocessing: true },
{ container: '#description', blocks: 'span', needsPreprocessing: true },
{ container: '.data_for_whats_included', blocks: 'li', needsPreprocessing: false }
];
const defaultClickAction = 'copyAll'; // 'copyOne' или 'copyAll'
// Список точных фраз для исключения.
const excludedPhrases = [
"Please see each product's page for details.",
"See each product page for details."
];
// --- КОНЕЦ НАСТРОЕК ---
let cumulativeClipboardText = '';
GM_addStyle(`
.tm-highlight-copy { background-color: #e6f7ff !important; outline: 1px solid #91d5ff !important; transition: all 0.2s ease-out; }
.tm-flash-clear { background-color: #fff1f0 !important; outline: 2px solid #ffccc7 !important; transition: all 0.1s ease-in; }
.tm-flash-copy-all { background-color: #e6f7ff !important; outline: 2px solid #91d5ff !important; transition: all 0.1s ease-in; }
${configurations.map(c => c.container).join(', ')} { cursor: copy; }
.tm-preprocessed span { display: block !important; margin-bottom: 1em; }
`);
// --- УЛУЧШЕННАЯ ФУНКЦИЯ ФИЛЬТРАЦИИ ---
const filterText = (text) => {
if (!text) return null;
const trimmedText = text.trim();
// 1. НОВОЕ ПРАВИЛО: Проверяем, начинается ли строка со звездочки.
if (trimmedText.startsWith('*')) {
console.log(`Фильтрация (по правилу '*'): Абзац "${trimmedText}" был исключен.`);
return null;
}
// 2. СТАРОЕ ПРАВИЛО: Проверяем на полное совпадение с фразами из списка.
const lowercasedText = trimmedText.toLowerCase();
const lowercasedExclusions = excludedPhrases.map(p => p.toLowerCase());
if (lowercasedExclusions.includes(lowercasedText)) {
console.log(`Фильтрация (по списку): Абзац "${trimmedText}" был исключен.`);
return null;
}
// Если все проверки пройдены, возвращаем оригинальный текст.
return text;
};
const clearBuffer = (targetElement) => {
cumulativeClipboardText = '';
GM_setClipboard('');
targetElement.classList.add('tm-flash-clear');
setTimeout(() => targetElement.classList.remove('tm-flash-clear'), 500);
};
const copyAllParagraphs = (targetElement, blockSelector) => {
const allTextElements = targetElement.querySelectorAll(blockSelector);
if (allTextElements.length === 0) return;
const texts = Array.from(allTextElements)
.map(el => filterText(el.textContent))
.filter(Boolean)
.map(text => text.trim());
if (texts.length === 0) {
console.log("Все абзацы были отфильтрованы.");
return;
}
cumulativeClipboardText = texts.join('\n\n');
GM_setClipboard(cumulativeClipboardText);
targetElement.classList.add('tm-flash-copy-all');
setTimeout(() => targetElement.classList.remove('tm-flash-copy-all'), 500);
};
const copyOneParagraph = (clickedBlock) => {
const paragraphText = filterText(clickedBlock.textContent);
if (!paragraphText) return;
const trimmedText = paragraphText.trim();
cumulativeClipboardText = cumulativeClipboardText === '' ? trimmedText : cumulativeClipboardText + '\n\n' + trimmedText;
GM_setClipboard(cumulativeClipboardText);
clickedBlock.classList.add('tm-highlight-copy');
setTimeout(() => clickedBlock.classList.remove('tm-highlight-copy'), 500);
};
const preprocessContainer = (containerElement) => {
containerElement.classList.add('tm-preprocessed');
let html = containerElement.innerHTML;
const separator = '{{TM_PARAGRAPH_BREAK}}';
html = html.replace(/<br\s*\/?>\s*<br\s*\/?>/gi, separator);
const htmlParagraphs = html.split(separator);
containerElement.innerHTML = '';
htmlParagraphs.forEach(p_html => {
const p_with_newlines = p_html.replace(/<br\s*\/?>/gi, '\n');
const tempDiv = document.createElement('div');
tempDiv.innerHTML = p_with_newlines;
const cleanText = (tempDiv.textContent || tempDiv.innerText || '').trim();
if (cleanText) {
const span = document.createElement('span');
span.textContent = cleanText;
containerElement.appendChild(span);
}
});
};
const attachListeners = (containerElement, finalBlockSelector) => {
containerElement.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const copyOneAction = () => {
const clickedBlock = event.target.closest(finalBlockSelector);
if (clickedBlock && containerElement.contains(clickedBlock)) {
copyOneParagraph(clickedBlock);
}
};
const copyAllAction = () => copyAllParagraphs(containerElement, finalBlockSelector);
if (event.ctrlKey) {
(defaultClickAction === 'copyAll') ? copyOneAction() : copyAllAction();
} else {
(defaultClickAction === 'copyAll') ? copyAllAction() : copyOneAction();
}
});
containerElement.addEventListener('contextmenu', (event) => {
event.preventDefault();
event.stopPropagation();
clearBuffer(containerElement);
});
};
function initialize() {
for (const config of configurations) {
const containerElement = document.querySelector(config.container);
if (containerElement) {
let finalBlockSelector = config.blocks;
if (config.needsPreprocessing) {
setTimeout(() => {
preprocessContainer(containerElement);
finalBlockSelector = 'span';
attachListeners(containerElement, finalBlockSelector);
}, 500);
} else {
attachListeners(containerElement, finalBlockSelector);
}
}
}
}
initialize();
})();