Google Sheets Diff alerts mods: Remover cabeçalhos de linhas e Modificar Links do Google

Remove elementos .row-header-wrapper, modifica links do Google removendo o prefixo de redirecionamento e decodifica %3D para = em todos os elementos de texto dentro de tabelas, garantindo que links como exemplo.com/?parametro%3Dvalor sejam corrigidos

目前為 2025-03-31 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Google Sheets Diff alerts mods: Remover cabeçalhos de linhas e Modificar Links do Google
// @namespace    http://tampermonkey.net/
// @version      3.4
// @description  Remove elementos .row-header-wrapper, modifica links do Google removendo o prefixo de redirecionamento e decodifica %3D para = em todos os elementos de texto dentro de tabelas, garantindo que links como exemplo.com/?parametro%3Dvalor sejam corrigidos
// @author       luascfl
// @icon         https://e7.pngegg.com/pngimages/660/350/png-clipart-green-and-white-sheet-icon-google-docs-google-sheets-spreadsheet-g-suite-google-angle-rectangle-thumbnail.png
// @match        https://docs.google.com/spreadsheets/d/*/notify/show*
// @match        https://docs.google.com/spreadsheets/u/*/d/*/revisions/show*
// @home         https://github.com/luascfl/gsheets-diff-alerts-mods
// @supportURL   https://github.com/luascfl/gsheets-diff-alerts-mods/issues
// @license      MIT
// @grant        none
// @run-at       document-start
// ==/UserScript==
(function() {
    'use strict';
    const INTERVALO = 100; // Intervalo em milissegundos

    // Função para remover os row headers
    function removerElementos() {
        document.querySelectorAll('.row-header-wrapper').forEach(el => {
            el.remove();
        });
    }

    // Função para modificar os links do Google
    function modificarLinks() {
        // Primeiro processa todos os links na página
        document.querySelectorAll('a').forEach(link => {
            processarLink(link);
        });

        // Depois processa especificamente os links dentro de tbody
        document.querySelectorAll('tbody a').forEach(link => {
            decodificarEncodingNoLink(link);
        });

        // Processa todos os elementos textuais dentro de tbody
        decodificarTextosEmTbody();
    }

    // Função para processar cada link individualmente
    function processarLink(link) {
        if (link.href.startsWith('https://www.google.com/url?q=')) {
            // Extrai a URL real removendo o prefixo e tudo depois de &sa=
            let novaUrl = link.href.replace('https://www.google.com/url?q=', '');

            // Se tiver o parâmetro &sa=, remove ele e tudo o que vem depois
            const index = novaUrl.indexOf('&sa=');
            if (index !== -1) {
                novaUrl = novaUrl.substring(0, index);
            }

            link.href = novaUrl;

            // Atualizar também o atributo data-href se existir
            if (link.hasAttribute('data-href')) {
                const dataHref = link.getAttribute('data-href');
                if (dataHref.startsWith('https://www.google.com/url?q=')) {
                    let novoDataHref = dataHref.replace('https://www.google.com/url?q=', '');

                    // Se tiver o parâmetro &sa=, remove ele e tudo o que vem depois
                    const dataIndex = novoDataHref.indexOf('&sa=');
                    if (dataIndex !== -1) {
                        novoDataHref = novoDataHref.substring(0, dataIndex);
                    }

                    link.setAttribute('data-href', novoDataHref);
                }
            }
        }
    }

    // Função específica para decodificar %3D para = nos links dentro de tbody
    function decodificarEncodingNoLink(link) {
        // Decodifica %3D para = no href (tanto no atributo quanto no valor real)
        if (link.href.includes('%3D')) {
            // Define o novo href decodificado
            link.href = link.href.replaceAll('%3D', '=');
        }

        // Também verifica e corrige o atributo href diretamente
        // (isso é importante porque às vezes link.href normaliza a URL enquanto o atributo original permanece)
        if (link.hasAttribute('href')) {
            const hrefAttr = link.getAttribute('href');
            if (hrefAttr.includes('%3D')) {
                link.setAttribute('href', hrefAttr.replaceAll('%3D', '='));
            }
        }

        // Decodifica %3D para = no data-href se existir
        if (link.hasAttribute('data-href')) {
            const dataHref = link.getAttribute('data-href');
            if (dataHref.includes('%3D')) {
                link.setAttribute('data-href', dataHref.replaceAll('%3D', '='));
            }
        }

        // Decodifica %3D para = no texto do link se necessário
        if (link.textContent.includes('%3D')) {
            link.textContent = link.textContent.replaceAll('%3D', '=');
        }

        // Verifica se o texto visível está correto mas o href não
        // (como no exemplo: texto é "codigoVaga=5520104" mas href é "codigoVaga%3D5520104")
        if (!link.textContent.includes('%3D') && link.textContent.includes('=')) {
            // Tenta encontrar todos os parâmetros no texto visível
            const paramsInText = link.textContent.match(/[?&][^?&=]+=[^?&=]+/g);
            if (paramsInText) {
                let hrefAtual = link.getAttribute('href');
                // Para cada parâmetro encontrado no texto
                paramsInText.forEach(param => {
                    // Extrai nome e valor do parâmetro
                    const [paramName, paramValue] = param.substring(1).split('=');
                    // Verifica se esse mesmo parâmetro existe no href mas com %3D
                    const encodedParam = `${paramName}%3D${paramValue}`;
                    if (hrefAtual.includes(encodedParam)) {
                        // Substitui a versão codificada pela decodificada
                        hrefAtual = hrefAtual.replace(encodedParam, `${paramName}=${paramValue}`);
                    }
                });
                // Atualiza o href se houve mudanças
                if (hrefAtual !== link.getAttribute('href')) {
                    link.setAttribute('href', hrefAtual);
                }
            }
        }
    }

    // Função para decodificar %3D em todos os elementos de texto dentro de tbody
    function decodificarTextosEmTbody() {
        // Seleciona todos os elementos tbody na página
        document.querySelectorAll('tbody').forEach(tbody => {
            // Itera sobre todos os elementos dentro do tbody
            iterarESusbstituirTextoEmElemento(tbody);
        });
    }

    // Função recursiva para iterar sobre todos os nós filhos e substituir texto
    function iterarESusbstituirTextoEmElemento(elemento) {
        // Para cada nó filho do elemento
        Array.from(elemento.childNodes).forEach(node => {
            // Se for um nó de texto
            if (node.nodeType === Node.TEXT_NODE) {
                if (node.textContent.includes('%3D')) {
                    node.textContent = node.textContent.replaceAll('%3D', '=');
                }
            }
            // Se for um elemento (como div, span, td, etc.) e não for um link (já tratado separadamente)
            else if (node.nodeType === Node.ELEMENT_NODE && node.nodeName !== 'A') {
                // Processo para td, th ou outros elementos com valor
                if (node.value && typeof node.value === 'string' && node.value.includes('%3D')) {
                    node.value = node.value.replaceAll('%3D', '=');
                }

                // Processa atributos comuns que podem conter texto
                ['title', 'alt', 'placeholder', 'data-text'].forEach(attr => {
                    if (node.hasAttribute(attr)) {
                        const attrValue = node.getAttribute(attr);
                        if (attrValue.includes('%3D')) {
                            node.setAttribute(attr, attrValue.replaceAll('%3D', '='));
                        }
                    }
                });

                // Continua a recursão para os filhos deste elemento
                iterarESusbstituirTextoEmElemento(node);
            }
        });
    }

    // Função que combina todas as funcionalidades
    function processarPagina() {
        removerElementos();
        modificarLinks();
    }

    // Configuração do observer para detectar mudanças no DOM
    let observer;
    const callback = () => {
        processarPagina();
        if (!observer) {
            observer = new MutationObserver(processarPagina);
            observer.observe(document.documentElement, {
                childList: true,
                subtree: true
            });
        }
    };

    // Executa imediatamente e mantém intervalo
    (function loop() {
        processarPagina();
        setTimeout(loop, INTERVALO);
    })();

    // Garante execução após o carregamento completo
    window.addEventListener('load', () => {
        processarPagina();
    });

    // Adicionar listener para os links que são adicionados dinamicamente
    document.addEventListener('DOMNodeInserted', function(event) {
        if (event.target.nodeName === 'A' ||
            (event.target.nodeType === 1 && event.target.querySelectorAll)) {
            modificarLinks();
        }
    }, false);
})();