Click-to-Copy Ultimate Pro

Быстрое копирование описяния продукта на 3D Маркетплейсах.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();
})();