【油猴脚本】集团OA高亮指定关键字

处理集团OA文件,高亮指定关键字,方便查看弱项情况等。

// ==UserScript==
// @name         集团OA高亮关键词“AA”“BB”
// @namespace    http://tampermonkey.net/
// @version      5.0
// @description  解决关键词被多个span拆分导致高亮失败的问题
// @author       subk
// @match        http://XXX/std-official-document-view/g4/process-form*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const keywords = [
        { word: 'AA', color: 'yellow' },
        { word: 'BB', color: 'lightskyblue' },
    ];

    // Escape special characters for regex
    const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

    // Create a single regex for all keywords
    const regex = new RegExp(keywords.map(({ word }) => escapeRegExp(word)).join('|'), 'g');

    function highlightInElement(el) {
        const treeWalker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, {
            acceptNode: (node) => {
                // Skip nodes inside script, style, or already highlighted spans
                if (node.parentNode.tagName === 'SCRIPT' || node.parentNode.tagName === 'STYLE' || node.parentNode.classList.contains('highlight')) {
                    return NodeFilter.FILTER_REJECT;
                }
                return node.nodeValue.match(regex) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
            }
        }, false);

        const textNodes = [];
        while (treeWalker.nextNode()) {
            textNodes.push(treeWalker.currentNode);
        }

        textNodes.forEach(node => {
            const parent = node.parentNode;
            const fragment = document.createDocumentFragment();
            const text = node.nodeValue;
            let lastIndex = 0;

            // Split text and wrap matches in spans
            text.replace(regex, (match, index) => {
                // Add text before the match
                if (index > lastIndex) {
                    fragment.appendChild(document.createTextNode(text.slice(lastIndex, index)));
                }

                // Find the matching keyword and its color
                const { color } = keywords.find(kw => kw.word === match) || { color: 'yellow' };
                const span = document.createElement('span');
                span.className = 'highlight';
                span.style.backgroundColor = color;
                span.textContent = match;
                fragment.appendChild(span);

                lastIndex = index + match.length;
            });

            // Add remaining text
            if (lastIndex < text.length) {
                fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
            }

            // Replace the original text node
            parent.replaceChild(fragment, node);
        });
    }

    function applyHighlight() {
        const container = document.querySelector('.WordSection1');
        if (container) {
            highlightInElement(container);
            return true;
        }
        return false;
    }

    // Observe DOM changes to handle dynamic content
    function observeDOM() {
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                if (mutation.addedNodes.length || mutation.type === 'childList') {
                    if (applyHighlight()) {
                        // Optionally stop observing if highlighting is successful
                        // observer.disconnect();
                    }
                }
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // Initial attempt
        applyHighlight();
    }

    // Run on DOMContentLoaded for faster execution
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', observeDOM);
    } else {
        observeDOM();
    }
})();