Pith - ayh_extension
ayh_extension/ayh_chrome_extension/content.js [5.0 kb]
Modified: 21:55:51 103 026 (30 Jun 026)
25 Days Ago
(async function init() {
    const domain = window.location.hostname;
    const { disabledSites = [] } = await browser.storage.local.get('disabledSites');

    if (disabledSites.includes(domain)) return;

    let conversionCount = 0;

    const pronounMap = { 'm': 'aym', 'll': 'ayll', 've': 'ayve', 'd': 'ayd' };
    const acronymMap = { 'TIL': 'TAL', 'AITAH': 'AATAH' };
    
    const pronounRegex = /\bI(?:['’](m|ll|ve|d))?\b/g;
    const acronymRegex = /\b(TIL|AITAH)\b/g;

    const skipTags = ['SCRIPT', 'STYLE', 'INPUT', 'TEXTAREA', 'IFRAME', 'CODE', 'PRE'];

    function updateBadge() {
        browser.runtime.sendMessage({ action: 'updateBadge', count: conversionCount });
    }

    function capitalize(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

    function isSentenceStart(text, offset) {
        if (offset === 0) return true;
        const before = text.slice(0, offset).trim();
        if (before.length === 0) return true;
        const lastChar = before.slice(-1);
        return /[.!?\"\'“”‘’:]/.test(lastChar);
    }

    function startsUppercase(word) {
        return !!word && word[0] === word[0].toUpperCase() && word[0] !== word[0].toLowerCase();
    }

    function isAllCaps(word) {
        return !!word && /[A-Za-z]/.test(word) && word === word.toUpperCase();
    }

    function prevWord(text, offset) {
        let i = offset - 1;
        while (i >= 0 && !/[A-Za-z]/.test(text[i])) i--;
        if (i < 0) return null;
        const end = i + 1;
        while (i >= 0 && /[A-Za-z'’]/.test(text[i])) i--;
        return text.slice(i + 1, end);
    }

    function nextWord(text, offset) {
        let i = offset;
        while (i < text.length && !/[A-Za-z]/.test(text[i])) i++;
        if (i >= text.length) return null;
        const start = i;
        while (i < text.length && /[A-Za-z'’]/.test(text[i])) i++;
        return text.slice(start, i);
    }

    function isTitleContext(text, offset, matchLen) {
        const prev = prevWord(text, offset);
        if (!startsUppercase(prev)) return false;
        const next = nextWord(text, offset + matchLen);
        if (next === null) return true;
        return startsUppercase(next);
    }

    function isAllCapsContext(text, offset, matchLen) {
        const prev = prevWord(text, offset);
        const next = nextWord(text, offset + matchLen);
        if (prev === null && next === null) return false;
        if (prev !== null && !isAllCaps(prev)) return false;
        if (next !== null && !isAllCaps(next)) return false;
        return true;
    }

    function transformText(text) {
        let changed = false;
        let result = text;

        result = result.replace(acronymRegex, (match) => {
            conversionCount++;
            changed = true;
            return acronymMap[match];
        });

        result = result.replace(pronounRegex, (match, suffix, offset, fullText) => {
            if (offset > 0 && fullText[offset - 1] === '.') {
                return match;
            }

            const nextCharIndex = offset + match.length;
            if (nextCharIndex < fullText.length && (fullText[nextCharIndex] === '.' || fullText[nextCharIndex] === '-' || fullText[nextCharIndex] === '/')) {
                return match;
            }

            conversionCount++;
            changed = true;
            const rep = suffix ? pronounMap[suffix] : 'ayh';
            
            if (isAllCapsContext(fullText, offset, match.length)) return rep.toUpperCase();
            const shouldCapitalize = isSentenceStart(fullText, offset) || isTitleContext(fullText, offset, match.length);
            return shouldCapitalize ? capitalize(rep) : rep;
        });

        if (changed) updateBadge();
        return result;
    }

    function walk(node) {
        const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, {
            acceptNode: (n) => skipTags.includes(n.parentElement?.tagName) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT
        });

        let textNode;
        while (textNode = walker.nextNode()) {
            const newVal = transformText(textNode.nodeValue);
            if (newVal !== textNode.nodeValue) textNode.nodeValue = newVal;
        }
    }

    walk(document.body);

    const observer = new MutationObserver((mutations) => {
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach(node => {
                if (node.nodeType === 1) {
                    walk(node);
                } 
                else if (node.nodeType === 3) {
                    const parentTag = node.parentElement?.tagName;
                    
                    if (parentTag && skipTags.includes(parentTag)) return;
                    
                    if (node.parentElement?.isContentEditable) return;

                    const newVal = transformText(node.nodeValue);
                    if (newVal !== node.nodeValue) node.nodeValue = newVal;
                }
            });
        });
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();
Updates
Wedge - Android 126.026
Wedge - Linux 124.026
Shim - Android 117.026
Miter - 114.026
Kerf - Android 112.026
Dev
TVShow (227) 'CSA'
TVShow (228) 'APT'
TVProgram (83) 'BXT'
Miter Update(s)
Peen (Messaging)

Menu
Calendar
Project Tin (024/029)
Miter
RSS Feed
User Avatar
@vgmlr
=SUM(parts)